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"?>
|
<?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>
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
bissC.c
|
||||||
|
bissC.h
|
||||||
flash.c
|
flash.c
|
||||||
flash.h
|
flash.h
|
||||||
hardware.c
|
hardware.c
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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]);
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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{
|
||||||
|
|||||||
@ -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){
|
||||||
|
EP_Write(1+ifno, NULL, 0); // send ZLP after 64 bits packet when nothing more to send
|
||||||
lastdsz[ifno] = 0;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -1,2 +1,2 @@
|
|||||||
#define BUILD_NUMBER "73"
|
#define BUILD_NUMBER "91"
|
||||||
#define BUILD_DATE "2025-03-27"
|
#define BUILD_DATE "2025-04-02"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user