mirror of
https://github.com/eddyem/stm32samples.git
synced 2025-12-06 02:35:23 +03:00
262 lines
7.4 KiB
C
262 lines
7.4 KiB
C
/*
|
|
* 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();
|
|
}
|