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 9329eaa..3e90e6b 100755 Binary files a/F3:F303/Seven_CDCs/sevenCDCs.bin and b/F3:F303/Seven_CDCs/sevenCDCs.bin differ diff --git a/F3:F303/Seven_CDCs/strfunc.c b/F3:F303/Seven_CDCs/strfunc.c index 69f10ea..614b263 100644 --- a/F3:F303/Seven_CDCs/strfunc.c +++ b/F3:F303/Seven_CDCs/strfunc.c @@ -268,6 +268,13 @@ const char *getint(const char *txt, int32_t *I){ return nxt; } +int mystrlen(const char *txt){ + if(!txt) return 0; + int r = 0; + while(*txt++) ++r; + return r; +} + /* void mymemcpy(char *dest, const char *src, int len){ if(len < 1) return; diff --git a/F3:F303/Seven_CDCs/strfunc.h b/F3:F303/Seven_CDCs/strfunc.h index fc61644..e1dd59e 100644 --- a/F3:F303/Seven_CDCs/strfunc.h +++ b/F3:F303/Seven_CDCs/strfunc.h @@ -28,5 +28,6 @@ const char *uhex2str(uint32_t val); const char *getnum(const char *txt, uint32_t *N); const char *omit_spaces(const char *buf); const char *getint(const char *txt, int32_t *I); +int mystrlen(const char *txt); //void mymemcpy(char *dest, const char *src, int len); char * float2str(float x, uint8_t prec); diff --git a/F3:F303/Seven_CDCs/usb.c b/F3:F303/Seven_CDCs/usb.c index 9129679..781cc53 100644 --- a/F3:F303/Seven_CDCs/usb.c +++ b/F3:F303/Seven_CDCs/usb.c @@ -19,6 +19,7 @@ #include #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"