RT-Thread 工业屏驱动开发实战:UART 串口屏协议解析 + 数据实时刷新 + 设备驱动框架完整实现

前言

在嵌入式工业控制项目中,串口显示屏常用于实时展示设备状态、IO 电平、电压电流、网络 IP 等信息。本文基于 RT-Thread 实时操作系统 ,实现一套标准化、模块化、可维护 的串口屏驱动方案。该方案通过 UART 串口发送自定义协议指令 控制屏幕刷新,支持 IO 状态、数字电位计、电压电流、IP 地址等多类型数据实时显示,并通过 RT-Thread 设备驱动框架 完成注册,支持多线程安全访问、命令行测试、动态数据刷新。

一、整体架构设计

本屏幕驱动采用 分层 + 模块化 设计,结构清晰、易于维护与扩展。

1. 系统架构分层

  1. 硬件层

    • UART5 串口与屏幕通信
    • 屏幕通过固定指令协议(0x5A 0xA5 开头)控制显示
  2. 驱动层(核心)

    • 串口初始化、波特率配置
    • 协议封装:界面切换、数据写入、清空、多数据写入
    • IP 地址转 GBK 十六进制编码发送
  3. 服务层

    • 数据采集:IO、电位计、电压电流、IP
    • 数据处理:二进制转换、格式整理、防重复刷新
    • 线程定时刷新
  4. 接口层

    • 注册为 RT-Thread 标准设备
    • 提供 control 接口供外部获取状态
    • MSH 命令行测试工具

2. 核心模块划分

  • 串口通信模块:put_char 发送单字节,底层数据发送
  • 协议指令模块:screen_write / screen_write_more / screen_clean
  • 数据处理模块:IO 转 8 位二进制、IP 转 GBK 十六进制
  • 刷新线程模块:lcd_refresh_data 定时刷新所有页面
  • 设备驱动框架:注册 display 设备,提供标准控制接口
  • 调试测试模块:display 命令支持状态查看与调试

3. 运行流程

  1. 系统启动 → 自动初始化串口 → 创建刷新线程
  2. 延时 3s 等待屏幕上电稳定
  3. 定时循环刷新:DI/DO 状态 → 电位计 → 电压电流 → IP 地址
  4. 外部可通过设备接口或 MSH 命令读取 / 测试显示状态

二、现有代码亮点(优势)

  1. RT-Thread 标准设备驱动模型符合系统设备框架,可通过 device 查找访问,通用性强。

  2. 多线程安全 + 互斥锁保护data_mutex 保证多线程同时操作屏幕不出现乱码。

  3. **IP 地址自动刷新(防重复发送)**仅当 IP 变化时才刷新,节省串口资源。

  4. 协议高度封装screen_write /clean/write_more 支持快速扩展显示内容。

  5. IO 状态自动转 8 位二进制直接显示 DI/DO 每一位状态,工业场景非常实用。

  6. MSH 命令行测试完善支持查看 IO、电位计、电压、IP、debug 开关。

  7. 自动初始化机制INIT_APP_EXPORT / INIT_DEVICE_EXPORT 开机自动运行。

三、现存可改进点(优化建议)

1. 架构可优化

  • 协议指令硬编码较多,可建立协议枚举结构体统一管理
  • 屏幕地址(0x1000、0x1016 等)可定义宏,便于维护
  • 数据刷新顺序固定,可支持多页面切换显示

2. 稳定性可优化

  • 缺少发送超时机制,阻塞发送可能影响系统实时性
  • 缺少串口发送完成回调,高波特率大量数据可能丢包
  • 缺少看门狗 / 异常重启机制

3. 功能可优化

  • 屏幕无返回数据解析,无法判断屏幕是否在线
  • 无亮度、背光、休眠控制
  • 无错误重发机制
  • 不支持动态配置页面刷新间隔

4. 代码规范可优化

  • 部分函数参数较多,可封装为结构体
  • 魔法数字较多,建议宏定义替换
  • 函数注释可更规范化(Doxygen)

四、后续优化方向(扩展规划)

1. 构建协议解析引擎

  • 屏幕指令统一管理
  • 支持自动校验和、超时重发

2. 多页面动态切换机制

  • 菜单切换
  • 页面自动轮询
  • 按键触发页面跳转

