一、RT-Thread Studio新建项目
根据自己硬件参数设置,此工程基于RT-Thread 5.1.0,此时编译会发现编译报错
这是由于RT-Thread 新老版本的不兼容报错,可以通过配置项目中的RT-Thread Settings-->组件-->旧版本兼容支持性打开,再次编译即可正常编译通过
/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-07-10 RT-Thread first version
*/
#include <rtthread.h>
#include <board.h>
#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
rt_thread_t Modbus_Thread_tid;
rt_thread_t Led_Thread_tid;
#define LED0 GET_PIN(F,9)
#define LED1 GET_PIN(F,10)
void LED_Init()
{
rt_pin_mode(LED0,PIN_MODE_OUTPUT);
rt_pin_mode(LED1,PIN_MODE_OUTPUT);
rt_pin_write(LED0, PIN_LOW);
rt_pin_write(LED0, PIN_LOW);
}
void Led_Thread_entry(void *parameter)
{
while(1)
{
rt_thread_mdelay(100);
}
}
void Modbus_Thread_entry(void *parameter)
{
while(1)
{
rt_pin_write(LED0, !(rt_pin_read(LED0)));
rt_thread_mdelay(1000);
}
}
int main(void)
{
LED_Init();
Modbus_Thread_tid =rt_thread_create("Modbus_Thread",Modbus_Thread_entry,RT_NULL,2048,23,5);
if(Modbus_Thread_tid != RT_NULL)
{
rt_thread_startup(Modbus_Thread_tid);
}
Led_Thread_tid =rt_thread_create("Led_Thread",Led_Thread_entry,RT_NULL,2048,23,5);
if(Led_Thread_tid != RT_NULL)
{
rt_thread_startup(Led_Thread_tid);
}
return RT_EOK;
}
修改main函数,这里开启了两个线程以及LED的简单使用,在添加Freemodbus之前,需要先配置LAN8720+LWIP,首先在board.h中开启ETH相关的宏chushihua
#define BSP_USING_ETH
#ifdef BSP_USING_ETH
#define PHY_USING_LAN8720A
/*#define PHY_USING_DM9161CEP*/
/*#define PHY_USING_DP83848C*/
#endif
初始化引脚和时钟,将HAL_ETH_MspInit函数复制到自己工程的board.c文件的末尾,使之参与编译
void HAL_ETH_MspInit(ETH_HandleTypeDef* heth)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(heth->Instance==ETH)
{
/* USER CODE BEGIN ETH_MspInit 0 */
/* USER CODE END ETH_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_ETH_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOG_CLK_ENABLE();
/**ETH GPIO Configuration
PC1 ------> ETH_MDC
PA1 ------> ETH_REF_CLK
PA2 ------> ETH_MDIO
PA7 ------> ETH_CRS_DV
PC4 ------> ETH_RXD0
PC5 ------> ETH_RXD1
PG11 ------> ETH_TX_EN
PG13 ------> ETH_TXD0
PG14 ------> ETH_TXD1
*/
GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_4|GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_13|GPIO_PIN_14;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);
/* USER CODE BEGIN ETH_MspInit 1 */
/* USER CODE END ETH_MspInit 1 */
}
}
在stm32f4xx_hal_conf.h文件中打开对ETH的支持,也就是取消掉#define HAL_ETH_MODULE_ENABLED这个宏定义的注释
/* #define HAL_DAC_MODULE_ENABLED */
/* #define HAL_DCMI_MODULE_ENABLED */
/* #define HAL_DMA2D_MODULE_ENABLED */
#define HAL_ETH_MODULE_ENABLED
/* #define HAL_NAND_MODULE_ENABLED */
/* #define HAL_NOR_MODULE_ENABLED */
/* #define HAL_PCCARD_MODULE_ENABLED */
在drv_eth.c文件中会调用phy_reset函数,该函数需要根据自己实际情况实现
#define RESET_IO GET_PIN(D, 3)
void phy_reset(void)
{
rt_pin_write(RESET_IO, PIN_LOW);
rt_thread_mdelay(50);
rt_pin_write(RESET_IO, PIN_HIGH);
}
int phy_init(void)
{
rt_pin_mode(RESET_IO, PIN_MODE_OUTPUT);
rt_pin_write(RESET_IO, PIN_HIGH);
return RT_EOK;
}
INIT_BOARD_EXPORT(phy_init);
配置项目中的RT-Thread Settings-->组件-->网络-->使能Lwip堆栈
使用静态地址,所以需要将通过DHCP分配IP地址关闭,编译下载程序后,将自己ip与电路板ip修改成同一网关,此时已经可以正常ping通电路板,但还不支持ifconfig指令,需要开启SAL
二、添加freemodbus软件包
在RT-Thread Settings-->添加软件包-->搜索modbus-->添加freemodbus软件包
在RT-Thread Settings-->软件包-->Freemodbus: Modbus 主从堆栈
使能Slave mode,将Enable RTU slave mode 关闭,使能Enable TCP slave mode,同时使能Enable slave sample(打开例程),由于是TCP,所以该选型下面的串口参数设置实际不用配置
其中可以在advanced configuration中配置自己所需的寄存器数量
ctrl+s保存RT-Thread Settings,会自动添加TCP Server 软件包,不需要去管
可以看到在packages中已经添加了对应的软件包程序,在packages-->freemodbus-latest-->samples-->sample_mb_slave.c中可以看到freemodbus的例程,
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2019-06-21 flybreak first version
*/
#include <rtthread.h>
#include "mb.h"
#include "user_mb_app.h"
#ifdef PKG_MODBUS_SLAVE_SAMPLE
#define SLAVE_ADDR MB_SAMPLE_SLAVE_ADDR
#define PORT_NUM MB_SLAVE_USING_PORT_NUM
#define PORT_BAUDRATE MB_SLAVE_USING_PORT_BAUDRATE
#else
#define SLAVE_ADDR 0x01
#define PORT_NUM 2
#define PORT_BAUDRATE 115200
#endif
#define PORT_PARITY MB_PAR_EVEN
#define MB_POLL_THREAD_PRIORITY 10
#define MB_SEND_THREAD_PRIORITY RT_THREAD_PRIORITY_MAX - 1
#define MB_POLL_CYCLE_MS 200
extern USHORT usSRegHoldBuf[S_REG_HOLDING_NREGS];
static void send_thread_entry(void *parameter)
{
USHORT *usRegHoldingBuf;
usRegHoldingBuf = usSRegHoldBuf;
rt_base_t level;
while (1)
{
/* Test Modbus Master */
level = rt_hw_interrupt_disable();
usRegHoldingBuf[3] = (USHORT)(rt_tick_get() / 100);
rt_hw_interrupt_enable(level);
rt_thread_mdelay(1000);
}
}
static void mb_slave_poll(void *parameter)
{
if (rt_strstr(parameter, "RTU"))
{
#ifdef PKG_MODBUS_SLAVE_RTU
eMBInit(MB_RTU, SLAVE_ADDR, PORT_NUM, PORT_BAUDRATE, PORT_PARITY);
#else
rt_kprintf("Error: Please open RTU mode first");
#endif
}
else if (rt_strstr(parameter, "ASCII"))
{
#ifdef PKG_MODBUS_SLAVE_ASCII
eMBInit(MB_ASCII, SLAVE_ADDR, PORT_NUM, PORT_BAUDRATE, PORT_PARITY);
#else
rt_kprintf("Error: Please open ASCII mode first");
#endif
}
else if (rt_strstr(parameter, "TCP"))
{
#ifdef PKG_MODBUS_SLAVE_TCP
eMBTCPInit(0);
#else
rt_kprintf("Error: Please open TCP mode first");
#endif
}
else
{
rt_kprintf("Error: unknown parameter");
}
eMBEnable();
while (1)
{
eMBPoll();
rt_thread_mdelay(MB_POLL_CYCLE_MS);
}
}
static int mb_slave_sample(int argc, char **argv)
{
static rt_uint8_t is_init = 0;
rt_thread_t tid1 = RT_NULL, tid2 = RT_NULL;
if (is_init > 0)
{
rt_kprintf("sample is running\n");
return -RT_ERROR;
}
if (argc < 2)
{
rt_kprintf("Usage: mb_slave_sample RTU/ASCII/TCP\n");
return -1;
}
tid1 = rt_thread_create("md_s_poll", mb_slave_poll, argv[1], 1024, MB_POLL_THREAD_PRIORITY, 10);
if (tid1 != RT_NULL)
{
rt_thread_startup(tid1);
}
else
{
goto __exit;
}
tid2 = rt_thread_create("md_s_send", send_thread_entry, RT_NULL, 512, MB_SEND_THREAD_PRIORITY, 10);
if (tid2 != RT_NULL)
{
rt_thread_startup(tid2);
}
else
{
goto __exit;
}
is_init = 1;
return RT_EOK;
__exit:
if (tid1)
rt_thread_delete(tid1);
if (tid2)
rt_thread_delete(tid2);
return -RT_ERROR;
}
MSH_CMD_EXPORT(mb_slave_sample, run a modbus slave sample);
可以看到在mb_slave_sample函数中,使能了mb_slave_poll和send_thread_entry两个线程
static void send_thread_entry(void *parameter)
{
USHORT *usRegHoldingBuf;
usRegHoldingBuf = usSRegHoldBuf;
rt_base_t level;
while (1)
{
/* Test Modbus Master */
level = rt_hw_interrupt_disable();
usRegHoldingBuf[3] = (USHORT)(rt_tick_get() / 100);
rt_hw_interrupt_enable(level);
rt_thread_mdelay(1000);
}
}
send_thread_entry函数为不断改变usRegHoldingBuf[3]的值
static void mb_slave_poll(void *parameter)
{
if (rt_strstr(parameter, "TCP"))
{
#ifdef PKG_MODBUS_SLAVE_TCP
eMBTCPInit(0);
#else
rt_kprintf("Error: Please open TCP mode first");
#endif
}
else
{
rt_kprintf("Error: unknown parameter");
}
eMBEnable();
while (1)
{
eMBPoll();
rt_thread_mdelay(MB_POLL_CYCLE_MS);
}
}
mb_slave_poll函数中剔除掉RTU和ASCII的例程后,如上代码所示,再次精简后代码如下
static void mb_slave_poll(void *parameter)
{
eMBTCPInit(0);
eMBEnable();
while (1)
{
eMBPoll();
rt_thread_mdelay(MB_POLL_CYCLE_MS);
}
}
eMBTCPInit(0);//配置TCPPort
eMBEnable();//使能
eMBPoll();//轮询
修改main.c中代码增加
#include <mb.h>
#include <user_mb_app.h>
void modbus_Init()
{
eMBTCPInit(502);
eMBEnable();
}
void Led_Thread_entry(void *parameter)
{
while(1)
{
eMBPoll();
rt_thread_mdelay(100);
}
}
int main(void)
{
LED_Init();
modbus_Init();
Modbus_Thread_tid =rt_thread_create("Modbus_Thread",Modbus_Thread_entry,RT_NULL,2048,23,5);
if(Modbus_Thread_tid != RT_NULL)
{
rt_thread_startup(Modbus_Thread_tid);
}
Led_Thread_tid =rt_thread_create("Led_Thread",Led_Thread_entry,RT_NULL,2048,23,5);
if(Led_Thread_tid != RT_NULL)
{
rt_thread_startup(Led_Thread_tid);
}
return RT_EOK;
}
编译下载