From f127a3ce1a20a7f0f19c8509a3d17df0c91952cf Mon Sep 17 00:00:00 2001 From: Edward Emelianov Date: Sat, 1 Jun 2024 22:23:11 +0300 Subject: [PATCH] FX3U: test CAN bus communications --- .../usbcan/kicad/stm32.kicad_prl | 6 + .../usbcan/kicad/stm32.kicad_pro | 74 ++++ F1:F103/FX3U/adc.c | 12 +- F1:F103/FX3U/adc.h | 19 +- F1:F103/FX3U/can.c | 154 ++++++-- F1:F103/FX3U/can.h | 21 +- F1:F103/FX3U/canproto.c | 248 +++++++++++++ F1:F103/FX3U/canproto.h | 75 ++++ F1:F103/FX3U/flash.c | 18 +- F1:F103/FX3U/flash.h | 12 +- F1:F103/FX3U/fx3u.bin | Bin 7064 -> 14032 bytes F1:F103/FX3U/fx3u.creator.user | 2 +- F1:F103/FX3U/fx3u.files | 2 + F1:F103/FX3U/main.c | 14 +- F1:F103/FX3U/proto.c | 344 +++++++++++++----- F1:F103/FX3U/proto.h | 26 +- F1:F103/FX3U/strfunc.h | 11 + F1:F103/FX3U/version.inc | 4 +- 18 files changed, 853 insertions(+), 189 deletions(-) create mode 100644 F1:F103/FX3U/canproto.c create mode 100644 F1:F103/FX3U/canproto.h diff --git a/F0:F030,F042,F072/usbcan/kicad/stm32.kicad_prl b/F0:F030,F042,F072/usbcan/kicad/stm32.kicad_prl index 09d6bbb..3371732 100644 --- a/F0:F030,F042,F072/usbcan/kicad/stm32.kicad_prl +++ b/F0:F030,F042,F072/usbcan/kicad/stm32.kicad_prl @@ -67,6 +67,12 @@ "visible_layers": "fffffff_ffffffff", "zone_display_mode": 0 }, + "git": { + "repo_password": "", + "repo_type": "", + "repo_username": "", + "ssh_key": "" + }, "meta": { "filename": "stm32.kicad_prl", "version": 3 diff --git a/F0:F030,F042,F072/usbcan/kicad/stm32.kicad_pro b/F0:F030,F042,F072/usbcan/kicad/stm32.kicad_pro index e2ed6ea..7235be6 100644 --- a/F0:F030,F042,F072/usbcan/kicad/stm32.kicad_pro +++ b/F0:F030,F042,F072/usbcan/kicad/stm32.kicad_pro @@ -187,6 +187,13 @@ "zones_allow_external_fillets": false, "zones_use_no_outline": true }, + "ipc2581": { + "dist": "", + "distpn": "", + "internal_id": "", + "mfg": "", + "mpn": "" + }, "layer_presets": [], "viewports": [] }, @@ -588,14 +595,75 @@ "gencad": "", "idf": "", "netlist": "stm32.net", + "plot": "", + "pos_files": "", "specctra_dsn": "", "step": "", + "svg": "", "vrml": "" }, "page_layout_descr_file": "" }, "schematic": { "annotate_start_num": 0, + "bom_fmt_presets": [], + "bom_fmt_settings": { + "field_delimiter": ",", + "keep_line_breaks": false, + "keep_tabs": false, + "name": "CSV", + "ref_delimiter": ",", + "ref_range_delimiter": "", + "string_delimiter": "\"" + }, + "bom_presets": [], + "bom_settings": { + "exclude_dnp": false, + "fields_ordered": [ + { + "group_by": false, + "label": "Reference", + "name": "Reference", + "show": true + }, + { + "group_by": true, + "label": "Value", + "name": "Value", + "show": true + }, + { + "group_by": false, + "label": "Datasheet", + "name": "Datasheet", + "show": true + }, + { + "group_by": false, + "label": "Footprint", + "name": "Footprint", + "show": true + }, + { + "group_by": false, + "label": "Qty", + "name": "${QUANTITY}", + "show": true + }, + { + "group_by": true, + "label": "DNP", + "name": "${DNP}", + "show": true + } + ], + "filter_string": "", + "group_symbols": true, + "name": "Grouped By Value", + "sort_asc": true, + "sort_field": "Обозначение" + }, + "connection_grid_size": 50.0, "drawing": { "dashed_lines_dash_length_ratio": 12.0, "dashed_lines_gap_length_ratio": 3.0, @@ -612,6 +680,11 @@ "intersheets_ref_suffix": "", "junction_size_choice": 3, "label_size_ratio": 0.3, + "operating_point_overlay_i_precision": 3, + "operating_point_overlay_i_range": "~A", + "operating_point_overlay_v_precision": 3, + "operating_point_overlay_v_range": "~V", + "overbar_offset_ratio": 1.23, "pin_symbol_size": 25.0, "text_offset_ratio": 0.3 }, @@ -637,6 +710,7 @@ "spice_external_command": "spice \"%I\"", "spice_model_current_sheet_as_root": true, "spice_save_all_currents": false, + "spice_save_all_dissipations": false, "spice_save_all_voltages": false, "subpart_first_id": 65, "subpart_id_separator": 0 diff --git a/F1:F103/FX3U/adc.c b/F1:F103/FX3U/adc.c index ee10071..466e7e0 100644 --- a/F1:F103/FX3U/adc.c +++ b/F1:F103/FX3U/adc.c @@ -18,7 +18,7 @@ #include "adc.h" -uint16_t ADC_array[NUMBER_OF_ADC_CHANNELS*9]; +uint16_t ADC_array[ADC_CHANNELS*9]; void adc_setup(){ uint32_t ctr = 0; @@ -29,7 +29,7 @@ void adc_setup(){ // DMA configuration DMA1_Channel1->CPAR = (uint32_t) (&(ADC1->DR)); DMA1_Channel1->CMAR = (uint32_t)(ADC_array); - DMA1_Channel1->CNDTR = NUMBER_OF_ADC_CHANNELS * 9; + DMA1_Channel1->CNDTR = ADC_CHANNELS * 9; DMA1_Channel1->CCR = DMA_CCR_MINC | DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_0 | DMA_CCR_CIRC | DMA_CCR_PL | DMA_CCR_EN; RCC->CFGR = (RCC->CFGR & ~(RCC_CFGR_ADCPRE)) | RCC_CFGR_ADCPRE_DIV8; // ADC clock = RCC / 8 @@ -38,7 +38,7 @@ void adc_setup(){ ADC1->SMPR1 = ADC_SMPR1_SMP16 | ADC_SMPR1_SMP17; // sequence order: 0 -> 16 -> 17 ADC1->SQR3 = (0 << 0) | (16<<5) | (17 << 10); - ADC1->SQR1 = (NUMBER_OF_ADC_CHANNELS - 1) << 20; // amount of conversions + ADC1->SQR1 = (ADC_CHANNELS - 1) << 20; // amount of conversions ADC1->CR1 = ADC_CR1_SCAN; // scan mode // DMA, continuous mode; enable vref & Tsens; enable SWSTART as trigger ADC1->CR2 = ADC_CR2_DMA | ADC_CR2_TSVREFE | ADC_CR2_CONT | ADC_CR2_EXTSEL | ADC_CR2_EXTTRIG; @@ -68,7 +68,7 @@ uint16_t getADCval(int nch){ #define PIX_SORT(a,b) { if ((a)>(b)) PIX_SWAP((a),(b)); } #define PIX_SWAP(a,b) { register uint16_t temp=(a);(a)=(b);(b)=temp; } uint16_t p[9]; - for(i = 0; i < 9; ++i, addr += NUMBER_OF_ADC_CHANNELS){ // first we should prepare array for optmed + for(i = 0; i < 9; ++i, addr += ADC_CHANNELS){ // first we should prepare array for optmed p[i] = ADC_array[addr]; } PIX_SORT(p[1], p[2]) ; PIX_SORT(p[4], p[5]) ; PIX_SORT(p[7], p[8]) ; @@ -95,7 +95,7 @@ uint32_t getADCvoltage(int nch){ int32_t getMCUtemp(){ // Temp = (V25 - Vsense)/Avg_Slope + 25 // V_25 = 1.45V, Slope = 4.3e-3 - int32_t Vsense = getVdd() * getADCval(CHTSENS); + int32_t Vsense = getVdd() * getADCval(ADC_CH_TSEN); int32_t temperature = 593920 - Vsense; // 593920 == 145*4096 temperature /= 172; // == /(4096*10*4.3e-3), 10 - to convert from *100 to *10 temperature += 250; @@ -105,6 +105,6 @@ int32_t getMCUtemp(){ // return Vdd * 100 (V) uint32_t getVdd(){ uint32_t vdd = 120 * 4096; // 1.2V - vdd /= getADCval(CHVDD); + vdd /= getADCval(ADC_CH_VDD); return vdd; } diff --git a/F1:F103/FX3U/adc.h b/F1:F103/FX3U/adc.h index b3539cd..cf7b82b 100644 --- a/F1:F103/FX3U/adc.h +++ b/F1:F103/FX3U/adc.h @@ -20,19 +20,16 @@ #include -#define NUMBER_OF_ADC_CHANNELS (3) - -// Vsen voltage channel -#define CHVSEN (0) -// channels for Tsens and Vdd -#define CHTSENS (1) -#define CHVDD (2) +// ADC channels in array +enum{ + ADC_CH_VSEN = 0, // ADC_ch0 + ADC_CH_TSEN, // T sensor + ADC_CH_VDD, // Vdd sensor + ADC_CHANNELS +}; /** - * @brief ADC_array - array for ADC channels with median filtering: - * 0 - Isensor voltage - * 1 - internal Tsens - * 2 - Vdd + * @brief ADC_array - array for ADC channels with median filtering */ extern uint16_t ADC_array[]; diff --git a/F1:F103/FX3U/can.c b/F1:F103/FX3U/can.c index ff837d4..1c1bb21 100644 --- a/F1:F103/FX3U/can.c +++ b/F1:F103/FX3U/can.c @@ -17,19 +17,31 @@ */ #include "can.h" +#include "canproto.h" +#include "flash.h" // CANID #include "hardware.h" #include "proto.h" +#include "strfunc.h" #include "usart.h" // REMAPPED to PD0/PD1!!! #include // memcpy +// CAN bus oscillator frequency: 36MHz +#define CAN_F_OSC (36000000UL) +// timing values TBS1 and TBS2 (in BTR [TBS1-1] and [TBS2-1]) +// use 3 and 2 to get 6MHz +#define CAN_TBS1 (3) +#define CAN_TBS2 (2) +// bitrate oscillator frequency +#define CAN_BIT_OSC (CAN_F_OSC / (1+CAN_TBS1+CAN_TBS2)) + // circular buffer for received messages static CAN_message messages[CAN_INMESSAGE_SIZE]; static uint8_t first_free_idx = 0; // index of first empty cell static int8_t first_nonfree_idx = -1; // index of first data cell -static uint16_t oldspeed = 100; // speed of last init +static uint32_t oldspeed = 100000; // speed of last init static CAN_status can_status = CAN_STOP; @@ -44,9 +56,11 @@ CAN_status CAN_get_status(){ // 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 usart_send("push\n"); #endif +*/ if(first_free_idx == first_nonfree_idx){ #ifdef EBUG usart_send("INBUF OVERFULL\n"); @@ -72,7 +86,7 @@ CAN_message *CAN_messagebuf_pop(){ return msg; } -void CAN_reinit(uint16_t speed){ +void CAN_reinit(uint32_t speed){ CAN1->TSR |= CAN_TSR_ABRQ0 | CAN_TSR_ABRQ1 | CAN_TSR_ABRQ2; RCC->APB1RSTR |= RCC_APB1RSTR_CAN1RST; RCC->APB1RSTR &= ~RCC_APB1RSTR_CAN1RST; @@ -100,12 +114,11 @@ so if TBS1=4 and TBS2=3, sum=8, bit sampling freq is 48/8 = 6MHz 1MBps - 6 */ -// speed - in kbps -void CAN_setup(uint16_t speed){ +// speed - in bps +void CAN_setup(uint32_t speed){ if(speed == 0) speed = oldspeed; - else if(speed < 50) speed = 50; - else if(speed > 3000) speed = 3000; - oldspeed = speed; + else if(speed < CAN_MIN_SPEED) speed = CAN_MIN_SPEED; + else if(speed > CAN_MAX_SPEED) speed = CAN_MAX_SPEED; uint32_t tmout = 16000000; // Configure GPIO: PD0 - CAN_Rx, PD1 - CAN_Tx AFIO->MAPR |= AFIO_MAPR_CAN_REMAP_REMAP3; @@ -122,7 +135,7 @@ void CAN_setup(uint16_t speed){ /* (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 */ + /* (9) Identifier mode for bank#0, mask mode for #1 */ /* (10) Set the Id list */ /* (12) Leave filter init */ /* (13) Set error interrupts enable (& bus off) */ @@ -131,19 +144,26 @@ void CAN_setup(uint16_t speed){ if(--tmout == 0) break; CAN1->MCR &=~ CAN_MCR_SLEEP; /* (3) */ CAN1->MCR |= CAN_MCR_ABOM; /* allow automatically bus-off */ - - CAN1->BTR = 2 << 20 | 3 << 16 | (4500/speed - 1); //| CAN_BTR_SILM | CAN_BTR_LBKM; /* (4) */ + CAN1->BTR = (CAN_TBS2-1) << 20 | (CAN_TBS1-1) << 16 | (CAN_BIT_OSC/speed - 1); //| CAN_BTR_SILM | CAN_BTR_LBKM; /* (4) */ + oldspeed = CAN_BIT_OSC/(uint32_t)((CAN1->BTR & CAN_BTR_BRP) + 1); +#ifdef EBUG + usart_send("canspeed->"); usart_send(u2str(oldspeed)); newline(); +#endif CAN1->MCR &= ~CAN_MCR_INRQ; /* (5) */ tmout = 16000000; while((CAN1->MSR & CAN_MSR_INAK) == CAN_MSR_INAK) /* (6) */ if(--tmout == 0) break; - // accept ALL + // accept depending of monitor flag CAN1->FMR = CAN_FMR_FINIT; /* (7) */ - CAN1->FA1R = CAN_FA1R_FACT0 | CAN_FA1R_FACT1; /* (8) */ - // set to 1 all needed bits of CAN1->FFA1R to switch given filters to FIFO1 - CAN1->sFilterRegister[0].FR1 = (1<<21)|(1<<5); // all odd IDs - CAN1->FFA1R = 2; // filter 1 for FIFO1, filter 0 - for FIFO0 - CAN1->sFilterRegister[1].FR1 = (1<<21); // all even IDs + CAN1->FA1R = CAN_FA1R_FACT0; /* (8) */ + CAN1->FM1R = CAN_FM1R_FBM0; + // filter 0 for FIFO0 + CAN1->sFilterRegister[0].FR1 = the_conf.CANID << 5; // (10) CANID and 0 + if(flags.can_monitor){ /* (11) */ + CAN1->FA1R |= CAN_FA1R_FACT1; // activate filter1 + CAN1->sFilterRegister[1].FR1 = 0; // all packets + CAN1->FFA1R = 2; // filter 1 for FIFO1 + } CAN1->FMR &= ~CAN_FMR_FINIT; /* (12) */ CAN1->IER |= CAN_IER_ERRIE | CAN_IER_FOVIE0 | CAN_IER_FOVIE1 | CAN_IER_BOFIE; /* (13) */ @@ -158,7 +178,45 @@ void CAN_setup(uint16_t speed){ can_status = CAN_READY; } -void can_proc(){ +/** + * @brief CAN_sniffer - reconfigure CAN in sniffer or normal mode + * @param issniffer - ==0 for normal mode + */ +void CAN_sniffer(uint8_t issniffer){ + flags.can_monitor = issniffer; + CAN_reinit(0); +} + +void CAN_printerr(){ + uint32_t last_err_code = CAN1->ESR; + if(!last_err_code){ + usart_send("No errors\n"); + return; + } + usart_send("Receive error counter: "); + usart_send(u2str((last_err_code & CAN_ESR_REC)>>24)); + usart_send("\nTransmit error counter: "); + usart_send(u2str((last_err_code & CAN_ESR_TEC)>>16)); + usart_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; + } + usart_send(errmsg); usart_send(" error\n"); + if(last_err_code & CAN_ESR_BOFF) usart_send("Bus off "); + if(last_err_code & CAN_ESR_EPVF) usart_send("Passive error limit "); + if(last_err_code & CAN_ESR_EWGF) usart_send("Error counter limit"); + newline(); +} + +void CAN_proc(){ // check for messages in FIFO0 & FIFO1 if(CAN1->RF0R & CAN_RF0R_FMP0){ can_process_fifo(0); @@ -168,22 +226,32 @@ void can_proc(){ } IWDG->KR = IWDG_REFRESH; if(CAN1->ESR & (CAN_ESR_BOFF | CAN_ESR_EPVF | CAN_ESR_EWGF)){ // much errors - restart CAN BUS - usart_send("\nToo much errors, restarting CAN!\n"); - // request abort for all mailboxes - CAN1->TSR |= CAN_TSR_ABRQ0 | CAN_TSR_ABRQ1 | CAN_TSR_ABRQ2; - // reset CAN bus - RCC->APB1RSTR |= RCC_APB1RSTR_CAN1RST; - RCC->APB1RSTR &= ~RCC_APB1RSTR_CAN1RST; - CAN_setup(0); + if(flags.can_printoff){ + usart_send("error=canerr\n"); + CAN_printerr(); + } + CAN_reinit(0); } } -CAN_status can_send(uint8_t *msg, uint8_t len, uint16_t target_id){ - uint8_t mailbox = 0; - // check first free mailbox - if(CAN1->TSR & (CAN_TSR_TME)){ - mailbox = (CAN1->TSR & CAN_TSR_CODE) >> 24; - }else{ // no free mailboxes +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 = 0xff; + uint32_t Tstart = Tms; + while(Tms - Tstart < SEND_TIMEOUT_MS/10){ + IWDG->KR = IWDG_REFRESH; + if(CAN1->TSR & (CAN_TSR_TME)){ + mailbox = (CAN1->TSR & CAN_TSR_CODE) >> 24; + break; + } + } + if(mailbox == 0xff){// no free mailboxes +#ifdef EBUG + usart_send("No free mailboxes\n"); +#endif return CAN_BUSY; } CAN_TxMailBox_TypeDef *box = &CAN1->sTxMailBox[mailbox]; @@ -220,6 +288,28 @@ CAN_status can_send(uint8_t *msg, uint8_t len, uint16_t target_id){ return CAN_OK; } +/** + * @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); + uint32_t Tstart = Tms; + while(Tms - Tstart < SEND_TIMEOUT_MS){ + if(CAN_OK == CAN_send(msg)) return; + IWDG->KR = IWDG_REFRESH; + } + usart_send("error=canbusy\n"); +} + static void can_process_fifo(uint8_t fifo_num){ if(fifo_num > 1) return; CAN_FIFOMailBox_TypeDef *box = &CAN1->sFIFOMailBox[fifo_num]; @@ -263,7 +353,9 @@ static void can_process_fifo(uint8_t fifo_num){ dat[0] = lb & 0xff; } } - if(CAN_messagebuf_push(&msg)) return; // error: buffer is full, try later + // run command for my or broadcast ID + if(msg.ID == the_conf.CANID || msg.ID == 0) parseCANcommand(&msg); + if(flags.can_monitor && 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; diff --git a/F1:F103/FX3U/can.h b/F1:F103/FX3U/can.h index 087bb75..c7a9a44 100644 --- a/F1:F103/FX3U/can.h +++ b/F1:F103/FX3U/can.h @@ -20,10 +20,12 @@ #include -// amount of filter banks in STM32F0 -#define STM32F0FBANKNO 28 -// flood period in milliseconds -#define FLOOD_PERIOD_MS 5 +// min/max speeds in bps +#define CAN_MAX_SPEED ((uint32_t)3000000UL) +#define CAN_MIN_SPEED ((uint32_t)9600UL) + +// wait to send not more tnah +#define SEND_TIMEOUT_MS (100) // incoming message buffer size #define CAN_INMESSAGE_SIZE (8) @@ -46,11 +48,12 @@ typedef enum{ CAN_status CAN_get_status(); -void CAN_reinit(uint16_t speed); -void CAN_setup(uint16_t speed); +void CAN_reinit(uint32_t speed); +void CAN_setup(uint32_t speed); -CAN_status can_send(uint8_t *msg, uint8_t len, uint16_t target_id); -void can_proc(); -void printCANerr(); +CAN_status CAN_send(CAN_message *message); +void CAN_proc(); +void CAN_printerr(); +void CAN_sniffer(uint8_t issniffer); CAN_message *CAN_messagebuf_pop(); diff --git a/F1:F103/FX3U/canproto.c b/F1:F103/FX3U/canproto.c new file mode 100644 index 0000000..2df7fb0 --- /dev/null +++ b/F1:F103/FX3U/canproto.c @@ -0,0 +1,248 @@ +/* + * This file is part of the fx3u 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 "canproto.h" +#include "flash.h" +#include "hardware.h" +#include "proto.h" +#include "strfunc.h" +#include "usart.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){ + usart_send("Soft reset\n"); + usart_transmit(); + 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); + *(int32_t*)&msg->data[4] = getMCUtemp(); + return ERR_OK; +} +// get ADC raw values +static errcodes adcraw(CAN_message *msg){ + FIXDL(msg); + uint8_t no = msg->data[2] & ~SETTER_FLAG; + if(no >= 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); + uint8_t no = msg->data[2] & ~SETTER_FLAG; + if(no >= ADC_CH_TSEN) return ERR_BADPAR; + float v = getADCvoltage(no) /** the_conf.adcmul[no] * 100.f*/; + *(uint32_t*)&msg->data[4] = (uint32_t) v; + 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_reinit(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; +} +/* +// relay management +static errcodes relay(CAN_message *msg){ + if(ISSETTER(msg->data)){ + if(msg->data[4] == 1) RELAY_ON(); + else RELAY_OFF(); + }else FIXDL(msg); + *(uint32_t*)&msg->data[4] = RELAY_GET(); + return ERR_OK; +} +// blocking ESW get status +static errcodes eswblk(CAN_message *msg){ + uint8_t no = msg->data[2] & ~SETTER_FLAG; + if(no > 1) return ERR_BADPAR; + FIXDL(msg); + *(uint32_t*)&msg->data[4] = getSwitches(no); + return ERR_OK; +} +// bounce-free ESW get status +static errcodes esw(CAN_message *msg){ + uint8_t no = msg->data[2] & ~SETTER_FLAG; + if(no > 1) return ERR_BADPAR; + FIXDL(msg); + *(uint32_t*)&msg->data[4] = getESW(no); + return ERR_OK; +} +*/ + +// common uint32_t setter/getter +static errcodes u32setget(CAN_message *msg){ + uint16_t idx = *(uint16_t*)msg->data; + uint32_t *ptr = NULL; + switch(idx){ + case CMD_CANSPEED: ptr = &the_conf.CANspeed; CAN_reinit(*(uint32_t*)&msg->data[4]); break; + //case CMD_BOUNCE: ptr = &the_conf.bounce_ms; break; + case CMD_USARTSPEED: ptr = &the_conf.usartspeed; break; + default: break; + } + if(!ptr) return ERR_CANTRUN; // unknown error + if(ISSETTER(msg->data)){ + *ptr = *(uint32_t*)&msg->data[4]; + }else FIXDL(msg); + *(uint32_t*)&msg->data[4] = *ptr; + return ERR_OK; +} + +/* +// common bitflag setter/getter +static errcodes flagsetget(CAN_message *msg){ + uint16_t idx = *(uint16_t*)msg->data; + uint8_t bit = 32; + switch(idx){ + case CMD_ENCISSSI: bit = FLAGBIT(ENC_IS_SSI); break; + case CMD_EMULPEP: bit = FLAGBIT(EMULATE_PEP); break; + default: break; + } + if(bit > 31) return ERR_CANTRUN; // unknown error + if(ISSETTER(msg->data)){ + if(msg->data[4]) the_conf.flags |= 1<data[4] = (the_conf.flags & (1<length; + uint8_t *data = msg->data; +#ifdef EBUG + DBG("Get data: "); + for(int i = 0; i < msg->length; ++i){ + usart_send(uhex2str(msg->data[i])); usart_putchar(' '); + } + //for(int i = msg->length-1; i < 8; ++i) msg->data[i] = 0; + newline(); +#endif + if(datalen < 2){ + FORMERR(msg, ERR_WRONGLEN); + return; + } + uint16_t idx = *(uint16_t*)data; + if(idx >= CMD_AMOUNT || funclist[idx].fn == NULL){ // bad command index + FORMERR(msg, ERR_BADCMD); return; + } + // check minimal length + if(funclist[idx].datalen > datalen){ + FORMERR(msg, ERR_WRONGLEN); return; + } + if(datalen > 3 && (data[2] & SETTER_FLAG)){ + // check setter's length + if(datalen != 8){ FORMERR(msg, 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(msg, ERR_BADVAL); return; + } + } + } + data[3] = funclist[idx].fn(msg); // set error field as result of function + data[2] &= ~SETTER_FLAG; // and clear setter flag +#ifdef EBUG + DBG("Return data: "); + for(int i = 0; i < msg->length; ++i){ + usart_send(uhex2str(msg->data[i])); usart_putchar(' '); + } + newline(); +#endif +} diff --git a/F1:F103/FX3U/canproto.h b/F1:F103/FX3U/canproto.h new file mode 100644 index 0000000..e3604ff --- /dev/null +++ b/F1:F103/FX3U/canproto.h @@ -0,0 +1,75 @@ +/* + * This file is part of the fx3u 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 +#include "can.h" + +// 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) +// base value of parameter (even if it is a setter) +#define PARBASE(x) (x & 0x7f) + +// make error for CAN answer +#define FORMERR(m, err) do{m->data[3] = err; if(m->length < 4) m->length = 4;}while(0) + +// error codes for answer message +typedef enum{ + ERR_OK, // 0 - all OK + ERR_BADPAR, // 1 - parameter's value 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 + ERR_AMOUNT // amount of error codes +} errcodes; + +// CAN commands indexes +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_RELAY, // 10 - switch relay ON/OFF + CMD_GETESW_BLK, // 11 - blocking read of ESW + CMD_GETESW, // 12 - current ESW state, bounce-free + CMD_BOUNCE, // 13 - get/set bounce constant (ms) + CMD_USARTSPEED, // 14 - get/set USART1 speed (if encoder on RS-422) + CMD_ENCISSSI, // 15 - encoder is SSI (1) or RS-422 (0) + CMD_SPIINIT, // 16 - init SPI2 + CMD_SPISEND, // 17 - send 1..4 bytes over SPI + CMD_ENCGET, // 18 - get encoder value + CMD_EMULPEP, // 19 - emulate (1) / not (0) PEP + CMD_ENCREINIT, // 20 - reinit encoder + CMD_TIMESTAMP, // 21 - 2mks 24-bit timestamp + CMD_SPIDEINIT, // 22 - turn off SPI2 + // should be the last: + CMD_AMOUNT // amount of CAN commands +}; + +void run_can_cmd(CAN_message *msg); diff --git a/F1:F103/FX3U/flash.c b/F1:F103/FX3U/flash.c index d81beea..5e74f87 100644 --- a/F1:F103/FX3U/flash.c +++ b/F1:F103/FX3U/flash.c @@ -24,21 +24,21 @@ #include // memcpy extern const uint32_t __varsstart, _BLOCKSIZE; -static const uint32_t FLASH_blocksize = (uint32_t)&_BLOCKSIZE; +const uint32_t FLASH_blocksize = (uint32_t)&_BLOCKSIZE; static uint32_t maxCnum = 1024 / sizeof(user_conf); // can't use blocksize here #define USERCONF_INITIALIZER { \ .userconf_sz = sizeof(user_conf) \ - ,.canspeed = 250 \ - ,.canID = 0xAA \ - ,.rs232speed = 115200 \ + ,.CANspeed = 250000 \ + ,.CANID = 1 \ + ,.usartspeed = 115200 \ } static int write2flash(const void*, const void*, uint32_t); const user_conf *Flash_Data = (const user_conf *)(&__varsstart); user_conf the_conf = USERCONF_INITIALIZER; -static int currentconfidx = -1; // index of current configuration +int currentconfidx = -1; // index of current configuration /** * @brief binarySearch - binary search in flash for last non-empty cell @@ -181,11 +181,3 @@ int erase_storage(int npage){ return ret; } -void dump_userconf(){ - usart_send("userconf_sz="); printu(the_conf.userconf_sz); - usart_send("\ncurrentconfidx="); usart_send(u2str(currentconfidx)); - usart_send("\nCAN_speed="); printu(the_conf.canspeed); - usart_send("\nCAN_ID="); printu(the_conf.canID); - usart_send("\nRS_232_speed="); printu(the_conf.rs232speed); - usart_send("\n"); -} diff --git a/F1:F103/FX3U/flash.h b/F1:F103/FX3U/flash.h index 7e6baf8..f64db19 100644 --- a/F1:F103/FX3U/flash.h +++ b/F1:F103/FX3U/flash.h @@ -26,16 +26,18 @@ /* * struct to save user configurations */ -typedef struct __attribute__((packed, aligned(4))){ +typedef struct __attribute__((aligned(4))){ uint16_t userconf_sz; // "magick number" - uint16_t canspeed; // CAN bus speed - uint16_t canID; // CAN bus device ID - uint32_t rs232speed; // speed of RS-232 + uint16_t CANID; // CAN bus device ID + uint32_t usartspeed; // RS-232 speed + uint32_t CANspeed; // CAN bus speed } user_conf; extern user_conf the_conf; +extern const uint32_t FLASH_blocksize; +extern const user_conf *Flash_Data; +extern int currentconfidx; void flashstorage_init(); int store_userconf(); int erase_storage(int npage); -void dump_userconf(); diff --git a/F1:F103/FX3U/fx3u.bin b/F1:F103/FX3U/fx3u.bin index 045d90aaa6824236b72550604eb324570dcf6461..96485bfd92a69939b962a48901181b7675e08dda 100755 GIT binary patch literal 14032 zcmc(F4Rl*YmS#OY*_LI;zxlCchrC#Xm?*(c4Ea+aitH$k6>x}Qpkrtx+j5?q*cO%( z96#tN2?KHH88U>P3^voJmu7al8FmOJ*=0}|LFnD-?d~}($1t1Nuq}FK8cx!)EhhoX zda<nCEUVbrd*&T&q!j*S@>83MDHVYgvUd(6ifRajwK%E1W~}?VQF*mqtT~ zun-F=vQVSA6ggp;ytEX3B=j+eCtbuN3zY*SqRlLs;dIZ^hLiTImlop50`%3-^eyCl z^GsT01>63Kja z;>>Z#s}KBHTfk9EL8PU@+OZ~9EGZq$ENh}t$f+~O%2z5KSVfDqL#(m3C>;jNl~3XW z*5iYA{iPcy)x&0o^~gKNRgO7_GI+%7P>=LK7krNTJc&{{YIX#VzB8Z>5at}pAV#T& z`;Q0ts3%b>7!^FS@i~Q$+Jw@IQOeP8N=woB8BmTBE#*l3xW>;7lq%6W`p$8U);W~K zvvRcmxK-mBCGlegb&bOtC~2kOFtyT6D6QzDV(mB2^UiT=N*Y!!4J+2A&9ez5tt*I8 zH}?r1FL6|2=T2bfXs-fipXU41CnqOwPl^uJ#iAQxI)^IjRPmbXip!bKAF8_I3#MI$ z;<|C8>QdzrM*;2qyMj=_PMqcT5M2_}I|L~?nT0t-Rey~%@`Eo5%~qFH&T2TKibcB$ z#}|#7s2#7N{}Kmj(1~|h=;+`7pek_o2>58c9);HGFwZ|HM2E?t$Zx4v`CKIq1E;Ck zANYJ+Jt#((h(d)fE>2`LoSMq2ZqByH%ms5a^HUZ{_ut@BAE!Rdk~3zN$A)va)A)Q* zZ!Hoo4CmCD4rE6NpS7KOX}F^?W}Y)!i2WOju$ehCon?KBOYambOg=>(AJ*1G7V%?7 za;5e3xlHxC=wDBn(AVTBb)KAJ)zHq75@27R63j~@^Er-LbYn$iW4?U!++990Jg=e9 zXL1xfnptt(5S!OfT$fvM5Hiz6|2a;S4Hn&qo{wPWv}`KxV1^%oHr%&Vje0%t!Tps+ znOTG}X1*=5fM`mi!$QsY@VADGoTa`rodVENT=O_jM1kAAFPpbbj%?5!v}cTDIFsNs zlT%kx`^Y^3=n+lgA-(>2@EN)AGJ#@v%G#4IqqEGLjW+WayR%TwY%ueBpUk4!F&5Hx z89t?r*~qV?^;40VMI7mLmdW5R0=@WR;bY>3#_J246juh?d*G*pOYKg@)$UXQ&9Voc z>OJ__HjFVxlVS^~z9<&E+|F^Yi*qpQehkmPjk*AQMnshbE9p1+%7-z72)tmqHiBIb zJ%FA~;8lm7_oKcOqim=*HYoD2dhMEE-2r+hibvfInE!)o*`iQW-#|}n4aKf)*zZ?k zcVk?MLx_HBT6EA(4#kX;E-G{UCd^G-tOfU91bjZ>)jAjT@Hd0Ad@2QT#% zRnF#PyM-0b4`Q~8RoE%wetei&^25uVqp|Wew}NAxcpkN-*trj4^%YB;I+XJ(7UMZv zQ_eX!k6I~Cvl6UEUt1fk$BD+4&WA||0geI& z0M7%y2dF^*!U+B6MV8_}hyNQQ66zWJUl&<|It#^QAv%~`40@+PPsBM4FAz4N{XB2$ zczY4r&+@jw+w;*rlr&00bVo8RUy|bv$nar+5Aa>U4*;(MUI&~3ybbs%;3D8-Kok%I z=&P8a91u9m-dN}y2OlD*11}Q&Bvu3qO=ARLK8)o#8@_xEu|N)_O%h_3yfADU+NR^$ zrVksy;T=inM3VMOoo1l`d_1@3YG$MTs^;|4aRI#2h~E4XY&hsvoT-9?f56y0q+Mu{{riy>TxdRgB1FmXtIGa~mmO|sQHYRb0!=F%x(k{104 z>B4UkI6RWXlS57Q6#2M~9mTcQmY`S1$E8~WAD3>?C@b%$ z${RZd;gRCrF<1O4Ro?9i9&gToztX9)$tA+`(^*dPeI0UTkT1#E#^w8`IDa%75-jZM zy8-o}NpLyg-&@s#ka_sqP8RvL)7tuKBx@p9R9lq2%{ayAOnf$GsXhg;?pOAf$<6bI zhSej~W|oGqvUglo(02&&9N`3OmSuBFa=*%DhEc#j0D^!w0e=N}3GgQXrNfk`c4XCD z7{-a&VaioIGShXV+L6KQYKOk2zk_;Md2f+pQAclGx{lsLsy;TXc9?we<169)2JO1A zdYoBq8{sQ*FL&-frh0cDR=s8YWLq&Wi+(y~hDHV5*^!txIe89IhG>}=UXs$Ta_CPG z`qOIp-K8?<&CjOE`Up zYSv)}nq@uTsnZ^2E5IJ*0?6m3_w51f8TVyp*c;j04hvvghHxh88Su;n;wWN`4DMHF z;%>~r{UskmmYN<@=GDd&yZ^m0W&C6zzq2x2g8CxR(Th#41>DBy&X5ryZ}ycD=K>_UEa4P2w|1 z46&A;#WT`zLQV+WU}t%j>~IbuTRJ&uA4o2fab2WlSA}S+N?yGn(l5jJ z)|K^z(PyrMRnj?4P^r6$9i%`sK@Mx!PJQFMbv?vyjLzfeEg|Fd(MTg z&i!nv=aGo|lxW#<$;wX#(qikF`jo+94A1TVI5;CtIx+7?KCht$avNB;y$ttglJ%f{ zN!aRbmV+*JU^VYS*mr<8jH?5yQe!lX@e@VX&2YUnHTM?GZRY*TBvJ%Lsla?)OK(S-VtPH~kwKbT;x{Y2BH$O{`@QAP8(VF7&5d}o=g zv(9fQlyfV{7KUKYg}@g8kN9bP9!{NksFyl&aNo;v6u1oQkzE_w+yM$k8cuHkER zGq;yGSTu+4T;N@zLo|q%Mu+0I^d2ZJ8+e2mz*%7ouXV0#*o~alTKtQVwW%|8H9yTL zdi`SL4(FW}W3H7LW5Jn%`;I#(BmUx%pHS0mP}~%gZHQQr8;W~PbyWVHH>a-Xm?D{k9w#Ajr*zCjSdz|W7WQoYn}Of`BgI*jtC?XvAaF+W?A-g|1TEsa zw-9BaV?jesU6UL8;Vs5$aa(kYk;2p(^5(1|$0!w=4AY0kx6TuZHPy1OE}^bVOsXsgwI zAKDwebWaVscR(`tPt~I*X+S2;wI<+3r(+z%e6{7b{)dFXodK|qk_3UqQEkY_btxGC_|2SvGbM&ldGKHpUWx#+hTITvtU(a zz?tA(3URY8<@H6B7hbd5wUEZzP4F*`(PL=m;^`Q&H`~6{+o`0MhRby}ei`T)bFt7& z>{tLDqPw_3un7VyWVxsxJ66I9P}2RpM6mro$(^P0VhZlmZ%qUa(QfBQj1AOoa^){2 zf0dj(pUeRFgNPuJALmh6IxkcZp`0FjLl12dYD9Rk*)_dRGUqly7J<{qHGp2n5RC?E zijf(B?zo-?2C-@ltZXDG+pp&19GJLtXa?IHehT#`u{#CG#`L3p5E>w&{sig=G!1h3 zojncBa+B+Er;nH2h@QKMPHU5EAL@G>%-5eRZgP21mx2Er^>&O$PAVGkgJx=$F*G8K$YB#6amoI&dG4BXQ`!)|gU_MD zzJ(F8FPy~vs{eLiiKl;3$-l5@NwN~NEJ!Ne<^gJ7P2qD9<*NtsBNFls|CCU_)9)R9 zgr5I8_522Ku+{_d@2JY4{mQ5j{mx_6&rJ7$60@8cu`Z_mD-!G7R%shbvt@K{BbNPFpAbqCcIk!o1&`UYF9_{uKV)^xeP@Os@t$&U3&dfxW4x|7m(9W_oSfg8#ps zwxq^BoqGEHY0{pd>E$W>qbd9g(`MkGpGJ=27|m9f!ap;;1o)%Vi@6oU=2H0nX%p~I zOfN$AgZi&b;k%|60Ka#-FlGNz3g5zIR5eFNbe0*QFLaj8M=1g5Y^z2|XPFHp*~Cti zj{w>LfwNlfE^DGdG&z{L*wy4ZNs%Vr;32a1D@RFld~wpF+>{<2QP;Qh%hnEhf1o_4 z#osH!7DRf8mOI{>NN&y>QIXYW-E<D;>Mb`&)iCti4GTV#E505T~TMKY=F``CT_N zl9BRxlWtmB%M#EDBqyA*zH?$V@ zWlh*{lM`c=E7gv&KAS~%$+|bk^4m|4OTuig1+peekZCHeqjyCMBg)<~qBl~=YxJy; z`_x}yo%Fs$*^8TCYbKt;SF|d7$yfZ#NO12WyxU1mzBGL#66=5-f|nrXH;x7OHrri% zt=g@RIQKz%xpVfJTEP|_&&~$-4h{!9Oo1SBb4$Pxt-8u0U7~V>rTpToA2||_wZuEf zkK~0_?0HZ=WXZVrj!YxzE-6L?E~gCsx}{hgPG0ZTuDmFRGHS@MDyg51D=P z67J6|+KyYEjYpT{yCvA$>ttuCyG4#e^({v3M3rgRNy8-AiuSIm8klmNDCzrV5Lz`h zOrE;bF@U>dGaDZsHH{C)Wnp97Rb7L8sciBE@fMBd`SpkjEzJn0XIq8YRGe1n8wU%8 zz{@}W;upV2=Y!Z%dQ&`%s5$-CG40uLTKj3vj4_<`s5RSzE&-e&6QPtk-o~g?e3W)B z1kOCm?L~(>i*+&H`r`(p_-PJ1Wm&+Lapcrgezj(;ivQHqJnWDzyfOlB;KyypZ|40> zj#E>4)kDl47q4mAYpdU?WLpdu6)!x4d!tglW@LDN!^i_!ku>HZVDfNXjBsS(aT=l?|2(@Vx-OIEo% zdI+(;)w>{q9EO8(1kFrw#}(qP+KRi;9NMEJR%AL%pfk%c3%&HxOQmyppf@0jdxOS9 z4`kn~_E&+H+P}e~yI75OK}7Ahaf}0)b--i+kz1vfHZE$LF4 zQ&on4@|FqUv0r_1)Px)iIQ=T0?{gV_o!7ZsYLdETj-<*Vsdbi8NJxyjzCzRLEdtH) zKPZ=ve~(=L&$L!^TBO(=T2JB*TvLYEnPaU$NgZ4W3L)EnnwQ}siuv1gpUyT2n!!k$w#R8`hxD1OVKLpYk_~G($1-+$M(({OL;TZWsGc)$%v=4j&CzCOKwOoGxPYc$ssyTE_2G~)3-wr@b^;y>nLG#U#dgB zj_GV|Li&9Q#ZM%iA|6i!ESZQn{+wr9h|faEq*SI1q|w89BeC_#R;6wnk~TK!0Q z&b?b>OKtP}jN8u(M$oEo(5Xk6AM)eO@)1r~O{Y)4JC|@%1CTFI` zY4JE|Chm4v)5p-q_W=I_Ae$n4BD*3Rqx=!sAo4)?jvzCfLHV{_yOC`o?q~B!400m9 z&e@TBZ$@N=;)M#Szy##-Ei804;C#XkB z@B`2#nu*WoKRv&(?$7LOB3Z*meA^1n7RChUpV`Hj&^R?x@NPj&umEEfR#b)t?UUiK z3F~5kD6>(-dN|6q*x8cMW0$vxFI*P$4_&Udn+i9lF;a4UE-Ilm`qu@#| z`(Q#61$Qml>sd9C2UQ&t|?d0EgAj`|wWH{FJ0AupTVePxUA*kvJC7kb(u zIG9u${m-?A&<`g9O+WmCiRa?qy=vI_!z<^T3dV)F?nY}J!~gjSNp}vCl;XOpwO4dZ zlcsN*4tVNC&v#zl!IsWGzx=V!x6RJ-E(}j38goV)qu&?bx>D4V{bA!}X08P<=D04( zxx~7PCfxbLh+U_n^SC;VWg*{wziecTZNJa{phIf>qpiCf5AN7uWUcNlzt7`oTXhd( z_jOC2o}TU=KRWL6w0gV;JQ_l3?cU$j>*+y)%lJSKMs#|6zxISN;|{lf7T@MUcg)FH zQ}6!vcF=Zo_jICQTUz%rJ7(ws4Zj~$fTMO>cc-_@-PKEdYIoJLO24O9YB?zRyW4x8 zaQAp_2VNsvd0@@$JsywL z>Gtkx>F)E;+GqN-?Dro;y9xBcEV$wF^7VB0cHhw|Rqj|*z2Wwa(l&6}=I(XVa!Hj*UwyuPPFuu?XK!jhT7bLcGRF8fczFI1 zo(m|GS$z7h{gtBsT;ztE+H;?MlREKp8XpCw`S~Vu{9hddUg{d}TmFD_@0JH@>zC5L z@9gQ`LkdH6$Br#c^^Y}e-@Wy2W^CW*@4p+m?CEMl!Hli@dwM)wy{+9{?cTONLNQ~f zyRWuue!87aU9s_>~8b$KI9u{ zp1I?>!;mUleE!>6+x|`;$%gt+T}sJ|*l73e+27;t^>%l`ruc;%;1D>=zsD85?dcB?G?q=jkZK-l~r+=kX zv!=z{OFZoNyL)=MkZ1dGJy|WKu#7#?27>UE^e;W(#{RW+@4;BPx7X+2SXH&h+bi#H zxudnav&z%fcF@yVtzi`u2JJRlV?eRh@9xo}Mbl>Wz-o)iqU)hie`**Q}Ln z(wgcucdn{lyQ+FMnFqrpo$jtSC^#IryLY4HbN9GAJs8^zD`@fbJlcFWbPMfAdH&Da7O54qvs!3&hArMnwW89%Na zes8}A&!v`q-K~4clankxJ+vQ>x!c-$@XR=a(5!j3&CMS6o;nbzFE<|egxL)Nma#@B zV=eABER&Z9-21Q}J>6Y<_IaS`9M{?jtHJPIxB}=i&LNC!bQ;;m$iu72Wb$*A(i&#S zME^>Z>j5`_uS59=AOm{<|oD1VHYdy^iujKsM?Tlrew~b-eX7NB~-RCCZ%u zLO+bM4KR&)WRxcW^x$QbrvY?Uyp8fhKpyH5lraFhCmG(M05$XLMc%THa+iQ(fENKT z1Cr?RgJ%Rr_CnvVfGllCnV~WGH`(Y5T)oREbZ?l_s^*`iBFdcJiEfzx(3A zL|1LvlaBB2{_eTwoOjPT_c_*grcNf;3AeG5=mLgfiSDQ&)G*8NPN4M}HBvkOeLVyf-qR7m)X2M%nP?Q}Dn#m9R2OcsBlRp=wkK z7bTR@&AjGztyu}@1KU5pQX6T|+`(v7!Y$3pXj!;LP{bCYP1@7!n7Qs~6<-pnBy%#$ z3eAq>+iaQGEZp-n0h7R#+l94Vzzq1jgd#DrO%WA|@XKQLFu`Jh;UC3Gf<$*fb$hcp zse$SXz}v9R4y=sUKYY9NC(5HojLk8lFSQDH-L+WX3|(rLM>wkKCzkoAmSfcNdVw+B zOcT>r1lAy_vR~I6;G@a?={2Wrl!pnW}}UG^sVJ#yh7n6IVFgyzrBn~Y+KFouvRKaoGJXG0x|w@tb?&L)f5 zAEeEepF(UyinmH?D;gVDPSQEyn`4Uio}{=H**hjpx+%%n^7);&nP0t}HYd$!;tEJ1 zB)g309z*^Cc>sw+6pwL5$eVBd+qlsOtPgpYE#ZQYH&4YOuhu%}p&FB_HmkD0(AF&B zf`cJ1lbhTf4|$C~`VIzXHZ?cTItjlrK8eU3Nlr8AHHs839{ro((OsejC6U0nnV|Sl0*}!>~o}==-xYj{=60n zxFtCQL=TxbUD3|S?*oM|k0J}ithk9eB>o8H{zKGY*jgrrB*q#N-vGnT5G&!e@sQN; zu-G4In7(uB3ELJs3H&NjUxF5qflr{KS>WTqA@H=#g2MNj+oV?Um@uS{gJ_(9OdWN$ zibpZ=HJfLSm$i!hz33CkU!3S6J5E?VsMh=x%oXp z+bGSLA>$I-y!3Vn2U-dxn@GAqEnlL>+w%jkj6WVQmg=hlCmp|A1X4E-c6UVkLo4kuj@ zA0((cq#wFUAtY8}e=uW8$RVi;ABe*EY7o9mNNQ8xttayWjBU;66H82o>i!Tk##D`i zs=M`=>rsMKi}1JOc>gE8SB*FQ9h{OXqSHZ6Uyi_e5V|-~6k$~m*RC8NK-T>U)OJ^4 zVnk}KhOyoQ)x%w-!Tf0#Q`pF+w``)yc3yKQ)cYL&*@OJ)a!shWtdV|zTasC;t!p4j z95(l&NmGnoGL=qxPTg}?IzQ+MNoO0Fm?GJ_cC4jr|NLSQ#Rx*C4MfSh*`Sy|U6R{) zWsadCiK_*rb4^0&X_Kham1ykmB5mva~JHJssqKzj`D=DVsW)rRBt0S$dWF>4cf0@i*vx zdrx$2TV9kZ7_F|;+Zsaw7v&=gGJZ^B7r5V2T(whylao;Yo z(GVq^Z&wMpm1)Mte04+gY(pv*Gx~P4|@8KaE|&tYq1Ff*D(~}4tov{a0ADnIMmNYezv*srM|8m zut_#?KDWPrAcx)lBY_>@JpHh9gpz^5p$@7S@bvk5yE%2*=sA42+uz+k$aN0%^||}I z0+AJ48su6FXXk1ywOeW|&(~NjG-Oi1;laq&aAd!K0D-}syRUn&+t2k6^>uXn3vztU z)YJ6 - + EnvironmentId diff --git a/F1:F103/FX3U/fx3u.files b/F1:F103/FX3U/fx3u.files index f964000..accf1c5 100644 --- a/F1:F103/FX3U/fx3u.files +++ b/F1:F103/FX3U/fx3u.files @@ -3,6 +3,8 @@ adc.h buttons.c can.c can.h +canproto.c +canproto.h flash.c flash.h hardware.c diff --git a/F1:F103/FX3U/main.c b/F1:F103/FX3U/main.c index e3a34bf..0b38cb5 100644 --- a/F1:F103/FX3U/main.c +++ b/F1:F103/FX3U/main.c @@ -38,19 +38,20 @@ int main(void){ flashstorage_init(); gpio_setup(); // should be run before other peripherial setup adc_setup(); - usart_setup(the_conf.rs232speed); - CAN_setup(the_conf.canspeed); + usart_setup(the_conf.usartspeed); + CAN_setup(the_conf.CANspeed); RCC->CSR |= RCC_CSR_RMVF; // remove reset flags #ifndef EBUG iwdg_setup(); #endif + usart_send("START\n"); while (1){ IWDG->KR = IWDG_REFRESH; // refresh watchdog if(Tms - lastT > 499){ // throw out short messages twice per second usart_transmit(); lastT = Tms; } - can_proc(); + CAN_proc(); CAN_status st = CAN_get_status(); if(st == CAN_FIFO_OVERRUN){ usart_send("CAN bus fifo overrun occured!\n"); @@ -58,8 +59,8 @@ int main(void){ usart_send("Some CAN error occured\n"); } while((can_mesg = CAN_messagebuf_pop())){ + DBG("got CAN message\n"); IWDG->KR = IWDG_REFRESH; - // TODO: check ID and process messages if(flags.can_monitor){ lastT = Tms; if(!lastT) lastT = 1; @@ -77,10 +78,7 @@ int main(void){ char *str; int g = usart_getline(&str); if(g < 0) usart_send("USART IN buffer overflow!\n"); - else if(g > 0){ - const char *ans = cmd_parser(str); - if(ans) usart_send(ans); - } + else if(g > 0) cmd_parser(str); } return 0; } diff --git a/F1:F103/FX3U/proto.c b/F1:F103/FX3U/proto.c index b50013d..fee514c 100644 --- a/F1:F103/FX3U/proto.c +++ b/F1:F103/FX3U/proto.c @@ -18,6 +18,8 @@ #include "adc.h" #include "can.h" +#include "canproto.h" +#include "flash.h" #include "hardware.h" #include "proto.h" #include "strfunc.h" @@ -25,62 +27,68 @@ #include "version.inc" flags_t flags = { - .can_monitor = 0 + 0 }; -/* -static void printans(int res){ - if(res) usart_send("OK"); - else usart_send("FAIL"); -} - -// setters of uint/int -static void usetter(int(*fn)(uint32_t), char* str){ - uint32_t d = 0; - if(str == getnum(str, &d)) printans(FALSE); - else printans(fn(d)); -} -static void isetter(int(*fn)(int32_t), char* str){ - int32_t d = 0; - if(str == getint(str, &d)) printans(FALSE); - else printans(fn(d)); -} -*/ - -// parno - number of parameter (or -1); cargs - string with arguments (after '=') (==NULL for getter), iarg - integer argument -static int goodstub(const char *cmd, int parno, const char *carg, int32_t iarg){ - usart_send("cmd="); usart_send(cmd); - usart_send(", parno="); usart_send(i2str(parno)); - usart_send(", args="); usart_send(carg); - usart_send(", intarg="); usart_send(i2str(iarg)); newline(); - return RET_GOOD; -} +// text-only commans indexes (0 is prohibited) +typedef enum{ + TCMD_PROHIBITED, // prohibited + TCMD_WDTEST, // test watchdog + TCMD_DUMPCONF, // dump configuration + TCMD_CANSEND, // send CAN message + TCMD_CANSNIFFER, // sniff all CAN messages + TCMD_CANBUSERRPRNT, // pring all errors of bus + TCMD_AMOUNT +} text_cmd; typedef struct{ - int (*fn)(const char*, int, const char*, int32_t); - const char *cmd; - const char *help; -} commands; + 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; -static commands cmdlist[] = { - {goodstub, "stub", "simple stub"}, - {NULL, "Different commands", NULL}, -// {adcval, "ADC", "get ADCx value (without x - for all)"}, -// {adcvoltage, "ADCv", "get ADCx voltage (without x - for all)"}, -// {mcut, "mcut", "get MCU temperature"}, - {NULL, NULL, NULL} +// list of all text functions; should be sorted and can be grouped (`help` is header when cmd == NULL) +static const funcdescr funclist[] = { +// {"adcmul", CMD_ADCMUL, "get/set ADC multipliers 0..3 (*1000)"}, +// {"adcraw", CMD_ADCRAW, "get raw ADC values of channel 0..4"}, +// {"adcv", CMD_ADCV, "get ADC voltage of channel 0..3 (*100V)"}, +// {"bounce", CMD_BOUNCE, "get/set bounce constant (ms)"}, + {"canbuserr", -TCMD_CANBUSERRPRNT, "print all CAN bus errors (a lot of if not connected)"}, + {"canid", CMD_CANID, "get/set CAN ID"}, + {"cansniff", -TCMD_CANSNIFFER, "switch CAN sniffer mode"}, + {"canspeed", CMD_CANSPEED, "get/set CAN speed (bps)"}, + {"dumpconf", -TCMD_DUMPCONF, "dump current configuration"}, + {"esw", CMD_GETESW, "anti-bounce read ESW of channel 0 or 1"}, + {"eraseflash", CMD_ERASESTOR, "erase all flash storage"}, + {"mcutemp", CMD_MCUTEMP, "get MCU temperature (*10degrC)"}, + {"relay", CMD_RELAY, "get/set relay state (0 - off, 1 - on)"}, + {"reset", CMD_RESET, "reset MCU"}, + {"s", -TCMD_CANSEND, "send CAN message: ID 0..8 data bytes"}, + {"saveconf", CMD_SAVECONF, "save configuration"}, + {"time", CMD_TIME, "get/set time (1ms, 32bit)"}, + {"usartspeed", CMD_USARTSPEED, "get/set USART1 speed"}, + {"wdtest", -TCMD_WDTEST, "test watchdog"}, + {NULL, 0, NULL} // last record }; static void printhelp(){ - commands *c = cmdlist; + const funcdescr *c = funclist; usart_send("https://github.com/eddyem/stm32samples/tree/master/F1:F103/FX3U#" BUILD_NUMBER " @ " BUILD_DATE "\n"); - while(c->cmd){ - if(!c->fn){ // header + usart_send("commands format: parameter[number][=setter]\n"); + usart_send("parameter [CAN idx] - help\n"); + usart_send("--------------------------\n"); + while(c->help){ + if(!c->cmd){ // header usart_send("\n "); - usart_send(c->cmd); + usart_send(c->help); usart_putchar(':'); }else{ usart_send(c->cmd); + if(c->idx > -1){ + usart_send(" ["); + usart_send(u2str(c->idx)); + usart_putchar(']'); + } usart_send(" - "); usart_send(c->help); } @@ -89,52 +97,224 @@ static void printhelp(){ } } -/** - * @brief parsecmd - parse text commands over RS-232 - * @param str - input string - * @return answer code - */ -static int parsecmd(const char *str){ - char cmd[CMD_MAXLEN + 1]; - //USB_sendstr("cmd="); USB_sendstr(str); USB_sendstr("__\n"); - if(!str || !*str) return RET_CMDNOTFOUND; - int i = 0; - while(*str > '@' && i < CMD_MAXLEN){ cmd[i++] = *str++; } - cmd[i] = 0; - int parno = -1; - int32_t iarg = __INT32_MAX__; - if(*str){ - uint32_t N; - const char *nxt = getnum(str, &N); - if(nxt != str) parno = (int) N; - str = strchr(str, '='); - if(str){ - str = omit_spaces(++str); - getint(str, &iarg); +/*********** START of all common functions list (for `textfunctions`) ***********/ + +static errcodes cansnif(const char *str){ + uint32_t U; + if(str){ + if(*str == '=') str = omit_spaces(str + 1); + const char *nxt = getnum(str, &U); + if(nxt != str){ // setter + CAN_sniffer((uint8_t)U); } - }else str = NULL; - commands *c = cmdlist; - while(c->cmd){ - if(strcmp(c->cmd, cmd) == 0){ - if(!c->fn) return RET_CMDNOTFOUND; - return c->fn(cmd, parno, str, iarg); - } - ++c; } - return RET_CMDNOTFOUND; + usart_send("cansniff="); usart_putchar('0' + flags.can_monitor); newline(); + return ERR_OK; +} + +static errcodes canbuserr(const char *str){ + uint32_t U; + if(str){ + if(*str == '=') str = omit_spaces(str + 1); + const char *nxt = getnum(str, &U); + if(nxt != str){ // setter + flags.can_printoff = U; + } + } + usart_send("canbuserr="); usart_putchar('0' + flags.can_printoff); newline(); + return ERR_OK; +} + +static errcodes wdtest(const char _U_ *str){ + usart_send("Wait for reboot\n"); + usart_transmit(); + while(1){nop();} + return ERR_OK; +} +/* +// names of bit flags (ordered from LSE of[0]) +static const char * const bitfields[] = { + "encisSSI", + "emulatePEP", + NULL +};*/ + +static errcodes dumpconf(const char _U_ *str){ +#ifdef EBUG + uint32_t sz = FLASH_SIZE*1024; + usart_send("flashsize="); printu(sz); usart_putchar('/'); + printu(FLASH_blocksize); usart_putchar('='); printu(sz/FLASH_blocksize); + usart_send(" blocks\n"); +#endif + usart_send("userconf_addr="); printuhex((uint32_t)Flash_Data); + usart_send("\nuserconf_idx="); printi(currentconfidx); + usart_send("\nuserconf_sz="); printu(the_conf.userconf_sz); + usart_send("\ncanspeed="); printu(the_conf.CANspeed); + usart_send("\ncanid="); printu(the_conf.CANID); + /*for(int i = 0; i < ADC_TSENS; ++i){ + usart_send("\nadcmul"); usart_putchar('0'+i); usart_putchar('='); + usart_send(float2str(the_conf.adcmul[i], 3)); + }*/ + usart_send("\nusartspeed="); printu(the_conf.usartspeed); + /* + const char * const *p = bitfields; + int bit = 0; + while(*p){ + newline(); + usart_send(*p); usart_putchar('='); usart_putchar((the_conf.flags & (1< 31) break; + ++p; + }*/ + newline(); + return ERR_OK; +} + +static errcodes cansend(const char *txt){ + CAN_message canmsg; + bzero(&canmsg, sizeof(canmsg)); + int ctr = -1; + canmsg.ID = 0xffff; + do{ + txt = omit_spaces(txt); + uint32_t N; + const char *n = getnum(txt, &N); + if(txt == n) break; + txt = n; + if(ctr == -1){ + if(N > 0x7ff){ + return ERR_BADPAR; + } + canmsg.ID = (uint16_t)(N&0x7ff); + ctr = 0; + continue; + } + if(ctr > 7){ + return ERR_WRONGLEN; + } + if(N > 0xff){ + return ERR_BADVAL; + } + canmsg.data[ctr++] = (uint8_t) N; + }while(1); + if(canmsg.ID == 0xffff){ + return ERR_BADPAR; + } + canmsg.length = (uint8_t) ctr; + uint32_t Tstart = Tms; + while(Tms - Tstart < SEND_TIMEOUT_MS){ + if(CAN_OK == CAN_send(&canmsg)){ + return ERR_OK; + } + } + return ERR_CANTRUN; +} + +/************ END of all common functions list (for `textfunctions`) ************/ + +// in `textfn` arg `str` is the rest of input string (spaces-omitted) after command +typedef errcodes (*textfn)(const char *str); +static textfn textfunctions[TCMD_AMOUNT] = { + [TCMD_PROHIBITED] = NULL, + [TCMD_WDTEST] = wdtest, + [TCMD_DUMPCONF] = dumpconf, + [TCMD_CANSEND] = cansend, + [TCMD_CANSNIFFER] = cansnif, + [TCMD_CANBUSERRPRNT] = canbuserr, +}; + +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){ + usart_send("error="); + usart_send(errors_txt[e]); + newline(); } /** * @brief cmd_parser - command parsing * @param txt - buffer with commands & data */ -const char *cmd_parser(const char *txt){ - int ret = parsecmd(txt); - switch(ret){ - case RET_WRONGPARNO: return "Wrong parameter number\n"; break; - case RET_CMDNOTFOUND: printhelp(); return NULL; break; - case RET_WRONGARG: return "Wrong command parameters\n"; break; - case RET_GOOD: return NULL; break; - default: return "FAIL\n"; break; +void cmd_parser(const char *str){ + if(!str || !*str) goto ret; + char cmd[MAXCMDLEN + 1]; + errcodes ecode = ERR_BADCMD; + 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; + } + ++c; + } + if(idx == CMD_AMOUNT){ // didn't found + // send help over USB + printhelp(); + goto ret; + } + 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 + ++str; + 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){ + usart_send("OK\n"); // non setters/getters will just print "OK" if all OK + }else{ + usart_send(cmd); + data[2] &= ~SETTER_FLAG; + if(data[2] != NO_PARNO) usart_send(u2str(data[2])); + usart_putchar('='); + usart_send(i2str(*(int32_t*)&data[4])); + newline(); + } } } diff --git a/F1:F103/FX3U/proto.h b/F1:F103/FX3U/proto.h index 0402f29..8ed6c12 100644 --- a/F1:F103/FX3U/proto.h +++ b/F1:F103/FX3U/proto.h @@ -21,31 +21,15 @@ #include #include "hardware.h" -#ifndef _U_ -#define _U_ __attribute__((__unused__)) -#endif - -#define CMD_MAXLEN (32) - -enum{ - RET_WRONGPARNO = -3, // wrong parameter number - RET_CMDNOTFOUND = -2, // command not found - RET_WRONGARG = -1, // wrong argument - RET_GOOD = 0, // all OK - RET_BAD = 1 // something wrong -}; +#define MAXCMDLEN (12) +// flags for some RS-232 comands typedef struct{ - uint32_t can_monitor : 1; + uint32_t can_monitor : 1; // monitor any CAN bus connections + uint32_t can_printoff : 1; // print errors to RS-232 if CAN bus is off or other bus errors } flags_t; extern flags_t flags; -#ifdef EBUG -#define DBG(str) do{usart_send(__FILE__ " (L" STR(__LINE__) "): " str); \ - usart_putchar('\n'); usart_transmit(); }while(0) -#else -#define DBG(str) -#endif -const char *cmd_parser(const char *txt); +void cmd_parser(const char *txt); diff --git a/F1:F103/FX3U/strfunc.h b/F1:F103/FX3U/strfunc.h index 3be4040..0d0b72d 100644 --- a/F1:F103/FX3U/strfunc.h +++ b/F1:F103/FX3U/strfunc.h @@ -23,6 +23,17 @@ #include "usart.h" +#ifndef _U_ +#define _U_ __attribute__((__unused__)) +#endif + +#ifdef EBUG +#define DBG(str) do{usart_send(__FILE__ " (L" STR(__LINE__) "): " str); \ +usart_putchar('\n'); usart_transmit(); }while(0) +#else +#define DBG(str) +#endif + void hexdump(int (*sendfun)(const char *s), uint8_t *arr, uint16_t len); char *u2str(uint32_t val); char *i2str(int32_t i); diff --git a/F1:F103/FX3U/version.inc b/F1:F103/FX3U/version.inc index 8c865e6..a4d3b7e 100644 --- a/F1:F103/FX3U/version.inc +++ b/F1:F103/FX3U/version.inc @@ -1,2 +1,2 @@ -#define BUILD_NUMBER "20" -#define BUILD_DATE "2024-05-30" +#define BUILD_NUMBER "36" +#define BUILD_DATE "2024-06-01"