3. 屏幕状态反馈

  • 接收屏幕返回数据
  • 检测屏幕在线状态
  • 异常日志上报

4. 增加配置化驱动

  • 可通过配置文件定义显示地址
  • 支持不同型号屏幕适配

5. 增加日志与诊断

  • 显示异常统计
  • 发送失败重试
  • 串口丢包检测

6. 支持远程更新显示内容

  • 结合网络、MQTT、WebSocket 远程更新屏幕
  • 适合工业远程监控、运维面板

五、代码及实战

首先根据迪文显示屏协议显示以下内容

1.怎么显示2张图片,并使用图片切换指令

5A A5 07 82 00 84 5A 01 00 01

2.DI命令显示

5a a5 13 82 10 00 00 01 00 02 00 03 00 04 00 05 00 06 00 07 00 08

3.ip地址显示

192.168.255.255的ASCII码显示查询

汉字字符集编码查询;中文字符集编码:GB2312、BIG5、GBK、GB18030、Unicode

5a a5 12 82 19 80 3139322E3136382E3235352E323535

4.两点之间的线段显示

5A A5 13 82 5440 000A 0001 0000 0010 0000 01C7 010E FF00

代码

cpp 复制代码
/*
 * @Descripttion: 显示屏驱动
 * @version: v 1.00
 * @Author: zss
 * @Date: 2020-09-03 15:21:35
 * @LastEditors: zss
 * @LastEditTime: 2020-09-24 17:23:28
 */
#include "stdio.h"
#include "math.h"
#include "display.h"
#include "common.h"
#include <string.h>
#include <stdarg.h>
#include <drv_log.h>



#define DEVICW_NAME "display"
#define UART_NAME "uart5"
#define THREAD_STACK_SIZE 2*1024
#define THREAD_PRIORITY 18
#define THREAD_TICK 30
#define BUFFER_SIZE 128
#define INIT_DELAY_MS 3000  // 延时3秒
#define REFRESH_INTERVAL 300

extern sys_config_t sys_config_value;
static rt_device_t serial;
static rt_uint8_t display_debug = 0;
static rt_uint8_t display_page = 0;
static char previous_ip_str[16] = {0};
static rt_mutex_t data_mutex;

static io_status_t io_info = {0};
static do_di_status_t do_di_info = {0};
static digital_pot_status_t dp_info = {0};
static vol_cur_info_t vol_cur_info = {0};
//定义底层函数:发送和接收
static void put_char(uint8_t ch)
{
    if(display_debug)
    {
      rt_kprintf("Sending byte: 0x%02X\n", ch);
    }
    rt_device_write(serial, 0, &ch, 1);
}

/************************************************************* 
函数功能:切换到界面1
*************************************************************/
static void switch_interface(void)
{
    put_char(0x5A);
    put_char(0xA5);
    put_char(0x07);
    put_char(0x82);
    put_char(0x00);
    put_char(0x84);
    put_char(0x5A);
    put_char(0x01);
    put_char(0x00);
    put_char(0x01);
}

static void screen_display1(void)
{
    put_char(0x5A);
    put_char(0xA5);
    put_char(0x07);
    put_char(0x82);
    put_char(0x10);
    put_char(0x00);
  
    put_char(0x00);
    put_char(0x03);
    put_char(0x00);
    put_char(0x05);
}

static void screen_display2(void)
{
    put_char(0x5A);
    put_char(0xA5);
    put_char(0x05);
    put_char(0x82);
    put_char(0x10);
    put_char(0x00);
    put_char(0x00);
    put_char(0x02);
}

static void screen_display3(void)
{
    put_char(0x5A);
    put_char(0xA5);
    put_char(0x0F);
    put_char(0x82);
    put_char(0x19);
    put_char(0x80);
  
    put_char(0x31);
    put_char(0x39);
    put_char(0x32);
    put_char(0x2E);
  
    put_char(0x31);
    put_char(0x36);
    put_char(0x38);
    put_char(0x2E);
    put_char(0x33);
    put_char(0x2E);
  
    put_char(0x31);
    put_char(0x30);

}

