嵌入式上位机开发入门(二十二):RTU/TCP 双协议互斥访问寄存器
目录一、前言二、设计思路共享寄存器 互斥锁三、modbus_mapping_t 结构体四、TCP Server 任务初始化与调度五、RTU Server 任务复用资源六、两个任务的协作关系七、总结八、结尾一、前言大家好这里是Hello_Embed。上篇对 H5 开发板程序进行了六处针对性改进。本篇来实现最终目标让中控同时支持 Modbus RTU 和 Modbus TCP 两种协议。整体效果上位机既可以通过USB 串口RTU与中控通信也可以通过WiFi 网络TCP访问两种协议读写的是同一套寄存器并通过 LVGL 界面实现协议切换。配合写文件功能还可以进行远程 IAP 升级。改造完全基于原 RTU 工程只展示关键改动部分。二、设计思路共享寄存器 互斥锁双协议兼容的核心只需解决一个问题如何让两种协议安全地访问同一套寄存器而不发生冲突答案是用一个全局互斥锁Mutex保护寄存器访问。┌─────────────────────────────────────────┐ │ 共享寄存器 mb_mapping │ └──────────────────┬──────────────────────┘ │ 受全局 Mutex 保护 ┌───────────┴───────────┐ ↓ ↓ LibmodbusTCPServerTask LibmodbusRTUServerTask WiFi / TCP 通道 USB / RTU 通道两个任务在处理上位机请求时都要先获取互斥锁操作完寄存器后再释放锁。这样无论哪个任务在运行寄存器数据始终是一致的。数据格式的适配RTU/TCP 帧头差异在前几篇中已经完成本篇专注于任务间的资源共享与互斥。三、modbus_mapping_t 结构体两个任务共同访问的寄存器映射表其结构如下typedefstruct_modbus_mapping_t{intnb_bits;/* 线圈数量 */intstart_bits;/* 线圈起始地址 */intnb_input_bits;/* 离散输入数量 */intstart_input_bits;intnb_input_registers;/* 输入寄存器数量 */intstart_input_registers;intnb_registers;/* 保持寄存器数量 */intstart_registers;uint8_t*tab_bits;/* 线圈数据 */uint8_t*tab_input_bits;/* 离散输入数据 */uint16_t*tab_input_registers;/* 输入寄存器数据 */uint16_t*tab_registers;/* 保持寄存器数据 */}modbus_mapping_t;解析mb_mapping是整个 Modbus 数据模型的核心——上位机读写的线圈、寄存器本质上都是读写这个结构体里的数组。两个协议任务共享同一个mb_mapping实例对同一套物理数据进行操作从而实现RTU 写入、TCP 可读的双向透明。四、TCP Server 任务初始化与调度TCP Server 任务承担了所有全局资源的初始化工作然后再创建 RTU 任务voidLibmodbusTCPServerTask(void*pvParameters){uint8_t*query;modbus_t*ctx;intrc;modbus_mapping_t*mb_mapping;PChannelInfo ptChannelInfo;intchannel;interr;intheader_length;intsocket_server,socket_client;/* 显示当前运行模式 */if(isBootloader())Draw_String(150,0,Bootloader,0xff0000,0);elseDraw_String(150,0,Application,0xff0000,0);/* ① 创建共享寄存器映射表供两个任务共用 */mb_mappingmodbus_mapping_new_start_address(0,500,0,500,0,500,0,500);memset(mb_mapping-tab_bits,0,mb_mapping-nb_bits);memset(mb_mapping-tab_registers,0,mb_mapping-nb_registers*2);g_mb_mappingmb_mapping;/* 存入全局变量供 RTU 任务使用 *//* ② 创建全局互斥锁 */g_mb_lockxSemaphoreCreateMutex();/* ③ 初始化通道映射信息 */for(channel1;channelMAX_CHANNEL_NUMBER;channel){ptChannelInfog_tChannelInfos[channel];ptChannelInfo-mb_mappingmb_mapping;ptChannelInfo-xMutexxSemaphoreCreateMutex();}/* ④ 创建业务任务Bootloader 模式下不需要 */if(!isBootloader()){xTaskCreate(CH0_Task,CH0_Task,400,mb_mapping,osPriorityNormal,NULL);for(channel1;channelMAX_CHANNEL_NUMBER;channel){ptChannelInfog_tChannelInfos[channel];xTaskCreate(CHn_Task,ptChannelInfo-uart_name,400,ptChannelInfo,osPriorityNormal,NULL);}}/* ⑤ 创建 RTU Server 任务在 TCP 初始化之后确保 g_mb_mapping 已就绪 */xTaskCreate(LibmodbusRTUServerTask,LibmodbusRTUServerTask,400,NULL,osPriorityNormal,NULL);/* ⑥ 连接 WiFi 热点 */at_init(uart1);while(1){errat_connect_ap(Programmers,100asktech);if(!err)break;vTaskDelay(1000);}/* ⑦ 创建 TCP 上下文并监听 */ctxmodbus_new_tcp(NULL,1502);querypvPortMalloc(MODBUS_TCP_MAX_ADU_LENGTH);header_lengthmodbus_get_header_length(ctx);Draw_String(0,48,Waiting connect ...,0xff0000,0);while(1){socket_servermodbus_tcp_listen(ctx,1);if(socket_server0)break;}while(1){socket_clientmodbus_tcp_accept(ctx,socket_server);if(socket_client0)break;}Draw_String(0,64,Modbus client connected,0xff0000,0);/* ⑧ 主循环接收 → 加锁 → 处理 → 回复 → 解锁 */for(;;){do{rcmodbus_receive(ctx,query);}while(rc0);if(rc0){/* Socket 出错等待重连 */Draw_String(0,80,wait re-connect ...,0xff0000,0);while(1){socket_clientmodbus_tcp_accept(ctx,socket_server);if(socket_client0)break;}Draw_String(0,96,Modbus client re-connected,0xff0000,0);continue;}/* 获取互斥锁保护寄存器访问 */xSemaphoreTake(g_mb_lock,portMAX_DELAY);errprocess_emergency_cmd(ctx,query,rc,mb_mapping);if(err){modbus_reply_exception(ctx,query,MODBUS_EXCEPTION_SLAVE_OR_SERVER_BUSY);xSemaphoreGive(g_mb_lock);continue;}errprocess_file_record(query[header_length-1],rc);if(err){modbus_reply_exception(ctx,query,MODBUS_EXCEPTION_SLAVE_OR_SERVER_BUSY);xSemaphoreGive(g_mb_lock);continue;}rcmodbus_reply(ctx,query,rc,mb_mapping);/* 释放互斥锁 */xSemaphoreGive(g_mb_lock);}modbus_mapping_free(mb_mapping);vPortFree(query);modbus_close(ctx);modbus_free(ctx);vTaskDelete(NULL);}解析初始化顺序很关键——g_mb_mapping和g_mb_lock必须在创建 RTU 任务之前就准备好否则 RTU 任务启动后访问全局变量会拿到空指针。因此 TCP 任务先做所有初始化再拉起 RTU 任务最后自己才去连 WiFi 和监听。五、RTU Server 任务复用资源RTU 任务无需重复初始化直接从全局变量取mb_mapping和互斥锁voidLibmodbusRTUServerTask(void*pvParameters){uint8_t*query;modbus_t*ctx;intrc;modbus_mapping_t*mb_mapping;interr;intheader_length;/* RTU 上下文通过 USB 串口通信 */ctxmodbus_new_st_rtu(usb,115200,N,8,1);modbus_set_slave(ctx,1);querypvPortMalloc(MODBUS_RTU_MAX_ADU_LENGTH);/* 直接复用 TCP 任务创建的共享寄存器映射表 */mb_mappingg_mb_mapping;header_lengthmodbus_get_header_length(ctx);rcmodbus_connect(ctx);if(rc-1){modbus_free(ctx);vTaskDelete(NULL);}/* 主循环与 TCP 任务结构完全对称 */for(;;){do{rcmodbus_receive(ctx,query);}while(rc0);if(rc0){/* RTU 出错直接跳过等待下一帧 */continue;}/* 获取互斥锁保护寄存器访问 */xSemaphoreTake(g_mb_lock,portMAX_DELAY);errprocess_emergency_cmd(ctx,query,rc,mb_mapping);if(err){modbus_reply_exception(ctx,query,MODBUS_EXCEPTION_SLAVE_OR_SERVER_BUSY);xSemaphoreGive(g_mb_lock);continue;}errprocess_file_record(query[header_length-1],rc);if(err){modbus_reply_exception(ctx,query,MODBUS_EXCEPTION_SLAVE_OR_SERVER_BUSY);xSemaphoreGive(g_mb_lock);continue;}rcmodbus_reply(ctx,query,rc,mb_mapping);/* 释放互斥锁 */xSemaphoreGive(g_mb_lock);}modbus_mapping_free(mb_mapping);vPortFree(query);modbus_close(ctx);modbus_free(ctx);vTaskDelete(NULL);}解析RTU 任务的逻辑与 TCP 任务完全对称——都是收包 → 加锁 → 处理 → 回复 → 解锁的循环。区别只在于底层的通信接口RTU 用 USB 串口TCP 用 WiFi。RTU 出错时不需要重连直接continue等下一帧即可因为串口连接是持久的。六、两个任务的协作关系维度TCP 任务RTU 任务通信接口WiFi / W800 模块USB 串口寄存器来源创建并初始化g_mb_mapping直接使用g_mb_mapping互斥锁创建g_mb_lock直接使用g_mb_lock出错处理modbus_tcp_accept等待重连continue跳过等待下一帧任务创建顺序先启动负责全局初始化由 TCP 任务拉起两个任务通过全局互斥锁g_mb_lock实现对mb_mapping的互斥访问任何时刻只有一个任务能持有锁、操作寄存器从而保证数据一致性。七、总结双协议兼容的核心共享mb_mapping用全局互斥锁保护访问初始化顺序TCP 任务负责创建所有共享资源再拉起 RTU 任务避免竞态对称的任务结构两个任务主循环逻辑完全一致仅底层通信接口不同数据格式适配RTU/TCP 帧头差异由header_length动态处理上层逻辑无感知经过本篇改造系统已经能够上位机通过 USB 串口RTU读写寄存器上位机通过 WiFiTCP读写同一套寄存器结合写文件功能支持远程 IAP 升级八、结尾本篇完成了 RTU/TCP 双协议并行的核心设计至此整个工业物联网设备管理系统的通信层已经全部打通。下一篇将学习MQTT 协议并尝试将相关代码移植到 FreeRTOS敬请期待Hello_Embed继续带你从原理到实践掌握嵌入式上位机开发的核心技能敬请关注
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2517660.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!