summaryrefslogtreecommitdiff
path: root/src/modbus.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/modbus.c')
-rw-r--r--src/modbus.c163
1 files changed, 163 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;
+}
+