// 打印8位二进制的函数
void print_binary_8bit(uint8_t value, uint8_t *bin_array)
{
    for (int i = 7; i >= 0; i--) {
        bin_array[7 - i] = (value & (1 << i)) ? 1 : 0;
    }
}
/*根据地址写入相应的变量*/
static void screen_write(int address, unsigned short value) {
    put_char(0x5A);
    put_char(0xA5);
    put_char(0x05);
    put_char(0x82);
    put_char((address >> 8) & 0xFF);
    put_char(address & 0xFF);
    put_char((value >> 8) & 0xFF);  // 高字节
    put_char(value & 0xFF);        // 低字节
}
/*根据地址写入相应的多变量*/
static void screen_write_more(int address, int num_values, ...) {
    int total_bytes = num_values * 2 + 3;
    
    put_char(0x5A);
    put_char(0xA5);
    put_char(total_bytes & 0xFF);  // Length byte
    put_char(0x82);  // Command byte
    
    // Address processing
    put_char((address >> 8) & 0xFF);  // Higher byte
    put_char(address & 0xFF);         // Lower byte
    
    va_list args;
    va_start(args, num_values);

    for (int i = 0; i < num_values; i++) {
        unsigned short value = va_arg(args, int);
        put_char((value >> 8) & 0xFF);
        put_char(value & 0xFF);
    }
    va_end(args);
}

/* 清空文本地址数据 */
static void screen_clean(int address, int num_bytes) 
{
    int total_bytes = 3 + (num_bytes * 2); // 每个字节对应2个0x20 
    
    put_char(0x5A);
    put_char(0xA5);
    put_char(total_bytes & 0xFF);          // 长度低位
    put_char(0x82);                        // 命令字节
    
    // 3. 发送地址(大端序)
    put_char((address >> 8) & 0xFF);       // 地址高字节 
    put_char(address & 0xFF);              // 地址低字节 
    
    // 4. 插入指定数量的0x20(每个数据字节对应2个)
    for (int i = 0; i < num_bytes * 2; i++) {
        put_char(0x20);                    // 插入空格字符 
    }
}
/**
 * @brief 将IP地址字符串转换为GBK编码的十六进制字符串 
 * @param ip IP地址字符串(如 "192.168.3.10")
 * @return 十六进制字符串(需调用者释放内存),失败返回NULL 
 */
char *ip_to_gbk_hex(const char *ip) {
    if (ip == NULL) {
        return NULL;
    }
 
    // 计算GBK编码后的字节长度(数字和点均为单字节)
    int gbk_len = strlen(ip); 
    char *gbk_hex = rt_malloc(gbk_len * 2 + 1); // 每个字节转为2字符十六进制 + 终止符 
    if (gbk_hex == NULL) {
        rt_kprintf("Memory allocation failed!\n");
        return NULL;
    }
 
    // 逐字节转换为十六进制(GBK编码下数字/符号与ASCII相同)
    for (int i = 0; i < gbk_len; i++) {
        unsigned char byte = (unsigned char)ip[i];
        rt_snprintf(gbk_hex + i * 2, 3, "%02X", byte); // 格式化为大写十六进制 
    }
    gbk_hex[gbk_len * 2] = '\0'; // 确保字符串终止 
   if(display_debug)
   {
     rt_kprintf("IP: %s -> GBK Hex: %s\n", ip, gbk_hex);
   }
    return gbk_hex;
}
 
/* 测试函数 */
/**
 * @brief 测试函数:打印IP的GBK十六进制编码(参数从argv传入)
 * @param ip_str 待转换的IP地址字符串 
 */
char* test_ip_to_gbk_hex(const char *ip_str) {
    char *hex_str = ip_to_gbk_hex(ip_str);
    if (hex_str != NULL) {
        rt_kprintf("IP: %s -> GBK Hex: %s\n", ip_str, hex_str);
        return hex_str;  // 返回十六进制字符串
    }
    return NULL;
}

// 辅助函数:将十六进制字符转换为数值
unsigned char char_to_hex_val(char c) {
    if (c >= '0' && c <= '9') 
        return c - '0';
    else if (c >= 'A' && c <= 'F') 
        return c - 'A' + 10;
    else if (c >= 'a' && c <= 'f') 
        return c - 'a' + 10;
    return 0; // 非十六进制字符
}

