一、简介

  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 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例程


点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部