From b9942f2e9fe7406e9df562203a1aa758f65e857a Mon Sep 17 00:00:00 2001 From: Edward Emelianov Date: Thu, 26 Sep 2024 17:53:16 +0300 Subject: [PATCH] almost ready but have IWDG reset when trying to send data over RS-232 in CANbus flooding --- F1:F103/FX3U/adc.c | 5 +- F1:F103/FX3U/can.c | 66 ++++------ F1:F103/FX3U/canproto.c | 27 +++- F1:F103/FX3U/canproto.h | 7 +- F1:F103/FX3U/flash.c | 5 +- F1:F103/FX3U/flash.h | 13 +- F1:F103/FX3U/fx3u.bin | Bin 15008 -> 15772 bytes F1:F103/FX3U/hardware.c | 55 ++++++-- F1:F103/FX3U/main.c | 24 +++- F1:F103/FX3U/modbusproto.c | 16 +-- F1:F103/FX3U/modbusrtu.c | 63 +++++---- F1:F103/FX3U/modbusrtu.h | 4 +- F1:F103/FX3U/proto.c | 255 +++++++++++++++++++++++++++---------- F1:F103/FX3U/proto.h | 3 +- F1:F103/FX3U/strfunc.c | 6 +- F1:F103/FX3U/strfunc.h | 7 +- F1:F103/FX3U/usart.c | 12 +- F1:F103/FX3U/usart.h | 4 +- F1:F103/FX3U/version.inc | 4 +- 19 files changed, 388 insertions(+), 188 deletions(-) diff --git a/F1:F103/FX3U/adc.c b/F1:F103/FX3U/adc.c index e8a7ddf..cb2952a 100644 --- a/F1:F103/FX3U/adc.c +++ b/F1:F103/FX3U/adc.c @@ -47,12 +47,13 @@ void adc_setup(){ ADC1->CR2 |= ADC_CR2_ADON; __DSB(); // wait for Tstab - at least 1us + IWDG->KR = IWDG_REFRESH; while(++ctr < 0xff) nop(); // calibration ADC1->CR2 |= ADC_CR2_RSTCAL; - ctr = 0; while((ADC1->CR2 & ADC_CR2_RSTCAL) && ++ctr < 0xfffff); + ctr = 0; while((ADC1->CR2 & ADC_CR2_RSTCAL) && ++ctr < 0xfffff) IWDG->KR = IWDG_REFRESH; ADC1->CR2 |= ADC_CR2_CAL; - ctr = 0; while((ADC1->CR2 & ADC_CR2_CAL) && ++ctr < 0xfffff); + ctr = 0; while((ADC1->CR2 & ADC_CR2_CAL) && ++ctr < 0xfffff) IWDG->KR = IWDG_REFRESH; // clear possible errors and start ADC1->SR = 0; ADC1->CR2 |= ADC_CR2_SWSTART; diff --git a/F1:F103/FX3U/can.c b/F1:F103/FX3U/can.c index b4374a6..5161d68 100644 --- a/F1:F103/FX3U/can.c +++ b/F1:F103/FX3U/can.c @@ -48,9 +48,7 @@ static CAN_status can_status = CAN_STOP; static void can_process_fifo(uint8_t fifo_num); CAN_status CAN_get_status(){ - int st = can_status; - can_status = CAN_OK; - return st; + return can_status; } // push next message into buffer; return 1 if buffer overfull @@ -141,8 +139,10 @@ void CAN_setup(uint32_t speed){ /* (12) Leave filter init */ /* (13) Set error interrupts enable (& bus off) */ CAN1->MCR |= CAN_MCR_INRQ; /* (1) */ - while((CAN1->MSR & CAN_MSR_INAK) != CAN_MSR_INAK) /* (2) */ + while((CAN1->MSR & CAN_MSR_INAK) != CAN_MSR_INAK){ /* (2) */ + IWDG->KR = IWDG_REFRESH; if(--tmout == 0) break; + } CAN1->MCR &=~ CAN_MCR_SLEEP; /* (3) */ CAN1->MCR |= CAN_MCR_ABOM; /* allow automatically bus-off */ CAN1->BTR = (CAN_TBS2-1) << 20 | (CAN_TBS1-1) << 16 | (CAN_BIT_OSC/speed - 1); //| CAN_BTR_SILM | CAN_BTR_LBKM; /* (4) */ @@ -152,8 +152,10 @@ void CAN_setup(uint32_t speed){ #endif CAN1->MCR &= ~CAN_MCR_INRQ; /* (5) */ tmout = 16000000; - while((CAN1->MSR & CAN_MSR_INAK) == CAN_MSR_INAK) /* (6) */ + while((CAN1->MSR & CAN_MSR_INAK) == CAN_MSR_INAK){ /* (6) */ + IWDG->KR = IWDG_REFRESH; if(--tmout == 0) break; + } // accept depending of monitor flag CAN1->FMR = CAN_FMR_FINIT; /* (7) */ CAN1->FA1R = CAN_FA1R_FACT0; /* (8) */ @@ -218,6 +220,22 @@ void CAN_printerr(){ } void CAN_proc(){ + IWDG->KR = IWDG_REFRESH; + if((CAN1->ESR & (CAN_ESR_BOFF | CAN_ESR_EPVF | CAN_ESR_EWGF)) + || can_status != CAN_READY){ // much errors - restart CAN BUS + if(flags.can_printoff){ + const char *e; + switch(can_status){ + case CAN_ERR: e = "ERRI"; break; + case CAN_FIFO_OVERRUN: e = "FIFO_OVERRUN"; break; + default: e = "UNKNOWN"; + } + usart_send("canerror="); + usart_send(e); newline(); + CAN_printerr(); + } + CAN_reinit(0); + } // check for messages in FIFO0 & FIFO1 if(CAN1->RF0R & CAN_RF0R_FMP0){ can_process_fifo(0); @@ -225,14 +243,6 @@ void CAN_proc(){ if(CAN1->RF1R & CAN_RF1R_FMP1){ can_process_fifo(1); } - IWDG->KR = IWDG_REFRESH; - if(CAN1->ESR & (CAN_ESR_BOFF | CAN_ESR_EPVF | CAN_ESR_EWGF)){ // much errors - restart CAN BUS - if(flags.can_printoff){ - usart_send("error=canerr\n"); - CAN_printerr(); - } - CAN_reinit(0); - } } CAN_status CAN_send(CAN_message *message){ @@ -290,28 +300,6 @@ CAN_status CAN_send(CAN_message *message){ 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.CANIDout; // set output ID for all output 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]; @@ -355,9 +343,8 @@ static void can_process_fifo(uint8_t fifo_num){ dat[0] = lb & 0xff; } } - // run command for my or broadcast ID - if(msg.ID == the_conf.CANIDin || msg.ID == 0) parseCANcommand(&msg); - if(flags.can_monitor && CAN_messagebuf_push(&msg)) return; // error: buffer is full, try later + IWDG->KR = IWDG_REFRESH; + 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; @@ -368,6 +355,7 @@ void usb_lp_can_rx0_isr(){ // Rx FIFO0 (overrun) if(CAN1->RF0R & CAN_RF0R_FOVR0){ // FIFO overrun CAN1->RF0R &= ~CAN_RF0R_FOVR0; can_status = CAN_FIFO_OVERRUN; + RCC->APB1ENR &= ~RCC_APB1ENR_CAN1EN; } } @@ -375,6 +363,7 @@ void can_rx1_isr(){ // Rx FIFO1 (overrun) if(CAN1->RF1R & CAN_RF1R_FOVR1){ CAN1->RF1R &= ~CAN_RF1R_FOVR1; can_status = CAN_FIFO_OVERRUN; + RCC->APB1ENR &= ~RCC_APB1ENR_CAN1EN; } } @@ -386,5 +375,6 @@ void can_sce_isr(){ // status changed if(CAN1->TSR & CAN_TSR_TERR1) CAN1->TSR |= CAN_TSR_ABRQ1; if(CAN1->TSR & CAN_TSR_TERR2) CAN1->TSR |= CAN_TSR_ABRQ2; can_status = CAN_ERR; + RCC->APB1ENR &= ~RCC_APB1ENR_CAN1EN; } } diff --git a/F1:F103/FX3U/canproto.c b/F1:F103/FX3U/canproto.c index 4b7b37c..466d6f9 100644 --- a/F1:F103/FX3U/canproto.c +++ b/F1:F103/FX3U/canproto.c @@ -27,8 +27,7 @@ #define FIXDL(m) do{m->length = 8;}while(0) /*********** START of all common functions list (for `funclist`) ***********/ -static errcodes ping(CAN_message *m){ - m->ID = the_conf.CANIDout; // change ID +static errcodes ping(CAN_message _U_ *m){ return ERR_OK; // send same message } // reset MCU @@ -163,6 +162,7 @@ static errcodes u32setget(CAN_message *msg){ case CMD_INCHNLS: val = inchannels(); ptr = &val; break; case CMD_OUTCHNLS: val = outchannels(); ptr = &val; break; case CMD_MODBUSID: ptr = &the_conf.modbusID; break; + case CMD_MODBUSIDOUT: ptr = &the_conf.modbusIDout; break; case CMD_MODBUSSPEED: ptr = &the_conf.modbusspeed; break; default: break; } @@ -230,6 +230,7 @@ static const commonfunction funclist[CMD_AMOUNT] = { [CMD_INCHNLS] = {u32setget, 0, 0, 0}, [CMD_OUTCHNLS] = {u32setget, 0, 0, 0}, [CMD_MODBUSID] = {u32setget, 0, MODBUS_MAX_ID, 0}, // 0 - master! + [CMD_MODBUSIDOUT] = {u32setget, 0, MODBUS_MAX_ID, 0}, [CMD_MODBUSSPEED] = {u32setget, 1200, 115200, 0}, }; @@ -288,3 +289,25 @@ void run_can_cmd(CAN_message *msg){ newline(); #endif } + +/** + * @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 + */ +void parseCANcommand(CAN_message *msg){ + // check PING + if(msg->length != 0) run_can_cmd(msg); + uint32_t Tstart = Tms; + // send answer + while(Tms - Tstart < SEND_TIMEOUT_MS){ + if(CAN_OK == CAN_send(msg)) return; + IWDG->KR = IWDG_REFRESH; + } + usart_send("error=canbusy\n"); +} diff --git a/F1:F103/FX3U/canproto.h b/F1:F103/FX3U/canproto.h index 07278cc..d6b731e 100644 --- a/F1:F103/FX3U/canproto.h +++ b/F1:F103/FX3U/canproto.h @@ -48,6 +48,9 @@ typedef enum{ // set command bytes in CAN message #define MSG_SET_CMD(msg, cmd) do{*((uint16_t*)msg.data) = (cmd);}while(0) #define MSGP_SET_CMD(msg, cmd) do{*((uint16_t*)msg->data) = (cmd);}while(0) +// set command parameter number +#define MSG_SET_PARNO(msg, n) do{msg.data[2] = (n);}while(0) +#define MSGP_SET_PARNO(msg, n) do{msg->data[2] = (n);}while(0) // set error #define MSG_SET_ERR(msg, err) do{msg.data[3] = (err);}while(0) #define MSGP_SET_ERR(msg, err) do{msg->data[3] = (err);}while(0) @@ -75,15 +78,17 @@ enum{ CMD_GETESW, // current ESW state, bounce-free CMD_GETESWNOW, // current ESW state, absolute CMD_BOUNCE, // get/set bounce constant (ms) - CMD_USARTSPEED, // get/set USART1 speed (if encoder on RS-422) + CMD_USARTSPEED, // get/set USART1 speed CMD_LED, // onboard LED CMD_FLAGS, // flags setter/getter CMD_INCHNLS, // all bits set are active supported IN channels CMD_OUTCHNLS, // all bits set are active supported OUT channels CMD_MODBUSID, // set/get modbus slave ID (or 0 if master) + CMD_MODBUSIDOUT,// slave ID to send modbus relay command if IN changes CMD_MODBUSSPEED,// speed of modbus interface // should be the last: CMD_AMOUNT // amount of CAN commands }; void run_can_cmd(CAN_message *msg); +void parseCANcommand(CAN_message *msg); diff --git a/F1:F103/FX3U/flash.c b/F1:F103/FX3U/flash.c index 25765d4..263efba 100644 --- a/F1:F103/FX3U/flash.c +++ b/F1:F103/FX3U/flash.c @@ -35,8 +35,10 @@ static uint32_t maxCnum = 1024 / sizeof(user_conf); // can't use blocksize here ,.CANIDout = 2 \ ,.usartspeed = 115200 \ ,.bouncetime = 50 \ - ,.modbusID = MODBUS_MASTER_ID \ + ,.modbusID = 1 \ + ,.modbusIDout = 2 \ ,.modbusspeed = 9600 \ + ,.flags.sw_send_relay_inv = 1 \ } static int write2flash(const void*, const void*, uint32_t); @@ -57,6 +59,7 @@ int currentconfidx = -1; // index of current configuration static int binarySearch(int r, const uint8_t *start, int stor_size){ int l = 0; while(r >= l){ + IWDG->KR = IWDG_REFRESH; int mid = l + (r - l) / 2; const uint8_t *s = start + mid * stor_size; if(*((const uint16_t*)s) == stor_size){ diff --git a/F1:F103/FX3U/flash.h b/F1:F103/FX3U/flash.h index c9a7f8d..51d1588 100644 --- a/F1:F103/FX3U/flash.h +++ b/F1:F103/FX3U/flash.h @@ -23,15 +23,21 @@ #define FLASH_SIZE_REG ((uint32_t)0x1FFFF7E0) #define FLASH_SIZE *((uint16_t*)FLASH_SIZE_REG) -// maximal bit number of flags -#define MAX_FLAG_BITNO (0) +// maximal bit NUMBER (0 etc) of flags +#define MAX_FLAG_BITNO (3) typedef union{ uint32_t u32; struct{ - uint32_t sw_send_relay_cmd; // switching ESW state will send also CMD_RELAY command with CANID_OUT + uint32_t sw_send_esw_can : 1; // 0 - if ESW state changes, send message over CAN with CANID_IN + uint32_t sw_send_relay_can : 1; // 1 - switching ESW state will send CMD_RELAY command with CANID_OUT + uint32_t sw_send_relay_inv : 1; // 2 - sw_send should be inverted related to current inputs value + uint32_t sw_send_relay_modbus : 1; // 3 - 1 for modbus }; } confflags_t; +// ALL sw_send flags mask (without relay_inv) +#define SW_SEND_MASK (0x0B) + /* * struct to save user configurations */ @@ -45,6 +51,7 @@ typedef struct __attribute__((aligned(4))){ uint32_t CANspeed; // CAN bus speed confflags_t flags; // different flags uint32_t modbusID; // MODBUS-RTU ID (0 for master) + uint32_t modbusIDout; // MODBUS-RTU slave ID to send relay command when IN changes uint32_t modbusspeed; // Speed of modbus interface } user_conf; diff --git a/F1:F103/FX3U/fx3u.bin b/F1:F103/FX3U/fx3u.bin index ef7b7ea1dd613c5bd2cb4878d92fbf430123f641..655429dfb6286362d465d73456c84762f352c9bd 100755 GIT binary patch literal 15772 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 F{{hzj-|GMX literal 15008 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 F{~vpkX@vj) diff --git a/F1:F103/FX3U/hardware.c b/F1:F103/FX3U/hardware.c index cec837c..395e48c 100644 --- a/F1:F103/FX3U/hardware.c +++ b/F1:F103/FX3U/hardware.c @@ -20,6 +20,7 @@ #include "canproto.h" #include "flash.h" #include "hardware.h" +#include "modbusrtu.h" /* #ifdef EBUG #include "strfunc.h" @@ -116,14 +117,16 @@ void gpio_setup(void){ void iwdg_setup(){ uint32_t tmout = 16000000; RCC->CSR |= RCC_CSR_LSION; - while((RCC->CSR & RCC_CSR_LSIRDY) != RCC_CSR_LSIRDY){if(--tmout == 0) break;} + while((RCC->CSR & RCC_CSR_LSIRDY) != RCC_CSR_LSIRDY) if(--tmout == 0) break; IWDG->KR = IWDG_START; IWDG->KR = IWDG_WRITE_ACCESS; IWDG->PR = IWDG_PR_PR_1; IWDG->RLR = 1250; tmout = 16000000; - while(IWDG->SR){if(--tmout == 0) break;} - IWDG->KR = IWDG_REFRESH; + while(IWDG->SR){ + IWDG->KR = IWDG_REFRESH; + if(--tmout == 0) break; + } } typedef struct{ @@ -263,22 +266,46 @@ void proc_esw(){ else ESW_ab_values &= ~mask; lastET[i] = Tms; } - if(oldesw != ESW_ab_values){ + if(oldesw != ESW_ab_values && (the_conf.flags.u32 & SW_SEND_MASK)){ // State changes and need to send info //usart_send("esw="); usart_send(u2str(ESW_ab_values)); newline(); - CAN_message msg = {.ID = the_conf.CANIDout, .length = 8}; - MSG_SET_CMD(msg, CMD_GETESW); - MSG_SET_U32(msg, ESW_ab_values); - uint32_t Tstart = Tms; - while(Tms - Tstart < SEND_TIMEOUT_MS){ - if(CAN_OK == CAN_send(&msg)) break; - } - if(the_conf.flags.sw_send_relay_cmd){ // send also CMD_RELAY - MSG_SET_CMD(msg, CMD_RELAY); - Tstart = Tms; + if(the_conf.flags.sw_send_esw_can){ + CAN_message msg = {.ID = the_conf.CANIDin, .length = 8, .data = {0}}; + MSG_SET_CMD(msg, CMD_GETESW); + MSG_SET_U32(msg, ESW_ab_values); + uint32_t Tstart = Tms; while(Tms - Tstart < SEND_TIMEOUT_MS){ + IWDG->KR = IWDG_REFRESH; if(CAN_OK == CAN_send(&msg)) break; } } + uint32_t v = ESW_ab_values; + if(the_conf.flags.sw_send_relay_inv) v = ~v; + if(the_conf.flags.sw_send_relay_can){ // send also CMD_RELAY + CAN_message msg = {.ID = the_conf.CANIDout, .length = 8, .data = {0}}; + MSG_SET_U32(msg, v); + MSG_SET_CMD(msg, CMD_RELAY); + MSG_SET_PARNO(msg, SETTER_FLAG | NO_PARNO); + uint32_t Tstart = Tms; + while(Tms - Tstart < SEND_TIMEOUT_MS){ + IWDG->KR = IWDG_REFRESH; + if(CAN_OK == CAN_send(&msg)) break; + } + } + if(the_conf.flags.sw_send_relay_modbus && the_conf.modbusID == MODBUS_MASTER_ID){ + modbus_request r = {.ID = the_conf.modbusIDout, + .Fcode = MC_WRITE_MUL_COILS, + .startreg = 0, + .regno = OUTMAX+1, + .datalen = OUTMAXBYTES + }; + uint8_t data[OUTMAXBYTES]; + r.data = data; + for(int i = OUTMAXBYTES - 1; i > -1; --i){ + data[i] = (uint8_t) v; + v >>= 8; + } + modbus_send_request(&r); + } } } diff --git a/F1:F103/FX3U/main.c b/F1:F103/FX3U/main.c index a3d79d2..4a17183 100644 --- a/F1:F103/FX3U/main.c +++ b/F1:F103/FX3U/main.c @@ -18,6 +18,7 @@ #include "adc.h" #include "can.h" +#include "canproto.h" #include "flash.h" #include "hardware.h" #include "modbusproto.h" @@ -48,7 +49,6 @@ int main(void){ uint32_t lastT = 0; CAN_message *can_mesg; StartHSE(); - RCC->CSR |= RCC_CSR_RMVF; // remove reset flags SysTick_Config(72000); flashstorage_init(); gpio_setup(); // should be run before other peripherial setup @@ -61,6 +61,19 @@ int main(void){ iwdg_setup(); #endif usart_send("START\n"); + if(RCC->CSR & RCC_CSR_IWDGRSTF){ // watchdog reset occured + usart_send("IWDGRSTF=1\n"); + } + if(RCC->CSR & RCC_CSR_SFTRSTF){ // software reset occured + usart_send("SFTRSTF=1\n"); + } + if(RCC->CSR & RCC_CSR_PORRSTF){ // POR/RDR + usart_send("PORRSTF=1\n"); + } + if(RCC->CSR & RCC_CSR_PINRSTF){ // reset by pin NRST + usart_send("PINRSTF=1\n"); + } + RCC->CSR |= RCC_CSR_RMVF; // remove reset flags while (1){ IWDG->KR = IWDG_REFRESH; // refresh watchdog if(Tms - lastT > 499){ // throw out short messages twice per second @@ -69,15 +82,12 @@ int main(void){ } proc_esw(); CAN_proc(); - CAN_status st = CAN_get_status(); - if(st == CAN_FIFO_OVERRUN){ - usart_send("CAN bus fifo overrun occured!\n"); - }else if(st == CAN_ERR){ - usart_send("Some CAN error occured\n"); - } while((can_mesg = CAN_messagebuf_pop())){ DBG("got CAN message\n"); IWDG->KR = IWDG_REFRESH; + // run command for my or broadcast ID + if(can_mesg->ID == the_conf.CANIDin || can_mesg->ID == 0) + parseCANcommand(can_mesg); if(flags.can_monitor){ lastT = Tms; if(!lastT) lastT = 1; diff --git a/F1:F103/FX3U/modbusproto.c b/F1:F103/FX3U/modbusproto.c index 322ed69..213a9e2 100644 --- a/F1:F103/FX3U/modbusproto.c +++ b/F1:F103/FX3U/modbusproto.c @@ -68,13 +68,12 @@ TRUE_INLINE void readdiscr(modbus_request *r){ return; } uint8_t bytes[INMAXBYTES] = {0}; - int curidx = INMAXBYTES - 1; int vals = get_esw(INMAX+1); - for(int i = 0; i < amount; ++i){ - bytes[--curidx] = vals & 0xff; + for(int i = amount - 1; i > -1; --i){ + bytes[i] = vals & 0xff; vals >>= 8; } - modbus_response resp = {.Fcode = r->Fcode, .ID = the_conf.modbusID, .data = bytes+curidx, .datalen = amount}; + modbus_response resp = {.Fcode = r->Fcode, .ID = the_conf.modbusID, .data = bytes, .datalen = amount}; modbus_send_response(&resp); } @@ -122,7 +121,7 @@ TRUE_INLINE void readadc(modbus_request *r){ uint16_t vals[ADC_CHANNELS]; for(int i = r->startreg; i < nlast; ++i){ uint16_t v = getADCval(i); - vals[i] = __builtin_bswap16(v); + vals[i - r->startreg] = __builtin_bswap16(v); } modbus_response resp = {.Fcode = r->Fcode, .ID = the_conf.modbusID, .data = (uint8_t*) vals, .datalen = r->regno * 2}; modbus_send_response(&resp); @@ -155,20 +154,21 @@ TRUE_INLINE void writereg(modbus_request *r){ // support ONLY write to ALL! // data - by bits, like in readcoil +// N registers is N bits TRUE_INLINE void writecoils(modbus_request *r){ if(r->startreg){ senderr(r, ME_ILLEGAL_ADDRESS); return; } - int amount = r->regno; - if(amount == 0 || amount > OUTMAX || r->datalen > 4){ + int amount = (r->regno + 7) >> 3; + if(amount == 0 || amount > OUTMAXBYTES || r->datalen < amount){ senderr(r, ME_ILLEGAL_VALUE); return; } uint32_t v = 0; for(int i = 0; i < amount; ++i){ - v |= r->data[i]; v <<= 8; + v |= r->data[i]; } if(set_relay(OUTMAX+1, v) < 0){ senderr(r, ME_NACK); diff --git a/F1:F103/FX3U/modbusrtu.c b/F1:F103/FX3U/modbusrtu.c index b3fef10..40fb6b8 100644 --- a/F1:F103/FX3U/modbusrtu.c +++ b/F1:F103/FX3U/modbusrtu.c @@ -39,11 +39,12 @@ static void us(){ }*/ // switch to Rx/Tx: -#define _485_Rx() do{LED(1); RS485_RX(); /*UART4->CR1 = (UART4->CR1 & ~USART_CR1_TE) | USART_CR1_RE;*/}while(0) -#define _485_Tx() do{LED(0); RS485_TX(); /*UART4->CR1 = (UART4->CR1 & ~USART_CR1_RE) | USART_CR1_TE;*/}while(0) +#define _485_Rx() do{ DMA2_Channel5->CCR &= ~DMA_CCR_EN; RS485_RX(); \ + DMA2_Channel3->CMAR = (uint32_t) rbuf[rbufno]; DMA2_Channel3->CNDTR = MODBUSBUFSZI; \ + DMA2_Channel3->CCR |= DMA_CCR_EN;}while(0) +#define _485_Tx() do{ RS485_TX(); }while(0) static volatile int modbus_txrdy = 1; -static volatile int idatalen[2] = {0,0}; // received data line length (including '\n') static volatile int modbus_rdy = 0 // received data ready ,dlen = 0 // length of data (including '\n') in current buffer @@ -68,9 +69,11 @@ static uint16_t getCRC(uint8_t *data, int l){ }else crc >>= 1; } } + /* #ifdef EBUG DBG("Calc CRC: "); printuhex(crc); newline(); #endif + */ // CRC have swapped bytes, so we can just send it as *((uint16_t*)&data[x]) = CRC return crc; } @@ -105,13 +108,13 @@ static int senddata(int l){ IWDG->KR = IWDG_REFRESH; if(--tmout == 0) return 0; }; // wait for previos buffer transmission - _485_Tx(); modbus_txrdy = 0; DMA2_Channel5->CCR &= ~DMA_CCR_EN; DMA2_Channel5->CMAR = (uint32_t) tbuf[tbufno]; // mem DMA2_Channel5->CNDTR = l + 2; // + CRC DMA2_Channel5->CCR |= DMA_CCR_EN; tbufno = !tbufno; + _485_Tx(); return l; } @@ -133,12 +136,13 @@ int modbus_send_request(modbus_request *r){ *curbuf++ = r->startreg >> 8; // H *curbuf++ = (uint8_t) r->startreg; // L *curbuf++ = r->regno >> 8; // H - *curbuf = (uint8_t) r->regno; // L + *curbuf++ = (uint8_t) r->regno; // L // if r->datalen == 0 - this is responce for request with fcode > 4 if((r->Fcode == MC_WRITE_MUL_COILS || r->Fcode == MC_WRITE_MUL_REGS) && r->datalen){ // request with data - *(++curbuf) = r->datalen; + if(r->datalen > MODBUSBUFSZO - 7) return -1; + *curbuf++ = r->datalen; memcpy(curbuf, r->data, r->datalen); - n += r->datalen; + n += r->datalen + 1; // + data length byte } packCRC(tbuf[tbufno], n) = getCRC(tbuf[tbufno], n); return senddata(n); @@ -207,62 +211,57 @@ int modbus_get_response(modbus_response* r){ // USART4: PC10 - Tx, PC11 - Rx void modbus_setup(uint32_t speed){ - uint32_t tmout = 16000000; // PC10 - Tx, PC11 - Rx RCC->APB1ENR |= RCC_APB1ENR_UART4EN; RCC->AHBENR |= RCC_AHBENR_DMA2EN; GPIOC->CRH = (GPIOC->CRH & ~(CRH(10,0xf)|CRH(11,0xf))) | CRH(10, CNF_AFPP|MODE_NORMAL) | CRH(11, CNF_FLINPUT|MODE_INPUT); - // UART4 Tx DMA - Channel5 (Rx - channel 3) + // UART4 Tx DMA - Channel5, Rx - channel 3 DMA2_Channel5->CPAR = (uint32_t) &UART4->DR; // periph DMA2_Channel5->CCR |= DMA_CCR_MINC | DMA_CCR_DIR | DMA_CCR_TCIE; // 8bit, mem++, mem->per, transcompl irq + DMA2_Channel3->CPAR = (uint32_t) &UART4->DR; + DMA2_Channel3->CCR = DMA_CCR_MINC | DMA_CCR_TCIE; // Tx CNDTR set @ each transmission due to data size NVIC_SetPriority(DMA2_Channel4_5_IRQn, 2); NVIC_EnableIRQ(DMA2_Channel4_5_IRQn); - NVIC_SetPriority(UART4_IRQn, 2); + NVIC_SetPriority(DMA2_Channel3_IRQn, 2); + NVIC_EnableIRQ(DMA2_Channel3_IRQn); // setup uart4 UART4->BRR = 36000000 / speed; // APB1 is 36MHz UART4->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE; // 1start,8data,nstop; enable Rx,Tx,USART + uint32_t tmout = 16000000; while(!(UART4->SR & USART_SR_TC)){ // polling idle frame Transmission IWDG->KR = IWDG_REFRESH; if(--tmout == 0) break; } + (void) UART4->DR; // clear IDLE etc UART4->SR = 0; // clear flags - UART4->CR1 |= USART_CR1_RXNEIE | USART_CR1_IDLEIE | USART_CR1_TCIE; // allow Rx and IDLE IRQ; TC IRQ for switching to Rx - UART4->CR3 = USART_CR3_DMAT; // enable DMA Tx + UART4->CR1 |= USART_CR1_IDLEIE | USART_CR1_TCIE; // allow IDLE IRQ; TC IRQ for switching to Rx + UART4->CR3 = USART_CR3_DMAT | USART_CR3_DMAR; // enable DMA Tx & Rx + NVIC_SetPriority(UART4_IRQn, 2); NVIC_EnableIRQ(UART4_IRQn); _485_Rx(); } void uart4_isr(){ if(UART4->SR & USART_SR_IDLE){ // idle - end of frame -usart_send("485: IDLE\n"); + DMA2_Channel3->CCR &= ~DMA_CCR_EN; modbus_rdy = 1; - dlen = idatalen[rbufno]; + dlen = MODBUSBUFSZI - DMA2_Channel3->CNDTR; recvdata = rbuf[rbufno]; // prepare other buffer rbufno = !rbufno; - idatalen[rbufno] = 0; (void) UART4->DR; // clear IDLE flag by reading DR - }else if(UART4->SR & USART_SR_RXNE){ // RX not emty - receive next char - uint8_t rb = UART4->DR; // clear RXNE flag - if(idatalen[rbufno] < MODBUSBUFSZI){ // put next char into buf - rbuf[rbufno][idatalen[rbufno]++] = rb; -usart_send("485: "); usart_putchar(rb); newline(); - }else{ // buffer overrun - bufovr = 1; - idatalen[rbufno] = 0; - } - }else if(UART4->SR & USART_SR_TC){ + _485_Rx(); // receive next + }else if(UART4->SR & USART_SR_TC){ // TC - switch to Rx if(modbus_txrdy){ -usart_send("->Rx\n"); _485_Rx(); } -usart_send("485: TC\n"); UART4->SR &= ~USART_SR_TC; } } +// Tx rdy void dma2_channel4_5_isr(){ if(DMA2->ISR & DMA_ISR_TCIF5){ // Tx DMA2->IFCR = DMA_IFCR_CTCIF5; // clear TC flag @@ -270,3 +269,13 @@ void dma2_channel4_5_isr(){ } } +// Rx full +void dma2_channel3_isr(){ + if(DMA2->ISR & DMA_ISR_TCIF3){ + DMA2_Channel3->CCR &= ~DMA_CCR_EN; + DMA2->IFCR = DMA_IFCR_CTCIF3; + bufovr = 1; + _485_Rx(); + } +} + diff --git a/F1:F103/FX3U/modbusrtu.h b/F1:F103/FX3U/modbusrtu.h index 85b5a5f..40e0a1b 100644 --- a/F1:F103/FX3U/modbusrtu.h +++ b/F1:F103/FX3U/modbusrtu.h @@ -19,8 +19,8 @@ #pragma once #include -// input and output buffers size -#define MODBUSBUFSZI (64) +// input and output buffers size: input buffer is greater as last symbol would be lost on Rx DMA IRQ +#define MODBUSBUFSZI (68) #define MODBUSBUFSZO (64) #define MODBUS_MASTER_ID (0) diff --git a/F1:F103/FX3U/proto.c b/F1:F103/FX3U/proto.c index 0703c8f..a2b2461 100644 --- a/F1:F103/FX3U/proto.c +++ b/F1:F103/FX3U/proto.c @@ -27,6 +27,54 @@ #include "usart.h" #include "version.inc" +// constants with commands and flags +static const char* S_adc = "adc"; +static const char* S_bounce = "bounce"; +static const char* S_canbuserr = "canbuserr"; +static const char* S_canid = "canid"; +static const char* S_canidin = "canidin"; +static const char* S_canidout = "canidout"; +static const char* S_cansniff = "cansniff"; +static const char* S_canspeed = "canspeed"; +static const char* S_dumpconf = "dumpconf"; +static const char* S_eraseflash = "eraseflash"; +static const char* S_esw = "esw"; +static const char* S_eswnow = "eswnow"; +static const char* S_flags = "flags"; +static const char* S_inchannels = "inchannels"; +static const char* S_led = "led"; +static const char* S_mcutemp = "mcutemp"; +static const char* S_modbusid = "modbusid"; +static const char* S_modbusidout = "modbusidout"; +static const char* S_modbus = "modbus"; +static const char* S_modbusraw = "modbusraw"; +static const char* S_modbusspeed = "modbusspeed"; +static const char* S_outchannels = "outchannels"; +static const char* S_relay = "relay"; +static const char* S_reset = "reset"; +static const char* S_saveconf = "saveconf"; +static const char* S_s = "s"; +static const char* S_time = "time"; +static const char* S_usartspeed = "usartspeed"; +static const char* S_wdtest = "wdtest"; + +// names of bit flags (ordered from LSE of[0]) +static const char* S_f_relay_inverted = "f_relay_inverted"; +static const char* S_f_send_esw_can = "f_send_esw_can"; +static const char* S_f_send_relay_can = "f_send_relay_can"; +static const char* S_f_send_relay_modbus = "f_send_relay_modbus"; + +// bitfield names should be in order of bit fields in confflags_t! +static const char ** const bitfields[] = { + &S_f_send_esw_can, + &S_f_send_relay_can, + &S_f_relay_inverted, + &S_f_send_relay_modbus, + NULL +}; + + +// runtime flags flags_t flags = { 0 }; @@ -39,15 +87,20 @@ typedef enum{ TCMD_CANSEND, // send CAN message TCMD_CANSNIFFER, // sniff all CAN messages TCMD_CANBUSERRPRNT, // pring all errors of bus - TCMD_SW_SEND_RELAY, // change of IN will send also command to change OUT - TCMD_MODBUS_SEND, // send raw modbus data (CRC added auto) + TCMD_MODBUS_SEND, // send modbus request + TCMD_MODBUS_SEND_RAW,// send raw modbus data (CRC added auto) + // change bit flags + TCMD_SW_SEND_CAN, // change of IN will send its current state over CAN + TCMD_SW_SEND_RCAN, // change of IN will send command to change OUT by CAN + TCMD_RELAY_INV, // inverted state between relay and inputs when send over CAN/MODBUS + TCMD_SW_SEND_RMODBUS,// modbus analog (if master) 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 + const char** const cmd; // command, if NULL - only display help message + int idx; // index in CAN cmd or text cmd list (if negative) + const char* const help; // help message } funcdescr; // list of all text functions; should be sorted and can be grouped (`help` is header when cmd == NULL) @@ -57,37 +110,42 @@ static const funcdescr funclist[] = { // {"adcv", CMD_ADCV, "get ADC voltage of channel 0..3 (*100V)"}, // {"bounce", CMD_BOUNCE, "get/set bounce constant (ms)"}, {NULL, 0, "CAN bus commands"}, - {"canbuserr", -TCMD_CANBUSERRPRNT, "print all CAN bus errors (a lot of if not connected)"}, - {"cansniff", -TCMD_CANSNIFFER, "switch CAN sniffer mode"}, - {"s", -TCMD_CANSEND, "send CAN message: ID 0..8 data bytes"}, + {&S_canbuserr, -TCMD_CANBUSERRPRNT, "print all CAN bus errors (a lot of if not connected)"}, + {&S_cansniff, -TCMD_CANSNIFFER, "switch CAN sniffer mode"}, + {&S_s, -TCMD_CANSEND, "send CAN message: ID 0..8 data bytes"}, {NULL, 0, "Configuration"}, - {"bounce", CMD_BOUNCE, "set/get anti-bounce timeout (ms, max: 1000)"}, - {"canid", CMD_CANID, "set both (in/out) CAN ID / get in CAN ID"}, - {"canidin", CMD_CANIDin, "get/set input CAN ID"}, - {"canidout", CMD_CANIDout, "get/set output CAN ID"}, - {"canspeed", CMD_CANSPEED, "get/set CAN speed (bps)"}, - {"dumpconf", -TCMD_DUMPCONF, "dump current configuration"}, - {"eraseflash", CMD_ERASESTOR, "erase all flash storage"}, - {"flags", CMD_FLAGS, "set/get configuration flags (as one U32 without parameter or Nth bit with)"}, - {"modbusid", CMD_MODBUSID, "set/get modbus slave ID (1..247) or set it master (0)"}, - {"modbusspeed", CMD_MODBUSSPEED, "set/get modbus speed (1200..115200)"}, - {"saveconf", CMD_SAVECONF, "save configuration"}, - {"sw_send_relay", -TCMD_SW_SEND_RELAY, "change of IN will send also command to change OUT with `canidout`"}, - {"usartspeed", CMD_USARTSPEED, "get/set USART1 speed"}, + {&S_bounce, CMD_BOUNCE, "set/get anti-bounce timeout (ms, max: 1000)"}, + {&S_canid, CMD_CANID, "set both (in/out) CAN ID / get in CAN ID"}, + {&S_canidin, CMD_CANIDin, "get/set input CAN ID"}, + {&S_canidout, CMD_CANIDout, "get/set output CAN ID"}, + {&S_canspeed, CMD_CANSPEED, "get/set CAN speed (bps)"}, + {&S_dumpconf, -TCMD_DUMPCONF, "dump current configuration"}, + {&S_eraseflash, CMD_ERASESTOR, "erase all flash storage"}, + {&S_f_relay_inverted, -TCMD_RELAY_INV, "inverted state between relay and inputs"}, + {&S_f_send_esw_can, -TCMD_SW_SEND_CAN, "change of IN will send status over CAN with `canidin`"}, + {&S_f_send_relay_can, -TCMD_SW_SEND_RCAN, "change of IN will send also CAN command to change OUT with `canidout`"}, + {&S_f_send_relay_modbus, -TCMD_SW_SEND_RMODBUS, "change of IN will send also MODBUS command to change OUT with `modbusidout` (only for master!)"}, + {&S_flags, CMD_FLAGS, "set/get configuration flags (as one U32 without parameter or Nth bit with)"}, + {&S_modbusid, CMD_MODBUSID, "set/get modbus slave ID (1..247) or set it master (0)"}, + {&S_modbusidout, CMD_MODBUSIDOUT, "set/get modbus slave ID (0..247) to send relay commands"}, + {&S_modbusspeed, CMD_MODBUSSPEED, "set/get modbus speed (1200..115200)"}, + {&S_saveconf, CMD_SAVECONF, "save configuration"}, + {&S_usartspeed, CMD_USARTSPEED, "get/set USART1 speed"}, {NULL, 0, "IN/OUT"}, - {"adc", CMD_ADCRAW, "get raw ADC values for given channel"}, - {"esw", CMD_GETESW, "anti-bounce read inputs"}, - {"eswnow", CMD_GETESWNOW, "read current inputs' state"}, - {"led", CMD_LED, "work with onboard LED"}, - {"relay", CMD_RELAY, "get/set relay state (0 - off, 1 - on)"}, + {&S_adc, CMD_ADCRAW, "get raw ADC values for given channel"}, + {&S_esw, CMD_GETESW, "anti-bounce read inputs"}, + {&S_eswnow, CMD_GETESWNOW, "read current inputs' state"}, + {&S_led, CMD_LED, "work with onboard LED"}, + {&S_relay, CMD_RELAY, "get/set relay state (0 - off, 1 - on)"}, {NULL, 0, "Other commands"}, - {"inchannels", CMD_INCHNLS, "get u32 with bits set on supported IN channels"}, - {"mcutemp", CMD_MCUTEMP, "get MCU temperature (*10degrC)"}, - {"modbus", -TCMD_MODBUS_SEND, "send modbus request, format: slaveID Fcode startReg numRegs"}, - {"outchannels", CMD_OUTCHNLS, "get u32 with bits set on supported OUT channels"}, - {"reset", CMD_RESET, "reset MCU"}, - {"time", CMD_TIME, "get/set time (1ms, 32bit)"}, - {"wdtest", -TCMD_WDTEST, "test watchdog"}, + {&S_inchannels, CMD_INCHNLS, "get u32 with bits set on supported IN channels"}, + {&S_mcutemp, CMD_MCUTEMP, "get MCU temperature (*10degrC)"}, + {&S_modbus, -TCMD_MODBUS_SEND, "send modbus request with format \"slaveID fcode regaddr nregs [N data]\", to send zeros you can omit rest of 'data'"}, + {&S_modbusraw, -TCMD_MODBUS_SEND_RAW, "send RAW modbus request (will send up to 62 bytes + calculated CRC)"}, + {&S_outchannels, CMD_OUTCHNLS, "get u32 with bits set on supported OUT channels"}, + {&S_reset, CMD_RESET, "reset MCU"}, + {&S_time, CMD_TIME, "get/set time (1ms, 32bit)"}, + {&S_wdtest, -TCMD_WDTEST, "test watchdog"}, {NULL, 0, NULL} // last record }; @@ -98,12 +156,13 @@ static void printhelp(){ usart_send("parameter [CAN idx] - help\n"); usart_send("--------------------------\n"); while(c->help){ + IWDG->KR = IWDG_REFRESH; if(!c->cmd){ // header usart_send("\n "); usart_send(c->help); usart_putchar(':'); }else{ - usart_send(c->cmd); + usart_send(*c->cmd); if(c->idx > -1){ usart_send(" ["); usart_send(u2str(c->idx)); @@ -128,7 +187,7 @@ static errcodes cansnif(const char *str, text_cmd _U_ cmd){ CAN_sniffer((uint8_t)U); } } - usart_send("cansniff="); usart_putchar('0' + flags.can_monitor); newline(); + usart_send(S_cansniff); EQ(); usart_putchar('0' + flags.can_monitor); newline(); return ERR_OK; } @@ -141,7 +200,7 @@ static errcodes canbuserr(const char *str, text_cmd _U_ cmd){ flags.can_printoff = U; } } - usart_send("canbuserr="); usart_putchar('0' + flags.can_printoff); newline(); + usart_send(S_canbuserr); EQ(); usart_putchar('0' + flags.can_printoff); newline(); return ERR_OK; } @@ -152,11 +211,7 @@ static errcodes wdtest(const char _U_ *str, text_cmd _U_ cmd){ return ERR_OK; } -// names of bit flags (ordered from LSE of[0]) -static const char * const bitfields[] = { - "sw_send_relay_cmd", - NULL -}; + static errcodes dumpconf(const char _U_ *str, text_cmd _U_ cmd){ #ifdef EBUG @@ -168,23 +223,21 @@ static errcodes dumpconf(const char _U_ *str, text_cmd _U_ cmd){ 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_in="); printu(the_conf.CANIDin); - usart_send("\ncanid_out="); printu(the_conf.CANIDout); - /*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); - usart_send("\nmodbus_id="); printu(the_conf.modbusID); - usart_send("\nmodbusspeed="); printu(the_conf.modbusspeed); - usart_send("\nbouncetime="); printu(the_conf.bouncetime); - const char * const *p = bitfields; + newline(); usart_send(S_canspeed); EQ(); printu(the_conf.CANspeed); + newline(); usart_send(S_canidin); EQ(); printu(the_conf.CANIDin); + newline(); usart_send(S_canidout); EQ(); printu(the_conf.CANIDout); + newline(); usart_send(S_usartspeed); EQ(); printu(the_conf.usartspeed); + newline(); usart_send(S_modbusid); EQ(); printu(the_conf.modbusID); + newline(); usart_send(S_modbusidout); EQ(); printu(the_conf.modbusIDout); + newline(); usart_send(S_modbusspeed); EQ(); printu(the_conf.modbusspeed); + newline(); usart_send(S_bounce); EQ(); printu(the_conf.bouncetime); + const char ** const *p = bitfields; int bit = 0; - usart_send("\nflags="); usart_putchar('='); printuhex(the_conf.flags.u32); + newline(); usart_send(S_flags); printuhex(the_conf.flags.u32); while(*p){ + IWDG->KR = IWDG_REFRESH; newline(); usart_putchar(' '); - usart_send(*p); usart_putchar('='); usart_putchar((the_conf.flags.u32 & (1< MAX_FLAG_BITNO) break; ++p; } @@ -198,6 +251,7 @@ static errcodes cansend(const char *txt, text_cmd _U_ cmd){ int ctr = -1; canmsg.ID = 0xffff; do{ + IWDG->KR = IWDG_REFRESH; txt = omit_spaces(txt); uint32_t N; const char *n = getnum(txt, &N); @@ -225,6 +279,7 @@ static errcodes cansend(const char *txt, text_cmd _U_ cmd){ canmsg.length = (uint8_t) ctr; uint32_t Tstart = Tms; while(Tms - Tstart < SEND_TIMEOUT_MS){ + IWDG->KR = IWDG_REFRESH; if(CAN_OK == CAN_send(&canmsg)){ return ERR_OK; } @@ -234,13 +289,22 @@ static errcodes cansend(const char *txt, text_cmd _U_ cmd){ // change configuration flags by one static errcodes confflags(const char _U_ *str, text_cmd cmd){ - if(str){ + if(str && *str){ if(*str == '=') str = omit_spaces(str + 1); if(*str != '0' && *str != '1') return ERR_BADVAL; uint8_t val = *str - '0'; switch(cmd){ - case TCMD_SW_SEND_RELAY: - the_conf.flags.sw_send_relay_cmd = val; + case TCMD_SW_SEND_CAN: + the_conf.flags.sw_send_esw_can = val; + break; + case TCMD_SW_SEND_RCAN: + the_conf.flags.sw_send_relay_can = val; + break; + case TCMD_RELAY_INV: + the_conf.flags.sw_send_relay_inv = val; + break; + case TCMD_SW_SEND_RMODBUS: + the_conf.flags.sw_send_relay_modbus = val; break; default: return ERR_BADCMD; @@ -248,21 +312,35 @@ static errcodes confflags(const char _U_ *str, text_cmd cmd){ } uint8_t val = 0, idx = 0; switch(cmd){ - case TCMD_SW_SEND_RELAY: - val = the_conf.flags.sw_send_relay_cmd; + case TCMD_SW_SEND_CAN: + val = the_conf.flags.sw_send_esw_can; idx = 0; break; + case TCMD_SW_SEND_RCAN: + val = the_conf.flags.sw_send_relay_can; + idx = 1; + break; + case TCMD_RELAY_INV: + val = the_conf.flags.sw_send_relay_inv; + idx = 2; + break; + case TCMD_SW_SEND_RMODBUS: + val = the_conf.flags.sw_send_relay_modbus; + idx = 3; + break; default: return ERR_BADCMD; } - usart_send(bitfields[idx]); usart_putchar('='); usart_putchar('0' + val); + usart_send(*bitfields[idx]); EQ(); usart_putchar('0' + val); newline(); return ERR_OK; } -// format: slaveID Fcode startReg numRegs +// format: slaveID Fcode startReg numRegs [N data]; +// to send zeros you can omit rest of 'data' static errcodes modbussend(const char *txt, text_cmd _U_ cmd){ modbus_request req = {0}; + uint8_t reqdata[MODBUSBUFSZO - 7] = {0}; uint32_t N = 0; const char *n = getnum(txt, &N); if(n == txt || N > MODBUS_MAX_ID){ @@ -283,15 +361,53 @@ static errcodes modbussend(const char *txt, text_cmd _U_ cmd){ } req.startreg = N; txt = n; n = getnum(txt, &N); - if(n == txt || N == 0){ - usart_send("Need registers amount\n"); + if(n == txt){ + usart_send("Need registers amount or data bytes\n"); return ERR_WRONGLEN; } req.regno = N; + txt = n; n = getnum(txt, &N); + if((req.Fcode == MC_WRITE_MUL_COILS || req.Fcode == MC_WRITE_MUL_REGS)){ // request with data + if(txt == n){ + usart_send("Need amount of data for given fcode\n"); + return ERR_WRONGLEN; + } + if(N == 0 || N > MODBUSBUFSZO - 7){ + usart_send("Data length too big\n"); + return ERR_BADVAL; + } + req.datalen = N; + for(int i = 0; i < req.datalen; ++i){ + txt = n; n = getnum(txt, &N); + if(txt == n) break; + reqdata[i] = N; + } + req.data = reqdata; + }else if(n != txt){ + usart_send("multiple data allows only for fcode 0x0f and 0x10; ignore other data\n"); + } if(modbus_send_request(&req) < 1) return ERR_CANTRUN; return ERR_OK; } +// raw data format +static errcodes modbussendraw(const char *txt, text_cmd _U_ cmd){ + uint32_t N = 0; + uint8_t reqdata[MODBUSBUFSZO], datalen = 0; + for(; datalen < MODBUSBUFSZO; ++datalen){ + const char *n = getnum(txt, &N); + if(n == txt) break; + reqdata[datalen] = N; + txt = n; + } + if(datalen == 0){ + usart_send("Need data bytes\n"); + return ERR_WRONGLEN; + } + if(modbus_send(reqdata, datalen) != datalen) return ERR_CANTRUN; + return ERR_OK; +} + /************ END of all text functions list ************/ // in `textfn` arg `str` is the rest of input string (spaces-omitted) after command @@ -303,12 +419,16 @@ static textfn textfunctions[TCMD_AMOUNT] = { [TCMD_CANSEND] = cansend, [TCMD_CANSNIFFER] = cansnif, [TCMD_CANBUSERRPRNT] = canbuserr, - [TCMD_SW_SEND_RELAY] = confflags, [TCMD_MODBUS_SEND] = modbussend, + [TCMD_MODBUS_SEND_RAW] = modbussendraw, + [TCMD_SW_SEND_CAN] = confflags, + [TCMD_SW_SEND_RCAN] = confflags, + [TCMD_RELAY_INV] = confflags, + [TCMD_SW_SEND_RMODBUS] = confflags, }; static const char* const errors_txt[ERR_AMOUNT] = { - [ERR_OK] = "OK" + [ERR_OK] = "OK" ,[ERR_BADPAR] = "badpar" ,[ERR_BADVAL] = "badval" ,[ERR_WRONGLEN] = "wronglen" @@ -339,7 +459,8 @@ void cmd_parser(const char *str){ if(l == 0) goto ret; cmd[l] = 0; while(c->help){ - if(c->cmd && 0 == strcmp(c->cmd, cmd)){ + IWDG->KR = IWDG_REFRESH; + if(c->cmd && 0 == strcmp(*c->cmd, cmd)){ idx = c->idx; break; } diff --git a/F1:F103/FX3U/proto.h b/F1:F103/FX3U/proto.h index 8ed6c12..52d0052 100644 --- a/F1:F103/FX3U/proto.h +++ b/F1:F103/FX3U/proto.h @@ -21,7 +21,7 @@ #include #include "hardware.h" -#define MAXCMDLEN (12) +#define MAXCMDLEN (32) // flags for some RS-232 comands typedef struct{ @@ -33,3 +33,4 @@ extern flags_t flags; void cmd_parser(const char *txt); + diff --git a/F1:F103/FX3U/strfunc.c b/F1:F103/FX3U/strfunc.c index 7c42a70..93ad3c2 100644 --- a/F1:F103/FX3U/strfunc.c +++ b/F1:F103/FX3U/strfunc.c @@ -64,8 +64,6 @@ static char *_2str(uint32_t val, uint8_t minus){ uint32_t x = val / 10; *(--bufptr) = (val - 10*x) + '0'; val = x; - //*(--bufptr) = val % 10 + '0'; - //val /= 10; } } if(minus) *(--bufptr) = '-'; @@ -257,8 +255,8 @@ const char *getint(const char *txt, int32_t *I){ int32_t sign = 1; uint32_t U; if(*s == '-'){ - sign = -1; - ++s; + sign = -1; + ++s; } const char *nxt = getnum(s, &U); if(nxt == s) return txt; diff --git a/F1:F103/FX3U/strfunc.h b/F1:F103/FX3U/strfunc.h index d7ceeb1..ff5607c 100644 --- a/F1:F103/FX3U/strfunc.h +++ b/F1:F103/FX3U/strfunc.h @@ -43,7 +43,8 @@ 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); -#define newline() do{usart_putchar('\n');}while(0) -#define printu(a) do{usart_send(u2str(a));}while(0) -#define printi(a) do{usart_send(i2str(a));}while(0) +#define EQ() do{usart_putchar('=');}while(0) +#define newline() do{usart_putchar('\n');}while(0) +#define printu(a) do{usart_send(u2str(a));}while(0) +#define printi(a) do{usart_send(i2str(a));}while(0) #define printuhex(a) do{usart_send(uhex2str(a));}while(0) diff --git a/F1:F103/FX3U/usart.c b/F1:F103/FX3U/usart.c index ac724e2..df891ab 100644 --- a/F1:F103/FX3U/usart.c +++ b/F1:F103/FX3U/usart.c @@ -56,18 +56,18 @@ int usart_getline(char **line){ int usart_transmit(){ register int l = odatalen[tbufno]; if(!l) return 0; - uint32_t tmout = 1600000; + uint32_t tmout = 18000000; while(!usart_txrdy){ IWDG->KR = IWDG_REFRESH; if(--tmout == 0) return 0; }; // wait for previos buffer transmission usart_txrdy = 0; - odatalen[tbufno] = 0; DMA1_Channel4->CCR &= ~DMA_CCR_EN; DMA1_Channel4->CMAR = (uint32_t) tbuf[tbufno]; // mem DMA1_Channel4->CNDTR = l; DMA1_Channel4->CCR |= DMA_CCR_EN; tbufno = !tbufno; + odatalen[tbufno] = 0; return l; } @@ -82,6 +82,7 @@ int usart_putchar(const char ch){ int usart_send(const char *str){ int l = 0; while(*str){ + IWDG->KR = IWDG_REFRESH; if(odatalen[tbufno] == UARTBUFSZO){ if(!usart_transmit()) return 0; } @@ -100,7 +101,6 @@ int usart_send(const char *str){ */ void usart_setup(uint32_t speed){ - uint32_t tmout = 16000000; // PA9 - Tx, PA10 - Rx RCC->APB2ENR |= RCC_APB2ENR_USART1EN; RCC->AHBENR |= RCC_AHBENR_DMA1EN; @@ -116,7 +116,11 @@ void usart_setup(uint32_t speed){ // setup usart1 USART1->BRR = 72000000 / speed; USART1->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE; // 1start,8data,nstop; enable Rx,Tx,USART - while(!(USART1->SR & USART_SR_TC)){if(--tmout == 0) break;} // polling idle frame Transmission + uint32_t tmout = 16000000; + while(!(USART1->SR & USART_SR_TC)){ + IWDG->KR = IWDG_REFRESH; + if(--tmout == 0) break; + } // polling idle frame Transmission USART1->SR = 0; // clear flags USART1->CR1 |= USART_CR1_RXNEIE; // allow Rx IRQ USART1->CR3 = USART_CR3_DMAT; // enable DMA Tx diff --git a/F1:F103/FX3U/usart.h b/F1:F103/FX3U/usart.h index 3b296e7..5800e9b 100644 --- a/F1:F103/FX3U/usart.h +++ b/F1:F103/FX3U/usart.h @@ -21,8 +21,8 @@ #include // input and output buffers size -#define UARTBUFSZI (64) -#define UARTBUFSZO (128) +#define UARTBUFSZI (196) +#define UARTBUFSZO (256) #define usartrx() (usart_linerdy) #define usartovr() (usart_bufovr) diff --git a/F1:F103/FX3U/version.inc b/F1:F103/FX3U/version.inc index 06d0e3f..60acc86 100644 --- a/F1:F103/FX3U/version.inc +++ b/F1:F103/FX3U/version.inc @@ -1,2 +1,2 @@ -#define BUILD_NUMBER "85" -#define BUILD_DATE "2024-09-24" +#define BUILD_NUMBER "105" +#define BUILD_DATE "2024-09-26"