From 9b0504944bab81c08f97949e9e148bd89ca163cd Mon Sep 17 00:00:00 2001 From: Edward Emelianov Date: Sat, 29 Apr 2023 21:49:45 +0300 Subject: [PATCH] add CAN --- F3:F303/Seven_CDCs/7CDCs.files | 4 + F3:F303/Seven_CDCs/can.c | 383 +++++++++++++++++++++++++++ F3:F303/Seven_CDCs/can.h | 59 +++++ F3:F303/Seven_CDCs/canproto.c | 438 +++++++++++++++++++++++++++++++ F3:F303/Seven_CDCs/canproto.h | 26 ++ F3:F303/Seven_CDCs/cmdproto.c | 15 +- F3:F303/Seven_CDCs/hardware.c | 31 ++- F3:F303/Seven_CDCs/main.c | 30 ++- F3:F303/Seven_CDCs/sevenCDCs.bin | Bin 8396 -> 12956 bytes F3:F303/Seven_CDCs/strfunc.c | 7 + F3:F303/Seven_CDCs/strfunc.h | 1 + F3:F303/Seven_CDCs/usb.c | 9 +- F3:F303/Seven_CDCs/usb_lib.c | 3 +- F3:F303/Seven_CDCs/version.inc | 2 +- 14 files changed, 993 insertions(+), 15 deletions(-) create mode 100644 F3:F303/Seven_CDCs/can.c create mode 100644 F3:F303/Seven_CDCs/can.h create mode 100644 F3:F303/Seven_CDCs/canproto.c create mode 100644 F3:F303/Seven_CDCs/canproto.h diff --git a/F3:F303/Seven_CDCs/7CDCs.files b/F3:F303/Seven_CDCs/7CDCs.files index a912955..45f8ac8 100644 --- a/F3:F303/Seven_CDCs/7CDCs.files +++ b/F3:F303/Seven_CDCs/7CDCs.files @@ -1,3 +1,7 @@ +can.c +can.h +canproto.c +canproto.h cmdproto.c cmdproto.h debug.h diff --git a/F3:F303/Seven_CDCs/can.c b/F3:F303/Seven_CDCs/can.c new file mode 100644 index 0000000..4db334f --- /dev/null +++ b/F3:F303/Seven_CDCs/can.c @@ -0,0 +1,383 @@ +/* + * This file is part of the 7CDCs 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 "can.h" +#include "hardware.h" +#include "strfunc.h" +#include "usb.h" + +#define SEND(str) do{USB_sendstr(CAN_IDX, str);}while(0) +#define SENDN(str) do{USB_sendstr(CAN_IDX, str); USB_putbyte(CAN_IDX, '\n');}while(0) + +// PB8/PB9!!! + +#include // memcpy + +// 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 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){ + //MSG("Try to push\n"); +#ifdef EBUG + SENDN("push"); +#endif + if(first_free_idx == first_nonfree_idx){ +#ifdef EBUG + SENDN("INBUF OVERFULL"); +#endif + 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; + } + return msg; +} + +void CAN_reinit(uint16_t speed){ + 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 +void CAN_setup(uint16_t speed){ + //LED_off(LED1); + if(speed == 0) speed = oldspeed; + else if(speed < 50) speed = 50; + else if(speed > 3000) speed = 3000; + oldspeed = speed; + uint32_t tmout = 16000000; + // Configure GPIO: PB8 - CAN_Rx, PB9 - CAN_Tx (both AF9) + RCC->AHBENR |= RCC_AHBENR_GPIOBEN; + GPIOB->MODER = (GPIOB->MODER & ~(GPIO_MODER_MODER8 | GPIO_MODER_MODER9)) + | (GPIO_MODER_MODER8_AF | GPIO_MODER_MODER9_AF); + GPIOB->AFR[1] = (GPIOB->AFR[1] &~ (GPIO_AFRH_AFRH0 | GPIO_AFRH_AFRH1)) + | AFRf(9, 8) | AFRf(9, 9); + /* Enable the peripheral clock CAN */ + RCC->APB1ENR |= RCC_APB1ENR_CANEN; + /* 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 to 100kb/s: 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; + CAN->MCR &=~ CAN_MCR_SLEEP; /* (3) */ + CAN->MCR |= CAN_MCR_ABOM; /* allow automatically bus-off */ + + CAN->BTR = 2 << 20 | 3 << 16 | (4500/speed - 1); //| CAN_BTR_SILM | CAN_BTR_LBKM; /* (4) */ + CAN->MCR &= ~CAN_MCR_INRQ; /* (5) */ + tmout = 16000000; + while((CAN->MSR & CAN_MSR_INAK) == CAN_MSR_INAK) /* (6) */ + if(--tmout == 0) break; + // 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; +} + +void printCANerr(){ + if(!last_err_code) last_err_code = CAN->ESR; + if(!last_err_code){ + SENDN("No errors"); + return; + } + SEND("Receive error counter: "); + SEND(u2str((last_err_code & CAN_ESR_REC)>>24)); + SEND("\nTransmit error counter: "); + SEND(u2str((last_err_code & CAN_ESR_TEC)>>16)); + SEND("\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; + } + SEND(errmsg); SENDN(" error"); + if(last_err_code & CAN_ESR_BOFF) SENDN("Bus off"); + if(last_err_code & CAN_ESR_EPVF) SENDN("Passive error limit"); + if(last_err_code & CAN_ESR_EWGF) SENDN("Error counter limit"); + last_err_code = 0; +} + +void can_proc(){ +#ifdef EBUG + if(last_err_code){ + SEND("Error, ESR="); + SENDN(u2str(last_err_code)); + last_err_code = 0; + } +#endif + // 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 + SENDN("\nToo much errors, restarting CAN!"); + printCANerr(); + // 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; + static uint32_t incrmessagectr = 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 +#ifdef EBUG + SENDN("No free mailboxes"); +#endif + return CAN_BUSY; + } +#ifdef EBUG + SEND("Send data. Len="); SENDN(u2str(len)); + SEND(", tagid="); SENDN(u2str(target_id)); + SEND(", data="); + for(int i = 0; i < len; ++i){ + SEND(" "); SEND(uhex2str(msg[i])); + } + SEND("\n"); +#endif + CAN_TxMailBox_TypeDef *box = &CAN->sTxMailBox[mailbox]; + uint32_t lb = 0, hb = 0; + switch(len){ + case 8: + hb |= (uint32_t)msg[7] << 24; + __attribute__((fallthrough)); + case 7: + hb |= (uint32_t)msg[6] << 16; + __attribute__((fallthrough)); + case 6: + hb |= (uint32_t)msg[5] << 8; + __attribute__((fallthrough)); + case 5: + hb |= (uint32_t)msg[4]; + __attribute__((fallthrough)); + case 4: + lb |= (uint32_t)msg[3] << 24; + __attribute__((fallthrough)); + case 3: + lb |= (uint32_t)msg[2] << 16; + __attribute__((fallthrough)); + case 2: + lb |= (uint32_t)msg[1] << 8; + __attribute__((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; +} + +void set_flood(CAN_message *msg, int incr){ + if(incr){ incrflood = 1; return; } + incrflood = 0; + if(!msg) flood_msg = NULL; + else{ + memcpy(&loc_flood_msg, msg, sizeof(CAN_message)); + flood_msg = &loc_flood_msg; + } +} + +static void can_process_fifo(uint8_t fifo_num){ + if(fifo_num > 1) return; + //LED_on(LED1); + CAN_FIFOMailBox_TypeDef *box = &CAN->sFIFOMailBox[fifo_num]; + volatile uint32_t *RFxR = (fifo_num) ? &CAN->RF1R : &CAN->RF0R; +#ifdef EBUG + SEND(u2str(*RFxR & CAN_RF0R_FMP0)); SENDN(" messages in FIFO"); +#endif + // 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; + __attribute__((fallthrough)); + case 7: + dat[6] = (hb>>16) & 0xff; + __attribute__((fallthrough)); + case 6: + dat[5] = (hb>>8) & 0xff; + __attribute__((fallthrough)); + case 5: + dat[4] = hb & 0xff; + __attribute__((fallthrough)); + case 4: + dat[3] = lb>>24; + __attribute__((fallthrough)); + case 3: + dat[2] = (lb>>16) & 0xff; + __attribute__((fallthrough)); + case 2: + dat[1] = (lb>>8) & 0xff; + __attribute__((fallthrough)); + case 1: + dat[0] = lb & 0xff; + } + } + 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 +#ifdef EBUG + last_err_code = CAN->ESR; +#endif + CAN->MSR &= ~CAN_MSR_ERRI; + // 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/Seven_CDCs/can.h b/F3:F303/Seven_CDCs/can.h new file mode 100644 index 0000000..b6720c0 --- /dev/null +++ b/F3:F303/Seven_CDCs/can.h @@ -0,0 +1,59 @@ +/* + * This file is part of the 7CDCs 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 + +// amount of filter banks in STM32F0 +#define STM32F0FBANKNO 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(uint16_t speed); + +CAN_status can_send(uint8_t *msg, uint8_t len, uint16_t target_id); +void can_proc(); +void printCANerr(); + +CAN_message *CAN_messagebuf_pop(); + +void set_flood(CAN_message *msg, int incr); diff --git a/F3:F303/Seven_CDCs/canproto.c b/F3:F303/Seven_CDCs/canproto.c new file mode 100644 index 0000000..0ac324b --- /dev/null +++ b/F3:F303/Seven_CDCs/canproto.c @@ -0,0 +1,438 @@ +/* + * 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 + +#include "can.h" +#include "hardware.h" +#include "canproto.h" +#include "version.inc" + +#include "usb.h" +#include "strfunc.h" + +#define SEND(str) do{USB_sendstr(CAN_IDX, str);}while(0) +#define SENDN(str) do{USB_sendstr(CAN_IDX, str); USB_putbyte(CAN_IDX, '\n');}while(0) +#define printu(x) SEND(u2str(x)) +#define printuhex(x) SEND(uhex2str(x)) + +// software ignore buffer size +#define IGN_SIZE 10 + +extern volatile uint32_t Tms; + +uint8_t ShowMsgs = 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){ + SENDN("ID should be 11-bit number!"); + return NULL; + } + canmsg.ID = (uint16_t)(N&0x7ff); + ctr = 0; + continue; + } + if(ctr > 7){ + SENDN("ONLY 8 data bytes allowed!"); + return NULL; + } + if(N > 0xff){ + SENDN("Every data portion is a byte!"); + return NULL; + } + canmsg.data[ctr++] = (uint8_t)(N&0xff); + }while(1); + if(canmsg.ID == 0xffff){ + SENDN("NO ID given, send nothing!"); + return NULL; + } + SENDN("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){ + SENDN("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){ + SEND("No speed given"); + return; + } + if(N < 50){ + SEND("Lowest speed is 50kbps"); + return; + }else if(N > 3000){ + SEND("Highest speed is 3000kbps"); + return; + } + CAN_reinit((uint16_t)N); + SEND("Reinit CAN bus with speed "); + printu(N); SENDN("kbps"); +} + +TRUE_INLINE void addIGN(const char *txt){ + if(IgnSz == IGN_SIZE){ + SENDN("Ignore buffer is full"); + return; + } + txt = omit_spaces(txt); + uint32_t N; + const char *n = getnum(txt, &N); + if(txt == n){ + SENDN("No ID given"); + return; + } + if(N > 0x7ff){ + SENDN("ID should be 11-bit number!"); + return; + } + Ignore_IDs[IgnSz++] = (uint16_t)(N & 0x7ff); + SEND("Added ID "); printu(N); + SENDN("\nIgn buffer size: "); SENDN(u2str(IgnSz)); +} + +TRUE_INLINE void print_ign_buf(){ + if(IgnSz == 0){ + SENDN("Ignore buffer is empty"); + return; + } + SENDN("Ignored IDs:"); + for(int i = 0; i < IgnSz; ++i){ + printu(i); + SEND(": "); + SENDN(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){ + SEND("Filter "); printu(ctr); SEND(", FIFO"); + if(CAN->FFA1R & mask) SEND("1"); + else SEND("0"); + SEND(" in "); + if(CAN->FM1R & mask){ // up to 4 filters in LIST mode + SEND("LIST mode, IDs: "); + printID(CAN->sFilterRegister[ctr].FR1 & 0xffff); + SEND(" "); + printID(CAN->sFilterRegister[ctr].FR1 >> 16); + SEND(" "); + printID(CAN->sFilterRegister[ctr].FR2 & 0xffff); + SEND(" "); + printID(CAN->sFilterRegister[ctr].FR2 >> 16); + }else{ // up to 2 filters in MASK mode + SEND("MASK mode: "); + if(!(CAN->sFilterRegister[ctr].FR1&0x1f)){ + SEND("ID="); printID(CAN->sFilterRegister[ctr].FR1 & 0xffff); + SEND(", MASK="); printID(CAN->sFilterRegister[ctr].FR1 >> 16); + SEND(" "); + } + if(!(CAN->sFilterRegister[ctr].FR2&0x1f)){ + SEND("ID="); printID(CAN->sFilterRegister[ctr].FR2 & 0xffff); + SEND(", MASK="); printID(CAN->sFilterRegister[ctr].FR2 >> 16); + } + } + SEND("\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){ + SENDN("t="); SENDN(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){ + SENDN("No bank# given"); + return; + } + if(N > STM32F0FBANKNO-1){ + SENDN("bank# > 27"); + return; + } + uint8_t bankno = (uint8_t)N; + str = omit_spaces(n); + if(!*str){ // deactivate filter + SEND("Deactivate filters in bank "); + printu(bankno); + SEND("\n"); + 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'){ + SENDN("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'){ + SENDN("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){ + SENDN("You should add at least one filter!"); + return; + } + if(mode && (nfilt&1)){ + SENDN("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; + SEND("Added filter with "); + printu(nfilt); SENDN(" parameters"); +} + +static const char *helpstring = + "https://github.com/eddyem/stm32samples/tree/master/F3:F303/Seven_CDCs build#" BUILD_NUMBER " @ " BUILD_DATE "\n" + "'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" +// "'o' - turn LEDs OFF\n" +// "'O' - turn LEDs ON\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(){ + SEND("CAN_MSR="); + printuhex(CAN->MSR); + SEND("\nCAN_TSR="); + printuhex(CAN->TSR); + SEND("\nCAN_RF0R="); + printuhex(CAN->RF0R); + SEND("\nCAN_RF1R="); + SENDN(u2str(CAN->RF1R)); +} + +/** + * @brief cmd_parser - command parsing + * @param txt - buffer with commands & data + * @param isUSB - == 1 if data got from USB + */ +void cmd_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': + set_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': + printCANerr(); + break; + case 'i': + set_flood(NULL, 1); + SENDN("Incremental flooding is ON ('F' to off)"); + break; + case 'I': + CAN_reinit(0); + break; + case 'l': + list_filters(); + break; +/* case 'o': + ledsON = 0; + LED_off(LED0); + LED_off(LED1); + break; + case 'O': + ledsON = 1; + break;*/ + case 'p': + print_ign_buf(); + break; + case 'P': + ShowMsgs = !ShowMsgs; + if(ShowMsgs) SENDN("Resume"); + else SENDN("Pause"); + break; + case 'R': + SENDN("Soft reset"); + USB_sendall(CAN_IDX); // wait until everything will gone + NVIC_SystemReset(); + break; + case 'T': + SEND("Time (ms): "); + SENDN(u2str(Tms)); + break; + default: // help + SENDN(helpstring); + break; + } +} + +// check Ignore_IDs & return 1 if ID isn't in list +uint8_t isgood(uint16_t ID){ + for(int i = 0; i < IgnSz; ++i) + if(Ignore_IDs[i] == ID) return 0; + return 1; +} diff --git a/F3:F303/Seven_CDCs/canproto.h b/F3:F303/Seven_CDCs/canproto.h new file mode 100644 index 0000000..1e5c05c --- /dev/null +++ b/F3:F303/Seven_CDCs/canproto.h @@ -0,0 +1,26 @@ +/* + * 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 + +extern uint8_t ShowMsgs; // show CAN messages flag + +void cmd_parser(char *txt); +uint8_t isgood(uint16_t ID); diff --git a/F3:F303/Seven_CDCs/cmdproto.c b/F3:F303/Seven_CDCs/cmdproto.c index 0776302..af8346a 100644 --- a/F3:F303/Seven_CDCs/cmdproto.c +++ b/F3:F303/Seven_CDCs/cmdproto.c @@ -19,6 +19,7 @@ #include "cmdproto.h" #include "debug.h" #include "strfunc.h" +#include "usart.h" #include "usb.h" #include "usb_lib.h" // USBON #include "version.inc" @@ -28,12 +29,13 @@ extern volatile uint32_t Tms; -const char* helpmsg = - "https://github.com/eddyem/stm32samples/tree/master/F3:F303/PL2303 build#" BUILD_NUMBER " @ " BUILD_DATE "\n" +static const char* helpmsg = + "https://github.com/eddyem/stm32samples/tree/master/F3:F303/Seven_CDCs build#" BUILD_NUMBER " @ " BUILD_DATE "\n" "2..7 - send next string to given EP\n" "'i' - print USB->ISTR state\n" "'N' - read number (dec, 0xhex, 0oct, bbin) and show it in decimal\n" "'R' - software reset\n" + "'ux data' - send data to USARTx\n" "'U' - get USB status\n" "'W' - test watchdog\n" ; @@ -94,6 +96,15 @@ void parse_cmd(const char *buf){ SEND(nxt); }else SEND("\n"); break; + case 'u': + nxt = getnum(buf, &Num); + if(buf == nxt || Num < 1 || Num > USARTSNO){ + if(Num == 0) SENDN("Wrong USART number"); + } + nxt = omit_spaces(nxt); + usart_sendn(Num, (uint8_t*)nxt, mystrlen(nxt)); + SENDN("OK"); + break; default: SEND(buf-1); // echo return; diff --git a/F3:F303/Seven_CDCs/hardware.c b/F3:F303/Seven_CDCs/hardware.c index 00e2f48..44dc333 100644 --- a/F3:F303/Seven_CDCs/hardware.c +++ b/F3:F303/Seven_CDCs/hardware.c @@ -19,19 +19,44 @@ #include "hardware.h" #include "usart.h" +TRUE_INLINE void iwdg_setup(){ + uint32_t tmout = 16000000; + /* Enable the peripheral clock RTC */ + /* (1) Enable the LSI (40kHz) */ + /* (2) Wait while it is not ready */ + RCC->CSR |= RCC_CSR_LSION; /* (1) */ + while((RCC->CSR & RCC_CSR_LSIRDY) != RCC_CSR_LSIRDY){if(--tmout == 0) break;} /* (2) */ + /* Configure IWDG */ + /* (1) Activate IWDG (not needed if done in option bytes) */ + /* (2) Enable write access to IWDG registers */ + /* (3) Set prescaler by 64 (1.6ms for each tick) */ + /* (4) Set reload value to have a rollover each 2s */ + /* (5) Check if flags are reset */ + /* (6) Refresh counter */ + IWDG->KR = IWDG_START; /* (1) */ + IWDG->KR = IWDG_WRITE_ACCESS; /* (2) */ + IWDG->PR = IWDG_PR_PR_1; /* (3) */ + IWDG->RLR = 1250; /* (4) */ + tmout = 16000000; + while(IWDG->SR){if(--tmout == 0) break;} /* (5) */ + IWDG->KR = IWDG_REFRESH; /* (6) */ +} + + static inline void gpio_setup(){ RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN; - // LEDs on PB0 and PB1 - GPIOB->MODER = GPIO_MODER_MODER0_O | GPIO_MODER_MODER1_O; - GPIOB->ODR = 1; // USB - alternate function 14 @ pins PA11/PA12; USART1 = AF7 @PA9/10; SWD - AF0 @PA13/14 GPIOA->AFR[1] = AFRf(7, 9) | AFRf(7, 10) | AFRf(14, 11) | AFRf(14, 12); // USART1: PA10(Rx), PA9(Tx); USB - PA11, PA12; SWDIO - PA13, PA14; Pullup - PA15 GPIOA->MODER = MODER_AF(9) | MODER_AF(10) | MODER_AF(11) | MODER_AF(12) | MODER_AF(13) | MODER_AF(14) | MODER_O(15); GPIOA->OSPEEDR = OSPEED_HI(11) | OSPEED_HI(12) | OSPEED_HI(13) | OSPEED_HI(14); + // LEDs on PB0 and PB1 + GPIOB->MODER = GPIO_MODER_MODER0_O | GPIO_MODER_MODER1_O; + GPIOB->ODR = 1; } void hw_setup(){ gpio_setup(); + iwdg_setup(); } diff --git a/F3:F303/Seven_CDCs/main.c b/F3:F303/Seven_CDCs/main.c index 3871534..a286234 100644 --- a/F3:F303/Seven_CDCs/main.c +++ b/F3:F303/Seven_CDCs/main.c @@ -16,6 +16,8 @@ * along with this program. If not, see . */ +#include "can.h" +#include "canproto.h" #include "cmdproto.h" #include "debug.h" #include "hardware.h" @@ -46,17 +48,40 @@ int main(void){ hw_setup(); usarts_setup(); USB_setup(); + CAN_setup(100); USBPU_ON(); + CAN_message *can_mesg; uint32_t ctr = Tms; while(1){ + IWDG->KR = IWDG_REFRESH; if(Tms - ctr > 499){ ctr = Tms; pin_toggle(GPIOB, 1 << 1 | 1 << 0); // toggle LED @ PB0 - USB_sendstr(CMD_IDX, "1"); + //USB_sendstr(CMD_IDX, "1"); //DBGmesg(u2str(Tms)); //DBGnl(); } + can_proc(); + if(CAN_get_status() == CAN_FIFO_OVERRUN){ + USB_sendstr(CAN_IDX, "CAN bus fifo overrun occured!\n"); + } + while((can_mesg = CAN_messagebuf_pop())){ + if(isgood(can_mesg->ID)){ + if(ShowMsgs){ // display message content + IWDG->KR = IWDG_REFRESH; + uint8_t len = can_mesg->length; + USB_sendstr(CAN_IDX, u2str(Tms)); + USB_sendstr(CAN_IDX, " #"); + USB_sendstr(CAN_IDX, uhex2str(can_mesg->ID)); + for(uint8_t i = 0; i < len; ++i){ + USB_putbyte(CAN_IDX, ' '); + USB_sendstr(CAN_IDX, uhex2str(can_mesg->data[i])); + } + USB_putbyte(CAN_IDX, '\n'); + } + } + } for(int i = 0; i < MAX_IDX; ++i){ int l = USB_receivestr(i, inbuff, MAXSTRLEN); if(l < 0){ @@ -73,6 +98,9 @@ int main(void){ case CMD_IDX: parse_cmd(inbuff); break; + case CAN_IDX: + cmd_parser(inbuff); + break; default: break; } diff --git a/F3:F303/Seven_CDCs/sevenCDCs.bin b/F3:F303/Seven_CDCs/sevenCDCs.bin index 9329eaa643c0dacef322bdebd0152a518adf7149..3e90e6b178bdcff32edab6d0ade22345e49bf3b7 100755 GIT binary patch literal 12956 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 literal 8396 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 #include "hardware.h" +#include "strfunc.h" #include "usb.h" #include "usb_lib.h" @@ -89,12 +90,8 @@ int USB_putbyte(int ifNo, uint8_t byte){ } int USB_sendstr(int ifNo, const char *string){ - if(!string || !USBON(ifNo)) return 0; - int len = 0; - const char *b = string; - while(*b++) ++len; - if(!len) return 0; - return USB_send(ifNo, (const uint8_t*)string, len); + if(!string) return 0; + return USB_send(ifNo, (const uint8_t*)string, mystrlen(string)); } /** diff --git a/F3:F303/Seven_CDCs/usb_lib.c b/F3:F303/Seven_CDCs/usb_lib.c index 3b8067d..ac0f04e 100644 --- a/F3:F303/Seven_CDCs/usb_lib.c +++ b/F3:F303/Seven_CDCs/usb_lib.c @@ -775,9 +775,8 @@ static void rxtx_Handler(uint8_t epno){ case USART4_EPNO: ; // we have no USART4 in STM32F303CBT6 break; - default: - if(RB_write((ringbuffer*)&rbin[idx], buf, sz) != sz) bufovrfl[idx] = 1; } + if(RB_write((ringbuffer*)&rbin[idx], buf, sz) != sz) bufovrfl[idx] = 1; } // set ACK Rx USB->EPnR[epno] = (KEEP_DTOG(USB->EPnR[epno]) & ~(USB_EPnR_STAT_TX)) ^ USB_EPnR_STAT_RX; diff --git a/F3:F303/Seven_CDCs/version.inc b/F3:F303/Seven_CDCs/version.inc index c982bd1..a050ec6 100644 --- a/F3:F303/Seven_CDCs/version.inc +++ b/F3:F303/Seven_CDCs/version.inc @@ -1,2 +1,2 @@ -#define BUILD_NUMBER "122" +#define BUILD_NUMBER "133" #define BUILD_DATE "2023-04-29"