encoders works fine

This commit is contained in:
Edward Emelianov 2025-04-04 11:56:37 +03:00
parent faeac98318
commit f46185feaf
18 changed files with 359 additions and 361 deletions

View File

@ -1,5 +1,124 @@
Get data from 2 encoders by BISS-C protocol
===========================================
This device works with two BISS-C encoders (resolution from 8 to 32 bit).
If you want to test readout from device, run `./testDev /dev/encoder_X0` or `./testDev /dev/encoder_X0`.
** Encoder cable pinout:
* 1 - NC or shield
* 2 - CLK_A - positive of SSI clock out
* 3 - CLC_B - negative of SSI clock out
* 4 - NC or shield
* 5 - +5V - 5V power for sensor (at least 250mA)
* 6 - DATA_A - positive of data in
* 7 - DATA_B - negative of data in
* 8 - NC
* 9 - Gnd - common ground
** Device interfaces
After connection you will see device 0483:5740 with three CDC interfaces. Each interface
have its own `iInterface` field, by default they are:
* encoder_cmd - configure/command/debugging interface
* encoder_X - X sensor output
* encoder_Y - Y sensor output
Add to udev-rules file `99-myHW.rules` which will create symlinks to each interface in `/dev/` directory.
You can change all three `iInterface` values and store them in device' flash memory.
The readout of encoders depends on settings. If you save in flash `autom=1`, readout of both encoders
will repeat each `amperiod` milliseconds. Also you always can ask for readout sending any '\n'-terminated
data into encoder's interface or running commands `readenc`, `readX` or `readY` in command interface.
** Protocol
The device have simple text protocol, each text string should be closed by '\n'.
Base format is 'param [ = value]', where 'param' could be command to run some procedure
or getter, 'value' is setter.
Answer for all getters is 'param=value'. Here are answers for setters and procedures:
* OK - all OK
* FAIL - procedure failed to run
* BADCMD - your entered wrong command
* BADPAR - parameter of setter is out of allowable range
Some procedures (like 'help' or 'readenc') returns a lot of data after calling.
*** Base commands on command interface
These are commands for directrly work with SPI interfaces:
* readenc - read both encoders once
* readX - read X encoder once
* readY - read Y encoder once
* help - show full help
* reset - reset MCU
* spideinit - deinit SPI
* spiinit - init SPI
* spistat - get status of both SPI interfaces
*** Configuration commands
This set of commands allows to change current configuration (remember: each time SPI configuration changes
you need to run `spiinit`) and store it into flash memory.
* autom - turn on or off automonitoring
* amperiod - period of monitoring, 1..255 milliseconds
* BR - change SPI BR register (1 - 18MHz ... 7 - 281kHz)
* CPHA - change CPHA value (0/1)
* CPOL - change CPOL value (0/1)
* dumpconf - dump current configuration
* encbits - set encoder data bits amount (26/32)
* encbufsz - change encoder auxiliary buffer size (8..32 bytes)
* erasestorage - erase full storage or current page (=n)
* maxzeros - maximal amount of zeros in preamble
* minzeros - minimal amount of zeros in preamble
* setiface1 - set name of first (command) interface
* setiface2 - set name of second (axis X) interface
* setiface3 - set name of third (axis Y) interface
* storeconf - store configuration in flash memory
Here is example of default configuration ouput (`dumpconf`):
```
userconf_sz=108
currentconfidx=-1
setiface1=
setiface2=
setiface3=
autom=0
amperiod=1
BR=4
CPHA=0
CPOL=1
encbits=26
encbufsz=12
maxzeros=50
minzeros=4
```
`userconf_sz` is some type of "magick sequence" to find start of last record in flash memory.
`currentconfidx` shows number of record (-1 means that the storage is empty and you see default values).
Empty field of `setifaceX` means default interface name.
*** Debugging commands
Some of these commands could be usefull when you're trying to play with settings or want to measure maximal
readout speed for encoders (when each reading starts immediately after parsing previous result).
* dummy - dummy integer setter/getter
* fin - reinit flash (e.g. to restore last configuration)
* sendx - send text string to X encoder's terminal
* sendy - send text string to Y encoder's terminal
* testx - test X-axis throughput
* testy - test Y-axis throughput
tl;dr

View File

