背景
- modbus有主机、从机之分
- libmodbus与freemodbus相比,是主从机一体的,freemodbus,官方的为从机。
- libmodbus感觉更适合rt-thread,因为,有了内存的管理,虽然软件包移植的没有那么细。
移植
- MCU 为 STM32F107VCT6,RS485 UART4,
- modbus采用RS485 RTU,MSH shell UART5。
- 先移植好RT-Thread最小系统
- 使用STM32CubeMX,生成UART4 UART5的引脚与时钟初始化代码,更新:stm32f1xx_hal_msp.c
/**
* @brief UART MSP Initialization
* This function configures the hardware resources used in this example
* @param huart: UART handle pointer
* @retval None
*/
void HAL_UART_MspInit(UART_HandleTypeDef* huart)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(huart->Instance==UART4)
{
/* USER CODE BEGIN UART4_MspInit 0 */
/* USER CODE END UART4_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_UART4_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
/**UART4 GPIO Configuration
PC10 ------> UART4_TX
PC11 ------> UART4_RX
*/
GPIO_InitStruct.Pin = GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_11;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/* USER CODE BEGIN UART4_MspInit 1 */
/* USER CODE END UART4_MspInit 1 */
}
else if(huart->Instance==UART5)
{
/* USER CODE BEGIN UART5_MspInit 0 */
/* USER CODE END UART5_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_UART5_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
/**UART5 GPIO Configuration
PC12 ------> UART5_TX
PD2 ------> UART5_RX
*/
GPIO_InitStruct.Pin = GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
/* USER CODE BEGIN UART5_MspInit 1 */
/* USER CODE END UART5_MspInit 1 */
}
else if(huart->Instance==USART2)
{
/* USER CODE BEGIN USART2_MspInit 0 */
/* USER CODE END USART2_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_USART2_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
/**USART2 GPIO Configuration
PD5 ------> USART2_TX
PD6 ------> USART2_RX
*/
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
__HAL_AFIO_REMAP_USART2_ENABLE();
/* USART2 interrupt Init */
HAL_NVIC_SetPriority(USART2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USART2_IRQn);
/* USER CODE BEGIN USART2_MspInit 1 */
/* USER CODE END USART2_MspInit 1 */
}
}
/**
* @brief UART MSP De-Initialization
* This function freeze the hardware resources used in this example
* @param huart: UART handle pointer
* @retval None
*/
void HAL_UART_MspDeInit(UART_HandleTypeDef* huart)
{
if(huart->Instance==UART4)
{
/* USER CODE BEGIN UART4_MspDeInit 0 */
/* USER CODE END UART4_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_UART4_CLK_DISABLE();
/**UART4 GPIO Configuration
PC10 ------> UART4_TX
PC11 ------> UART4_RX
*/
HAL_GPIO_DeInit(GPIOC, GPIO_PIN_10|GPIO_PIN_11);
/* USER CODE BEGIN UART4_MspDeInit 1 */
/* USER CODE END UART4_MspDeInit 1 */
}
else if(huart->Instance==UART5)
{
/* USER CODE BEGIN UART5_MspDeInit 0 */
/* USER CODE END UART5_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_UART5_CLK_DISABLE();
/**UART5 GPIO Configuration
PC12 ------> UART5_TX
PD2 ------> UART5_RX
*/
HAL_GPIO_DeInit(GPIOC, GPIO_PIN_12);
HAL_GPIO_DeInit(GPIOD, GPIO_PIN_2);
/* USER CODE BEGIN UART5_MspDeInit 1 */
/* USER CODE END UART5_MspDeInit 1 */
}
else if(huart->Instance==USART2)
{
/* USER CODE BEGIN USART2_MspDeInit 0 */
/* USER CODE END USART2_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_USART2_CLK_DISABLE();
/**USART2 GPIO Configuration
PD5 ------> USART2_TX
PD6 ------> USART2_RX
*/
HAL_GPIO_DeInit(GPIOD, GPIO_PIN_5|GPIO_PIN_6);
/* USART2 interrupt DeInit */
HAL_NVIC_DisableIRQ(USART2_IRQn);
/* USER CODE BEGIN USART2_MspDeInit 1 */
/* USER CODE END USART2_MspDeInit 1 */
}
}
UART引脚配置
配置board下的Kconfig,使能UART4、UART5
menu "On-chip Peripheral Drivers"
config BSP_USING_GPIO
bool "Enable GPIO"
select RT_USING_PIN
default y
menuconfig BSP_USING_UART
bool "Enable UART"
default y
select RT_USING_SERIAL
if BSP_USING_UART
config BSP_USING_UART2
bool "Enable UART2"
default y
config BSP_UART2_RX_USING_DMA
bool "Enable UART2 RX DMA"
depends on BSP_USING_UART2 && RT_SERIAL_USING_DMA
default n
config BSP_USING_UART3
bool "Enable UART3"
default y
config BSP_USING_UART4
bool "Enable UART4"
default y
config BSP_USING_UART5
bool "Enable UART5"
default y
endif
source "../libraries/HAL_Drivers/Kconfig"
endmenu
使能libmodbus
使用RT-Thread ENV menuconfig,配置使能libmodbus
libmodbus,配置比较简单,开启主机功能即可。
调试:
测试demo可以使用:libmodbus提供的,注意手动修改RS485引脚、串口、从机地址等。
#include <rtthread.h>
#include <board.h>
#include "modbus.h"
#include "stdio.h"
#include "string.h"
#define RS485_RE GET_PIN(C, 14)
static void mb_master_thread(void *param)
{
uint16_t tab_reg[64] = {0};
modbus_t *ctx = RT_NULL;
ctx = modbus_new_rtu("/dev/uart4", 115200, 'N', 8, 1);
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);
modbus_connect(ctx);
modbus_set_response_timeout(ctx, 0, 1000000);
int num = 0;
while (1)
{
memset(tab_reg, 0, 64 * 2);
int regs = modbus_read_registers(ctx, 0, 20, tab_reg);
rt_kprintf("-------------------------------------------\n");
rt_kprintf("[%4d][read num = %d]", num, regs);
num++;
int i;
for (i = 0; i < 20; i++)
{
rt_kprintf("<%#x>", tab_reg[i]);
}
rt_kprintf("\n");
rt_kprintf("-------------------------------------------\n");
rt_thread_mdelay(1000);
}
//7-关闭modbus端口
modbus_close(ctx);
//8-释放modbus资源
modbus_free(ctx);
}
static int mb_rtu_master_init(void)
{
rt_pin_mode(RS485_RE, PIN_MODE_OUTPUT);
rt_thread_t tid;
tid = rt_thread_create("mb_m",
mb_master_thread, RT_NULL,
2048,
12, 10);
if (tid != RT_NULL)
rt_thread_startup(tid);
return RT_EOK;
}
INIT_APP_EXPORT(mb_rtu_master_init);
接线,连接USB转RS485,打开Modbus Slave,模拟modbus从机
注意从机地址!!
手动设置Modbus从机 数据,用于主机读取
查看通信情况
主机端的打印
- libmodbus主机可以正常的工作,目前测试10分钟以上,通信一直正常。
- freemodbus主机,貌似运行时间久了,会停掉,待分析确认!
总结
- 因为libmodbus使用了类似设备的架构,使用动态内存,所以,内存占用较高,RT-Thread开启了DFS。
- libmodbus从机功能简单,主机功能强大,API比较丰富。
- 对比freemodbus,建议freemodbus做从机,libmodbus做主机。