mirror of
https://github.com/eddyem/stm32samples.git
synced 2025-12-06 10:45:11 +03:00
Working (but still dev-mode; need some more different configurators)
This commit is contained in:
parent
c449cb7108
commit
faeac98318
146
F1:F103/BISS_C_encoders/bissC.c
Normal file
146
F1:F103/BISS_C_encoders/bissC.c
Normal 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;
|
||||
}
|
||||
31
F1:F103/BISS_C_encoders/bissC.h
Normal file
31
F1:F103/BISS_C_encoders/bissC.h
Normal 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.
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!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>
|
||||
<data>
|
||||
<variable>EnvironmentId</variable>
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
bissC.c
|
||||
bissC.h
|
||||
flash.c
|
||||
flash.h
|
||||
hardware.c
|
||||
|
||||
@ -27,13 +27,14 @@ extern const uint32_t __varsstart, _BLOCKSIZE;
|
||||
static const uint32_t FLASH_blocksize = (uint32_t)&_BLOCKSIZE;
|
||||
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);
|
||||
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
|
||||
|
||||
|
||||
@ -27,6 +27,12 @@
|
||||
// maximal size (in letters) of iInterface for settings
|
||||
#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
|
||||
*/
|
||||
@ -34,6 +40,8 @@ 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
|
||||
} user_conf;
|
||||
|
||||
extern user_conf the_conf;
|
||||
|
||||
@ -16,25 +16,97 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "bissC.h"
|
||||
#include "flash.h"
|
||||
#include "hardware.h"
|
||||
#include "proto.h"
|
||||
#include "spi.h"
|
||||
#include "strfunc.h"
|
||||
#include "usart.h"
|
||||
//#include "usart.h"
|
||||
#include "usb_dev.h"
|
||||
|
||||
volatile uint32_t Tms = 0;
|
||||
static char inbuff[RBINSZ];
|
||||
|
||||
/* Called when systick fires */
|
||||
void sys_tick_handler(void){
|
||||
++Tms;
|
||||
}
|
||||
|
||||
int main(){
|
||||
char inbuff[RBINSZ];
|
||||
uint32_t lastT = 0, lastS = 0;
|
||||
// usefull data bytes: start/0+26bit+8bit=36
|
||||
#define USEFULL_DATA_BYTES (5)
|
||||
|
||||
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];
|
||||
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();
|
||||
flashstorage_init();
|
||||
hw_setup();
|
||||
@ -68,50 +140,18 @@ int main(){
|
||||
#endif
|
||||
*/
|
||||
if(CDCready[I_CMD]){
|
||||
if(Tms - lastS > 9999){
|
||||
/*if(Tms - lastS > 9999){
|
||||
USB_sendstr(I_CMD, "Tms=");
|
||||
USB_sendstr(I_CMD, u2str(Tms));
|
||||
newline(I_CMD);
|
||||
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);
|
||||
}
|
||||
int showval = spi_read_enc(0, encbuf);
|
||||
if(CDCready[I_CMD] && showval){
|
||||
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);
|
||||
}
|
||||
proc_enc(0);
|
||||
proc_enc(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -25,6 +25,9 @@
|
||||
#include "usb_dev.h"
|
||||
#include "version.inc"
|
||||
|
||||
// user runtime parameters
|
||||
parameters_t user_pars = {0};
|
||||
|
||||
// error codes
|
||||
typedef enum {
|
||||
ERR_OK, // no errors
|
||||
@ -63,6 +66,12 @@ typedef enum{
|
||||
C_spistat,
|
||||
C_spiinit,
|
||||
C_spideinit,
|
||||
C_cpol,
|
||||
C_cpha,
|
||||
C_br,
|
||||
C_encbits,
|
||||
C_testX,
|
||||
C_testY,
|
||||
C_AMOUNT
|
||||
} cmd_e;
|
||||
|
||||
@ -81,7 +90,6 @@ static const funcdescr_t commands[C_AMOUNT];
|
||||
CMDWR(i2str(ival)); CMDn(); return ERR_SILENCE; }while(0)
|
||||
|
||||
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 int32_t val = -111;
|
||||
@ -215,6 +223,110 @@ static errcode_e spideinit(_U_ cmd_e idx, _U_ char *par){
|
||||
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
|
||||
static const funcdescr_t commands[C_AMOUNT] = {
|
||||
[C_dummy] = {"dummy", dummy},
|
||||
@ -235,17 +347,14 @@ 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_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{
|
||||
int idx; // command index (if < 0 - just display `help` as grouping header)
|
||||
const char *help; // help message
|
||||
@ -271,9 +380,15 @@ static const help_t helpmessages[] = {
|
||||
{C_spiinit, "init SPI"},
|
||||
{C_spistat, "get status of both SPI interfaces"},
|
||||
{-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_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},
|
||||
};
|
||||
|
||||
|
||||
@ -18,4 +18,13 @@
|
||||
|
||||
#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);
|
||||
|
||||
@ -25,12 +25,14 @@
|
||||
#include <stm32f3.h>
|
||||
#endif
|
||||
|
||||
#include <stdatomic.h>
|
||||
|
||||
typedef struct{
|
||||
uint8_t *data; // data buffer
|
||||
const int length; // its length
|
||||
int head; // head 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;
|
||||
|
||||
int RB_read(ringbuffer *b, uint8_t *s, int len);
|
||||
|
||||
@ -17,9 +17,10 @@
|
||||
*/
|
||||
|
||||
#include <stm32f1.h>
|
||||
|
||||
#include "spi.h"
|
||||
#include <string.h> // memcpy
|
||||
|
||||
#include "flash.h"
|
||||
#include "spi.h"
|
||||
#include "usb_dev.h"
|
||||
#ifdef EBUG
|
||||
#include "strfunc.h"
|
||||
@ -31,7 +32,7 @@
|
||||
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 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 freshdata[AMOUNT_OF_SPI] = {0};
|
||||
@ -46,13 +47,14 @@ void spi_setup(uint8_t idx){
|
||||
SPI->CR2 = 0;
|
||||
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
|
||||
volatile DMA_Channel_TypeDef *DMA = DMAs[idx];
|
||||
uint32_t clkflags = 0;
|
||||
if(idx == 1){ // PA5/PA6; 72MHz
|
||||
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
|
||||
RCC->APB2RSTR = RCC_APB2RSTR_SPI1RST;
|
||||
RCC->APB2RSTR = 0; // clear reset
|
||||
GPIOA->CRL = (GPIOA->CRL & ~(GPIO_CRL_CNF5 | GPIO_CRL_CNF6))
|
||||
| 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
|
||||
}else if(idx == 2){ // PB13/PB14; 36MHz
|
||||
RCC->APB1ENR |= RCC_APB1ENR_SPI2EN;
|
||||
@ -60,11 +62,14 @@ void spi_setup(uint8_t idx){
|
||||
RCC->APB1RSTR = 0;
|
||||
GPIOB->CRH = (GPIOB->CRH & ~(GPIO_CRH_CNF13 | GPIO_CRH_CNF14))
|
||||
| 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);
|
||||
}else return; // err
|
||||
// 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;
|
||||
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
|
||||
@ -76,8 +81,8 @@ 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");
|
||||
//DBGs(u2str(idx));
|
||||
//DBG("turn on SPI");
|
||||
SPI->CR1 |= SPI_CR1_SPE;
|
||||
spi_status[idx] = SPI_BUSY;
|
||||
}else{
|
||||
@ -117,9 +122,9 @@ static int spi_waitbsy(uint8_t idx){
|
||||
|
||||
// just copy last read encoder value into `buf`
|
||||
// @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;
|
||||
DBGs(u2str(encno)); DBG("Read encoder data");
|
||||
//DBGs(u2str(encno)); DBG("Read encoder data");
|
||||
memcpy(buf, encoderbuf[encno], ENCODER_BUFSZ);
|
||||
freshdata[encno] = 0; // clear fresh status
|
||||
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!
|
||||
int spi_start_enc(int encodernum){
|
||||
int spiidx = encodernum + 1;
|
||||
DBG("start enc");
|
||||
//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!");
|
||||
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");
|
||||
//DBG("turn on spi");
|
||||
spi_onoff(spiidx, 1);
|
||||
DMA->CCR |= DMA_CCR_EN;
|
||||
return TRUE;
|
||||
@ -149,6 +154,7 @@ int spi_start_enc(int encodernum){
|
||||
void dma1_channel2_isr(){
|
||||
// turn off DMA
|
||||
DMA1_Channel2->CCR &= ~DMA_CCR_EN;
|
||||
SPI1->CR1 &= ~SPI_CR1_SPE;
|
||||
if(DMA1->ISR & DMA_ISR_TEIF2){
|
||||
DMA1->IFCR = DMA_IFCR_CTEIF2;
|
||||
}
|
||||
@ -160,12 +166,13 @@ void dma1_channel2_isr(){
|
||||
//encoderbuf[6] = (ctr >> 8 ) & 0xff;
|
||||
//encoderbuf[7] = (ctr >> 0 ) & 0xff;
|
||||
}
|
||||
spi_onoff(1, 0);
|
||||
spi_status[1] = SPI_READY;
|
||||
}
|
||||
|
||||
void dma1_channel4_isr(){
|
||||
// turn off DMA
|
||||
DMA1_Channel4->CCR &= ~DMA_CCR_EN;
|
||||
SPI2->CR1 &= ~SPI_CR1_SPE;
|
||||
if(DMA1->ISR & DMA_ISR_TEIF4){
|
||||
DMA1->IFCR = DMA_IFCR_CTEIF4;
|
||||
}
|
||||
@ -173,6 +180,6 @@ void dma1_channel4_isr(){
|
||||
DMA1->IFCR = DMA_IFCR_CTCIF4;
|
||||
freshdata[1] = 1;
|
||||
}
|
||||
spi_onoff(2, 0);
|
||||
spi_status[2] = SPI_READY;
|
||||
}
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
#define AMOUNT_OF_SPI (2)
|
||||
|
||||
#define ENCODER_BUFSZ (8)
|
||||
#define ENCODER_BUFSZ (12)
|
||||
|
||||
typedef enum{
|
||||
SPI_NOTREADY,
|
||||
@ -18,4 +18,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[8]);
|
||||
int spi_read_enc(uint8_t encno, uint8_t buf[ENCODER_BUFSZ]);
|
||||
|
||||
@ -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
|
||||
USB->EPnR[0] = (epstatus & ~(USB_EPnR_CTR_RX|USB_EPnR_CTR_TX|USB_EPnR_STAT_RX))
|
||||
^ USB_EPnR_STAT_TX;
|
||||
uint32_t ctr = 1000000;
|
||||
uint32_t ctr = 10000;
|
||||
while(--ctr && (USB->ISTR & USB_ISTR_CTR) == 0){IWDG->KR = IWDG_REFRESH;};
|
||||
if((USB->ISTR & USB_ISTR_CTR) == 0){
|
||||
return;
|
||||
|
||||
@ -45,9 +45,9 @@
|
||||
#define USB_EP0BUFSZ 64
|
||||
// virtual
|
||||
#define USB_EP1BUFSZ 10
|
||||
// Rx/Tx EPs: (512-64-128)/14 rouded to 8
|
||||
#define USB_RXBUFSZ 16
|
||||
#define USB_TXBUFSZ 16
|
||||
// Rx/Tx EPs: (512-64-128)/6 rouded to 8
|
||||
#define USB_RXBUFSZ 40
|
||||
#define USB_TXBUFSZ 64
|
||||
|
||||
// string descriptors
|
||||
enum{
|
||||
|
||||
@ -60,8 +60,8 @@ static uint8_t obuf[bTotNumEndpoints][RBOUTSZ], ibuf[bTotNumEndpoints][RBINSZ];
|
||||
static volatile ringbuffer rbout[bTotNumEndpoints] = {OBUF(0), OBUF(1), OBUF(2)};
|
||||
#define IBUF(N) {.data = ibuf[N], .length = RBINSZ, .head = 0, .tail = 0}
|
||||
static volatile ringbuffer rbin[bTotNumEndpoints] = {IBUF(0), IBUF(1), IBUF(2)};
|
||||
// last send data size
|
||||
static volatile int lastdsz[bTotNumEndpoints] = {0};
|
||||
// last send data size (<0 if USB transfer ready)
|
||||
static volatile int lastdsz[bTotNumEndpoints] = {-1, -1, -1};
|
||||
|
||||
static void chkin(uint8_t ifno){
|
||||
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){
|
||||
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");
|
||||
@ -140,11 +146,13 @@ void WEAK clstate_handler(uint8_t ifno, uint16_t val){
|
||||
DBGs(uhex2str(ifno));
|
||||
DBGs(uhex2str(val));
|
||||
CDCready[ifno] = val; // CONTROL_DTR | CONTROL_RTS -> interface connected; 0 -> disconnected
|
||||
lastdsz[ifno] = -1;
|
||||
}
|
||||
|
||||
// SEND_BREAK
|
||||
void WEAK break_handler(uint8_t ifno){
|
||||
CDCready[ifno] = 0;
|
||||
lastdsz[ifno] = -1;
|
||||
DBG("break_handler()");
|
||||
DBGs(uhex2str(ifno));
|
||||
}
|
||||
@ -154,6 +162,7 @@ void WEAK break_handler(uint8_t ifno){
|
||||
void set_configuration(){
|
||||
DBG("set_configuration()");
|
||||
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);
|
||||
if(r){
|
||||
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){
|
||||
while(lastdsz[ifno] > 0){
|
||||
if(!CDCready[ifno]) return FALSE;
|
||||
IWDG->KR = IWDG_REFRESH;
|
||||
}
|
||||
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;
|
||||
DBG("USB_send");
|
||||
while(len){
|
||||
if(!CDCready[ifno]) return FALSE;
|
||||
IWDG->KR = IWDG_REFRESH;
|
||||
int a = RB_write((ringbuffer*)&rbout[ifno], buf, len);
|
||||
if(a > 0){
|
||||
len -= a;
|
||||
buf += a;
|
||||
} else if (a < 0) continue; // do nothing if buffer is in reading state
|
||||
if(lastdsz[ifno] == 0) send_next(ifno); // need to run manually - all data sent, so no IRQ on IN
|
||||
}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;
|
||||
}
|
||||
|
||||
@ -238,9 +252,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(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;
|
||||
}
|
||||
|
||||
|
||||
@ -48,8 +48,8 @@ void linecoding_handler(uint8_t ifno, usb_LineCoding *lc);
|
||||
|
||||
|
||||
// sizes of ringbuffers for outgoing and incoming data
|
||||
#define RBOUTSZ (256)
|
||||
#define RBINSZ (256)
|
||||
#define RBOUTSZ (512)
|
||||
#define RBINSZ (128)
|
||||
|
||||
#define newline(ifno) USB_putbyte(ifno, '\n')
|
||||
#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(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 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
|
||||
#define DBG(s)
|
||||
#define DBGs(s)
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
#define BUILD_NUMBER "73"
|
||||
#define BUILD_DATE "2025-03-27"
|
||||
#define BUILD_NUMBER "91"
|
||||
#define BUILD_DATE "2025-04-02"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user