mirror of
https://github.com/eddyem/stm32samples.git
synced 2025-12-06 10:45:11 +03:00
add CAN
This commit is contained in:
parent
72ef5c9cc6
commit
9b0504944b
@ -1,3 +1,7 @@
|
|||||||
|
can.c
|
||||||
|
can.h
|
||||||
|
canproto.c
|
||||||
|
canproto.h
|
||||||
cmdproto.c
|
cmdproto.c
|
||||||
cmdproto.h
|
cmdproto.h
|
||||||
debug.h
|
debug.h
|
||||||
|
|||||||
383
F3:F303/Seven_CDCs/can.c
Normal file
383
F3:F303/Seven_CDCs/can.c
Normal file
@ -0,0 +1,383 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the 7CDCs project.
|
||||||
|
* Copyright 2023 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "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 <string.h> // 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
59
F3:F303/Seven_CDCs/can.h
Normal file
59
F3:F303/Seven_CDCs/can.h
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the 7CDCs project.
|
||||||
|
* Copyright 2023 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// 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);
|
||||||
438
F3:F303/Seven_CDCs/canproto.c
Normal file
438
F3:F303/Seven_CDCs/canproto.c
Normal file
@ -0,0 +1,438 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the canusb project.
|
||||||
|
* Copyright 2023 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stm32f3.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#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<<bankno);
|
||||||
|
CAN->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<<bankno;
|
||||||
|
CAN->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;
|
||||||
|
}
|
||||||
26
F3:F303/Seven_CDCs/canproto.h
Normal file
26
F3:F303/Seven_CDCs/canproto.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the canusb project.
|
||||||
|
* Copyright 2023 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
extern uint8_t ShowMsgs; // show CAN messages flag
|
||||||
|
|
||||||
|
void cmd_parser(char *txt);
|
||||||
|
uint8_t isgood(uint16_t ID);
|
||||||
@ -19,6 +19,7 @@
|
|||||||
#include "cmdproto.h"
|
#include "cmdproto.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "strfunc.h"
|
#include "strfunc.h"
|
||||||
|
#include "usart.h"
|
||||||
#include "usb.h"
|
#include "usb.h"
|
||||||
#include "usb_lib.h" // USBON
|
#include "usb_lib.h" // USBON
|
||||||
#include "version.inc"
|
#include "version.inc"
|
||||||
@ -28,12 +29,13 @@
|
|||||||
|
|
||||||
extern volatile uint32_t Tms;
|
extern volatile uint32_t Tms;
|
||||||
|
|
||||||
const char* helpmsg =
|
static const char* helpmsg =
|
||||||
"https://github.com/eddyem/stm32samples/tree/master/F3:F303/PL2303 build#" BUILD_NUMBER " @ " BUILD_DATE "\n"
|
"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"
|
"2..7 - send next string to given EP\n"
|
||||||
"'i' - print USB->ISTR state\n"
|
"'i' - print USB->ISTR state\n"
|
||||||
"'N' - read number (dec, 0xhex, 0oct, bbin) and show it in decimal\n"
|
"'N' - read number (dec, 0xhex, 0oct, bbin) and show it in decimal\n"
|
||||||
"'R' - software reset\n"
|
"'R' - software reset\n"
|
||||||
|
"'ux data' - send data to USARTx\n"
|
||||||
"'U' - get USB status\n"
|
"'U' - get USB status\n"
|
||||||
"'W' - test watchdog\n"
|
"'W' - test watchdog\n"
|
||||||
;
|
;
|
||||||
@ -94,6 +96,15 @@ void parse_cmd(const char *buf){
|
|||||||
SEND(nxt);
|
SEND(nxt);
|
||||||
}else SEND("\n");
|
}else SEND("\n");
|
||||||
break;
|
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:
|
default:
|
||||||
SEND(buf-1); // echo
|
SEND(buf-1); // echo
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -19,19 +19,44 @@
|
|||||||
#include "hardware.h"
|
#include "hardware.h"
|
||||||
#include "usart.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(){
|
static inline void gpio_setup(){
|
||||||
RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN;
|
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
|
// 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);
|
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
|
// 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->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);
|
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(){
|
void hw_setup(){
|
||||||
gpio_setup();
|
gpio_setup();
|
||||||
|
iwdg_setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -16,6 +16,8 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "can.h"
|
||||||
|
#include "canproto.h"
|
||||||
#include "cmdproto.h"
|
#include "cmdproto.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "hardware.h"
|
#include "hardware.h"
|
||||||
@ -46,17 +48,40 @@ int main(void){
|
|||||||
hw_setup();
|
hw_setup();
|
||||||
usarts_setup();
|
usarts_setup();
|
||||||
USB_setup();
|
USB_setup();
|
||||||
|
CAN_setup(100);
|
||||||
USBPU_ON();
|
USBPU_ON();
|
||||||
|
|
||||||
|
CAN_message *can_mesg;
|
||||||
uint32_t ctr = Tms;
|
uint32_t ctr = Tms;
|
||||||
while(1){
|
while(1){
|
||||||
|
IWDG->KR = IWDG_REFRESH;
|
||||||
if(Tms - ctr > 499){
|
if(Tms - ctr > 499){
|
||||||
ctr = Tms;
|
ctr = Tms;
|
||||||
pin_toggle(GPIOB, 1 << 1 | 1 << 0); // toggle LED @ PB0
|
pin_toggle(GPIOB, 1 << 1 | 1 << 0); // toggle LED @ PB0
|
||||||
USB_sendstr(CMD_IDX, "1");
|
//USB_sendstr(CMD_IDX, "1");
|
||||||
//DBGmesg(u2str(Tms));
|
//DBGmesg(u2str(Tms));
|
||||||
//DBGnl();
|
//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){
|
for(int i = 0; i < MAX_IDX; ++i){
|
||||||
int l = USB_receivestr(i, inbuff, MAXSTRLEN);
|
int l = USB_receivestr(i, inbuff, MAXSTRLEN);
|
||||||
if(l < 0){
|
if(l < 0){
|
||||||
@ -73,6 +98,9 @@ int main(void){
|
|||||||
case CMD_IDX:
|
case CMD_IDX:
|
||||||
parse_cmd(inbuff);
|
parse_cmd(inbuff);
|
||||||
break;
|
break;
|
||||||
|
case CAN_IDX:
|
||||||
|
cmd_parser(inbuff);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
@ -268,6 +268,13 @@ const char *getint(const char *txt, int32_t *I){
|
|||||||
return nxt;
|
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){
|
void mymemcpy(char *dest, const char *src, int len){
|
||||||
if(len < 1) return;
|
if(len < 1) return;
|
||||||
|
|||||||
@ -28,5 +28,6 @@ const char *uhex2str(uint32_t val);
|
|||||||
const char *getnum(const char *txt, uint32_t *N);
|
const char *getnum(const char *txt, uint32_t *N);
|
||||||
const char *omit_spaces(const char *buf);
|
const char *omit_spaces(const char *buf);
|
||||||
const char *getint(const char *txt, int32_t *I);
|
const char *getint(const char *txt, int32_t *I);
|
||||||
|
int mystrlen(const char *txt);
|
||||||
//void mymemcpy(char *dest, const char *src, int len);
|
//void mymemcpy(char *dest, const char *src, int len);
|
||||||
char * float2str(float x, uint8_t prec);
|
char * float2str(float x, uint8_t prec);
|
||||||
|
|||||||
@ -19,6 +19,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "hardware.h"
|
#include "hardware.h"
|
||||||
|
#include "strfunc.h"
|
||||||
#include "usb.h"
|
#include "usb.h"
|
||||||
#include "usb_lib.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){
|
int USB_sendstr(int ifNo, const char *string){
|
||||||
if(!string || !USBON(ifNo)) return 0;
|
if(!string) return 0;
|
||||||
int len = 0;
|
return USB_send(ifNo, (const uint8_t*)string, mystrlen(string));
|
||||||
const char *b = string;
|
|
||||||
while(*b++) ++len;
|
|
||||||
if(!len) return 0;
|
|
||||||
return USB_send(ifNo, (const uint8_t*)string, len);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -775,9 +775,8 @@ static void rxtx_Handler(uint8_t epno){
|
|||||||
case USART4_EPNO:
|
case USART4_EPNO:
|
||||||
; // we have no USART4 in STM32F303CBT6
|
; // we have no USART4 in STM32F303CBT6
|
||||||
break;
|
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
|
// set ACK Rx
|
||||||
USB->EPnR[epno] = (KEEP_DTOG(USB->EPnR[epno]) & ~(USB_EPnR_STAT_TX)) ^ USB_EPnR_STAT_RX;
|
USB->EPnR[epno] = (KEEP_DTOG(USB->EPnR[epno]) & ~(USB_EPnR_STAT_TX)) ^ USB_EPnR_STAT_RX;
|
||||||
|
|||||||
@ -1,2 +1,2 @@
|
|||||||
#define BUILD_NUMBER "122"
|
#define BUILD_NUMBER "133"
|
||||||
#define BUILD_DATE "2023-04-29"
|
#define BUILD_DATE "2023-04-29"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user