From dce9b85a841dc05ed38f62da2924faebd587bfce Mon Sep 17 00:00:00 2001 From: Zakhar Panteleev Date: Sat, 13 Dec 2025 14:37:44 +0300 Subject: [PATCH] up --- .gitignore | 1 + modbus.c | 430 +++++++++++++++++++++++++++++++++++++ modbus.h | 38 ++++ modbus_constants_example.h | 28 +++ readme.txt | 10 + 5 files changed, 507 insertions(+) create mode 100644 .gitignore create mode 100644 modbus.c create mode 100644 modbus.h create mode 100644 modbus_constants_example.h create mode 100644 readme.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..34a5b4b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +modbus_constants.h \ No newline at end of file diff --git a/modbus.c b/modbus.c new file mode 100644 index 0000000..1e0a877 --- /dev/null +++ b/modbus.c @@ -0,0 +1,430 @@ +#include "modbus.h" +#define TX_EN GPIO_WriteBit(RS485_GPIO, RS485_GPIO_Pin, Bit_SET); +#define TX_DIS GPIO_WriteBit(RS485_GPIO, RS485_GPIO_Pin, Bit_RESET); +#define MODBUS_LED_ON GPIO_WriteBit(Modbus_LED, Modbus_LED_Pin, Bit_SET); +#define MODBUS_LED_OFF GPIO_WriteBit(Modbus_LED, Modbus_LED_Pin, Bit_RESET); + +static uint8_t buf_ptr = 0; +static unsigned char rx_buf_ptr = 0; +static unsigned char rx_buf[128]; +static unsigned char tx_buf[128]; + +static uint16_t addr_buf_1 = 0, addr_buf_2 = 0; +static uint16_t regs_to_read = 0; +static uint16_t CRC16 = 0; +static unsigned char crc_buf[300]; +static unsigned char modbus_id = 0; +static uint16_t reg_wr_data = 0; +static uint16_t modbus_reg_addr; + + +static uint16_t holding_registers[MAX_HOLDING_REGISTERS]; +static uint16_t input_registers[MAX_INPUT_REGISTERS]; + +static unsigned char rx_flag = 0; +static unsigned char timer_state = 0; + + +static uint16_t dev_id = DEFAULT_DEVICE_MODBUS_ID; +const uint16_t com_dev_id = 247; + +static void (*ModbusUartSendByte)(USART_TypeDef*, unsigned char); +static int32_t (*ModbusTcpSendByte)(uint8_t, unsigned char*, uint16_t); +static void (*ModbusRIRPoll)(void); +static void (*ModbusRHRPoll)(void); +static void (*ModbusWSRPoll)(uint16_t, uint16_t); + +void modbus_connect_callback_send_byte(void (*s)(USART_TypeDef*, unsigned char)) { + ModbusUartSendByte = s; +} + +void modbus_connect_callback_send_buf_tcp(int32_t (*s)(uint8_t, unsigned char*, uint16_t)) { + ModbusTcpSendByte = s; +} + +void modbus_connect_callback_rir_poll(void (*s)(void)) { + ModbusRIRPoll = s; +} + +void modbus_connect_callback_rhr_poll(void (*s)(void)) { + ModbusRHRPoll = s; +} + +void modbus_connect_callback_wsr_poll(void (*s)(uint16_t, uint16_t)) { + ModbusWSRPoll = s; +} + +static uint16_t modbus_CRC16(unsigned char buf[], uint16_t len) +{ + /// ������ crc16 + + uint16_t crc = 0xFFFF; + //U8 crc_lsb, crc_msb; + for (uint16_t pos = 0; pos < len; pos++) { + crc ^= (uint16_t)buf[pos]; // XOR byte into least sig. byte of crc + for (int i = 8; i != 0; i--) { // Loop over each bit + if ((crc & 0x0001) != 0) { // If the LSB is set + crc >>= 1; // Shift right and XOR 0xA001 + crc ^= 0xA001; + } + else // Else LSB is not set + crc >>= 1; // Just shift right + } + } + + // Note, this number has low and high bytes swapped, + // so use it accordingly (or swap bytes) + // swapping bytes + crc = ((crc<<8)&0xff00)|((crc>>8)&0x00ff); + + return crc; +} + +void modbus_wsr_answer() { + crc_buf[0] = (modbus_id == dev_id) ? dev_id : com_dev_id; + crc_buf[1] = (unsigned char) MODBUS_WSR_CMD; + crc_buf[2] = (unsigned char) (modbus_reg_addr >> 8); + crc_buf[3] = (unsigned char) (modbus_reg_addr & 0xff); + crc_buf[4] = (unsigned char) (reg_wr_data >> 8); + crc_buf[5] = (unsigned char) (reg_wr_data & 0xff); + + CRC16 = modbus_CRC16(crc_buf, 6); + + switch(CURRENT_OPERATION_MODE) { + case 0: + TX_EN; + ModbusUartSendByte(Modbus_UART, (modbus_id == dev_id) ? dev_id : com_dev_id); + ModbusUartSendByte(Modbus_UART, (unsigned char)MODBUS_WSR_CMD); + ModbusUartSendByte(Modbus_UART, modbus_reg_addr >> 8); + ModbusUartSendByte(Modbus_UART, modbus_reg_addr & 0xff); + ModbusUartSendByte(Modbus_UART, reg_wr_data >> 8); + ModbusUartSendByte(Modbus_UART, reg_wr_data & 0xff); + + ModbusUartSendByte(Modbus_UART, CRC16 >> 8); + ModbusUartSendByte(Modbus_UART, CRC16 & 0xff); + TX_DIS; + break; + case 1: + tx_buf[0] = (modbus_id == dev_id) ? dev_id : com_dev_id; + tx_buf[1] = MODBUS_WSR_CMD; + tx_buf[2] = modbus_reg_addr >> 8; + tx_buf[3] = modbus_reg_addr & 0xff; + tx_buf[4] = reg_wr_data >> 8; + tx_buf[5] = reg_wr_data & 0xff; + tx_buf[6] = CRC16 >> 8; + tx_buf[7] = CRC16 & 0xff; + + ModbusTcpSendByte(DEFAULT_TCP_MODBUS_SOCKET, tx_buf, 8); + break; + } +} + +void modbus_rir_answer() { + buf_ptr = 0; + addr_buf_1 = modbus_reg_addr; + addr_buf_2 = addr_buf_1; + + crc_buf[0] = (modbus_id == dev_id) ? dev_id : com_dev_id; + crc_buf[1] = MODBUS_RIR_CMD; + crc_buf[2] = regs_to_read * 2; + + unsigned char cnt = CRC_COUNT_OFFSET; + + for (int i = 0; i < regs_to_read; i++) { + crc_buf[cnt++] = (unsigned char)(input_registers[addr_buf_1] >> 8); + crc_buf[cnt++] = (unsigned char)(input_registers[addr_buf_1] & 0xff); + ++addr_buf_1; + } + + CRC16 = modbus_CRC16(crc_buf, regs_to_read * 2 + CRC_COUNT_OFFSET); + + switch(CURRENT_OPERATION_MODE) { + case 0: + TX_EN; + ModbusUartSendByte(Modbus_UART, (modbus_id == dev_id) ? dev_id : com_dev_id); + ModbusUartSendByte(Modbus_UART, (unsigned char) MODBUS_RIR_CMD); + ModbusUartSendByte(Modbus_UART, regs_to_read * 2); + + for (int i = 0; i < regs_to_read; i++) { + ModbusUartSendByte(Modbus_UART, (unsigned char)(input_registers[addr_buf_2] >> 8)); + ModbusUartSendByte(Modbus_UART, (unsigned char)(input_registers[addr_buf_2] & 0xff)); + ++addr_buf_2; + } + ModbusUartSendByte(Modbus_UART, (unsigned char)(CRC16 >> 8)); + ModbusUartSendByte(Modbus_UART, (unsigned char)(CRC16 & 0xff)); + TX_DIS; + break; + case 1: + tx_buf[buf_ptr++] = (modbus_id == dev_id) ? dev_id : com_dev_id; + tx_buf[buf_ptr++] = (unsigned char) MODBUS_RIR_CMD; + tx_buf[buf_ptr++] = regs_to_read * 2; + + for (int i = 0; i < regs_to_read; i++) { + tx_buf[buf_ptr++] = (unsigned char)(input_registers[addr_buf_2] >> 8); + tx_buf[buf_ptr++] = (unsigned char)(input_registers[addr_buf_2] & 0xff); + ++addr_buf_2; + } + tx_buf[buf_ptr++] = CRC16 >> 8; + tx_buf[buf_ptr++] = CRC16 & 0xff; + ModbusTcpSendByte(DEFAULT_TCP_MODBUS_SOCKET, tx_buf, buf_ptr); + break; + } + +} + +void modbus_rhr_answer() { + buf_ptr = 0; + addr_buf_1 = modbus_reg_addr; + addr_buf_2 = addr_buf_1; + + crc_buf[0] = (modbus_id == dev_id) ? dev_id : com_dev_id; + crc_buf[1] = MODBUS_RHR_CMD; + crc_buf[2] = regs_to_read * 2; + + unsigned char cnt = CRC_COUNT_OFFSET; + + for (int i = 0; i < regs_to_read; i++) { + crc_buf[cnt++] = (unsigned char)(holding_registers[addr_buf_1] >> 8); + crc_buf[cnt++] = (unsigned char)(holding_registers[addr_buf_1] & 0xff); + ++addr_buf_1; + } + + CRC16 = modbus_CRC16(crc_buf, regs_to_read * 2 + CRC_COUNT_OFFSET); + + switch(CURRENT_OPERATION_MODE) { + case 0: + TX_EN; + ModbusUartSendByte(Modbus_UART, (modbus_id == dev_id) ? dev_id : com_dev_id); + ModbusUartSendByte(Modbus_UART, (unsigned char) MODBUS_RHR_CMD); + ModbusUartSendByte(Modbus_UART, regs_to_read * 2); + + for (int i = 0; i < regs_to_read; i++) { + ModbusUartSendByte(Modbus_UART, (unsigned char)(holding_registers[addr_buf_2] >> 8)); + ModbusUartSendByte(Modbus_UART, (unsigned char)(holding_registers[addr_buf_2] & 0xff)); + ++addr_buf_2; + } + ModbusUartSendByte(Modbus_UART, (unsigned char)(CRC16 >> 8)); + ModbusUartSendByte(Modbus_UART, (unsigned char)(CRC16 & 0xff)); + TX_DIS; + break; + case 1: + tx_buf[buf_ptr++] = (modbus_id == dev_id) ? dev_id : com_dev_id; + tx_buf[buf_ptr++] = (unsigned char) MODBUS_RHR_CMD; + tx_buf[buf_ptr++] = regs_to_read * 2; + + for (int i = 0; i < regs_to_read; i++) { + tx_buf[buf_ptr++] = (unsigned char)(holding_registers[addr_buf_2] >> 8); + tx_buf[buf_ptr++] = (unsigned char)(holding_registers[addr_buf_2] & 0xff); + ++addr_buf_2; + } + tx_buf[buf_ptr++] = CRC16 >> 8; + tx_buf[buf_ptr++] = CRC16 & 0xff; + ModbusTcpSendByte(DEFAULT_TCP_MODBUS_SOCKET, tx_buf, buf_ptr); + + break; + } +} + +static unsigned char modbus_rx_CRC_check(unsigned char modbus_cmd) { + /// ���������� ������� CRC ��� �������� � ��������� � ����������� + uint16_t CRC16_calc = 0; // ���������� ����������� ����� + unsigned char ans = 0; + + modbus_reg_addr = (uint16_t)((rx_buf[2] << 8) | rx_buf[3]); // get starting reg addr + + crc_buf[0] = (modbus_id == dev_id) ? dev_id : com_dev_id; + crc_buf[2] = (unsigned char)(modbus_reg_addr >> 8); + crc_buf[3] = (unsigned char)(modbus_reg_addr & 0x00ff); + + + switch(modbus_cmd) { + case MODBUS_WSR_CMD: + reg_wr_data = (rx_buf[4] << 8) | rx_buf[5]; // get data to write into reg + + crc_buf[1] = (unsigned char) MODBUS_WSR_CMD; + crc_buf[4] = (unsigned char)(reg_wr_data >> 8); + crc_buf[5] = (unsigned char)(reg_wr_data & 0x00ff); + break; + //---- + case MODBUS_RHR_CMD: + regs_to_read = (rx_buf[4] << 8) | rx_buf[5]; // get number of regs to read + + crc_buf[1] = (unsigned char) MODBUS_RHR_CMD; + crc_buf[4] = (unsigned char)(regs_to_read >> 8); + crc_buf[5] = (unsigned char)(regs_to_read & 0x00ff); + break; + //---- + case MODBUS_RIR_CMD: + regs_to_read = (rx_buf[4] << 8) | rx_buf[5]; // get number of regs to read + + crc_buf[1] = (unsigned char) MODBUS_RIR_CMD; + crc_buf[4] = (unsigned char)(regs_to_read >> 8); + crc_buf[5] = (unsigned char)(regs_to_read & 0x00ff); + break; + //---- + default: break; + } + + CRC16 = (rx_buf[6] << 8) | rx_buf[7]; // get CRC16 from rx msg + CRC16_calc = modbus_CRC16(crc_buf,6); // calc CRC16 + + if(CRC16_calc == CRC16) + ans = modbus_cmd; + + return ans; +} + +unsigned char modbus_get_poll(void) +{ + /// update modbus regs and vars, send answer to master + unsigned char rx_cmd_code = 0; // ??? ??????????? ???????rx_cmd_code = 0; + // state 1 and 2, transmit end, rx buf has > 7 bytes ? + // + if ( rx_flag == 1 ) { + if( rx_buf_ptr > 7 ) { + modbus_id = rx_buf[0]; // get device ID from master msg + + if((modbus_id == dev_id) || (modbus_id == com_dev_id)) { + switch(rx_buf[1]) { + case MODBUS_RHR_CMD: // ???? ??????? - ?????? R/W ????????? + if(modbus_rx_CRC_check(MODBUS_RHR_CMD) == MODBUS_RHR_CMD) { + rx_cmd_code = MODBUS_RHR_CMD; + } + break; + ////------------------------------------------------------------------- + case MODBUS_WSR_CMD: // ???? ??????? - ?????? Read-only ????????? + if(modbus_rx_CRC_check(MODBUS_WSR_CMD) == MODBUS_WSR_CMD) { + rx_cmd_code = MODBUS_WSR_CMD; + holding_reg_write(modbus_reg_addr, reg_wr_data); // !!!! + } + break; + ////------------------------------------------------------------------- + case MODBUS_RIR_CMD: + if(modbus_rx_CRC_check(MODBUS_RIR_CMD) == MODBUS_RIR_CMD) { + rx_cmd_code = MODBUS_RIR_CMD; + } + break; + } // switch(rx_buf[1]) + modbus_reset(); + } else { + modbus_reset(); + }// if dev_id + } else { modbus_reset(); } // if(rx_buf_pos > 7) + } + + return rx_cmd_code; +} + +void modbus_reset(void) +{ + for(int i = 0; i < 128; i++) + rx_buf[i] = 0; + + rx_buf_ptr = 0; + rx_flag = 0; +} + +void holding_reg_write(uint16_t red_addr, uint16_t value) { + holding_registers[red_addr] = value; +} + +void holding_reg_read(uint16_t red_addr, uint16_t *usr_var_ptr) { + *usr_var_ptr = holding_registers[red_addr]; +} + +void input_reg_read(uint16_t red_addr, uint16_t *usr_var_ptr) { + *usr_var_ptr = input_registers[red_addr]; +} + +void input_reg_write(uint16_t reg_addr, uint16_t value) { + input_registers[reg_addr] = value; +} + +uint16_t get_wr_reg_addr(void) { + return modbus_reg_addr; +} + +uint16_t get_wr_reg_val(void) { + return reg_wr_data; +} + +unsigned char get_modbus_id(void) { + return dev_id; +} + +void set_modbus_id(unsigned char newID) +{ + dev_id = newID; + modbus_reset(); +} + +static uint16_t RegisterAddr = 0; +static uint16_t RegisterValue = 0; + +void modbus_poll(void) { + uint8_t mb_get_poll = modbus_get_poll(); + switch(mb_get_poll) { + //////////////////////////// READING INPUTS ////////////////////////// + case MODBUS_RIR_CMD: + MODBUS_LED_ON; + ModbusRIRPoll(); + modbus_rir_answer(); + MODBUS_LED_OFF; + break; + //////////////////////////// READING HOLDING ////////////////////////// + case MODBUS_RHR_CMD: + MODBUS_LED_ON; + ModbusRHRPoll(); + modbus_rhr_answer(); + MODBUS_LED_OFF; + break; + //////////////////////////// WRITING HOLDING ////////////////////////// + case MODBUS_WSR_CMD: + MODBUS_LED_ON; + modbus_wsr_answer(); + + RegisterAddr = get_wr_reg_addr(); // get address + RegisterValue = get_wr_reg_val(); // get the new value + + ModbusWSRPoll(RegisterAddr, RegisterValue); + MODBUS_LED_OFF; // LED toggle + break; + } // switch +} +//======================= UART HANDLERS =========================// +void Modbus_UART_IRQHandler(void) { + if (USART_GetITStatus(Modbus_UART, USART_IT_RXNE) != RESET) { + while(USART_GetFlagStatus(Modbus_UART,USART_FLAG_RXNE) == RESET) {} + if (rx_buf_ptr < 128) { + rx_buf[rx_buf_ptr++] = (unsigned char)Modbus_UART -> DR; + } else { + Modbus_UART -> DR; + } + USART_ClearITPendingBit(Modbus_UART, USART_IT_RXNE); + + if (timer_state == 0) { + timer_state = 1; + Modbus_TIM -> CNT = 0; + TIM_Cmd(Modbus_TIM, ENABLE); + } + } +} + +void Modbus_TIM_IRQHandler(void) { + if (TIM_GetITStatus(Modbus_TIM, TIM_IT_Update) != RESET) + TIM_ClearITPendingBit(Modbus_TIM, TIM_IT_Update); + + TIM_Cmd(Modbus_TIM, DISABLE); + timer_state = 0; + rx_flag = 1; +} + + +//======================== TCP HANDLERS ========================// +void Modbus_TCP_Handler(unsigned char* buf, uint8_t size) { + for (rx_buf_ptr = 0; rx_buf_ptr < size; rx_buf_ptr++) { + rx_buf[rx_buf_ptr] = buf[rx_buf_ptr]; + } + + rx_flag = 1; +} diff --git a/modbus.h b/modbus.h new file mode 100644 index 0000000..faac8bd --- /dev/null +++ b/modbus.h @@ -0,0 +1,38 @@ +#ifndef _MODBUS_H +#define _MODBUS_H + +// Includes +#include "modbus_constants.h" +#include "stdlib.h" + +void Modbus_TCP_Handler(unsigned char*, uint8_t); +static unsigned char modbus_rx_buf[128]; + +#define CRC_COUNT_OFFSET 3 + +#define MODBUS_RHR_CMD (unsigned char)0x03 +#define MODBUS_RIR_CMD (unsigned char)0x04 +#define MODBUS_WSR_CMD (unsigned char)0x06 + +void modbus_wsr_answer(void); +void modbus_rhr_answer(void); +unsigned char modbus_get_poll(void); +void holding_reg_write(uint16_t, uint16_t); +void holding_reg_read(uint16_t, uint16_t*); +void input_reg_write(uint16_t, uint16_t); +void input_reg_read(uint16_t, uint16_t*); +uint16_t get_wr_reg_addr(void); +uint16_t get_wr_reg_val(void); +unsigned char get_modbus_id(void); +void set_modbus_id(unsigned char); + + +void modbus_connect_callback_send_byte(void (*s)(USART_TypeDef*, unsigned char)); +void modbus_connect_callback_send_buf_tcp(int32_t (*s)(uint8_t, unsigned char*, uint16_t)); +void modbus_connect_callback_rir_poll(void (*s)(void)); +void modbus_connect_callback_rhr_poll(void (*s)(void)); +void modbus_connect_callback_wsr_poll(void (*s)(uint16_t, uint16_t)); +void modbus_poll(void); +void modbus_reset(void); + +#endif diff --git a/modbus_constants_example.h b/modbus_constants_example.h new file mode 100644 index 0000000..cc78b44 --- /dev/null +++ b/modbus_constants_example.h @@ -0,0 +1,28 @@ +#ifndef _MODBUS_CONSTANTS_H +#define _MODBUS_CONSTANTS_H + +#define MAX_HOLDING_REGISTERS 300 +#define MAX_INPUT_REGISTERS 300 + +// MCU Includes + +#include "stm32f4xx.h" +#include "stm32f4xx_usart.h" + + +// Needed Constants + +#define DEFAULT_DEVICE_MODBUS_ID 1 +#define DEFAULT_TCP_MODBUS_SOCKET 0 +#define CURRENT_OPERATION_MODE 1 // UART - 0, TCP - 1 + +#define RS485_GPIO GPIOB +#define RS485_GPIO_Pin GPIO_Pin_6 +#define Modbus_UART USART2 +#define Modbus_TIM TIM2 +#define Modbus_UART_IRQHandler USART2_IRQHandler +#define Modbus_TIM_IRQHandler TIM2_IRQHandler +#define Modbus_LED GPIOD +#define Modbus_LED_Pin GPIO_Pin_2 + +#endif diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..bce5e35 --- /dev/null +++ b/readme.txt @@ -0,0 +1,10 @@ +Modbus Library for slave devices based on STM32 MCU (v5) +Supports Modbus RTU and Modbus RTU over TCP modes. + +Original library was written by @ponkin_dmitry + +Single header library which allow you to connect your devices using Modbus protocol. +Main configuration takes place in `modbus_constants.h` file. + +Buffer sizes needs to be set via definitions `MAX_HOLDING_REGISTERS` and `MAX_INPUT_REGISTERS`. +Create your `modbus_constants.h` header file from example which is in the current repo.