Working (but still dev-mode; need some more different configurators)

This commit is contained in:
Edward Emelianov 2025-04-02 18:26:02 +03:00
parent c449cb7108
commit faeac98318
18 changed files with 476 additions and 95 deletions

View File

@ -0,0 +1,146 @@
/*
* This file is part of the encoders project.
* Copyright 2025 Edward V. Emelianov <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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include "bissC.h"
#include "usb_dev.h"
#ifdef EBUG
#include "strfunc.h"
#endif
#include "flash.h"
#define MAX_BITSTREAM_UINTS 4 // 4 * 32 = 128 bits capacity
// min/max zeros before preambule
#define MINZEROS 4
#define MAXZEROS 40
static uint32_t bitstream[MAX_BITSTREAM_UINTS] = {0};
// Get bit at position n from uint32_t array (MSB-first packing)
static inline uint8_t get_bit(uint8_t n) {
return (bitstream[n >> 5] >> (31 - (n & 0x1F))) & 1;
}
// Convert bytes to packed uint32_t bitstream (MSB-first)
static void bytes_to_bitstream(const uint8_t *bytes, uint32_t num_bytes, uint32_t *num_bits) {
*num_bits = 0;
uint32_t current = 0;
uint32_t pos = 0;
for(uint32_t i = 0; i < num_bytes; i++){
for(int8_t j = 7; j >= 0; j--){ // MSB first
current = (current << 1) | ((bytes[i] >> j) & 1);
if (++(*num_bits) % 32 == 0){
bitstream[pos++] = current;
current = 0;
}
}
}
/* 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
static uint8_t compute_crc(uint8_t start_bit, uint8_t num_bits) {
uint8_t crc = 0x00;
for(uint32_t i = 0; i < num_bits; i++) {
uint8_t bit = get_bit(start_bit + i);
uint8_t msb = (crc >> 5) ^ bit;
crc = ((crc << 1) & 0x3F);
if (msb) crc ^= 0x03;
}
DBG("Compute CRC:");
DBGs(uhex2str(crc));
return crc;
}
BiSS_Frame parse_biss_frame(const uint8_t *bytes, uint32_t num_bytes){
BiSS_Frame frame = {0};
uint32_t num_bits = 0;
register uint8_t enclen = the_conf.encbits;
bytes_to_bitstream(bytes, num_bytes, &num_bits);
// Preamble detection state machine
enum {SEARCH_START, SEARCH_ZERO, COUNT_ZEROS} state = SEARCH_START;
uint32_t zero_count = 0;
uint32_t data_start = 0;
int found = 0;
for(uint32_t i = 0; i < num_bits && !found; i++){
uint8_t curbit = get_bit(i);
switch(state){
case SEARCH_START:
if(curbit){
state = SEARCH_ZERO;
}
break;
case SEARCH_ZERO:
if(!curbit){
state = COUNT_ZEROS;
zero_count = 1;
}
break;
case COUNT_ZEROS:
if(!curbit){
zero_count++;
}else{
if(zero_count >= MINZEROS && zero_count <= MAXZEROS){
if((i + 1) < num_bits && !get_bit(i + 1)){
data_start = i + 2;
found = 1;
}
}
state = SEARCH_START;
}
break;
}
}
// Validate frame structure
if(!found || (data_start + enclen+8) > num_bits){
frame.frame_valid = 0;
return frame;
}
DBG("Data start:");
DBGs(u2str(data_start));
// Extract 26-bit data
for(int i = 0; i < enclen; i++){
frame.data <<= 1;
frame.data |= get_bit(data_start + i);
}
// Extract 2-bit flags (INVERTED!)
frame.error = !get_bit(data_start + enclen);
frame.warning = !get_bit(data_start + enclen + 1);
// Extract and validate CRC
uint8_t received_crc = 0;
for(uint32_t i = 0; i < 6; i++){
received_crc <<= 1;
// CRC transmitted MSB and inverted! Include position data and EW bits
if(!get_bit(data_start + enclen + 2 + i)) received_crc |= 1;
}
DBG("received CRC:");
DBGs(uhex2str(received_crc));
frame.crc_valid = (compute_crc(data_start, enclen + 2) == received_crc);
frame.frame_valid = 1;
return frame;
}

View File

@ -0,0 +1,31 @@
/*
* This file is part of the encoders project.
* Copyright 2025 Edward V. Emelianov <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 3 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
typedef struct {
uint32_t data; // 26/32/36-bit data
uint8_t error : 1; // error flag (0 - error: bad value or high temperature)
uint8_t warning : 1; // warning flag (0 - warning: need to be cleaned)
uint8_t crc_valid : 1; // CRC validation result
uint8_t frame_valid : 1; // Overall frame validity
} BiSS_Frame;
BiSS_Frame parse_biss_frame(const uint8_t *bytes, uint32_t num_bytes);

Binary file not shown.

View File

@ -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-03-26T17:57:31. --> <!-- Written by QtCreator 15.0.1, 2025-04-02T14:36:26. -->
<qtcreator> <qtcreator>
<data> <data>
<variable>EnvironmentId</variable> <variable>EnvironmentId</variable>

View File

@ -1,3 +1,5 @@
bissC.c
bissC.h
flash.c flash.c
flash.h flash.h
hardware.c hardware.c

View File

@ -27,13 +27,14 @@ extern const uint32_t __varsstart, _BLOCKSIZE;
static const uint32_t FLASH_blocksize = (uint32_t)&_BLOCKSIZE; static const uint32_t FLASH_blocksize = (uint32_t)&_BLOCKSIZE;
static uint32_t maxCnum = 1024 / sizeof(user_conf); // can't use blocksize here static uint32_t maxCnum = 1024 / sizeof(user_conf); // can't use blocksize here
#define USERCONF_INITIALIZER { \
.userconf_sz = sizeof(user_conf) \
}
static int write2flash(const void*, const void*, uint32_t); static int write2flash(const void*, const void*, uint32_t);
const user_conf *Flash_Data = (const user_conf *)(&__varsstart); const user_conf *Flash_Data = (const user_conf *)(&__varsstart);
user_conf the_conf = USERCONF_INITIALIZER; user_conf the_conf = {
.userconf_sz = sizeof(user_conf),
.flags.CPOL = 1,
.flags.BR = 4,
.encbits = 26
};
int currentconfidx = -1; // index of current configuration int currentconfidx = -1; // index of current configuration

View File

@ -27,6 +27,12 @@
// 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)
typedef struct{
uint8_t CPOL : 1;
uint8_t CPHA : 1;
uint8_t BR : 3;
} flags_t;
/* /*
* struct to save user configurations * struct to save user configurations
*/ */
@ -34,6 +40,8 @@ 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
} user_conf; } user_conf;
extern user_conf the_conf; extern user_conf the_conf;