// 优化后的IP协议发送函数
void send_ip_address(sys_config_t *config, int address) {
    // 1. 格式化为点分十进制IP字符串 
    char ip_str[16];
    rt_snprintf(ip_str, sizeof(ip_str), "%d.%d.%d.%d", 
               config->LocalIP1[0], config->LocalIP1[1], 
               config->LocalIP1[2], config->LocalIP1[3]);
 
    // 2. 转换为GBK十六进制字符串 
    char *gbk_hex = ip_to_gbk_hex(ip_str);
    if (!gbk_hex) {
        rt_kprintf("Error: GBK hex conversion failed!\n");
        return;
    }
    int gbk_hex_len = strlen(gbk_hex); // 实际长度(如"313932...")
    //rt_kprintf("Inserted GBK hex data length: %d bytes\n", gbk_hex_len);
    // 3. 计算协议参数 
    int num_values = (gbk_hex_len) / 2; // 16位值的数量(向上取整)
    //rt_kprintf("Inserted GBK hex data num_values length: %d bytes\n", num_values);
    int total_bytes = num_values + 3;   // 协议总长度(数据+3字节头)
 
    // 4. 构建协议报文
    // --- 报文头 ---
    put_char(0x5A);
    put_char(0xA5);
    put_char(total_bytes & 0xFF);          // 长度低位 
    put_char(0x82);                        // 命令字节 
 
    // --- 地址部分 ---
    put_char((address >> 8) & 0xFF);       // 地址高字节 
    put_char(address & 0xFF);              // 地址低字节 
 
    //将每两个十六进制字符转换为一个字节值 ---
    for (int i = 0; i < gbk_hex_len; i += 2) {
        // 获取两个连续的十六进制字符
        char high_char = gbk_hex[i];
        char low_char = gbk_hex[i+1];
        // 将字符转换为对应的数值
        unsigned char high_val = char_to_hex_val(high_char);
        unsigned char low_val = char_to_hex_val(low_char);
        // 组合成一个字节值 (高4位 + 低4位)
        unsigned char byte_val = (high_val << 4) | low_val;
        put_char(byte_val);
    }
    // 5. 释放内存 
    rt_free(gbk_hex);
}

static void process_io_info_and_display(io_status_t *io_info,do_di_status_t *do_di_info)
{
    get_io_info(io_info);
    
    // 转换并存储为二进制
    print_binary_8bit(io_info->do_state, do_di_info->do_state_bin);
    print_binary_8bit(io_info->do_status, do_di_info->do_status_bin);
    print_binary_8bit(io_info->di_status, do_di_info->di_status_bin);
    
    // 写入屏幕
    screen_write_more(0x1000, 8,
                      do_di_info->di_status_bin[7-0], do_di_info->di_status_bin[7-1], do_di_info->di_status_bin[7-2],
                      do_di_info->di_status_bin[7-3], do_di_info->di_status_bin[7-4], do_di_info->di_status_bin[7-5],
                      do_di_info->di_status_bin[7-6], do_di_info->di_status_bin[7-7]);
    screen_write_more(0x1008, 8,
                      do_di_info->do_status_bin[7-0], do_di_info->do_status_bin[7-1], do_di_info->do_status_bin[7-2],
                      do_di_info->do_status_bin[7-3], do_di_info->do_status_bin[7-4], do_di_info->do_status_bin[7-5],
                      do_di_info->do_status_bin[7-6], do_di_info->do_status_bin[7-7]);
}

static void process_gt_info_and_display(digital_pot_status_t  *dp_info)
{
    get_digital_pot_info(dp_info);
    //数字电位器的信息获取与屏幕展示
    screen_write(0x1016, dp_info->resistances[0]/10);
    screen_write(0x1020, dp_info->resistances[1]/10);
    screen_write(0x1025, dp_info->resistances[2]/10);
    screen_write(0x1030, dp_info->resistances[3]/10);
    screen_write(0x1035, dp_info->resistances[4]/10);
    screen_write(0x1040, dp_info->resistances[5]/10);
    screen_write(0x1045, dp_info->resistances[6]/10);
    screen_write(0x1050, dp_info->resistances[7]/10);
}

static void process_vol_cur_info_and_display(vol_cur_info_t *vol_cur_info)
{
      //电流电压
    get_vol_cur_info(vol_cur_info);
    screen_write(0x1060, vol_cur_info->vol_value); //电压mV 3000mv-12000mv
    screen_write(0x1065,vol_cur_info->cur_value/1000); //电流微安,如525964 0.5A
}

