米尔电子
直播中

jf_46710756

2年用户 20经验值
擅长:嵌入式技术
私信 关注
[技术]

【米尔NXP i.MX 91开发板评测】基于RS485接口实现modbus从机

介绍

RS485接口在工业、物联网领域应用广泛,工业自动化领域一般在RS485基础上移植modbus协议,i.MX91开发板上集成1路RS485接口,我们可以用来进行数据采集和控制

img

libmodus

拷贝开源仓库

git clone https://github.com/stephane/libmodbus.git

交叉编译

source ~/myd-lmx91-toolchain/environment-setup-armv8a-poky-linux
 ​
 ./configure \
     --host=aarch64-poky-linux \
     --prefix=/usr/local \
     --disable-static \
     --enable-shared
     
 
make install DESTDIR=$(pwd)/install-new

modbus通信

从机

编写RS485从机

#include <modbus.h>
 #include <stdio.h>
 #include <unistd.h>
 #include <signal.h>
 #include <errno.h>
 #include <stdlib.h>
 #include <fcntl.h>
 #include <string.h>modbus_t *ctx = NULL;
 modbus_mapping_t *mb_mapping = NULL;
 ​
 // LED控制相关定义
 #define LED_PATH "/sys/class/leds/91x:led1/brightness"
 #define LED_CONTROL_REGISTER 10    // LED控制寄存器地址// 全局变量
 int prev_led_state = -1;  // 记录上一次的LED状态// LED控制函数
 int control_led(int value) {
     FILE *led_file = fopen(LED_PATH, "w");
     if (led_file == NULL) {
         perror("Failed to open LED file");
         return -1;
     }
     
     int ret = fprintf(led_file, "%d", value ? 1 : 0);
     if (ret < 0) {
         perror("Failed to write to LED file");
     }
     fclose(led_file);
     
     printf("LED control: writing %d to %s (result: %s)\n", 
            value, LED_PATH, ret < 0 ? "FAILED" : "SUCCESS");
     
     return ret < 0 ? -1 : 0;
 }
 ​
 void signal_handler(int sig) {
     if (ctx != NULL) {
         modbus_close(ctx);
         modbus_free(ctx);
     }
     
     if (mb_mapping != NULL) {
         modbus_mapping_free(mb_mapping);
     }
     
     // 确保 LED 关闭
     control_led(0);
     
     printf("从机程序终止\n");
     exit(0);
 }
 ​
 // 简化的 modbus_reply 函数,只处理LED控制
 int modbus_reply_simple(modbus_t *ctx, uint8_t *req, int req_length) {
     int ret;
     
     // 解析请求类型和地址
     uint8_t function_code = req[1];
     uint16_t start_addr = (req[2] << 8) | req[3];
     uint16_t value = 0;
     
     // 寄存器10 LED控制
     if (function_code == 6 && start_addr == LED_CONTROL_REGISTER) {
         // 功能码6:写单个寄存器
         value = (req[4] << 8) | req[5];
         printf("*** 寄存器10 LED控制:值=%d ***\n", value);
         
         // 直接控制LED
         if (control_led(value ? 1 : 0) == 0) {
             printf("LED控制成功:%s\n", value ? "ON" : "OFF");
             // 更新寄存器值
             mb_mapping->tab_registers[LED_CONTROL_REGISTER] = value;
             prev_led_state = value ? 1 : 0;
         } else {
             printf("LED控制失败\n");
         }
     }
     
     // 执行标准回复
     ret = modbus_reply(ctx, req, req_length, mb_mapping);
     
     return ret;
 }
 ​
 int main() {
     // 注册信号处理函数
     signal(SIGINT, signal_handler);
     signal(SIGTERM, signal_handler);
 ​
     // 初始化 Modbus RTU 上下文
     ctx = modbus_new_rtu("/dev/ttyLP2", 9600, 'N', 8, 1);
     if (ctx == NULL) {
         fprintf(stderr, "[从机] 无法创建 Modbus 上下文: %s\n", modbus_strerror(errno));
         return -1;
     }
 ​
     // 设置从机地址
     modbus_set_slave(ctx, 1);
 ​
     // 设置响应超时
     modbus_set_response_timeout(ctx, 5, 0);
 ​
     // 建立连接
     if (modbus_connect(ctx) == -1) {
         fprintf(stderr, "[从机] 连接失败: %s\n", modbus_strerror(errno));
         modbus_free(ctx);
         return -1;
     }
 ​
     // 初始化 Modbus 映射
     mb_mapping = modbus_mapping_new(
         0,      // 离散输入数量
         0,      // 线圈数量
         LED_CONTROL_REGISTER + 1,  // 保持寄存器数量
         0       // 输入寄存器数量
     );
     
     if (mb_mapping == NULL) {
         fprintf(stderr, "[从机] 无法创建寄存器映射: %s\n", modbus_strerror(errno));
         modbus_close(ctx);
         modbus_free(ctx);
         return -1;
     }
 ​
     printf("[从机] 寄存器映射创建成功,保持寄存器数量: %d\n", LED_CONTROL_REGISTER + 1);
 ​
     // 初始化保持寄存器值
     for (int i = 0; i < 10; i++) {
         mb_mapping->tab_registers[i] = i * 10;
     }
     
     // 初始化LED控制寄存器
     mb_mapping->tab_registers[LED_CONTROL_REGISTER] = 0;
     control_led(0);  // 初始状态关闭LED
     prev_led_state = 0;
 ​
     printf("[从机] 启动(使用/dev/ttyLP2),从机地址:1,等待请求...\n");
     printf("LED控制寄存器地址: %d\n", LED_CONTROL_REGISTER);
 ​
     // 主循环处理请求
     while (1) {
         uint8_t query[MODBUS_RTU_MAX_ADU_LENGTH];
         
         int rc = modbus_receive(ctx, query);
         
         if (rc > 0) {
             // 解析Modbus请求类型和地址
             if (rc >= 8) {
                 uint8_t slave_id = query[0];
                 uint8_t function_code = query[1];
                 uint16_t start_addr = (query[2] << 8) | query[3] + 1;
                 uint16_t count = (query[4] << 8) | query[5];
                 
                 printf("Modbus请求解析: 从机ID=%d, 功能码=%d, 起始地址=%d, 数量=%d\n", 
                        slave_id, function_code, start_addr, count);
             }
             
             // 使用简化的回复函数
             modbus_reply_simple(ctx, query, rc);
         } else if (rc == -1) {
             fprintf(stderr, "[从机] 接收错误: %s\n", modbus_strerror(errno));
             usleep(100000);  // 100ms
         }
     }
 ​
     return 0;
 }

主机

使用modpoll给从机发送LED控制指令

while true; do sudo ./modpoll -m rtu -a 1 -r 10 -c 1 -t 4 -1 -b 9600 -p none /dev/ttyUSB0 1; sleep 1; sudo ./modpoll -m rtu -a 1 -r 10 -c 1 -t 4 -1 -b 9600 -p none /dev/ttyUSB0 0; sleep 1; done;

测试效果

modbus从机(开发板)日志,10为LED寄存器控制地址

img

开发板效果

modbus

更多回帖

发帖
×
20
完善资料,
赚取积分