@ -17,16 +17,14 @@
*/
#include "bissC.h"
#include "usb_dev.h"
#include "flash.h"
#ifdef EBUG
#include "strfunc.h"
#endif
#include "flash.h"
#include "spi.h"
#include "usb_dev.h"
#define MAX_BITSTREAM_UINTS 4 // 4 * 32 = 128 bits capacity
// min/max zeros before preambule
#define MINZEROS 4
#define MAXZEROS 40
#define MAX_BITSTREAM_UINTS (ENCODER_BUFSZ_MAX / 4) // ENCODER_BUFSZ_MAX bits capacity
static uint32_t bitstream[MAX_BITSTREAM_UINTS] = {0};
@ -50,11 +48,6 @@ static void bytes_to_bitstream(const uint8_t *bytes, uint32_t num_bytes, uint32_
}
}
}
/* Store remaining bits if any
if(*num_bits % 32 != 0){
current <<= (32 - (*num_bits % 32));
bitstream[pos] = current;
}*/
}
// Compute CRC-6 using polynomial x^6 + x + 1
@ -102,7 +95,7 @@ BiSS_Frame parse_biss_frame(const uint8_t *bytes, uint32_t num_bytes){
if(!curbit){
zero_count++;
}else{
if(zero_count >= MINZEROS && zero_count <= MAXZEROS){
if(zero_count >= the_conf.minzeros && zero_count <= the_conf.maxzeros){
if((i + 1) < num_bits && !get_bit(i + 1)){
data_start = i + 2;
found = 1;

View File

@ -20,6 +20,11 @@
#include <stdint.h>
#include "spi.h"
#if ENCRESOL_MAX > 32
#error "Change full code. Current don't support more than 32 bits of encoder resolution."
#endif
typedef struct {
uint32_t data; // 26/32/36-bit data
uint8_t error : 1; // error flag (0 - error: bad value or high temperature)

Binary file not shown.

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 15.0.1, 2025-04-02T14:36:26. -->
<!-- Written by QtCreator 15.0.1, 2025-04-02T18:29:56. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>

View File

@ -33,7 +33,11 @@ user_conf the_conf = {
.userconf_sz = sizeof(user_conf),
.flags.CPOL = 1,
.flags.BR = 4,
.encbits = 26
.encbits = 26,
.encbufsz = 12,
.minzeros = 4,
.maxzeros = 50,
.monittime = 1,
};
int currentconfidx = -1; // index of current configuration

View File

@ -27,10 +27,17 @@
// maximal size (in letters) of iInterface for settings
#define MAX_IINTERFACE_SZ (16)
// min/max zeros before preambule
#define MINZEROS_MIN 2
#define MINZEROS_MAX 60
#define MAXZEROS_MIN 4
#define MAXZEROS_MAX 120
typedef struct{
uint8_t CPOL : 1;
uint8_t CPHA : 1;
uint8_t BR : 3;
uint8_t monit: 1; // auto monitoring of encoder each `monittime` milliseconds
} flags_t;
/*
@ -40,8 +47,12 @@ typedef struct __attribute__((packed, aligned(4))){
uint16_t userconf_sz; // "magick number"
uint16_t iInterface[bTotNumEndpoints][MAX_IINTERFACE_SZ]; // hryunikod!
uint8_t iIlengths[bTotNumEndpoints];
flags_t flags; // flags: CPOL, CPHA etc
uint8_t encbits; // encoder bits: 26 or 32
uint8_t encbufsz; // encoder buffer size (up to ENCODER_BUFSZ_MAX)
uint8_t minzeros; // min/max zeros in preamble when searching start of record
uint8_t maxzeros;
uint8_t monittime; // auto monitoring period (ms)
flags_t flags; // flags: CPOL, CPHA etc
} user_conf;
extern user_conf the_conf;

View File

@ -27,6 +27,7 @@
volatile uint32_t Tms = 0;
static char inbuff[RBINSZ];
static uint32_t monitT[2] = {0};
/* Called when systick fires */
void sys_tick_handler(void){
@ -47,7 +48,6 @@ static void printResult(BiSS_Frame *result){
}
static void proc_enc(uint8_t idx){
uint8_t encbuf[ENCODER_BUFSZ];
static uint32_t lastMSG[2], gotgood[2], gotwrong[2];
int iface = idx ? I_Y : I_X;
char ifacechr = idx ? 'Y' : 'X';
@ -65,9 +65,11 @@ static void proc_enc(uint8_t idx){
CMDWR("'\n");
}
}
if(l > 0) spi_start_enc(idx); // start encoder reading on each request from given interface
}
if(!spi_read_enc(idx, encbuf)) return;
BiSS_Frame result = parse_biss_frame(encbuf, ENCODER_BUFSZ);
uint8_t *encbuf = spi_read_enc(idx);
if(!encbuf) return;
BiSS_Frame result = parse_biss_frame(encbuf, the_conf.encbufsz);
char *str = result.crc_valid ? u2str(result.data) : NULL;
uint8_t testflag = (idx) ? user_pars.testy : user_pars.testx;
if(CDCready[I_CMD]){
@ -86,11 +88,11 @@ static void proc_enc(uint8_t idx){
if(str) ++gotgood[idx];
else ++gotwrong[idx];
}
}else{
}else if(!the_conf.flags.monit){
printResult(&result);
CMDWR("ENC"); USB_putbyte(I_CMD, ifacechr);
USB_putbyte(I_CMD, '=');
hexdump(I_CMD, encbuf, ENCODER_BUFSZ);
hexdump(I_CMD, encbuf, the_conf.encbufsz);
CMDWR(" (");
if(str) CMDWR(str);
else CMDWR("wrong");
@ -102,22 +104,17 @@ static void proc_enc(uint8_t idx){
USB_putbyte(iface, '\n');
}
if(result.error) spi_setup(1+idx); // reinit SPI in case of error
if(testflag) spi_start_enc(idx);
if(the_conf.flags.monit) monitT[idx] = Tms;
else if(testflag) spi_start_enc(idx);
}
int main(){
uint32_t lastT = 0;//, lastS = 0;
uint32_t lastT = 0;
StartHSE();
flashstorage_init();
hw_setup();
USBPU_OFF();
SysTick_Config(72000);
/*
#ifdef EBUG
usart_setup();
uint32_t tt = 0;
#endif
*/
USB_setup();
#ifndef EBUG
iwdg_setup();
@ -129,29 +126,18 @@ int main(){
LED_blink(LED0);
lastT = Tms;
}
/*
#ifdef EBUG
if(Tms != tt){
__disable_irq();
usart_transmit();
tt = Tms;
__enable_irq();
}
#endif
*/
if(CDCready[I_CMD]){
/*if(Tms - lastS > 9999){
USB_sendstr(I_CMD, "Tms=");
USB_sendstr(I_CMD, u2str(Tms));
CMDn();
lastS = Tms;
}*/
int l = USB_receivestr(I_CMD, inbuff, RBINSZ);
if(l < 0) CMDWRn("ERROR: CMD USB buffer overflow or string was too long");
else if(l) parse_cmd(inbuff);
}
proc_enc(0);
proc_enc(1);
if(the_conf.flags.monit){
for(int i = 0; i < 2; ++i){
if(Tms - monitT[i] >= the_conf.monittime) spi_start_enc(i);
}
}
}
return 0;
}

View File

@ -1,94 +1,4 @@
set FLASH_SIZE 0x10000
source [find interface/stlink.cfg]
# script for stm32f1x family
#
# stm32 devices support both JTAG and SWD transports.
#
source [find target/swj-dp.tcl]
source [find mem_helper.tcl]
if { [info exists CHIPNAME] } {
set _CHIPNAME $CHIPNAME
} else {
set _CHIPNAME stm32f1x
}
set _ENDIAN little
# Work-area is a space in RAM used for flash programming
# By default use 4kB (as found on some STM32F100s)
if { [info exists WORKAREASIZE] } {
set _WORKAREASIZE $WORKAREASIZE
} else {
set _WORKAREASIZE 0x1000
}
# Allow overriding the Flash bank size
if { [info exists FLASH_SIZE] } {
set _FLASH_SIZE $FLASH_SIZE
} else {
# autodetect size
set _FLASH_SIZE 0
}
#jtag scan chain
if { [info exists CPUTAPID] } {
set _CPUTAPID $CPUTAPID
} else {
if { [using_jtag] } {
# See STM Document RM0008 Section 26.6.3
set _CPUTAPID 0x2ba01477
} {
# this is the SW-DP tap id not the jtag tap id
set _CPUTAPID 0x2ba01477
}
}
swj_newdap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_CPUTAPID
dap create $_CHIPNAME.dap -chain-position $_CHIPNAME.cpu
if {[using_jtag]} {
jtag newtap $_CHIPNAME bs -irlen 5
}
set _TARGETNAME $_CHIPNAME.cpu
target create $_TARGETNAME cortex_m -endian $_ENDIAN -dap $_CHIPNAME.dap
$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0
# flash size will be probed
set _FLASHNAME $_CHIPNAME.flash
flash bank $_FLASHNAME stm32f1x 0x08000000 $_FLASH_SIZE 0 0 $_TARGETNAME
# JTAG speed should be <= F_CPU/6. F_CPU after reset is 8MHz, so use F_JTAG = 1MHz
adapter_khz 1000
adapter_nsrst_delay 100
if {[using_jtag]} {
jtag_ntrst_delay 100
}
reset_config srst_nogate
if {![using_hla]} {
# if srst is not fitted use SYSRESETREQ to
# perform a soft reset
cortex_m reset_config sysresetreq
}
$_TARGETNAME configure -event examine-end {
# DBGMCU_CR |= DBG_WWDG_STOP | DBG_IWDG_STOP |
# DBG_STANDBY | DBG_STOP | DBG_SLEEP
mmw 0xE0042004 0x00000307 0
}
$_TARGETNAME configure -event trace-config {
# Set TRACE_IOEN; TRACE_MODE is set to async; when using sync
# change this value accordingly to configure trace pins
# assignment
mmw 0xE0042004 0x00000020 0
}
source [find target/stm32f1x.cfg]

View File

@ -1,4 +0,0 @@
set FLASH_SIZE 0x10000
source [find interface/stlink.cfg]
source [find target/stm32f1x.cfg]

View File

@ -72,6 +72,11 @@ typedef enum{
C_encbits,
C_testX,
C_testY,
C_encbufsz,
C_minzeros,
C_maxzeros,
C_autom,
C_amperiod,
C_AMOUNT
} cmd_e;
@ -223,58 +228,64 @@ static errcode_e spideinit(_U_ cmd_e idx, _U_ char *par){
return ERR_SILENCE;
}
static errcode_e setflags(cmd_e idx, char *par){
static errcode_e setuintpar(cmd_e idx, char *par){
uint32_t val;
if(par){
if(*par < '0' || *par > '9') return ERR_BADPAR;
uint8_t val = *par - '0';
if(par == getnum(par, &val)) return ERR_BADPAR;
switch(idx){
case C_cpha:
if(val > 1) return ERR_BADPAR;
the_conf.flags.CPHA = val;
break;
case C_cpol:
if(val > 1) return ERR_BADPAR;
the_conf.flags.CPOL = val;
break;
case C_br:
if(val == 0 || val > 7) return ERR_BADPAR;
the_conf.flags.BR = val;
break;
case C_encbits:
if(val < ENCRESOL_MIN || val > ENCRESOL_MAX) return ERR_BADPAR; // don't support less than 8 of more than 32
the_conf.encbits = val;
break;
case C_encbufsz:
if(val < 8 || val > ENCODER_BUFSZ_MAX) return ERR_BADPAR;
the_conf.encbufsz = val;
break;
case C_minzeros:
if(val < MINZEROS_MIN || val > MINZEROS_MAX) return ERR_BADPAR;
the_conf.minzeros = val;
break;
case C_maxzeros:
if(val < MAXZEROS_MIN || val > MAXZEROS_MAX) return ERR_BADPAR;
the_conf.maxzeros = val;
break;
case C_amperiod:
if(val > 255 || val == 0) return ERR_BADPAR;
the_conf.monittime = val;
break;
default:
return ERR_BADCMD;
}
}
uint8_t val = 0;
CMDWR(commands[idx].cmd);
USB_putbyte(I_CMD, '=');
switch(idx){
case C_cpha:
val = the_conf.flags.CPHA;
break;
case C_cpol:
val = the_conf.flags.CPOL;
break;
case C_br:
val = the_conf.flags.BR;
break;
case C_encbits:
val = the_conf.encbits;
break;
case C_encbufsz:
val = the_conf.encbufsz;
break;
case C_minzeros:
val = the_conf.minzeros;
break;
case C_maxzeros:
val = the_conf.maxzeros;
break;
case C_amperiod:
val = the_conf.monittime;
break;
default:
return ERR_BADCMD;
}
CMDWR(commands[idx].cmd);
USB_putbyte(I_CMD, '=');
USB_putbyte(I_CMD, '0' + val);
CMDn();
return ERR_SILENCE;
}
static errcode_e encbits(cmd_e idx, char *par){
if(par){
uint32_t N;
if(par == getnum(par, &N)) return ERR_BADPAR;
if(N < 26 || N > 32) return ERR_BADPAR; // don't support less than 26 of more than 32
the_conf.encbits = N;
}
CMDWR(commands[idx].cmd);
USB_putbyte(I_CMD, '=');
CMDWR(u2str(the_conf.encbits));
CMDWR(u2str(val));
CMDn();
return ERR_SILENCE;
}
@ -285,24 +296,42 @@ static errcode_e setboolpar(cmd_e idx, char *par){
if(*par != '0' && *par != '1') return ERR_BADPAR;
val = *par - '0';
switch(idx){
case C_cpha:
the_conf.flags.CPHA = val;
break;
case C_cpol:
the_conf.flags.CPOL = val;
break;
case C_testX:
user_pars.testx = val;
if(val) spi_start_enc(0);
break;
case C_testY:
user_pars.testy = val;
if(val) spi_start_enc(1);
break;
case C_autom:
the_conf.flags.monit = val;
break;
default:
return ERR_BADCMD;
}
}
switch(idx){
case C_cpha:
val = the_conf.flags.CPHA;
break;
case C_cpol:
val = the_conf.flags.CPOL;
break;
case C_testX:
val = user_pars.testx;
if(val) spi_start_enc(0);
break;
case C_testY:
val = user_pars.testy;
if(val) spi_start_enc(1);
break;
case C_autom:
val = the_conf.flags.monit;
break;
default:
return ERR_BADCMD;
@ -320,10 +349,15 @@ static errcode_e dumpconf(cmd_e _U_ idx, char _U_ *par){
CMDn();
for(int i = 0; i < bTotNumEndpoints; ++i)
setiface(C_setiface1 + i, NULL);
setflags(C_br, NULL);
setflags(C_cpha, NULL);
setflags(C_cpol, NULL);
encbits(C_encbits, NULL);
setboolpar(C_autom, NULL);
setuintpar(C_amperiod, NULL);
setuintpar(C_br, NULL);
setboolpar(C_cpha, NULL);
setboolpar(C_cpol, NULL);
setuintpar(C_encbits, NULL);
setuintpar(C_encbufsz, NULL);
setuintpar(C_maxzeros, NULL);
setuintpar(C_minzeros, NULL);
return ERR_SILENCE;
}
@ -347,12 +381,17 @@ static const funcdescr_t commands[C_AMOUNT] = {
[C_spistat] = {"spistat", spistat},
[C_spiinit] = {"spiinit", spiinit},
[C_spideinit] = {"spideinit", spideinit},
[C_cpol] = {"CPOL", setflags},
[C_cpha] = {"CPHA", setflags},
[C_br] = {"BR", setflags},
[C_encbits] = {"encbits", encbits},
[C_cpol] = {"CPOL", setboolpar},
[C_cpha] = {"CPHA", setboolpar},
[C_br] = {"BR", setuintpar},
[C_encbits] = {"encbits", setuintpar},
[C_testX] = {"testx", setboolpar},
[C_testY] = {"testy", setboolpar},
[C_encbufsz] = {"encbufsz", setuintpar},
[C_minzeros] = {"minzeros", setuintpar},
[C_maxzeros] = {"maxzeros", setuintpar},
[C_autom] = {"autom", setboolpar},
[C_amperiod] = {"amperiod", setuintpar},
};
typedef struct{
@ -362,28 +401,33 @@ typedef struct{
// SHOUL be sorted and grouped
static const help_t helpmessages[] = {
{-1, "Configuration"},
{C_dumpconf, "dump current configuration"},
{C_erasestorage, "erase full storage or current page (=n)"},
{C_setiface1, "set name of first (command) interface"},
{C_setiface2, "set name of second (axis X) interface"},
{C_setiface3, "set name of third (axis Y) interface"},
{C_storeconf, "store configuration in flash memory"},
{-1, "Different commands"},
{C_dummy, "dummy integer setter/getter"},
{C_encstart, "start reading encoders"},
{C_encX, "read only X encoder"},
{C_encY, "read only Y encoder"},
{-1, "Base commands"},
{C_encstart, "read both encoders once"},
{C_encX, "read X encoder once"},
{C_encY, "read Y encoder once"},
{C_help, "show this help"},
{C_reset, "reset MCU"},
{C_spideinit, "deinit SPI"},
{C_spiinit, "init SPI"},
{C_spistat, "get status of both SPI interfaces"},
{-1, "Debug"},
{-1, "Configuration"},
{C_autom, "turn on or off automonitoring"},
{C_amperiod, "period (ms) of monitoring, 1..255"},
{C_br, "change SPI BR register (1 - 18MHz ... 7 - 281kHz)"},
{C_cpha, "change CPHA value (0/1)"},
{C_cpol, "change CPOL value (0/1)"},
{C_encbits, "set encoder data bits amount"},
{C_dumpconf, "dump current configuration"},
{C_encbits, "set encoder data bits amount (26/32)"},
{C_encbufsz, "change encoder auxiliary buffer size (8..32 bytes)"},
{C_erasestorage, "erase full storage or current page (=n)"},
{C_maxzeros, "maximal amount of zeros in preamble"},
{C_minzeros, "minimal amount of zeros in preamble"},
{C_setiface1, "set name of first (command) interface"},
{C_setiface2, "set name of second (axis X) interface"},
{C_setiface3, "set name of third (axis Y) interface"},
{C_storeconf, "store configuration in flash memory"},
{-1, "Debug commands"},
{C_dummy, "dummy integer setter/getter"},
{C_fin, "reinit flash"},
{C_sendX, "send text string to X encoder's terminal"},
{C_sendY, "send text string to Y encoder's terminal"},

View File

@ -34,7 +34,7 @@ static volatile SPI_TypeDef* const SPIs[AMOUNT_OF_SPI+1] = {NULL, SPI1, SPI2};
static volatile DMA_Channel_TypeDef * const DMAs[AMOUNT_OF_SPI+1] = {NULL, DMA1_Channel2, DMA1_Channel4};
#define WAITX(x) do{volatile uint32_t wctr = 0; while((x) && (++wctr < 3600)) IWDG->KR = IWDG_REFRESH; if(wctr==3600){ DBG("timeout"); return 0;}}while(0)
static uint8_t encoderbuf[AMOUNT_OF_SPI][ENCODER_BUFSZ] = {0};
static uint8_t encoderbuf[AMOUNT_OF_SPI][ENCODER_BUFSZ_MAX] = {0};
static uint8_t freshdata[AMOUNT_OF_SPI] = {0};
// init SPI to work RX-only with DMA
@ -81,8 +81,6 @@ void spi_onoff(uint8_t idx, uint8_t on){
CHKIDX(idx);
volatile SPI_TypeDef *SPI = SPIs[idx];
if(on){
//DBGs(u2str(idx));
//DBG("turn on SPI");
SPI->CR1 |= SPI_CR1_SPE;
spi_status[idx] = SPI_BUSY;
}else{
@ -114,20 +112,15 @@ static int spi_waitbsy(uint8_t idx){
DBG("Busy - turn off");
spi_onoff(idx, 0); // turn off SPI if it's busy
}
//DBGs(u2str(idx));
//DBG("wait busy");
//WAITX(SPIs[idx]->SR & SPI_SR_BSY);
return 1;
}
// just copy last read encoder value into `buf`
// @return TRUE if got fresh data
int spi_read_enc(uint8_t encno, uint8_t buf[ENCODER_BUFSZ]){
if(encno > 1 || !freshdata[encno]) return FALSE;
//DBGs(u2str(encno)); DBG("Read encoder data");
memcpy(buf, encoderbuf[encno], ENCODER_BUFSZ);
// @return pointer to buffer if got fresh data
uint8_t *spi_read_enc(uint8_t encno){
if(encno > 1 || !freshdata[encno]) return NULL;
freshdata[encno] = 0; // clear fresh status
return TRUE;
return encoderbuf[encno];
}
// start encoder reading over DMA
@ -135,16 +128,12 @@ int spi_read_enc(uint8_t encno, uint8_t buf[ENCODER_BUFSZ]){
// here `encodernum` is 0 (SPI1) or 1 (SPI2), not 1/2 as SPI index!
int spi_start_enc(int encodernum){
int spiidx = encodernum + 1;
//DBG("start enc");
if(spiidx < 1 || spiidx > AMOUNT_OF_SPI) return FALSE;
if(spi_status[spiidx] != SPI_READY) return FALSE;
if(!spi_waitbsy(spiidx)) return FALSE;
if(SPI1->CR1 & SPI_CR1_SPE){ DBG("spi1 works!");}
if(SPI2->CR1 & SPI_CR1_SPE){ DBG("spi2 works!");}
volatile DMA_Channel_TypeDef *DMA = DMAs[spiidx];
DMA->CMAR = (uint32_t) encoderbuf[encodernum];
DMA->CNDTR = ENCODER_BUFSZ;
//DBG("turn on spi");
DMA->CNDTR = the_conf.encbufsz;
spi_onoff(spiidx, 1);
DMA->CCR |= DMA_CCR_EN;
return TRUE;
@ -159,12 +148,8 @@ void dma1_channel2_isr(){
DMA1->IFCR = DMA_IFCR_CTEIF2;
}
if(DMA1->ISR & DMA_ISR_TCIF2){
//uint32_t ctr = TIM2->CNT;
DMA1->IFCR = DMA_IFCR_CTCIF2;
freshdata[0] = 1;
//encoderbuf[5] = (ctr >> 16) & 0xff;
//encoderbuf[6] = (ctr >> 8 ) & 0xff;
//encoderbuf[7] = (ctr >> 0 ) & 0xff;
}
spi_status[1] = SPI_READY;
}

View File

@ -2,9 +2,13 @@
#pragma once
#include <stdint.h>
// two SPI interfaces for two sensors
#define AMOUNT_OF_SPI (2)
#define ENCODER_BUFSZ (12)
// static buffer size
#define ENCODER_BUFSZ_MAX (32)
// encoder resolution
#define ENCRESOL_MIN (8)
#define ENCRESOL_MAX (32)
typedef enum{
SPI_NOTREADY,
@ -18,4 +22,4 @@ void spi_onoff(uint8_t idx, uint8_t on);
void spi_deinit(uint8_t idx);
void spi_setup(uint8_t idx);
int spi_start_enc(int encodernum);
int spi_read_enc(uint8_t encno, uint8_t buf[ENCODER_BUFSZ]);
uint8_t *spi_read_enc(uint8_t encno);

View File

@ -1,100 +0,0 @@
/*
* usart.c
*
* Copyright 2018 Edward V. Emelianoff <eddy@sao.ru, edward.emelianoff@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include <string.h>
#include "stm32f1.h"
#include "usart.h"
#include "ringbuffer.h"
extern volatile uint32_t Tms;
// buffers for out ringbuffer and DMA send
static uint8_t buf[UARTBUFSZ], txbuf[UARTBUFSZ];
static ringbuffer ringbuf = {.data = buf, .length = UARTBUFSZ};
static volatile int usart_txrdy = 1; // transmission done
// transmit current tbuf
void usart_transmit(){
if(RB_hasbyte(&ringbuf, '\n') < 0 || !usart_txrdy) return;
int L = 0, l = 0;
do{
l = RB_readto(&ringbuf, '\n', txbuf + L, UARTBUFSZ - L);
if(l > 0) L += l;
}while(l > 0 && L < UARTBUFSZ);
if(L < 1) return;
usart_txrdy = 0;
if(L < UARTBUFSZ-1){
txbuf[L++] = '$'; txbuf[L++] = '\n';
}
DMA1_Channel4->CCR &= ~DMA_CCR_EN;
DMA1_Channel4->CMAR = (uint32_t) txbuf; // mem
DMA1_Channel4->CNDTR = L;
DMA1_Channel4->CCR |= DMA_CCR_EN;
}
void usart_putchar(const char ch){
RB_write(&ringbuf, (const uint8_t*)&ch, 1);
}
void usart_send(const char *str){
int l = strlen(str);
if(RB_datalen(&ringbuf) > UARTBUFSZ/2) usart_transmit();
RB_write(&ringbuf, (const uint8_t*)str, l);
}
/*
* USART speed: baudrate = Fck/(USARTDIV)
* USARTDIV stored in USART->BRR
*
* for 72MHz USARTDIV=72000/f(kboud); so for 115200 USARTDIV=72000/115.2=625 -> BRR=0x271
* 9600: BRR = 7500 (0x1D4C)
*/
void usart_setup(){
uint32_t tmout = 16000000;
// PA9 - Tx, PA10 - Rx
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_USART1EN | RCC_APB2ENR_AFIOEN;
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
GPIOA->CRH |= CRH(9, CNF_AFPP|MODE_NORMAL) | CRH(10, CNF_FLINPUT|MODE_INPUT);
// USART1 Tx DMA - Channel4 (Rx - channel 5)
DMA1_Channel4->CPAR = (uint32_t) &USART1->DR; // periph
DMA1_Channel4->CCR |= DMA_CCR_MINC | DMA_CCR_DIR | DMA_CCR_TCIE; // 8bit, mem++, mem->per, transcompl irq
// Tx CNDTR set @ each transmission due to data size
NVIC_SetPriority(DMA1_Channel4_IRQn, 3);
NVIC_EnableIRQ(DMA1_Channel4_IRQn);
NVIC_SetPriority(USART1_IRQn, 0);
// setup usart1
USART1->BRR = 72000000 / 4000000;
USART1->CR1 = USART_CR1_TE | USART_CR1_UE; // 1start,8data,nstop; enable Rx,Tx,USART
while(!(USART1->SR & USART_SR_TC)){if(--tmout == 0) break;} // polling idle frame Transmission
USART1->SR = 0; // clear flags
USART1->CR1 |= USART_CR1_RXNEIE; // allow Rx IRQ
USART1->CR3 = USART_CR3_DMAT; // enable DMA Tx
}
void dma1_channel4_isr(){
if(DMA1->ISR & DMA_ISR_TCIF4){ // Tx
DMA1->IFCR = DMA_IFCR_CTCIF4; // clear TC flag
usart_txrdy = 1;
}
}

View File

@ -1,2 +1,2 @@
#define BUILD_NUMBER "91"
#define BUILD_DATE "2025-04-02"
#define BUILD_NUMBER "96"
#define BUILD_DATE "2025-04-04"

View File

@ -78,13 +78,19 @@ static void chkin(uint8_t ifno){
static void send_next(uint8_t ifno){
uint8_t usbbuff[USB_TXBUFSZ];
int buflen = RB_read((ringbuffer*)&rbout[ifno], (uint8_t*)usbbuff, USB_TXBUFSZ);
if(!CDCready[ifno]){
lastdsz[ifno] = -1;
return;
}
if(buflen == 0){
if(lastdsz[ifno] == 64) EP_Write(1+ifno, NULL, 0); // send ZLP after 64 bits packet when nothing more to send
if(lastdsz[ifno] == USB_TXBUFSZ){
EP_Write(1+ifno, NULL, 0); // send ZLP after 64 bits packet when nothing more to send
lastdsz[ifno] = 0;
}else lastdsz[ifno] = -1; // OK. User can start sending data
return;
}else if(buflen < 0){
DBG("Buff busy");
lastdsz[ifno] = 0;
lastdsz[ifno] = -1;
return;
}
DBG("Got data in buf");
@ -221,14 +227,17 @@ int USB_send(uint8_t ifno, const uint8_t *buf, int len){
if(!buf || !CDCready[ifno] || !len) return FALSE;
DBG("USB_send");
while(len){
if(!CDCready[ifno]) return FALSE;
IWDG->KR = IWDG_REFRESH;
int a = RB_write((ringbuffer*)&rbout[ifno], buf, len);
if(lastdsz[ifno] == 0) send_next(ifno); // need to run manually - all data sent, so no IRQ on IN
if(a > 0){
len -= a;
buf += a;
} else if (a < 0) continue; // do nothing if buffer is in reading state
}else if(a == 0){ // overfull
if(lastdsz[ifno] < 0) send_next(ifno);
}
}
if(buf[len-1] == '\n' && lastdsz[ifno] < 0) send_next(ifno);
return TRUE;
}
@ -236,10 +245,15 @@ int USB_putbyte(uint8_t ifno, uint8_t byte){
if(!CDCready[ifno]) return FALSE;
int l = 0;
while((l = RB_write((ringbuffer*)&rbout[ifno], &byte, 1)) != 1){
if(!CDCready[ifno]) return FALSE;
IWDG->KR = IWDG_REFRESH;
if(lastdsz[ifno] == 0) send_next(ifno); // need to run manually - all data sent, so no IRQ on IN
if(l < 0) continue;
if(l == 0){ // overfull
if(lastdsz[ifno] < 0) send_next(ifno);
continue;
}
}
// send line if got EOL
if(byte == '\n' && lastdsz[ifno] < 0) send_next(ifno);
return TRUE;
}

View File

@ -75,13 +75,19 @@ static void chkin(){
static void send_next(){
uint8_t usbbuff[USB_TXBUFSZ];
int buflen = RB_read((ringbuffer*)&rbout, (uint8_t*)usbbuff, USB_TXBUFSZ);
if(!CDCready){
lastdsz = -1;
return;
}
if(buflen == 0){
if(lastdsz == 64) EP_Write(1, NULL, 0); // send ZLP after 64 bits packet when nothing more to send
if(lastdsz == USB_TXBUFSZ){
EP_Write(1, NULL, 0); // send ZLP after 64 bits packet when nothing more to send
lastdsz = 0;
}else lastdsz = -1;
return;
}else if(buflen < 0){
DBG("Buff busy");
lastdsz = 0;
lastdsz = -1;
return;
}
DBG("Got data in buf");
@ -193,14 +199,17 @@ int USB_send(const uint8_t *buf, int len){
if(!buf || !CDCready || !len) return FALSE;
DBG("USB_send");
while(len){
if(!CDCready) return FALSE;
IWDG->KR = IWDG_REFRESH;
int a = RB_write((ringbuffer*)&rbout, buf, len);
if(lastdsz == 0) send_next(); // need to run manually - all data sent, so no IRQ on IN
if(a > 0){
len -= a;
buf += a;
} else if (a < 0) continue; // do nothing if buffer is in reading state
}else if(a == 0){ // overfull
if(lastdsz < 0) send_next();
}
}
if(buf[len-1] == '\n' && lastdsz < 0) send_next();
return TRUE;
}
@ -208,10 +217,14 @@ int USB_putbyte(uint8_t byte){
if(!CDCready) return FALSE;
int l = 0;
while((l = RB_write((ringbuffer*)&rbout, &byte, 1)) != 1){
if(!CDCready) return FALSE;
IWDG->KR = IWDG_REFRESH;
if(lastdsz == 0) send_next(); // need to run manually - all data sent, so no IRQ on IN
if(l < 0) continue;
if(l == 0){ // overfull
if(lastdsz < 0) send_next();
continue;
}
}
if(byte == '\n' && lastdsz < 0) send_next();
return TRUE;
}

View File

@ -81,12 +81,18 @@ static void chkin(){
static void send_next(){
uint8_t usbbuff[USB_TXBUFSZ];
int buflen = RB_read((ringbuffer*)&rbout, (uint8_t*)usbbuff, USB_TXBUFSZ);
if(!CDCready){
lastdsz = -1;
return;
}
if(buflen == 0){
if(lastdsz == 64) EP_Write(3, NULL, 0); // send ZLP after 64 bits packet when nothing more to send
if(lastdsz == 64){
EP_Write(3, NULL, 0); // send ZLP after 64 bits packet when nothing more to send
lastdsz = 0;
} else lastdsz = -1; // OK. User can start sending data
return;
}else if(buflen < 0){
lastdsz = 0;
lastdsz = -1;
return;
}
EP_Write(3, (uint8_t*)usbbuff, buflen);
@ -231,16 +237,19 @@ int USB_sendall(){
// put `buf` into queue to send
int USB_send(const uint8_t *buf, int len){
if(!buf || !CDCready || !len) return FALSE;
DBG("send");
DBG("USB_send");
while(len){
if(!CDCready) return FALSE;
IWDG->KR = IWDG_REFRESH;
int a = RB_write((ringbuffer*)&rbout, buf, len);
if(lastdsz == 0) send_next(); // need to run manually - all data sent, so no IRQ on IN
if(a > 0){
len -= a;
buf += a;
} else if (a < 0) continue; // do nothing if buffer is in reading state
}else if(a == 0){ // overfull
if(lastdsz < 0) send_next();
}
}
if(buf[len-1] == '\n' && lastdsz < 0) send_next();
return TRUE;
}
@ -248,10 +257,15 @@ int USB_putbyte(uint8_t byte){
if(!CDCready) return FALSE;
int l = 0;
while((l = RB_write((ringbuffer*)&rbout, &byte, 1)) != 1){
if(!CDCready) return FALSE;
IWDG->KR = IWDG_REFRESH;
if(lastdsz == 0) send_next(); // need to run manually - all data sent, so no IRQ on IN
if(l < 0) continue;
if(l == 0){ // overfull
if(lastdsz < 0) send_next();
continue;
}
}
// send line if got EOL
if(byte == '\n' && lastdsz < 0) send_next();
return TRUE;
}