static void process_ip_info_and_display()
{
    char current_ip_str[16];
    rt_snprintf(current_ip_str, sizeof(current_ip_str), "%d.%d.%d.%d",
               sys_config_value.LocalIP1[0], sys_config_value.LocalIP1[1],
               sys_config_value.LocalIP1[2], sys_config_value.LocalIP1[3]);
      // 比较当前IP地址和之前记录的IP地址
    if (strcmp(current_ip_str, previous_ip_str) != 0)
    {
        screen_clean(0x1980, 8);
        // IP地址不同,发送协议
        send_ip_address(&sys_config_value, 0x1980);
        
        // 更新记录的IP地址
        strcpy(previous_ip_str, current_ip_str);
    }
}

static void lcd_refresh_data(void *parameter)
{
    // 等待显示屏上电初始化完成
    rt_thread_mdelay(INIT_DELAY_MS);
    // 函数指针数组,用于调用不同的处理函数
    void (*process_functions[])(void *, void *) = {
        (void (*)(void *, void *))process_io_info_and_display,
        (void (*)(void *, void *))process_gt_info_and_display,
        (void (*)(void *, void *))process_vol_cur_info_and_display,
        (void (*)(void *, void *))process_ip_info_and_display
    };

    // 参数数组,与函数指针数组对应
    void *params[] = {
        &io_info, &do_di_info,
        &dp_info, NULL,
        &vol_cur_info, NULL,
        NULL, NULL
    };

    while (1)
    {
        for (int i = 0; i < sizeof(process_functions) / sizeof(process_functions[0]); i++)
        {
            rt_mutex_take(data_mutex, RT_WAITING_FOREVER);
            process_functions[i](params[i * 2], params[i * 2 + 1]);
            rt_mutex_release(data_mutex);
            rt_thread_mdelay(REFRESH_INTERVAL);
        }
    }
}

int lcd_init(void)
{
  
    struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
    serial = rt_device_find(UART_NAME);
    config.baud_rate = BAUD_RATE_115200;
    config.data_bits = DATA_BITS_8;
    config.stop_bits = STOP_BITS_1;
    config.bufsz = 128;
    config.parity = PARITY_NONE;

    rt_device_control(serial, RT_DEVICE_CTRL_CONFIG, &config);
    rt_device_open(serial, RT_DEVICE_FLAG_INT_RX);
  
    rt_thread_t thread = rt_thread_create(DEVICW_NAME, lcd_refresh_data, RT_NULL, THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TICK);
    if (thread != RT_NULL)
    {
        rt_thread_startup(thread);
    }
    else
    {
        LOG_E("init %s thread failed", DEVICW_NAME);
    }
    return 0;
}
INIT_APP_EXPORT(lcd_init);

static struct rt_device device;
static rt_err_t rt_display_control(struct rt_device *dev, int cmd, void *arg)
{
    RT_ASSERT(dev != RT_NULL);
    if(arg == NULL)
      return -RT_ERROR;
    io_status_t  io_info = {0};
    //do_di_status_t do_di_info = {0};
    //digital_pot_status_t dp_info = {0};
    //vol_cur_info_t vol_cur_info = {0};
    do_di_status_t do_di_info2 = {0};
    switch (cmd)
    {
    case RT_DEVICE_DISPLAY_MODE:
        display_page = *(uint8_t*)arg;
        break;
    case RT_DEVICE_DISPLAY_DIDO:
        //process_io_info_and_display(&io_info,&do_di_info2);do_di_status_t
        rt_memcpy((do_di_status_t*)arg,&do_di_info,sizeof(do_di_status_t));
        break;
    case RT_DEVICE_DISPLAY_GT:
        //process_gt_info_and_display(&dp_info);
        rt_memcpy((digital_pot_status_t*)arg,&dp_info,sizeof(digital_pot_status_t));
        break;
    case RT_DEVICE_DISPLAY_VC:
        rt_memcpy((vol_cur_info_t*)arg,&vol_cur_info,sizeof(vol_cur_info_t));
        //process_vol_cur_info_and_display(&vol_cur_info);
        break;
    case RT_DEVICE_DISPLAY_IP:
        display_page = *(uint8_t*)arg;
        break;
    default:
        break;
    }
    return RT_EOK;
}

