mirror of
https://github.com/eddyem/stm32samples.git
synced 2025-12-06 02:35:23 +03:00
add modbus (not tested yet)
This commit is contained in:
parent
bdbd7d68d9
commit
22a205001a
@ -7,6 +7,9 @@ You can see pinout table in file `hardware.c`.
|
|||||||
|
|
||||||
## Serial protocol (each string ends with '\n').
|
## Serial protocol (each string ends with '\n').
|
||||||
|
|
||||||
|
(TODO: add new)
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
commands format: parameter[number][=setter]
|
commands format: parameter[number][=setter]
|
||||||
parameter [CAN idx] - help
|
parameter [CAN idx] - help
|
||||||
@ -49,11 +52,11 @@ Value in square brackets is CAN bus command code.
|
|||||||
|
|
||||||
All data in little-endian format!
|
All data in little-endian format!
|
||||||
|
|
||||||
BIT - MEANING
|
BYTE - MEANING
|
||||||
|
|
||||||
0, 1 - (uint16_t) - command code (value in square brackets upper);
|
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);
|
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.
|
5 - `ERR_CANTRUN` - can't run given command due to bad parameters or other reason.
|
||||||
|
|
||||||
|
|
||||||
|
## MODBUS-RTU protocol
|
||||||
|
|
||||||
|
(TODO)
|
||||||
@ -236,6 +236,7 @@ void CAN_proc(){
|
|||||||
|
|
||||||
CAN_status CAN_send(CAN_message *message){
|
CAN_status CAN_send(CAN_message *message){
|
||||||
if(!message) return CAN_ERR;
|
if(!message) return CAN_ERR;
|
||||||
|
IWDG->KR = IWDG_REFRESH;
|
||||||
uint8_t *msg = message->data;
|
uint8_t *msg = message->data;
|
||||||
uint8_t len = message->length;
|
uint8_t len = message->length;
|
||||||
uint16_t target_id = message->ID;
|
uint16_t target_id = message->ID;
|
||||||
|
|||||||
@ -20,7 +20,7 @@
|
|||||||
#include "canproto.h"
|
#include "canproto.h"
|
||||||
#include "flash.h"
|
#include "flash.h"
|
||||||
#include "hardware.h"
|
#include "hardware.h"
|
||||||
#include "proto.h"
|
#include "modbusrtu.h"
|
||||||
#include "strfunc.h"
|
#include "strfunc.h"
|
||||||
#include "usart.h"
|
#include "usart.h"
|
||||||
|
|
||||||
@ -41,49 +41,53 @@ static errcodes reset(CAN_message _U_ *msg){
|
|||||||
// get/set Tms
|
// get/set Tms
|
||||||
static errcodes time_getset(CAN_message *msg){
|
static errcodes time_getset(CAN_message *msg){
|
||||||
if(ISSETTER(msg->data)){
|
if(ISSETTER(msg->data)){
|
||||||
Tms = *(uint32_t*)&msg->data[4];
|
Tms = MSGP_GET_U32(msg);
|
||||||
}else FIXDL(msg);
|
}
|
||||||
*(uint32_t*)&msg->data[4] = Tms;
|
FIXDL(msg);
|
||||||
|
MSGP_SET_U32(msg, Tms);
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
// get MCU T
|
// get MCU T
|
||||||
static errcodes mcut(CAN_message *msg){
|
static errcodes mcut(CAN_message *msg){
|
||||||
FIXDL(msg);
|
FIXDL(msg);
|
||||||
*(int32_t*)&msg->data[4] = getMCUtemp();
|
MSGP_SET_U32(msg, getMCUtemp());
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
// get ADC raw values
|
// get ADC raw values
|
||||||
static errcodes adcraw(CAN_message *msg){
|
static errcodes adcraw(CAN_message *msg){
|
||||||
FIXDL(msg);
|
FIXDL(msg);
|
||||||
uint8_t no = msg->data[2] & ~SETTER_FLAG;
|
uint8_t no = PARVAL(msg->data);
|
||||||
if(no >= ADC_CHANNELS) return ERR_BADPAR;
|
if(no >= ADC_CHANNELS) return ERR_BADPAR;
|
||||||
*(uint32_t*)&msg->data[4] = getADCval(no);
|
MSGP_SET_U32(msg, getADCval(no));
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
// set common CAN ID / get CAN IN in
|
// set common CAN ID / get CAN IN in
|
||||||
static errcodes canid(CAN_message *msg){
|
static errcodes canid(CAN_message *msg){
|
||||||
if(ISSETTER(msg->data)){
|
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
|
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;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
// get/set input CAN ID
|
// get/set input CAN ID
|
||||||
static errcodes canidin(CAN_message *msg){
|
static errcodes canidin(CAN_message *msg){
|
||||||
if(ISSETTER(msg->data)){
|
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
|
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;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
// get/set output CAN ID
|
// get/set output CAN ID
|
||||||
static errcodes canidout(CAN_message *msg){
|
static errcodes canidout(CAN_message *msg){
|
||||||
if(ISSETTER(msg->data)){
|
if(ISSETTER(msg->data)){
|
||||||
the_conf.CANIDout = *(uint32_t*)&msg->data[4];
|
the_conf.CANIDout = (uint16_t)MSGP_GET_U32(msg);
|
||||||
}else FIXDL(msg);
|
}
|
||||||
*(uint32_t*)&msg->data[4] = the_conf.CANIDout;
|
FIXDL(msg);
|
||||||
|
MSGP_SET_U32(msg, the_conf.CANIDout);
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,32 +105,42 @@ static errcodes erasestor(CAN_message _U_ *msg){
|
|||||||
// relay management
|
// relay management
|
||||||
static errcodes relay(CAN_message *msg){
|
static errcodes relay(CAN_message *msg){
|
||||||
uint8_t no = OUTMAX+1;
|
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(ISSETTER(msg->data)){
|
||||||
if(set_relay(no, *(uint32_t*)&msg->data[4]) < 0) return ERR_BADPAR;
|
if(set_relay(no, MSGP_GET_U32(msg)) < 0) return ERR_BADPAR;
|
||||||
}else FIXDL(msg);
|
}
|
||||||
|
FIXDL(msg);
|
||||||
int rval = get_relay(no);
|
int rval = get_relay(no);
|
||||||
if(rval < 0) return ERR_BADPAR;
|
if(rval < 0) return ERR_BADPAR;
|
||||||
*(uint32_t*)&msg->data[4] = (uint32_t)rval;
|
MSGP_SET_U32(msg, (uint32_t)rval);
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
// get current ESW status
|
// get current ESW status
|
||||||
static errcodes esw(CAN_message *msg){
|
static errcodes esw(CAN_message *msg){
|
||||||
uint8_t no = INMAX+1;
|
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);
|
int val = get_esw(no);
|
||||||
if(val < 0) return ERR_BADPAR;
|
if(val < 0) return ERR_BADPAR;
|
||||||
*(uint32_t*)&msg->data[4] = (uint32_t) val;
|
MSGP_SET_U32(msg, (uint32_t)val);
|
||||||
FIXDL(msg);
|
FIXDL(msg);
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
// bounce-free ESW get status
|
// bounce-free ESW get status
|
||||||
static errcodes eswg(CAN_message *msg){
|
static errcodes eswg(CAN_message *msg){
|
||||||
uint8_t no = INMAX+1;
|
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();
|
uint32_t curval = get_ab_esw();
|
||||||
if(no > INMAX) *(uint32_t*)&msg->data[4] = curval;
|
if(no > INMAX) MSGP_SET_U32(msg, curval);
|
||||||
else *(uint32_t*)&msg->data[4] = (curval & (1<<no)) ? 0 : 1;
|
else MSGP_SET_U32(msg, (curval & (1<<no)) ? 0 : 1);
|
||||||
FIXDL(msg);
|
FIXDL(msg);
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
@ -140,41 +154,49 @@ static errcodes led(CAN_message *m){
|
|||||||
|
|
||||||
// common uint32_t setter/getter
|
// common uint32_t setter/getter
|
||||||
static errcodes u32setget(CAN_message *msg){
|
static errcodes u32setget(CAN_message *msg){
|
||||||
uint16_t idx = *(uint16_t*)msg->data;
|
uint16_t cmd = *(uint16_t*)msg->data;
|
||||||
uint32_t *ptr = NULL;
|
uint32_t *ptr = NULL, val;
|
||||||
switch(idx){
|
switch(cmd){
|
||||||
case CMD_CANSPEED: ptr = &the_conf.CANspeed; CAN_reinit(*(uint32_t*)&msg->data[4]); break;
|
case CMD_CANSPEED: ptr = &the_conf.CANspeed; CAN_reinit(MSGP_GET_U32(msg)); break;
|
||||||
case CMD_BOUNCE: ptr = &the_conf.bouncetime; break;
|
case CMD_BOUNCE: ptr = &the_conf.bouncetime; break;
|
||||||
case CMD_USARTSPEED: ptr = &the_conf.usartspeed; 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;
|
default: break;
|
||||||
}
|
}
|
||||||
if(!ptr) return ERR_CANTRUN; // unknown error
|
if(!ptr) return ERR_CANTRUN; // unknown error
|
||||||
if(ISSETTER(msg->data)){
|
if(ISSETTER(msg->data)){
|
||||||
*ptr = *(uint32_t*)&msg->data[4];
|
if(cmd == CMD_INCHNLS || cmd == CMD_OUTCHNLS) return ERR_CANTRUN; // can't set getter-only
|
||||||
}else FIXDL(msg);
|
*ptr = MSGP_GET_U32(msg);
|
||||||
*(uint32_t*)&msg->data[4] = *ptr;
|
}
|
||||||
|
FIXDL(msg);
|
||||||
|
MSGP_SET_U32(msg, *ptr);
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
// common bitflag setter/getter
|
// common bitflag setter/getter
|
||||||
|
// without parno - all flags, with - flag with number N
|
||||||
static errcodes flagsetget(CAN_message *msg){
|
static errcodes flagsetget(CAN_message *msg){
|
||||||
uint16_t idx = *(uint16_t*)msg->data;
|
uint8_t idx = NO_PARNO;
|
||||||
uint8_t bit = 32;
|
if(msg->length > 2){
|
||||||
switch(idx){
|
idx = PARVAL(msg->data);
|
||||||
case CMD_ENCISSSI: bit = FLAGBIT(ENC_IS_SSI); break;
|
if(idx != NO_PARNO && idx > MAX_FLAG_BITNO) return ERR_BADPAR;
|
||||||
case CMD_EMULPEP: bit = FLAGBIT(EMULATE_PEP); break;
|
|
||||||
default: break;
|
|
||||||
}
|
}
|
||||||
if(bit > 31) return ERR_CANTRUN; // unknown error
|
|
||||||
if(ISSETTER(msg->data)){
|
if(ISSETTER(msg->data)){
|
||||||
if(msg->data[4]) the_conf.flags |= 1<<bit;
|
uint32_t val = MSGP_GET_U32(msg);
|
||||||
else the_conf.flags &= ~(1<<bit);
|
if(idx == NO_PARNO) the_conf.flags.u32 = val; // all bits
|
||||||
}else FIXDL(msg);
|
else{ // only selected
|
||||||
*(uint32_t*)&msg->data[4] = (the_conf.flags & (1<<bit)) ? 1 : 0;
|
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;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
************ END of all common functions list (for `funclist`) ************/
|
/************ END of all common functions list (for `funclist`) ************/
|
||||||
|
|
||||||
typedef struct{
|
typedef struct{
|
||||||
errcodes (*fn)(CAN_message *msg); // function to run with can packet `msg`
|
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_BOUNCE] = {u32setget, 0, 1000, 0},
|
||||||
[CMD_USARTSPEED] = {u32setget, 1200, 3000000, 0},
|
[CMD_USARTSPEED] = {u32setget, 1200, 3000000, 0},
|
||||||
[CMD_LED] = {led, 0, 0, 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){
|
for(int i = 0; i < msg->length; ++i){
|
||||||
usart_send(uhex2str(msg->data[i])); usart_putchar(' ');
|
usart_send(uhex2str(msg->data[i])); usart_putchar(' ');
|
||||||
}
|
}
|
||||||
//for(int i = msg->length-1; i < 8; ++i) msg->data[i] = 0;
|
|
||||||
newline();
|
newline();
|
||||||
#endif
|
#endif
|
||||||
if(datalen < 2){
|
if(datalen < 2){
|
||||||
|
|||||||
@ -25,9 +25,11 @@
|
|||||||
#define SETTER_FLAG (0x80)
|
#define SETTER_FLAG (0x80)
|
||||||
#define ISSETTER(data) ((data[2] & SETTER_FLAG))
|
#define ISSETTER(data) ((data[2] & SETTER_FLAG))
|
||||||
// parameter number 127 means there no parameter number at all (don't need paremeter or get all)
|
// 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)
|
// base value of parameter (even if it is a setter)
|
||||||
#define PARBASE(x) (x & 0x7f)
|
#define PARBASE(x) (x & 0x7f)
|
||||||
|
// get parameter value of msg->data
|
||||||
|
#define PARVAL(data) (data[2] & 0x7f)
|
||||||
|
|
||||||
// make error for CAN answer
|
// make error for CAN answer
|
||||||
#define FORMERR(m, err) do{m->data[3] = err; if(m->length < 4) m->length = 4;}while(0)
|
#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
|
ERR_AMOUNT // amount of error codes
|
||||||
} errcodes;
|
} 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
|
// CAN commands indexes
|
||||||
enum{
|
enum{
|
||||||
CMD_PING, // just ping
|
CMD_PING, // just ping
|
||||||
@ -62,6 +77,11 @@ enum{
|
|||||||
CMD_BOUNCE, // get/set bounce constant (ms)
|
CMD_BOUNCE, // get/set bounce constant (ms)
|
||||||
CMD_USARTSPEED, // get/set USART1 speed (if encoder on RS-422)
|
CMD_USARTSPEED, // get/set USART1 speed (if encoder on RS-422)
|
||||||
CMD_LED, // onboard LED
|
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:
|
// should be the last:
|
||||||
CMD_AMOUNT // amount of CAN commands
|
CMD_AMOUNT // amount of CAN commands
|
||||||
};
|
};
|
||||||
|
|||||||
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#include "stm32f1.h"
|
#include "stm32f1.h"
|
||||||
|
|
||||||
|
#include "modbusrtu.h"
|
||||||
#include "flash.h"
|
#include "flash.h"
|
||||||
#include "strfunc.h"
|
#include "strfunc.h"
|
||||||
#include "usart.h"
|
#include "usart.h"
|
||||||
@ -34,6 +35,8 @@ static uint32_t maxCnum = 1024 / sizeof(user_conf); // can't use blocksize here
|
|||||||
,.CANIDout = 2 \
|
,.CANIDout = 2 \
|
||||||
,.usartspeed = 115200 \
|
,.usartspeed = 115200 \
|
||||||
,.bouncetime = 50 \
|
,.bouncetime = 50 \
|
||||||
|
,.modbusID = MODBUS_MASTER_ID \
|
||||||
|
,.modbusspeed = 9600 \
|
||||||
}
|
}
|
||||||
|
|
||||||
static int write2flash(const void*, const void*, uint32_t);
|
static int write2flash(const void*, const void*, uint32_t);
|
||||||
|
|||||||
@ -23,6 +23,15 @@
|
|||||||
#define FLASH_SIZE_REG ((uint32_t)0x1FFFF7E0)
|
#define FLASH_SIZE_REG ((uint32_t)0x1FFFF7E0)
|
||||||
#define FLASH_SIZE *((uint16_t*)FLASH_SIZE_REG)
|
#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
|
* struct to save user configurations
|
||||||
*/
|
*/
|
||||||
@ -30,10 +39,13 @@ typedef struct __attribute__((aligned(4))){
|
|||||||
uint16_t userconf_sz; // "magick number"
|
uint16_t userconf_sz; // "magick number"
|
||||||
uint16_t CANIDin; // CAN bus device ID for input commands
|
uint16_t CANIDin; // CAN bus device ID for input commands
|
||||||
uint16_t CANIDout; // -//- for output signals
|
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 bouncetime; // anti-bounce timeout (ms)
|
||||||
uint32_t usartspeed; // RS-232 speed
|
uint32_t usartspeed; // RS-232 speed
|
||||||
uint32_t CANspeed; // CAN bus 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;
|
} user_conf;
|
||||||
|
|
||||||
extern user_conf the_conf;
|
extern user_conf the_conf;
|
||||||
|
|||||||
Binary file not shown.
@ -10,6 +10,10 @@ flash.h
|
|||||||
hardware.c
|
hardware.c
|
||||||
hardware.h
|
hardware.h
|
||||||
main.c
|
main.c
|
||||||
|
modbusproto.c
|
||||||
|
modbusproto.h
|
||||||
|
modbusrtu.c
|
||||||
|
modbusrtu.h
|
||||||
proto.c
|
proto.c
|
||||||
proto.h
|
proto.h
|
||||||
strfunc.c
|
strfunc.c
|
||||||
|
|||||||
@ -20,10 +20,12 @@
|
|||||||
#include "canproto.h"
|
#include "canproto.h"
|
||||||
#include "flash.h"
|
#include "flash.h"
|
||||||
#include "hardware.h"
|
#include "hardware.h"
|
||||||
|
/*
|
||||||
#ifdef EBUG
|
#ifdef EBUG
|
||||||
#include "strfunc.h"
|
#include "strfunc.h"
|
||||||
#include "uchar.h"
|
#include "usart.h"
|
||||||
#endif
|
#endif
|
||||||
|
*/
|
||||||
|
|
||||||
/* pinout:
|
/* pinout:
|
||||||
|
|
||||||
@ -257,11 +259,18 @@ void proc_esw(){
|
|||||||
if(oldesw != ESW_ab_values){
|
if(oldesw != ESW_ab_values){
|
||||||
//usart_send("esw="); usart_send(u2str(ESW_ab_values)); newline();
|
//usart_send("esw="); usart_send(u2str(ESW_ab_values)); newline();
|
||||||
CAN_message msg = {.ID = the_conf.CANIDout, .length = 8};
|
CAN_message msg = {.ID = the_conf.CANIDout, .length = 8};
|
||||||
msg.data[0] = CMD_GETESW;
|
MSG_SET_CMD(msg, CMD_GETESW);
|
||||||
*((uint32_t*)(&msg.data[4])) = ESW_ab_values;
|
MSG_SET_U32(msg, ESW_ab_values);
|
||||||
uint32_t Tstart = Tms;
|
uint32_t Tstart = Tms;
|
||||||
while(Tms - Tstart < SEND_TIMEOUT_MS){
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,6 +33,9 @@
|
|||||||
// max number of in/out pins
|
// max number of in/out pins
|
||||||
#define INMAX (15)
|
#define INMAX (15)
|
||||||
#define OUTMAX (11)
|
#define OUTMAX (11)
|
||||||
|
// and 8-multiple
|
||||||
|
#define INMAXBYTES (2)
|
||||||
|
#define OUTMAXBYTES (2)
|
||||||
|
|
||||||
// onboard LED - PD10
|
// onboard LED - PD10
|
||||||
#define LEDPORT GPIOD
|
#define LEDPORT GPIOD
|
||||||
|
|||||||
@ -20,6 +20,8 @@
|
|||||||
#include "can.h"
|
#include "can.h"
|
||||||
#include "flash.h"
|
#include "flash.h"
|
||||||
#include "hardware.h"
|
#include "hardware.h"
|
||||||
|
#include "modbusproto.h"
|
||||||
|
#include "modbusrtu.h"
|
||||||
#include "proto.h"
|
#include "proto.h"
|
||||||
#include "strfunc.h"
|
#include "strfunc.h"
|
||||||
|
|
||||||
@ -30,17 +32,31 @@ void sys_tick_handler(void){
|
|||||||
++Tms;
|
++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){
|
int main(void){
|
||||||
uint32_t lastT = 0;
|
uint32_t lastT = 0;
|
||||||
CAN_message *can_mesg;
|
CAN_message *can_mesg;
|
||||||
StartHSE();
|
StartHSE();
|
||||||
|
RCC->CSR |= RCC_CSR_RMVF; // remove reset flags
|
||||||
SysTick_Config(72000);
|
SysTick_Config(72000);
|
||||||
flashstorage_init();
|
flashstorage_init();
|
||||||
gpio_setup(); // should be run before other peripherial setup
|
gpio_setup(); // should be run before other peripherial setup
|
||||||
adc_setup();
|
adc_setup();
|
||||||
usart_setup(the_conf.usartspeed);
|
usart_setup(the_conf.usartspeed);
|
||||||
CAN_setup(the_conf.CANspeed);
|
CAN_setup(the_conf.CANspeed);
|
||||||
RCC->CSR |= RCC_CSR_RMVF; // remove reset flags
|
modbus_setup(the_conf.modbusspeed);
|
||||||
|
|
||||||
#ifndef EBUG
|
#ifndef EBUG
|
||||||
iwdg_setup();
|
iwdg_setup();
|
||||||
#endif
|
#endif
|
||||||
@ -80,6 +96,7 @@ int main(void){
|
|||||||
int g = usart_getline(&str);
|
int g = usart_getline(&str);
|
||||||
if(g < 0) usart_send("USART IN buffer overflow!\n");
|
if(g < 0) usart_send("USART IN buffer overflow!\n");
|
||||||
else if(g > 0) cmd_parser(str);
|
else if(g > 0) cmd_parser(str);
|
||||||
|
parsemodbus();
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
261
F1:F103/FX3U/modbusproto.c
Normal file
261
F1:F103/FX3U/modbusproto.c
Normal 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*)®val, .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();
|
||||||
|
}
|
||||||
48
F1:F103/FX3U/modbusproto.h
Normal file
48
F1:F103/FX3U/modbusproto.h
Normal 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
238
F1:F103/FX3U/modbusrtu.c
Normal 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
80
F1:F103/FX3U/modbusrtu.h
Normal 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);
|
||||||
@ -22,6 +22,7 @@
|
|||||||
#include "flash.h"
|
#include "flash.h"
|
||||||
#include "hardware.h"
|
#include "hardware.h"
|
||||||
#include "proto.h"
|
#include "proto.h"
|
||||||
|
#include "modbusrtu.h"
|
||||||
#include "strfunc.h"
|
#include "strfunc.h"
|
||||||
#include "usart.h"
|
#include "usart.h"
|
||||||
#include "version.inc"
|
#include "version.inc"
|
||||||
@ -38,6 +39,8 @@ typedef enum{
|
|||||||
TCMD_CANSEND, // send CAN message
|
TCMD_CANSEND, // send CAN message
|
||||||
TCMD_CANSNIFFER, // sniff all CAN messages
|
TCMD_CANSNIFFER, // sniff all CAN messages
|
||||||
TCMD_CANBUSERRPRNT, // pring all errors of bus
|
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
|
TCMD_AMOUNT
|
||||||
} text_cmd;
|
} text_cmd;
|
||||||
|
|
||||||
@ -56,6 +59,7 @@ static const funcdescr funclist[] = {
|
|||||||
{NULL, 0, "CAN bus commands"},
|
{NULL, 0, "CAN bus commands"},
|
||||||
{"canbuserr", -TCMD_CANBUSERRPRNT, "print all CAN bus errors (a lot of if not connected)"},
|
{"canbuserr", -TCMD_CANBUSERRPRNT, "print all CAN bus errors (a lot of if not connected)"},
|
||||||
{"cansniff", -TCMD_CANSNIFFER, "switch CAN sniffer mode"},
|
{"cansniff", -TCMD_CANSNIFFER, "switch CAN sniffer mode"},
|
||||||
|
{"s", -TCMD_CANSEND, "send CAN message: ID 0..8 data bytes"},
|
||||||
{NULL, 0, "Configuration"},
|
{NULL, 0, "Configuration"},
|
||||||
{"bounce", CMD_BOUNCE, "set/get anti-bounce timeout (ms, max: 1000)"},
|
{"bounce", CMD_BOUNCE, "set/get anti-bounce timeout (ms, max: 1000)"},
|
||||||
{"canid", CMD_CANID, "set both (in/out) CAN ID / get in CAN ID"},
|
{"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)"},
|
{"canspeed", CMD_CANSPEED, "get/set CAN speed (bps)"},
|
||||||
{"dumpconf", -TCMD_DUMPCONF, "dump current configuration"},
|
{"dumpconf", -TCMD_DUMPCONF, "dump current configuration"},
|
||||||
{"eraseflash", CMD_ERASESTOR, "erase all flash storage"},
|
{"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"},
|
{"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"},
|
{"usartspeed", CMD_USARTSPEED, "get/set USART1 speed"},
|
||||||
{NULL, 0, "IN/OUT"},
|
{NULL, 0, "IN/OUT"},
|
||||||
{"adc", CMD_ADCRAW, "get raw ADC values for given channel"},
|
{"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"},
|
{"led", CMD_LED, "work with onboard LED"},
|
||||||
{"relay", CMD_RELAY, "get/set relay state (0 - off, 1 - on)"},
|
{"relay", CMD_RELAY, "get/set relay state (0 - off, 1 - on)"},
|
||||||
{NULL, 0, "Other commands"},
|
{NULL, 0, "Other commands"},
|
||||||
|
{"inchannels", CMD_INCHNLS, "get u32 with bits set on supported IN channels"},
|
||||||
{"mcutemp", CMD_MCUTEMP, "get MCU temperature (*10degrC)"},
|
{"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"},
|
{"reset", CMD_RESET, "reset MCU"},
|
||||||
{"s", -TCMD_CANSEND, "send CAN message: ID 0..8 data bytes"},
|
|
||||||
{"time", CMD_TIME, "get/set time (1ms, 32bit)"},
|
{"time", CMD_TIME, "get/set time (1ms, 32bit)"},
|
||||||
{"wdtest", -TCMD_WDTEST, "test watchdog"},
|
{"wdtest", -TCMD_WDTEST, "test watchdog"},
|
||||||
{NULL, 0, NULL} // last record
|
{NULL, 0, NULL} // last record
|
||||||
@ -83,7 +93,7 @@ static const funcdescr funclist[] = {
|
|||||||
|
|
||||||
static void printhelp(){
|
static void printhelp(){
|
||||||
const funcdescr *c = funclist;
|
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("commands format: parameter[number][=setter]\n");
|
||||||
usart_send("parameter [CAN idx] - help\n");
|
usart_send("parameter [CAN idx] - help\n");
|
||||||
usart_send("--------------------------\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;
|
uint32_t U;
|
||||||
if(str){
|
if(str){
|
||||||
if(*str == '=') str = omit_spaces(str + 1);
|
if(*str == '=') str = omit_spaces(str + 1);
|
||||||
@ -122,7 +132,7 @@ static errcodes cansnif(const char *str){
|
|||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static errcodes canbuserr(const char *str){
|
static errcodes canbuserr(const char *str, text_cmd _U_ cmd){
|
||||||
uint32_t U;
|
uint32_t U;
|
||||||
if(str){
|
if(str){
|
||||||
if(*str == '=') str = omit_spaces(str + 1);
|
if(*str == '=') str = omit_spaces(str + 1);
|
||||||
@ -135,21 +145,20 @@ static errcodes canbuserr(const char *str){
|
|||||||
return ERR_OK;
|
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_send("Wait for reboot\n");
|
||||||
usart_transmit();
|
usart_transmit();
|
||||||
while(1){nop();}
|
while(1){nop();}
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
// names of bit flags (ordered from LSE of[0])
|
// names of bit flags (ordered from LSE of[0])
|
||||||
static const char * const bitfields[] = {
|
static const char * const bitfields[] = {
|
||||||
"encisSSI",
|
"sw_send_relay_cmd",
|
||||||
"emulatePEP",
|
|
||||||
NULL
|
NULL
|
||||||
};*/
|
};
|
||||||
|
|
||||||
static errcodes dumpconf(const char _U_ *str){
|
static errcodes dumpconf(const char _U_ *str, text_cmd _U_ cmd){
|
||||||
#ifdef EBUG
|
#ifdef EBUG
|
||||||
uint32_t sz = FLASH_SIZE*1024;
|
uint32_t sz = FLASH_SIZE*1024;
|
||||||
usart_send("flashsize="); printu(sz); usart_putchar('/');
|
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(float2str(the_conf.adcmul[i], 3));
|
||||||
}*/
|
}*/
|
||||||
usart_send("\nusartspeed="); printu(the_conf.usartspeed);
|
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);
|
usart_send("\nbouncetime="); printu(the_conf.bouncetime);
|
||||||
/*
|
|
||||||
const char * const *p = bitfields;
|
const char * const *p = bitfields;
|
||||||
int bit = 0;
|
int bit = 0;
|
||||||
|
usart_send("\nflags="); usart_putchar('='); printuhex(the_conf.flags.u32);
|
||||||
while(*p){
|
while(*p){
|
||||||
newline();
|
newline(); usart_putchar(' ');
|
||||||
usart_send(*p); usart_putchar('='); usart_putchar((the_conf.flags & (1<<bit)) ? '1' : '0');
|
usart_send(*p); usart_putchar('='); usart_putchar((the_conf.flags.u32 & (1<<bit)) ? '1' : '0');
|
||||||
if(++bit > 31) break;
|
if(++bit > MAX_FLAG_BITNO) break;
|
||||||
++p;
|
++p;
|
||||||
}*/
|
}
|
||||||
newline();
|
newline();
|
||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static errcodes cansend(const char *txt){
|
static errcodes cansend(const char *txt, text_cmd _U_ cmd){
|
||||||
CAN_message canmsg;
|
CAN_message canmsg;
|
||||||
bzero(&canmsg, sizeof(canmsg));
|
bzero(&canmsg, sizeof(canmsg));
|
||||||
int ctr = -1;
|
int ctr = -1;
|
||||||
@ -221,10 +232,70 @@ static errcodes cansend(const char *txt){
|
|||||||
return ERR_CANTRUN;
|
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
|
// 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] = {
|
static textfn textfunctions[TCMD_AMOUNT] = {
|
||||||
[TCMD_PROHIBITED] = NULL,
|
[TCMD_PROHIBITED] = NULL,
|
||||||
[TCMD_WDTEST] = wdtest,
|
[TCMD_WDTEST] = wdtest,
|
||||||
@ -232,6 +303,8 @@ static textfn textfunctions[TCMD_AMOUNT] = {
|
|||||||
[TCMD_CANSEND] = cansend,
|
[TCMD_CANSEND] = cansend,
|
||||||
[TCMD_CANSNIFFER] = cansnif,
|
[TCMD_CANSNIFFER] = cansnif,
|
||||||
[TCMD_CANBUSERRPRNT] = canbuserr,
|
[TCMD_CANBUSERRPRNT] = canbuserr,
|
||||||
|
[TCMD_SW_SEND_RELAY] = confflags,
|
||||||
|
[TCMD_MODBUS_SEND] = modbussend,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char* const errors_txt[ERR_AMOUNT] = {
|
static const char* const errors_txt[ERR_AMOUNT] = {
|
||||||
@ -254,9 +327,9 @@ static void errtext(errcodes e){
|
|||||||
* @param txt - buffer with commands & data
|
* @param txt - buffer with commands & data
|
||||||
*/
|
*/
|
||||||
void cmd_parser(const char *str){
|
void cmd_parser(const char *str){
|
||||||
|
errcodes ecode = ERR_BADCMD;
|
||||||
if(!str || !*str) goto ret;
|
if(!str || !*str) goto ret;
|
||||||
char cmd[MAXCMDLEN + 1];
|
char cmd[MAXCMDLEN + 1];
|
||||||
errcodes ecode = ERR_BADCMD;
|
|
||||||
int idx = CMD_AMOUNT;
|
int idx = CMD_AMOUNT;
|
||||||
const funcdescr *c = funclist;
|
const funcdescr *c = funclist;
|
||||||
int l = 0;
|
int l = 0;
|
||||||
@ -279,7 +352,7 @@ void cmd_parser(const char *str){
|
|||||||
}
|
}
|
||||||
str = omit_spaces(ptr);
|
str = omit_spaces(ptr);
|
||||||
if(idx < 0){ // text-only function
|
if(idx < 0){ // text-only function
|
||||||
ecode = textfunctions[-idx](str);
|
ecode = textfunctions[-idx](str, -idx);
|
||||||
goto ret;
|
goto ret;
|
||||||
}
|
}
|
||||||
// common CAN/text function: we need to form 8-byte data buffer
|
// common CAN/text function: we need to form 8-byte data buffer
|
||||||
|
|||||||
@ -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 idatalen[2] = {0,0}; // received data line length (including '\n')
|
||||||
static volatile int odatalen[2] = {0,0};
|
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
|
static volatile int usart_linerdy = 0 // received data ready
|
||||||
,dlen = 0 // length of data (including '\n') in current buffer
|
,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 rbuf[2][UARTBUFSZI], tbuf[2][UARTBUFSZO]; // receive & transmit buffers
|
||||||
static char *recvdata = NULL;
|
static char *recvdata = NULL;
|
||||||
|
|
||||||
|
|||||||
@ -18,6 +18,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
// input and output buffers size
|
// input and output buffers size
|
||||||
#define UARTBUFSZI (64)
|
#define UARTBUFSZI (64)
|
||||||
#define UARTBUFSZO (128)
|
#define UARTBUFSZO (128)
|
||||||
@ -25,8 +27,6 @@
|
|||||||
#define usartrx() (usart_linerdy)
|
#define usartrx() (usart_linerdy)
|
||||||
#define usartovr() (usart_bufovr)
|
#define usartovr() (usart_bufovr)
|
||||||
|
|
||||||
extern volatile int usart_txrdy;
|
|
||||||
|
|
||||||
int usart_transmit();
|
int usart_transmit();
|
||||||
void usart_setup(uint32_t speed);
|
void usart_setup(uint32_t speed);
|
||||||
int usart_getline(char **line);
|
int usart_getline(char **line);
|
||||||
|
|||||||
@ -1,2 +1,2 @@
|
|||||||
#define BUILD_NUMBER "56"
|
#define BUILD_NUMBER "63"
|
||||||
#define BUILD_DATE "2024-06-04"
|
#define BUILD_DATE "2024-09-19"
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* This file is part of the i2cscan project.
|
* Copyright 2024 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||||
* Copyright 2023 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -16,15 +15,16 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stm32g0.h>
|
#include <math.h>
|
||||||
|
#include <stm32f0.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "usb.h"
|
||||||
/**
|
/**
|
||||||
* @brief hexdump - dump hex array by 16 bytes in string
|
* @brief hexdump - dump hex array by 16 bytes in string
|
||||||
* @param sendfun - function to send data
|
|
||||||
* @param arr - array to dump
|
* @param arr - array to dump
|
||||||
* @param len - length of `arr`
|
* @param len - length of `arr`
|
||||||
*/
|
*/
|
||||||
void hexdump(int (*sendfun)(const char *s), uint8_t *arr, uint16_t len){
|
void hexdump(uint8_t *arr, uint16_t len){
|
||||||
char buf[52], *bptr = buf;
|
char buf[52], *bptr = buf;
|
||||||
for(uint16_t l = 0; l < len; ++l, ++arr){
|
for(uint16_t l = 0; l < len; ++l, ++arr){
|
||||||
for(int16_t j = 1; j > -1; --j){
|
for(int16_t j = 1; j > -1; --j){
|
||||||
@ -35,14 +35,14 @@ void hexdump(int (*sendfun)(const char *s), uint8_t *arr, uint16_t len){
|
|||||||
if(l % 16 == 15){
|
if(l % 16 == 15){
|
||||||
*bptr++ = '\n';
|
*bptr++ = '\n';
|
||||||
*bptr = 0;
|
*bptr = 0;
|
||||||
sendfun(buf);
|
USB_sendstr(buf);
|
||||||
bptr = buf;
|
bptr = buf;
|
||||||
}else *bptr++ = ' ';
|
}else *bptr++ = ' ';
|
||||||
}
|
}
|
||||||
if(bptr != buf){
|
if(bptr != buf){
|
||||||
*bptr++ = '\n';
|
*bptr++ = '\n';
|
||||||
*bptr = 0;
|
*bptr = 0;
|
||||||
sendfun(buf);
|
USB_sendstr(buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,8 +60,11 @@ static char *_2str(uint32_t val, uint8_t minus){
|
|||||||
*(--bufptr) = '0';
|
*(--bufptr) = '0';
|
||||||
}else{
|
}else{
|
||||||
while(val){
|
while(val){
|
||||||
*(--bufptr) = val % 10 + '0';
|
uint32_t x = val / 10;
|
||||||
val /= 10;
|
*(--bufptr) = (val - 10*x) + '0';
|
||||||
|
val = x;
|
||||||
|
//*(--bufptr) = val % 10 + '0';
|
||||||
|
//val /= 10;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(minus) *(--bufptr) = '-';
|
if(minus) *(--bufptr) = '-';
|
||||||
@ -113,12 +116,12 @@ char *uhex2str(uint32_t val){
|
|||||||
* @param buf - string
|
* @param buf - string
|
||||||
* @return - pointer to first character in `buf` > ' '
|
* @return - pointer to first character in `buf` > ' '
|
||||||
*/
|
*/
|
||||||
char *omit_spaces(const char *buf){
|
const char *omit_spaces(const char *buf){
|
||||||
while(*buf){
|
while(*buf){
|
||||||
if(*buf > ' ') break;
|
if(*buf > ' ') break;
|
||||||
++buf;
|
++buf;
|
||||||
}
|
}
|
||||||
return (char*)buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -127,7 +130,7 @@ char *omit_spaces(const char *buf){
|
|||||||
* @param N - number read
|
* @param N - number read
|
||||||
* @return Next non-number symbol. In case of overflow return `buf` and N==0xffffffff
|
* @return Next non-number symbol. In case of overflow return `buf` and N==0xffffffff
|
||||||
*/
|
*/
|
||||||
static char *getdec(const char *buf, uint32_t *N){
|
static const char *getdec(const char *buf, uint32_t *N){
|
||||||
char *start = (char*)buf;
|
char *start = (char*)buf;
|
||||||
uint32_t num = 0;
|
uint32_t num = 0;
|
||||||
while(*buf){
|
while(*buf){
|
||||||
@ -144,11 +147,11 @@ static char *getdec(const char *buf, uint32_t *N){
|
|||||||
++buf;
|
++buf;
|
||||||
}
|
}
|
||||||
*N = num;
|
*N = num;
|
||||||
return (char*)buf;
|
return buf;
|
||||||
}
|
}
|
||||||
// read hexadecimal number (without 0x prefix!)
|
// read hexadecimal number (without 0x prefix!)
|
||||||
static char *gethex(const char *buf, uint32_t *N){
|
static const char *gethex(const char *buf, uint32_t *N){
|
||||||
char *start = (char*)buf;
|
const char *start = buf;
|
||||||
uint32_t num = 0;
|
uint32_t num = 0;
|
||||||
while(*buf){
|
while(*buf){
|
||||||
char c = *buf;
|
char c = *buf;
|
||||||
@ -173,11 +176,11 @@ static char *gethex(const char *buf, uint32_t *N){
|
|||||||
++buf;
|
++buf;
|
||||||
}
|
}
|
||||||
*N = num;
|
*N = num;
|
||||||
return (char*)buf;
|
return buf;
|
||||||
}
|
}
|
||||||
// read octal number (without 0 prefix!)
|
// read octal number (without 0 prefix!)
|
||||||
static char *getoct(const char *buf, uint32_t *N){
|
static const char *getoct(const char *buf, uint32_t *N){
|
||||||
char *start = (char*)buf;
|
const char *start = (char*)buf;
|
||||||
uint32_t num = 0;
|
uint32_t num = 0;
|
||||||
while(*buf){
|
while(*buf){
|
||||||
char c = *buf;
|
char c = *buf;
|
||||||
@ -193,11 +196,11 @@ static char *getoct(const char *buf, uint32_t *N){
|
|||||||
++buf;
|
++buf;
|
||||||
}
|
}
|
||||||
*N = num;
|
*N = num;
|
||||||
return (char*)buf;
|
return buf;
|
||||||
}
|
}
|
||||||
// read binary number (without b prefix!)
|
// read binary number (without b prefix!)
|
||||||
static char *getbin(const char *buf, uint32_t *N){
|
static const char *getbin(const char *buf, uint32_t *N){
|
||||||
char *start = (char*)buf;
|
const char *start = (char*)buf;
|
||||||
uint32_t num = 0;
|
uint32_t num = 0;
|
||||||
while(*buf){
|
while(*buf){
|
||||||
char c = *buf;
|
char c = *buf;
|
||||||
@ -213,7 +216,7 @@ static char *getbin(const char *buf, uint32_t *N){
|
|||||||
++buf;
|
++buf;
|
||||||
}
|
}
|
||||||
*N = num;
|
*N = num;
|
||||||
return (char*)buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -223,9 +226,9 @@ static char *getbin(const char *buf, uint32_t *N){
|
|||||||
* @return pointer to first non-number symbol in buf
|
* @return pointer to first non-number symbol in buf
|
||||||
* (if it is == buf, there's no number or if *N==0xffffffff there was overflow)
|
* (if it is == buf, there's no number or if *N==0xffffffff there was overflow)
|
||||||
*/
|
*/
|
||||||
char *getnum(const char *txt, uint32_t *N){
|
const char *getnum(const char *txt, uint32_t *N){
|
||||||
char *nxt = NULL;
|
const char *nxt = NULL;
|
||||||
char *s = omit_spaces(txt);
|
const char *s = omit_spaces(txt);
|
||||||
if(*s == '0'){ // hex, oct or 0
|
if(*s == '0'){ // hex, oct or 0
|
||||||
if(s[1] == 'x' || s[1] == 'X'){ // hex
|
if(s[1] == 'x' || s[1] == 'X'){ // hex
|
||||||
nxt = gethex(s+2, N);
|
nxt = gethex(s+2, N);
|
||||||
@ -246,3 +249,101 @@ char *getnum(const char *txt, uint32_t *N){
|
|||||||
}
|
}
|
||||||
return nxt;
|
return nxt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get signed integer
|
||||||
|
const char *getint(const char *txt, int32_t *I){
|
||||||
|
const char *s = omit_spaces(txt);
|
||||||
|
int32_t sign = 1;
|
||||||
|
uint32_t U;
|
||||||
|
if(*s == '-'){
|
||||||
|
sign = -1;
|
||||||
|
++s;
|
||||||
|
}
|
||||||
|
const char *nxt = getnum(s, &U);
|
||||||
|
if(nxt == s) return txt;
|
||||||
|
if(U & 0x80000000) return txt; // overfull
|
||||||
|
*I = sign * (int32_t)U;
|
||||||
|
return nxt;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mystrlen(const char *txt){
|
||||||
|
if(!txt) return 0;
|
||||||
|
int r = 0;
|
||||||
|
while(*txt++) ++r;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
void mymemcpy(char *dest, const char *src, int len){
|
||||||
|
if(len < 1) return;
|
||||||
|
while(len--) *dest++ = *src++;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// be careful: if pow10 would be bigger you should change str[] size!
|
||||||
|
static const float pwr10[] = {1.f, 10.f, 100.f, 1000.f, 10000.f};
|
||||||
|
static const float rounds[] = {0.5f, 0.05f, 0.005f, 0.0005f, 0.00005f};
|
||||||
|
#define P10L (sizeof(pwr10)/sizeof(uint32_t) - 1)
|
||||||
|
char * float2str(float x, uint8_t prec){
|
||||||
|
static char str[16] = {0}; // -117.5494E-36\0 - 14 symbols max!
|
||||||
|
if(prec > P10L) prec = P10L;
|
||||||
|
if(isnan(x)){ memcpy(str, "NAN", 4); return str;}
|
||||||
|
else{
|
||||||
|
int i = isinf(x);
|
||||||
|
if(i){memcpy(str, "-INF", 5); if(i == 1) return str+1; else return str;}
|
||||||
|
}
|
||||||
|
char *s = str + 14; // go to end of buffer
|
||||||
|
uint8_t minus = 0;
|
||||||
|
if(x < 0){
|
||||||
|
x = -x;
|
||||||
|
minus = 1;
|
||||||
|
}
|
||||||
|
int pow = 0; // xxxEpow
|
||||||
|
// now convert float to 1.xxxE3y
|
||||||
|
while(x > 1000.f){
|
||||||
|
x /= 1000.f;
|
||||||
|
pow += 3;
|
||||||
|
}
|
||||||
|
if(x > 0.) while(x < 1.){
|
||||||
|
x *= 1000.f;
|
||||||
|
pow -= 3;
|
||||||
|
}
|
||||||
|
// print Eyy
|
||||||
|
if(pow){
|
||||||
|
uint8_t m = 0;
|
||||||
|
if(pow < 0){pow = -pow; m = 1;}
|
||||||
|
while(pow){
|
||||||
|
register int p10 = pow/10;
|
||||||
|
*s-- = '0' + (pow - 10*p10);
|
||||||
|
pow = p10;
|
||||||
|
}
|
||||||
|
if(m) *s-- = '-';
|
||||||
|
*s-- = 'E';
|
||||||
|
}
|
||||||
|
// now our number is in [1, 1000]
|
||||||
|
uint32_t units;
|
||||||
|
if(prec){
|
||||||
|
units = (uint32_t) x;
|
||||||
|
uint32_t decimals = (uint32_t)((x-units+rounds[prec])*pwr10[prec]);
|
||||||
|
// print decimals
|
||||||
|
while(prec){
|
||||||
|
register int d10 = decimals / 10;
|
||||||
|
*s-- = '0' + (decimals - 10*d10);
|
||||||
|
decimals = d10;
|
||||||
|
--prec;
|
||||||
|
}
|
||||||
|
// decimal point
|
||||||
|
*s-- = '.';
|
||||||
|
}else{ // without decimal part
|
||||||
|
units = (uint32_t) (x + 0.5);
|
||||||
|
}
|
||||||
|
// print main units
|
||||||
|
if(units == 0) *s-- = '0';
|
||||||
|
else while(units){
|
||||||
|
register uint32_t u10 = units / 10;
|
||||||
|
*s-- = '0' + (units - 10*u10);
|
||||||
|
units = u10;
|
||||||
|
}
|
||||||
|
if(minus) *s-- = '-';
|
||||||
|
return s+1;
|
||||||
|
}
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* This file is part of the i2cscan project.
|
* Copyright 2024 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
||||||
* Copyright 2023 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -18,9 +17,15 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
void hexdump(int (*sendfun)(const char *s), uint8_t *arr, uint16_t len);
|
#include <stdint.h>
|
||||||
char *u2str(uint32_t val);
|
|
||||||
char *i2str(int32_t i);
|
void hexdump(int ifno, uint8_t *arr, uint16_t len);
|
||||||
char *uhex2str(uint32_t val);
|
const char *u2str(uint32_t val);
|
||||||
char *getnum(const char *txt, uint32_t *N);
|
const char *i2str(int32_t i);
|
||||||
char *omit_spaces(const char *buf);
|
const char *uhex2str(uint32_t val);
|
||||||
|
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);
|
||||||
|
int mystrlen(const char *txt);
|
||||||
|
//void mymemcpy(char *dest, const char *src, int len);
|
||||||
|
char *float2str(float x, uint8_t prec);
|
||||||
|
|||||||
@ -124,6 +124,10 @@ typedef struct {
|
|||||||
__IO uint32_t FNR;
|
__IO uint32_t FNR;
|
||||||
__IO uint32_t DADDR;
|
__IO uint32_t DADDR;
|
||||||
__IO uint32_t BTABLE;
|
__IO uint32_t BTABLE;
|
||||||
|
#ifdef STM32F0
|
||||||
|
__IO uint32_t LPMCSR;
|
||||||
|
__IO uint32_t BCDR;
|
||||||
|
#endif
|
||||||
} USB_TypeDef;
|
} USB_TypeDef;
|
||||||
|
|
||||||
// F303 D/E have 2x16 access scheme
|
// F303 D/E have 2x16 access scheme
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user