【RTT-Studio】详细使用教程十七:FreeModbus通信--LCD

文章目录

    • 一、简介
    • 二、Modbus通信
    • 三、LCD通信
    • 四、配置FreeModbus通信协议
    • 四、完整代码
    • 五、测试验证

一、简介

  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校验等。
Modbus功能码列表(全)
功能码翻译解释作用
0x01Read Coils读线圈状态读取远程设备中1到2000个连续的线圈的状态
0x02Read Discrete Inputs读离散输入状态读取远程设备中1到2000个连续的离散输入的状态
0x03Read Holding Registers读保持寄存器内容读取远程设备中1到125个连续的保持寄存器的内容
0x04Read Input Registers读输入寄存器内容读取远程设备中1到125个连续的输入寄存器的内容
0x05Write Single Coil写单个线圈在远程设备中把单个线圈状态改变为打开或关闭的状态
0x06Write Single Register写单个保持寄存器在远程设备中写入单个保持寄存器
0x07Read Exception Status (Serial Line only)读取异常状态(仅限串行线路)读取远程设备中八个异常状态输出的内容
0x08Diagnostics (Serial Line only)通信系统诊断(仅限串行线路)
0x0BGet Comm Event Counter (Serial Line only)获取通讯事件计数器(仅限串行线路)从远程设备的通信事件计数器获取状态字和事件计数
0x0CGet Comm Event Log (Serial Line only)获取通讯事件日志(仅限串行线路)从远程设备获取状态字、事件计数、消息计数和事件字节字段
0x0FWrite Multiple Coils写多个线圈强制远程设备中线圈序列中的每个线圈接通或断开
0x10Write Multiple registers写多个保持寄存器在远程设备中写入连续寄存器块
0x11Report Slave ID (Serial Line only)报导从机信息(仅限串行线路)读取远程设备特有的类型、当前状态和其他信息的说明。数据内容特定于每种类型的设备
0x14Read File Record读取文件记录
0x15Write File Record写文件记录
0x16Mask Write Register带屏蔽字写入寄存器
0x17Read/Write Multiple registers读、写多个寄存器执行一次连续写和连续读,写入操作在读取之前执行
0x18Read FIFO Queue读取先进先出队列
0x2BEncapsulated 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 20extern 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_RTUeMBInit(MB_RTU, SLAVE_ADDR, PORT_NUM, PORT_BAUDRATE, PORT_PARITY);
#elsert_kprintf("Error: Please open RTU mode first");
#endif}else if (rt_strstr(parameter, "ASCII")){
#ifdef PKG_MODBUS_SLAVE_ASCIIeMBInit(MB_ASCII, SLAVE_ADDR, PORT_NUM, PORT_BAUDRATE, PORT_PARITY);
#elsert_kprintf("Error: Please open ASCII mode first");
#endif}else if (rt_strstr(parameter, "TCP")){
#ifdef PKG_MODBUS_SLAVE_TCPeMBTCPInit(0);
#elsert_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例程


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/453392.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PostgreSQL的学习心得和知识总结(一百五十五)|[performance]优化期间将 WHERE 子句中的 IN VALUES 替换为 ANY

目录结构 注&#xff1a;提前言明 本文借鉴了以下博主、书籍或网站的内容&#xff0c;其列表如下&#xff1a; 1、参考书籍&#xff1a;《PostgreSQL数据库内核分析》 2、参考书籍&#xff1a;《数据库事务处理的艺术&#xff1a;事务管理与并发控制》 3、PostgreSQL数据库仓库…

《MYSQL实战45讲 》 优化器如何选择索引?

SHOW VARIABLES LIKE long_query_time; set long_query_time0 优化器如何选择索引&#xff1f; 1.扫描的行数 估计出各个索引大致的要扫描的行数&#xff0c;行数越少&#xff0c;效率越高。 索引的基数也叫区分度&#xff0c;就是这个索引所在的字段上不同的值又多少个。优…

MySQL【知识改变命运】06

前言&#xff1a;在05这节数据结构里面&#xff0c;我们知道select * from 这个操作很危险&#xff0c;如果数据库很大&#xff0c;会把服务器资源耗尽&#xff0c;接下来提到的查询可以有效的限制返回记录 1&#xff1a;分页查询 分页查询可以有效控制一次查询出来的结果集的…

数据结构与集合源码

目录 一、数据结构 1.1 数据结构概念 1.2 研究对象 1.3 常见存储结构 1.3.1 数组 1.3.2 链表 1.单向链表 2.双向链表 1.3.3 二叉树 1.3.4 栈&#xff08;FILO&#xff0c;先进后出&#xff09; 1.3.5 队列&#xff08;FIFO&#xff0c;先进先出&#xff09; 二、集合…

基于卷积神经网络的蔬菜识别系统,resnet50,mobilenet模型【pytorch框架+python源码】

更多目标检测和图像分类识别项目可看我主页其他文章 功能演示&#xff1a; 基于卷积神经网络的蔬菜识别系统&#xff0c;resnet50&#xff0c;mobilenet【pytorch框架&#xff0c;python&#xff0c;tkinter】_哔哩哔哩_bilibili &#xff08;一&#xff09;简介 基于卷积神…

Java设计模式梳理:行为型模式(策略,观察者等)

