diff --git a/F3:F303/CANbus4BTA/BTA_CAN.bin b/F3:F303/CANbus4BTA/BTA_CAN.bin index 6a0471f..e0abd49 100755 Binary files a/F3:F303/CANbus4BTA/BTA_CAN.bin and b/F3:F303/CANbus4BTA/BTA_CAN.bin differ diff --git a/F3:F303/CANbus4BTA/Readme.md b/F3:F303/CANbus4BTA/Readme.md index bc6a3b9..9c3c277 100644 --- a/F3:F303/CANbus4BTA/Readme.md +++ b/F3:F303/CANbus4BTA/Readme.md @@ -115,10 +115,35 @@ PP - push-pull, OD - open drain, I - floating input, A - analog input, AFn - alt | 47 | VSS | gnd | | | |---------|-------------|-------------|-------------|------------------| -## DMA usage -### DMA1 +# DMA usage +## DMA1 Channel1 - ADC1. -### DMA2 +## DMA2 +# Text command protocol +Text commands have format of `parameter[number][=setter]`. Where +- *parameter* is any possible command, +- *number* is its number (if need, like esw5), +- *setter* is new value to set parameter. + +Without setter command works like getter or run some routine. + +**All commands should have codes more than '@'.** + +## Text commands list + + +# CAN bus command protocol +All commands have variable length format: e.g. you don't need to send 8 random data bytes for commands like +`reset`, which needs no arguments and couldn't be a setter. So, starting from 1st byte in data packet: + +**0** - command code (L) +**1** - command code (H) - uint16_t +**2** - command parameter, 0..127 (setter have 1 in MSB) +**3** - error code (only in answers) +**4..7** - data (int32_t, little endian) + + +## CAN bus command codes diff --git a/F3:F303/CANbus4BTA/can.c b/F3:F303/CANbus4BTA/can.c new file mode 100644 index 0000000..f37f6ed --- /dev/null +++ b/F3:F303/CANbus4BTA/can.c @@ -0,0 +1,425 @@ +/* + * This file is part of the canbus4bta project. + * Copyright 2024 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 "commonfunctions.h" +#include "flash.h" +#include "hardware.h" +#include "proto.h" +#include "strfunc.h" +#include "usb.h" + +// PD1 - Tx, PD0 - Rx !!! + +#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 uint32_t oldspeed = 100000; // 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 + USB_sendstr("push: "); + for(int i = 0; i < msg->length; ++i){ + printuhex(msg->data[i]); USB_putbyte(' '); + } + newline(); +#endif + if(first_free_idx == first_nonfree_idx){ +#ifdef EBUG + USB_sendstr("INBUF OVERFULL\n"); +#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; + #ifdef EBUG + //MSG("read from idx "); printu(first_nonfree_idx); NL(); + #endif + 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; + } +#ifdef EBUG + USB_sendstr("pop: "); + for(int i = 0; i < msg->length; ++i){ + printuhex(msg->data[i]); USB_putbyte(' '); + } + newline(); +#endif + 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 36/8 = 4.5MHz +-> to get 100kbps we need prescaler=60 + 250kbps - 18 + 500kbps - 9 + 1MBps - [4.5] +*/ + +// speed - in kbps +// GPIO configured in hw_setup +void CAN_setup(uint32_t speed){ + if(speed == 0) speed = oldspeed; + else if(speed < 50) speed = 50; + else if(speed > 3000) speed = 3000; + uint32_t tmout = 10000; + /* 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; + if(tmout==0){ DBG("timeout!\n");} + CAN->MCR &=~ CAN_MCR_SLEEP; /* (3) */ + CAN->MCR |= CAN_MCR_ABOM; /* allow automatically bus-off */ + + CAN->BTR = 2 << 20 | 3 << 16 | (4500000/speed - 1); //| CAN_BTR_SILM | CAN_BTR_LBKM; /* (4) */ + oldspeed = 4500000/((CAN->BTR & CAN_BTR_BRP) + 1); + CAN->MCR &= ~CAN_MCR_INRQ; /* (5) */ + tmout = 10000; + while(CAN->MSR & CAN_MSR_INAK) /* (6) */ + if(--tmout == 0) break; + if(tmout==0){ DBG("timeout!\n");} + // accept ALL + CAN->FMR = CAN_FMR_FINIT; /* (7) */ + CAN->FA1R = CAN_FA1R_FACT0 | CAN_FA1R_FACT1; /* (8) */ + // set to 1 all needed bits of CAN->FFA1R to switch given filters to FIFO1 + CAN->sFilterRegister[0].FR1 = (1<<21)|(1<<5); // all odd IDs + CAN->FFA1R = 2; // filter 1 for FIFO1, filter 0 - for FIFO0 + CAN->sFilterRegister[1].FR1 = (1<<21); // all even IDs + CAN->FMR &= ~CAN_FMR_FINIT; /* (12) */ + CAN->IER |= CAN_IER_ERRIE | CAN_IER_FOVIE0 | CAN_IER_FOVIE1 | CAN_IER_BOFIE; /* (13) */ + + /* Configure IT */ + NVIC_SetPriority(USB_LP_CAN_RX0_IRQn, 0); // RX FIFO0 IRQ + NVIC_SetPriority(CAN_RX1_IRQn, 0); // RX FIFO1 IRQ + NVIC_SetPriority(CAN_SCE_IRQn, 0); // RX status changed IRQ + NVIC_EnableIRQ(USB_LP_CAN_RX0_IRQn); + NVIC_EnableIRQ(CAN_RX1_IRQn); + NVIC_EnableIRQ(CAN_SCE_IRQn); + CAN->MSR = 0; // clear SLAKI, WKUI, ERRI + can_status = CAN_READY; +} + +void CAN_printerr(){ + if(!last_err_code) last_err_code = CAN->ESR; + if(!last_err_code){ + USB_sendstr("No errors\n"); + return; + } + USB_sendstr("Receive error counter: "); + USB_sendstr(u2str((last_err_code & CAN_ESR_REC)>>24)); + USB_sendstr("\nTransmit error counter: "); + USB_sendstr(u2str((last_err_code & CAN_ESR_TEC)>>16)); + USB_sendstr("\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; + } + USB_sendstr(errmsg); USB_sendstr(" error\n"); + if(last_err_code & CAN_ESR_BOFF) USB_sendstr("Bus off "); + if(last_err_code & CAN_ESR_EPVF) USB_sendstr("Passive error limit "); + if(last_err_code & CAN_ESR_EWGF) USB_sendstr("Error counter limit"); + last_err_code = 0; + USB_putbyte('\n'); +} + +void CAN_proc(){ +#ifdef EBUG + if(last_err_code){ + USB_sendstr("Error, ESR="); + USB_sendstr(u2str(last_err_code)); + USB_putbyte('\n'); + 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 + USB_sendstr("\nToo much errors, restarting CAN!\n"); + CAN_printerr(); + // request abort for all mailboxes + CAN->TSR |= CAN_TSR_ABRQ0 | CAN_TSR_ABRQ1 | CAN_TSR_ABRQ2; + // reset CAN bus + RCC->APB1RSTR |= RCC_APB1RSTR_CANRST; + RCC->APB1RSTR &= ~RCC_APB1RSTR_CANRST; + CAN_setup(0); + } + static uint32_t lastFloodTime = 0; + static uint32_t incrmessagectr = 0; + if(flood_msg && (Tms - lastFloodTime) >= floodT){ // flood every ~5ms + lastFloodTime = Tms; + CAN_send(flood_msg); + }else if(incrflood && (Tms - lastFloodTime) >= floodT){ + lastFloodTime = Tms; + *(uint32_t*)flood_msg->data = incrmessagectr; + flood_msg->length = 4; + if(CAN_OK == CAN_send(flood_msg)) ++incrmessagectr; + } +} + +CAN_status CAN_send(CAN_message *message){ + if(!message) return CAN_ERR; + uint8_t *msg = message->data; + uint8_t len = message->length; + uint16_t target_id = message->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 + USB_sendstr("No free mailboxes\n"); +#endif + return CAN_BUSY; + } +#ifdef EBUG + USB_sendstr("Send data. Len="); USB_sendstr(u2str(len)); + USB_sendstr(", tagid="); USB_sendstr(u2str(target_id)); + USB_sendstr(", data="); + for(int i = 0; i < len; ++i){ + USB_sendstr(" "); USB_sendstr(uhex2str(msg[i])); + } + USB_putbyte('\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 CAN_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; + } +} + +uint32_t CAN_speed(){ + return oldspeed; +} + +/** + * @brief parseCANcommand - parser + * @param msg - incoming message @ my CANID + * FORMAT: + * 0 1 2 3 4 5 6 7 + * [CMD][PAR][errcode][VALUE] + * CMD - uint16_t, PAR - uint8_t, errcode - one of CAN_errcodes, VALUE - int32_t + * `errcode` of incoming message doesn't matter + * incoming data may have variable length + */ +TRUE_INLINE void parseCANcommand(CAN_message *msg){ + msg->ID = the_conf.CANID; // set own ID for broadcast messages + // check PING + if(msg->length != 0) run_can_cmd(msg); + int N = 1000; + while(CAN_BUSY == CAN_send(msg)) + if(--N == 0) break; +} + +static void can_process_fifo(uint8_t fifo_num){ + if(fifo_num > 1) return; + CAN_FIFOMailBox_TypeDef *box = &CAN->sFIFOMailBox[fifo_num]; + volatile uint32_t *RFxR = (fifo_num) ? &CAN->RF1R : &CAN->RF0R; +#ifdef EBUG + USB_sendstr(u2str(*RFxR & CAN_RF0R_FMP0)); USB_sendstr(" messages in FIFO #"); + USB_sendstr(u2str(fifo_num)); newline(); +#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(msg.ID == the_conf.CANID || msg.ID == 0) parseCANcommand(&msg); + if(CAN_messagebuf_push(&msg)) return; // error: buffer is full, try later + *RFxR |= CAN_RF0R_RFOM0; // release fifo for access to next message + } + //if(*RFxR & CAN_RF0R_FULL0) *RFxR &= ~CAN_RF0R_FULL0; + *RFxR = 0; // clear FOVR & FULL +} + +void usb_lp_can1_rx0_isr(){ // Rx FIFO0 (overrun) + if(CAN->RF0R & CAN_RF0R_FOVR0){ // FIFO overrun + CAN->RF0R &= ~CAN_RF0R_FOVR0; + can_status = CAN_FIFO_OVERRUN; + } +} + +void can1_rx1_isr(){ // Rx FIFO1 (overrun) + if(CAN->RF1R & CAN_RF1R_FOVR1){ + CAN->RF1R &= ~CAN_RF1R_FOVR1; + can_status = CAN_FIFO_OVERRUN; + } +} + +void can1_sce_isr(){ // status changed + if(CAN->MSR & CAN_MSR_ERRI){ // Error +#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/CANbus4BTA/can.h b/F3:F303/CANbus4BTA/can.h new file mode 100644 index 0000000..4414cda --- /dev/null +++ b/F3:F303/CANbus4BTA/can.h @@ -0,0 +1,60 @@ +/* + * This file is part of the canbus4bta project. + * Copyright 2024 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(uint32_t speed); + +CAN_status CAN_send(CAN_message *message); +void CAN_proc(); +void CAN_printerr(); + +CAN_message *CAN_messagebuf_pop(); + +void CAN_flood(CAN_message *msg, int incr); +uint32_t CAN_speed(); diff --git a/F3:F303/CANbus4BTA/canbus4bta.creator.user b/F3:F303/CANbus4BTA/canbus4bta.creator.user index 46759c5..332da69 100644 --- a/F3:F303/CANbus4BTA/canbus4bta.creator.user +++ b/F3:F303/CANbus4BTA/canbus4bta.creator.user @@ -1,6 +1,6 @@ - + EnvironmentId diff --git a/F3:F303/CANbus4BTA/canbus4bta.files b/F3:F303/CANbus4BTA/canbus4bta.files index 49e65a3..9930bb6 100644 --- a/F3:F303/CANbus4BTA/canbus4bta.files +++ b/F3:F303/CANbus4BTA/canbus4bta.files @@ -1,14 +1,21 @@ adc.c adc.h +can.c +can.h +commonfunctions.c +commonfunctions.h +flash.c +flash.h hardware.c hardware.h main.c -proto.c proto.h ringbuffer.c ringbuffer.h strfunc.c strfunc.h +textfunctions.c +textfunctions.h usart.c usart.h usb.c diff --git a/F3:F303/CANbus4BTA/commonfunctions.c b/F3:F303/CANbus4BTA/commonfunctions.c new file mode 100644 index 0000000..6dac9ed --- /dev/null +++ b/F3:F303/CANbus4BTA/commonfunctions.c @@ -0,0 +1,180 @@ +/* + * This file is part of the canbus4bta project. + * Copyright 2024 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 "adc.h" +#include "can.h" +#include "commonfunctions.h" +#include "flash.h" +#include "proto.h" +#include "usb.h" + +#define FIXDL(m) do{m->length = 8;}while(0) +/*********** START of all common functions list (for `funclist`) ***********/ +// reset MCU +static errcodes reset(CAN_message _U_ *msg){ + USB_sendstr("Soft reset\n"); + USB_sendall(); + NVIC_SystemReset(); + return ERR_OK; // never reached +} +// get/set Tms +static errcodes time_getset(CAN_message *msg){ + if(ISSETTER(msg->data)){ + Tms = *(uint32_t*)&msg->data[4]; + }else FIXDL(msg); + *(uint32_t*)&msg->data[4] = Tms; + return ERR_OK; +} +// get MCU T +static errcodes mcut(CAN_message *msg){ + FIXDL(msg); + *(uint32_t*)&msg->data[4] = (uint32_t)(10.f * getMCUtemp()); + return ERR_OK; +} +// get ADC raw values +static errcodes adcraw(CAN_message *msg){ + FIXDL(msg); + register uint8_t no = msg->data[2] & ~SETTER_FLAG; + if(no >= NUMBER_OF_ADC_CHANNELS) return ERR_BADPAR; + *(uint32_t*)&msg->data[4] = getADCval(no); + return ERR_OK; +} +// get ADC voltage +static errcodes adcv(CAN_message *msg){ + FIXDL(msg); + register uint8_t no = msg->data[2] & ~SETTER_FLAG; + if(no >= ADC_TSENS) return ERR_BADPAR; + float v = getADCvoltage(no) * the_conf.adcmul[no] * 100.f; + *(uint32_t*)&msg->data[4] = (uint32_t) v; // or float?? + return ERR_OK; +} +// get/set CAN speed +static errcodes canspeed(CAN_message *msg){ + if(ISSETTER(msg->data)){ + uint32_t spd = *(uint32_t*)&msg->data[4]; + CAN_setup(spd); + the_conf.CANspeed = CAN_speed(); + }else FIXDL(msg); + *(uint32_t*)&msg->data[4] = CAN_speed(); + return ERR_OK; +} +// get/set CAN ID +static errcodes canid(CAN_message *msg){ + if(ISSETTER(msg->data)){ + the_conf.CANID = *(uint32_t*)&msg->data[4]; + // CAN_setup(0); // setup with new ID + }else FIXDL(msg); + *(uint32_t*)&msg->data[4] = the_conf.CANID; + return ERR_OK; +} +// get/set ADC multiplier +static errcodes adcmul(CAN_message *msg){ + uint8_t no = msg->data[2] & ~SETTER_FLAG; + if(no >= ADC_TSENS) return ERR_BADPAR; + if(ISSETTER(msg->data)){ + the_conf.adcmul[no] = ((float)*(uint32_t*)&msg->data[4])/1000.f; + }else FIXDL(msg); + *(uint32_t*)&msg->data[4] = (uint32_t)(1000.f * the_conf.adcmul[no]); + return ERR_OK; +} +// save config +static errcodes saveconf(CAN_message _U_ *msg){ + if(0 == store_userconf()) return ERR_OK; + return ERR_CANTRUN; +} +// erase storage +static errcodes erasestor(CAN_message _U_ *msg){ + if(0 == erase_storage(-1)) return ERR_OK; + return ERR_CANTRUN; +} + +/************ END of all common functions list (for `funclist`) ************/ + + +typedef struct{ + errcodes (*fn)(CAN_message *msg); // function to run with can packet `msg` + int32_t minval; // minimal/maximal values of *(int32_t*)(&data[4]) - if minval != maxval + int32_t maxval; + uint8_t datalen; // nominal data length +} commonfunction; + +// list of common (CAN/USB) functions +// !!!!!!!!! Getters should set message length to 8 !!!!!!!!!!! +static const commonfunction funclist[CMD_AMOUNT] = { + [CMD_RESET] = {reset, 0, 0, 0}, + [CMD_TIME] = {time_getset, 0, 0, 0}, + [CMD_MCUTEMP] = {mcut, 0, 0, 0}, + [CMD_ADCRAW] = {adcraw, 0, 0, 3}, // need parno: 0..4 + [CMD_ADCV] = {adcv, 0, 0, 3}, // need parno: 0..3 + [CMD_CANSPEED] = {canspeed, 50, 3000, 0}, + [CMD_CANID] = {canid, 1, 0x7ff, 0}, + [CMD_ADCMUL] = {adcmul, 0, 0, 3}, // at least parno + [CMD_SAVECONF] = {saveconf, 0, 0, 0}, + [CMD_ERASESTOR] = {erasestor, 0, 0, 0}, +}; + + +/** + * FORMAT: + * 0 1 2 3 4 5 6 7 + * [CMD][PAR][errcode][VALUE] + * CMD - uint16_t, PAR - uint8_t, errcode - one of `errcodes`, VALUE - int32_t + */ + +/** + * @brief run_can_cmd - run common CAN/USB commands with limits checking + * @param msg - incoming message + */ +void run_can_cmd(CAN_message *msg){ + uint8_t datalen = msg->length; + uint8_t *data = msg->data; +#ifdef EBUG + DBG("Get data: "); + for(int i = 0; i < msg->length; ++i){ + USB_sendstr(uhex2str(msg->data[i])); USB_putbyte(' '); + } + for(int i = msg->length-1; i < 8; ++i) msg->data[i] = 0; + newline(); +#endif + if(datalen < 2){ + FORMERR(data, ERR_WRONGLEN); + return; + } + uint16_t idx = *(uint16_t*)data; + if(idx >= CMD_AMOUNT || funclist[idx].fn == NULL){ // bad command index + FORMERR(data, ERR_BADCMD); + return; + } + // check minimal length (2 or 3) + if(funclist[idx].datalen > datalen){ + FORMERR(data, ERR_WRONGLEN); return; + } + if(datalen > 3 && (data[2] & SETTER_FLAG)){ + // check setter's length + if(datalen != 8){ FORMERR(data, ERR_WRONGLEN); return; } + // check setter's values + if(funclist[idx].maxval != funclist[idx].minval){ + int32_t newval = *(int32_t*)&data[4]; + if(newval < funclist[idx].minval || newval > funclist[idx].maxval){ + FORMERR(data, ERR_BADVAL); return; + } + } + } + data[3] = funclist[idx].fn(msg); // set error field as result of function + data[2] &= ~SETTER_FLAG; // and clear setter flag +} diff --git a/F3:F303/CANbus4BTA/commonfunctions.h b/F3:F303/CANbus4BTA/commonfunctions.h new file mode 100644 index 0000000..c7609bb --- /dev/null +++ b/F3:F303/CANbus4BTA/commonfunctions.h @@ -0,0 +1,27 @@ +/* + * This file is part of the canbus4bta project. + * Copyright 2024 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 . + */ + +// common functions for USB and CAN commands + +#pragma once + +#include + +#include "can.h" + +void run_can_cmd(CAN_message *msg); diff --git a/F3:F303/CANbus4BTA/flash.c b/F3:F303/CANbus4BTA/flash.c new file mode 100644 index 0000000..94d8c52 --- /dev/null +++ b/F3:F303/CANbus4BTA/flash.c @@ -0,0 +1,196 @@ +/* + * This file is part of the multistepper project. + * Copyright 2023 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include // memcpy +#include "flash.h" +#include "strfunc.h" +#include "usb.h" + +// from ld script +extern const uint32_t __varsstart, _BLOCKSIZE; + +const uint32_t FLASH_blocksize = (uint32_t)&_BLOCKSIZE; + +// max amount of Config records stored (will be recalculate in flashstorage_init() +static uint32_t maxCnum = 1024 / sizeof(user_conf); // can't use blocksize here + + +#define USERCONF_INITIALIZER { \ + .userconf_sz = sizeof(user_conf) \ + ,.CANspeed = 100000 \ + ,.CANID = 0xaa \ + ,.adcmul[0] = 1.f \ + ,.adcmul[1] = 1.f \ + ,.adcmul[2] = 1.f \ + ,.adcmul[3] = 1.f \ + } + +static int write2flash(const void*, const void*, uint32_t); +// don't write `static` here, or get error: +// 'memcpy' forming offset 8 is out of the bounds [0, 4] of object '__varsstart' with type 'uint32_t' +const user_conf *Flash_Data = (const user_conf *)(&__varsstart); + +user_conf the_conf = USERCONF_INITIALIZER; + +int currentconfidx = -1; // index of current configuration + +/** + * @brief binarySearch - binary search in flash for last non-empty cell + * any struct searched should have its sizeof() @ the first field!!! + * @param l - left index + * @param r - right index (should be @1 less than last index!) + * @param start - starting address + * @param stor_size - size of structure to search + * @return index of non-empty cell or -1 + */ +static int binarySearch(int r, const uint8_t *start, int stor_size){ + int l = 0; + while(r >= l){ + int mid = l + (r - l) / 2; + const uint8_t *s = start + mid * stor_size; + if(*((const uint16_t*)s) == stor_size){ + if(*((const uint16_t*)(s + stor_size)) == 0xffff){ // next is free + return mid; + }else{ // element is to the right + l = mid + 1; + } + }else{ // element is to the left + r = mid - 1; + } + } + return -1; // not found +} + +/** + * @brief flashstorage_init - initialization of user conf storage + * run in once @ start + */ +void flashstorage_init(){ + if(FLASH_SIZE > 0 && FLASH_SIZE < 20000){ + uint32_t flsz = FLASH_SIZE * 1024; // size in bytes + flsz -= (uint32_t)(&__varsstart) - FLASH_BASE; + maxCnum = flsz / sizeof(user_conf); + } + // -1 if there's no data at all & flash is clear; maxnum-1 if flash is full + currentconfidx = binarySearch((int)maxCnum-2, (const uint8_t*)Flash_Data, sizeof(user_conf)); + if(currentconfidx > -1){ + memcpy(&the_conf, &Flash_Data[currentconfidx], sizeof(user_conf)); + } +} + +// store new configuration +// @return 0 if all OK +int store_userconf(){ + // maxnum - 3 means that there always should be at least one empty record after last data + // for binarySearch() checking that there's nothing more after it! + if(currentconfidx > (int)maxCnum - 3){ // there's no more place + currentconfidx = 0; + if(erase_storage(-1)) return 1; + }else ++currentconfidx; // take next data position (0 - within first run after firmware flashing) + return write2flash((const void*)&Flash_Data[currentconfidx], &the_conf, sizeof(the_conf)); +} + +static int write2flash(const void *start, const void *wrdata, uint32_t stor_size){ + int ret = 0; + if (FLASH->CR & FLASH_CR_LOCK){ // unloch flash + FLASH->KEYR = FLASH_KEY1; + FLASH->KEYR = FLASH_KEY2; + } + while(FLASH->SR & FLASH_SR_BSY) IWDG->KR = IWDG_REFRESH; + FLASH->SR = FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPERR; // clear all flags + FLASH->CR |= FLASH_CR_PG; + const uint16_t *data = (const uint16_t*) wrdata; + volatile uint16_t *address = (volatile uint16_t*) start; + USB_sendstr("Start address="); printuhex((uint32_t)start); newline(); + uint32_t i, count = (stor_size + 1) / 2; + for(i = 0; i < count; ++i){ + IWDG->KR = IWDG_REFRESH; + *(volatile uint16_t*)(address + i) = data[i]; + while (FLASH->SR & FLASH_SR_BSY) IWDG->KR = IWDG_REFRESH; + if(*(volatile uint16_t*)(address + i) != data[i]){ + USB_sendstr("DON'T MATCH!\n"); + ret = 1; + break; + } +#ifdef EBUG + else{ USB_sendstr("Written "); printuhex(data[i]); newline();} +#endif + if(FLASH->SR & FLASH_SR_PGERR){ + USB_sendstr("Prog err\n"); + ret = 1; // program error - meet not 0xffff + break; + } + FLASH->SR = FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPERR; + } + FLASH->CR &= ~(FLASH_CR_PG); + return ret; +} + +// erase Nth page of flash storage (flash should be prepared!) +static int erase_pageN(int N){ + int ret = 0; +#ifdef EBUG + USB_sendstr("Erase block #"); printu(N); newline(); +#endif + FLASH->AR = (uint32_t)Flash_Data + N*FLASH_blocksize; + FLASH->CR |= FLASH_CR_STRT; + while(FLASH->SR & FLASH_SR_BSY) IWDG->KR = IWDG_REFRESH; + FLASH->SR = FLASH_SR_EOP; + if(FLASH->SR & FLASH_SR_WRPERR){ /* Check Write protection error */ + ret = 1; + FLASH->SR = FLASH_SR_WRPERR; /* Clear the flag by software by writing it at 1*/ + } + return ret; +} + +// erase full storage (npage < 0) or its nth page; @return 0 if all OK +int erase_storage(int npage){ + int ret = 0; + uint32_t end = 1, start = 0, flsz = 0; + if(FLASH_SIZE > 0 && FLASH_SIZE < 20000){ + flsz = FLASH_SIZE * 1024; // size in bytes + flsz -= (uint32_t)Flash_Data - FLASH_BASE; + } + end = flsz / FLASH_blocksize; + if(end == 0 || end >= FLASH_SIZE) return 1; + if(npage > -1){ // erase only one page + if((uint32_t)npage >= end) return 1; + start = npage; + end = start + 1; + } + if((FLASH->CR & FLASH_CR_LOCK) != 0){ + FLASH->KEYR = FLASH_KEY1; + FLASH->KEYR = FLASH_KEY2; + } + /*USB_sendstr("size/block size/nblocks/FLASH_SIZE: "); printu(flsz); + USB_putbyte('/'); printu(FLASH_blocksize); USB_putbyte('/'); + printu(nblocks); USB_putbyte('/'); printu(FLASH_SIZE); newline(); USB_sendall();*/ + while(FLASH->SR & FLASH_SR_BSY) IWDG->KR = IWDG_REFRESH; + FLASH->SR = FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPERR; + FLASH->CR |= FLASH_CR_PER; + for(uint32_t i = start; i < end; ++i){ + if(erase_pageN(i)){ + ret = 1; + break; + } + } + FLASH->CR &= ~FLASH_CR_PER; + return ret; +} + diff --git a/F3:F303/CANbus4BTA/flash.h b/F3:F303/CANbus4BTA/flash.h new file mode 100644 index 0000000..4ce7036 --- /dev/null +++ b/F3:F303/CANbus4BTA/flash.h @@ -0,0 +1,49 @@ +/* + * This file is part of the canbus4bta project. + * Copyright 2024 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 "adc.h" +#include "hardware.h" + +// register with flash size (in blocks) +#ifndef FLASH_SIZE_BASE +#define FLASH_SIZE_BASE ((uint32_t)0x1FFFF7CC) +#endif + +#define FLASH_SIZE *((uint16_t*)FLASH_SIZE_BASE) + +/* + * struct to save user configurations + */ +typedef struct __attribute__((packed, aligned(4))){ + uint16_t userconf_sz; // "magick number" + uint32_t CANspeed; // default CAN speed + uint16_t CANID; // identifier + float adcmul[ADC_TSENS]; // ADC voltage multipliers +} user_conf; + +extern user_conf the_conf; // global user config (read from FLASH to RAM) +// these variables/constants need for dumpconf() +extern const user_conf *Flash_Data; +extern const uint32_t FLASH_blocksize; +extern int currentconfidx; + +void flashstorage_init(); +int store_userconf(); +int erase_storage(int npage); diff --git a/F3:F303/CANbus4BTA/main.c b/F3:F303/CANbus4BTA/main.c index 9da95fb..b4cce98 100644 --- a/F3:F303/CANbus4BTA/main.c +++ b/F3:F303/CANbus4BTA/main.c @@ -16,8 +16,10 @@ * along with this program. If not, see . */ +#include "can.h" +#include "flash.h" #include "hardware.h" -#include "proto.h" +#include "textfunctions.h" #include "usart.h" #include "usb.h" @@ -38,24 +40,30 @@ int main(void){ StartHSI(); SysTick_Config((uint32_t)48000); // 1ms } + flashstorage_init(); hw_setup(); usart_setup(); USB_setup(); + CAN_setup(the_conf.CANspeed); USBPU_ON(); while(1){ + CAN_proc(); + if(CAN_get_status() == CAN_FIFO_OVERRUN){ + USB_sendstr("CAN bus fifo overrun occured!\n"); + } if(bufovr){ bufovr = 0; usart_send("bufovr\n"); } char *txt = NULL; if(usart_getline(&txt)){ - const char *ans = parse_cmd(txt); + const char *ans = run_text_cmd(txt); if(ans) usart_send(ans); } int l = USB_receivestr(inbuff, MAXSTRLEN); - if(l < 0) USB_sendstr("ERROR: USB buffer overflow or string was too long\n"); + if(l < 0) USB_sendstr("error=overflow\n"); else if(l){ - const char *ans = parse_cmd(inbuff); + const char *ans = run_text_cmd(inbuff); if(ans) USB_sendstr(ans); } } diff --git a/F3:F303/CANbus4BTA/proto.h b/F3:F303/CANbus4BTA/proto.h index 1261771..e76a122 100644 --- a/F3:F303/CANbus4BTA/proto.h +++ b/F3:F303/CANbus4BTA/proto.h @@ -16,9 +16,50 @@ * along with this program. If not, see . */ +/** + * USB-only and common (CAN/USB) protocol functions parser + */ + #pragma once #include -const char *parse_cmd(const char *buf); +#ifndef _U_ +#define _U_ __attribute__((__unused__)) +#endif + +// command parameter flag means this is a setter +#define SETTER_FLAG (0x80) +#define ISSETTER(data) ((data[2] & SETTER_FLAG)) +// parameter number 127 means there no parameter number at all (don't need paremeter or get all) +#define NO_PARNO (0x1f) + +// make error for CAN answer +#define FORMERR(data, err) do{data[3] = err;}while(0) + +// error codes for answer message +typedef enum{ + ERR_OK, // 0 - all OK + ERR_BADPAR, // 1 - parameter number is wrong + ERR_BADVAL, // 2 - wrong parameter's value + ERR_WRONGLEN, // 3 - wrong message length + ERR_BADCMD, // 4 - unknown command + ERR_CANTRUN, // 5 - can't run given command due to bad parameters or other problems + ERR_AMOUNT // amount of error codes +} errcodes; + +// CAN commands indexes +typedef enum{ + CMD_RESET, // 0 - reset MCU + CMD_TIME, // 1 - get/set Tms + CMD_MCUTEMP, // 2 - get MCU temperature (*10) + CMD_ADCRAW, // 3 - get ADC raw values + CMD_ADCV, // 4 - get ADC voltage (*100) + CMD_CANSPEED, // 5 - get/set CAN speed (kbps) + CMD_CANID, // 6 - get/set CAN ID + CMD_ADCMUL, // 7 - get/set ADC multipliers 0..4 + CMD_SAVECONF, // 8 - save configuration + CMD_ERASESTOR, // 9 - erase all flash storage + CMD_AMOUNT // amount of CAN commands +} can_cmd; diff --git a/F3:F303/CANbus4BTA/strfunc.h b/F3:F303/CANbus4BTA/strfunc.h index 8019c2f..6eb2073 100644 --- a/F3:F303/CANbus4BTA/strfunc.h +++ b/F3:F303/CANbus4BTA/strfunc.h @@ -29,3 +29,4 @@ char *float2str(float x, uint8_t prec); 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); + diff --git a/F3:F303/CANbus4BTA/textfunctions.c b/F3:F303/CANbus4BTA/textfunctions.c new file mode 100644 index 0000000..7985a7d --- /dev/null +++ b/F3:F303/CANbus4BTA/textfunctions.c @@ -0,0 +1,246 @@ +/* + * This file is part of the canbus4bta project. + * Copyright 2024 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 "commonfunctions.h" +#include "flash.h" +#include "proto.h" +#include "strfunc.h" +#include "textfunctions.h" +#include "usb.h" +#include "version.inc" + +// maximal length of command +#define MAXCMDLEN (16) + +// text-only commans indexes (0 is prohibited) +typedef enum{ + TCMD_PROHIBITED + ,TCMD_WDTEST + ,TCMD_DUMPCONF + ,TCMD_AMOUNT +} text_cmd; + + +typedef struct{ + const char *cmd; // command, if NULL - only display help message + int idx; // index in CAN cmd or text cmd list (if negative) + const char *help; // help message +} funcdescr; + +// list of all text functions; should be sorted and can be grouped +static const funcdescr funclist[] = { + {"adcmul", CMD_ADCMUL, "get/set ADC multipliers 0..4 (*1000)"}, + {"adcraw", CMD_ADCRAW, "get raw ADC values of channel 0..4"}, + {"adcv", CMD_ADCV, "get ADC voltage of channel 0..3 (*100V)"}, + {"canid", CMD_CANID, "get/set CAN ID"}, + {"canspeed", CMD_CANSPEED, "get/set CAN speed (bps)"}, + {"dumpconf", -TCMD_DUMPCONF, "dump current configuration"}, + {"eraseflash", CMD_ERASESTOR, "erase all flash storage"}, + {"mcutemp", CMD_MCUTEMP, "get MCU temperature (*10degrC)"}, + {"reset", CMD_RESET, "reset MCU"}, + {"saveconf", CMD_SAVECONF, "save configuration"}, + {"time", CMD_TIME, "get/set time (ms)"}, + {"wdtest", -TCMD_WDTEST, "test watchdog"}, + {NULL, 0, NULL} // last record +}; + +/*********** START of all common functions list (for `textfunctions`) ***********/ + +static errcodes wdtest(const char _U_ *str){ + USB_sendstr("Wait for reboot\n"); + while(1){nop();} + return ERR_OK; +} + +static errcodes dumpconf(const char _U_ *str){ +#ifdef EBUG + USB_sendstr("flashsize="); printu(FLASH_SIZE); USB_putbyte('*'); + printu(FLASH_blocksize); USB_putbyte('='); printu(FLASH_SIZE*FLASH_blocksize); + newline(); +#endif + USB_sendstr("userconf_addr="); printuhex((uint32_t)Flash_Data); + USB_sendstr("\nuserconf_idx="); printi(currentconfidx); + USB_sendstr("\nuserconf_sz="); printu(the_conf.userconf_sz); + USB_sendstr("\ncanspeed="); printu(the_conf.CANspeed); + USB_sendstr("\ncanid="); printu(the_conf.CANID); + for(int i = 0; i < ADC_TSENS; ++i){ + USB_sendstr("\nadcmul"); USB_putbyte('0'+i); USB_putbyte('='); + USB_sendstr(float2str(the_conf.adcmul[i], 3)); + } +//#define PROPNAME(nm) do{newline(); USB_sendstr(nm); USB_putbyte(cur); USB_putbyte('=');}while(0) +//#undef PROPNAME + newline(); + return ERR_OK; +} + +/************ END of all common functions list (for `textfunctions`) ************/ + +// in `textfn` arg `str` is rest of input string (spaces-omitted) after command +typedef errcodes (*textfn)(const char *str); +// array of text-only functions +static textfn textfunctions[TCMD_AMOUNT] = { + [TCMD_PROHIBITED] = NULL + ,[TCMD_WDTEST] = wdtest + ,[TCMD_DUMPCONF] = dumpconf +}; + +static char stbuf[256], *bptr = NULL; +static int blen = 0; +static void initbuf(){bptr = stbuf; blen = 255; *bptr = 0;} +static void bufputchar(char c){ + if(blen == 0) return; + *bptr++ = c; --blen; + *bptr = 0; +} +static void add2buf(const char *s){ + while(blen && *s){ + *bptr++ = *s++; + --blen; + } + *bptr = 0; +} + +static void printhelp(){ + const funcdescr *c = funclist; + USB_sendstr("https://github.com/eddyem/stm32samples/tree/master/F3:F303/CANbus4BTA build#" BUILD_NUMBER " @ " BUILD_DATE "\n"); + USB_sendstr("commands format: parameter[number][=setter]"); + USB_sendstr("parameter [CAN idx] - help\n"); + USB_sendstr("--------------------------\n"); + while(c->help){ + if(!c->cmd){ // header + USB_sendstr("\n "); + USB_sendstr(c->help); + USB_putbyte(':'); + }else{ + USB_sendstr(c->cmd); + if(c->idx > -1){ + USB_sendstr(" ["); + USB_sendstr(u2str(c->idx)); + USB_putbyte(']'); + } + USB_sendstr(" - "); + USB_sendstr(c->help); + } + newline(); + ++c; + } +} + +extern uint8_t usbON; + +static const char* const errors_txt[ERR_AMOUNT] = { + [ERR_OK] = "OK" + ,[ERR_BADPAR] = "badpar" + ,[ERR_BADVAL] = "badval" + ,[ERR_WRONGLEN] = "wronglen" + ,[ERR_BADCMD] = "badcmd" + ,[ERR_CANTRUN] = "cantrun" +}; + +static void errtext(errcodes e){ + add2buf("error="); + add2buf(errors_txt[e]); + bufputchar('\n'); +} + +/** + * @brief run_text_cmd - run text-only command + * @param str - command string + * @return NULL or string to send + * WARNING! Sending help works only for USB! + */ +char *run_text_cmd(const char *str){ + char cmd[MAXCMDLEN + 1]; + errcodes ecode = ERR_BADCMD; + if(!str || !*str) goto ret; + int idx = CMD_AMOUNT; + const funcdescr *c = funclist; + int l = 0; + str = omit_spaces(str); + const char *ptr = str; + while(*ptr > '@' && l < MAXCMDLEN){ cmd[l++] = *ptr++;} + if(l == 0) goto ret; + cmd[l] = 0; + while(c->help){ + if(!c->cmd) continue; + if(0 == strcmp(c->cmd, cmd)){ + idx = c->idx; + break; + } + } + if(idx == CMD_AMOUNT){ // didn't found + // send help over USB + printhelp(); + goto ret; + } + initbuf(); + str = omit_spaces(ptr); + if(idx < 0){ // text-only function + ecode = textfunctions[-idx](str); + goto ret; + } + // common CAN/text function: we need to form 8-byte data buffer + CAN_message msg; + bzero(&msg, sizeof(msg)); + uint8_t *data = msg.data; + uint8_t datalen = 2; // only command for start + *((uint16_t*)data) = (uint16_t)idx; + data[2] = NO_PARNO; // no parameter number by default + if(*str >= '0' && *str <= '9'){ // have parameter with number + uint32_t N; + ptr = getnum(str, &N); + if(ptr != str){ + str = ptr; + if(N <= 0x7F) data[2] = (uint8_t)N; + else{ ecode = ERR_BADPAR; goto ret; } + } + datalen = 3; + } + str = omit_spaces(str); + if(*str == '='){ // setter + ptr = getint(str, ((int32_t*)&data[4])); + if(str == ptr){ + ecode = ERR_BADVAL; + goto ret; + } + data[2] |= SETTER_FLAG; + datalen = 8; + } + msg.length = datalen; + run_can_cmd(&msg); + // now check error code + ecode = data[3]; +ret: + if(ecode != ERR_OK) errtext(ecode); + else if(idx > -1){ // parce all back for common functions + if(msg.length != 8){ + return "OK\n"; // non setters/getters will just print "OK" if all OK + }else{ + add2buf(funclist[idx].cmd); + data[2] &= ~SETTER_FLAG; + if(data[2] != NO_PARNO) add2buf(u2str(data[2])); + bufputchar('='); + add2buf(i2str(*(int32_t*)&data[4])); + bufputchar('\n'); + } + } + return stbuf; +} diff --git a/F3:F303/CANbus4BTA/textfunctions.h b/F3:F303/CANbus4BTA/textfunctions.h new file mode 100644 index 0000000..0242d05 --- /dev/null +++ b/F3:F303/CANbus4BTA/textfunctions.h @@ -0,0 +1,28 @@ +/* + * This file is part of the canbus4bta project. + * Copyright 2024 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 . + */ + +// USB-only functions such as CAN-USB gateway +// also it can be any other serial text protocol (such as RS-422) +#pragma once + +#include + +#include "proto.h" + +// run text protocol command +char *run_text_cmd(const char *str); diff --git a/F3:F303/CANbus4BTA/usb.h b/F3:F303/CANbus4BTA/usb.h index 3e4ea3f..18fc024 100644 --- a/F3:F303/CANbus4BTA/usb.h +++ b/F3:F303/CANbus4BTA/usb.h @@ -47,3 +47,11 @@ int USB_putbyte(uint8_t byte); int USB_sendstr(const char *string); int USB_receive(uint8_t *buf, int len); int USB_receivestr(char *buf, int len); + +#ifdef EBUG +#include "strfunc.h" +#define printu(x) do{USB_sendstr(u2str(x));}while(0) +#define printi(x) do{USB_sendstr(i2str(x));}while(0) +#define printuhex(x) do{USB_sendstr(uhex2str(x));}while(0) +#define printf(x) do{USB_sendstr(float2str(x, 2));}while(0) +#endif diff --git a/F3:F303/CANbus4BTA/version.inc b/F3:F303/CANbus4BTA/version.inc index 2d17ece..f01306f 100644 --- a/F3:F303/CANbus4BTA/version.inc +++ b/F3:F303/CANbus4BTA/version.inc @@ -1,2 +1,2 @@ -#define BUILD_NUMBER "9" -#define BUILD_DATE "2024-01-05" +#define BUILD_NUMBER "22" +#define BUILD_DATE "2024-01-07"