mirror of
https://github.com/eddyem/stm32samples.git
synced 2025-12-06 10:45:11 +03:00
encoders works fine
This commit is contained in:
parent
faeac98318
commit
f46185feaf
@ -1,5 +1,124 @@
|
|||||||
Get data from 2 encoders by BISS-C protocol
|
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
|
|
||||||
|
|||||||
@ -17,16 +17,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "bissC.h"
|
#include "bissC.h"
|
||||||
#include "usb_dev.h"
|
#include "flash.h"
|
||||||
#ifdef EBUG
|
#ifdef EBUG
|
||||||
#include "strfunc.h"
|
#include "strfunc.h"
|
||||||
#endif
|
#endif
|
||||||
#include "flash.h"
|
#include "spi.h"
|
||||||
|
#include "usb_dev.h"
|
||||||
|
|
||||||
#define MAX_BITSTREAM_UINTS 4 // 4 * 32 = 128 bits capacity
|
#define MAX_BITSTREAM_UINTS (ENCODER_BUFSZ_MAX / 4) // ENCODER_BUFSZ_MAX bits capacity
|
||||||
// min/max zeros before preambule
|
|
||||||
#define MINZEROS 4
|
|
||||||
#define MAXZEROS 40
|
|
||||||
|
|
||||||
static uint32_t bitstream[MAX_BITSTREAM_UINTS] = {0};
|
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
|
// 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){
|
if(!curbit){
|
||||||
zero_count++;
|
zero_count++;
|
||||||
}else{
|
}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)){
|
if((i + 1) < num_bits && !get_bit(i + 1)){
|
||||||
data_start = i + 2;
|
data_start = i + 2;
|
||||||
found = 1;
|
found = 1;
|
||||||
|
|||||||
@ -20,6 +20,11 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#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 {
|
typedef struct {
|
||||||
uint32_t data; // 26/32/36-bit data
|
uint32_t data; // 26/32/36-bit data
|
||||||
uint8_t error : 1; // error flag (0 - error: bad value or high temperature)
|
uint8_t error : 1; // error flag (0 - error: bad value or high temperature)
|
||||||
|
|||||||
Binary file not shown.
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE QtCreatorProject>
|
<!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>
|
<qtcreator>
|
||||||
<data>
|
<data>
|
||||||
<variable>EnvironmentId</variable>
|
<variable>EnvironmentId</variable>
|
||||||
|
|||||||
@ -33,7 +33,11 @@ user_conf the_conf = {
|
|||||||
.userconf_sz = sizeof(user_conf),
|
.userconf_sz = sizeof(user_conf),
|
||||||
.flags.CPOL = 1,
|
.flags.CPOL = 1,
|
||||||
.flags.BR = 4,
|
.flags.BR = 4,
|
||||||
.encbits = 26
|
.encbits = 26,
|
||||||
|
.encbufsz = 12,
|
||||||
|
.minzeros = 4,
|
||||||
|
.maxzeros = 50,
|
||||||
|
.monittime = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
int currentconfidx = -1; // index of current configuration
|
int currentconfidx = -1; // index of current configuration
|
||||||
|
|||||||
@ -27,10 +27,17 @@
|
|||||||
// maximal size (in letters) of iInterface for settings
|
// maximal size (in letters) of iInterface for settings
|
||||||
#define MAX_IINTERFACE_SZ (16)
|
#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{
|
typedef struct{
|
||||||
uint8_t CPOL : 1;
|
uint8_t CPOL : 1;
|
||||||
uint8_t CPHA : 1;
|
uint8_t CPHA : 1;
|
||||||
uint8_t BR : 3;
|
uint8_t BR : 3;
|
||||||
|
uint8_t monit: 1; // auto monitoring of encoder each `monittime` milliseconds
|
||||||
} flags_t;
|
} flags_t;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -40,8 +47,12 @@ typedef struct __attribute__((packed, aligned(4))){
|
|||||||
uint16_t userconf_sz; // "magick number"
|
uint16_t userconf_sz; // "magick number"
|
||||||
uint16_t iInterface[bTotNumEndpoints][MAX_IINTERFACE_SZ]; // hryunikod!
|
uint16_t iInterface[bTotNumEndpoints][MAX_IINTERFACE_SZ]; // hryunikod!
|
||||||
uint8_t iIlengths[bTotNumEndpoints];
|
uint8_t iIlengths[bTotNumEndpoints];
|
||||||
flags_t flags; // flags: CPOL, CPHA etc
|
|
||||||
uint8_t encbits; // encoder bits: 26 or 32
|
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;
|
} user_conf;
|
||||||
|
|
||||||
extern user_conf the_conf;
|
extern user_conf the_conf;
|
||||||
|
|||||||
@ -27,6 +27,7 @@
|
|||||||
|
|
||||||
volatile uint32_t Tms = 0;
|
volatile uint32_t Tms = 0;
|
||||||
static char inbuff[RBINSZ];
|
static char inbuff[RBINSZ];
|
||||||
|
static uint32_t monitT[2] = {0};
|
||||||
|
|
||||||
/* Called when systick fires */
|
/* Called when systick fires */
|
||||||
void sys_tick_handler(void){
|
void sys_tick_handler(void){
|
||||||
@ -47,7 +48,6 @@ static void printResult(BiSS_Frame *result){
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void proc_enc(uint8_t idx){
|
static void proc_enc(uint8_t idx){
|
||||||
uint8_t encbuf[ENCODER_BUFSZ];
|
|
||||||
static uint32_t lastMSG[2], gotgood[2], gotwrong[2];
|
static uint32_t lastMSG[2], gotgood[2], gotwrong[2];
|
||||||
int iface = idx ? I_Y : I_X;
|
int iface = idx ? I_Y : I_X;
|
||||||
char ifacechr = idx ? 'Y' : 'X';
|
char ifacechr = idx ? 'Y' : 'X';
|
||||||
@ -65,9 +65,11 @@ static void proc_enc(uint8_t idx){
|
|||||||
CMDWR("'\n");
|
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;
|
uint8_t *encbuf = spi_read_enc(idx);
|
||||||
BiSS_Frame result = parse_biss_frame(encbuf, ENCODER_BUFSZ);
|
if(!encbuf) return;
|
||||||
|
BiSS_Frame result = parse_biss_frame(encbuf, the_conf.encbufsz);
|
||||||
char *str = result.crc_valid ? u2str(result.data) : NULL;
|
char *str = result.crc_valid ? u2str(result.data) : NULL;
|
||||||
uint8_t testflag = (idx) ? user_pars.testy : user_pars.testx;
|
uint8_t testflag = (idx) ? user_pars.testy : user_pars.testx;
|
||||||
if(CDCready[I_CMD]){
|
if(CDCready[I_CMD]){
|
||||||
@ -86,11 +88,11 @@ static void proc_enc(uint8_t idx){
|
|||||||
if(str) ++gotgood[idx];
|
if(str) ++gotgood[idx];
|
||||||
else ++gotwrong[idx];
|
else ++gotwrong[idx];
|
||||||
}
|
}
|
||||||
}else{
|
}else if(!the_conf.flags.monit){
|
||||||
printResult(&result);
|
printResult(&result);
|
||||||
CMDWR("ENC"); USB_putbyte(I_CMD, ifacechr);
|
CMDWR("ENC"); USB_putbyte(I_CMD, ifacechr);
|
||||||
USB_putbyte(I_CMD, '=');
|
USB_putbyte(I_CMD, '=');
|
||||||
hexdump(I_CMD, encbuf, ENCODER_BUFSZ);
|
hexdump(I_CMD, encbuf, the_conf.encbufsz);
|
||||||
CMDWR(" (");
|
CMDWR(" (");
|
||||||
if(str) CMDWR(str);
|
if(str) CMDWR(str);
|
||||||
else CMDWR("wrong");
|
else CMDWR("wrong");
|
||||||
@ -102,22 +104,17 @@ static void proc_enc(uint8_t idx){
|
|||||||
USB_putbyte(iface, '\n');
|
USB_putbyte(iface, '\n');
|
||||||
}
|
}
|
||||||
if(result.error) spi_setup(1+idx); // reinit SPI in case of error
|
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(){
|
int main(){
|
||||||
uint32_t lastT = 0;//, lastS = 0;
|
uint32_t lastT = 0;
|
||||||
StartHSE();
|
StartHSE();
|
||||||
flashstorage_init();
|
flashstorage_init();
|
||||||
hw_setup();
|
hw_setup();
|
||||||
USBPU_OFF();
|
USBPU_OFF();
|
||||||
SysTick_Config(72000);
|
SysTick_Config(72000);
|
||||||
/*
|
|
||||||
#ifdef EBUG
|
|
||||||
usart_setup();
|
|
||||||
uint32_t tt = 0;
|
|
||||||
#endif
|
|
||||||
*/
|
|
||||||
USB_setup();
|
USB_setup();
|
||||||
#ifndef EBUG
|
#ifndef EBUG
|
||||||
iwdg_setup();
|
iwdg_setup();
|
||||||
@ -129,29 +126,18 @@ int main(){
|
|||||||
LED_blink(LED0);
|
LED_blink(LED0);
|
||||||
lastT = Tms;
|
lastT = Tms;
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
#ifdef EBUG
|
|
||||||
if(Tms != tt){
|
|
||||||
__disable_irq();
|
|
||||||
usart_transmit();
|
|
||||||
tt = Tms;
|
|
||||||
__enable_irq();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
*/
|
|
||||||
if(CDCready[I_CMD]){
|
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);
|
int l = USB_receivestr(I_CMD, inbuff, RBINSZ);
|
||||||
if(l < 0) CMDWRn("ERROR: CMD USB buffer overflow or string was too long");
|
if(l < 0) CMDWRn("ERROR: CMD USB buffer overflow or string was too long");
|
||||||
else if(l) parse_cmd(inbuff);
|
else if(l) parse_cmd(inbuff);
|
||||||
}
|
}
|
||||||
proc_enc(0);
|
proc_enc(0);
|
||||||
proc_enc(1);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,94 +1,4 @@
|
|||||||
set FLASH_SIZE 0x10000
|
set FLASH_SIZE 0x10000
|
||||||
|
|
||||||
source [find interface/stlink.cfg]
|
source [find interface/stlink.cfg]
|
||||||
|
source [find target/stm32f1x.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
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,4 +0,0 @@
|
|||||||
set FLASH_SIZE 0x10000
|
|
||||||
|
|
||||||
source [find interface/stlink.cfg]
|
|
||||||
source [find target/stm32f1x.cfg]
|
|
||||||
@ -72,6 +72,11 @@ typedef enum{
|
|||||||
C_encbits,
|
C_encbits,
|
||||||
C_testX,
|
C_testX,
|
||||||
C_testY,
|
C_testY,
|
||||||
|
C_encbufsz,
|
||||||
|
C_minzeros,
|
||||||
|
C_maxzeros,
|
||||||
|
C_autom,
|
||||||
|
C_amperiod,
|
||||||
C_AMOUNT
|
C_AMOUNT
|
||||||
} cmd_e;
|
} cmd_e;
|
||||||
|
|
||||||
@ -223,58 +228,64 @@ static errcode_e spideinit(_U_ cmd_e idx, _U_ char *par){
|
|||||||
return ERR_SILENCE;
|
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){
|
||||||
if(*par < '0' || *par > '9') return ERR_BADPAR;
|
if(par == getnum(par, &val)) return ERR_BADPAR;
|
||||||
uint8_t val = *par - '0';
|
|
||||||
switch(idx){
|
switch(idx){
|
||||||
case C_cpha:
|
case C_br:
|
||||||
if(val > 1) return ERR_BADPAR;
|
if(val == 0 || val > 7) return ERR_BADPAR;
|
||||||
the_conf.flags.CPHA = val;
|
the_conf.flags.BR = val;
|
||||||
break;
|
break;
|
||||||
case C_cpol:
|
case C_encbits:
|
||||||
if(val > 1) return ERR_BADPAR;
|
if(val < ENCRESOL_MIN || val > ENCRESOL_MAX) return ERR_BADPAR; // don't support less than 8 of more than 32
|
||||||
the_conf.flags.CPOL = val;
|
the_conf.encbits = val;
|
||||||
break;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CMDWR(commands[idx].cmd);
|
||||||
|
USB_putbyte(I_CMD, '=');
|
||||||
|
switch(idx){
|
||||||
case C_br:
|
case C_br:
|
||||||
if(val == 0 || val > 7) return ERR_BADPAR;
|
val = the_conf.flags.BR;
|
||||||
the_conf.flags.BR = val;
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
return ERR_BADCMD;
|
return ERR_BADCMD;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
uint8_t val = 0;
|
CMDWR(u2str(val));
|
||||||
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;
|
|
||||||
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));
|
|
||||||
CMDn();
|
CMDn();
|
||||||
return ERR_SILENCE;
|
return ERR_SILENCE;
|
||||||
}
|
}
|
||||||
@ -285,25 +296,43 @@ static errcode_e setboolpar(cmd_e idx, char *par){
|
|||||||
if(*par != '0' && *par != '1') return ERR_BADPAR;
|
if(*par != '0' && *par != '1') return ERR_BADPAR;
|
||||||
val = *par - '0';
|
val = *par - '0';
|
||||||
switch(idx){
|
switch(idx){
|
||||||
|
case C_cpha:
|
||||||
|
the_conf.flags.CPHA = val;
|
||||||
|
break;
|
||||||
|
case C_cpol:
|
||||||
|
the_conf.flags.CPOL = val;
|
||||||
|
break;
|
||||||
case C_testX:
|
case C_testX:
|
||||||
user_pars.testx = val;
|
user_pars.testx = val;
|
||||||
break;
|
if(val) spi_start_enc(0);
|
||||||
|
break;
|
||||||
case C_testY:
|
case C_testY:
|
||||||
user_pars.testy = val;
|
user_pars.testy = val;
|
||||||
break;
|
if(val) spi_start_enc(1);
|
||||||
|
break;
|
||||||
|
case C_autom:
|
||||||
|
the_conf.flags.monit = val;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return ERR_BADCMD;
|
return ERR_BADCMD;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch(idx){
|
switch(idx){
|
||||||
|
case C_cpha:
|
||||||
|
val = the_conf.flags.CPHA;
|
||||||
|
break;
|
||||||
|
case C_cpol:
|
||||||
|
val = the_conf.flags.CPOL;
|
||||||
|
break;
|
||||||
case C_testX:
|
case C_testX:
|
||||||
val = user_pars.testx;
|
val = user_pars.testx;
|
||||||
if(val) spi_start_enc(0);
|
break;
|
||||||
break;
|
|
||||||
case C_testY:
|
case C_testY:
|
||||||
val = user_pars.testy;
|
val = user_pars.testy;
|
||||||
if(val) spi_start_enc(1);
|
break;
|
||||||
break;
|
case C_autom:
|
||||||
|
val = the_conf.flags.monit;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return ERR_BADCMD;
|
return ERR_BADCMD;
|
||||||
}
|
}
|
||||||
@ -320,10 +349,15 @@ static errcode_e dumpconf(cmd_e _U_ idx, char _U_ *par){
|
|||||||
CMDn();
|
CMDn();
|
||||||
for(int i = 0; i < bTotNumEndpoints; ++i)
|
for(int i = 0; i < bTotNumEndpoints; ++i)
|
||||||
setiface(C_setiface1 + i, NULL);
|
setiface(C_setiface1 + i, NULL);
|
||||||
setflags(C_br, NULL);
|
setboolpar(C_autom, NULL);
|
||||||
setflags(C_cpha, NULL);
|
setuintpar(C_amperiod, NULL);
|
||||||
setflags(C_cpol, NULL);
|
setuintpar(C_br, NULL);
|
||||||
encbits(C_encbits, 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;
|
return ERR_SILENCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -347,12 +381,17 @@ static const funcdescr_t commands[C_AMOUNT] = {
|
|||||||
[C_spistat] = {"spistat", spistat},
|
[C_spistat] = {"spistat", spistat},
|
||||||
[C_spiinit] = {"spiinit", spiinit},
|
[C_spiinit] = {"spiinit", spiinit},
|
||||||
[C_spideinit] = {"spideinit", spideinit},
|
[C_spideinit] = {"spideinit", spideinit},
|
||||||
[C_cpol] = {"CPOL", setflags},
|
[C_cpol] = {"CPOL", setboolpar},
|
||||||
[C_cpha] = {"CPHA", setflags},
|
[C_cpha] = {"CPHA", setboolpar},
|
||||||
[C_br] = {"BR", setflags},
|
[C_br] = {"BR", setuintpar},
|
||||||
[C_encbits] = {"encbits", encbits},
|
[C_encbits] = {"encbits", setuintpar},
|
||||||
[C_testX] = {"testx", setboolpar},
|
[C_testX] = {"testx", setboolpar},
|
||||||
[C_testY] = {"testy", 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{
|
typedef struct{
|
||||||
@ -362,28 +401,33 @@ typedef struct{
|
|||||||
|
|
||||||
// SHOUL be sorted and grouped
|
// SHOUL be sorted and grouped
|
||||||
static const help_t helpmessages[] = {
|
static const help_t helpmessages[] = {
|
||||||
{-1, "Configuration"},
|
{-1, "Base commands"},
|
||||||
{C_dumpconf, "dump current configuration"},
|
{C_encstart, "read both encoders once"},
|
||||||
{C_erasestorage, "erase full storage or current page (=n)"},
|
{C_encX, "read X encoder once"},
|
||||||
{C_setiface1, "set name of first (command) interface"},
|
{C_encY, "read Y encoder once"},
|
||||||
{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"},
|
|
||||||
{C_help, "show this help"},
|
{C_help, "show this help"},
|
||||||
{C_reset, "reset MCU"},
|
{C_reset, "reset MCU"},
|
||||||
{C_spideinit, "deinit SPI"},
|
{C_spideinit, "deinit SPI"},
|
||||||
{C_spiinit, "init SPI"},
|
{C_spiinit, "init SPI"},
|
||||||
{C_spistat, "get status of both SPI interfaces"},
|
{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_br, "change SPI BR register (1 - 18MHz ... 7 - 281kHz)"},
|
||||||
{C_cpha, "change CPHA value (0/1)"},
|
{C_cpha, "change CPHA value (0/1)"},
|
||||||
{C_cpol, "change CPOL 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_fin, "reinit flash"},
|
||||||
{C_sendX, "send text string to X encoder's terminal"},
|
{C_sendX, "send text string to X encoder's terminal"},
|
||||||
{C_sendY, "send text string to Y encoder's terminal"},
|
{C_sendY, "send text string to Y encoder's terminal"},
|
||||||
|
|||||||
@ -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};
|
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)
|
#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};
|
static uint8_t freshdata[AMOUNT_OF_SPI] = {0};
|
||||||
|
|
||||||
// init SPI to work RX-only with DMA
|
// init SPI to work RX-only with DMA
|
||||||
@ -81,8 +81,6 @@ void spi_onoff(uint8_t idx, uint8_t on){
|
|||||||
CHKIDX(idx);
|
CHKIDX(idx);
|
||||||
volatile SPI_TypeDef *SPI = SPIs[idx];
|
volatile SPI_TypeDef *SPI = SPIs[idx];
|
||||||
if(on){
|
if(on){
|
||||||
//DBGs(u2str(idx));
|
|
||||||
//DBG("turn on SPI");
|
|
||||||
SPI->CR1 |= SPI_CR1_SPE;
|
SPI->CR1 |= SPI_CR1_SPE;
|
||||||
spi_status[idx] = SPI_BUSY;
|
spi_status[idx] = SPI_BUSY;
|
||||||
}else{
|
}else{
|
||||||
@ -114,20 +112,15 @@ static int spi_waitbsy(uint8_t idx){
|
|||||||
DBG("Busy - turn off");
|
DBG("Busy - turn off");
|
||||||
spi_onoff(idx, 0); // turn off SPI if it's busy
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// just copy last read encoder value into `buf`
|
// just copy last read encoder value into `buf`
|
||||||
// @return TRUE if got fresh data
|
// @return pointer to buffer if got fresh data
|
||||||
int spi_read_enc(uint8_t encno, uint8_t buf[ENCODER_BUFSZ]){
|
uint8_t *spi_read_enc(uint8_t encno){
|
||||||
if(encno > 1 || !freshdata[encno]) return FALSE;
|
if(encno > 1 || !freshdata[encno]) return NULL;
|
||||||
//DBGs(u2str(encno)); DBG("Read encoder data");
|
|
||||||
memcpy(buf, encoderbuf[encno], ENCODER_BUFSZ);
|
|
||||||
freshdata[encno] = 0; // clear fresh status
|
freshdata[encno] = 0; // clear fresh status
|
||||||
return TRUE;
|
return encoderbuf[encno];
|
||||||
}
|
}
|
||||||
|
|
||||||
// start encoder reading over DMA
|
// 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!
|
// here `encodernum` is 0 (SPI1) or 1 (SPI2), not 1/2 as SPI index!
|
||||||
int spi_start_enc(int encodernum){
|
int spi_start_enc(int encodernum){
|
||||||
int spiidx = encodernum + 1;
|
int spiidx = encodernum + 1;
|
||||||
//DBG("start enc");
|
|
||||||
if(spiidx < 1 || spiidx > AMOUNT_OF_SPI) return FALSE;
|
if(spiidx < 1 || spiidx > AMOUNT_OF_SPI) return FALSE;
|
||||||
if(spi_status[spiidx] != SPI_READY) return FALSE;
|
if(spi_status[spiidx] != SPI_READY) return FALSE;
|
||||||
if(!spi_waitbsy(spiidx)) 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];
|
volatile DMA_Channel_TypeDef *DMA = DMAs[spiidx];
|
||||||
DMA->CMAR = (uint32_t) encoderbuf[encodernum];
|
DMA->CMAR = (uint32_t) encoderbuf[encodernum];
|
||||||
DMA->CNDTR = ENCODER_BUFSZ;
|
DMA->CNDTR = the_conf.encbufsz;
|
||||||
//DBG("turn on spi");
|
|
||||||
spi_onoff(spiidx, 1);
|
spi_onoff(spiidx, 1);
|
||||||
DMA->CCR |= DMA_CCR_EN;
|
DMA->CCR |= DMA_CCR_EN;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
@ -159,12 +148,8 @@ void dma1_channel2_isr(){
|
|||||||
DMA1->IFCR = DMA_IFCR_CTEIF2;
|
DMA1->IFCR = DMA_IFCR_CTEIF2;
|
||||||
}
|
}
|
||||||
if(DMA1->ISR & DMA_ISR_TCIF2){
|
if(DMA1->ISR & DMA_ISR_TCIF2){
|
||||||
//uint32_t ctr = TIM2->CNT;
|
|
||||||
DMA1->IFCR = DMA_IFCR_CTCIF2;
|
DMA1->IFCR = DMA_IFCR_CTCIF2;
|
||||||
freshdata[0] = 1;
|
freshdata[0] = 1;
|
||||||
//encoderbuf[5] = (ctr >> 16) & 0xff;
|
|
||||||
//encoderbuf[6] = (ctr >> 8 ) & 0xff;
|
|
||||||
//encoderbuf[7] = (ctr >> 0 ) & 0xff;
|
|
||||||
}
|
}
|
||||||
spi_status[1] = SPI_READY;
|
spi_status[1] = SPI_READY;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,9 +2,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// two SPI interfaces for two sensors
|
||||||
#define AMOUNT_OF_SPI (2)
|
#define AMOUNT_OF_SPI (2)
|
||||||
|
// static buffer size
|
||||||
#define ENCODER_BUFSZ (12)
|
#define ENCODER_BUFSZ_MAX (32)
|
||||||
|
// encoder resolution
|
||||||
|
#define ENCRESOL_MIN (8)
|
||||||
|
#define ENCRESOL_MAX (32)
|
||||||
|
|
||||||
typedef enum{
|
typedef enum{
|
||||||
SPI_NOTREADY,
|
SPI_NOTREADY,
|
||||||
@ -18,4 +22,4 @@ void spi_onoff(uint8_t idx, uint8_t on);
|
|||||||
void spi_deinit(uint8_t idx);
|
void spi_deinit(uint8_t idx);
|
||||||
void spi_setup(uint8_t idx);
|
void spi_setup(uint8_t idx);
|
||||||
int spi_start_enc(int encodernum);
|
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);
|
||||||
|
|||||||
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,2 +1,2 @@
|
|||||||
#define BUILD_NUMBER "91"
|
#define BUILD_NUMBER "96"
|
||||||
#define BUILD_DATE "2025-04-02"
|
#define BUILD_DATE "2025-04-04"
|
||||||
|
|||||||
@ -78,13 +78,19 @@ static void chkin(uint8_t ifno){
|
|||||||
static void send_next(uint8_t ifno){
|
static void send_next(uint8_t ifno){
|
||||||
uint8_t usbbuff[USB_TXBUFSZ];
|
uint8_t usbbuff[USB_TXBUFSZ];
|
||||||
int buflen = RB_read((ringbuffer*)&rbout[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(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){
|
||||||
lastdsz[ifno] = 0;
|
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;
|
return;
|
||||||
}else if(buflen < 0){
|
}else if(buflen < 0){
|
||||||
DBG("Buff busy");
|
DBG("Buff busy");
|
||||||
lastdsz[ifno] = 0;
|
lastdsz[ifno] = -1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
DBG("Got data in buf");
|
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;
|
if(!buf || !CDCready[ifno] || !len) return FALSE;
|
||||||
DBG("USB_send");
|
DBG("USB_send");
|
||||||
while(len){
|
while(len){
|
||||||
|
if(!CDCready[ifno]) return FALSE;
|
||||||
IWDG->KR = IWDG_REFRESH;
|
IWDG->KR = IWDG_REFRESH;
|
||||||
int a = RB_write((ringbuffer*)&rbout[ifno], buf, len);
|
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){
|
if(a > 0){
|
||||||
len -= a;
|
len -= a;
|
||||||
buf += 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;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,10 +245,15 @@ int USB_putbyte(uint8_t ifno, uint8_t byte){
|
|||||||
if(!CDCready[ifno]) return FALSE;
|
if(!CDCready[ifno]) return FALSE;
|
||||||
int l = 0;
|
int l = 0;
|
||||||
while((l = RB_write((ringbuffer*)&rbout[ifno], &byte, 1)) != 1){
|
while((l = RB_write((ringbuffer*)&rbout[ifno], &byte, 1)) != 1){
|
||||||
|
if(!CDCready[ifno]) return FALSE;
|
||||||
IWDG->KR = IWDG_REFRESH;
|
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){ // overfull
|
||||||
if(l < 0) continue;
|
if(lastdsz[ifno] < 0) send_next(ifno);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// send line if got EOL
|
||||||
|
if(byte == '\n' && lastdsz[ifno] < 0) send_next(ifno);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -75,13 +75,19 @@ static void chkin(){
|
|||||||
static void send_next(){
|
static void send_next(){
|
||||||
uint8_t usbbuff[USB_TXBUFSZ];
|
uint8_t usbbuff[USB_TXBUFSZ];
|
||||||
int buflen = RB_read((ringbuffer*)&rbout, (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(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){
|
||||||
lastdsz = 0;
|
EP_Write(1, NULL, 0); // send ZLP after 64 bits packet when nothing more to send
|
||||||
|
lastdsz = 0;
|
||||||
|
}else lastdsz = -1;
|
||||||
return;
|
return;
|
||||||
}else if(buflen < 0){
|
}else if(buflen < 0){
|
||||||
DBG("Buff busy");
|
DBG("Buff busy");
|
||||||
lastdsz = 0;
|
lastdsz = -1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
DBG("Got data in buf");
|
DBG("Got data in buf");
|
||||||
@ -193,14 +199,17 @@ int USB_send(const uint8_t *buf, int len){
|
|||||||
if(!buf || !CDCready || !len) return FALSE;
|
if(!buf || !CDCready || !len) return FALSE;
|
||||||
DBG("USB_send");
|
DBG("USB_send");
|
||||||
while(len){
|
while(len){
|
||||||
|
if(!CDCready) return FALSE;
|
||||||
IWDG->KR = IWDG_REFRESH;
|
IWDG->KR = IWDG_REFRESH;
|
||||||
int a = RB_write((ringbuffer*)&rbout, buf, len);
|
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){
|
if(a > 0){
|
||||||
len -= a;
|
len -= a;
|
||||||
buf += 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;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,10 +217,14 @@ int USB_putbyte(uint8_t byte){
|
|||||||
if(!CDCready) return FALSE;
|
if(!CDCready) return FALSE;
|
||||||
int l = 0;
|
int l = 0;
|
||||||
while((l = RB_write((ringbuffer*)&rbout, &byte, 1)) != 1){
|
while((l = RB_write((ringbuffer*)&rbout, &byte, 1)) != 1){
|
||||||
|
if(!CDCready) return FALSE;
|
||||||
IWDG->KR = IWDG_REFRESH;
|
IWDG->KR = IWDG_REFRESH;
|
||||||
if(lastdsz == 0) send_next(); // need to run manually - all data sent, so no IRQ on IN
|
if(l == 0){ // overfull
|
||||||
if(l < 0) continue;
|
if(lastdsz < 0) send_next();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if(byte == '\n' && lastdsz < 0) send_next();
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -81,12 +81,18 @@ static void chkin(){
|
|||||||
static void send_next(){
|
static void send_next(){
|
||||||
uint8_t usbbuff[USB_TXBUFSZ];
|
uint8_t usbbuff[USB_TXBUFSZ];
|
||||||
int buflen = RB_read((ringbuffer*)&rbout, (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(buflen == 0){
|
||||||
if(lastdsz == 64) EP_Write(3, NULL, 0); // send ZLP after 64 bits packet when nothing more to send
|
if(lastdsz == 64){
|
||||||
lastdsz = 0;
|
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;
|
return;
|
||||||
}else if(buflen < 0){
|
}else if(buflen < 0){
|
||||||
lastdsz = 0;
|
lastdsz = -1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
EP_Write(3, (uint8_t*)usbbuff, buflen);
|
EP_Write(3, (uint8_t*)usbbuff, buflen);
|
||||||
@ -231,16 +237,19 @@ int USB_sendall(){
|
|||||||
// put `buf` into queue to send
|
// put `buf` into queue to send
|
||||||
int USB_send(const uint8_t *buf, int len){
|
int USB_send(const uint8_t *buf, int len){
|
||||||
if(!buf || !CDCready || !len) return FALSE;
|
if(!buf || !CDCready || !len) return FALSE;
|
||||||
DBG("send");
|
DBG("USB_send");
|
||||||
while(len){
|
while(len){
|
||||||
|
if(!CDCready) return FALSE;
|
||||||
IWDG->KR = IWDG_REFRESH;
|
IWDG->KR = IWDG_REFRESH;
|
||||||
int a = RB_write((ringbuffer*)&rbout, buf, len);
|
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){
|
if(a > 0){
|
||||||
len -= a;
|
len -= a;
|
||||||
buf += 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;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,10 +257,15 @@ int USB_putbyte(uint8_t byte){
|
|||||||
if(!CDCready) return FALSE;
|
if(!CDCready) return FALSE;
|
||||||
int l = 0;
|
int l = 0;
|
||||||
while((l = RB_write((ringbuffer*)&rbout, &byte, 1)) != 1){
|
while((l = RB_write((ringbuffer*)&rbout, &byte, 1)) != 1){
|
||||||
|
if(!CDCready) return FALSE;
|
||||||
IWDG->KR = IWDG_REFRESH;
|
IWDG->KR = IWDG_REFRESH;
|
||||||
if(lastdsz == 0) send_next(); // need to run manually - all data sent, so no IRQ on IN
|
if(l == 0){ // overfull
|
||||||
if(l < 0) continue;
|
if(lastdsz < 0) send_next();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// send line if got EOL
|
||||||
|
if(byte == '\n' && lastdsz < 0) send_next();
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user