当前位置: 首页 > news >正文

modbus轮询实现

/* ============================

  • 文件:modbus_reg.h
  • ============================ */
#ifndef __MODBUS_REG_H__
#define __MODBUS_REG_H__#include <stdint.h>
#include <modbus.h>
#include <pthread.h>extern pthread_mutex_t reg_lock;typedef enum {MB_HOLDING,MB_INPUT,MB_COIL,MB_DISCRETE
} mb_reg_type_t;typedef enum {REG_RO,REG_RW
} reg_attr_t;typedef struct {uint8_t         slave_id;uint16_t        addr;mb_reg_type_t   type;reg_attr_t      attr;uint16_t        length;uint32_t        poll_ms;void           *data;void           *shadow;   /* 用于RW寄存器变化检测 */uint16_t        data_size;
} modbus_reg_t;extern modbus_reg_t g_modbus_regs[];
extern int g_modbus_reg_num;#endif

/* ============================

  • 文件:modbus_reg.c
  • ============================ */
#include "modbus_reg.h"pthread_mutex_t reg_lock = PTHREAD_MUTEX_INITIALIZER;uint16_t reg_voltage;
uint16_t reg_current;
uint16_t reg_enable;
uint16_t reg_enable_shadow;modbus_reg_t g_modbus_regs[] = {/* 输入寄存器:轮询 */
{ .slave_id = 1, .addr = 0x0001, .type = MB_INPUT, .attr = REG_RO, .length = 1,.poll_ms = 1000, .data = &reg_voltage, .shadow = NULL, .data_size = sizeof(reg_voltage) },
{ .slave_id = 1, .addr = 0x0002, .type = MB_INPUT, .attr = REG_RO, .length = 1,.poll_ms = 1000, .data = &reg_current, .shadow = NULL, .data_size = sizeof(reg_current) },/* 保持寄存器:可读可写 */
{ .slave_id = 1, .addr = 0x0100, .type = MB_HOLDING, .attr = REG_RW, .length = 1,.poll_ms = 1000, .data = &reg_enable, .shadow = &reg_enable_shadow, .data_size = sizeof(reg_enable) },
};int g_modbus_reg_num = sizeof(g_modbus_regs) / sizeof(g_modbus_regs[0]);

/* ============================

  • 文件:modbus_service.h
  • ============================ */
