From 80c894517eb27fffe5f7b7c1f7d8da9de9830b54 Mon Sep 17 00:00:00 2001 From: Edward Emelianov Date: Wed, 18 Feb 2026 22:54:20 +0300 Subject: [PATCH] add CAN, not tested yet --- F3:F303/InterfaceBoard/Debug.c | 4 +- F3:F303/InterfaceBoard/can.c | 387 ++++++++++++++++ F3:F303/InterfaceBoard/can.h | 63 +++ F3:F303/InterfaceBoard/canproto.c | 424 ++++++++++++++++++ F3:F303/InterfaceBoard/canproto.h | 39 ++ F3:F303/InterfaceBoard/flash.c | 1 + F3:F303/InterfaceBoard/flash.h | 1 + F3:F303/InterfaceBoard/main.c | 32 +- F3:F303/InterfaceBoard/multiiface.bin | Bin 18476 -> 28024 bytes F3:F303/InterfaceBoard/multiiface.config | 1 + .../InterfaceBoard/multiiface.creator.user | 2 +- F3:F303/InterfaceBoard/multiiface.files | 4 + F3:F303/InterfaceBoard/proto.c | 52 +++ F3:F303/InterfaceBoard/usb_dev.c | 19 + F3:F303/InterfaceBoard/usb_dev.h | 2 + F3:F303/InterfaceBoard/version.inc | 4 +- 16 files changed, 1028 insertions(+), 7 deletions(-) create mode 100644 F3:F303/InterfaceBoard/can.c create mode 100644 F3:F303/InterfaceBoard/can.h create mode 100644 F3:F303/InterfaceBoard/canproto.c create mode 100644 F3:F303/InterfaceBoard/canproto.h diff --git a/F3:F303/InterfaceBoard/Debug.c b/F3:F303/InterfaceBoard/Debug.c index f8ad6d4..a1b197f 100644 --- a/F3:F303/InterfaceBoard/Debug.c +++ b/F3:F303/InterfaceBoard/Debug.c @@ -41,7 +41,7 @@ void debug_message_text(const char *str){ void debug_message_char(char ch){ if(!Config_mode) return; - RB_write(&dbgrb, (const uint8_t*)&ch, 1); + RB_write(&dbgrb, (const uint8_t*)&ch, 1); } void debug_newline_only(){ @@ -67,5 +67,3 @@ void print_debug_messages(){ } #endif - - diff --git a/F3:F303/InterfaceBoard/can.c b/F3:F303/InterfaceBoard/can.c new file mode 100644 index 0000000..d4a284b --- /dev/null +++ b/F3:F303/InterfaceBoard/can.c @@ -0,0 +1,387 @@ +/* + * This file is part of the multiiface project. + * Copyright 2026 Edward V. Emelianov . + * + * 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 . + */ + +#include // memcpy + +// read private defines from "canproto.h" +#define CANPRIVATE__ + +#include "can.h" +#include "canproto.h" +#include "Debug.h" +#include "hardware.h" +#include "strfunc.h" + +// PB9 - Tx, PB8 - Rx !!! + +// CAN settings +// CAN bus oscillator frequency: 36MHz (APB1) +#define CAN_F_OSC (36000000UL) +// timing values TBS1 and TBS2 (in BTR [TBS1-1] and [TBS2-1]) +// use 3 and 2 to get 6MHz +#define CAN_TBS1 (3) +#define CAN_TBS2 (2) +// bitrate oscillator frequency +#define CAN_BIT_OSC (CAN_F_OSC / (1+CAN_TBS1+CAN_TBS2)) + + + +// circular buffer for received messages +static CAN_message messages[CAN_INMESSAGE_SIZE]; +static uint8_t first_free_idx = 0; // index of first empty cell +static int8_t first_nonfree_idx = -1; // index of first data cell +static uint16_t oldspeed = 100; // speed of last init +uint32_t floodT = FLOOD_PERIOD_MS; // flood period in ms +static uint8_t incrflood = 0; // ==1 for incremental flooding +static uint32_t incrmessagectr = 0; // counter for incremental flooding + +static uint32_t last_err_code = 0; +static CAN_status can_status = CAN_STOP; + +static void can_process_fifo(uint8_t fifo_num); + +static CAN_message loc_flood_msg; +static CAN_message *flood_msg = NULL; // == loc_flood_msg - to flood + +CAN_status CAN_get_status(){ + int st = can_status; + can_status = CAN_OK; + return st; +} + +// push next message into buffer; return 1 if buffer overfull +static int CAN_messagebuf_push(CAN_message *msg){ + DBG("PUSH"); + if(first_free_idx == first_nonfree_idx){ + DBG("INBUF OVERFULL"); + return 1; // no free space + } + if(first_nonfree_idx < 0) first_nonfree_idx = 0; // first message in empty buffer + memcpy(&messages[first_free_idx++], msg, sizeof(CAN_message)); + // need to roll? + if(first_free_idx == CAN_INMESSAGE_SIZE) first_free_idx = 0; + return 0; +} + +// pop message from buffer +CAN_message *CAN_messagebuf_pop(){ + if(first_nonfree_idx < 0) return NULL; + CAN_message *msg = &messages[first_nonfree_idx++]; + if(first_nonfree_idx == CAN_INMESSAGE_SIZE) first_nonfree_idx = 0; + if(first_nonfree_idx == first_free_idx){ // buffer is empty - refresh it + first_nonfree_idx = -1; + first_free_idx = 0; + } + DBG("POP"); + return msg; +} + +void CAN_reinit(uint16_t speed){ + DBG("CAN_reinit"); + CAN->TSR |= CAN_TSR_ABRQ0 | CAN_TSR_ABRQ1 | CAN_TSR_ABRQ2; + RCC->APB1RSTR |= RCC_APB1RSTR_CANRST; + RCC->APB1RSTR &= ~RCC_APB1RSTR_CANRST; + CAN_setup(speed); +} + +/* +Can filtering: FSCx=0 (CAN->FS1R) -> 16-bit identifiers +MASK: FBMx=0 (CAN->FM1R), two filters (n in FR1 and n+1 in FR2) + ID: CAN->sFilterRegister[x].FRn[0..15] + MASK: CAN->sFilterRegister[x].FRn[16..31] + FR bits: STID[10:0] RTR IDE EXID[17:15] +LIST: FBMx=1, four filters (n&n+1 in FR1, n+2&n+3 in FR2) + IDn: CAN->sFilterRegister[x].FRn[0..15] + IDn+1: CAN->sFilterRegister[x].FRn[16..31] +*/ + +/* +Can timing: main freq - APB (PLL=48MHz) +segment = 1sync + TBS1 + TBS2, sample point is between TBS1 and TBS2, +so if TBS1=4 and TBS2=3, sum=8, bit sampling freq is 48/8 = 6MHz +-> to get 100kbps we need prescaler=60 + 250kbps - 24 + 500kbps - 12 + 1MBps - 6 +*/ + +// speed - in kbps +// GPIO configured in hw_setup +void CAN_setup(uint32_t speed){ + DBG("CAN_setup"); + if(speed == 0) speed = oldspeed; + else if(speed < CAN_MIN_SPEED) speed = CAN_MIN_SPEED; + else if(speed > CAN_MAX_SPEED) speed = CAN_MAX_SPEED; + uint32_t tmout = 10000; + // CAN clocking and pins enabled in hardware.c + /* Configure CAN */ + /* (1) Enter CAN init mode to write the configuration */ + /* (2) Wait the init mode entering */ + /* (3) Exit sleep mode */ + /* (4) Normal mode, set timing: TBS1 = 4, TBS2 = 3, prescaler = 60 */ + /* (5) Leave init mode */ + /* (6) Wait the init mode leaving */ + /* (7) Enter filter init mode, (16-bit + mask, bank 0 for FIFO 0) */ + /* (8) Acivate filter 0 for two IDs */ + /* (9) Identifier list mode */ + /* (10) Set the Id list */ + /* (12) Leave filter init */ + /* (13) Set error interrupts enable (& bus off) */ + CAN->MCR |= CAN_MCR_INRQ; /* (1) */ + while((CAN->MSR & CAN_MSR_INAK) != CAN_MSR_INAK) /* (2) */ + if(--tmout == 0) break; + if(tmout==0){ DBG("timeout!\n");} + CAN->MCR &=~ CAN_MCR_SLEEP; /* (3) */ + CAN->MCR |= CAN_MCR_ABOM; /* allow automatically bus-off */ + CAN->BTR = (CAN_TBS2-1) << 20 | (CAN_TBS1-1) << 16 | (CAN_BIT_OSC/speed - 1); /* (4) */ + oldspeed = CAN_BIT_OSC/(uint32_t)((CAN->BTR & CAN_BTR_BRP) + 1); + CAN->MCR &= ~CAN_MCR_INRQ; /* (5) */ + tmout = 10000; + while(CAN->MSR & CAN_MSR_INAK) /* (6) */ + if(--tmout == 0) break; + if(tmout==0){ DBG("timeout!\n");} + // accept ALL + CAN->FMR = CAN_FMR_FINIT; /* (7) */ + CAN->FA1R = CAN_FA1R_FACT0 | CAN_FA1R_FACT1; /* (8) */ + // set to 1 all needed bits of CAN->FFA1R to switch given filters to FIFO1 + CAN->sFilterRegister[0].FR1 = (1<<21)|(1<<5); // all odd IDs + CAN->FFA1R = 2; // filter 1 for FIFO1, filter 0 - for FIFO0 + CAN->sFilterRegister[1].FR1 = (1<<21); // all even IDs + CAN->FMR &= ~CAN_FMR_FINIT; /* (12) */ + CAN->IER |= CAN_IER_ERRIE | CAN_IER_FOVIE0 | CAN_IER_FOVIE1 | CAN_IER_BOFIE; /* (13) */ + + /* Configure IT */ + NVIC_SetPriority(USB_LP_CAN_RX0_IRQn, 0); // RX FIFO0 IRQ + NVIC_SetPriority(CAN_RX1_IRQn, 0); // RX FIFO1 IRQ + NVIC_SetPriority(CAN_SCE_IRQn, 0); // RX status changed IRQ + NVIC_EnableIRQ(USB_LP_CAN_RX0_IRQn); + NVIC_EnableIRQ(CAN_RX1_IRQn); + NVIC_EnableIRQ(CAN_SCE_IRQn); + CAN->MSR = 0; // clear SLAKI, WKUI, ERRI + can_status = CAN_READY; + DBGs("CAN configured"); +} + +void CAN_printerr(){ + if(!last_err_code) last_err_code = CAN->ESR; + if(!last_err_code){ + PRIstr("No errors\n"); + return; + } + PRIstr("Receive error counter: "); + PRIstr(u2str((last_err_code & CAN_ESR_REC)>>24)); + PRIstr("\nTransmit error counter: "); + PRIstr(u2str((last_err_code & CAN_ESR_TEC)>>16)); + PRIstr("\nLast error code: "); + int lec = (last_err_code & CAN_ESR_LEC) >> 4; + const char *errmsg = "No"; + switch(lec){ + case 1: errmsg = "Stuff"; break; + case 2: errmsg = "Form"; break; + case 3: errmsg = "Ack"; break; + case 4: errmsg = "Bit recessive"; break; + case 5: errmsg = "Bit dominant"; break; + case 6: errmsg = "CRC"; break; + case 7: errmsg = "(set by software)"; break; + } + PRIstr(errmsg); PRIstr(" error\n"); + if(last_err_code & CAN_ESR_BOFF) PRIstr("Bus off "); + if(last_err_code & CAN_ESR_EPVF) PRIstr("Passive error limit "); + if(last_err_code & CAN_ESR_EWGF) PRIstr("Error counter limit"); + last_err_code = 0; + PRIn(); +} + +void CAN_proc(){ + // check for messages in FIFO0 & FIFO1 + if(CAN->RF0R & CAN_RF0R_FMP0){ + can_process_fifo(0); + } + if(CAN->RF1R & CAN_RF1R_FMP1){ + can_process_fifo(1); + } + IWDG->KR = IWDG_REFRESH; + if(CAN->ESR & (CAN_ESR_BOFF | CAN_ESR_EPVF | CAN_ESR_EWGF)){ // much errors - restart CAN BUS + PRIstr("\nToo much errors, restarting CAN!\n"); + CAN_printerr(); + // request abort for all mailboxes + CAN->TSR |= CAN_TSR_ABRQ0 | CAN_TSR_ABRQ1 | CAN_TSR_ABRQ2; + // reset CAN bus + RCC->APB1RSTR |= RCC_APB1RSTR_CANRST; + RCC->APB1RSTR &= ~RCC_APB1RSTR_CANRST; + CAN_setup(0); + } + static uint32_t lastFloodTime = 0; + if(flood_msg && (Tms - lastFloodTime) >= floodT){ // flood every ~5ms + lastFloodTime = Tms; + CAN_send(flood_msg->data, flood_msg->length, flood_msg->ID); + }else if(incrflood && (Tms - lastFloodTime) >= floodT){ + lastFloodTime = Tms; + if(CAN_OK == CAN_send((uint8_t*)&incrmessagectr, 4, flood_msg->ID)) ++incrmessagectr; + } +} + +CAN_status CAN_send(uint8_t *msg, uint8_t len, uint16_t target_id){ + uint8_t mailbox = 0; + // check first free mailbox + if(CAN->TSR & (CAN_TSR_TME)){ + mailbox = (CAN->TSR & CAN_TSR_CODE) >> 24; + }else{ // no free mailboxes + DBG("No free mailboxes"); + return CAN_BUSY; + } +#ifdef EBUG + DBGs("Send data. Len="); DBGs(u2str(len)); + DBGs(", tagid="); DBGs(u2str(target_id)); + DBGs(", data="); + for(int i = 0; i < len; ++i){ + DBGs(" "); DBGs(uhex2str(msg[i])); + } + DBGn(); +#endif + CAN_TxMailBox_TypeDef *box = &CAN->sTxMailBox[mailbox]; + uint32_t lb = 0, hb = 0; + switch(len){ + case 8: + hb |= (uint32_t)msg[7] << 24; + // fallthrough + case 7: + hb |= (uint32_t)msg[6] << 16; + // fallthrough + case 6: + hb |= (uint32_t)msg[5] << 8; + // fallthrough + case 5: + hb |= (uint32_t)msg[4]; + // fallthrough + case 4: + lb |= (uint32_t)msg[3] << 24; + // fallthrough + case 3: + lb |= (uint32_t)msg[2] << 16; + // fallthrough + case 2: + lb |= (uint32_t)msg[1] << 8; + // fallthrough + default: + lb |= (uint32_t)msg[0]; + } + box->TDLR = lb; + box->TDHR = hb; + box->TDTR = len; + box->TIR = (target_id & 0x7FF) << 21 | CAN_TI0R_TXRQ; + return CAN_OK; +} + +// return: 1 - flood is ON, 0 - flood is OFF +int CAN_flood(CAN_message *msg, int incr){ + if(incr){ + incrmessagectr = 0; + incrflood = 1; + return 1; + }else incrflood = 0; + if(!msg){ + flood_msg = NULL; + return 0; + }else{ + memcpy(&loc_flood_msg, msg, sizeof(CAN_message)); + flood_msg = &loc_flood_msg; + } + return 1; +} + +uint32_t CAN_speed(){ + return oldspeed; +} + +static void can_process_fifo(uint8_t fifo_num){ + if(fifo_num > 1) return; + CAN_FIFOMailBox_TypeDef *box = &CAN->sFIFOMailBox[fifo_num]; + volatile uint32_t *RFxR = (fifo_num) ? &CAN->RF1R : &CAN->RF0R; + // read all + while(*RFxR & CAN_RF0R_FMP0){ // amount of messages pending + // CAN_RDTxR: (16-31) - timestamp, (8-15) - filter match index, (0-3) - data length + /* TODO: check filter match index if more than one ID can receive */ + CAN_message msg; + uint8_t *dat = msg.data; + uint8_t len = box->RDTR & 0x0f; + msg.length = len; + msg.ID = box->RIR >> 21; + //msg.filterNo = (box->RDTR >> 8) & 0xff; + //msg.fifoNum = fifo_num; + if(len){ // message can be without data + uint32_t hb = box->RDHR, lb = box->RDLR; + switch(len){ + case 8: + dat[7] = hb>>24; + // fallthrough + case 7: + dat[6] = (hb>>16) & 0xff; + // fallthrough + case 6: + dat[5] = (hb>>8) & 0xff; + // fallthrough + case 5: + dat[4] = hb & 0xff; + // fallthrough + case 4: + dat[3] = lb>>24; + // fallthrough + case 3: + dat[2] = (lb>>16) & 0xff; + // fallthrough + case 2: + dat[1] = (lb>>8) & 0xff; + // fallthrough + case 1: + dat[0] = lb & 0xff; + } + } + //if(msg.ID == the_conf.CANID) parseCANcommand(&msg); + if(CAN_messagebuf_push(&msg)) return; // error: buffer is full, try later + *RFxR |= CAN_RF0R_RFOM0; // release fifo for access to next message + } + //if(*RFxR & CAN_RF0R_FULL0) *RFxR &= ~CAN_RF0R_FULL0; + *RFxR = 0; // clear FOVR & FULL +} + +void usb_lp_can1_rx0_isr(){ // Rx FIFO0 (overrun) + if(CAN->RF0R & CAN_RF0R_FOVR0){ // FIFO overrun + CAN->RF0R = CAN_RF0R_FOVR0; + can_status = CAN_FIFO_OVERRUN; + } +} + +void can1_rx1_isr(){ // Rx FIFO1 (overrun) + if(CAN->RF1R & CAN_RF1R_FOVR1){ + CAN->RF1R = CAN_RF1R_FOVR1; + can_status = CAN_FIFO_OVERRUN; + } +} + +void can1_sce_isr(){ // status changed + if(CAN->MSR & CAN_MSR_ERRI){ // Error + last_err_code = CAN->ESR; + CAN->MSR = CAN_MSR_ERRI; // clear flag + // request abort for problem mailbox + if(CAN->TSR & CAN_TSR_TERR0) CAN->TSR |= CAN_TSR_ABRQ0; + if(CAN->TSR & CAN_TSR_TERR1) CAN->TSR |= CAN_TSR_ABRQ1; + if(CAN->TSR & CAN_TSR_TERR2) CAN->TSR |= CAN_TSR_ABRQ2; + can_status = CAN_ERR; + } +} diff --git a/F3:F303/InterfaceBoard/can.h b/F3:F303/InterfaceBoard/can.h new file mode 100644 index 0000000..230bfbd --- /dev/null +++ b/F3:F303/InterfaceBoard/can.h @@ -0,0 +1,63 @@ +/* + * This file is part of the multistepper project. + * Copyright 2023 Edward V. Emelianov . + * + * 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 . + */ + +#pragma once + +#include + +#define CAN_MAX_SPEED ((uint32_t)3000000UL) +#define CAN_MIN_SPEED ((uint32_t)9600UL) + +// amount of filter banks in STM32F +#define STM32FBANKNO 28 +// flood period in milliseconds +#define FLOOD_PERIOD_MS 5 + +// incoming message buffer size +#define CAN_INMESSAGE_SIZE (8) +extern uint32_t floodT; + +// CAN message +typedef struct{ + uint8_t data[8]; // up to 8 bytes of data + uint8_t length; // data length + uint16_t ID; // ID of receiver +} CAN_message; + +typedef enum{ + CAN_STOP, + CAN_READY, + CAN_BUSY, + CAN_OK, + CAN_ERR, + CAN_FIFO_OVERRUN +} CAN_status; + +CAN_status CAN_get_status(); + +void CAN_reinit(uint16_t speed); +void CAN_setup(uint32_t speed); + +CAN_status CAN_send(uint8_t *msg, uint8_t len, uint16_t target_id); +void CAN_proc(); +void CAN_printerr(); + +CAN_message *CAN_messagebuf_pop(); + +int CAN_flood(CAN_message *msg, int incr); +uint32_t CAN_speed(); diff --git a/F3:F303/InterfaceBoard/canproto.c b/F3:F303/InterfaceBoard/canproto.c new file mode 100644 index 0000000..01cd407 --- /dev/null +++ b/F3:F303/InterfaceBoard/canproto.c @@ -0,0 +1,424 @@ +/* + * This file is part of the canusb project. + * Copyright 2023 Edward V. Emelianov . + * + * 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 . + */ + +#include +#include + +// read private defines from "canproto.h" +#define CANPRIVATE__ + +#include "can.h" +#include "canproto.h" +#include "hardware.h" + +#define printu(x) PRIstr(u2str(x)) +#define printuhex(x) PRIstr(uhex2str(x)) + +// software ignore buffer size +#define IGN_SIZE 10 + +extern volatile uint32_t Tms; + +uint8_t CANShowMsgs = 1; +// software ignore buffers +static uint16_t Ignore_IDs[IGN_SIZE]; +static uint8_t IgnSz = 0; + +// parse `txt` to CAN_message +static CAN_message *parseCANmsg(const char *txt){ + static CAN_message canmsg; + uint32_t N; + int ctr = -1; + canmsg.ID = 0xffff; + do{ + const char *n = getnum(txt, &N); + if(txt == n) break; + txt = n; + if(ctr == -1){ + if(N > 0x7ff){ + PRIstrn("ID should be 11-bit number!"); + return NULL; + } + canmsg.ID = (uint16_t)(N&0x7ff); + ctr = 0; + continue; + } + if(ctr > 7){ + PRIstrn("ONLY 8 data bytes allowed!"); + return NULL; + } + if(N > 0xff){ + PRIstrn("Every data portion is a byte!"); + return NULL; + } + canmsg.data[ctr++] = (uint8_t)(N&0xff); + }while(1); + if(canmsg.ID == 0xffff){ + PRIstrn("NO ID given, send nothing!"); + return NULL; + } + PRIstrn("Message parsed OK"); + canmsg.length = (uint8_t) ctr; + return &canmsg; +} + +// USB_sendstr command, format: ID (hex/bin/dec) data bytes (up to 8 bytes, space-delimeted) +TRUE_INLINE void USB_sendstrCANcommand(char *txt){ + if(CAN->MSR & CAN_MSR_INAK){ + PRIstrn("CAN bus is off, try to restart it"); + return; + } + CAN_message *msg = parseCANmsg(txt); + if(!msg) return; + uint32_t N = 5; + while(CAN_BUSY == CAN_send(msg->data, msg->length, msg->ID)){ + if(--N == 0) break; + } +} + +TRUE_INLINE void CANini(const char *txt){ + uint32_t N; + const char *n = getnum(txt, &N); + if(txt == n){ + PRIstr("No speed given"); + return; + } + if(N < 50){ + PRIstr("Lowest speed is 50kbps"); + return; + }else if(N > 3000){ + PRIstr("Highest speed is 3000kbps"); + return; + } + CAN_reinit((uint16_t)N); + PRIstr("Reinit CAN bus with speed "); + printu(N); PRIstrn("kbps"); +} + +TRUE_INLINE void addIGN(const char *txt){ + if(IgnSz == IGN_SIZE){ + PRIstrn("Ignore buffer is full"); + return; + } + txt = omit_spaces(txt); + uint32_t N; + const char *n = getnum(txt, &N); + if(txt == n){ + PRIstrn("No ID given"); + return; + } + if(N > 0x7ff){ + PRIstrn("ID should be 11-bit number!"); + return; + } + Ignore_IDs[IgnSz++] = (uint16_t)(N & 0x7ff); + PRIstr("Added ID "); printu(N); + PRIstrn("\nIgn buffer size: "); PRIstrn(u2str(IgnSz)); +} + +TRUE_INLINE void print_ign_buf(){ + if(IgnSz == 0){ + PRIstrn("Ignore buffer is empty"); + return; + } + PRIstrn("Ignored IDs:"); + for(int i = 0; i < IgnSz; ++i){ + printu(i); + PRIstr(": "); + PRIstrn(u2str(Ignore_IDs[i])); + } +} + +// print ID/mask of CAN->sFilterRegister[x] half +static void printID(uint16_t FRn){ + if(FRn & 0x1f) return; // trash + printuhex(FRn >> 5); +} +/* +Can filtering: FSCx=0 (CAN->FS1R) -> 16-bit identifiers +CAN->FMR = (sb)<<8 | FINIT - init filter in starting bank sb +CAN->FFA1R FFAx = 1 -> FIFO1, 0 -> FIFO0 +CAN->FA1R FACTx=1 - filter active +MASK: FBMx=0 (CAN->FM1R), two filters (n in FR1 and n+1 in FR2) + ID: CAN->sFilterRegister[x].FRn[0..15] + MASK: CAN->sFilterRegister[x].FRn[16..31] + FR bits: STID[10:0] RTR IDE EXID[17:15] +LIST: FBMx=1, four filters (n&n+1 in FR1, n+2&n+3 in FR2) + IDn: CAN->sFilterRegister[x].FRn[0..15] + IDn+1: CAN->sFilterRegister[x].FRn[16..31] +*/ +TRUE_INLINE void list_filters(){ + uint32_t fa = CAN->FA1R, ctr = 0, mask = 1; + while(fa){ + if(fa & 1){ + PRIstr("Filter "); printu(ctr); PRIstr(", FIFO"); + if(CAN->FFA1R & mask) PRIstr("1"); + else PRIstr("0"); + PRIstr(" in "); + if(CAN->FM1R & mask){ // up to 4 filters in LIST mode + PRIstr("LIST mode, IDs: "); + printID(CAN->sFilterRegister[ctr].FR1 & 0xffff); + PRIstr(" "); + printID(CAN->sFilterRegister[ctr].FR1 >> 16); + PRIstr(" "); + printID(CAN->sFilterRegister[ctr].FR2 & 0xffff); + PRIstr(" "); + printID(CAN->sFilterRegister[ctr].FR2 >> 16); + }else{ // up to 2 filters in MASK mode + PRIstr("MASK mode: "); + if(!(CAN->sFilterRegister[ctr].FR1&0x1f)){ + PRIstr("ID="); printID(CAN->sFilterRegister[ctr].FR1 & 0xffff); + PRIstr(", MASK="); printID(CAN->sFilterRegister[ctr].FR1 >> 16); + PRIstr(" "); + } + if(!(CAN->sFilterRegister[ctr].FR2&0x1f)){ + PRIstr("ID="); printID(CAN->sFilterRegister[ctr].FR2 & 0xffff); + PRIstr(", MASK="); printID(CAN->sFilterRegister[ctr].FR2 >> 16); + } + } + PRIstr("\n"); + } + fa >>= 1; + ++ctr; + mask <<= 1; + } +} + +TRUE_INLINE void setfloodt(const char *s){ + uint32_t N; + s = omit_spaces(s); + const char *n = getnum(s, &N); + if(s == n){ + PRIstrn("t="); PRIstrn(u2str(floodT)); + return; + } + floodT = N; +} + +/** + * @brief add_filter - add/modify filter + * @param str - string in format "bank# FIFO# mode num0 .. num3" + * where bank# - 0..27 + * if there's nothing after bank# - delete filter + * FIFO# - 0,1 + * mode - 'I' for ID, 'M' for mask + * num0..num3 - IDs in ID mode, ID/MASK for mask mode + */ +static void add_filter(const char *str){ + uint32_t N; + str = omit_spaces(str); + const char *n = getnum(str, &N); + if(n == str){ + PRIstrn("No bank# given"); + return; + } + if(N > STM32FBANKNO-1){ + PRIstrn("bank# too big"); + return; + } + uint8_t bankno = (uint8_t)N; + str = omit_spaces(n); + if(!*str){ // deactivate filter + PRIstr("Deactivate filters in bank "); + printu(bankno); + PRIn(); + CAN->FMR = CAN_FMR_FINIT; + CAN->FA1R &= ~(1<FMR &=~ CAN_FMR_FINIT; + return; + } + uint8_t fifono = 0; + if(*str == '1') fifono = 1; + else if(*str != '0'){ + PRIstrn("FIFO# is 0 or 1"); + return; + } + str = omit_spaces(str + 1); + const char c = *str; + uint8_t mode = 0; // ID + if(c == 'M' || c == 'm') mode = 1; + else if(c != 'I' && c != 'i'){ + PRIstrn("mode is 'M/m' for MASK and 'I/i' for IDLIST"); + return; + } + str = omit_spaces(str + 1); + uint32_t filters[4]; + uint32_t nfilt; + for(nfilt = 0; nfilt < 4; ++nfilt){ + n = getnum(str, &N); + if(n == str) break; + filters[nfilt] = N; + str = omit_spaces(n); + } + if(nfilt == 0){ + PRIstrn("You should add at least one filter!"); + return; + } + if(mode && (nfilt&1)){ + PRIstrn("In MASK mode you should point pairs of ID/MASK"); + return; + } + CAN->FMR = CAN_FMR_FINIT; + uint32_t mask = 1<FA1R |= mask; // activate given filter + if(fifono) CAN->FFA1R |= mask; // set FIFO number + else CAN->FFA1R &= ~mask; + if(mode) CAN->FM1R &= ~mask; // MASK + else CAN->FM1R |= mask; // LIST + uint32_t F1 = (0x8f<<16); + uint32_t F2 = (0x8f<<16); + // reset filter registers to wrong value + CAN->sFilterRegister[bankno].FR1 = (0x8f<<16) | 0x8f; + CAN->sFilterRegister[bankno].FR2 = (0x8f<<16) | 0x8f; + switch(nfilt){ + case 4: + F2 = filters[3] << 21; + // fallthrough + case 3: + CAN->sFilterRegister[bankno].FR2 = (F2 & 0xffff0000) | (filters[2] << 5); + // fallthrough + case 2: + F1 = filters[1] << 21; + // fallthrough + case 1: + CAN->sFilterRegister[bankno].FR1 = (F1 & 0xffff0000) | (filters[0] << 5); + } + CAN->FMR &=~ CAN_FMR_FINIT; + PRIstr("Added filter with "); + printu(nfilt); PRIstrn(" parameters"); +} + +static const char *helpstring = + "'a' - add ID to ignore list (max 10 IDs)\n" + "'b' - reinit CAN with given baudrate\n" + "'c' - get CAN status\n" + "'d' - delete ignore list\n" + "'e' - get CAN errcodes\n" + "'f' - add/delete filter, format: bank# FIFO# mode(M/I) num0 [num1 [num2 [num3]]]\n" + "'F' - send/clear flood message: F ID byte0 ... byteN\n" + "'i' - send incremental flood message (ID == ID for `F`)\n" + "'I' - reinit CAN\n" + "'l' - list all active filters\n" + "'p' - print ignore buffer\n" + "'P' - pause/resume in packets displaying\n" + "'R' - software reset\n" + "'s/S' - send data over CAN: s ID byte0 .. byteN\n" + "'t' - change flood period (>=0ms)\n" + "'T' - get time from start (ms)\n" +; + + +TRUE_INLINE void getcanstat(){ + PRIstr("CAN_MSR="); + printuhex(CAN->MSR); + PRIstr("\nCAN_TSR="); + printuhex(CAN->TSR); + PRIstr("\nCAN_RF0R="); + printuhex(CAN->RF0R); + PRIstr("\nCAN_RF1R="); + PRIstrn(u2str(CAN->RF1R)); +} + +/** + * @brief CANcmd_parser - CAN command parsing + * @param txt - buffer with commands & data + * @param isUSB - == 1 if data got from USB + */ +void CANcmd_parser(char *txt){ + char _1st = txt[0]; + ++txt; + /* + * parse long commands here + */ + switch(_1st){ + case 'a': + addIGN(txt); + return; + break; + case 'b': + CANini(txt); + return; + break; + case 'f': + add_filter(txt); + return; + break; + case 'F': + CAN_flood(parseCANmsg(txt), 0); + return; + break; + case 's': + case 'S': + USB_sendstrCANcommand(txt); + return; + break; + case 't': + setfloodt(txt); + return; + break; + } + if(*txt) _1st = '?'; // help for wrong message length + switch(_1st){ + case 'c': + getcanstat(); + break; + case 'd': + IgnSz = 0; + break; + case 'e': + CAN_printerr(); + break; + case 'i': + CAN_flood(NULL, 1); + PRIstrn("Incremental flooding is ON ('F' to off)"); + break; + case 'I': + CAN_reinit(0); + break; + case 'l': + list_filters(); + break; + case 'p': + print_ign_buf(); + break; + case 'P': + CANShowMsgs = !CANShowMsgs; + if(CANShowMsgs) PRIstrn("Resume"); + else PRIstrn("Pause"); + break; + case 'R': + PRIstrn("Soft reset"); + USB_sendall(ICAN); // wait until everything will gone + NVIC_SystemReset(); + break; + case 'T': + PRIstr("Time (ms): "); + PRIstrn(u2str(Tms)); + break; + default: // help + PRIstrn(helpstring); + break; + } +} + +// check Ignore_IDs & return 1 if ID isn't in list +uint8_t CANsoftFilter(uint16_t ID){ + for(int i = 0; i < IgnSz; ++i) + if(Ignore_IDs[i] == ID) return 0; + return 1; +} diff --git a/F3:F303/InterfaceBoard/canproto.h b/F3:F303/InterfaceBoard/canproto.h new file mode 100644 index 0000000..a89b05d --- /dev/null +++ b/F3:F303/InterfaceBoard/canproto.h @@ -0,0 +1,39 @@ +/* + * This file is part of the canusb project. + * Copyright 2023 Edward V. Emelianov . + * + * 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 . + */ + +#pragma once + +#include + +#ifdef CANPRIVATE__ + +#include "strfunc.h" +#include "usb_descr.h" +#include "usb_dev.h" + +// USB messages to CAN interface +#define PRIstr(s) USB_sendstr(ICAN, s) +#define PRIstrn(s) do{USB_sendstr(ICAN, s); USB_putbyte(ICAN, '\n');}while(0) +#define PRIch(c) USB_putbyte(ICAN, c) +#define PRIn() USB_putbyte(ICAN, '\n') + +#endif // CANPRIVATE__ + +extern uint8_t CANShowMsgs; // show CAN messages flag +void CANcmd_parser(char *txt); +uint8_t CANsoftFilter(uint16_t ID); diff --git a/F3:F303/InterfaceBoard/flash.c b/F3:F303/InterfaceBoard/flash.c index 7f62333..c567d90 100644 --- a/F3:F303/InterfaceBoard/flash.c +++ b/F3:F303/InterfaceBoard/flash.c @@ -42,6 +42,7 @@ user_conf the_conf = { [ICAN] = u"usbCAN" }, .iIlengths = {22,22,22,22,22,12,12}, + .CANspeed = 100000 }; int currentconfidx = -1; // index of current configuration diff --git a/F3:F303/InterfaceBoard/flash.h b/F3:F303/InterfaceBoard/flash.h index e8ff614..a3ba09f 100644 --- a/F3:F303/InterfaceBoard/flash.h +++ b/F3:F303/InterfaceBoard/flash.h @@ -37,6 +37,7 @@ typedef struct __attribute__((packed, aligned(4))){ // we store iInterface "as is" uint16_t iInterface[InterfacesAmount][MAX_IINTERFACE_SZ]; // hryunikod! uint8_t iIlengths[InterfacesAmount]; + uint32_t CANspeed; } user_conf; extern user_conf the_conf; diff --git a/F3:F303/InterfaceBoard/main.c b/F3:F303/InterfaceBoard/main.c index 55da0c0..fc94d4b 100644 --- a/F3:F303/InterfaceBoard/main.c +++ b/F3:F303/InterfaceBoard/main.c @@ -16,10 +16,13 @@ * along with this program. If not, see . */ +#include "can.h" +#include "canproto.h" #include "Debug.h" #include "flash.h" #include "hardware.h" #include "proto.h" +#include "strfunc.h" #include "usart.h" #include "usb_dev.h" @@ -44,6 +47,7 @@ int main(void){ hw_setup(); USBPU_OFF(); USB_setup(); + CAN_setup(the_conf.CANspeed); //uint32_t ctr = Tms; //usb_LineCoding lc = {9600, 0, 0, 8}; //for(int i = 0; i < 5; ++i) usart_config(i, &lc); // configure all U[S]ARTs for default data @@ -51,6 +55,7 @@ int main(void){ while(1){ // Put here code working WITOUT USB connected if(!usbON) continue; + // and here is code what should run when USB connected usarts_process(); DBGpri(); /*for(int i = 0; i < 6; ++i){ // just echo for first time @@ -58,7 +63,32 @@ int main(void){ int l = USB_receive(i, (uint8_t*)inbuff, MAXSTRLEN); if(l) USB_send(i, (uint8_t*)inbuff, l); }*/ - // and here is code what should run when USB connected + if(CDCready[ICAN]){ + CAN_proc(); + if(CAN_get_status() == CAN_FIFO_OVERRUN){ + USB_sendstr(ICAN, "CAN bus fifo overrun occured!\n"); + } + CAN_message *can_mesg; + while((can_mesg = CAN_messagebuf_pop())){ + if(can_mesg && CANsoftFilter(can_mesg->ID)){ + if(CANShowMsgs){ // display message content + IWDG->KR = IWDG_REFRESH; + uint8_t len = can_mesg->length; + USB_sendstr(ICAN, u2str(Tms)); + USB_sendstr(ICAN, " #"); + USB_sendstr(ICAN, u2str(can_mesg->ID)); + for(uint8_t i = 0; i < len; ++i){ + USB_putbyte(ICAN, ' '); + USB_sendstr(ICAN, u2str(can_mesg->data[i])); + } + newline(ICAN); + } + } + } + int l = USB_receivestr(ICAN, inbuff, MAXSTRLEN); + if(l < 0) USB_sendstr(ICAN, "ERROR: USB buffer overflow or string was too long\n"); + else if(l) CANcmd_parser(inbuff); + } if(Config_mode && CDCready[ICFG]){ /*if(Tms - ctr > 4999){ ctr = Tms; diff --git a/F3:F303/InterfaceBoard/multiiface.bin b/F3:F303/InterfaceBoard/multiiface.bin index 9a2c9a7a935e8c707be912cc24d3a94a81650672..01e7c0af2ba016fc279a1857fb75c354bfc74bb1 100755 GIT binary patch delta 13894 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 ShEgbhyro!2`{7D->c0Uc29&G- delta 4407 zcmbVP4^SIb8h^Wi5GbXgp%{_?f%Fe#`zL^43jqS8TSzFhm1?i$ywE>qXt@?!&Py9H z*sHZ_>u|ogxt@5QwmRd@^=vKnMvE0ky|La^4c5_CtY*}+I!EhrcHG@8*}d;=06UcF zjF*{T-rMi*d*A!MZ{G@^P{(?y5PBL}*E*4I0B~vw(3BsMAs}Qj{%ppLI#X>-eV$Vg z*I5=HL#orCj}xZ~|G&@gE8#hBd*n7$bxwv9rPK!Z;ql(d;A7>&=>U2@f?vf zwhhS{9)r)|HT&rbgD2fTl5b=czE9DxdZ5n&I$~xHr+Q8PRG4S6BQ}jheQ$7&Lu?p!1oqx~IfE^hDItp1WxYW#02Ha+zv*1T{ZG(|TT&kd9jSw+3 zNHmBv(X!w_nP1GIxdEB71vh3!^xk2Jc7g^m+VJcK5{DOL++lH*WmF>FTtLsMed^&L zo8-$?1$yM5-^fxNCu-O%mZzRx!?NnB;|% zWAU~5W7v`NQXYu`S(^*c2xtMc0!Vy1Va(`H%r$H1KoZW+e=X@g$hMwSXZtVbx6vUd z1F3h!liOb6PhLpR;gg6#+(SJScT`mPZ^adA%8rIvZ{P0W z5HLr=$o95iDRM=UMs@VZFf+JKfvN^Fo8cw~o~T_Eu82oUB-G)PMJ2;0l8ILk?BreQ}98{!6q^@-Lp8@Dwt*Q&>|cQKU-9BFHR}IejTpl*%bdpOCi}7>I z6L(%lgPoVrB0cTuxmt$QW9aylUJA!SinGyztoOmm9RM5z90r`L#AOAy-d&{nS}8iz z^h4aMV-p!$CoWWZ)gq&&4B`eRd8Ol?-xWyC-xjf{l6Fz@51({Q2J*6&4GA4jtW>VFxh`ENRS?qPx5P-w=SVUG^khmnX?+{ERd6p zFp?bfRpYIN`ShU}eyq?!r^xtNp~<*rOlXtZ5mObG+DVHn4-4%yC&%z-g+6+ZfL(S= zXq#YF4H9D)AOxQRCTcfnG*y$u2KK3@3w%!ru!W_aSxmos4XIBSAk^ z5rThnV0lF@v-1LVWQ}0sZZ{xZJK%3W0r!CHuK^FmbwWQ$`Fcky!GOX$!6VoKxZf1bgw0n?lncyA)3%Dl@cPFlw=>p zZBR%RQQY^kHj)h7ICYf*322*o(g(>QKtZMUKd^W=MZYTHu_bfqzexT0jz*f^8^xW) z7J65-|FPmbDEiSP{B_B?er>5f!6fseVuhb{WraTr=VN4L{u@!|jZy4aR+}mFZvuP6 zZc;mh^%1Z}M0{Xb6R>^Zm*K+uA43J90qAe>r*W#{Ykn2cYqTuA?0##bvd zQ!mP;^)Wu6#Q5hGtgo^sAnrQ_S5-N+E}(TPxTnf(H^bDcJRO;Ed1Bn{!WwuJ!&(o+ z+FM}WS%a@uu~ZE%t+HV{a}u9t0iV|3Cf82-jD+8Cm1~t)j9(&Sx@L{`JWMq*USD&U ziL7;EZ3(qVX9dLd`#-2@qOu8}_#)vW{)k<|CwtN3%DTHLJ$|mv2LJD<|5w!ibazR1 zow6(@MZJy8$Xyc7jw{7&?nCrd8FOxT=EXv|Dgz`yr9F;CcI;WchW>+$ zpIV-i=7dGH0y^A~9JTnL%QsV6yo4>9e?>_2I`V%joU=|`($FAjy+(*Oc`F?Tg|8{R zpJl1d_+55e+BsOd8(vjsX=MG-jM)_hR21)6QAvLp!!N9Gncs*>yAvCt?KOhU8t>#z zkR&g+7Mxt)PCp;RUG=L{hzVi}OuSd$MCovL!veZFhS>%OT^7TE25T~@VdBM!pJ})~ zF>XNwsYVOV_t>eKc%^3n{l|s)UQc;w=Gf|p)J-wYA<4fXV6D(FCFA`N4Yn_V&Ib51 z;5c9a8gfxsf;;eH_)lW2L&5G>)S@4eO_4p_26h;v6!Ul>C9D355KmW*m)LU3vPh1X^HFLcB& zO-42q$h~LCrkuF6aR>Dxey*{S>cU?(&ZGBBIAvuqwE|bJT##K7qoBjZBdJ~yAS%d# zTqLS3_<@x>s4IAMWfq+%;nXH;_WNT*JYKrji??Wqr-r6Nsv7TVnpZ)1J*W1=l~}{R z;@jgR?;o-uW(-Ep`Brj;%Lt>vXwbm+A%nXQ!AF3bw7s+Vd=o?O6d!LkCXy38i?f?= zp}xhX&3W|xC|=iWr=Jn=zGgdg2?jd+lePMx>+Q@Mx8sfC_nM1!stMQ&wl3X|zgRV& z+J=)?muNd-z2yw9UA;Kx%`vT)$~SeA%Le%jAgpV4!baAm41R2ND`en*R?nk4ar&Ai z@L#j$6m1{F!kWWr0?19sgX~kcAE8_9xVGg782ZcCK0>8+fi={MT*!x9K>Og1`P6K3 zB_s4$DSRudbeyv8UV4^-_pWQTY*%Es$-yVI^(80{bOl8MAWPt4aCF^WnDcd*8n$9vUHxHx=RVCToA!=17A5R&eA7v;vj_NOhJ2S^@V1o(8-D_=>{!x94P&wV(v@ zmH*>U{pg2JuMs?R>o+riuE6grxES^8w=JX!TOc10^`khMwPa62!>c - + EnvironmentId diff --git a/F3:F303/InterfaceBoard/multiiface.files b/F3:F303/InterfaceBoard/multiiface.files index 4102133..66efa66 100644 --- a/F3:F303/InterfaceBoard/multiiface.files +++ b/F3:F303/InterfaceBoard/multiiface.files @@ -1,5 +1,9 @@ Debug.c Debug.h +can.c +can.h +canproto.c +canproto.h flash.c flash.h hardware.c diff --git a/F3:F303/InterfaceBoard/proto.c b/F3:F303/InterfaceBoard/proto.c index ebaee10..a5a3ee4 100644 --- a/F3:F303/InterfaceBoard/proto.c +++ b/F3:F303/InterfaceBoard/proto.c @@ -19,6 +19,7 @@ #include #include +#include "can.h" // min/max speeds #include "flash.h" #include "strfunc.h" #include "usart.h" @@ -39,13 +40,16 @@ const char *helpstring = "1..5x - send data over IF1..5\n" "d - dump flash\n" "ix - rename interface number x (0..6)\n" + "Cx - starting CAN bus speed (9600...3000000)\n" "Ex - erase full storage (witout x) or only page x\n" "F - reinit configurations from flash\n" + "I - show current config of all interfaces\n" "R - soft reset\n" "S - store new parameters into flash\n" "T - print current Tms\n" ; +// dump flash configuration static void dumpflash(){ CFGWR("userconf_sz="); CFGWR(u2str(the_conf.userconf_sz)); CFGWR("\ncurrentconfidx="); CFGWRn(i2str(currentconfidx)); @@ -60,8 +64,10 @@ static void dumpflash(){ } CFGn(); } + CFGWR("canspeed="); CFGWRn(u2str(the_conf.CANspeed)); } +// set new interface name static void setiface(const char *str){ if(!str || !*str) goto err; uint32_t N; @@ -85,6 +91,7 @@ err: CFGWR(sERRn); } +// erase custom flash page static const char* erpg(const char *str){ uint32_t N; if(str == getnum(str, &N)) return sERRn; @@ -92,6 +99,7 @@ static const char* erpg(const char *str){ return sOKn; } +// send message over U[S]ART static void sendoverU(uint8_t ifno, char *str){ int len = strlen(str); CFGWR("try to send "); CFGWRn(str); @@ -99,6 +107,45 @@ static void sendoverU(uint8_t ifno, char *str){ CFGWR("sent "); CFGWR(i2str(len)); CFGWR("bytes\n"); } +// show interfaces settings +static void Uconfig(){ + usb_LineCoding lc; + for(int i = 0; i < InterfacesAmount; ++i){ + uint8_t on = IFconfig(i, &lc); + CFGWR("Interface "); USB_putbyte(ICFG, '0' + i); + CFGWR(" -> status: "); CFGWR(on ? "ON" : "OFF"); + if(on){ + CFGWR("; baudrate: "); CFGWR(u2str(lc.dwDTERate)); + CFGWR("; "); + const char *s; + switch(lc.bCharFormat){ + case USB_CDC_1_STOP_BITS: s = "1"; break; + case USB_CDC_1_5_STOP_BITS: s = "1.5"; break; + case USB_CDC_2_STOP_BITS: s = "2"; break; + } + CFGWR(s); CFGWR(" stop bits; parity: "); + switch(lc.bParityType){ + case USB_CDC_NO_PARITY: s = "no"; break; + case USB_CDC_ODD_PARITY: s = "odd"; break; + case USB_CDC_EVEN_PARITY: s = "even"; break; + default: s = "prohibited"; + } + CFGWR(s); CFGWR("; "); + USB_putbyte(ICFG, '0' + lc.bDataBits); + CFGWR(" data bits"); + } + CFGn(); + } +} + +static const char* setCANspeed(char *buf){ + uint32_t N; + if(buf == getnum(buf, &N)) return sERRn; + if(N < CAN_MIN_SPEED || N > CAN_MAX_SPEED) return sERRn; + the_conf.CANspeed = N; + return sOKn; +} + const char *parse_cmd(char *buf){ if(!buf || !*buf) return NULL; if(strlen(buf) > 1){ @@ -109,6 +156,8 @@ const char *parse_cmd(char *buf){ return NULL; } switch(c){ + case 'C': + return setCANspeed(buf); case 'E': return erpg(buf); case 'i': @@ -131,6 +180,9 @@ const char *parse_cmd(char *buf){ case 'F': flashstorage_init(); return sOKn; + case 'I': + Uconfig(); + break; case 'R': NVIC_SystemReset(); return NULL; diff --git a/F3:F303/InterfaceBoard/usb_dev.c b/F3:F303/InterfaceBoard/usb_dev.c index d5ff457..0bc3cd5 100644 --- a/F3:F303/InterfaceBoard/usb_dev.c +++ b/F3:F303/InterfaceBoard/usb_dev.c @@ -189,12 +189,14 @@ void usb_class_request(config_pack_t *req, uint8_t *data, uint16_t datalen){ linecoding_handler(ifno, (usb_LineCoding*)data); break; case GET_LINE_CODING: + DBG("GET_LINE_CODING"); EP_WriteIRQ(0, (uint8_t*)&lineCoding[ifno], sizeof(lineCoding)); break; case SET_CONTROL_LINE_STATE: clstate_handler(ifno, req->wValue); break; case SEND_BREAK: + DBG("SEND_BREAK"); break_handler(ifno); break; default: @@ -266,6 +268,11 @@ int USB_send(uint8_t ifno, const uint8_t *buf, int len){ return TRUE; } +/* only try to add data to USB buffer, without sending +int USB_adddata(uint8_t ifno, const uint8_t *buf, int len){ + return RB_write((ringbuffer*)&rbout[ifno], buf, len); +} +*/ int USB_putbyte(uint8_t ifno, uint8_t byte){ if(!CDCready[ifno]) return FALSE; int l = 0; @@ -339,3 +346,15 @@ int USB_receivestr(uint8_t ifno, char *buf, int len){ buf[l-1] = 0; // replace '\n' with strend return l; } + +/** + * @brief IFconfig - interface configuration + * @param ifno - index + * @param l (o, user allocated) - settings + * @return value of CDCready + */ +uint8_t IFconfig(uint8_t ifno, usb_LineCoding *l){ + if(ifno >= InterfacesAmount) return 0; + if(l) *l = lineCoding[ifno]; + return CDCready[ifno]; +} diff --git a/F3:F303/InterfaceBoard/usb_dev.h b/F3:F303/InterfaceBoard/usb_dev.h index 3ea0e41..303d30e 100644 --- a/F3:F303/InterfaceBoard/usb_dev.h +++ b/F3:F303/InterfaceBoard/usb_dev.h @@ -58,7 +58,9 @@ void linecoding_handler(uint8_t ifno, usb_LineCoding *lc); int USB_sendbufspace(uint8_t ifno); int USB_sendall(uint8_t ifno); int USB_send(uint8_t ifno, const uint8_t *buf, int len); +//int USB_adddata(uint8_t ifno, const uint8_t *buf, int len); int USB_putbyte(uint8_t ifno, uint8_t byte); int USB_sendstr(uint8_t ifno, const char *string); int USB_receive(uint8_t ifno, uint8_t *buf, int len); int USB_receivestr(uint8_t ifno, char *buf, int len); +uint8_t IFconfig(uint8_t ifno, usb_LineCoding *l); diff --git a/F3:F303/InterfaceBoard/version.inc b/F3:F303/InterfaceBoard/version.inc index e22d49e..d09273c 100644 --- a/F3:F303/InterfaceBoard/version.inc +++ b/F3:F303/InterfaceBoard/version.inc @@ -1,2 +1,2 @@ -#define BUILD_NUMBER "140" -#define BUILD_DATE "2026-02-17" +#define BUILD_NUMBER "145" +#define BUILD_DATE "2026-02-18"