View File

@ -16,25 +16,97 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "bissC.h"
#include "flash.h" #include "flash.h"
#include "hardware.h" #include "hardware.h"
#include "proto.h" #include "proto.h"
#include "spi.h" #include "spi.h"
#include "strfunc.h" #include "strfunc.h"
#include "usart.h" //#include "usart.h"
#include "usb_dev.h" #include "usb_dev.h"
volatile uint32_t Tms = 0; volatile uint32_t Tms = 0;
static char inbuff[RBINSZ];
/* Called when systick fires */ /* Called when systick fires */
void sys_tick_handler(void){ void sys_tick_handler(void){
++Tms; ++Tms;
} }
int main(){ // usefull data bytes: start/0+26bit+8bit=36
char inbuff[RBINSZ]; #define USEFULL_DATA_BYTES (5)
uint32_t lastT = 0, lastS = 0;
static void printResult(BiSS_Frame *result){
if(result->frame_valid){
CMDWR("Data: "); CMDWRn(uhex2str(result->data));
if(result->error) CMDWRn("ERROR flag set");
if(result->warning) CMDWRn("WARNING flag set");
CMDWR("CRC is "); if(!result->crc_valid) CMDWR("NOT ");
CMDWRn("valid");
}else CMDWRn("Invalid frame");
}
static void proc_enc(uint8_t idx){
uint8_t encbuf[ENCODER_BUFSZ]; 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';
if(CDCready[iface]){
int l = USB_receivestr(iface, inbuff, RBINSZ);
if(CDCready[I_CMD]){
if(l){
CMDWR("Enc"); USB_putbyte(I_CMD, ifacechr);
CMDWR(": ");
}
if(l < 0) CMDWRn("ERROR! USB buffer overflow or string was too long");
else if(l){
CMDWR("got string: '");
CMDWR(inbuff);
CMDWR("'\n");
}
}
}
if(!spi_read_enc(idx, encbuf)) return;
BiSS_Frame result = parse_biss_frame(encbuf, ENCODER_BUFSZ);
char *str = result.crc_valid ? u2str(result.data) : NULL;
uint8_t testflag = (idx) ? user_pars.testy : user_pars.testx;
if(CDCready[I_CMD]){
if(testflag){
if(Tms - lastMSG[idx] > 9999){
USB_putbyte(I_CMD, ifacechr);
CMDWR(" 10sec stat: good=");
CMDWR(u2str(gotgood[idx]));
CMDWR(", bad=");
CMDWR(u2str(gotwrong[idx]));
CMDn();
gotgood[idx] = 0;
gotwrong[idx] = 0;
lastMSG[idx] = Tms;
}else{
if(str) ++gotgood[idx];
else ++gotwrong[idx];
}
}else{
printResult(&result);
CMDWR("ENC"); USB_putbyte(I_CMD, ifacechr);
USB_putbyte(I_CMD, '=');
hexdump(I_CMD, encbuf, ENCODER_BUFSZ);
CMDWR(" (");
if(str) CMDWR(str);
else CMDWR("wrong");
CMDWR(")\n");
}
}
if(CDCready[iface] && str && !result.error){
USB_sendstr(iface, str);
USB_putbyte(iface, '\n');
}
if(result.error) spi_setup(1+idx); // reinit SPI in case of error
if(testflag) spi_start_enc(idx);
}
int main(){
uint32_t lastT = 0;//, lastS = 0;
StartHSE(); StartHSE();
flashstorage_init(); flashstorage_init();
hw_setup(); hw_setup();
@ -68,50 +140,18 @@ int main(){
#endif #endif
*/ */
if(CDCready[I_CMD]){ if(CDCready[I_CMD]){
if(Tms - lastS > 9999){ /*if(Tms - lastS > 9999){
USB_sendstr(I_CMD, "Tms="); USB_sendstr(I_CMD, "Tms=");
USB_sendstr(I_CMD, u2str(Tms)); USB_sendstr(I_CMD, u2str(Tms));
newline(I_CMD); CMDn();
lastS = Tms; 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);
} }
int showval = spi_read_enc(0, encbuf); proc_enc(0);
if(CDCready[I_CMD] && showval){ proc_enc(1);
CMDWR("ENCX=");
hexdump(I_CMD, encbuf, ENCODER_BUFSZ);
newline(I_CMD);
}
if(CDCready[I_X]){
int l = USB_receivestr(I_X, inbuff, RBINSZ);
if(l < 0) CMDWRn("ERROR: encX USB buffer overflow or string was too long");
else if(l){
CMDWR("EncX got: '");
CMDWR(inbuff);
CMDWR("'\n");
}
if(showval) // send encoder data
hexdump(I_X, encbuf, ENCODER_BUFSZ);
}
showval = spi_read_enc(1, encbuf);
if(CDCready[I_CMD] && showval){
CMDWR("ENCY=");
hexdump(I_CMD, encbuf, ENCODER_BUFSZ);
newline(I_CMD);
}
if(CDCready[I_Y]){
int l = USB_receivestr(I_Y, inbuff, RBINSZ);
if(l < 0) CMDWRn("ERROR: encY USB buffer overflow or string was too long");
else if(l){
CMDWR("EncY got: '");
CMDWR(inbuff);
CMDWR("'\n");
}
if(showval) // send encoder data
hexdump(I_Y, encbuf, ENCODER_BUFSZ);
}
} }
return 0; return 0;
} }

View File

@ -25,6 +25,9 @@
#include "usb_dev.h" #include "usb_dev.h"
#include "version.inc" #include "version.inc"
// user runtime parameters
parameters_t user_pars = {0};
// error codes // error codes
typedef enum { typedef enum {
ERR_OK, // no errors ERR_OK, // no errors
@ -63,6 +66,12 @@ typedef enum{
C_spistat, C_spistat,
C_spiinit, C_spiinit,
C_spideinit, C_spideinit,
C_cpol,
C_cpha,
C_br,
C_encbits,
C_testX,
C_testY,
C_AMOUNT C_AMOUNT
} cmd_e; } cmd_e;
@ -81,7 +90,6 @@ static const funcdescr_t commands[C_AMOUNT];
CMDWR(i2str(ival)); CMDn(); return ERR_SILENCE; }while(0) CMDWR(i2str(ival)); CMDn(); return ERR_SILENCE; }while(0)
static errcode_e help(cmd_e idx, char* par); static errcode_e help(cmd_e idx, char* par);
static errcode_e dumpconf(cmd_e idx, char *par);
static errcode_e dummy(cmd_e idx, char *par){ static errcode_e dummy(cmd_e idx, char *par){
static int32_t val = -111; static int32_t val = -111;
@ -215,6 +223,110 @@ 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){
if(par){
if(*par < '0' || *par > '9') return ERR_BADPAR;
uint8_t val = *par - '0';
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;
default:
return ERR_BADCMD;
}
}
uint8_t val = 0;
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();
return ERR_SILENCE;
}
static errcode_e setboolpar(cmd_e idx, char *par){
uint8_t val;
if(par){
if(*par != '0' && *par != '1') return ERR_BADPAR;
val = *par - '0';
switch(idx){
case C_testX:
user_pars.testx = val;
break;
case C_testY:
user_pars.testy = val;
break;
default:
return ERR_BADCMD;
}
}
switch(idx){
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;
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 dumpconf(cmd_e _U_ idx, char _U_ *par){
CMDWR("userconf_sz="); CMDWR(u2str(the_conf.userconf_sz));
CMDWR("\ncurrentconfidx="); CMDWR(i2str(currentconfidx));
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);
return ERR_SILENCE;
}
// text commands // text commands
static const funcdescr_t commands[C_AMOUNT] = { static const funcdescr_t commands[C_AMOUNT] = {
[C_dummy] = {"dummy", dummy}, [C_dummy] = {"dummy", dummy},
@ -235,17 +347,14 @@ 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_cpha] = {"CPHA", setflags},
[C_br] = {"BR", setflags},
[C_encbits] = {"encbits", encbits},
[C_testX] = {"testx", setboolpar},
[C_testY] = {"testy", setboolpar},
}; };
static errcode_e dumpconf(cmd_e _U_ idx, char _U_ *par){
CMDWR("userconf_sz="); CMDWR(u2str(the_conf.userconf_sz));
CMDWR("\ncurrentconfidx="); CMDWR(i2str(currentconfidx));
CMDn();
for(int i = 0; i < bTotNumEndpoints; ++i)
setiface(C_setiface1 + i, NULL);
return ERR_SILENCE;
}
typedef struct{ typedef struct{
int idx; // command index (if < 0 - just display `help` as grouping header) int idx; // command index (if < 0 - just display `help` as grouping header)
const char *help; // help message const char *help; // help message
@ -271,9 +380,15 @@ static const help_t helpmessages[] = {
{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, "Debug"},
{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_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"},
{C_fin, "reinit flash"}, {C_testX, "test X-axis throughput"},
{C_testY, "test Y-axis throughput"},
{-1, NULL}, {-1, NULL},
}; };

View File

@ -18,4 +18,13 @@
#pragma once #pragma once
#include <stdint.h>
typedef struct{
uint8_t testx : 1;
uint8_t testy : 1;
} parameters_t;
extern parameters_t user_pars;
void parse_cmd(char *cmd); void parse_cmd(char *cmd);

View File

@ -25,12 +25,14 @@
#include <stm32f3.h> #include <stm32f3.h>
#endif #endif
#include <stdatomic.h>
typedef struct{ typedef struct{
uint8_t *data; // data buffer uint8_t *data; // data buffer
const int length; // its length const int length; // its length
int head; // head index int head; // head index
int tail; // tail index int tail; // tail index
volatile int busy; // == TRUE if buffer is busy now volatile atomic_int busy; // == TRUE if buffer is busy now
} ringbuffer; } ringbuffer;
int RB_read(ringbuffer *b, uint8_t *s, int len); int RB_read(ringbuffer *b, uint8_t *s, int len);

View File

@ -17,9 +17,10 @@
*/ */
#include <stm32f1.h> #include <stm32f1.h>
#include "spi.h"
#include <string.h> // memcpy #include <string.h> // memcpy
#include "flash.h"
#include "spi.h"
#include "usb_dev.h" #include "usb_dev.h"
#ifdef EBUG #ifdef EBUG
#include "strfunc.h" #include "strfunc.h"
@ -31,7 +32,7 @@
spiStatus spi_status[AMOUNT_OF_SPI+1] = {0, SPI_NOTREADY, SPI_NOTREADY}; spiStatus spi_status[AMOUNT_OF_SPI+1] = {0, SPI_NOTREADY, SPI_NOTREADY};
static volatile SPI_TypeDef* const SPIs[AMOUNT_OF_SPI+1] = {NULL, SPI1, SPI2}; 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 < 360000)) IWDG->KR = IWDG_REFRESH; if(wctr==360000){ 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] = {0};
static uint8_t freshdata[AMOUNT_OF_SPI] = {0}; static uint8_t freshdata[AMOUNT_OF_SPI] = {0};
@ -46,13 +47,14 @@ void spi_setup(uint8_t idx){
SPI->CR2 = 0; SPI->CR2 = 0;
RCC->AHBENR |= RCC_AHBENR_DMA1EN; RCC->AHBENR |= RCC_AHBENR_DMA1EN;
volatile DMA_Channel_TypeDef *DMA = DMAs[idx]; volatile DMA_Channel_TypeDef *DMA = DMAs[idx];
uint32_t clkflags = 0;
if(idx == 1){ // PA5/PA6; 72MHz if(idx == 1){ // PA5/PA6; 72MHz
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN; RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
RCC->APB2RSTR = RCC_APB2RSTR_SPI1RST; RCC->APB2RSTR = RCC_APB2RSTR_SPI1RST;
RCC->APB2RSTR = 0; // clear reset RCC->APB2RSTR = 0; // clear reset
GPIOA->CRL = (GPIOA->CRL & ~(GPIO_CRL_CNF5 | GPIO_CRL_CNF6)) GPIOA->CRL = (GPIOA->CRL & ~(GPIO_CRL_CNF5 | GPIO_CRL_CNF6))
| CRL(5, CNF_AFPP|MODE_FAST) | CRL(6, CNF_FLINPUT); | CRL(5, CNF_AFPP|MODE_FAST) | CRL(6, CNF_FLINPUT);
SPI->CR1 = SPI_CR1_BR_1 | SPI_CR1_BR_2; // Fpclk/128 clkflags = ((uint32_t)the_conf.flags.BR) << 3;
NVIC_EnableIRQ(DMA1_Channel2_IRQn); // enable Rx interrupt NVIC_EnableIRQ(DMA1_Channel2_IRQn); // enable Rx interrupt
}else if(idx == 2){ // PB13/PB14; 36MHz }else if(idx == 2){ // PB13/PB14; 36MHz
RCC->APB1ENR |= RCC_APB1ENR_SPI2EN; RCC->APB1ENR |= RCC_APB1ENR_SPI2EN;
@ -60,11 +62,14 @@ void spi_setup(uint8_t idx){
RCC->APB1RSTR = 0; RCC->APB1RSTR = 0;
GPIOB->CRH = (GPIOB->CRH & ~(GPIO_CRH_CNF13 | GPIO_CRH_CNF14)) GPIOB->CRH = (GPIOB->CRH & ~(GPIO_CRH_CNF13 | GPIO_CRH_CNF14))
| CRH(13, CNF_AFPP|MODE_FAST) | CRH(14, CNF_FLINPUT); | CRH(13, CNF_AFPP|MODE_FAST) | CRH(14, CNF_FLINPUT);
SPI->CR1 = SPI_CR1_BR_0 | SPI_CR1_BR_2; // Fpclk/64 clkflags = ((uint32_t)the_conf.flags.BR - 1) << 3; // freq/2
NVIC_EnableIRQ(DMA1_Channel4_IRQn); NVIC_EnableIRQ(DMA1_Channel4_IRQn);
}else return; // err }else return; // err
// Baudrate = 0b110 - fpclk/128 // Baudrate = 0b110 - fpclk/128
SPI->CR1 |= SPI_CR1_MSTR | SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_RXONLY; // CPOL=1 (CLK is 1 on idle); CPHA=0 (capture in first front)
if(the_conf.flags.CPHA) clkflags |= SPI_CR1_CPHA;
if(the_conf.flags.CPOL) clkflags |= SPI_CR1_CPOL;
SPI->CR1 |= SPI_CR1_MSTR | SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_RXONLY | clkflags;
SPI->CR2 = SPI_CR2_RXDMAEN; SPI->CR2 = SPI_CR2_RXDMAEN;
DMA->CPAR = (uint32_t)&(SPI->DR); DMA->CPAR = (uint32_t)&(SPI->DR);
DMA->CCR = DMA_CCR_MINC | DMA_CCR_TCIE | DMA_CCR_TEIE; // mem inc, hw->mem, Rx complete and error interrupt DMA->CCR = DMA_CCR_MINC | DMA_CCR_TCIE | DMA_CCR_TEIE; // mem inc, hw->mem, Rx complete and error interrupt
@ -76,8 +81,8 @@ 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)); //DBGs(u2str(idx));
DBG("turn on SPI"); //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{
@ -117,9 +122,9 @@ static int spi_waitbsy(uint8_t idx){
// just copy last read encoder value into `buf` // just copy last read encoder value into `buf`
// @return TRUE if got fresh data // @return TRUE if got fresh data
int spi_read_enc(uint8_t encno, uint8_t buf[8]){ int spi_read_enc(uint8_t encno, uint8_t buf[ENCODER_BUFSZ]){
if(encno > 1 || !freshdata[encno]) return FALSE; if(encno > 1 || !freshdata[encno]) return FALSE;
DBGs(u2str(encno)); DBG("Read encoder data"); //DBGs(u2str(encno)); DBG("Read encoder data");
memcpy(buf, encoderbuf[encno], ENCODER_BUFSZ); memcpy(buf, encoderbuf[encno], ENCODER_BUFSZ);
freshdata[encno] = 0; // clear fresh status freshdata[encno] = 0; // clear fresh status
return TRUE; return TRUE;
@ -130,16 +135,16 @@ int spi_read_enc(uint8_t encno, uint8_t buf[8]){
// 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"); //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(SPI1->CR1 & SPI_CR1_SPE){ DBG("spi1 works!");}
if(SPI2->CR1 & SPI_CR1_SPE) DBG("spi2 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 = ENCODER_BUFSZ;
DBG("turn on spi"); //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;
@ -149,6 +154,7 @@ int spi_start_enc(int encodernum){
void dma1_channel2_isr(){ void dma1_channel2_isr(){
// turn off DMA // turn off DMA
DMA1_Channel2->CCR &= ~DMA_CCR_EN; DMA1_Channel2->CCR &= ~DMA_CCR_EN;
SPI1->CR1 &= ~SPI_CR1_SPE;
if(DMA1->ISR & DMA_ISR_TEIF2){ if(DMA1->ISR & DMA_ISR_TEIF2){
DMA1->IFCR = DMA_IFCR_CTEIF2; DMA1->IFCR = DMA_IFCR_CTEIF2;
} }
@ -160,12 +166,13 @@ void dma1_channel2_isr(){
//encoderbuf[6] = (ctr >> 8 ) & 0xff; //encoderbuf[6] = (ctr >> 8 ) & 0xff;
//encoderbuf[7] = (ctr >> 0 ) & 0xff; //encoderbuf[7] = (ctr >> 0 ) & 0xff;
} }
spi_onoff(1, 0); spi_status[1] = SPI_READY;
} }
void dma1_channel4_isr(){ void dma1_channel4_isr(){
// turn off DMA // turn off DMA
DMA1_Channel4->CCR &= ~DMA_CCR_EN; DMA1_Channel4->CCR &= ~DMA_CCR_EN;
SPI2->CR1 &= ~SPI_CR1_SPE;
if(DMA1->ISR & DMA_ISR_TEIF4){ if(DMA1->ISR & DMA_ISR_TEIF4){
DMA1->IFCR = DMA_IFCR_CTEIF4; DMA1->IFCR = DMA_IFCR_CTEIF4;
} }
@ -173,6 +180,6 @@ void dma1_channel4_isr(){
DMA1->IFCR = DMA_IFCR_CTCIF4; DMA1->IFCR = DMA_IFCR_CTCIF4;
freshdata[1] = 1; freshdata[1] = 1;
} }
spi_onoff(2, 0); spi_status[2] = SPI_READY;
} }

View File

@ -4,7 +4,7 @@
#define AMOUNT_OF_SPI (2) #define AMOUNT_OF_SPI (2)
#define ENCODER_BUFSZ (8) #define ENCODER_BUFSZ (12)
typedef enum{ typedef enum{
SPI_NOTREADY, SPI_NOTREADY,
@ -18,4 +18,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[8]); int spi_read_enc(uint8_t encno, uint8_t buf[ENCODER_BUFSZ]);

View File

@ -215,7 +215,7 @@ static void wr0(const uint8_t *buf, uint16_t size, uint16_t askedsize){
// keep DTOGs, clear CTR_RX,TX, set TX VALID, leave stat_Rx // keep DTOGs, clear CTR_RX,TX, set TX VALID, leave stat_Rx
USB->EPnR[0] = (epstatus & ~(USB_EPnR_CTR_RX|USB_EPnR_CTR_TX|USB_EPnR_STAT_RX)) USB->EPnR[0] = (epstatus & ~(USB_EPnR_CTR_RX|USB_EPnR_CTR_TX|USB_EPnR_STAT_RX))
^ USB_EPnR_STAT_TX; ^ USB_EPnR_STAT_TX;
uint32_t ctr = 1000000; uint32_t ctr = 10000;
while(--ctr && (USB->ISTR & USB_ISTR_CTR) == 0){IWDG->KR = IWDG_REFRESH;}; while(--ctr && (USB->ISTR & USB_ISTR_CTR) == 0){IWDG->KR = IWDG_REFRESH;};
if((USB->ISTR & USB_ISTR_CTR) == 0){ if((USB->ISTR & USB_ISTR_CTR) == 0){
return; return;

View File

@ -45,9 +45,9 @@
#define USB_EP0BUFSZ 64 #define USB_EP0BUFSZ 64
// virtual // virtual
#define USB_EP1BUFSZ 10 #define USB_EP1BUFSZ 10
// Rx/Tx EPs: (512-64-128)/14 rouded to 8 // Rx/Tx EPs: (512-64-128)/6 rouded to 8
#define USB_RXBUFSZ 16 #define USB_RXBUFSZ 40
#define USB_TXBUFSZ 16 #define USB_TXBUFSZ 64
// string descriptors // string descriptors
enum{ enum{

View File

@ -60,8 +60,8 @@ static uint8_t obuf[bTotNumEndpoints][RBOUTSZ], ibuf[bTotNumEndpoints][RBINSZ];
static volatile ringbuffer rbout[bTotNumEndpoints] = {OBUF(0), OBUF(1), OBUF(2)}; static volatile ringbuffer rbout[bTotNumEndpoints] = {OBUF(0), OBUF(1), OBUF(2)};
#define IBUF(N) {.data = ibuf[N], .length = RBINSZ, .head = 0, .tail = 0} #define IBUF(N) {.data = ibuf[N], .length = RBINSZ, .head = 0, .tail = 0}
static volatile ringbuffer rbin[bTotNumEndpoints] = {IBUF(0), IBUF(1), IBUF(2)}; static volatile ringbuffer rbin[bTotNumEndpoints] = {IBUF(0), IBUF(1), IBUF(2)};
// last send data size // last send data size (<0 if USB transfer ready)
static volatile int lastdsz[bTotNumEndpoints] = {0}; static volatile int lastdsz[bTotNumEndpoints] = {-1, -1, -1};
static void chkin(uint8_t ifno){ static void chkin(uint8_t ifno){
if(bufovrfl[ifno]) return; // allow user to know that previous buffer was overflowed and cleared if(bufovrfl[ifno]) return; // allow user to know that previous buffer was overflowed and cleared
@ -82,13 +82,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");
@ -140,11 +146,13 @@ void WEAK clstate_handler(uint8_t ifno, uint16_t val){
DBGs(uhex2str(ifno)); DBGs(uhex2str(ifno));
DBGs(uhex2str(val)); DBGs(uhex2str(val));
CDCready[ifno] = val; // CONTROL_DTR | CONTROL_RTS -> interface connected; 0 -> disconnected CDCready[ifno] = val; // CONTROL_DTR | CONTROL_RTS -> interface connected; 0 -> disconnected
lastdsz[ifno] = -1;
} }
// SEND_BREAK // SEND_BREAK
void WEAK break_handler(uint8_t ifno){ void WEAK break_handler(uint8_t ifno){
CDCready[ifno] = 0; CDCready[ifno] = 0;
lastdsz[ifno] = -1;
DBG("break_handler()"); DBG("break_handler()");
DBGs(uhex2str(ifno)); DBGs(uhex2str(ifno));
} }
@ -154,6 +162,7 @@ void WEAK break_handler(uint8_t ifno){
void set_configuration(){ void set_configuration(){
DBG("set_configuration()"); DBG("set_configuration()");
for(int i = 0; i < bTotNumEndpoints; ++i){ for(int i = 0; i < bTotNumEndpoints; ++i){
IWDG->KR = IWDG_REFRESH;
int r = EP_Init(1+i, EP_TYPE_BULK, USB_TXBUFSZ, USB_RXBUFSZ, rxtx_handler); int r = EP_Init(1+i, EP_TYPE_BULK, USB_TXBUFSZ, USB_RXBUFSZ, rxtx_handler);
if(r){ if(r){
DBG("Can't init EP"); DBG("Can't init EP");
@ -215,6 +224,7 @@ void usb_class_request(config_pack_t *req, uint8_t *data, uint16_t datalen){
int USB_sendall(uint8_t ifno){ int USB_sendall(uint8_t ifno){
while(lastdsz[ifno] > 0){ while(lastdsz[ifno] > 0){
if(!CDCready[ifno]) return FALSE; if(!CDCready[ifno]) return FALSE;
IWDG->KR = IWDG_REFRESH;
} }
return TRUE; return TRUE;
} }
@ -224,13 +234,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;
int a = RB_write((ringbuffer*)&rbout[ifno], buf, len); int a = RB_write((ringbuffer*)&rbout[ifno], buf, len);
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); // need to run manually - all data sent, so no IRQ on IN if(lastdsz[ifno] < 0) send_next(ifno);
}
} }
if(buf[len-1] == '\n' && lastdsz[ifno] < 0) send_next(ifno);
return TRUE; return TRUE;
} }
@ -238,9 +252,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(l < 0) continue; if(!CDCready[ifno]) return FALSE;
IWDG->KR = IWDG_REFRESH;
if(l == 0){ // overfull
if(lastdsz[ifno] < 0) send_next(ifno);
continue;
}
} }
if(lastdsz[ifno] == 0) send_next(ifno); // need to run manually - all data sent, so no IRQ on IN // send line if got EOL
if(byte == '\n' && lastdsz[ifno] < 0) send_next(ifno);
return TRUE; return TRUE;
} }

View File

@ -48,8 +48,8 @@ void linecoding_handler(uint8_t ifno, usb_LineCoding *lc);
// sizes of ringbuffers for outgoing and incoming data // sizes of ringbuffers for outgoing and incoming data
#define RBOUTSZ (256) #define RBOUTSZ (512)
#define RBINSZ (256) #define RBINSZ (128)
#define newline(ifno) USB_putbyte(ifno, '\n') #define newline(ifno) USB_putbyte(ifno, '\n')
#define USND(ifno, s) do{USB_sendstr(ifno, s); USB_putbyte(ifno, '\n');}while(0) #define USND(ifno, s) do{USB_sendstr(ifno, s); USB_putbyte(ifno, '\n');}while(0)
@ -61,7 +61,7 @@ void linecoding_handler(uint8_t ifno, usb_LineCoding *lc);
#define STR_HELPER(s) #s #define STR_HELPER(s) #s
#define STR(s) STR_HELPER(s) #define STR(s) STR_HELPER(s)
#define DBG(str) do{USB_sendstr(I_CMD, __FILE__ " (L" STR(__LINE__) "): " str); USB_putbyte(I_CMD, '\n');}while(0) #define DBG(str) do{USB_sendstr(I_CMD, __FILE__ " (L" STR(__LINE__) "): " str); USB_putbyte(I_CMD, '\n');}while(0)
#define DBGs(str) do{USB_sendstr(I_CMD, str); USB_sendstr(I_CMD, "<--\n");}while(0) #define DBGs(str) do{USB_sendstr(I_CMD, str); USB_sendstr(I_CMD, "\n");}while(0)
#else #else
#define DBG(s) #define DBG(s)
#define DBGs(s) #define DBGs(s)

View File

@ -1,2 +1,2 @@
#define BUILD_NUMBER "73" #define BUILD_NUMBER "91"
#define BUILD_DATE "2025-03-27" #define BUILD_DATE "2025-04-02"