#ifndef __MODBUS_SERVICE_H__
#define __MODBUS_SERVICE_H__#include <stdint.h>
#include <modbus.h>
#include "modbus_reg.h"int modbus_service_init(modbus_t *ctx);
void modbus_poll_once(modbus_t *ctx);#endif/* ============================* 文件:modbus_service.c* ============================ */
#include <string.h>
#include <stdio.h>
#include <time.h>
#include "modbus_service.h"static uint64_t get_ms(void)
{struct timespec ts;clock_gettime(CLOCK_MONOTONIC, &ts);return (uint64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
}static uint64_t last_poll[32];int modbus_service_init(modbus_t *ctx)
{memset(last_poll, 0, sizeof(last_poll));return 0;
}// static int read_one(modbus_t *ctx, modbus_reg_t *r)
// {
//     uint16_t buf[16];
//     int rc;//     modbus_set_slave(ctx, r->slave_id);//     switch (r->type) {
//     case MB_INPUT:
//         rc = modbus_read_input_registers(ctx, r->addr, r->length, buf);
//         break;
//     case MB_HOLDING:
//         rc = modbus_read_registers(ctx, r->addr, r->length, buf);
//         break;
//     default:
//         return -1;
//     }//     if (rc != r->length)
//         return -1;//     memcpy(r->data, buf, r->data_size);
//     return 0;
// }//加锁
static int read_one(modbus_t *ctx, modbus_reg_t *r)
{uint16_t buf[16];int rc;modbus_set_slave(ctx, r->slave_id);switch (r->type) {case MB_INPUT:rc = modbus_read_input_registers(ctx, r->addr, r->length, buf);break;case MB_HOLDING:rc = modbus_read_registers(ctx, r->addr, r->length, buf);break;default:return -1;}if (rc != r->length)return -1;pthread_mutex_lock(&reg_lock);memcpy(r->data, buf, r->data_size);pthread_mutex_unlock(&reg_lock);return 0;
}static int write_one(modbus_t *ctx, modbus_reg_t *r)
{modbus_set_slave(ctx, r->slave_id);return modbus_write_register(ctx, r->addr, *(uint16_t *)r->data);
}// void modbus_poll_once(modbus_t *ctx)
// {
//     uint64_t now = get_ms();//     for (int i = 0; i < g_modbus_reg_num; i++) {
//         modbus_reg_t *r = &g_modbus_regs[i];//         /* 轮询 */
//         if (r->poll_ms && now - last_poll[i] >= r->poll_ms) {
//             if (read_one(ctx, r) == 0) {
//                 printf("[MB] read 0x%04X = %d\n", r->addr, *(uint16_t *)r->data);
//             }
//             last_poll[i] = now;
//         }//         /* RW寄存器变化检测并写下去 */
//         if (r->attr == REG_RW && r->shadow) {
//             if (memcmp(r->data, r->shadow, r->data_size) != 0) {
//                 if (write_one(ctx, r) == 1) {
//                     memcpy(r->shadow, r->data, r->data_size);
//                     printf("[MB] write 0x%04X = %d\n", r->addr, *(uint16_t *)r->data);
//                 }
//             }
//         }
//     }
// }//加锁
void modbus_poll_once(modbus_t *ctx)
{uint64_t now = get_ms();for (int i = 0; i < g_modbus_reg_num; i++) {modbus_reg_t *r = &g_modbus_regs[i];/* ---------- 轮询读 ---------- */if (r->poll_ms && now - last_poll[i] >= r->poll_ms) {if (read_one(ctx, r) == 0) {pthread_mutex_lock(&reg_lock);printf("[MB] read 0x%04X = %d\n",r->addr, *(uint16_t *)r->data);pthread_mutex_unlock(&reg_lock);}last_poll[i] = now;}/* ---------- 写检测(锁内只做判断) ---------- */uint16_t write_val = 0;int need_write = 0;if (r->attr == REG_RW && r->shadow) {pthread_mutex_lock(&reg_lock);if (memcmp(r->data, r->shadow, r->data_size) != 0) {write_val = *(uint16_t *)r->data;need_write = 1;}pthread_mutex_unlock(&reg_lock);}/* ---------- 真正写 Modbus(锁外) ---------- */if (need_write) {if (write_one(ctx, r) == 1) {pthread_mutex_lock(&reg_lock);memcpy(r->shadow, &write_val, r->data_size);pthread_mutex_unlock(&reg_lock);printf("[MB] write 0x%04X = %d\n", r->addr, write_val);}}}
}

/* ============================

  • 文件:main.c
  • ============================ */
#include <stdio.h>
#include <unistd.h>
#include <modbus.h>
#include "modbus_service.h"int main(void)
{modbus_t *ctx = modbus_new_rtu("/dev/ttyS1", 115200, 'N', 8, 1);if (!ctx)return -1;modbus_set_response_timeout(ctx, 0, 300000);if (modbus_connect(ctx) == -1) {perror("modbus_connect");return -1;}modbus_service_init(ctx);while (1) {modbus_poll_once(ctx);usleep(10 * 1000);}
}

/* ============================

  • 文件:lv_xx.c
  • ============================ */
pthread_mutex_lock(&reg_lock); 
reg_enable = 1; 
pthread_mutex_unlock(&reg_lock);
http://www.jsqmd.com/news/585832/

相关文章:

  • 终极指南:3分钟掌握LeaguePrank打造专属英雄联盟形象
  • initramfs及rpm/dracut操作
  • OpenClaw定时任务详解:Qwen3-32B-Chat镜像实现凌晨数据备份
  • SimpleBar在Vue应用中的终极实践指南:打造完美滚动体验的10个技巧
  • Windows远程桌面使用微软账户连接提示你的凭证不工作问题解决
  • 2026年大理民宿运营指导排名,半亩酒店管理性价比高获认可 - 工业推荐榜
  • 百联OK卡回收的隐藏技巧:提升价值的实用方法 - 团团收购物卡回收
  • Android Studio中文语言包:构建无缝本地化开发环境的完整指南
  • 藏在网络深处的宝藏:爱娃子博客,最具温度的优秀个人独立博客
  • 无需显卡和命令行!Ollama图形界面部署Llama-3.2-3B全流程
  • Pixel Dream Workshop 游戏开发应用:快速生成游戏角色与场景原画
  • 无人机数据分析终极指南:UAV Log Viewer 免费开源工具完全解析
  • 3步攻克B站直播推流限制:让第三方工具适配效率提升70%
  • 瑞祥卡在哪里可以使用?获取使用范围及回收方法! - 团团收购物卡回收
  • 细聊民宿运营管理服务价格,丽江半亩酒店管理费用多少钱? - myqiye
  • 实战指南:基于快马平台深度开发,构建企业级workbuddy团队项目管理看板
  • 从RTCM2到RTCM3e:一文搞懂RTKLib差分数据兼容性设计与扩展开发
  • 告别适配烦恼:v-scale-screen实现Vue大屏自适应的终极方案
  • 智能文档聚合系统:自动化构建企业知识库的完整方案
  • B站m4s格式转MP4完全指南:从格式解析到跨设备播放全攻略
  • 网络安全攻防战:由 Agent 驱动的自动化渗透测试
  • OpenClaw+Qwen3.5-9B:非程序员如何搞定邮件自动化
  • WinAsar:3分钟搞定Electron asar文件,告别繁琐命令行的终极方案
  • Hexo-Theme-Matery主题终极移动端适配与优化指南:打造完美的响应式博客体验
  • FreeGPT-WebUI网络搜索功能终极指南:如何获取实时AI对话体验
  • FPGA开发:音乐播放器
  • 05:输出保留12位小数的浮点数
  • 从零开始构建P2P视频分发网络:PCDN实战指南
  • 盘点2026年徐州能做一般纳税人升级的好用代理记账公司 - 工业品牌热点
  • 如何通过脚本化工作流突破Adobe Illustrator的效率瓶颈?