static rt_err_t rt_hw_device_register(rt_device_t device, const char *name, rt_uint32_t flag)
{
    RT_ASSERT(device != RT_NULL);

    device->init = RT_NULL;
    device->open = RT_NULL;
    device->close = RT_NULL;
    device->read = RT_NULL;
    device->write = RT_NULL;
    device->control = rt_display_control;
    device->type = RT_Device_Class_Miscellaneous;
    device->rx_indicate = RT_NULL;
    device->tx_complete = RT_NULL;
    device->user_data = RT_NULL;

    return rt_device_register(device, name, flag);
}

static int rt_hw_display_init(void)
{
    rt_err_t result;
    result = rt_hw_device_register(&device, DEVICW_NAME, RT_DEVICE_FLAG_RDWR);
    if (result != RT_EOK)
    {
        LOG_E("%s register err code: %d", DEVICW_NAME, result);
        return result;
    }
    LOG_I("%s register success", DEVICW_NAME);
    return RT_EOK;
}
INIT_DEVICE_EXPORT(rt_hw_display_init);

void display_test(int argc, char *argv[])
{
    if (argc == 1 || argc > 3)
    {
        rt_kprintf("Please check the command you entered!\r\n");
        goto __usage;
    }

    if (rt_strcmp(argv[1], "--help") == 0)
    {
        goto __usage;
    }else if(rt_strcmp(argv[1], "--io_info") == 0)
    {
//        io_status_t io_info = {0};
//        get_io_info(&io_info);
//        rt_kprintf("io_info.do_state: %d\r\n", io_info.do_state);
//        rt_kprintf("io_info.di_status: %d\r\n", io_info.di_status);
//        rt_kprintf("io_info.do_status: %d\r\n", io_info.do_status);

        // 创建一个新的结构体存储DO/DI的二进制值
        do_di_status_t do_di_info = {0};

        // 转换并存储为二进制
//        print_binary_8bit(io_info.do_state, do_di_info.do_state_bin);
//        print_binary_8bit(io_info.do_status, do_di_info.do_status_bin);
//        print_binary_8bit(io_info.di_status, do_di_info.di_status_bin);
        rt_device_control(serial, RT_DEVICE_DISPLAY_GT, &do_di_info);
        // 输出二进制
        rt_kprintf("do_di_info.do_state_bin: ");
        for (int i = 0; i < 8; i++) {
            rt_kprintf("byte[%d]=%d ", 7-i, do_di_info.do_state_bin[i]);
        }
        rt_kprintf("\r\n");

        rt_kprintf("do_di_info.do_status_bin: ");
        for (int i = 0; i < 8; i++) {
            rt_kprintf("byte[%d]=%d ", 7-i, do_di_info.do_status_bin[i]);
        }
        rt_kprintf("\r\n");

        rt_kprintf("do_di_info.di_status_bin: ");
        for (int i = 0; i < 8; i++) {
            rt_kprintf("byte[%d]=%d ", 7-i, do_di_info.di_status_bin[i]);
        }
        rt_kprintf("\r\n");
        
        rt_mutex_take(data_mutex, RT_WAITING_FOREVER);
        screen_write_more(0x1000, 8, do_di_info.di_status_bin[7], do_di_info.di_status_bin[6], do_di_info.di_status_bin[5],
                          do_di_info.di_status_bin[4], do_di_info.di_status_bin[3], do_di_info.di_status_bin[2], do_di_info.di_status_bin[1],
                          do_di_info.di_status_bin[0]);
        screen_write_more(0x1008, 8, do_di_info.do_status_bin[7], do_di_info.do_status_bin[6], do_di_info.do_status_bin[5],
                          do_di_info.do_status_bin[4], do_di_info.do_status_bin[3], do_di_info.do_status_bin[2], do_di_info.do_status_bin[1],
                          do_di_info.do_status_bin[0]);
        rt_mutex_release(data_mutex);
        return;
    }else if(rt_strcmp(argv[1], "--gt_info") == 0)
    {
        digital_pot_status_t dp_info = {0};
//        get_digital_pot_info(&dp_info);
//        rt_kprintf("dp_info.dev1_status: %d\r\n", dp_info.dev1_status);
//        rt_kprintf("dp_info.dev2_status: %d\r\n", dp_info.dev2_status);
//        for (int i = 0; i < 8; i++) {
//            float resistance_in_ohms = dp_info.resistances[i] / 1000.0;
//            int integer_part = (int)resistance_in_ohms;
//            int fractional_part = (int)((resistance_in_ohms - integer_part) * 100);
//            rt_kprintf("dp_info.levels[%d]:%d, dp_info.resistances[%d]:%d.%02d\r\n", 
//                       i, dp_info.levels[i], i, integer_part, fractional_part);
//        }
        rt_device_control(serial, RT_DEVICE_DISPLAY_GT, &dp_info);
        for (int i = 0; i < 8; i++) 
        {
          float resistance_in_ohms = dp_info.resistances[i] / 1000.0;
          int integer_part = (int)resistance_in_ohms;
          int fractional_part = (int)((resistance_in_ohms - integer_part) * 100);
          rt_kprintf("dp_info.levels[%d]:%d, dp_info.resistances[%d]:%d.%02d\r\n", 
                     i, dp_info.levels[i], i, integer_part, fractional_part);
        }
        rt_mutex_take(data_mutex, RT_WAITING_FOREVER);
        screen_write(0x1016, dp_info.resistances[0] / 10);
        screen_write(0x1020, dp_info.resistances[1] / 10);
        screen_write(0x1025, dp_info.resistances[2] / 10);
        screen_write(0x1030, dp_info.resistances[3] / 10);
        screen_write(0x1035, dp_info.resistances[4] / 10);
        screen_write(0x1040, dp_info.resistances[5] / 10);
        screen_write(0x1045, dp_info.resistances[6] / 10);
        screen_write(0x1050, dp_info.resistances[7] / 10);
        rt_mutex_release(data_mutex);
        return;
    }else if(rt_strcmp(argv[1], "--vol_cur") == 0)
    {
        vol_cur_info_t vol_cur_info = {0};
//        get_vol_cur_info(&vol_cur_info);
        rt_device_control(serial, RT_DEVICE_DISPLAY_VC, &vol_cur_info);
        rt_kprintf("vol_cur_info.state: %d\r\n", vol_cur_info.state);
        rt_kprintf("vol_cur_info.switch_flag: %d\r\n", vol_cur_info.switch_flag);
        rt_kprintf("vol_cur_info.vol_value: %d\r\n", vol_cur_info.vol_value);
        rt_kprintf("vol_cur_info.cur_value: %d\r\n", vol_cur_info.cur_value);
        rt_mutex_take(data_mutex, RT_WAITING_FOREVER);
        screen_write(0x1060, vol_cur_info.vol_value);
        screen_write(0x1065, vol_cur_info.cur_value / 1000);
        rt_mutex_release(data_mutex);
        return;
    }else if(rt_strcmp(argv[1], "--debug_on") == 0)
    {
        display_debug = 1;
        return;
    }else if(rt_strcmp(argv[1], "--debug_off") == 0)
    {
        display_debug = 0;
        return;
    }else if(rt_strcmp(argv[1], "--ip_info") == 0)
    {
        rt_mutex_take(data_mutex, RT_WAITING_FOREVER);
        send_ip_address(&sys_config_value, 0x1980);
        rt_mutex_release(data_mutex);
        return;
    }

__usage:
    rt_kprintf("Usage:\n");
    rt_kprintf("display --help              - printf help\r\n");
    rt_kprintf("display --io_info           - screen DI DO display effect detection\r\n");
    rt_kprintf("display --gt_info           - screen digital potentiometer display effect detection\r\n");
    rt_kprintf("display --vol_cur           - screen current and voltage display effect detection\r\n");
    rt_kprintf("display --ip_info           - display IP info\r\n");
    rt_kprintf("display --debug_on          - display debugging information open\r\n");
    rt_kprintf("display --debug_off         - display debugging information close\r\n");


    return;
}