行为型模式 行为型模式关注的是各个类之间的相互作用&#xff0c;将职责划分清楚&#xff0c;使得我们的代码更加地清晰。 策略模式 策略模式太常用了&#xff0c;所以把它放到最前面进行介绍。它比较简单&#xff0c;我就不废话&#xff0c;直接用代码说事吧。 下面设计的…

软件架构之构件复用技术

简介 软件架构复用 在应用软件系统的开发过程中&#xff0c;通常包含以下几个关键阶段&#xff1a;需求分析、设计、编码、测试和维护。在这些阶段中&#xff0c;复用技术均可以得到有效应用。特别是&#xff0c;软件架构复用作为一种大粒度、高抽象级别的复用方式&#xff0…

55 | 享元模式(下):剖析享元模式在Java Integer、String中的应用

上篇文章&#xff0c;我们通过棋牌游戏和文本编辑器这样两个实际的例子&#xff0c;学习了享元模式的原理、实现以及应用场景。用一句话总结一下&#xff0c;享元模式中的“享元”指被共享的单元。享元模式通过复用对象&#xff0c;以达到节省内存的目的。 今天&#xff0c;我…

[PHP]重复的Notice错误信息

<?php $a []; var_dump($a[name]);执行结果&#xff1a; 原因&#xff1a; display_errors和error_reporting都打开了Notice错误信息

线性回归实现

1.从数据流水线、模型、损失函数、小批量随机梯度下降优化器 %matplotlib inline import random import torch from d2l import torch as d2l 2.根据带有噪声的线性模型构造人造数据集。使用线性模型参数w [2,-3.4]T、b 4.2和噪声项ε生成数据集及标签 y Xw b ε def …

windows 上验证请求接口是否有延迟

文件名&#xff1a;api_request_script.bat &#xff0c;直接右键点击执行即可。 echo off setlocal:: 配置:: 替换为实际接口URL set "logFilelog.txt" set "errorLogFileerror_log.txt" set "interval3" :: 请求间隔&#xff08;秒&#xff…

React之组件渲染性能优化

关键词&#xff1a; shouldComponentUpdate、PureComnent、React.memo、useMemo、useCallback shouldComponentUpdate 与 PureComnent shouldComponentUpdate 与 PureComnent 用于类组件。虽然官方推荐使用函数组件&#xff0c;但我们依然需要对类组件的渲染优化策略有所了解…

面经汇总——第一篇

1. int数据类型做了什么优化 Java在处理整数类型时&#xff0c;进行了多种优化&#xff0c;主要体现在编译器层面和JVM层面&#xff0c;目的是提高性能、减少内存开销。 常量池优化 Java中的Integer类有一个缓存机制&#xff0c;对于值在-128到127之间的int数字&#xff0c;Int…

springBoot集成nacos注册中心以及配置中心

一、安装启动nacos 访问&#xff1a;http://127.0.0.1:8848/nacos/index.html#/login 二、工程集成nacos 1、引入依赖 我这里搭建的父子工程哈&#xff0c;在子工程引入 <dependencies><!-- SpringBoot Web --><dependency><groupId>org.sp…

代码审计-Python Flask

1.Jinjia2模版注入 Flask是一个使用 Python 编写的轻量级 Web 应用框架。其 WSGI 工具箱采用 Werkzeug &#xff0c;模板引擎则使用 Jinja2。jinja2是Flask作者开发的一个模板系统&#xff0c;起初是仿django模板的一个模板引擎&#xff0c;为Flask提供模板支持&#xff0c;由于…

MySQL-30.索引-介绍

一.索引 为什么需要索引&#xff1f;当我们没有建立索引时&#xff0c;要在一张数据量极其庞大的表中查询表里的某一个值&#xff0c;会非常的消耗时间。以一个6000000数据量的表为例&#xff0c;查询一条记录的时间耗时约为13s&#xff0c;这是因为要查询符合某个值的数据&am…

RabbitMQ系列学习笔记(八)--发布订阅模式

文章目录 一、发布订阅模式原理二、发布订阅模式实战1、消费者代码2、生产者代码3、查看运行结果 本文参考&#xff1a; 尚硅谷RabbitMQ教程丨快速掌握MQ消息中间件rabbitmq RabbitMQ 详解 Centos7环境安装Erlang、RabbitMQ详细过程(配图) 一、发布订阅模式原理 在开发过程中&…

SpringBoot+MyBatis+MySQL项目基础搭建

一、新建项目 1.1 新建springboot项目 新建项目 选择SpringBoot&#xff0c;填写基本信息&#xff0c;主要是JDK版本和项目构建方式&#xff0c;此处以JDK17和Maven举例。 1.2 引入依赖 选择SpringBoot版本&#xff0c;勾选Lombok&#xff0c;Spring Web&#xff0c;MyBa…

UI自动化测试 —— web端元素获取元素等待实践!

前言 Web UI自动化测试是一种软件测试方法&#xff0c;通过模拟用户行为&#xff0c;自动执行Web界面的各种操作&#xff0c;并验证操作结果是否符合预期&#xff0c;从而提高测试效率和准确性。 目的&#xff1a; 确保Web应用程序的界面在不同环境(如不同浏览器、操作系统)下…

设计模式和软件框架的关系

设计模式和软件框架在软件开发中都有助于解决复杂问题和提高代码质量&#xff0c;但它们在概念和使用上存在一些区别。它们的关系可以通过以下几点理解&#xff1a; 层次与抽象程度 设计模式&#xff08;Design Patterns&#xff09;是一组通用的、可复用的解决方案&#xff0c…