From daed2486999c88bf048e3f6fa2ce255d4a27c29a Mon Sep 17 00:00:00 2001 From: Aditya Naik Date: Wed, 12 Feb 2020 13:02:53 -0500 Subject: Initial with non-portable ST HAL source --- src/modbus.c | 163 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/modbus.h | 48 ++++++++++++++++++ 2 files changed, 211 insertions(+) create mode 100644 src/modbus.c create mode 100644 src/modbus.h diff --git a/src/modbus.c b/src/modbus.c new file mode 100644 index 0000000..98f40bd --- /dev/null +++ b/src/modbus.c @@ -0,0 +1,163 @@ + +#include "modbus.h" + +/** @ingroup util_crc16 + Processor-independent CRC-16 calculation. + + Polynomial: x^16 + x^15 + x^2 + 1 (0xA001)
+ Initial value: 0xFFFF + + This CRC is normally used in disk-drive controllers. + + @param uint16_t crc (0x0000..0xFFFF) + @param uint8_t a (0x00..0xFF) + @return calculated CRC (0x0000..0xFFFF) +*/ +uint16_t crc16_update(uint16_t crc, uint8_t a) +{ + int i; + crc ^= a; + for (i = 0; i < 8; ++i) { + if (crc & 1) + crc = (crc >> 1) ^ 0xA001; + else + crc = (crc >> 1); + } + + return crc; +} + +void ClearResponseBuffer(modbus_slave_device *device) +{ + memset(device->response_buffer, 0, sizeof(device->response_buffer)); +} + +MB_StatusTypeDef ReadHoldingRegisters(modbus_slave_device *device, uint16_t reg_addr, int read_num) +{ + return modbus_transaction(read_holding_regs, device, reg_addr, read_num, 0); +} + +MB_StatusTypeDef ReadInputRegisters(modbus_slave_device *device, uint16_t reg_addr, int read_num) +{ + return modbus_transaction(read_input_regs, device, reg_addr, read_num, 0); +} + +MB_StatusTypeDef WriteSingleRegister(modbus_slave_device *device, uint16_t reg_addr, uint16_t write_val) +{ + return modbus_transaction(write_single_reg, device, reg_addr, 1, write_val); +} + +MB_StatusTypeDef modbus_transaction(uint8_t func, modbus_slave_device *device, + uint16_t rw_addr, int read_num, uint16_t write_value) +{ + uint8_t adu[256], adu_currsize=0; + MB_StatusTypeDef MB_status = MB_OK; + HAL_StatusTypeDef UART_status = HAL_OK; + + /* Add the values that are relevant to all (supported) functions to the buffer */ + adu[adu_currsize++] = device->slave_id; + adu[adu_currsize++] = func; + adu[adu_currsize++] = highByte(rw_addr); + adu[adu_currsize++] = lowByte(rw_addr); + + /* Put data in the buffer based on function code */ + switch (func) { + case read_holding_regs: + case read_input_regs: + adu[adu_currsize++] = highByte(read_num); + adu[adu_currsize++] = lowByte(read_num); + break; + case write_single_reg: + adu[adu_currsize++] = highByte(write_value); + adu[adu_currsize++] = lowByte(write_value); + } + + + /* Calculate and append the CRC16 to the buffer */ + uint16_t crc = 0xffff; + for (int i=0; i < adu_currsize; i++) { + crc = crc16_update(crc, adu[i]); + } + + adu[adu_currsize++] = lowByte(crc); + adu[adu_currsize++] = highByte(crc); + adu[adu_currsize] = 0; + + /* Send data using HAL UART */ + for (int i=0; i < adu_currsize; i++) { + UART_status = HAL_UART_Transmit(&device->modbus_uart, &adu[i], 1, MODBUS_TIMEOUT); + } + + + /* Reset the ADU currsize pointer in preperation of data RX */ + adu_currsize = 0; + + int rx_curr_bytesize = 8; + while (rx_curr_bytesize > 0 && MB_status == MB_OK && UART_status == HAL_OK) { + UART_status = HAL_UART_Receive(&device->modbus_uart, &adu[adu_currsize++], + 1, MODBUS_TIMEOUT); + if (UART_status == HAL_OK) { + rx_curr_bytesize--; + if (adu_currsize == 5) { + if (adu[0] != device->slave_id) { + /* Return invalid slave ID error */ + MB_status = MB_SLAVE_ID_ERR; + break; + } + if ((adu[1] & 0x7f) != func) { + /* Return invalid function error */ + MB_status = MB_FUNC_ERR; + break; + } + + /* Need to read n more bytes based on the function code */ + switch (adu[1]) { + case read_holding_regs: + case read_input_regs: + rx_curr_bytesize = adu[2]; + break; + case write_single_reg: + rx_curr_bytesize = 3; + break; + } + } + } + else { + MB_status = UART_status + 3; + } + } + + if (adu_currsize > 5 && MB_status == MB_OK && UART_status == HAL_OK) { + crc = 0xffff; + for (int i=0; i < adu_currsize - 2; i++) { + crc = crc16_update(crc, adu[i]); + } + if((lowByte(crc) != adu[adu_currsize - 2] || + highByte(crc) != adu[adu_currsize - 1])) { + /* Invalid CRC error */ + MB_status = MB_CRC_ERR; + } + } + + /* For read operations: disassemble ADU into words */ + if (MB_status == MB_OK && UART_status == HAL_OK) { + switch (adu[1]) { + case read_input_regs: + case read_holding_regs: + for (int i=0; i < (adu[2] >> 1); i++) { + if (i < MAX_BUFSIZE) { + device->response_buffer[i] = word(adu[2*i+3], adu[2*i+4]); + } + device->rbuf_len = i; + } + break; + } + } + + if (!MB_status && UART_status) { + MB_status = UART_status + 3; + } + + return MB_status; +} + diff --git a/src/modbus.h b/src/modbus.h new file mode 100644 index 0000000..32ca596 --- /dev/null +++ b/src/modbus.h @@ -0,0 +1,48 @@ +#ifndef _MODBUS_H +#define _MODBUS_H + +#include "stm32f1xx_hal.h" +#include "string.h" + +#define read_holding_regs 0x03 +#define read_input_regs 0x04 +#define write_single_reg 0x06 + +#define lowByte(w) ((uint8_t) ((w) & 0xff)) +#define highByte(w) ((uint8_t) ((w) >> 8)) +#define word(h, l) ((h << 8) | l) + +#define MODBUS_TIMEOUT 50 +#define MAX_BUFSIZE 64 + +typedef struct { + uint8_t slave_id; + uint16_t response_buffer[MAX_BUFSIZE]; + uint8_t rbuf_len; + UART_HandleTypeDef modbus_uart; +} modbus_slave_device; + +typedef enum { + MB_OK = 0x0, + MB_SLAVE_ID_ERR = 0x1, + MB_FUNC_ERR = 0x2, + MB_CRC_ERR = 0x3, + MB_UART_ERR = 0x4, + MB_BUSY_ERR = 0x5, + MB_TIMEOUT_ERR = 0x6 +} MB_StatusTypeDef; + + +uint16_t crc16_update(uint16_t crc, uint8_t a); +MB_StatusTypeDef modbus_transaction (uint8_t func, modbus_slave_device *device, + uint16_t rw_addr, int read_num, + uint16_t write_value); +MB_StatusTypeDef ReadHoldingRegisters(modbus_slave_device *device, uint16_t reg_addr, + int read_num); +MB_StatusTypeDef ReadInputRegisters(modbus_slave_device *device, uint16_t reg_addr, + int read_num); +MB_StatusTypeDef WriteSingleRegister(modbus_slave_device *device, uint16_t reg_addr, + uint16_t write_val); +void ClearResponseBuffer(modbus_slave_device *device); + +#endif -- cgit v1.2.3