1 获取组件代码
-
直接通过env中
image.png - github下载
2 导入
通过env导入将自动添加文件,组件代码在RTT\components\net\freemodbus\modbus目录下,可以直接使用,而通过git下载的代码需要手动加入,首先进入FreeModbus_Slave-Master-RTT-STM32-1.1\FreeModbus目录,modbus文件夹是协议相关文件,port文件夹是移植相关文件。还需要在rtconfig中添加配置
#define RT_USING_MODBUS
#define RT_MODBUS_MASTER_RTU //这里使用的是RTU
3 移植
目前使用stm32f103,无需移植,之后更新补充该章节
3.1定时器
不同波特率定时时间计算:
1.freemodbus一帧的结束是通过串口3.5位传输的时间来判断的,当串口的波特率大于19200时超时时间是固定为1750us,当串口通信的波特率小于等于19200时,就有一个计算公式,用来计算超时时间。
2.串口可以设置成以下模式
起始位1bit + 数据位8bit + 停止位1bit
起始位1bit + 数据位8bit + 停止位2bit
起始位1bit + 数据位8bit + 校验位1bit + 停止位1bit
起始位1bit + 数据位8bit + 校验位1bit + 停止位2bit
位数10 11 12 ,取中间值11
3.5位时间的计算:1位传输时间是 1/BaudRate(s),1字节就是 11* 1/BaudRate(s) ,定时器溢出时间3.511 1/BaudRate(s),转成整数运算711 1/BaudRate2(s)
定时配置是50us计一次数,假设为X次,则得出计算公式50X(us) = 711 1/BaudRate2(s),推出X = 7220000UL/BaudRate 2,将X设置到定时器的计数值,将定时器配置成50us计数一次,x50us就会溢出中断。
这里说明一下在定时器初始化中超时时间计算的代码
(50 * usT35TimeOut50us) / (1000 * 1000 / RT_TICK_PER_SECOND) + 1
其中usT35TimeOut50us变量由函数传入,是由波特率计算出的超时时间,单位是us,转化成ms,除以1000,然后再转化成街拍数,乘上 RT_TICK_PER_SECOND/1000,最后的+1是为了至少大于超时时间,因为前面转化小数被去掉了的。
这里要注意一下是,如果RT_TICK_PER_SECOND最好是调高一些,例程中给的是10000。
4 使用
4.1 运行协议栈
需要建立一个线程,下列例子是参考的例程代码
#define thread_ModbusMasterPoll_Prio 9
static rt_uint8_t thread_ModbusMasterPoll_stack[512];
struct rt_thread thread_ModbusMasterPoll;
void thread_entry_ModbusMasterPoll(void* parameter)
{
eMBMasterInit(MB_RTU, 3, 115200, MB_PAR_EVEN);
eMBMasterEnable();
while (1)
{
eMBMasterPoll();
rt_thread_delay(1);
}
}
int main()
{
rt_thread_init(&thread_ModbusMasterPoll, "MBMasterPoll",
thread_entry_ModbusMasterPoll, RT_NULL, thread_ModbusMasterPoll_stack,
sizeof(thread_ModbusMasterPoll_stack), thread_ModbusMasterPoll_Prio,5);
rt_thread_startup(&thread_ModbusMasterPoll);
}
4.2 读写
4.2.1 freemodebus功能字说明
4.2.2 读写API
函数均返回eMBMasterReqErrCode 类型数据,当返回值等于MB_MRE_NO_ERR时则表示操作失败
功能 | 保持寄存器API函数 |
---|---|
单写 | eMBMasterReqWriteHoldingRegister( 从机地址,寄存器地址,无符号16位数据,超时时间); |
多写 | eMBMasterReqWriteMultipleHoldingRegister(从机地址,寄存器地址,写寄存器数,数据首地址,超时时间) |
多读 | eMBMasterReqReadHoldingRegister(从机地址,寄存器地址,读取数量,超时时间) |
读写 | eMBMasterReqReadWriteMultipleHoldingRegister(从机地址,读寄存器地址,读寄存器数量,写数据首地址,写寄存器地址,写寄存器数量,超时时间) |
功能 | 输入寄存器API函数 |
---|---|
多读 | eMBMasterReqReadInputRegister(从机地址,读寄存器地址,读寄存器数量,超时时间) |
功能 | 线圈API函数 |
---|---|
单写 | eMBMasterReqWriteCoil(从机地址,写线圈地址,写线圈数量,超时时间) |
多写 | eMBMasterReqWriteMultipleCoils(从机地址,写线圈起始地址,写线圈数量,写数据首地址,超时时间) |
多读 | eMBMasterReqReadCoils(从机地址,读线圈地址,读线圈数量,超时时间) |
功能 | 离散输入API函数 |
---|---|
多读 | eMBMasterReqReadDiscreteInputs(从机地址,读离散输入地址,读离散输入数量,超时时间) |
例如
//执行
if(MB_MRE_NO_ERR == eMBMasterReqReadHoldingRegister(1,3,1,1))
//则串口输出 01 03 00 03 00 01 74 0A
函数的超时时间是等待发送的时间,并不是发送完成等待应答的时间,单位是毫秒
4.2.3 读数据存储
由user_mb_app_m.c和user_mb_app.c来管理,数据被分类保存在数组中。
从机默认使用 一维数组 作为缓存区数据结构,主机可以存储所有网内从机的数据,所以主机采用 二维数组 对所有从机节点数据进行存储。二维数组的列号代表寄存器、线圈及离散量地址,行号代表从机节点ID,但需要做减一处理,例如usMRegHoldBuf[2][1]代表从机ID为 3,保持寄存器地址为 1 的从机数据。
文件中的函数是协议内部使用,并不是用来读buffer的,也就如果需要读buffer数据,可以先这么处理:
读从机地址为1.寄存器地址为3的数据
extern USHORT usMRegHoldBuf[MB_MASTER_TOTAL_SLAVE_NUM][M_REG_HOLDING_NREGS];
rt_kprintf("Receive success reg %d",usMRegHoldBuf[0][3]);
4.3 使用例程
//main.c
#include
#include "user_mb_app.h"
#define thread_ModbusMasterPoll_Prio 9
#define thread_MDtest_Prio 10
static rt_uint8_t thread_ModbusMasterPoll_stack[512];
static rt_uint8_t thread_MDtest_stack[512];
struct rt_thread thread_ModbusMasterPoll;
struct rt_thread thread_MDtest;
void thread_entry_ModbusMasterPoll(void* parameter)
{
eMBMasterInit(MB_RTU, 3, 115200, MB_PAR_EVEN);
eMBMasterEnable();
while (1)
{
eMBMasterPoll();
rt_thread_delay(1);
}
}
USHORT usModbusUserData[10]={1,2,3,4,5,6,7,8,9,0};
UCHAR temp[2];
void mbMasterThreadEntry(void * para)
{
rt_thread_mdelay(5000);
while (1)
{
// if(MB_MRE_NO_ERR == eMBMasterReqReadInputRegister(1, 3, 2, 1)) //这里的超时时等待获取信号量的时间 01 04 00 03 00 02 81 CB
// if(MB_MRE_NO_ERR == eMBMasterReqWriteHoldingRegister(1,3,usModbusUserData[0],1)) // 01 06 00 03 00 01 B8 0A
if(MB_MRE_NO_ERR == eMBMasterReqReadHoldingRegister(1,3,1,1))
{
//eMBMasterRegHoldingCB(temp,3,1,MB_REG_READ);
//rt_kprintf("Receive success reg 3= %d\n",((uint16_t)temp[0])<<8 + temp[1] );
extern USHORT usMRegHoldBuf[MB_MASTER_TOTAL_SLAVE_NUM][M_REG_HOLDING_NREGS];
rt_kprintf("Receive success reg %d",usMRegHoldBuf[0][3]);
}
else
{
rt_kprintf(" poll failed\n");
}
rt_thread_mdelay(10000);
}
}
int main(void)
{
/* user app entry */
rt_thread_init(&thread_ModbusMasterPoll, "MBMasterPoll",
thread_entry_ModbusMasterPoll, RT_NULL, thread_ModbusMasterPoll_stack,
sizeof(thread_ModbusMasterPoll_stack), thread_ModbusMasterPoll_Prio,5);
rt_thread_startup(&thread_ModbusMasterPoll);
rt_thread_init(&thread_MDtest, "MBtest",
mbMasterThreadEntry, RT_NULL, thread_MDtest_stack,
sizeof(thread_MDtest_stack), thread_MDtest_Prio,5);
rt_thread_startup(&thread_MDtest);
return 0;
}
5 参考
https://blog.csdn.net/weixin_42867108/article/details/82227635
https://github.com/armink/FreeModbus_Slave-Master-RTT-STM32
https://blog.csdn.net/byxdaz/article/details/77979114
6 附件
度盘(nfjc)