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

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;
#define LED_PATH "/sys/class/leds/91x:led1/brightness"
#define LED_CONTROL_REGISTER 10
int prev_led_state = -1;
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);
}
control_led(0);
printf("从机程序终止\n");
exit(0);
}
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;
if (function_code == 6 && start_addr == LED_CONTROL_REGISTER) {
value = (req[4] << 8) | req[5];
printf("*** 寄存器10 LED控制:值=%d ***\n", value);
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);
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;
}
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;
}
mb_mapping->tab_registers[LED_CONTROL_REGISTER] = 0;
control_led(0);
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) {
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);
}
}
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寄存器控制地址

开发板效果