/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT_ALIAS(display_test, display, display server.Help : display --help);
    // 参数校验:允许两种输入格式:
    // 1. display --set_ip 192.168.233.233 (argc=3)
    // 2. display --set_ip 192 168 233 233 (argc=6)
//    if (argc != 3 && argc != 6) {
//        rt_kprintf("Usage:\n");
//        rt_kprintf("  %s --set_ip <IP> (e.g. 192.168.233.233)\n", argv[0]);
//        rt_kprintf("  %s --set_ip <IP1> <IP2> <IP3> <IP4> (e.g. 192 168 233 233)\n", argv[0]);
//        return;
//    }
// 
//    char ip_str[16];
//    if (argc == 3) {
//        // 直接使用用户输入的完整IP字符串 
//        rt_strncpy(ip_str, argv[2], sizeof(ip_str));
//    } else {
//        // 组合分段的IP参数
//        rt_snprintf(ip_str, sizeof(ip_str), "%s.%s.%s.%s", argv[2], argv[3], argv[4], argv[5]);
//    }
// 
//    // 调用GBK转换函数并打印结果 
//    test_ip_to_gbk_hex(ip_str);

.h代码问题

cpp 复制代码
/*
 * @Descripttion: 配置参数驱动
 * @version: v 1.00
 * @Author: zss
 * @Date: 2020-09-03 15:21:35
 * @LastEditors: zss
 * @LastEditTime: 2020-09-24 17:23:28
 */
