本帖最后由 forgot 于 2022-12-13 13:58 编辑
#申请原创# 最近在STM32平台上开发DTU网关设备,需要用到RS485接口实现ModBus-RTU协议来进行一些线圈寄存器的控制(继电器开关)和一些保持寄存器的读写(模拟量的读写和一些设备参数配置等),其实通过自己编写函数进行命令解析也可以实现,但是对于多个MODBUS命令及多个不定数寄存器的读写操作略显麻烦,就一次性移植了一下FreeModbus协议栈进行开发。以下是详细过程介绍,希望有需要的可以用到。
一、首先去下载FreeModbus软件包,解压可以看到rtu、tcp、asscii等文件夹,其中ascii与tcp是用不到的,这两个是对应ModBus-asscii与ModBus-tcp开发,functions、include、port文件加是三种协议格式都要用到的共用文件。 目录结构如图:
二、通过MDK的 manage project items操作将整个文件包添加到自己的keil工程下, MDK结构如图:
三、在mbconfig.h中配置好需要用到的协议类型及寄存器类型等基础配置,寄存器部分根据自己的需要进行配置:
协议类型如图:
寄存器配置如图:
接下来就是重点工作,通过编写功能函数来实现几个主要功能,主要是在portserial.c文件中: 首先在实现串口与定时器的初始化,包括地址、波特率、校验等xMBPortSerialInit( ucPort, ulBaudRate, 8, eParity ) ; 使能失能串口的中断vMBPortSerialEnabl(); 在xMBPortSerialPutByte()中和xMBPortSerialGetByte()中调用串口发送函数和串口接收函数; 编写一下串口中断函数,里面分为接收中断和发送中断,实现prvvUARTRxISR();与prvvUARTTxReadyISR();的调用;
再初始化一个定时器xMBPortTimersInit( ( USHORT ) usTimerT35_50us )、xMBPortTimersInit(); 编写vMBPortTimersEnable( )与vMBPortTimersDisable( )其实就是在里面开启和关闭定时器操作; 需要用到定时器中断,在中断中调用prvvTIMERExpiredISR();整个定时器对为了判断完整的数据帧; 到此处,接口函数就全部写好了。
四、在.c中定义自己的寄存器变量和自己的寄存器其实地址,如线圈和输入寄存器的定义 //线圈起始地址 #define REG_COILS_START 0x0000 //线圈数量 #define REG_COILS_SIZE 16。 //线圈状态 uint8_t ucRegCoilsBuf[REG_COILS_SIZE / 8] = {0x01,0x02}; //输入寄存器起始地址 #define REG_INPUT_START 0x0000 //输入寄存器数量 #define REG_INPUT_NREGS 8 //输入寄存器内容 uint16_t usRegInputBuf[REG_INPUT_NREGS] = {0x1000,0x1001,0x1002,0x1003,0x1004,0x1005,0x1006,0x1007}; //输入寄存器起始地址 uint16_t usRegInputStart = REG_INPUT_START; 五、在主函数main();内调用eMBInit(XX);和eMBEnable();。
六、在主程序循环中调用eMBPoll();就可以实现之前定义的寄存器变量中的数据读写功能。函数执行判断是通过事件判断xMBPortEventGet( &eEvent ) == TRUE;里面主要是通过串口的接收中断来发送事件信息。最后的实现是在eException = xFuncHandlers.pxHandler( ucMBFrame, &usLength );中通过接收不同功能码对于不同类型的寄存器的操作。 主要调用相关处理函数如图:
这样整个移植就算是完成了,最后可以通过Modbus Poll等调试工具与进行Modbus的调试。
因为Modbus RTU 协议是一种开放协议。允许制造商在其设备中使用通用 Modbus 协议结构,添加自定义功能以访问硬件。这也是为什么这个协议会在多领域普及的原因吧。 说得不对的地方也欢迎大家指出来。
|