add modbus (not tested yet)

This commit is contained in:
2024-09-19 17:32:05 +03:00
parent bdbd7d68d9
commit 22a205001a
22 changed files with 1030 additions and 118 deletions

View File

@@ -7,6 +7,9 @@ You can see pinout table in file `hardware.c`.
## Serial protocol (each string ends with '\n').
(TODO: add new)
```
commands format: parameter[number][=setter]
parameter [CAN idx] - help
@@ -49,11 +52,11 @@ Value in square brackets is CAN bus command code.
All data in little-endian format!
BIT - MEANING
BYTE - MEANING
0, 1 - (uint16_t) - command code (value in square brackets upper);
2 - (uint8_t) - parameter number (e.g. ADC channel or X/Y channel number), 0..127 [ORed with 0x80 for setter];
2 - (uint8_t) - parameter number (e.g. ADC channel or X/Y channel number), 0..126 [ORed with 0x80 for setter], 127 means "no parameter";
3 - (uint8_t) - error code (only when device answers for requests);
@@ -73,3 +76,7 @@ BIT - MEANING
5 - `ERR_CANTRUN` - can't run given command due to bad parameters or other reason.
## MODBUS-RTU protocol
(TODO)

View File

@@ -236,6 +236,7 @@ void CAN_proc(){
CAN_status CAN_send(CAN_message *message){
if(!message) return CAN_ERR;
IWDG->KR = IWDG_REFRESH;
uint8_t *msg = message->data;
uint8_t len = message->length;
uint16_t target_id = message->ID;

View File

@@ -20,7 +20,7 @@
#include "canproto.h"
#include "flash.h"
#include "hardware.h"
#include "proto.h"
#include "modbusrtu.h"
#include "strfunc.h"
#include "usart.h"
@@ -41,49 +41,53 @@ static errcodes reset(CAN_message _U_ *msg){
// 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;
Tms = MSGP_GET_U32(msg);
}
FIXDL(msg);
MSGP_SET_U32(msg, Tms);
return ERR_OK;
}
// get MCU T
static errcodes mcut(CAN_message *msg){
FIXDL(msg);
*(int32_t*)&msg->data[4] = getMCUtemp();
MSGP_SET_U32(msg, getMCUtemp());
return ERR_OK;
}
// get ADC raw values
static errcodes adcraw(CAN_message *msg){
FIXDL(msg);
uint8_t no = msg->data[2] & ~SETTER_FLAG;
uint8_t no = PARVAL(msg->data);
if(no >= ADC_CHANNELS) return ERR_BADPAR;
*(uint32_t*)&msg->data[4] = getADCval(no);
MSGP_SET_U32(msg, getADCval(no));
return ERR_OK;
}
// set common CAN ID / get CAN IN in
static errcodes canid(CAN_message *msg){
if(ISSETTER(msg->data)){
the_conf.CANIDin = the_conf.CANIDout = *(uint32_t*)&msg->data[4];
the_conf.CANIDin = the_conf.CANIDout = (uint16_t)MSGP_GET_U32(msg);
CAN_reinit(0); // setup with new ID
}else FIXDL(msg);
*(uint32_t*)&msg->data[4] = the_conf.CANIDin;
}
FIXDL(msg);
MSGP_SET_U32(msg, the_conf.CANIDin);
return ERR_OK;
}
// get/set input CAN ID
static errcodes canidin(CAN_message *msg){
if(ISSETTER(msg->data)){
the_conf.CANIDin = *(uint32_t*)&msg->data[4];
the_conf.CANIDin = (uint16_t)MSGP_GET_U32(msg);
CAN_reinit(0); // setup with new ID
}else FIXDL(msg);
*(uint32_t*)&msg->data[4] = the_conf.CANIDin;
}
FIXDL(msg);
MSGP_SET_U32(msg, the_conf.CANIDin);
return ERR_OK;
}
// get/set output CAN ID
static errcodes canidout(CAN_message *msg){
if(ISSETTER(msg->data)){
the_conf.CANIDout = *(uint32_t*)&msg->data[4];
}else FIXDL(msg);
*(uint32_t*)&msg->data[4] = the_conf.CANIDout;
the_conf.CANIDout = (uint16_t)MSGP_GET_U32(msg);
}
FIXDL(msg);
MSGP_SET_U32(msg, the_conf.CANIDout);
return ERR_OK;
}
@@ -101,32 +105,42 @@ static errcodes erasestor(CAN_message _U_ *msg){
// relay management
static errcodes relay(CAN_message *msg){
uint8_t no = OUTMAX+1;
if(msg->length > 2) no = msg->data[2] & ~SETTER_FLAG;
if(msg->length > 2){
uint8_t chnl = PARVAL(msg->data);
if(chnl != NO_PARNO) no = chnl;
}
if(ISSETTER(msg->data)){
if(set_relay(no, *(uint32_t*)&msg->data[4]) < 0) return ERR_BADPAR;
}else FIXDL(msg);
if(set_relay(no, MSGP_GET_U32(msg)) < 0) return ERR_BADPAR;
}
FIXDL(msg);
int rval = get_relay(no);
if(rval < 0) return ERR_BADPAR;
*(uint32_t*)&msg->data[4] = (uint32_t)rval;
MSGP_SET_U32(msg, (uint32_t)rval);
return ERR_OK;
}
// get current ESW status
static errcodes esw(CAN_message *msg){
uint8_t no = INMAX+1;
if(msg->length > 2) no = msg->data[2] & ~SETTER_FLAG;
if(msg->length > 2){
uint8_t chnl = PARVAL(msg->data);
if(chnl != NO_PARNO) no = chnl;
}
int val = get_esw(no);
if(val < 0) return ERR_BADPAR;
*(uint32_t*)&msg->data[4] = (uint32_t) val;
MSGP_SET_U32(msg, (uint32_t)val);
FIXDL(msg);
return ERR_OK;
}
// bounce-free ESW get status
static errcodes eswg(CAN_message *msg){
uint8_t no = INMAX+1;
if(msg->length > 2) no = msg->data[2] & ~SETTER_FLAG;
if(msg->length > 2){
uint8_t chnl = PARVAL(msg->data);
if(chnl != NO_PARNO) no = chnl;
}
uint32_t curval = get_ab_esw();
if(no > INMAX) *(uint32_t*)&msg->data[4] = curval;
else *(uint32_t*)&msg->data[4] = (curval & (1<<no)) ? 0 : 1;
if(no > INMAX) MSGP_SET_U32(msg, curval);
else MSGP_SET_U32(msg, (curval & (1<<no)) ? 0 : 1);
FIXDL(msg);
return ERR_OK;
}
@@ -140,41 +154,49 @@ static errcodes led(CAN_message *m){
// 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.bouncetime; break;
case CMD_USARTSPEED: ptr = &the_conf.usartspeed; break;
uint16_t cmd = *(uint16_t*)msg->data;
uint32_t *ptr = NULL, val;
switch(cmd){
case CMD_CANSPEED: ptr = &the_conf.CANspeed; CAN_reinit(MSGP_GET_U32(msg)); break;
case CMD_BOUNCE: ptr = &the_conf.bouncetime; break;
case CMD_USARTSPEED: ptr = &the_conf.usartspeed; break;
case CMD_INCHNLS: val = inchannels(); ptr = &val; break;
case CMD_OUTCHNLS: val = outchannels(); ptr = &val; break;
case CMD_MODBUSID: ptr = &the_conf.modbusID; 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;
if(cmd == CMD_INCHNLS || cmd == CMD_OUTCHNLS) return ERR_CANTRUN; // can't set getter-only
*ptr = MSGP_GET_U32(msg);
}
FIXDL(msg);
MSGP_SET_U32(msg, *ptr);
return ERR_OK;
}
/*
// common bitflag setter/getter
// without parno - all flags, with - flag with number N
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;
uint8_t idx = NO_PARNO;
if(msg->length > 2){
idx = PARVAL(msg->data);
if(idx != NO_PARNO && idx > MAX_FLAG_BITNO) return ERR_BADPAR;
}
if(bit > 31) return ERR_CANTRUN; // unknown error
if(ISSETTER(msg->data)){
if(msg->data[4]) the_conf.flags |= 1<<bit;
else the_conf.flags &= ~(1<<bit);
}else FIXDL(msg);
*(uint32_t*)&msg->data[4] = (the_conf.flags & (1<<bit)) ? 1 : 0;
uint32_t val = MSGP_GET_U32(msg);
if(idx == NO_PARNO) the_conf.flags.u32 = val; // all bits
else{ // only selected
if(val) the_conf.flags.u32 |= 1 << idx;
else the_conf.flags.u32 &= ~(1 << idx);
}
}
FIXDL(msg);
if(idx == NO_PARNO) MSGP_SET_U32(msg, the_conf.flags.u32);
else MSGP_SET_U32(msg, (the_conf.flags.u32 & (1<<idx)) ? 1:0);
return ERR_OK;
}
************ END of all common functions list (for `funclist`) ************/
/************ END of all common functions list (for `funclist`) ************/
typedef struct{
errcodes (*fn)(CAN_message *msg); // function to run with can packet `msg`
@@ -203,6 +225,11 @@ static const commonfunction funclist[CMD_AMOUNT] = {
[CMD_BOUNCE] = {u32setget, 0, 1000, 0},
[CMD_USARTSPEED] = {u32setget, 1200, 3000000, 0},
[CMD_LED] = {led, 0, 0, 0},
[CMD_FLAGS] = {flagsetget, 0, 0, 0},
[CMD_INCHNLS] = {u32setget, 0, 0, 0},
[CMD_OUTCHNLS] = {u32setget, 0, 0, 0},
[CMD_MODBUSID] = {u32setget, 0, MODBUS_MAX_ID, 0}, // 0 - master!
[CMD_MODBUSSPEED] = {u32setget, 1200, 115200, 0},
};
@@ -225,7 +252,6 @@ void run_can_cmd(CAN_message *msg){
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){

View File

@@ -25,9 +25,11 @@
#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)
#define NO_PARNO (0x7f)
// base value of parameter (even if it is a setter)
#define PARBASE(x) (x & 0x7f)
// get parameter value of msg->data
#define PARVAL(data) (data[2] & 0x7f)
// make error for CAN answer
#define FORMERR(m, err) do{m->data[3] = err; if(m->length < 4) m->length = 4;}while(0)
@@ -43,6 +45,19 @@ typedef enum{
ERR_AMOUNT // amount of error codes
} errcodes;
// 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 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)
// set uint32_t data
#define MSG_SET_U32(msg, d) do{*((uint32_t*)(&msg.data[4])) = (d);}while(0)
#define MSGP_SET_U32(msg, d) do{*((uint32_t*)(&msg->data[4])) = (d);}while(0)
// get uint32_t data
#define MSG_GET_U32(msg) (*(uint32_t*)&msg.data[4])
#define MSGP_GET_U32(msg) (*(uint32_t*)&msg->data[4])
// CAN commands indexes
enum{
CMD_PING, // just ping
@@ -62,6 +77,11 @@ enum{
CMD_BOUNCE, // get/set bounce constant (ms)
CMD_USARTSPEED, // get/set USART1 speed (if encoder on RS-422)
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_MODBUSSPEED,// speed of modbus interface
// should be the last:
CMD_AMOUNT // amount of CAN commands
};

View File

@@ -18,6 +18,7 @@
#include "stm32f1.h"
#include "modbusrtu.h"
#include "flash.h"
#include "strfunc.h"
#include "usart.h"
@@ -34,6 +35,8 @@ static uint32_t maxCnum = 1024 / sizeof(user_conf); // can't use blocksize here
,.CANIDout = 2 \
,.usartspeed = 115200 \
,.bouncetime = 50 \
,.modbusID = MODBUS_MASTER_ID \
,.modbusspeed = 9600 \
}
static int write2flash(const void*, const void*, uint32_t);

View File

@@ -23,6 +23,15 @@
#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)
typedef union{
uint32_t u32;
struct{
uint32_t sw_send_relay_cmd; // switching ESW state will send also CMD_RELAY command with CANID_OUT
};
} confflags_t;
/*
* struct to save user configurations
*/
@@ -30,10 +39,13 @@ typedef struct __attribute__((aligned(4))){
uint16_t userconf_sz; // "magick number"
uint16_t CANIDin; // CAN bus device ID for input commands
uint16_t CANIDout; // -//- for output signals
uint16_t reserved;
uint16_t reserved; // added for 32-bit align
uint32_t bouncetime; // anti-bounce timeout (ms)
uint32_t usartspeed; // RS-232 speed
uint32_t CANspeed; // CAN bus speed
confflags_t flags; // different flags
uint32_t modbusID; // MODBUS-RTU ID (0 for master)
uint32_t modbusspeed; // Speed of modbus interface
} user_conf;
extern user_conf the_conf;

Binary file not shown.

View File

@@ -10,6 +10,10 @@ flash.h
hardware.c
hardware.h
main.c
modbusproto.c
modbusproto.h
modbusrtu.c
modbusrtu.h
proto.c
proto.h
strfunc.c

View File

@@ -20,10 +20,12 @@
#include "canproto.h"
#include "flash.h"
#include "hardware.h"
/*
#ifdef EBUG
#include "strfunc.h"
#include "uchar.h"
#include "usart.h"
#endif
*/
/* pinout:
@@ -257,11 +259,18 @@ void proc_esw(){
if(oldesw != ESW_ab_values){
//usart_send("esw="); usart_send(u2str(ESW_ab_values)); newline();
CAN_message msg = {.ID = the_conf.CANIDout, .length = 8};
msg.data[0] = CMD_GETESW;
*((uint32_t*)(&msg.data[4])) = ESW_ab_values;
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)) return;
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;
while(Tms - Tstart < SEND_TIMEOUT_MS){
if(CAN_OK == CAN_send(&msg)) break;
}
}
}
}

View File

@@ -33,6 +33,9 @@
// max number of in/out pins
#define INMAX (15)
#define OUTMAX (11)
// and 8-multiple
#define INMAXBYTES (2)
#define OUTMAXBYTES (2)
// onboard LED - PD10
#define LEDPORT GPIOD

View File

@@ -20,6 +20,8 @@
#include "can.h"
#include "flash.h"
#include "hardware.h"
#include "modbusproto.h"
#include "modbusrtu.h"
#include "proto.h"
#include "strfunc.h"
@@ -30,17 +32,31 @@ void sys_tick_handler(void){
++Tms;
}
TRUE_INLINE void parsemodbus(){
if(the_conf.modbusID != MODBUS_MASTER_ID){ // slave
modbus_request req;
if(1 == modbus_get_request(&req))
parse_modbus_request(&req);
}else{ // master
modbus_response res;
if(1 == modbus_get_response(&res))
parse_modbus_response(&res);
}
}
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
adc_setup();
usart_setup(the_conf.usartspeed);
CAN_setup(the_conf.CANspeed);
RCC->CSR |= RCC_CSR_RMVF; // remove reset flags
modbus_setup(the_conf.modbusspeed);
#ifndef EBUG
iwdg_setup();
#endif
@@ -80,6 +96,7 @@ int main(void){
int g = usart_getline(&str);
if(g < 0) usart_send("USART IN buffer overflow!\n");
else if(g > 0) cmd_parser(str);
parsemodbus();
}
return 0;
}

261
F1:F103/FX3U/modbusproto.c Normal file
View File

@@ -0,0 +1,261 @@
/*
* This file is part of the fx3u project.
* Copyright 2024 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "adc.h"
#include "flash.h"
#include "hardware.h"
#include "modbusproto.h"
#include "modbusrtu.h"
#include "strfunc.h"
#include "usart.h"
// send error
static void senderr(modbus_request *r, uint8_t exception){
modbus_response resp = {.ID = the_conf.modbusID, .Fcode = r->Fcode | MODBUS_RESPONSE_ERRMARK, .datalen = exception};
modbus_send_response(&resp);
}
// relays status
TRUE_INLINE void readcoil(modbus_request *r){
// starting number shoul be 0
if(r->startreg){
senderr(r, ME_ILLEGAL_ADDRESS);
return;
}
// amount of bytes: should be 8-multiple
int amount = r->regno >> 3;
if(amount == 0 || (r->regno & 7) || amount > OUTMAXBYTES){
senderr(r, ME_ILLEGAL_VALUE);
return;
}
uint8_t bytes[OUTMAXBYTES] = {0};
int curidx = OUTMAXBYTES;
int vals = get_relay(OUTMAX+1);
for(int i = 0; i < amount; ++i){
bytes[--curidx] = vals & 0xff;
vals >>= 8;
}
modbus_response resp = {.Fcode = r->Fcode, .ID = the_conf.modbusID, .data = bytes+curidx, .datalen = amount};
modbus_send_response(&resp);
}
// input status
TRUE_INLINE void readdiscr(modbus_request *r){
if(r->startreg){
senderr(r, ME_ILLEGAL_ADDRESS);
return;
}
// amount of bytes: should be 8-multiple
int amount = r->regno >> 3;
if(amount == 0 || (r->regno & 7) || amount > INMAXBYTES){
senderr(r, ME_ILLEGAL_VALUE);
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;
vals >>= 8;
}
modbus_response resp = {.Fcode = r->Fcode, .ID = the_conf.modbusID, .data = bytes+curidx, .datalen = amount};
modbus_send_response(&resp);
}
// registers: read only by one!
// !!! To simplify code I suppose that all registers read are 32-bit!
TRUE_INLINE void readreg(modbus_request *r){
if(r->regno != 1){
senderr(r, ME_ILLEGAL_VALUE);
return;
}
uint32_t regval;
switch(r->startreg){
case MR_TIME:
regval = Tms;
break;
case MR_LED:
regval = LED(-1);
break;
case MR_INCHANNELS:
regval = inchannels();
break;
case MR_OUTCHANNELS:
regval = outchannels();
break;
default:
senderr(r, ME_ILLEGAL_ADDRESS);
return;
}
regval = __builtin_bswap32(regval);
modbus_response resp = {.Fcode = r->Fcode, .ID = the_conf.modbusID, .data = (uint8_t*)&regval, .datalen = 4};
modbus_send_response(&resp);
}
// read ADC values; startreg = N of starting sensor, regno = N values
TRUE_INLINE void readadc(modbus_request *r){
if(r->startreg >= ADC_CHANNELS){
senderr(r, ME_ILLEGAL_ADDRESS);
return;
}
int nlast = r->regno + r->startreg;
if(r->regno == 0 || nlast > ADC_CHANNELS){
senderr(r, ME_ILLEGAL_VALUE);
return;
}
uint16_t vals[ADC_CHANNELS];
for(int i = r->startreg; i < nlast; ++i){
uint16_t v = getADCval(i);
vals[i] = __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);
}
TRUE_INLINE void writecoil(modbus_request *r){
if(r->startreg > OUTMAX || set_relay(r->startreg, r->regno) < 0){
senderr(r, ME_ILLEGAL_ADDRESS);
return;
}
modbus_send_request(r); // answer with same data
}
TRUE_INLINE void writereg(modbus_request *r){
switch(r->startreg){
case MR_LED:
LED(r->regno);
break;
case MR_RESET:
NVIC_SystemReset();
break;
default:
senderr(r, ME_ILLEGAL_ADDRESS);
return;
}
modbus_send_request(r);
}
// support ONLY write to ALL!
// data - by bits, like in readcoil
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){
senderr(r, ME_ILLEGAL_VALUE);
return;
}
uint32_t v = 0;
for(int i = 0; i < amount; ++i){
v |= r->data[i];
v <<= 8;
}
if(set_relay(OUTMAX+1, v) < 0){
senderr(r, ME_NACK);
return;
}
r->datalen = 0;
modbus_send_request(r);
}
// amount of registers should be equal 1, data should have 4 bytes
TRUE_INLINE void writeregs(modbus_request *r){
if(r->datalen != 4 || r->regno != 1){
senderr(r, ME_ILLEGAL_VALUE);
return;
}
uint32_t val = __builtin_bswap32(*(uint32_t*)r->data);
switch(r->startreg){
case MR_TIME:
Tms = val;
break;
default:
senderr(r, ME_ILLEGAL_ADDRESS);
return;
}
r->datalen = 0;
modbus_send_request(r);
}
// get request as slave and send answer about some registers
// DO NOT ANSWER for broadcasting requests!
// Understand only requests with codes <= 6
void parse_modbus_request(modbus_request *r){
if(!r) return;
switch(r->Fcode){
case MC_READ_COIL:
readcoil(r);
break;
case MC_READ_DISCRETE:
readdiscr(r);
break;
case MC_READ_HOLDING_REG:
readreg(r);
break;
case MC_READ_INPUT_REG:
readadc(r);
break;
case MC_WRITE_COIL:
writecoil(r);
break;
case MC_WRITE_REG:
writereg(r);
break;
case MC_WRITE_MUL_COILS:
writecoils(r);
break;
case MC_WRITE_MUL_REGS: // write uint32_t as 2 registers
writeregs(r);
break;
default:
senderr(r, ME_ILLEGAL_FUNCION);
}
}
// get responce as master and show it on terminal
void parse_modbus_response(modbus_response *r){
if(!r) return;
if(r->Fcode & MODBUS_RESPONSE_ERRMARK){ // error - three bytes
usart_send("MODBUS ERR (ID=");
printuhex(r->ID);
usart_send(", Fcode=");
printuhex(r->Fcode & ~MODBUS_RESPONSE_ERRMARK);
usart_send(") > ");
printuhex(r->datalen);
newline();
return;
}
// regular answer
usart_send("MODBUS RESP (ID=");
printuhex(r->ID);
usart_send(", Fcode=");
printuhex(r->Fcode & ~MODBUS_RESPONSE_ERRMARK);
usart_send(", Datalen=");
printu(r->datalen);
usart_send(") > ");
if(r->datalen){
for(int i = 0; i < r->datalen; ++i){
//uint16_t nxt = r->data[i] >> 8 | r->data[i] << 8;
printuhex(r->data[i]);
usart_putchar(' ');
}
}
newline();
}

View File

@@ -0,0 +1,48 @@
/*
* This file is part of the fx3u project.
* Copyright 2024 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "modbusrtu.h"
#include "strfunc.h"
/*
MODBUS PROTOCOL:
uint32_t translates as two uint16_t in big-endian format
MC_READ_COIL - coil values (by bits)
MC_READ_DISCRETE - discrete -//-
MC_READ_HOLDING_REG - read time/led/inch/outch
MC_READ_INPUT_REG - read ADC channel (regno is ADC ch number)
MC_WRITE_COIL - change single relay
MC_WRITE_REG - set/reset LED, restart MCU
MC_WRITE_MUL_COILS - change several relays
MC_WRITE_MUL_REGS - change Tms
*/
// holding registers list (comment - corresponding CANbus command)
typedef enum{
MR_RESET, // CMD_RESET
MR_TIME, // CMD_TIME
MR_LED, // CMD_LED
MR_INCHANNELS, // CMD_INCHNLS
MR_OUTCHANNELS, // CMD_OUTCHNLS
} modbus_registers;
void parse_modbus_request(modbus_request *r);
void parse_modbus_response(modbus_response *r);

238
F1:F103/FX3U/modbusrtu.c Normal file
View File

@@ -0,0 +1,238 @@
/*
* This file is part of the fx3u project.
* Copyright 2024 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "modbusrtu.h"
#include "flash.h"
#include "strfunc.h"
#ifdef EBUG
#include "usart.h"
#endif
#include <stm32f1.h>
#include <string.h> // memcpy
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
,bufovr = 0 // input buffer overfull
;
static int rbufno = 0, tbufno = 0; // current rbuf/tbuf numbers
static uint8_t rbuf[2][MODBUSBUFSZI], tbuf[2][MODBUSBUFSZO]; // receive & transmit buffers
static uint8_t *recvdata = NULL;
#define packCRC(d, l) *((uint16_t*) &d[l])
// calculate CRC for given data
static uint16_t getCRC(uint8_t *data, int l){
uint16_t crc = 0xFFFF;
for(int pos = 0; pos < l; ++pos){
crc ^= (uint16_t)data[pos];
for(int i = 8; i; --i){
if((crc & 1)){
crc >>= 1;
crc ^= 0xA001;
}else crc >>= 1;
}
}
// CRC have swapped bytes, so we can just send it as *((uint16_t*)&data[x]) = CRC
return crc;
}
/**
* return length of received data without CRC or -1 if buffer overflow or bad CRC
*/
int modbus_receive(uint8_t **packet){
if(!modbus_rdy) return 0;
if(bufovr){
DBG("Modbus buffer overflow\n");
bufovr = 0;
modbus_rdy = 0;
return -1;
}
*packet = recvdata;
modbus_rdy = 0;
int x = dlen - 2;
dlen = 0;
uint16_t chk = getCRC(recvdata, x);
if(packCRC(recvdata, x) != chk){
DBG("Bad CRC\n");
return -1;
}
return x;
}
// send current tbuf
static int senddata(int l){
uint32_t tmout = 1600000;
while(!modbus_txrdy){
IWDG->KR = IWDG_REFRESH;
if(--tmout == 0) return 0;
}; // wait for previos buffer transmission
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;
return l;
}
// transmit raw data with length l; amount of bytes (without CRC) sent
int modbus_send(uint8_t *data, int l){
if(l < 1) return 0;
if(l > MODBUSBUFSZO - 2) return -1;
memcpy(tbuf[tbufno], data, l);
packCRC(tbuf[tbufno], l) = getCRC(data, l);
return senddata(l);
}
// send request: return the same as modbus_receive()
int modbus_send_request(modbus_request *r){
uint8_t *curbuf = tbuf[tbufno];
int n = 6;
*curbuf++ = r->ID;
*curbuf++ = r->Fcode;
*curbuf++ = r->startreg >> 8; // H
*curbuf++ = (uint8_t) r->startreg; // L
*curbuf++ = r->regno >> 8; // H
*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;
memcpy(curbuf, r->data, r->datalen);
n += r->datalen;
}
return senddata(n);
}
// return -1 in case of error, 0 if no data received, 1 if got good packet
int modbus_get_request(modbus_request* r){
if(!r) return -1;
uint8_t *pack;
int l = modbus_receive(&pack);
if(l < 1) return l;
if(l < 6) return -1; // not a request
// check "broadcasting" and common requests
if(*pack && *pack != the_conf.modbusID) return 0; // alien request
r->ID = *pack++;
r->Fcode = *pack++;
r->startreg = pack[0] << 8 | pack[1];
r->regno = pack[2] << 8 | pack[3];
if(l > 6){ // request with data
if(r->Fcode != MC_WRITE_MUL_COILS && r->Fcode != MC_WRITE_MUL_REGS) return -1; // bad request
r->datalen = pack[4];
if(r->datalen > l-6) r->datalen = l-6; // fix if data bytes less than field
r->data = pack + 5;
}else{
r->datalen = 0;
r->data = NULL;
}
return 1;
}
// send responce: return the same as modbus_receive()
int modbus_send_response(modbus_response *r){
uint8_t *curbuf = tbuf[tbufno];
int len = 3; // packet data length without CRC
*curbuf++ = r->ID;
*curbuf++ = r->Fcode;
*curbuf++ = r->datalen;
if(0 == (r->Fcode & MODBUS_RESPONSE_ERRMARK)){ // data
len += r->datalen;
if(len > MODBUSBUFSZO - 2) return -1; // too much data
memcpy(curbuf, r->data, r->datalen);
}
return senddata(len);
}
// get recponce; warning: all data is a pointer to last rbuf, it could be corrupted after next reading
int modbus_get_response(modbus_response* r){
if(!r) return -1;
uint8_t *pack;
int l = modbus_receive(&pack);
if(l < 1) return l;
if(l < 3) return -1; // not a responce
r->ID = *pack++;
r->Fcode = *pack++;
r->datalen = *pack++;
// error
if(r->Fcode & MODBUS_RESPONSE_ERRMARK) r->data = NULL;
else{
// wrong datalen - fix
if(r->datalen != l-3){ r->datalen = l-3; }
r->data = pack;
}
return 1;
}
// USART4: PC10 - Tx, PC11 - Rx
void modbus_setup(uint32_t speed){
uint32_t tmout = 16000000;
// PA9 - Tx, PA10 - Rx
RCC->APB1ENR |= RCC_APB1ENR_UART4EN;
RCC->AHBENR |= RCC_AHBENR_DMA2EN;
GPIOA->CRH = (GPIOA->CRH & ~(CRH(9,0xf)|CRH(10,0xf))) |
CRH(9, CNF_AFPP|MODE_NORMAL) | CRH(10, CNF_FLINPUT|MODE_INPUT);
// 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
// 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);
// 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
while(!(UART4->SR & USART_SR_TC)){if(--tmout == 0) break;} // polling idle frame Transmission
UART4->SR = 0; // clear flags
UART4->CR1 |= USART_CR1_RXNEIE | USART_CR1_IDLEIE; // allow Rx and IDLE IRQ
UART4->CR3 = USART_CR3_DMAT; // enable DMA Tx
NVIC_EnableIRQ(UART4_IRQn);
}
void uart4_isr(){
if(UART4->SR & USART_SR_IDLE){ // idle - end of frame
modbus_rdy = 1;
dlen = idatalen[rbufno];
recvdata = rbuf[rbufno];
// prepare other buffer
rbufno = !rbufno;
idatalen[rbufno] = 0;
(void) UART4->DR; // clear IDLE flag by reading DR
}
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;
}else{ // buffer overrun
bufovr = 1;
idatalen[rbufno] = 0;
}
}
}
void dma2_channel4_5_isr(){
if(DMA2->ISR & DMA_ISR_TCIF5){ // Tx
DMA2->IFCR = DMA_IFCR_CTCIF5; // clear TC flag
modbus_txrdy = 1;
}
}

80
F1:F103/FX3U/modbusrtu.h Normal file
View File

@@ -0,0 +1,80 @@
/*
* This file is part of the fx3u project.
* Copyright 2024 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h>
// input and output buffers size
#define MODBUSBUFSZI (64)
#define MODBUSBUFSZO (64)
#define MODBUS_MASTER_ID (0)
#define MODBUS_MAX_ID (247)
// some exceptions
typedef enum{
ME_ILLEGAL_FUNCION = 1, // The function code received in the request is not an authorized action for the slave.
ME_ILLEGAL_ADDRESS = 2, // The data address received by the slave is not an authorized address for the slave.
ME_ILLEGAL_VALUE = 3, // The value in the request data field is not an authorized value for the slave.
ME_SLAVE_FAILURE = 4, // The slave fails to perform a requested action because of an unrecoverable error.
ME_ACK = 5, // The slave accepts the request but needs a long time to process it.
ME_SLAVE_BUSY = 6, // The slave is busy processing another command.
ME_NACK = 7, // The slave cannot perform the programming request sent by the master.
ME_PARITY_ERROR = 8, // Memory parity error: slave is almost dead.
} modbus_exceptions;
// common function codes; "by bits" means that output data length = (requested+15)/16
typedef enum{
MC_READ_COIL = 1, // read output[s] state (by bits)
MC_READ_DISCRETE = 2, // read input[s] state (by bits!)
MC_READ_HOLDING_REG = 3,
MC_READ_INPUT_REG = 4,
MC_WRITE_COIL = 5,
MC_WRITE_REG = 6,
MC_WRITE_MUL_COILS = 0xF,
MC_WRITE_MUL_REGS = 0x10,
} modbus_fcode;
// modbus master request (without CRC)
typedef struct{
uint8_t ID; // slave ID
uint8_t Fcode; // functional code
uint16_t startreg; // started register or single register address
uint16_t regno; // number of registers or data to write
uint8_t datalen;// data for 0xf/0x10
uint8_t *data;
} modbus_request;
// responce->Fcode & RESPONCE_ERRMARK -> error packet
#define MODBUS_RESPONSE_ERRMARK (0x80)
// modbus slave responce (without CRC)
typedef struct{
uint8_t ID; // slave ID
uint8_t Fcode; // functional code
uint8_t datalen; // length of data in BYTES! (or exception code)
uint8_t *data; // data (or NULL for error packet); BE CAREFUL: all data is big-endian!
} modbus_response;
void modbus_setup(uint32_t speed);
int modbus_receive(uint8_t **packet);
int modbus_get_request(modbus_request* r);
int modbus_get_response(modbus_response* r);
int modbus_send(uint8_t *data, int l);
int modbus_send_request(modbus_request *r);
int modbus_send_response(modbus_response *r);

View File

@@ -22,6 +22,7 @@
#include "flash.h"
#include "hardware.h"
#include "proto.h"
#include "modbusrtu.h"
#include "strfunc.h"
#include "usart.h"
#include "version.inc"
@@ -38,6 +39,8 @@ 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_AMOUNT
} text_cmd;
@@ -56,6 +59,7 @@ static const funcdescr funclist[] = {
{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"},
{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"},
@@ -64,7 +68,11 @@ static const funcdescr funclist[] = {
{"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"},
{NULL, 0, "IN/OUT"},
{"adc", CMD_ADCRAW, "get raw ADC values for given channel"},
@@ -73,9 +81,11 @@ static const funcdescr funclist[] = {
{"led", CMD_LED, "work with onboard LED"},
{"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"},
{"s", -TCMD_CANSEND, "send CAN message: ID 0..8 data bytes"},
{"time", CMD_TIME, "get/set time (1ms, 32bit)"},
{"wdtest", -TCMD_WDTEST, "test watchdog"},
{NULL, 0, NULL} // last record
@@ -83,7 +93,7 @@ static const funcdescr funclist[] = {
static void printhelp(){
const funcdescr *c = funclist;
usart_send("https://github.com/eddyem/stm32samples/tree/master/F1:F103/FX3U#" BUILD_NUMBER " @ " BUILD_DATE "\n");
usart_send("https://github.com/eddyem/stm32samples/tree/master/F1:F103/FX3U build #" BUILD_NUMBER " @ " BUILD_DATE "\n");
usart_send("commands format: parameter[number][=setter]\n");
usart_send("parameter [CAN idx] - help\n");
usart_send("--------------------------\n");
@@ -107,9 +117,9 @@ static void printhelp(){
}
}
/*********** START of all common functions list (for `textfunctions`) ***********/
/*********** START of all text functions list ***********/
static errcodes cansnif(const char *str){
static errcodes cansnif(const char *str, text_cmd _U_ cmd){
uint32_t U;
if(str){
if(*str == '=') str = omit_spaces(str + 1);
@@ -122,7 +132,7 @@ static errcodes cansnif(const char *str){
return ERR_OK;
}
static errcodes canbuserr(const char *str){
static errcodes canbuserr(const char *str, text_cmd _U_ cmd){
uint32_t U;
if(str){
if(*str == '=') str = omit_spaces(str + 1);
@@ -135,21 +145,20 @@ static errcodes canbuserr(const char *str){
return ERR_OK;
}
static errcodes wdtest(const char _U_ *str){
static errcodes wdtest(const char _U_ *str, text_cmd _U_ cmd){
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",
"sw_send_relay_cmd",
NULL
};*/
};
static errcodes dumpconf(const char _U_ *str){
static errcodes dumpconf(const char _U_ *str, text_cmd _U_ cmd){
#ifdef EBUG
uint32_t sz = FLASH_SIZE*1024;
usart_send("flashsize="); printu(sz); usart_putchar('/');
@@ -167,21 +176,23 @@ static errcodes dumpconf(const char _U_ *str){
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;
int bit = 0;
usart_send("\nflags="); usart_putchar('='); printuhex(the_conf.flags.u32);
while(*p){
newline();
usart_send(*p); usart_putchar('='); usart_putchar((the_conf.flags & (1<<bit)) ? '1' : '0');
if(++bit > 31) break;
newline(); usart_putchar(' ');
usart_send(*p); usart_putchar('='); usart_putchar((the_conf.flags.u32 & (1<<bit)) ? '1' : '0');
if(++bit > MAX_FLAG_BITNO) break;
++p;
}*/
}
newline();
return ERR_OK;
}
static errcodes cansend(const char *txt){
static errcodes cansend(const char *txt, text_cmd _U_ cmd){
CAN_message canmsg;
bzero(&canmsg, sizeof(canmsg));
int ctr = -1;
@@ -221,10 +232,70 @@ static errcodes cansend(const char *txt){
return ERR_CANTRUN;
}
/************ END of all common functions list (for `textfunctions`) ************/
// change configuration flags by one
static errcodes confflags(const char _U_ *str, text_cmd cmd){
if(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;
break;
default:
return ERR_BADCMD;
}
}
uint8_t val = 0, idx = 0;
switch(cmd){
case TCMD_SW_SEND_RELAY:
val = the_conf.flags.sw_send_relay_cmd;
idx = 0;
break;
default:
return ERR_BADCMD;
}
usart_send(bitfields[idx]); usart_putchar('='); usart_putchar('0' + val);
newline();
return ERR_OK;
}
// format: slaveID Fcode startReg numRegs
static errcodes modbussend(const char *txt, text_cmd _U_ cmd){
modbus_request req = {0};
uint32_t N = 0;
const char *n = getnum(txt, &N);
if(n == txt || N > MODBUS_MAX_ID){
usart_send("Need slave ID (0..247)\n");
return ERR_WRONGLEN;
}
req.ID = N;
txt = n; n = getnum(txt, &N);
if(n == txt || N > 127 || N == 0){
usart_send("Need function code (1..127)\n");
return ERR_WRONGLEN;
}
req.Fcode = N;
txt = n; n = getnum(txt, &N);
if(n == txt){
usart_send("Need starting register address\n");
return ERR_WRONGLEN;
}
req.startreg = N;
txt = n; n = getnum(txt, &N);
if(n == txt || N == 0){
usart_send("Need registers amount\n");
return ERR_WRONGLEN;
}
req.regno = N;
if(modbus_send_request(&req) < 1) 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
typedef errcodes (*textfn)(const char *str);
typedef errcodes (*textfn)(const char *str, text_cmd cmd);
static textfn textfunctions[TCMD_AMOUNT] = {
[TCMD_PROHIBITED] = NULL,
[TCMD_WDTEST] = wdtest,
@@ -232,6 +303,8 @@ static textfn textfunctions[TCMD_AMOUNT] = {
[TCMD_CANSEND] = cansend,
[TCMD_CANSNIFFER] = cansnif,
[TCMD_CANBUSERRPRNT] = canbuserr,
[TCMD_SW_SEND_RELAY] = confflags,
[TCMD_MODBUS_SEND] = modbussend,
};
static const char* const errors_txt[ERR_AMOUNT] = {
@@ -254,9 +327,9 @@ static void errtext(errcodes e){
* @param txt - buffer with commands & data
*/
void cmd_parser(const char *str){
errcodes ecode = ERR_BADCMD;
if(!str || !*str) goto ret;
char cmd[MAXCMDLEN + 1];
errcodes ecode = ERR_BADCMD;
int idx = CMD_AMOUNT;
const funcdescr *c = funclist;
int l = 0;
@@ -279,7 +352,7 @@ void cmd_parser(const char *str){
}
str = omit_spaces(ptr);
if(idx < 0){ // text-only function
ecode = textfunctions[-idx](str);
ecode = textfunctions[-idx](str, -idx);
goto ret;
}
// common CAN/text function: we need to form 8-byte data buffer

View File

@@ -23,7 +23,7 @@ extern volatile uint32_t Tms;
static volatile int idatalen[2] = {0,0}; // received data line length (including '\n')
static volatile int odatalen[2] = {0,0};
volatile int usart_txrdy = 1; // transmission done
static volatile int usart_txrdy = 1; // transmission done
static volatile int usart_linerdy = 0 // received data ready
,dlen = 0 // length of data (including '\n') in current buffer
@@ -31,7 +31,7 @@ static volatile int usart_linerdy = 0 // received data ready
;
int rbufno = 0, tbufno = 0; // current rbuf/tbuf numbers
static int rbufno = 0, tbufno = 0; // current rbuf/tbuf numbers
static char rbuf[2][UARTBUFSZI], tbuf[2][UARTBUFSZO]; // receive & transmit buffers
static char *recvdata = NULL;

View File

@@ -18,6 +18,8 @@
#pragma once
#include <stdint.h>
// input and output buffers size
#define UARTBUFSZI (64)
#define UARTBUFSZO (128)
@@ -25,8 +27,6 @@
#define usartrx() (usart_linerdy)
#define usartovr() (usart_bufovr)
extern volatile int usart_txrdy;
int usart_transmit();
void usart_setup(uint32_t speed);
int usart_getline(char **line);

View File

@@ -1,2 +1,2 @@
#define BUILD_NUMBER "56"
#define BUILD_DATE "2024-06-04"
#define BUILD_NUMBER "63"
#define BUILD_DATE "2024-09-19"