一、简介
Modbus协议是一种用于工业控制的网络通讯协议,可以片面的理解为,Modbus协议一种机器与机器之间进行数据、信息传递的一种格式规范。
Modbus协议还遵循主从协议,支持单主机,多从机,最多支持247个从机设备。并且,在同一个通信线路上只会有一个主机,所有的通讯过程全部由主机主动发起,从机接收到主机请求后,会对请求做出响应。从机不会主动进行数据的发送,从机之间也不会有通讯过程。
Modbus的通讯方式有:串行通讯方式、以太网通讯方式、串行-以太网转换方式、无线通讯方式
。表现在物理层就是,可使用3线232、2线485、4线422进行主从机之间的连接,或通过光纤、网线、无线网络等进行主从机之间的连接。
特点:标准、开放、免费;支持多种电器接口,如串行接口RS-232、RS-485等,还可以在各种介质上传递,如:光纤、无线等;Modbus的帧格式简单、紧凑、通俗易懂。用户使用简单,厂商开发简单。
Modbus的4种寄存器类型:线圈
(Coils)、离散量输入
(Discrete Input)、输入寄存器
(Input registers)、保持寄存器
(Holding registers)。
区块 | 访问长度 | 访问方式 | 说明 |
---|---|---|---|
离散量输入 | 位(bit) | 只读 | 数据由IO系统提供 |
线圈 | 位(bit) | 读/写 | 可通过应用程序改写 |
输入寄存器 | 字(word) | 只读 | 数据由IO系统提供 |
保持寄存器 | 字(word) | 读/写 | 可通过应用程序改写 |
二、Modbus通信
包括Modbus RTU、Modbus ASCII、Modbus TCP/IP、Modbus UDP/IP等:
Modbus RTU与Modbus ASCII
:都使用串口通讯协议,Modbus RTU使用二进制格式进行数据传输,通讯效率更高,Modbus ASCII使用ASCII码进行数据传输,可读性好,但通讯效率更低。Modbus TCP/IP
:是基于以太网的一种通讯方式,它将Modbus协议封装在TCP/IP协议栈中,通过以太网传输数据。具有高速、稳定的特点。Modbus UDP/IP
:是基于UDP/IP协议的一种通讯方式。与Modbus TCP/IP不同,Modbus UDP/IP采用无连接的通讯方式,不保证数据的可靠性和顺序。相比于Modbus TCP/IP,Modbus UDP/IP的通讯开销较小,可以减少网络负载。
1.Modbus数据帧格式
无论哪一种Modbus协议版本的帧格式都是一样的,包含以上几个字节:
- 地址域:主机要访问的从机的地址
- 功能码:主机对从机实现的操作,功能码有很多,不同的功能码也对应操作不同类型的寄存器。比如:0x01读线圈、0x03读保持寄存器、0x06写单个寄存器、0x10写多个寄存器等。
- 数据:根据功能的不同,以及传输的数据为请求数据还是响应数据的不同,会有不同的内容。
- 差错校验:为保障传输数据的准确性,modbus会进行差错校验,如Modbus CRC16校验等。
功能码 | 翻译 | 解释 | 作用 |
---|---|---|---|
0x01 | Read Coils | 读线圈状态 | 读取远程设备中1到2000个连续的线圈的状态 |
0x02 | Read Discrete Inputs | 读离散输入状态 | 读取远程设备中1到2000个连续的离散输入的状态 |
0x03 | Read Holding Registers | 读保持寄存器内容 | 读取远程设备中1到125个连续的保持寄存器的内容 |
0x04 | Read Input Registers | 读输入寄存器内容 | 读取远程设备中1到125个连续的输入寄存器的内容 |
0x05 | Write Single Coil | 写单个线圈 | 在远程设备中把单个线圈状态改变为打开或关闭的状态 |
0x06 | Write Single Register | 写单个保持寄存器 | 在远程设备中写入单个保持寄存器 |
0x07 | Read Exception Status (Serial Line only) | 读取异常状态(仅限串行线路) | 读取远程设备中八个异常状态输出的内容 |
0x08 | Diagnostics (Serial Line only) | 通信系统诊断(仅限串行线路) | |
0x0B | Get Comm Event Counter (Serial Line only) | 获取通讯事件计数器(仅限串行线路) | 从远程设备的通信事件计数器获取状态字和事件计数 |
0x0C | Get Comm Event Log (Serial Line only) | 获取通讯事件日志(仅限串行线路) | 从远程设备获取状态字、事件计数、消息计数和事件字节字段 |
0x0F | Write Multiple Coils | 写多个线圈 | 强制远程设备中线圈序列中的每个线圈接通或断开 |
0x10 | Write Multiple registers | 写多个保持寄存器 | 在远程设备中写入连续寄存器块 |
0x11 | Report Slave ID (Serial Line only) | 报导从机信息(仅限串行线路) | 读取远程设备特有的类型、当前状态和其他信息的说明。数据内容特定于每种类型的设备 |
0x14 | Read File Record | 读取文件记录 | |
0x15 | Write File Record | 写文件记录 | |
0x16 | Mask Write Register | 带屏蔽字写入寄存器 | |
0x17 | Read/Write Multiple registers | 读、写多个寄存器 | 执行一次连续写和连续读,写入操作在读取之前执行 |
0x18 | Read FIFO Queue | 读取先进先出队列 | |
0x2B | Encapsulated Interface Transport | 封装接口传输 |
2.报文结构解析
使用Modbus RTU版本、使用Modbus CRC16校验的保持寄存器(Holding registers)做演示,解析其三个常用功能0x03读、0x06写单个、0x10写的报文结构。
(1)0x03请求应答方式
示例:01 03 00 01 00 0A 94 0D
含义:从机设备地址(01)+功能码(03)+起始寄存器完整地址(00 01)+要读取的寄存器个数(00 0A)+CRC16校验码(94 0D)
解析:从地址为1的从机读取寄存器块内容,寄存器开始地址为1,连续读取10个寄存器,即读取地址为1到10的寄存器块。
示例:01 03 14 00 D7 3F 70 00 14 00 0F 00 11 00 08 00 0B 00 0B 00 02 00 00 7E 3F
含义:从机设备地址(01)+功能码(03)+数据字节数(14)+读取到的数据内容(00 D7 3F 70 00 14 00 0F 00 11 00 08 00 0B 00 0B 00 02 00 00)+CRC16校验码(7E 3F)
解析:从地址为1的从机读取寄存器块内容,返回的数据字节20个,寄存器返回数据:寄存器1–215,寄存器2–16240,寄存器3–20,寄存器4–15,寄存器5–17,寄存器6–8,寄存器7–11,寄存器8–11,寄存器9–2,寄存器10–0。
(2)0x06请求应答方式
示例:01 06 27 11 00 01 12 BB
含义:从机设备地址(01)+功能码(06)+寄存器完整地址(27 11)+写入的数据(00 01)+CRC16校验码(12 BB)
解析:在地址为1的从机中,向地址为10001的寄存器,写入数据1。
示例:01 06 27 11 00 01 12 BB
含义:从机设备地址(01)+功能码(06)+寄存器完整地址(27 11)+成功写入的数据(00 01)+CRC16校验码(12 BB)
解析:在地址为1的从机,地址为10001的寄存器中,成功写入数据1。如果06功能写入成功的话,请求码和响应码会是一样的。
(3)0x10请求应答方式
示例:01 10 4E 21 00 03 06 00 01 00 11 00 08 BB 05
含义:从机设备地址(01)+功能码(10)+起始寄存器地址(4E 21)+写入的寄存器个数(00 03)+数据字节数(00 06)+数据内容(00 01、00 11、00 08)+CRC16校验码(BB 05)
解析:在地址为1的从机中,向起始地址为20001的连续3个寄存器,分别写入1、17、8,字节数6个。
示例:01 10 4E 21 00 03 C7 2A
含义:从机设备地址(01)+功能码(10)起始寄存器地址(4E 21)+写入的寄存器个数(00 03)+CRC16校验码(C7 2A)
解析:在地址为1的从机,起始地址为20001的连续3个寄存器中(20001、20002、20003),写入数值。
三、LCD通信
1.界面编写:使用Modbus通信协议编写UI界面程序,使用0x03保持寄存器的地址来进行通信,使其能够持续进行数据的查询。
2.设置模拟串口:使用模拟串口进行串口模拟,分别模拟出两个串口进行数据通信。
3.数据通信:使用Modbus Slave进行数据通信,先将两个模拟串口进行连接,然后进行设置寄存器的地址。
4.通信测试:进行通信,通过更改UI界面的数据和模拟串口的数据来观察数据的变化,可以看到数据通信正常。
四、配置FreeModbus通信协议
1.配置RT-Thread Settings:保存并添加到工程中
2.开启board.h宏定义
3.根据需求配置modbus通讯参数:配置参数都在sample_mb_master.c中
四、完整代码
1.代码分析
- 在初始化时使用eMBInit()这个函数时,设置为
MB_RTU
模式,并且将校验位设置为无校验MB_PAR_NONE
。 - 创建一个线程,专门用来进行屏幕和控制板之间数据的收发,使用到的寄存器数据的接收和发送全部在回调函数中进行即可。
- 回调函数大部分使用的是保持寄存器回调函数,直接在该函数里进行数据的发送处理和数据的接收处理,也可以进行命令的执行。
2.sample_mb_slave.c
#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_NONE
#define MB_POLL_THREAD_PRIORITY 10
#define MB_SEND_THREAD_PRIORITY RT_THREAD_PRIORITY_MAX - 1
#define MB_POLL_CYCLE_MS 20
extern USHORT usSRegHoldBuf[S_REG_HOLDING_NREGS];
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()
{
static rt_uint8_t is_init = 0;
rt_thread_t tid1 = RT_NULL;
if (is_init > 0)
{
rt_kprintf("sample is running\n");
return -RT_ERROR;
}
tid1 = rt_thread_create("md_s_poll", mb_slave_poll, "RTU", 5120, MB_POLL_THREAD_PRIORITY, 20);
if (tid1 != RT_NULL)
{
rt_thread_startup(tid1);
}
else
{
goto __exit;
}
// tid2 = rt_thread_create("md_s_send", send_thread_entry, RT_NULL, 1024, MB_SEND_THREAD_PRIORITY, MB_SEND_CYCLE_MS);
// 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);
INIT_ENV_EXPORT(mb_slave_sample);
3.user_mb_app.c
#include "user_mb_app.h"
extern uint16_t param1_value;
extern uint16_t param2_value;
extern uint16_t param3_value;
extern uint16_t param4_value;
extern uint16_t param5_value;
extern uint16_t param6_value;
/*------------------------Slave mode use these variables----------------------*/
//Slave mode:DiscreteInputs variables
USHORT usSDiscInStart = S_DISCRETE_INPUT_START;
#if S_DISCRETE_INPUT_NDISCRETES%8
UCHAR ucSDiscInBuf[S_DISCRETE_INPUT_NDISCRETES/8+1];
#else
UCHAR ucSDiscInBuf[S_DISCRETE_INPUT_NDISCRETES/8] ;
#endif
//Slave mode:Coils variables
USHORT usSCoilStart = S_COIL_START;
#if S_COIL_NCOILS%8
UCHAR ucSCoilBuf[S_COIL_NCOILS/8+1] ;
#else
UCHAR ucSCoilBuf[S_COIL_NCOILS/8] ;
#endif
//Slave mode:InputRegister variables
USHORT usSRegInStart = S_REG_INPUT_START;
USHORT usSRegInBuf[S_REG_HOLDING_NREGS] ;
//Slave mode:HoldingRegister variables
USHORT usSRegHoldStart = S_REG_HOLDING_START;
USHORT usSRegHoldBuf[S_REG_HOLDING_NREGS] ;
/**
* Modbus slave input register callback function.
*
* @param pucRegBuffer input register buffer
* @param usAddress input register address
* @param usNRegs input register number
*
* @return result
*/
eMBErrorCode eMBRegInputCB(UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
{
eMBErrorCode eStatus = MB_ENOERR;
USHORT iRegIndex;
USHORT * pusRegInputBuf;
USHORT REG_INPUT_START;
USHORT REG_INPUT_NREGS;
USHORT usRegInStart;
pusRegInputBuf = usSRegInBuf;
REG_INPUT_START = S_REG_INPUT_START;
REG_INPUT_NREGS = S_REG_INPUT_NREGS;
usRegInStart = usSRegInStart;
/* it already plus one in modbus function method. */
usAddress--;
if ((usAddress >= REG_INPUT_START)
&& (usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS))
{
iRegIndex = usAddress - usRegInStart;
while (usNRegs > 0)
{
*pucRegBuffer++ = (UCHAR) (pusRegInputBuf[iRegIndex] >> 8);
*pucRegBuffer++ = (UCHAR) (pusRegInputBuf[iRegIndex] & 0xFF);
iRegIndex++;
usNRegs--;
}
}
else
{
eStatus = MB_ENOREG;
}
return eStatus;
}
/**
* Modbus slave holding register callback function.
*
* @param pucRegBuffer holding register buffer
* @param usAddress holding register address
* @param usNRegs holding register number
* @param eMode read or write
*
* @return result
*/
eMBErrorCode eMBRegHoldingCB(UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNRegs, eMBRegisterMode eMode)
{
eMBErrorCode eStatus = MB_ENOERR;
USHORT iRegIndex;
USHORT nowIRegIndex;
USHORT * pusRegHoldingBuf;
USHORT REG_HOLDING_START;
USHORT REG_HOLDING_NREGS;
USHORT usRegHoldStart;
pusRegHoldingBuf = usSRegHoldBuf;
REG_HOLDING_START = S_REG_HOLDING_START;
REG_HOLDING_NREGS = S_REG_HOLDING_NREGS;
usRegHoldStart = usSRegHoldStart;
/* it already plus one in modbus function method. */
usAddress--;
if ((usAddress >= REG_HOLDING_START)
&& (usAddress + usNRegs <= REG_HOLDING_START + REG_HOLDING_NREGS))
{
iRegIndex = usAddress - usRegHoldStart;
switch (eMode)
{
/* read current register values from the protocol stack. */
case MB_REG_READ:
// 数据赋值
switch (iRegIndex / 2)
{
case 0: pusRegHoldingBuf[iRegIndex] = param1_value; break;
case 1: pusRegHoldingBuf[iRegIndex] = param2_value; break;
case 2: pusRegHoldingBuf[iRegIndex] = param3_value; break;
case 3: pusRegHoldingBuf[iRegIndex] = param4_value; break;
case 4: pusRegHoldingBuf[iRegIndex] = param5_value; break;
case 5: pusRegHoldingBuf[iRegIndex] = param6_value; break;
}
// 读取数据
while (usNRegs > 0)
{
*pucRegBuffer++ = (UCHAR) (pusRegHoldingBuf[iRegIndex] >> 8);
*pucRegBuffer++ = (UCHAR) (pusRegHoldingBuf[iRegIndex] & 0xFF);
iRegIndex++;
usNRegs--;
}
break;
/* write current register values with new values from the protocol stack. */
case MB_REG_WRITE:
// 写入数据
nowIRegIndex = iRegIndex;
while (usNRegs > 0)
{
pusRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8;
pusRegHoldingBuf[iRegIndex] |= *pucRegBuffer++;
iRegIndex++;
usNRegs--;
}
// 数据处理
switch (nowIRegIndex / 2)
{
case 0: param1_value = pusRegHoldingBuf[nowIRegIndex]; break;
case 1: param2_value = pusRegHoldingBuf[nowIRegIndex]; break;
case 2: param3_value = pusRegHoldingBuf[nowIRegIndex]; break;
case 3: param4_value = pusRegHoldingBuf[nowIRegIndex]; break;
case 4: param5_value = pusRegHoldingBuf[nowIRegIndex]; break;
case 5: param6_value = pusRegHoldingBuf[nowIRegIndex]; break;
}
break;
}
}
else
{
eStatus = MB_ENOREG;
}
return eStatus;
}
/**
* Modbus slave coils callback function.
*
* @param pucRegBuffer coils buffer
* @param usAddress coils address
* @param usNCoils coils number
* @param eMode read or write
*
* @return result
*/
eMBErrorCode eMBRegCoilsCB(UCHAR * pucRegBuffer, USHORT usAddress,
USHORT usNCoils, eMBRegisterMode eMode)
{
eMBErrorCode eStatus = MB_ENOERR;
USHORT iRegIndex , iRegBitIndex , iNReg;
UCHAR * pucCoilBuf;
USHORT COIL_START;
USHORT COIL_NCOILS;
USHORT usCoilStart;
iNReg = usNCoils / 8 + 1;
pucCoilBuf = ucSCoilBuf;
COIL_START = S_COIL_START;
COIL_NCOILS = S_COIL_NCOILS;
usCoilStart = usSCoilStart;
/* it already plus one in modbus function method. */
usAddress--;
if( ( usAddress >= COIL_START ) &&
( usAddress + usNCoils <= COIL_START + COIL_NCOILS ) )
{
iRegIndex = (USHORT) (usAddress - usCoilStart) / 8;
iRegBitIndex = (USHORT) (usAddress - usCoilStart) % 8;
switch ( eMode )
{
/* read current coil values from the protocol stack. */
case MB_REG_READ:
while (iNReg > 0)
{
*pucRegBuffer++ = xMBUtilGetBits(&pucCoilBuf[iRegIndex++],
iRegBitIndex, 8);
iNReg--;
}
pucRegBuffer--;
/* last coils */
usNCoils = usNCoils % 8;
/* filling zero to high bit */
*pucRegBuffer = *pucRegBuffer << (8 - usNCoils);
*pucRegBuffer = *pucRegBuffer >> (8 - usNCoils);
break;
/* write current coil values with new values from the protocol stack. */
case MB_REG_WRITE:
while (iNReg > 1)
{
xMBUtilSetBits(&pucCoilBuf[iRegIndex++], iRegBitIndex, 8,
*pucRegBuffer++);
iNReg--;
}
/* last coils */
usNCoils = usNCoils % 8;
/* xMBUtilSetBits has bug when ucNBits is zero */
if (usNCoils != 0)
{
xMBUtilSetBits(&pucCoilBuf[iRegIndex++], iRegBitIndex, usNCoils,
*pucRegBuffer++);
}
break;
}
}
else
{
eStatus = MB_ENOREG;
}
return eStatus;
}
/**
* Modbus slave discrete callback function.
*
* @param pucRegBuffer discrete buffer
* @param usAddress discrete address
* @param usNDiscrete discrete number
*
* @return result
*/
eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
{
eMBErrorCode eStatus = MB_ENOERR;
USHORT iRegIndex , iRegBitIndex , iNReg;
UCHAR * pucDiscreteInputBuf;
USHORT DISCRETE_INPUT_START;
USHORT DISCRETE_INPUT_NDISCRETES;
USHORT usDiscreteInputStart;
iNReg = usNDiscrete / 8 + 1;
pucDiscreteInputBuf = ucSDiscInBuf;
DISCRETE_INPUT_START = S_DISCRETE_INPUT_START;
DISCRETE_INPUT_NDISCRETES = S_DISCRETE_INPUT_NDISCRETES;
usDiscreteInputStart = usSDiscInStart;
/* it already plus one in modbus function method. */
usAddress--;
if ((usAddress >= DISCRETE_INPUT_START)
&& (usAddress + usNDiscrete <= DISCRETE_INPUT_START + DISCRETE_INPUT_NDISCRETES))
{
iRegIndex = (USHORT) (usAddress - usDiscreteInputStart) / 8;
iRegBitIndex = (USHORT) (usAddress - usDiscreteInputStart) % 8;
while (iNReg > 0)
{
*pucRegBuffer++ = xMBUtilGetBits(&pucDiscreteInputBuf[iRegIndex++],
iRegBitIndex, 8);
iNReg--;
}
pucRegBuffer--;
/* last discrete */
usNDiscrete = usNDiscrete % 8;
/* filling zero to high bit */
*pucRegBuffer = *pucRegBuffer << (8 - usNDiscrete);
*pucRegBuffer = *pucRegBuffer >> (8 - usNDiscrete);
}
else
{
eStatus = MB_ENOREG;
}
return eStatus;
}
4.user_mb_app.h
#ifndef USER_APP
#define USER_APP
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mb_m.h"
#include "mbconfig.h"
#include "mbframe.h"
#include "mbutils.h"
/* -----------------------Slave Defines -------------------------------------*/
#define S_DISCRETE_INPUT_START RT_S_DISCRETE_INPUT_START
#define S_DISCRETE_INPUT_NDISCRETES RT_S_DISCRETE_INPUT_NDISCRETES
#define S_COIL_START RT_S_COIL_START
#define S_COIL_NCOILS RT_S_COIL_NCOILS
#define S_REG_INPUT_START RT_S_REG_INPUT_START
#define S_REG_INPUT_NREGS RT_S_REG_INPUT_NREGS
#define S_REG_HOLDING_START RT_S_REG_HOLDING_START
#define S_REG_HOLDING_NREGS RT_S_REG_HOLDING_NREGS
/* salve mode: holding register's all address */
#define S_HD_RESERVE RT_S_HD_RESERVE
/* salve mode: input register's all address */
#define S_IN_RESERVE RT_S_IN_RESERVE
/* salve mode: coil's all address */
#define S_CO_RESERVE RT_S_CO_RESERVE
/* salve mode: discrete's all address */
#define S_DI_RESERVE RT_S_DI_RESERVE
/* -----------------------Master Defines -------------------------------------*/
#define M_DISCRETE_INPUT_START RT_M_DISCRETE_INPUT_START
#define M_DISCRETE_INPUT_NDISCRETES RT_M_DISCRETE_INPUT_NDISCRETES
#define M_COIL_START RT_M_COIL_START
#define M_COIL_NCOILS RT_M_COIL_NCOILS
#define M_REG_INPUT_START RT_M_REG_INPUT_START
#define M_REG_INPUT_NREGS RT_M_REG_INPUT_NREGS
#define M_REG_HOLDING_START RT_M_REG_HOLDING_START
#define M_REG_HOLDING_NREGS RT_M_REG_HOLDING_NREGS
/* master mode: holding register's all address */
#define M_HD_RESERVE RT_M_HD_RESERVE
/* master mode: input register's all address */
#define M_IN_RESERVE RT_M_IN_RESERVE
/* master mode: coil's all address */
#define M_CO_RESERVE RT_M_CO_RESERVE
/* master mode: discrete's all address */
#define M_DI_RESERVE RT_M_DI_RESERVE
#endif
5.main.c
/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-10-15 RT-Thread first version
*/
#include <string.h>
#include <stdlib.h>
#include <rtthread.h>
#include <drv_common.h>
#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
short param1_value = 0;
short param2_value = 0;
short param3_value = 0;
short param4_value = 0;
short param5_value = 0;
short param6_value = 0;
/* 心跳灯线程的入口函数 */
static void thread_heartbeat_entry(void *parameter)
{
int count = 1;
while (1)
{
count++;
rt_pin_write(GET_PIN(E, 12), count % 2);
rt_thread_mdelay(1000);
}
}
/* 创建心跳灯线程 */
static int thread_heartbeat(void)
{
rt_pin_mode(GET_PIN(E, 12), PIN_MODE_OUTPUT);
/* 创建线程 1,名称是 thread1,入口是 thread1_entry,动态创建*/
rt_thread_t tid1 = rt_thread_create("heartbeat", thread_heartbeat_entry, RT_NULL, 256, 25, 5);
/* 如果获得线程控制块,启动这个线程 */
if (tid1 != RT_NULL)
rt_thread_startup(tid1);
return 0;
}
int main(void)
{
int count = 1;
thread_heartbeat();
while (count)
{
rt_thread_mdelay(1000);
}
return RT_EOK;
}
/**
* @brief 设置参数的值
*/
static void Set_Param_Value(int argc, char **argv)
{
if (argc == 1)
{
if (strcmp(argv[0], "printf") == 0)
{
rt_kprintf("param1: %d\n", param1_value);
rt_kprintf("param2: %d\n", param2_value);
rt_kprintf("param3: %d\n", param3_value);
rt_kprintf("param4: %d\n", param4_value);
rt_kprintf("param5: %d\n", param5_value);
rt_kprintf("param6: %d\n", param6_value);
}
}
else if (argc == 3)
{
if (strcmp(argv[0], "set") == 0)
{
if (strcmp(argv[1], "param1") == 0)
{
param1_value = atof(argv[2]);
}
else if (strcmp(argv[1], "param2") == 0)
{
param2_value = atof(argv[2]);
}
else if (strcmp(argv[1], "param3") == 0)
{
param3_value = atof(argv[2]);
}
else if (strcmp(argv[1], "param4") == 0)
{
param4_value = atof(argv[2]);
}
else if (strcmp(argv[1], "param5") == 0)
{
param5_value = atof(argv[2]);
}
else if (strcmp(argv[1], "param6") == 0)
{
param6_value = atof(argv[2]);
}
}
}
}
MSH_CMD_EXPORT_ALIAS(Set_Param_Value, set, set param value);
MSH_CMD_EXPORT_ALIAS(Set_Param_Value, printf, printf set param value);
五、测试验证
通过串口进行数据的发送和接收来验证数据是否能够同步。通过设置不同的数据,发现显示的数据和设置的数据是一致的,则说明显示屏和控制板可以实现数据的通信。
=== 》》》FreeModbus-大彩屏程序
=== 》》》FreeModbus通信-LCD例程
本站资源均来自互联网,仅供研究学习,禁止违法使用和商用,产生法律纠纷本站概不负责!如果侵犯了您的权益请与我们联系!
转载请注明出处: 免费源码网-免费的源码资源网站 » 【RTT-Studio】详细使用教程十七:FreeModbus通信--LCD
发表评论 取消回复