数码管LED显示屏矩阵驱动技术详解

1. 矩阵驱动原理

矩阵驱动是LED显示屏常用的一种高效驱动方式,利用COM(Common,公共端)和SEG(Segment,段选)线的交叉点控制单个LED的亮灭。相比直接驱动,矩阵驱动可以显著减少所需I/O引脚数量。

基本原理

  • **直接驱动**:每个LED需要一个独立的I/O引脚

  • **矩阵驱动**:N×M矩阵只需N+M个I/O引脚

例如,驱动16个LED:

  • 直接驱动:需要16个I/O引脚

  • 4×4矩阵驱动:只需8个I/O引脚(4个COM + 4个SEG)

2. COM-SEG引脚对应关系

标准7段数码管引脚定义

复制代码
    a
   ---
f |   | b
  | g |
   ---
e |   | c
  |   |
   ---
    d    • dp

每个段与数据位的对应关系:

  • bit0 → a段

  • bit1 → b段

  • bit2 → c段

  • bit3 → d段

  • bit4 → e段

  • bit5 → f段

  • bit6 → g段

  • bit7 → dp段(小数点)

硬件连接方式

在4位数码管(常见配置)中:

  • COM线连接到每个数码管的公共端(可能是共阳或共阴)

  • SEG线并联连接到所有数码管的对应段

    COM0 → 数码管1公共端
    COM1 → 数码管2公共端
    COM2 → 数码管3公共端
    COM3 → 数码管4公共端

    SEG0 → 所有数码管的a段
    SEG1 → 所有数码管的b段
    ...
    SEG7 → 所有数码管的dp段

3. 扫描刷新机制

矩阵驱动采用时分复用技术,通过快速扫描实现视觉上的"同时显示":

  1. 激活COM0,设置SEG0-7的状态,点亮数码管1

  2. 关闭COM0,激活COM1,设置SEG0-7的状态,点亮数码管2

  3. 依此类推,循环重复

刷新过程人眼不可察觉,通常刷新频率需>60Hz。

4. 代码实现示例

初始化设置

cpp 复制代码
void User_LED_Init()
{
    LED_InitStruct.Instance = LED_LCD;
    LED_InitStruct.Init.COMdriveLock = LED_LCD_COMNOLOCK; //COM口大电流不开启
    LED_InitStruct.Init.SegIOSel = 0x00ff; //LED segl口选择
    LED_InitStruct.Init.ComIOSel = 0x0f;   //COMl口选择(使用COM0-COM3四个口)
    LED_InitStruct.Init.ScanWidth = 0x05;  //LED周期配置
    LED_InitStruct.Init.DutySel = LED_DUTYSEL_4_8; //占空比设置为4/8

    HAL_LED_Init(&LED_InitStruct);
    HAL_LED_StartScan(&LED_InitStruct); //启动LED循环扫描模式
}

段码定义

cpp 复制代码
// 定义0-9数字对应的段码
uint32_t Led_arr_num[10] = {
    0x3F, // 0: 0011 1111 - 点亮a,b,c,d,e,f
    0x06, // 1: 0000 0110 - 点亮b,c
    0x5B, // 2: 0101 1011 - 点亮a,b,d,e,g
    0x4F, // 3: 0100 1111 - 点亮a,b,c,d,g
    0x66, // 4: 0110 0110 - 点亮b,c,f,g
    0x6D, // 5: 0110 1101 - 点亮a,c,d,f,g
    0x7D, // 6: 0111 1101 - 点亮a,c,d,e,f,g
    0x07, // 7: 0000 0111 - 点亮a,b,c
    0x7F, // 8: 0111 1111 - 点亮a,b,c,d,e,f,g
    0x6F  // 9: 0110 1111 - 点亮a,b,c,d,f,g
};

数据更新

cpp 复制代码
// 所有数码管显示相同数字
void display_same_number(uint8_t number) {
    if(number > 9) return; // 验证输入
    
    HAL_LED_ARR_Data(LED_LCD_COM_0, Led_arr_num[number]);
    HAL_LED_ARR_Data(LED_LCD_COM_1, Led_arr_num[number]);
    HAL_LED_ARR_Data(LED_LCD_COM_2, Led_arr_num[number]);
    HAL_LED_ARR_Data(LED_LCD_COM_3, Led_arr_num[number]);
}

// 显示4位数字(范围0-9999)
void display_number(uint16_t number) {
    uint8_t digit1 = number % 10;
    uint8_t digit2 = (number / 10) % 10;
    uint8_t digit3 = (number / 100) % 10;
    uint8_t digit4 = (number / 1000) % 10;
    
    HAL_LED_ARR_Data(LED_LCD_COM_0, Led_arr_num[digit1]);
    HAL_LED_ARR_Data(LED_LCD_COM_1, Led_arr_num[digit2]);
    HAL_LED_ARR_Data(LED_LCD_COM_2, Led_arr_num[digit3]);
    HAL_LED_ARR_Data(LED_LCD_COM_3, Led_arr_num[digit4]);
}

