make changes for anderen anlagenteil
parent
38d316bb45
commit
6bb5bfe5ba
@ -1,331 +1,311 @@
|
|||||||
#include <avr/io.h>
|
#include <avr/io.h>
|
||||||
#include <avr/interrupt.h>
|
#include <avr/interrupt.h>
|
||||||
#include <avr/wdt.h> // WatchDog
|
#include <avr/wdt.h> // WatchDog
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "Ethernet/socket.h"
|
#include "Ethernet/socket.h"
|
||||||
#include "Ethernet/wizchip_conf.h"
|
#include "Ethernet/wizchip_conf.h"
|
||||||
|
|
||||||
#include "Internet/MQTT/mqtt_interface.h"
|
#include "Internet/MQTT/mqtt_interface.h"
|
||||||
#include "Internet/MQTT/MQTTClient.h"
|
#include "Internet/MQTT/MQTTClient.h"
|
||||||
|
|
||||||
#include "avrIOhelper/io-helper.h"
|
#include "avrIOhelper/io-helper.h"
|
||||||
#include "millis.h"
|
#include "millis.h"
|
||||||
#include "uart.h"
|
#include "uart.h"
|
||||||
#include "spi.h"
|
#include "spi.h"
|
||||||
#include "mqtt.h"
|
#include "mqtt.h"
|
||||||
|
|
||||||
#include "util/delay.h"
|
#include "util/delay.h"
|
||||||
|
|
||||||
#define PLC_MQTT_ENABLED 0
|
#include "modbus.h"
|
||||||
|
#include "modbus_master.h"
|
||||||
Client mqtt_client;
|
|
||||||
|
#define PLC_MQTT_ENABLED 1
|
||||||
//***********Prologue for fast WDT disable & and save reason of reset/power-up: BEGIN
|
|
||||||
uint8_t mcucsr_mirror __attribute__ ((section (".noinit")));
|
Client mqtt_client;
|
||||||
|
|
||||||
// This is for fast WDT disable & and save reason of reset/power-up
|
//***********Prologue for fast WDT disable & and save reason of reset/power-up: BEGIN
|
||||||
void get_mcusr(void) \
|
uint8_t mcucsr_mirror __attribute__ ((section (".noinit")));
|
||||||
__attribute__((naked)) \
|
|
||||||
__attribute__((section(".init3")));
|
// This is for fast WDT disable & and save reason of reset/power-up
|
||||||
void get_mcusr(void)
|
void get_mcusr(void) \
|
||||||
{
|
__attribute__((naked)) \
|
||||||
mcucsr_mirror = MCUSR;
|
__attribute__((section(".init3")));
|
||||||
MCUSR = 0;
|
void get_mcusr(void)
|
||||||
wdt_disable();
|
{
|
||||||
}
|
mcucsr_mirror = MCUSR;
|
||||||
//***********Prologue for fast WDT disable & and save reason of reset/power-up: END
|
MCUSR = 0;
|
||||||
|
wdt_disable();
|
||||||
|
}
|
||||||
//FUNC headers
|
//***********Prologue for fast WDT disable & and save reason of reset/power-up: END
|
||||||
static void avr_init(void);
|
|
||||||
void timer0_init(void);
|
|
||||||
void print_network_information(void);
|
//FUNC headers
|
||||||
|
static void avr_init(void);
|
||||||
void IO_LIBRARY_Init(void) {
|
void modbus_master_init(void);
|
||||||
uint8_t bufSize[] = {2, 2, 2, 2, 2, 2, 2, 2};
|
void timer0_init(void);
|
||||||
|
void timer2_init(void);
|
||||||
reg_wizchip_cs_cbfunc(spi_select, spi_deselect);
|
void timer3_init(void);
|
||||||
reg_wizchip_spi_cbfunc(spi_read, spi_write);
|
void print_network_information(void);
|
||||||
//reg_wizchip_spiburst_cbfunc(spi_rb_burst, spi_wb_burst);
|
|
||||||
|
void IO_LIBRARY_Init(void) {
|
||||||
wizchip_init(bufSize, bufSize);
|
uint8_t bufSize[] = {2, 2, 2, 2, 2, 2, 2, 2};
|
||||||
wizchip_setnetinfo(&netInfo);
|
|
||||||
//wizchip_setinterruptmask(IK_SOCK_0);
|
reg_wizchip_cs_cbfunc(spi_select, spi_deselect);
|
||||||
}
|
reg_wizchip_spi_cbfunc(spi_read, spi_write);
|
||||||
|
//reg_wizchip_spiburst_cbfunc(spi_rb_burst, spi_wb_burst);
|
||||||
#define STEP_SIZE 5
|
|
||||||
#define TOP_VALUE 40
|
wizchip_init(bufSize, bufSize);
|
||||||
void do_luefter(){
|
wizchip_setnetinfo(&netInfo);
|
||||||
static uint8_t fan_value = TOP_VALUE*0.8;
|
//wizchip_setinterruptmask(IK_SOCK_0);
|
||||||
static uint8_t fan_state = 0;
|
}
|
||||||
|
|
||||||
if(!read_Input(IN_ANLAGE_EIN, LEVEL)){
|
void modbus_master_init(){
|
||||||
fan_state = 0;
|
modbusSetAddress(1); //better set this to sth.
|
||||||
ioHelperSetBit(outStates, LED_LUEFTER, 0);
|
modbusInit();
|
||||||
}
|
timer2_init(); // modbus tick timer
|
||||||
else{
|
}
|
||||||
if (read_Input(BTN_LUEFTER_EIN, RISING)) {
|
|
||||||
#if PLC_MQTT_ENABLED
|
static void avr_init()
|
||||||
mqtt_pub(&mqtt_client, "/Filamentanlage/04_Messmodul/state/Luefter", "ein", 3);
|
{
|
||||||
#endif
|
// Initialize device here.
|
||||||
OCR3B = TOP_VALUE/2;
|
// WatchDog INIT
|
||||||
fan_state = 1;
|
wdt_enable(WDTO_8S); // set up wdt reset interval 2 second
|
||||||
ioHelperSetBit(outStates, LED_LUEFTER, 1);
|
wdt_reset(); // wdt reset ~ every <2000ms
|
||||||
}
|
|
||||||
|
timer0_init();// Timer0 millis engine init
|
||||||
if (read_Input(BTN_LUEFTER_AUS, RISING)) {
|
//timer3_init();
|
||||||
#if PLC_MQTT_ENABLED
|
uart_init();
|
||||||
mqtt_pub(&mqtt_client, "/Filamentanlage/04_Messmodul/state/Luefter", "aus", 3);
|
|
||||||
#endif
|
sei(); //re-enable global interrupts
|
||||||
fan_state = 0;
|
|
||||||
ioHelperSetBit(outStates, LED_LUEFTER, 0);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (read_Input(BTN_LUEFTER_PLUS, RISING) && (fan_value+STEP_SIZE <= TOP_VALUE)) {
|
void do_kraftsensor(){
|
||||||
fan_value += STEP_SIZE;
|
uint16_t m_data[4];
|
||||||
#if PLC_MQTT_ENABLED
|
char msg[64];
|
||||||
char _msg[3];
|
|
||||||
sprintf(_msg, "%d", fan_value);
|
readReg(1,0,2);
|
||||||
mqtt_pub(&mqtt_client, "/Filamentanlage/04_Messmodul/state/Speed", _msg, 3);
|
if(wait_receive(2, m_data, 10))
|
||||||
#endif
|
sprintf(msg, "n/a");
|
||||||
printf("luefter %d\n\r", fan_value);
|
else{
|
||||||
}
|
int32_t tmp = (uint32_t)m_data[0]<<16;
|
||||||
|
tmp |= m_data[1];
|
||||||
if (read_Input(BTN_LUEFTER_MINUS, RISING) && (fan_value-STEP_SIZE >= STEP_SIZE)) {
|
sprintf(msg, "%ld", tmp);
|
||||||
fan_value -= STEP_SIZE;
|
}
|
||||||
printf("luefter %d\n\r", fan_value);
|
|
||||||
}
|
#if PLC_MQTT_ENABLED
|
||||||
}
|
mqtt_pub(&mqtt_client, "/Filamentanlage/05_Abzug/state/kraft", msg, strlen(msg));
|
||||||
|
#endif
|
||||||
if(fan_state)
|
}
|
||||||
OCR3B = fan_value;
|
|
||||||
else
|
void do_notaus(){
|
||||||
OCR3B = 0;
|
if(!read_Input(IN_NOTAUS_ANLAGE, LEVEL) || read_Input(IN_NOTAUS_SCHRANK, LEVEL) || read_Input(IN_NOTAUS_DISPLAY, LEVEL)){
|
||||||
}
|
/* at least on pressed */
|
||||||
|
ioHelperSetBit(outStates, AMPEL_ROT, 1);
|
||||||
void do_zumbach(){
|
ioHelperSetBit(outStates, AMPEL_GELB, 0);
|
||||||
|
ioHelperSetBit(outStates, AMPEL_GRUEN, 0);
|
||||||
if (read_Input(BTN_ZUMBACH_EIN, RISING) && read_Input(IN_ANLAGE_EIN, LEVEL)) {
|
|
||||||
ioHelperSetBit(outStates, SCHUETZ_ZUMBACH, 1);
|
set_Output(LED_GRN_NOTAUS_SCHRANK, OFF);
|
||||||
ioHelperSetBit(outStates, LED_ZUMBACH, 1);
|
set_Output(LED_GRN_NOTAUS_ANLAGE, OFF);
|
||||||
#if PLC_MQTT_ENABLED
|
set_Output(LED_GRN_NOTAUS_DISPLAY, OFF);
|
||||||
mqtt_pub(&mqtt_client, "/Filamentanlage/04_Messmodul/state/Zumbach", "ein", 3);
|
}
|
||||||
#endif
|
else if(!read_Input(IN_ANLAGE_EIN, LEVEL)){
|
||||||
}
|
/* nothing pressed, but power not on */
|
||||||
|
ioHelperSetBit(outStates, AMPEL_ROT, 0);
|
||||||
if (read_Input(BTN_ZUMBACH_AUS, RISING) || read_Input(IN_ANLAGE_EIN, FALLING)) {
|
ioHelperSetBit(outStates, AMPEL_GELB, 1);
|
||||||
ioHelperSetBit(outStates, SCHUETZ_ZUMBACH, 0);
|
ioHelperSetBit(outStates, AMPEL_GRUEN, 0);
|
||||||
ioHelperSetBit(outStates, LED_ZUMBACH, 0);
|
|
||||||
#if PLC_MQTT_ENABLED
|
set_Output(LED_ROT_NOTAUS_ANLAGE, OFF);
|
||||||
mqtt_pub(&mqtt_client, "/Filamentanlage/04_Messmodul/state/Zumbach", "aus", 3);
|
set_Output(LED_ROT_NOTAUS_SCHRANK, OFF);
|
||||||
#endif
|
set_Output(LED_ROT_NOTAUS_DISPLAY, OFF);
|
||||||
}
|
|
||||||
}
|
set_Output(LED_GRN_NOTAUS_SCHRANK, BLINK);
|
||||||
|
set_Output(LED_GRN_NOTAUS_ANLAGE, BLINK);
|
||||||
void do_notaus(){
|
set_Output(LED_GRN_NOTAUS_DISPLAY, BLINK);
|
||||||
if(read_Input(IN_NOTAUS_ANLAGE, LEVEL) || read_Input(IN_NOTAUS_SCHRANK, LEVEL)){ // NOTAUS
|
}
|
||||||
set_Output(LED_GRN_NOTAUS_SCHRANK, OFF); // disable green lamps
|
else{
|
||||||
set_Output(LED_GRN_NOTAUS_ANLAGE, OFF);
|
/* powered on */
|
||||||
}
|
ioHelperSetBit(outStates, AMPEL_ROT, 0);
|
||||||
|
ioHelperSetBit(outStates, AMPEL_GELB, 0);
|
||||||
if(read_Input(IN_NOTAUS_ANLAGE, LEVEL) && read_Input(IN_NOTAUS_SCHRANK, LEVEL)){ // both activated
|
ioHelperSetBit(outStates, AMPEL_GRUEN, 1);
|
||||||
set_Output(LED_ROT_NOTAUS_ANLAGE, BLINK);
|
|
||||||
set_Output(LED_ROT_NOTAUS_SCHRANK, BLINK);
|
set_Output(LED_GRN_NOTAUS_SCHRANK, ON);
|
||||||
}
|
set_Output(LED_GRN_NOTAUS_ANLAGE, ON);
|
||||||
else if(read_Input(IN_NOTAUS_ANLAGE, LEVEL)){ // top one activated
|
set_Output(LED_GRN_NOTAUS_DISPLAY, ON);
|
||||||
set_Output(LED_ROT_NOTAUS_ANLAGE, BLINK);
|
}
|
||||||
set_Output(LED_ROT_NOTAUS_SCHRANK, ON);
|
|
||||||
}
|
if(!read_Input(IN_NOTAUS_ANLAGE, LEVEL)){
|
||||||
else if(read_Input(IN_NOTAUS_SCHRANK, LEVEL)){ // bottom one activated
|
set_Output(LED_ROT_NOTAUS_ANLAGE, BLINK);
|
||||||
set_Output(LED_ROT_NOTAUS_SCHRANK, BLINK);
|
set_Output(LED_ROT_NOTAUS_SCHRANK, ON);
|
||||||
set_Output(LED_ROT_NOTAUS_ANLAGE, ON);
|
set_Output(LED_ROT_NOTAUS_DISPLAY, ON);
|
||||||
}
|
}
|
||||||
else{ // none activated
|
|
||||||
set_Output(LED_ROT_NOTAUS_SCHRANK, OFF);
|
if(read_Input(IN_NOTAUS_SCHRANK, LEVEL)){
|
||||||
set_Output(LED_ROT_NOTAUS_ANLAGE, OFF);
|
set_Output(LED_ROT_NOTAUS_ANLAGE, ON);
|
||||||
if(read_Input(IN_ANLAGE_EIN, LEVEL)){
|
set_Output(LED_ROT_NOTAUS_SCHRANK, BLINK);
|
||||||
set_Output(LED_GRN_NOTAUS_ANLAGE, ON);
|
set_Output(LED_ROT_NOTAUS_DISPLAY, ON);
|
||||||
set_Output(LED_GRN_NOTAUS_SCHRANK, ON);
|
}
|
||||||
}
|
|
||||||
else{
|
if(read_Input(IN_NOTAUS_DISPLAY, LEVEL)){
|
||||||
set_Output(LED_GRN_NOTAUS_ANLAGE, BLINK);
|
set_Output(LED_ROT_NOTAUS_ANLAGE, ON);
|
||||||
set_Output(LED_GRN_NOTAUS_SCHRANK, BLINK);
|
set_Output(LED_ROT_NOTAUS_SCHRANK, ON);
|
||||||
}
|
set_Output(LED_ROT_NOTAUS_DISPLAY, BLINK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
// INIT MCU
|
// INIT MCU
|
||||||
avr_init();
|
avr_init();
|
||||||
spi_init(); //SPI Master, MODE0, 4Mhz(DIV4), CS_PB.3=HIGH - suitable for WIZNET 5x00(1/2/5)
|
spi_init(); //SPI Master, MODE0, 4Mhz(DIV4), CS_PB.3=HIGH - suitable for WIZNET 5x00(1/2/5)
|
||||||
//spi_speed_tst(); / Here on SPI pins: MOSI 400Khz freq out, on SCLK 3.2MhzOUT (Witk SPI CLK 4Mhz)
|
modbus_master_init();
|
||||||
|
|
||||||
ioHelperInitBuffer();
|
printf("moin!\n\r");
|
||||||
ioHelperIoConf();
|
|
||||||
|
ioHelperInitBuffer();
|
||||||
//Wizchip WIZ5500 Ethernet initialize
|
ioHelperIoConf();
|
||||||
IO_LIBRARY_Init(); //After that ping must working
|
|
||||||
print_network_information();
|
|
||||||
|
//Wizchip WIZ5500 Ethernet initialize
|
||||||
#if PLC_MQTT_ENABLED
|
IO_LIBRARY_Init(); //After that ping must working
|
||||||
//****************MQTT client initialize
|
print_network_information();
|
||||||
//Find MQTT broker and connect with it
|
|
||||||
uint8_t mqtt_buf[100];
|
#if PLC_MQTT_ENABLED
|
||||||
int32_t mqtt_rc = 0;
|
//****************MQTT client initialize
|
||||||
Network mqtt_network;
|
//Find MQTT broker and connect with it
|
||||||
mqtt_network.my_socket = SOCK_MQTT;
|
uint8_t mqtt_buf[100];
|
||||||
|
int32_t mqtt_rc = 0;
|
||||||
printf(">>Trying connect to MQTT broker: %d.%d.%d.%d ..\r\n", MQTT_targetIP[0], MQTT_targetIP[1], MQTT_targetIP[2], MQTT_targetIP[3]);
|
Network mqtt_network;
|
||||||
NewNetwork(&mqtt_network);
|
mqtt_network.my_socket = SOCK_MQTT;
|
||||||
ConnectNetwork(&mqtt_network, MQTT_targetIP, 1883);
|
|
||||||
MQTTClient(&mqtt_client, &mqtt_network, 1000, mqtt_buf, 100, mqtt_readBuffer, MQTT_BUFFER_SIZE);
|
printf(">>Trying connect to MQTT broker: %d.%d.%d.%d ..\r\n", MQTT_targetIP[0], MQTT_targetIP[1], MQTT_targetIP[2], MQTT_targetIP[3]);
|
||||||
|
NewNetwork(&mqtt_network);
|
||||||
//Connection to MQTT broker
|
ConnectNetwork(&mqtt_network, MQTT_targetIP, 1883);
|
||||||
MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
|
MQTTClient(&mqtt_client, &mqtt_network, 1000, mqtt_buf, 100, mqtt_readBuffer, MQTT_BUFFER_SIZE);
|
||||||
data.willFlag = 0;
|
|
||||||
data.MQTTVersion = 4;//3;
|
//Connection to MQTT broker
|
||||||
data.clientID.cstring = (char*)"controllino";
|
MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
|
||||||
data.username.cstring = (char*)"Messmodul";
|
data.willFlag = 0;
|
||||||
data.password.cstring = (char*)"\0";
|
data.MQTTVersion = 4;//3;
|
||||||
data.keepAliveInterval = 10;
|
data.clientID.cstring = (char*)"controllino";
|
||||||
data.cleansession = 1;
|
data.username.cstring = (char*)"Messmodul";
|
||||||
mqtt_rc = MQTTConnect(&mqtt_client, &data);
|
data.password.cstring = (char*)"\0";
|
||||||
if (mqtt_rc == SUCCESSS)
|
data.keepAliveInterval = 10;
|
||||||
{
|
data.cleansession = 1;
|
||||||
printf("++MQTT Connected SUCCESS: %ld\r\n", mqtt_rc);
|
mqtt_rc = MQTTConnect(&mqtt_client, &data);
|
||||||
}
|
if (mqtt_rc == SUCCESSS)
|
||||||
else
|
{
|
||||||
{
|
printf("++MQTT Connected SUCCESS: %ld\r\n", mqtt_rc);
|
||||||
printf("--MQTT Connected ERROR: %ld\r\n", mqtt_rc);
|
}
|
||||||
//while(1); //Reboot the board
|
else
|
||||||
}
|
{
|
||||||
|
printf("--MQTT Connected ERROR: %ld\r\n", mqtt_rc);
|
||||||
// Subscribe to all topics
|
//while(1); //Reboot the board
|
||||||
char SubString[] = "/Filamentanlage/04_Messmodul/set/#";
|
}
|
||||||
mqtt_rc = MQTTSubscribe(&mqtt_client, SubString, QOS0, messageArrived);
|
|
||||||
printf("Subscribed (%s) %ld\r\n", SubString, mqtt_rc);
|
char SubString[] = "/Filamentanlage/04_Messmodul/set/#";
|
||||||
#endif
|
mqtt_rc = MQTTSubscribe(&mqtt_client, SubString, QOS0, messageArrived);
|
||||||
|
printf("Subscribed (%s) %ld\r\n", SubString, mqtt_rc);
|
||||||
|
#endif
|
||||||
ioHelperSetBit(outStatesBlinking, LED_PLC_OK, 1);
|
|
||||||
|
ioHelperSetBit(outStates, RELAY_INTERLOCK, 1);
|
||||||
uint32_t timer_blink_outs = millis();
|
|
||||||
uint32_t timer_send_uptime = millis();
|
ioHelperSetBit(outStatesBlinking, LED_PLC_OK, 1);
|
||||||
|
|
||||||
OCR3B = 127;
|
uint32_t timer_blink_outs = millis();
|
||||||
|
uint32_t timer_send_uptime = millis();
|
||||||
while(1)
|
uint32_t timer_modbus_poll = millis();
|
||||||
{
|
|
||||||
wdt_reset(); // WDT reset at least every sec
|
while(1)
|
||||||
|
{
|
||||||
//if (flag_refresh_inStates) {
|
wdt_reset(); // WDT reset at least every sec
|
||||||
ioHelperReadPins();
|
|
||||||
ioHelperDebounce();
|
//if (flag_refresh_inStates) {
|
||||||
ioHelperEdgeDetector();
|
ioHelperReadPins();
|
||||||
// flag_refresh_inStates = 0;
|
ioHelperDebounce();
|
||||||
//}
|
ioHelperEdgeDetector();
|
||||||
|
// flag_refresh_inStates = 0;
|
||||||
// Toggle all outs which are set to blinking
|
//}
|
||||||
if(millis() - timer_blink_outs > 500){
|
|
||||||
outStates[0] ^= outStatesBlinking[0];
|
// Toggle all outs which are set to blinking
|
||||||
outStates[1] ^= outStatesBlinking[1];
|
if(millis() - timer_blink_outs > 500){
|
||||||
timer_blink_outs = millis();
|
outStates[0] ^= outStatesBlinking[0];
|
||||||
}
|
outStates[1] ^= outStatesBlinking[1];
|
||||||
|
outStates[2] ^= outStatesBlinking[2];
|
||||||
#if PLC_MQTT_ENABLED
|
outStates[3] ^= outStatesBlinking[3];
|
||||||
// send misc info
|
timer_blink_outs = millis();
|
||||||
if(millis() - timer_send_uptime > 5000){
|
}
|
||||||
timer_send_uptime += 5000;
|
|
||||||
char msg[64];
|
#if PLC_MQTT_ENABLED
|
||||||
sprintf(msg, "%ld", millis()/1000);
|
// send misc info
|
||||||
mqtt_pub(&mqtt_client, "/Filamentanlage/04_Messmodul/state/uptime", msg, strlen(msg));
|
if(millis() - timer_send_uptime > 5000){
|
||||||
}
|
timer_send_uptime += 5000;
|
||||||
#endif
|
char msg[64];
|
||||||
|
sprintf(msg, "%ld", millis()/1000);
|
||||||
|
mqtt_pub(&mqtt_client, "/Filamentanlage/04_Messmodul/state/uptime", msg, strlen(msg));
|
||||||
if(read_Input(BTN_ANLAGE_EIN, RISING)){
|
}
|
||||||
printf("anlage in: %x\n\r", read_Input(IN_ANLAGE_EIN, LEVEL));
|
#endif
|
||||||
}
|
if(millis() - timer_modbus_poll > 1000){
|
||||||
// ioHelperSetBit(outStates, LED_GRN_NOTAUS_SCHRANK, 1);
|
timer_modbus_poll += 1000;
|
||||||
//}
|
do_kraftsensor();
|
||||||
//else{
|
}
|
||||||
// ioHelperSetBit(outStates, LED_GRN_NOTAUS_SCHRANK, 0);
|
|
||||||
//}
|
do_notaus();
|
||||||
|
|
||||||
do_luefter();
|
#if PLC_MQTT_ENABLED
|
||||||
do_notaus();
|
ioHelperSetBit(outStates, LED_BUS_OK, 1);
|
||||||
do_zumbach();
|
ioHelperSetOuts();
|
||||||
|
MQTTYield(&mqtt_client, 10); //blocking call
|
||||||
#if PLC_MQTT_ENABLED
|
ioHelperSetBit(outStates, LED_BUS_OK, 0);
|
||||||
ioHelperSetBit(outStates, LED_BUS_OK, 1);
|
#endif
|
||||||
ioHelperSetOuts();
|
ioHelperSetOuts();
|
||||||
MQTTYield(&mqtt_client, 10); //blocking call
|
}
|
||||||
ioHelperSetBit(outStates, LED_BUS_OK, 0);
|
return 0;
|
||||||
#endif
|
}
|
||||||
ioHelperSetOuts();
|
|
||||||
}
|
// Timer0
|
||||||
return 0;
|
// 1ms IRQ
|
||||||
}
|
// Used for millis() timing
|
||||||
|
void timer0_init()
|
||||||
// Timer0
|
{
|
||||||
// 1ms IRQ
|
TCCR0A = (1<<WGM01); //TIMER0 SET-UP: CTC MODE
|
||||||
// Used for millis() timing
|
TCCR0B = (1<<CS01)|(1<<CS00); // PS 1:64
|
||||||
void timer0_init(void)
|
OCR0A = 249; // 1ms reach for clear (16mz:64=>250kHz:250-=>1kHz)
|
||||||
{
|
TIMSK0 |= 1<<OCIE0A; //IRQ on TIMER0 output compareA
|
||||||
TCCR0A = (1<<WGM01); //TIMER0 SET-UP: CTC MODE
|
}
|
||||||
TCCR0B = (1<<CS01)|(1<<CS00); // PS 1:64
|
|
||||||
OCR0A = 249; // 1ms reach for clear (16mz:64=>250kHz:250-=>1kHz)
|
void timer2_init()
|
||||||
TIMSK0 |= 1<<OCIE0A; //IRQ on TIMER0 output compareA
|
{
|
||||||
}
|
TCCR2A = (1<<WGM21); //TIMER0 SET-UP: CTC MODE
|
||||||
|
TCCR2B|=(1<<CS21); //prescaler 8
|
||||||
void timer3_init(void)
|
OCR2A = 200; // 1ms reach for clear (16mz/8/200=>10kHz)
|
||||||
{
|
TIMSK2|=(1<<OCIE2A);
|
||||||
TCCR3A |= (1<<WGM30); // PWM Mode with ocra top
|
}
|
||||||
TCCR3B |= (1<<WGM33);
|
|
||||||
OCR3A = TOP_VALUE;
|
void print_network_information()
|
||||||
|
{
|
||||||
TCCR3B |= (0<<CS32)|(1<<CS31)|(0<<CS30); // PS 1:1
|
|
||||||
TCCR3A |= (1<<COM3B1) | (0<<COM3B0);
|
uint8_t tmpstr[6] = {0,};
|
||||||
DDRE |= 1 << 4;
|
ctlwizchip(CW_GET_ID,(void*)tmpstr); // Get WIZCHIP name
|
||||||
}
|
printf("\r\n=======================================\r\n");
|
||||||
|
printf(" WIZnet chip: %s \r\n", tmpstr);
|
||||||
static void avr_init(void)
|
printf("=======================================\r\n");
|
||||||
{
|
|
||||||
// Initialize device here.
|
wiz_NetInfo gWIZNETINFO;
|
||||||
// WatchDog INIT
|
wizchip_getnetinfo(&gWIZNETINFO);
|
||||||
wdt_enable(WDTO_8S); // set up wdt reset interval 2 second
|
if (gWIZNETINFO.dhcp == NETINFO_STATIC)
|
||||||
wdt_reset(); // wdt reset ~ every <2000ms
|
printf("STATIC IP\r\n");
|
||||||
|
else
|
||||||
timer0_init();// Timer0 millis engine init
|
printf("DHCP IP\r\n");
|
||||||
timer3_init();
|
printf("Mac address: %02x:%02x:%02x:%02x:%02x:%02x\n\r",gWIZNETINFO.mac[0],gWIZNETINFO.mac[1],gWIZNETINFO.mac[2],gWIZNETINFO.mac[3],gWIZNETINFO.mac[4],gWIZNETINFO.mac[5]);
|
||||||
uart_init();
|
printf("IP address : %d.%d.%d.%d\n\r",gWIZNETINFO.ip[0],gWIZNETINFO.ip[1],gWIZNETINFO.ip[2],gWIZNETINFO.ip[3]);
|
||||||
|
printf("SM Mask : %d.%d.%d.%d\n\r",gWIZNETINFO.sn[0],gWIZNETINFO.sn[1],gWIZNETINFO.sn[2],gWIZNETINFO.sn[3]);
|
||||||
sei(); //re-enable global interrupts
|
printf("Gate way : %d.%d.%d.%d\n\r",gWIZNETINFO.gw[0],gWIZNETINFO.gw[1],gWIZNETINFO.gw[2],gWIZNETINFO.gw[3]);
|
||||||
|
printf("DNS Server : %d.%d.%d.%d\n\r",gWIZNETINFO.dns[0],gWIZNETINFO.dns[1],gWIZNETINFO.dns[2],gWIZNETINFO.dns[3]);
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
ISR(TIMER2_COMPA_vect) { //this ISR is called 9765.625 times per second
|
||||||
void print_network_information(void)
|
modbusTickTimer();
|
||||||
{
|
}
|
||||||
|
|
||||||
uint8_t tmpstr[6] = {0,};
|
|
||||||
ctlwizchip(CW_GET_ID,(void*)tmpstr); // Get WIZCHIP name
|
|
||||||
printf("\r\n=======================================\r\n");
|
|
||||||
printf(" WIZnet chip: %s \r\n", tmpstr);
|
|
||||||
printf("=======================================\r\n");
|
|
||||||
|
|
||||||
wiz_NetInfo gWIZNETINFO;
|
|
||||||
wizchip_getnetinfo(&gWIZNETINFO);
|
|
||||||
if (gWIZNETINFO.dhcp == NETINFO_STATIC)
|
|
||||||
printf("STATIC IP\r\n");
|
|
||||||
else
|
|
||||||
printf("DHCP IP\r\n");
|
|
||||||
printf("Mac address: %02x:%02x:%02x:%02x:%02x:%02x\n\r",gWIZNETINFO.mac[0],gWIZNETINFO.mac[1],gWIZNETINFO.mac[2],gWIZNETINFO.mac[3],gWIZNETINFO.mac[4],gWIZNETINFO.mac[5]);
|
|
||||||
printf("IP address : %d.%d.%d.%d\n\r",gWIZNETINFO.ip[0],gWIZNETINFO.ip[1],gWIZNETINFO.ip[2],gWIZNETINFO.ip[3]);
|
|
||||||
printf("SM Mask : %d.%d.%d.%d\n\r",gWIZNETINFO.sn[0],gWIZNETINFO.sn[1],gWIZNETINFO.sn[2],gWIZNETINFO.sn[3]);
|
|
||||||
printf("Gate way : %d.%d.%d.%d\n\r",gWIZNETINFO.gw[0],gWIZNETINFO.gw[1],gWIZNETINFO.gw[2],gWIZNETINFO.gw[3]);
|
|
||||||
printf("DNS Server : %d.%d.%d.%d\n\r",gWIZNETINFO.dns[0],gWIZNETINFO.dns[1],gWIZNETINFO.dns[2],gWIZNETINFO.dns[3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,436 @@
|
|||||||
|
/*************************************************************************
|
||||||
|
Title: Yet another (small) modbus (server) implementation for the avr.
|
||||||
|
Author: Max Brueggemann
|
||||||
|
Hardware: any AVR with hardware UART, tested on Atmega 88/168 at 20Mhz
|
||||||
|
License: BSD-3-Clause
|
||||||
|
|
||||||
|
DESCRIPTION:
|
||||||
|
Refer to the header file yaMBSiavr.h.
|
||||||
|
|
||||||
|
USAGE:
|
||||||
|
Refer to the header file yaMBSiavr.h.
|
||||||
|
|
||||||
|
LICENSE:
|
||||||
|
|
||||||
|
Copyright 2017 Max Brueggemann, www.maxbrueggemann.de
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors
|
||||||
|
may be used to endorse or promote products derived from this software without
|
||||||
|
specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||||
|
THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
#include <avr/io.h>
|
||||||
|
#include "modbus.h"
|
||||||
|
#include <avr/interrupt.h>
|
||||||
|
|
||||||
|
volatile unsigned char BusState = 0;
|
||||||
|
volatile uint16_t modbusTimer = 0;
|
||||||
|
volatile unsigned char rxbuffer[MaxFrameIndex+1];
|
||||||
|
volatile uint16_t DataPos = 0;
|
||||||
|
volatile unsigned char PacketTopIndex = 7;
|
||||||
|
volatile unsigned char modBusStaMaStates = 0;
|
||||||
|
volatile uint16_t modbusDataAmount = 0;
|
||||||
|
volatile uint16_t modbusDataLocation = 0;
|
||||||
|
|
||||||
|
/* @brief: save address and amount
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void modbusSaveLocation(void)
|
||||||
|
{
|
||||||
|
modbusDataLocation=(rxbuffer[3]|(rxbuffer[2]<<8));
|
||||||
|
if (rxbuffer[1]==fcPresetSingleRegister || rxbuffer[1]==fcForceSingleCoil) modbusDataAmount=1;
|
||||||
|
else modbusDataAmount=(rxbuffer[5]|(rxbuffer[4]<<8));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @brief: returns 1 if data location adr is touched by current command
|
||||||
|
*
|
||||||
|
* Arguments: - adr: address of the data object
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
uint8_t modbusIsInRange(uint16_t adr)
|
||||||
|
{
|
||||||
|
if((modbusDataLocation <= adr) && (adr<(modbusDataLocation+modbusDataAmount)))
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @brief: returns 1 if range of data locations is touched by current command
|
||||||
|
*
|
||||||
|
* Arguments: - startAdr: address of first data object in range
|
||||||
|
* - lastAdr: address of last data object in range
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
uint8_t modbusIsRangeInRange(uint16_t startAdr, uint16_t lastAdr)
|
||||||
|
{
|
||||||
|
if(modbusIsInRange(startAdr) && modbusIsInRange(lastAdr))
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t modbusGetBusState(void)
|
||||||
|
{
|
||||||
|
return BusState;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if ADDRESS_MODE == SINGLE_ADR
|
||||||
|
volatile unsigned char Address = 0x00;
|
||||||
|
uint8_t modbusGetAddress(void)
|
||||||
|
{
|
||||||
|
return Address;
|
||||||
|
}
|
||||||
|
|
||||||
|
void modbusSetAddress(unsigned char newadr)
|
||||||
|
{
|
||||||
|
Address = newadr;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if PHYSICAL_TYPE == 485
|
||||||
|
void transceiver_txen(void)
|
||||||
|
{
|
||||||
|
TRANSCEIVER_ENABLE_PORT|=(1<<TRANSCEIVER_ENABLE_PIN);
|
||||||
|
TRANSCEIVER_ENABLE_PORT|=(1<<TRANSCEIVER_ENABLE_PIN_2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void transceiver_rxen(void)
|
||||||
|
{
|
||||||
|
TRANSCEIVER_ENABLE_PORT&=~(1<<TRANSCEIVER_ENABLE_PIN);
|
||||||
|
TRANSCEIVER_ENABLE_PORT&=~(1<<TRANSCEIVER_ENABLE_PIN_2);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* @brief: A fairly simple Modbus compliant 16 Bit CRC algorithm.
|
||||||
|
*
|
||||||
|
* Returns 1 if the crc check is positive, returns 0 and saves the calculated CRC bytes
|
||||||
|
* at the end of the data array if it fails.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
uint8_t crc16(volatile uint8_t *ptrToArray,uint8_t inputSize) //A standard CRC algorithm
|
||||||
|
{
|
||||||
|
uint16_t out=0xffff;
|
||||||
|
uint16_t carry;
|
||||||
|
unsigned char n;
|
||||||
|
inputSize++;
|
||||||
|
for (int l=0; l<inputSize; l++) {
|
||||||
|
out ^= ptrToArray[l];
|
||||||
|
for (n = 0; n < 8; n++) {
|
||||||
|
carry = out & 1;
|
||||||
|
out >>= 1;
|
||||||
|
if (carry) out ^= 0xA001;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//out=0x1234;
|
||||||
|
if ((ptrToArray[inputSize]==out%256) && (ptrToArray[inputSize+1]==out/256)) //check
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
ptrToArray[inputSize]=out%256; //append Lo
|
||||||
|
ptrToArray[inputSize+1]=out/256; //append Hi
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @brief: copies a single bit from one char to another char (or arrays thereof)
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void listBitCopy(volatile uint8_t *source, uint16_t sourceNr,volatile uint8_t *target, uint16_t targetNr)
|
||||||
|
{
|
||||||
|
if(*(source+(sourceNr/8))&(1<<(sourceNr-((sourceNr/8)*8))))
|
||||||
|
{
|
||||||
|
*(target+(targetNr/8))|=(1<<(targetNr-((targetNr/8)*8)));
|
||||||
|
} else *(target+(targetNr/8))&=~(1<<(targetNr-((targetNr/8)*8)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @brief: Back to receiving state.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void modbusReset(void)
|
||||||
|
{
|
||||||
|
BusState=(1<<TimerActive); //stop receiving (error)
|
||||||
|
modbusTimer=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void modbusTickTimer(void)
|
||||||
|
{
|
||||||
|
if (BusState&(1<<TimerActive))
|
||||||
|
{
|
||||||
|
modbusTimer++;
|
||||||
|
if (BusState&(1<<Receiving)) //we are in receiving mode
|
||||||
|
{
|
||||||
|
if ((modbusTimer==modbusInterCharTimeout)) {
|
||||||
|
BusState|=(1<<GapDetected);
|
||||||
|
} else if ((modbusTimer==modbusInterFrameDelayReceiveEnd)) { //end of message
|
||||||
|
#if ADDRESS_MODE == MULTIPLE_ADR
|
||||||
|
if (crc16(rxbuffer,DataPos-3)) { //perform crc check only. This is for multiple/all address mode.
|
||||||
|
modbusSaveLocation();
|
||||||
|
BusState=(1<<ReceiveCompleted);
|
||||||
|
} else modbusReset();
|
||||||
|
#endif
|
||||||
|
#if ADDRESS_MODE == SINGLE_ADR
|
||||||
|
if (rxbuffer[0]==Address && crc16(rxbuffer,DataPos-3)) { //is the message for us? => perform crc check
|
||||||
|
modbusSaveLocation();
|
||||||
|
BusState=(1<<ReceiveCompleted);
|
||||||
|
} else modbusReset();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
} else if (modbusTimer==modbusInterFrameDelayReceiveStart){
|
||||||
|
BusState|=(1<<BusTimedOut);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ISR(UART_RECEIVE_INTERRUPT)
|
||||||
|
{
|
||||||
|
unsigned char data;
|
||||||
|
data = UART_DATA;
|
||||||
|
//printf("%x ", data);
|
||||||
|
modbusTimer=0; //reset timer
|
||||||
|
if (!(BusState & (1<<ReceiveCompleted)) && !(BusState & (1<<TransmitRequested)) && !(BusState & (1<<Transmitting)) && (BusState & (1<<Receiving)) && !(BusState & (1<<BusTimedOut)))
|
||||||
|
{
|
||||||
|
if (DataPos>MaxFrameIndex) modbusReset();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rxbuffer[DataPos]=data;
|
||||||
|
DataPos++; //TODO: maybe prevent this from exceeding 255?
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
// Bus state is Timed out..
|
||||||
|
if (!(BusState & (1<<ReceiveCompleted)) && !(BusState & (1<<TransmitRequested)) && !(BusState & (1<<Transmitting)) && !(BusState & (1<<Receiving)) && (BusState & (1<<BusTimedOut)))
|
||||||
|
{
|
||||||
|
rxbuffer[0]=data;
|
||||||
|
BusState=((1<<Receiving)|(1<<TimerActive));
|
||||||
|
DataPos=1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ISR(UART_TRANSMIT_INTERRUPT)
|
||||||
|
{
|
||||||
|
BusState&=~(1<<TransmitRequested);
|
||||||
|
BusState|=(1<<Transmitting);
|
||||||
|
UART_DATA=rxbuffer[DataPos];
|
||||||
|
DataPos++;
|
||||||
|
if (DataPos==(PacketTopIndex+1)) {
|
||||||
|
UART_CONTROL&=~(1<<UART_UDRIE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ISR(UART_TRANSMIT_COMPLETE_INTERRUPT)
|
||||||
|
{
|
||||||
|
#if PHYSICAL_TYPE == 485
|
||||||
|
transceiver_rxen();
|
||||||
|
#endif
|
||||||
|
modbusReset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void modbusInit(void)
|
||||||
|
{
|
||||||
|
UBRRH = (unsigned char)((_UBRR) >> 8);
|
||||||
|
UBRRL = (unsigned char) _UBRR;
|
||||||
|
UART_STATUS = (1<<U2X); //double speed mode.
|
||||||
|
#ifdef URSEL // if UBRRH and UCSRC share the same I/O location , e.g. ATmega8
|
||||||
|
UCSRC = (1<<URSEL)|(3<<UCSZ0); //Frame Size
|
||||||
|
#else
|
||||||
|
UCSRC = (3<<UCSZ0); //Frame Size
|
||||||
|
#endif
|
||||||
|
UART_CONTROL = (1<<TXCIE)|(1<<RXCIE)|(1<<RXEN)|(1<<TXEN); // USART receiver and transmitter and receive complete interrupt
|
||||||
|
#if PHYSICAL_TYPE == 485
|
||||||
|
TRANSCEIVER_ENABLE_PORT_DDR|=(1<<TRANSCEIVER_ENABLE_PIN);
|
||||||
|
TRANSCEIVER_ENABLE_PORT_DDR|=(1<<TRANSCEIVER_ENABLE_PIN_2);
|
||||||
|
transceiver_rxen();
|
||||||
|
#endif
|
||||||
|
BusState=(1<<TimerActive);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @brief: Sends a response.
|
||||||
|
*
|
||||||
|
* Arguments: - packtop: Position of the last byte containing data.
|
||||||
|
* modbusSendException is a good usage example.
|
||||||
|
*/
|
||||||
|
void modbusSendMessage(unsigned char packtop)
|
||||||
|
{
|
||||||
|
PacketTopIndex=packtop+2;
|
||||||
|
crc16(rxbuffer,packtop);
|
||||||
|
BusState|=(1<<TransmitRequested);
|
||||||
|
DataPos=0;
|
||||||
|
#if PHYSICAL_TYPE == 485
|
||||||
|
transceiver_txen();
|
||||||
|
#endif
|
||||||
|
UART_CONTROL|=(1<<UART_UDRIE);
|
||||||
|
BusState&=~(1<<ReceiveCompleted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @brief: Sends an exception response.
|
||||||
|
*
|
||||||
|
* Arguments: - exceptionCode
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void modbusSendException(unsigned char exceptionCode)
|
||||||
|
{
|
||||||
|
rxbuffer[1]|=(1<<7); //setting MSB of the function code (the exception flag)
|
||||||
|
rxbuffer[2]=exceptionCode; //Exceptioncode. Also the last byte containing data
|
||||||
|
modbusSendMessage(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* @brief: Returns the amount of requested data objects (coils, discretes, registers)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
uint16_t modbusRequestedAmount(void)
|
||||||
|
{
|
||||||
|
return modbusDataAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @brief: Returns the address of the first requested data object (coils, discretes, registers)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
uint16_t modbusRequestedAddress(void)
|
||||||
|
{
|
||||||
|
return modbusDataLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @brief: copies a single or multiple bytes from one array of bytes to an array of 16-bit-words
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void intToModbusRegister(volatile uint16_t *inreg, volatile uint8_t *outreg, uint8_t amount)
|
||||||
|
{
|
||||||
|
for (uint8_t c=0; c<amount; c++)
|
||||||
|
{
|
||||||
|
*(outreg+c*2) = (uint8_t)(*(inreg+c) >> 8);
|
||||||
|
*(outreg+1+c*2) = (uint8_t)(*(inreg+c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @brief: copies a single or multiple 16-bit-words from one array of integers to an array of bytes
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void modbusRegisterToInt(volatile uint8_t *inreg, volatile uint16_t *outreg, uint8_t amount)
|
||||||
|
{
|
||||||
|
for (uint8_t c=0; c<amount; c++)
|
||||||
|
{
|
||||||
|
*(outreg+c) = (*(inreg+c*2) << 8) + *(inreg+1+c*2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @brief: Handles single/multiple register reading and single/multiple register writing.
|
||||||
|
*
|
||||||
|
* Arguments: - ptrToInArray: pointer to the user's data array containing registers
|
||||||
|
* - startAddress: address of the first register in the supplied array
|
||||||
|
* - size: input array size in the requested format (16bit-registers)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
uint8_t modbusExchangeRegisters(volatile uint16_t *ptrToInArray, uint16_t startAddress, uint16_t size)
|
||||||
|
{
|
||||||
|
if ((modbusDataLocation>=startAddress) && ((startAddress+size)>=(modbusDataAmount+modbusDataLocation))) {
|
||||||
|
|
||||||
|
if ((rxbuffer[1]==fcReadHoldingRegisters) || (rxbuffer[1]==fcReadInputRegisters) )
|
||||||
|
{
|
||||||
|
if ((modbusDataAmount*2)<=(MaxFrameIndex-4)) //message buffer big enough?
|
||||||
|
{
|
||||||
|
rxbuffer[2]=(unsigned char)(modbusDataAmount*2);
|
||||||
|
intToModbusRegister(ptrToInArray+(modbusDataLocation-startAddress),rxbuffer+3,modbusDataAmount);
|
||||||
|
modbusSendMessage(2+rxbuffer[2]);
|
||||||
|
return 1;
|
||||||
|
} else modbusSendException(ecIllegalDataValue);
|
||||||
|
}
|
||||||
|
else if (rxbuffer[1]==fcPresetMultipleRegisters)
|
||||||
|
{
|
||||||
|
if (((rxbuffer[6])>=modbusDataAmount*2) && ((DataPos-9)>=rxbuffer[6])) //enough data received?
|
||||||
|
{
|
||||||
|
modbusRegisterToInt(rxbuffer+7,ptrToInArray+(modbusDataLocation-startAddress),(unsigned char)(modbusDataAmount));
|
||||||
|
modbusSendMessage(5);
|
||||||
|
return 1;
|
||||||
|
} else modbusSendException(ecIllegalDataValue);//too few data bytes received
|
||||||
|
}
|
||||||
|
else if (rxbuffer[1]==fcPresetSingleRegister)
|
||||||
|
{
|
||||||
|
modbusRegisterToInt(rxbuffer+4,ptrToInArray+(modbusDataLocation-startAddress),1);
|
||||||
|
modbusSendMessage(5);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
//modbusSendException(ecSlaveDeviceFailure); //inapropriate call of modbusExchangeRegisters
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
modbusSendException(ecIllegalDataValue);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @brief: Handles single/multiple input/coil reading and single/multiple coil writing.
|
||||||
|
*
|
||||||
|
* Arguments: - ptrToInArray: pointer to the user's data array containing bits
|
||||||
|
* - startAddress: address of the first bit in the supplied array
|
||||||
|
* - size: input array size in the requested format (bits)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
uint8_t modbusExchangeBits(volatile uint8_t *ptrToInArray, uint16_t startAddress, uint16_t size)
|
||||||
|
{
|
||||||
|
if ((modbusDataLocation>=startAddress) && ((startAddress+size)>=(modbusDataAmount+modbusDataLocation)))
|
||||||
|
{
|
||||||
|
if ((rxbuffer[1]==fcReadInputStatus) || (rxbuffer[1]==fcReadCoilStatus))
|
||||||
|
{
|
||||||
|
if (modbusDataAmount<=((MaxFrameIndex-4)*8)) //message buffer big enough?
|
||||||
|
{
|
||||||
|
rxbuffer[2]=(modbusDataAmount/8);
|
||||||
|
if (modbusDataAmount%8>0)
|
||||||
|
{
|
||||||
|
rxbuffer[(uint8_t)(modbusDataAmount/8)+3]=0x00; //fill last data byte with zeros
|
||||||
|
rxbuffer[2]++;
|
||||||
|
}
|
||||||
|
for (uint16_t c = 0; c<modbusDataAmount; c++)
|
||||||
|
{
|
||||||
|
listBitCopy(ptrToInArray,modbusDataLocation-startAddress+c,rxbuffer+3,c);
|
||||||
|
}
|
||||||
|
modbusSendMessage(rxbuffer[2]+2);
|
||||||
|
return 1;
|
||||||
|
} else modbusSendException(ecIllegalDataValue); //too many bits requested within single request
|
||||||
|
}
|
||||||
|
else if (rxbuffer[1]==fcForceMultipleCoils)
|
||||||
|
{
|
||||||
|
if (((rxbuffer[6]*8)>=modbusDataAmount) && ((DataPos-9)>=rxbuffer[6])) //enough data received?
|
||||||
|
{
|
||||||
|
for (uint16_t c = 0; c<modbusDataAmount; c++)
|
||||||
|
{
|
||||||
|
listBitCopy(rxbuffer+7,c,ptrToInArray,modbusDataLocation-startAddress+c);
|
||||||
|
}
|
||||||
|
modbusSendMessage(5);
|
||||||
|
return 1;
|
||||||
|
} else modbusSendException(ecIllegalDataValue);//exception too few data bytes received
|
||||||
|
}
|
||||||
|
else if (rxbuffer[1]==fcForceSingleCoil) {
|
||||||
|
listBitCopy(rxbuffer+4,0,ptrToInArray,modbusDataLocation-startAddress);
|
||||||
|
modbusSendMessage(5);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
//modbusSendException(ecSlaveDeviceFailure); //inanpropriate call of modbusExchangeBits
|
||||||
|
return 0;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
modbusSendException(ecIllegalDataValue);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,421 @@
|
|||||||
|
#ifndef yaMBIavr_H
|
||||||
|
#define yaMBIavr_H
|
||||||
|
/************************************************************************
|
||||||
|
Title: Yet another (small) Modbus (server) implementation for the avr.
|
||||||
|
Author: Max Brueggemann
|
||||||
|
Hardware: any AVR with hardware UART, tested on Atmega 88/168 at 20Mhz
|
||||||
|
License: BSD-3-Clause
|
||||||
|
|
||||||
|
LICENSE:
|
||||||
|
|
||||||
|
Copyright 2017 Max Brueggemann, www.maxbrueggemann.de
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its contributors
|
||||||
|
may be used to endorse or promote products derived from this software without
|
||||||
|
specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||||
|
THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
************************************************************************/
|
||||||
|
#include <avr/io.h>
|
||||||
|
/**
|
||||||
|
* @code #include <yaMBSIavr.h> @endcode
|
||||||
|
*
|
||||||
|
* @brief Interrupt-based Modbus implementation for small avr microcontrollers.
|
||||||
|
* The Modbus implementation guidelines at modbus.org call for response
|
||||||
|
* timeouts in the range of several seconds , hence only timing critical
|
||||||
|
* parts have been implemented within ISRs. The actual handling of the Modbus
|
||||||
|
* frame can easily be done in the main while loop.
|
||||||
|
*
|
||||||
|
* @author Max Brueggemann www.maxbrueggemann.de
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* define baudrate of modbus */
|
||||||
|
#ifndef BAUD
|
||||||
|
#define BAUD 38400L
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Definitions for transceiver enable pin.
|
||||||
|
*/
|
||||||
|
#ifndef TRANSCEIVER_ENABLE_PORT
|
||||||
|
#define TRANSCEIVER_ENABLE_PORT PORTJ
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TRANSCEIVER_ENABLE_PIN
|
||||||
|
#define TRANSCEIVER_ENABLE_PIN 6
|
||||||
|
#define TRANSCEIVER_ENABLE_PIN_2 5
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef TRANSCEIVER_ENABLE_PORT_DDR
|
||||||
|
#define TRANSCEIVER_ENABLE_PORT_DDR DDRJ
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
* At the moment the user has to set the value for Baudrate and
|
||||||
|
* speed mode manually. The values depend on the operating frequency
|
||||||
|
* of your AVR and can be found in its datasheet.
|
||||||
|
*/
|
||||||
|
#if defined(__AVR_ATtiny2313__)
|
||||||
|
#define UART_TRANSMIT_COMPLETE_INTERRUPT USART_TX_vect
|
||||||
|
#define UART_RECEIVE_INTERRUPT USART_RX_vect
|
||||||
|
#define UART_TRANSMIT_INTERRUPT USART_UDRE_vect
|
||||||
|
#define UART_STATUS UCSRA
|
||||||
|
#define UART_CONTROL UCSRB
|
||||||
|
#define UART_DATA UDR
|
||||||
|
#define UART_UDRIE UDRIE
|
||||||
|
|
||||||
|
#elif defined(__AVR_ATmega164P__)
|
||||||
|
#define UART_TRANSMIT_COMPLETE_INTERRUPT USART1_TX_vect
|
||||||
|
#define UART_RECEIVE_INTERRUPT USART1_RX_vect
|
||||||
|
#define UART_TRANSMIT_INTERRUPT USART1_UDRE_vect
|
||||||
|
#define UART_STATUS UCSR1A
|
||||||
|
#define UART_CONTROL UCSR1B
|
||||||
|
#define UART_DATA UDR1
|
||||||
|
#define UART_UDRIE UDRIE1
|
||||||
|
#define UCSRC UCSR1C
|
||||||
|
#define RXCIE RXCIE1
|
||||||
|
#define TXCIE TXCIE1
|
||||||
|
#define RXEN RXEN1
|
||||||
|
#define TXEN TXEN1
|
||||||
|
#define UCSZ0 UCSZ10
|
||||||
|
#define U2X U2X1
|
||||||
|
#define UBRRH UBRR1H
|
||||||
|
#define UBRRL UBRR1L
|
||||||
|
|
||||||
|
#elif defined(__AVR_ATmega168PA__)|(__AVR_ATmega88PA__)|(__AVR_ATmega328P__)|(__AVR_ATmega168P__)|(__AVR_ATmega88P__)
|
||||||
|
#define UART_TRANSMIT_COMPLETE_INTERRUPT USART_TX_vect
|
||||||
|
#define UART_RECEIVE_INTERRUPT USART_RX_vect
|
||||||
|
#define UART_TRANSMIT_INTERRUPT USART_UDRE_vect
|
||||||
|
#define UART_STATUS UCSR0A
|
||||||
|
#define UART_CONTROL UCSR0B
|
||||||
|
#define UART_DATA UDR0
|
||||||
|
#define UART_UDRIE UDRIE0
|
||||||
|
#define UCSRC UCSR0C
|
||||||
|
#define RXCIE RXCIE0
|
||||||
|
#define TXCIE TXCIE0
|
||||||
|
#define RXEN RXEN0
|
||||||
|
#define TXEN TXEN0
|
||||||
|
#define UCSZ0 UCSZ00
|
||||||
|
#define U2X U2X0
|
||||||
|
#define UBRRH UBRR0H
|
||||||
|
#define UBRRL UBRR0L
|
||||||
|
|
||||||
|
#elif defined(__AVR_ATmega328PB__)
|
||||||
|
#define UART_TRANSMIT_COMPLETE_INTERRUPT USART0_TX_vect
|
||||||
|
#define UART_RECEIVE_INTERRUPT USART0_RX_vect
|
||||||
|
#define UART_TRANSMIT_INTERRUPT USART0_UDRE_vect
|
||||||
|
#define UART_STATUS UCSR0A
|
||||||
|
#define UART_CONTROL UCSR0B
|
||||||
|
#define UART_DATA UDR0
|
||||||
|
#define UART_UDRIE UDRIE0
|
||||||
|
#define UCSRC UCSR0C
|
||||||
|
#define RXCIE RXCIE0
|
||||||
|
#define TXCIE TXCIE0
|
||||||
|
#define RXEN RXEN0
|
||||||
|
#define TXEN TXEN0
|
||||||
|
#define UCSZ0 UCSZ00
|
||||||
|
#define U2X U2X0
|
||||||
|
#define UBRRH UBRR0H
|
||||||
|
#define UBRRL UBRR0L
|
||||||
|
|
||||||
|
#elif defined(__AVR_ATtiny441__)
|
||||||
|
#define UART_TRANSMIT_COMPLETE_INTERRUPT USART0_TX_vect
|
||||||
|
#define UART_RECEIVE_INTERRUPT USART0_RX_vect
|
||||||
|
#define UART_TRANSMIT_INTERRUPT USART0_UDRE_vect
|
||||||
|
#define UART_STATUS UCSR0A
|
||||||
|
#define UART_CONTROL UCSR0B
|
||||||
|
#define UART_DATA UDR0
|
||||||
|
#define UART_UDRIE UDRIE0
|
||||||
|
#define UCSRC UCSR0C
|
||||||
|
#define RXCIE RXCIE0
|
||||||
|
#define TXCIE TXCIE0
|
||||||
|
#define RXEN RXEN0
|
||||||
|
#define TXEN TXEN0
|
||||||
|
#define UCSZ0 UCSZ00
|
||||||
|
#define U2X U2X0
|
||||||
|
#define UBRRH UBRR0H
|
||||||
|
#define UBRRL UBRR0L
|
||||||
|
|
||||||
|
#elif defined(__AVR_ATmega8__)|| defined(__AVR_ATmega16__) || defined(__AVR_ATmega32__) || defined(__AVR_ATmega323__)
|
||||||
|
#define UART_TRANSMIT_COMPLETE_INTERRUPT USART_TXC_vect
|
||||||
|
#define UART_RECEIVE_INTERRUPT USART_RXC_vect
|
||||||
|
#define UART_TRANSMIT_INTERRUPT USART_UDRE_vect
|
||||||
|
#define UART_STATUS UCSRA
|
||||||
|
#define UART_CONTROL UCSRB
|
||||||
|
#define UART_DATA UDR
|
||||||
|
#define UART_UDRIE UDRIE
|
||||||
|
|
||||||
|
#elif defined(__AVR_AT90PWM3B__)
|
||||||
|
#define UART_TRANSMIT_COMPLETE_INTERRUPT USART_TX_vect
|
||||||
|
#define UART_RECEIVE_INTERRUPT USART_RX_vect
|
||||||
|
#define UART_TRANSMIT_INTERRUPT USART_UDRE_vect
|
||||||
|
#define UART_STATUS UCSRA
|
||||||
|
#define UART_CONTROL UCSRB
|
||||||
|
#define UART_DATA UDR
|
||||||
|
#define UART_UDRIE UDRIE
|
||||||
|
|
||||||
|
#elif defined(__AVR_ATmega1284P__)
|
||||||
|
#define UART_TRANSMIT_COMPLETE_INTERRUPT USART0_TX_vect
|
||||||
|
#define UART_RECEIVE_INTERRUPT USART0_RX_vect
|
||||||
|
#define UART_TRANSMIT_INTERRUPT USART0_UDRE_vect
|
||||||
|
#define UART_STATUS UCSR0A
|
||||||
|
#define UART_CONTROL UCSR0B
|
||||||
|
#define UART_DATA UDR0
|
||||||
|
#define UART_UDRIE UDRIE0
|
||||||
|
#define UCSRC UCSR0C
|
||||||
|
#define RXCIE RXCIE0
|
||||||
|
#define TXCIE TXCIE0
|
||||||
|
#define RXEN RXEN0
|
||||||
|
#define TXEN TXEN0
|
||||||
|
#define UCSZ0 UCSZ00
|
||||||
|
#define U2X U2X0
|
||||||
|
#define UBRRH UBRR0H
|
||||||
|
#define UBRRL UBRR0L
|
||||||
|
|
||||||
|
#elif defined(__AVR_ATmega2560__)
|
||||||
|
#define UART_TRANSMIT_COMPLETE_INTERRUPT USART3_TX_vect
|
||||||
|
#define UART_RECEIVE_INTERRUPT USART3_RX_vect
|
||||||
|
#define UART_TRANSMIT_INTERRUPT USART3_UDRE_vect
|
||||||
|
#define UART_STATUS UCSR3A
|
||||||
|
#define UART_CONTROL UCSR3B
|
||||||
|
#define UART_DATA UDR3
|
||||||
|
#define UART_UDRIE UDRIE3
|
||||||
|
#define UCSRC UCSR3C
|
||||||
|
#define RXCIE RXCIE3
|
||||||
|
#define TXCIE TXCIE3
|
||||||
|
#define RXEN RXEN3
|
||||||
|
#define TXEN TXEN3
|
||||||
|
#define UCSZ0 UCSZ30
|
||||||
|
#define U2X U2X0
|
||||||
|
#define UBRRH UBRR3H
|
||||||
|
#define UBRRL UBRR3L
|
||||||
|
|
||||||
|
#else
|
||||||
|
#error "no definition available"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef F_CPU
|
||||||
|
#error " F_CPU not defined "
|
||||||
|
#else
|
||||||
|
#define _UBRR (F_CPU / 8 / BAUD ) -1
|
||||||
|
#endif /* F_CPU */
|
||||||
|
/*
|
||||||
|
* Available address modes.
|
||||||
|
*/
|
||||||
|
#define MULTIPLE_ADR 2
|
||||||
|
#define SINGLE_ADR 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use SINGLE_ADR or MULTIPLE_ADR, default: SINGLE_ADR
|
||||||
|
* This is useful for building gateways, routers or clients that for whatever reason need multiple addresses.
|
||||||
|
*/
|
||||||
|
#define ADDRESS_MODE SINGLE_ADR
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use 485 or 232, default: 485
|
||||||
|
* Use 232 for testing purposes or very simple applications that do not require RS485 and bus topology.
|
||||||
|
*/
|
||||||
|
#define PHYSICAL_TYPE 485 //possible values: 485, 232
|
||||||
|
|
||||||
|
#if BAUD>=19200
|
||||||
|
#define modbusInterFrameDelayReceiveStart 16
|
||||||
|
#define modbusInterFrameDelayReceiveEnd 18
|
||||||
|
#define modbusInterCharTimeout 7
|
||||||
|
#else
|
||||||
|
#define modbusBlocksize 10
|
||||||
|
#define modbusBlockTime ((float)modbusBlocksize*1000000)/((float) BAUD) //is 260 for 38400
|
||||||
|
#define timerISROccurenceTime 100 //time in microseconds between two calls of modbusTickTimer
|
||||||
|
#define modbusInterFrameDelayReceiveStart (uint16_t)(modbusBlockTime*3.5/(float)timerISROccurenceTime)
|
||||||
|
#define modbusInterFrameDelayReceiveEnd (uint16_t)(modbusBlockTime*4/(float)timerISROccurenceTime)
|
||||||
|
#define modbusInterCharTimeout (uint16_t)(modbusBlockTime*1.5/(float)timerISROccurenceTime)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Defines the maximum Modbus frame size accepted by the device. 255 is the default
|
||||||
|
* and also the maximum value. However, it might be useful to set this to lower
|
||||||
|
* values, with 8 being the lowest possible value, in order to save on ram space.
|
||||||
|
*/
|
||||||
|
#define MaxFrameIndex 255
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Modbus Function Codes
|
||||||
|
* Refer to modbus.org for further information.
|
||||||
|
* It's good practice to return exception code 01 in case you receive a function code
|
||||||
|
* that you haven't implemented in your application.
|
||||||
|
*/
|
||||||
|
#define fcReadCoilStatus 1 //read single/multiple coils
|
||||||
|
#define fcReadInputStatus 2 //read single/multiple inputs
|
||||||
|
#define fcReadHoldingRegisters 3 //read analog output registers
|
||||||
|
#define fcReadInputRegisters 4 //read analog input registers (2 Bytes per register)
|
||||||
|
#define fcForceSingleCoil 5 //write single bit
|
||||||
|
#define fcPresetSingleRegister 6 //write analog output register (2 Bytes)
|
||||||
|
#define fcForceMultipleCoils 15 //write multiple bits
|
||||||
|
#define fcPresetMultipleRegisters 16 //write multiple analog output registers (2 Bytes each)
|
||||||
|
#define fcReportSlaveID 17 //read device description, run status and other device specific information
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Modbus Exception Codes
|
||||||
|
* Refer to modbus.org for further information.
|
||||||
|
* It's good practice to return exception code 01 in case you receive a function code
|
||||||
|
* that you haven't implemented in your application.
|
||||||
|
*/
|
||||||
|
#define ecIllegalFunction 1
|
||||||
|
#define ecIllegalDataAddress 2
|
||||||
|
#define ecIllegalDataValue 3
|
||||||
|
#define ecSlaveDeviceFailure 4
|
||||||
|
#define ecAcknowledge 5
|
||||||
|
#define ecSlaveDeviceBusy 6
|
||||||
|
#define ecNegativeAcknowledge 7
|
||||||
|
#define ecMemoryParityError 8
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Internal bit definitions
|
||||||
|
*/
|
||||||
|
#define BusTimedOut 0
|
||||||
|
#define Receiving 1
|
||||||
|
#define Transmitting 2
|
||||||
|
#define ReceiveCompleted 3
|
||||||
|
#define TransmitRequested 4
|
||||||
|
#define TimerActive 5
|
||||||
|
#define GapDetected 6
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Configures the UART. Call this function only once.
|
||||||
|
*/
|
||||||
|
extern void modbusInit(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief receive/transmit data array
|
||||||
|
*/
|
||||||
|
extern volatile unsigned char rxbuffer[MaxFrameIndex+1];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Current receive/transmit position
|
||||||
|
*/
|
||||||
|
extern volatile uint16_t DataPos;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This only applies to single address mode.
|
||||||
|
*/
|
||||||
|
#if ADDRESS_MODE == SINGLE_ADR
|
||||||
|
/**
|
||||||
|
* @brief: Read the device address
|
||||||
|
*/
|
||||||
|
extern uint8_t modbusGetAddress(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief: Set the device address
|
||||||
|
* Arguments: - newadr: the new device address
|
||||||
|
*/
|
||||||
|
extern void modbusSetAddress(unsigned char newadr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* @brief: Sends a response.
|
||||||
|
*
|
||||||
|
* Arguments: - packtop, index of the last byte in rxbuffer
|
||||||
|
* that contains payload. Maximum value is
|
||||||
|
* MaxFrameIndex-2.
|
||||||
|
*/
|
||||||
|
extern void modbusSendMessage(unsigned char packtop);
|
||||||
|
|
||||||
|
/* @brief: Sends a Modbus exception.
|
||||||
|
*
|
||||||
|
* Arguments: - exceptionCode
|
||||||
|
*/
|
||||||
|
extern void modbusSendException(unsigned char exceptionCode);
|
||||||
|
|
||||||
|
/* @brief: Discards the current transaction. For MULTIPLE_ADR-mode and general
|
||||||
|
* testing purposes. Call this function if you don't want to reply at all.
|
||||||
|
*/
|
||||||
|
void modbusReset(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Call this function whenever possible and check if its return value has the ReceiveCompleted Bit set.
|
||||||
|
* Preferably do this in the main while. I do not recommend calling this function within ISRs.
|
||||||
|
* @example if (modbusGetBusState() & (1<<ReceiveCompleted)) {
|
||||||
|
* modbusSendExcepton(ecIllegalFunction);
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
extern uint8_t modbusGetBusState(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Call every 100us using a timer ISR.
|
||||||
|
*/
|
||||||
|
extern void modbusTickTimer(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns amount of bits/registers requested.
|
||||||
|
*/
|
||||||
|
extern uint16_t modbusRequestedAmount(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the address of the first requested bit/register.
|
||||||
|
*/
|
||||||
|
extern uint16_t modbusRequestedAddress(void);
|
||||||
|
|
||||||
|
/* A fairly simple and hopefully Modbus compliant 16 Bit CRC algorithm.
|
||||||
|
* Returns 1 if the crc check is positive, returns 0 if it fails.
|
||||||
|
* Appends two crc bytes to the array.
|
||||||
|
*/
|
||||||
|
extern uint8_t crc16(volatile uint8_t *ptrToArray,uint8_t inputSize);
|
||||||
|
|
||||||
|
/* @brief: Handles single/multiple input/coil reading and single/multiple coil writing.
|
||||||
|
*
|
||||||
|
* Arguments: - ptrToInArray: pointer to the user's data array containing bits
|
||||||
|
* - startAddress: address of the first bit in the supplied array
|
||||||
|
* - size: input array size in the requested format (bits)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
extern uint8_t modbusExchangeBits(volatile uint8_t *ptrToInArray, uint16_t startAddress, uint16_t size);
|
||||||
|
|
||||||
|
/* @brief: Handles single/multiple register reading and single/multiple register writing.
|
||||||
|
*
|
||||||
|
* Arguments: - ptrToInArray: pointer to the user's data array containing registers
|
||||||
|
* - startAddress: address of the first register in the supplied array
|
||||||
|
* - size: input array size in the requested format (16bit-registers)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
extern uint8_t modbusExchangeRegisters(volatile uint16_t *ptrToInArray, uint16_t startAddress, uint16_t size);
|
||||||
|
|
||||||
|
/* @brief: returns 1 if data location adr is touched by current command
|
||||||
|
*
|
||||||
|
* Arguments: - adr: address of the data object
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
extern uint8_t modbusIsInRange(uint16_t adr);
|
||||||
|
|
||||||
|
/* @brief: returns 1 if range of data locations is touched by current command
|
||||||
|
*
|
||||||
|
* Arguments: - startAdr: address of first data object in range
|
||||||
|
* - lastAdr: address of last data object in range
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
extern uint8_t modbusIsRangeInRange(uint16_t startAdr, uint16_t lastAdr);
|
||||||
|
|
||||||
|
extern volatile uint16_t modbusDataAmount;
|
||||||
|
extern volatile uint16_t modbusDataLocation;
|
||||||
|
#endif
|
||||||
|
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
#include "modbus_master.h"
|
||||||
|
#include "modbus.h"
|
||||||
|
#include <util/delay.h>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
uint8_t wait_receive(uint8_t len, uint16_t dest[], uint8_t timeout){
|
||||||
|
|
||||||
|
uint8_t breaker = timeout;
|
||||||
|
while(!receiveOkay && breaker) { //wait for client response, time out after 1s
|
||||||
|
breaker--;
|
||||||
|
_delay_ms(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(receiveOkay) { //if this fails, there was either no response or a crc error
|
||||||
|
if(rxbuffer[1]&0x80) { //client responded with an error code
|
||||||
|
//handle the error
|
||||||
|
printf("error\n\r");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for(uint8_t x=0;x<len;x++) { //rxbuffer[2] should be 8 (4 registers => 8 bytes). You might want to check this at this point.
|
||||||
|
dest[x]=(rxbuffer[3+x*2]<<8)+rxbuffer[4+x*2]; //do sth with the acquired data.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void readReg(uint8_t slaveid, uint16_t address, uint8_t amount) {
|
||||||
|
_delay_ms(2);
|
||||||
|
rxbuffer[0]=slaveid;
|
||||||
|
modbusSetAddress(slaveid);
|
||||||
|
rxbuffer[1]=0x03;
|
||||||
|
rxbuffer[2]=(address>>8)&0xFF;
|
||||||
|
rxbuffer[3]=address&0xFF;
|
||||||
|
rxbuffer[4]=0x00;
|
||||||
|
rxbuffer[5]=amount;
|
||||||
|
modbusSendMessage(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeReg(uint8_t slaveid, uint16_t address, uint16_t value) {
|
||||||
|
_delay_ms(2);
|
||||||
|
rxbuffer[0]=slaveid;
|
||||||
|
modbusSetAddress(slaveid);
|
||||||
|
rxbuffer[1]=0x06;
|
||||||
|
rxbuffer[2]=(address>>8)&0xFF;
|
||||||
|
rxbuffer[3]=address&0xFF;
|
||||||
|
rxbuffer[4]=(value>>8)&0xFF;;
|
||||||
|
rxbuffer[5]=value&0xFF;
|
||||||
|
modbusSendMessage(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
#ifndef _MODBUS_MASTER_H_
|
||||||
|
#define _MODBUS_MASTER_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define receiveOkay (modbusGetBusState() & (1<<ReceiveCompleted))
|
||||||
|
|
||||||
|
uint8_t wait_receive(uint8_t len, uint16_t dest[], uint8_t timeout);
|
||||||
|
void readReg(uint8_t slaveid, uint16_t address, uint8_t amount);
|
||||||
|
void writeReg(uint8_t slaveid, uint16_t address, uint16_t value);
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue