RT-Thread 应用笔记 - 不正确使用LOG也会引发hard fault
RT-Thread 应用笔记 - RTC Alarm 组件的使用
RT-Thread 应用笔记 - freemodbus RTU RS485 从机
RT-Thread 应用笔记 - freemodbus RTU RS485 主机
RT-Thread 应用笔记 - libmodbus RTU RS485 从机
RT-Thread 应用笔记 - libmodbus RTU RS485 主机
RT-Thread 应用笔记 - STM32 CAN 通信双机
背景
- modbus协议,简单,实用,一些小数据通信控制场合,用的比较多。
- 部分用户使用起来,并不是想象中那么的顺利。
- libmodbus,可用于modbus主从机的开发。
- libmodbus代码基于C库,有动态内存的使用,与freemodbus相比,在适配时稍有不同。
- 基于rt-thread,已经有libmodbus的软件包,默认是主机。
- 这里基于STM32F103 实现modbus RS485 RTU从机的功能。
- 部分使用libmodbus的用户,配置好libmodbus,发现无法通信,就开始【求助】,可能需要了解引脚配置!
前言
- 嵌入式软件工程师,需要了解原理图,了解引脚定义,如串口、RS485控制引脚等等。
- 我的板子,MCU 为STM32F103C8T6, Flash与SRAM都相对不大。
- UART1用于rt-thread MSH串口。
- UART3用于RS485,用于modbus rtu串口。
移植
- 首先需要配置好rt-thread最小系统,在rt-thread源码工程里,找一个与使用MCU平台相似的BSP,修改使用。
- 下载配置libmodbus软件包,使用ENV menuconfig
- 配置完成后,需要ENV pkg --update,更新下载libmodbus。
- ENV scons --target=mdk5生成Keil MDK5的工程。
- 编译,STM32F103C8T6,发现Flash不够了,需要更改编译选项: -O2.
- 需要更改libmodbus的demo,默认为主机,这里,我简单修改了一下,实现从机功能
#include <rtthread.h>
#include <board.h>
#include "modbus.h"
#define RS485_RE GET_PIN(A, 8) //RS485控制引脚
static void mb_slave_thread(void *param)
{
modbus_t *ctx = RT_NULL;
int rc = 0;
modbus_mapping_t *mb_mapping = NULL; /* 线圈与寄存器 */
uint8_t mb_reply[MODBUS_TCP_MAX_ADU_LENGTH];
ctx = modbus_new_rtu("/dev/uart3", 115200, 'N', 8, 1); /* uart3 */
modbus_rtu_set_serial_mode(ctx, MODBUS_RTU_RS485);
modbus_rtu_set_rts(ctx, RS485_RE, MODBUS_RTU_RTS_UP);
modbus_set_slave(ctx, 3); /* 设置从机地址 */
mb_mapping = modbus_mapping_new(16, 16, 10, 10);
if (mb_mapping == RT_NULL)
{
rt_kprintf("modbus_mapping_new failed! \n");
modbus_free(ctx);
return;
}
mb_mapping->tab_registers[0] = 'R';
mb_mapping->tab_registers[1] = 'T';
mb_mapping->tab_registers[2] = '-';
mb_mapping->tab_registers[3] = 'T';
mb_mapping->tab_registers[4] = 'h';
mb_mapping->tab_registers[5] = 'r';
mb_mapping->tab_registers[6] = 'e';
mb_mapping->tab_registers[7] = 'a';
mb_mapping->tab_registers[8] = 'd';
modbus_connect(ctx);
while (1)
{
//modbus loop
rc = modbus_receive(ctx, mb_reply);
if (rc > 0)
{
modbus_reply(ctx, mb_reply, rc, mb_mapping);
}
else if (rc == -1)
{
/* Connection closed by the client or error */
break;
}
}
modbus_close(ctx);
modbus_free(ctx);
}
static int rtu_test_init(void)
{
rt_pin_mode(RS485_RE, PIN_MODE_OUTPUT);
rt_thread_t tid;
tid = rt_thread_create("mb_test",
mb_slave_thread, RT_NULL,
2048,
12, 10);
if (tid != RT_NULL)
rt_thread_startup(tid);
return RT_EOK;
}
INIT_APP_EXPORT(rtu_test_init);
- 注意RS485 引脚的配置:串口引脚,这里是uart3,RS485控制引脚,这里是PA8。
- uart3的串口引脚配置,rt-thread使用uart3设备,并没有UART3具体引脚的配置。需要手动更改xxx_msp.c文件!
这里修改:stm32f1xx_hal_msp.c
void HAL_UART_MspInit(UART_HandleTypeDef* huart)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(huart->Instance==USART1)
{
/* USER CODE BEGIN USART1_MspInit 0 */
/* USER CODE END USART1_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_USART1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**USART1 GPIO Configuration
PA9 ------> USART1_TX
PA10 ------> USART1_RX
*/
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USER CODE BEGIN USART1_MspInit 1 */
/* USER CODE END USART1_MspInit 1 */
}
else if (huart->Instance==USART3)
{
__HAL_RCC_USART3_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/**USART3 GPIO Configuration
PB10 ------> USART3_TX
PB11 ------> USART3_RX
*/
GPIO_InitStruct.Pin = GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_11;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
}
- 如果串口不通,首先确认下串口引脚配置,RS485的控制引脚
- libmodbus,默认打开了dfs,所以,ROM空间占用较大,使用动态内存,使用 stdlib string等C库。
- 可以打开Keil MDK的微库 Use microLib支持。
调试
- 因为配置的为从机,需要RS485主机进行调试
- 这里使用USB转RS485,接入电脑,打开modbus poll,开启主机功能。
- 从机配置的是保持寄存器的读取
- 通信成功的样子。
总结
- 参考demo
https://gitee.com/zhangsz0516/rtt_stm32_libmodbus
- 如果调试不通,需要检查引脚配置、波特率设置、modbus主机设置,可以使用逻辑分析仪,查看串口的数据。
- libmodbus,主机功能挺全面,可以用于modbus 主从一体协议的实现,
- libmodbus,好像没有从机地址的判断过滤接口,需要实现时,自己注意,否则,收到主机的命令,从机都会响应。
- 总线上有多个从机时,每个从机接收数据包后,判断是自己的从机地址,才能响应。