// 显示带小数点的数字
void display_with_decimal(uint16_t number, uint8_t decimal_pos) {
    uint8_t digit1 = number % 10;
    uint8_t digit2 = (number / 10) % 10;
    uint8_t digit3 = (number / 100) % 10;
    uint8_t digit4 = (number / 1000) % 10;
    
    HAL_LED_ARR_Data(LED_LCD_COM_0, Led_arr_num[digit1]);
    HAL_LED_ARR_Data(LED_LCD_COM_1, Led_arr_num[digit2]);
    HAL_LED_ARR_Data(LED_LCD_COM_2, Led_arr_num[digit3]);
    HAL_LED_ARR_Data(LED_LCD_COM_3, Led_arr_num[digit4]);
    
    // 在指定位置添加小数点(0为最右侧数码管)
    if(decimal_pos < 4) {
        uint32_t current_value = 0;
        switch(decimal_pos) {
            case 0: 
                current_value = HAL_LED_Read_Data(LED_LCD_COM_0);
                HAL_LED_ARR_Data(LED_LCD_COM_0, current_value | 0x80);
                break;
            case 1: 
                current_value = HAL_LED_Read_Data(LED_LCD_COM_1);
                HAL_LED_ARR_Data(LED_LCD_COM_1, current_value | 0x80);
                break;
            case 2: 
                current_value = HAL_LED_Read_Data(LED_LCD_COM_2);
                HAL_LED_ARR_Data(LED_LCD_COM_2, current_value | 0x80);
                break;
            case 3: 
                current_value = HAL_LED_Read_Data(LED_LCD_COM_3);
                HAL_LED_ARR_Data(LED_LCD_COM_3, current_value | 0x80);
                break;
        }
    }
}

5. 扫描频率与占空比

关键参数说明:

  1. **扫描宽度(ScanWidth)**:
  • 决定COM口扫描时间:period = (scan_width+1) × 16us

  • 示例:扫描宽度5对应96us的COM周期

  1. **占空比(DutySel)**:
  • 定义COM口导通时间占周期的比例

  • 常见配置:4/8(50%)占空比

这两个参数影响显示的亮度和功耗。

6. 应用实例:时钟显示

cpp 复制代码
void display_clock(uint8_t hours, uint8_t minutes) {
    uint8_t hour_tens = hours / 10;
    uint8_t hour_units = hours % 10;
    uint8_t min_tens = minutes / 10;
    uint8_t min_units = minutes % 10;
    
    // 显示时间格式: HH:MM (小数点作为冒号)
    HAL_LED_ARR_Data(LED_LCD_COM_0, Led_arr_num[min_units]);
    HAL_LED_ARR_Data(LED_LCD_COM_1, Led_arr_num[min_tens]);
    HAL_LED_ARR_Data(LED_LCD_COM_2, Led_arr_num[hour_units] | 0x80); // 添加小数点作为冒号
    HAL_LED_ARR_Data(LED_LCD_COM_3, Led_arr_num[hour_tens]);
}

7. 矩阵驱动优缺点

优点

  • 显著减少I/O引脚需求
  • 降低功耗
  • 简化PCB设计

缺点

  • 需要软件不断刷新
  • 同一时刻只有一位数码管点亮
  • 亮度受刷新频率和占空比影响

8. 结论

矩阵驱动技术是数码管和LED显示应用中的基础技术,通过时分复用实现多位数显示,平衡了硬件复杂度和显示效果。掌握COM-SEG对应关系和扫描机制,可以轻松实现各种数字、字符甚至自定义图形的显示。

相关推荐
天月风沙1 小时前
PX4 | 无人机关闭磁力计罗盘飞行(yaw estimate error报错解决方法)
单片机·嵌入式硬件·mcu·无人机
计蒙不吃鱼4 小时前
星闪开发之Server-Client 指令交互控制红灯亮灭案例解析(SLE_LED详解)
嵌入式硬件·物联网·iot·星闪·星闪开发
想搞嵌入式的小白5 小时前
STM32 NVIC中断控制器
stm32·单片机·嵌入式硬件·nvic
A-花开堪折5 小时前
Android7 Input(十)View 处理Input事件pipeline
android·嵌入式硬件
深圳市尚想信息技术有限公司5 小时前
【深尚想】OPA855QDSGRQ1运算放大器IC德州仪器TI汽车级高速8GHz增益带宽的全面解析
单片机·嵌入式硬件
陕西艾瑞科惯性技术有限公司6 小时前
让飞行姿态 “可感知”:为什么无人机需要三轴陀螺仪?
嵌入式硬件·机器学习·机器人·无人机·pcb工艺
代码总长两年半6 小时前
STM32----IAP远程升级
stm32·单片机·嵌入式硬件
广药门徒7 小时前
STM32手册上标称的18MHz GPIO翻转速度和你实际测量到的速度之间的差异是预期之内且合理的
单片机·嵌入式硬件
广药门徒7 小时前
在使用一些不用驱动大电流的设备就可以用stm32的自己的上下拉但是本身上下拉不就是给iicspi这些他通信给信号的吗中怎么还跟驱动能力扯上了有什么场景嘛
stm32·单片机·fpga开发
jz_ddk8 小时前
[zynq] Zynq Linux 环境下 AXI BRAM 控制器驱动方法详解(代码示例)
linux·运维·c语言·网络·嵌入式硬件