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 9a2c9a7..01e7c0a 100755 Binary files a/F3:F303/InterfaceBoard/multiiface.bin and b/F3:F303/InterfaceBoard/multiiface.bin differ diff --git a/F3:F303/InterfaceBoard/multiiface.config b/F3:F303/InterfaceBoard/multiiface.config index 1aff3d9..9082956 100644 --- a/F3:F303/InterfaceBoard/multiiface.config +++ b/F3:F303/InterfaceBoard/multiiface.config @@ -6,3 +6,4 @@ #define __thumb2__ 1 #define __ARM_ARCH_7M__ #define USB1_16 +#define CANPRIVATE__ diff --git a/F3:F303/InterfaceBoard/multiiface.creator.user b/F3:F303/InterfaceBoard/multiiface.creator.user index cb27183..bc3d850 100644 --- a/F3:F303/InterfaceBoard/multiiface.creator.user +++ b/F3:F303/InterfaceBoard/multiiface.creator.user @@ -1,6 +1,6 @@ - + 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"