#ifndef __DISPLAY_H__
#define __DISPLAY_H__

#ifdef __cplusplus
extern "C"
{
#endif

#include <rtthread.h>
#include <rtdevice.h>
#include "drv_gpio.h"
#include "main_logic_proc.h"
#include "sys_config.h"


#define RT_DEVICE_DISPLAY_NAME					"display"
#define RT_DEVICE_DISPLAY_MODE          0x01
#define RT_DEVICE_DISPLAY_DIDO					0x02
#define RT_DEVICE_DISPLAY_GT					  0x03
#define RT_DEVICE_DISPLAY_VC					  0x04  
#define RT_DEVICE_DISPLAY_IP					  0x05

typedef struct {
    uint8_t do_state_bin[8];    // 存储do_state的8位二进制
    uint8_t do_status_bin[8];   // 存储do_status的8位二进制
    uint8_t di_status_bin[8];   // 存储di_status的8位二进制
    //uint32_t combined_di_value; // 用于存储DO/DI的合并值
} do_di_status_t;

//外部函数声明
int  get_io_info(io_status_t * p_io_info);
int  get_digital_pot_info(digital_pot_status_t * p_dps_info);
int  get_vol_cur_info(vol_cur_info_t * p_vol_cur_info);

#endif
#ifdef __cplusplus
}
#endif /*__DISPLAY_H__ */

六、总结

本文实现的 RT-Thread 串口屏驱动 是一套工业级可用、结构清晰、易于扩展的标准方案。它完成了:

  • 串口屏协议封装
  • 多类型数据采集与显示
  • 设备驱动框架化
  • 多线程安全刷新
  • 命令行调试工具

整体满足工业交互屏、状态监控屏、参数显示面板等场景需求。通过后续优化,可进一步提升稳定性、通用性与扩展性,成为可跨平台、跨屏幕型号的通用显示框架。

相关推荐
LDR0061 小时前
突破快充壁垒,赋能全场景体验——LDR6600 2C1A快充协议芯片重磅来袭
嵌入式硬件
weixin_457943301 小时前
arduino-舵机驱动
单片机·嵌入式硬件
AUTO_150756759652 小时前
SCT2160FNBR:7V 输入、6A、高效率同步降压 DC-DC 转换器
单片机·嵌入式硬件
LCG元2 小时前
STM32实战:基于STM32F103的编码器电机测速与闭环控制
stm32·单片机·嵌入式硬件
djarmy2 小时前
哪些海外国家最可能落地矿鸿/OpenHarmony矿山方案?1. 资源型发展中国家(最优先)
嵌入式硬件·开源
kaikaile19954 小时前
基于 STM32 的双闭环控制直流无刷电机(BLDC)方案
stm32·单片机·嵌入式硬件
Heartache boy4 小时前
野火STM32_HAL库版课程笔记-DWT应用与DHT11温湿度传感器
笔记·stm32·单片机·嵌入式硬件
无人装备硬件开发爱好者11 小时前
STM32G474 + 1.32 寸 OLED(128×96)俄罗斯方块游戏实现指南
stm32·嵌入式硬件·游戏
三佛科技-1341638421211 小时前
SM2850P无电感离线稳压器 5V输出 典型应用电路分析(管脚、关键设计要点)
单片机·嵌入式硬件·物联网·智能家居·pcb工艺