summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAditya Naik2020-02-12 13:02:53 -0500
committerAditya Naik2020-02-12 13:02:53 -0500
commitdaed2486999c88bf048e3f6fa2ce255d4a27c29a (patch)
treea964a06d4b3b7f4fa9a7ff7dc4f8f9254cd3f72d
Initial with non-portable ST HAL source
-rw-r--r--src/modbus.c163
-rw-r--r--src/modbus.h48
2 files changed, 211 insertions, 0 deletions
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)<br>
+ 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