STM32数码管和LCD显示技术深度解析(超详细)

STM32数码管和LCD显示技术深度解析

1. 引言

在现代嵌入式系统中,显示技术是实现人机交互的关键组成部分。STM32作为广泛应用的ARM Cortex-M系列微控制器,具备强大的外设支持能力,能够灵活驱动各种显示设备。本文将深入分析数码管和LCD的工作原理,并提供丰富的STM32驱动实现代码,涵盖从基础原理到高级应用的完整知识体系。

2. 数码管显示原理与技术实现

2.1 数码管基本结构与分类

数码管(Digital Tube)是一种价格低廉、结构简单的显示设备,广泛应用于工业控制、仪器仪表等领域。

2.1.1 数码管内部结构

数码管由多个发光二极管(LED)组成,通常排列成"8"字形,加上小数点共计8个段(7段数码管)或更多。

c 复制代码
// 数码管段位定义
typedef struct {
    uint8_t a : 1;  // 上横
    uint8_t b : 1;  // 右上竖
    uint8_t c : 1;  // 右下竖
    uint8_t d : 1;  // 下横
    uint8_t e : 1;  // 左下竖
    uint8_t f : 1;  // 左上竖
    uint8_t g : 1;  // 中横
    uint8_t dp : 1; // 小数点
} DigitalTubeSegment;

// 数字0-9的段码表(共阳极)
const uint8_t DIGITAL_TUBE_CODE_ANODE[10] = {
    0xC0, // 0: 1100 0000
    0xF9, // 1: 1111 1001
    0xA4, // 2: 1010 0100
    0xB0, // 3: 1011 0000
    0x99, // 4: 1001 1001
    0x92, // 5: 1001 0010
    0x82, // 6: 1000 0010
    0xF8, // 7: 1111 1000
    0x80, // 8: 1000 0000
    0x90, // 9: 1001 0000
};

// 数字0-9的段码表(共阴极)
const uint8_t DIGITAL_TUBE_CODE_CATHODE[10] = {
    0x3F, // 0: 0011 1111
    0x06, // 1: 0000 0110
    0x5B, // 2: 0101 1011
    0x4F, // 3: 0100 1111
    0x66, // 4: 0110 0110
    0x6D, // 5: 0110 1101
    0x7D, // 6: 0111 1101
    0x07, // 7: 0000 0111
    0x7F, // 8: 0111 1111
    0x6F, // 9: 0110 1111
};
2.1.2 数码管分类
  1. 按极性分类

    • 共阳极数码管:所有LED阳极连接在一起,接高电平
    • 共阴极数码管:所有LED阴极连接在一起,接低电平
  2. 按位数分类

    • 单位数码管
    • 多位数码管(2位、4位、8位等)
  3. 按颜色分类

    • 红色、绿色、蓝色、白色等

2.2 数码管驱动原理

2.2.1 静态驱动方式

每个数码管的段码都由独立的I/O口控制,显示稳定但占用I/O资源多。

c 复制代码
// 静态驱动接口定义
typedef struct {
    GPIO_TypeDef* port;  // GPIO端口
    uint16_t pin;        // GPIO引脚
} SegmentPin;

typedef struct {
    SegmentPin segA;
    SegmentPin segB;
    SegmentPin segC;
    SegmentPin segD;
    SegmentPin segE;
    SegmentPin segF;
    SegmentPin segG;
    SegmentPin segDP;
    uint8_t isCommonAnode; // 是否为共阳极
} StaticDigitalTube;

// 初始化静态数码管
void StaticDigitalTube_Init(StaticDigitalTube* tube) {
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    
    // 初始化所有段引脚
    SegmentPin pins[] = {tube->segA, tube->segB, tube->segC, tube->segD,
                        tube->segE, tube->segF, tube->segG, tube->segDP};
    
    for(int i = 0; i < 8; i++) {
        GPIO_InitStruct.Pin = pins[i].pin;
        GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
        HAL_GPIO_Init(pins[i].port, &GPIO_InitStruct);
    }
}

// 显示数字
void StaticDigitalTube_Display(StaticDigitalTube* tube, uint8_t number) {
    uint8_t code;
    
    if(tube->isCommonAnode) {
        code = DIGITAL_TUBE_CODE_ANODE[number % 10];
    } else {
        code = DIGITAL_TUBE_CODE_CATHODE[number % 10];
    }
    
    // 设置各段状态
    SegmentPin pins[] = {tube->segA, tube->segB, tube->segC, tube->segD,
                        tube->segE, tube->segF, tube->segG, tube->segDP};
    
    for(int i = 0; i < 8; i++) {
        uint8_t state = (code >> (7 - i)) & 0x01;
        if(tube->isCommonAnode) {
            state = !state; // 共阳极取反
        }
        HAL_GPIO_WritePin(pins[i].port, pins[i].pin, state ? GPIO_PIN_SET : GPIO_PIN_RESET);
    }
}
2.2.2 动态扫描驱动方式

通过分时复用技术,依次点亮多个数码管,利用人眼视觉暂留效应实现稳定显示。

c 复制代码
// 动态扫描数码管控制器
typedef struct {
    GPIO_TypeDef* segPorts[8];   // 段码端口
    uint16_t segPins[8];         // 段码引脚
    GPIO_TypeDef* bitPorts[4];   // 位选端口
    uint16_t bitPins[4];         // 位选引脚
    uint8_t buffer[4];           // 显示缓冲区
    uint8_t currentBit;          // 当前显示位
    uint8_t isCommonAnode;       // 是否为共阳极
    uint32_t lastScanTime;       // 上次扫描时间
    uint32_t scanInterval;       // 扫描间隔(ms)
} DynamicDigitalTube;

// 初始化动态数码管
void DynamicDigitalTube_Init(DynamicDigitalTube* tube) {
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    
    // 初始化段码引脚
    for(int i = 0; i < 8; i++) {
        GPIO_InitStruct.Pin = tube->segPins[i];
        GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
        HAL_GPIO_Init(tube->segPorts[i], &GPIO_InitStruct);
    }
    
    // 初始化位选引脚
    for(int i = 0; i < 4; i++) {
        GPIO_InitStruct.Pin = tube->bitPins[i];
        GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
        HAL_GPIO_Init(tube->bitPorts[i], &GPIO_InitStruct);
        
        // 初始关闭所有位选
        HAL_GPIO_WritePin(tube->bitPorts[i], tube->bitPins[i], 
                         tube->isCommonAnode ? GPIO_PIN_RESET : GPIO_PIN_SET);
    }
    
    tube->currentBit = 0;
    tube->lastScanTime = HAL_GetTick();
    tube->scanInterval = 2; // 2ms扫描间隔
}

// 扫描显示函数(需要在主循环中定期调用)
void DynamicDigitalTube_Scan(DynamicDigitalTube* tube) {
    uint32_t currentTime = HAL_GetTick();
    
    if(currentTime - tube->lastScanTime < tube->scanInterval) {
        return;
    }
    
    tube->lastScanTime = currentTime;
    
    // 关闭当前位
    HAL_GPIO_WritePin(tube->bitPorts[tube->currentBit], 
                     tube->bitPins[tube->currentBit],
                     tube->isCommonAnode ? GPIO_PIN_RESET : GPIO_PIN_SET);
    
    // 切换到下一位
    tube->currentBit = (tube->currentBit + 1) % 4;
    
    // 获取当前位的段码
    uint8_t number = tube->buffer[tube->currentBit];
    uint8_t code;
    
    if(tube->isCommonAnode) {
        code = DIGITAL_TUBE_CODE_ANODE[number % 10];
    } else {
        code = DIGITAL_TUBE_CODE_CATHODE[number % 10];
    }
    
    // 设置段码
    for(int i = 0; i < 8; i++) {
        uint8_t state = (code >> (7 - i)) & 0x01;
        if(tube->isCommonAnode) {
            state = !state; // 共阳极取反
        }
        HAL_GPIO_WritePin(tube->segPorts[i], tube->segPins[i],
                         state ? GPIO_PIN_SET : GPIO_PIN_RESET);
    }
    
    // 打开当前位
    HAL_GPIO_WritePin(tube->bitPorts[tube->currentBit],
                     tube->bitPins[tube->currentBit],
                     tube->isCommonAnode ? GPIO_PIN_SET : GPIO_PIN_RESET);
}

// 设置显示缓冲区
void DynamicDigitalTube_SetNumber(DynamicDigitalTube* tube, uint16_t number) {
    tube->buffer[0] = number % 10;           // 个位
    tube->buffer[1] = (number / 10) % 10;    // 十位
    tube->buffer[2] = (number / 100) % 10;   // 百位
    tube->buffer[3] = (number / 1000) % 10;  // 千位
}

2.3 数码管显示优化技术

2.3.1 亮度控制
c 复制代码
// PWM调光数码管控制器
typedef struct {
    TIM_HandleTypeDef* tim;           // PWM定时器
    uint32_t channel;                 // PWM通道
    uint8_t brightness;               // 亮度(0-100)
    DynamicDigitalTube tube;          // 数码管控制器
    uint8_t displayEnabled;           // 显示使能
} PWMDigitalTube;

// 初始化PWM调光
void PWMDigitalTube_Init(PWMDigitalTube* pwmTube, TIM_HandleTypeDef* tim, uint32_t channel) {
    pwmTube->tim = tim;
    pwmTube->channel = channel;
    pwmTube->brightness = 100;
    pwmTube->displayEnabled = 1;
    
    // 启动PWM
    HAL_TIM_PWM_Start(pwmTube->tim, pwmTube->channel);
    PWMDigitalTube_SetBrightness(pwmTube, pwmTube->brightness);
}

// 设置亮度
void PWMDigitalTube_SetBrightness(PWMDigitalTube* pwmTube, uint8_t brightness) {
    if(brightness > 100) brightness = 100;
    pwmTube->brightness = brightness;
    
    // 计算PWM占空比
    uint32_t pulse = (pwmTube->tim->Init.Period + 1) * brightness / 100;
    
    switch(pwmTube->channel) {
        case TIM_CHANNEL_1:
            __HAL_TIM_SET_COMPARE(pwmTube->tim, TIM_CHANNEL_1, pulse);
            break;
        case TIM_CHANNEL_2:
            __HAL_TIM_SET_COMPARE(pwmTube->tim, TIM_CHANNEL_2, pulse);
            break;
        case TIM_CHANNEL_3:
            __HAL_TIM_SET_COMPARE(pwmTube->tim, TIM_CHANNEL_3, pulse);
            break;
        case TIM_CHANNEL_4:
            __HAL_TIM_SET_COMPARE(pwmTube->tim, TIM_CHANNEL_4, pulse);
            break;
    }
}

// 扫描函数(集成PWM控制)
void PWMDigitalTube_Scan(PWMDigitalTube* pwmTube) {
    if(!pwmTube->displayEnabled) {
        // 关闭所有显示
        for(int i = 0; i < 4; i++) {
            HAL_GPIO_WritePin(pwmTube->tube.bitPorts[i], 
                            pwmTube->tube.bitPins[i],
                            GPIO_PIN_RESET);
        }
        return;
    }
    
    DynamicDigitalTube_Scan(&pwmTube->tube);
}
2.3.2 消隐处理
c 复制代码
// 改进的动态扫描函数(带消隐)
void DynamicDigitalTube_ScanWithBlank(DynamicDigitalTube* tube) {
    uint32_t currentTime = HAL_GetTick();
    
    if(currentTime - tube->lastScanTime < tube->scanInterval) {
        return;
    }
    
    tube->lastScanTime = currentTime;
    
    // 消隐:先关闭所有显示
    for(int i = 0; i < 4; i++) {
        HAL_GPIO_WritePin(tube->bitPorts[i], tube->bitPins[i],
                         tube->isCommonAnode ? GPIO_PIN_RESET : GPIO_PIN_SET);
    }
    
    // 短暂延时,确保完全消隐
    uint32_t delay = 50; // 50微秒
    while(delay--);
    
    // 切换到下一位
    tube->currentBit = (tube->currentBit + 1) % 4;
    
    // 获取段码并显示
    uint8_t number = tube->buffer[tube->currentBit];
    uint8_t code = tube->isCommonAnode ? 
                  DIGITAL_TUBE_CODE_ANODE[number % 10] :
                  DIGITAL_TUBE_CODE_CATHODE[number % 10];
    
    for(int i = 0; i < 8; i++) {
        uint8_t state = (code >> (7 - i)) & 0x01;
        if(tube->isCommonAnode) {
            state = !state;
        }
        HAL_GPIO_WritePin(tube->segPorts[i], tube->segPins[i],
                         state ? GPIO_PIN_SET : GPIO_PIN_RESET);
    }
    
    // 打开当前位
    HAL_GPIO_WritePin(tube->bitPorts[tube->currentBit],
                     tube->bitPins[tube->currentBit],
                     tube->isCommonAnode ? GPIO_PIN_SET : GPIO_PIN_RESET);
}

3. LCD显示原理与技术实现

3.1 LCD技术基础

3.1.1 LCD工作原理

液晶显示器(LCD)基于液晶的光电效应,通过改变电压控制液晶分子的排列,从而改变光的透过率。

c 复制代码
// LCD基本参数定义
typedef struct {
    uint16_t width;      // 屏幕宽度
    uint16_t height;     // 屏幕高度
    uint16_t id;         // 屏幕ID
    uint8_t dir;         // 显示方向
    uint16_t wramcmd;    // 写GRAM命令
    uint16_t setxcmd;    // 设置X坐标命令
    uint16_t setycmd;    // 设置Y坐标命令
} LCD_TypeDef;

// 常用LCD控制器类型
typedef enum {
    LCD_ILI9341 = 0x9341,
    LCD_ST7789V = 0x7789,
    LCD_SSD1306 = 0x1306,  // OLED
    LCD_ST7735S = 0x7735
} LCD_ControllerType;
3.1.2 LCD接口类型
  1. 并行接口

    • 8位/16位数据总线
    • 控制信号线(RS, WR, RD, CS, RST)
  2. SPI接口

    • 串行外设接口
    • 适合小尺寸LCD
  3. I2C接口

    • 主要用于OLED显示屏
    • 节省引脚资源

3.2 STM32驱动LCD实现

3.2.1 FSMC并行接口驱动
c 复制代码
// FSMC LCD控制器
typedef struct {
    // FSMC地址定义
    __IO uint16_t LCD_REG;   // 命令寄存器地址
    __IO uint16_t LCD_RAM;   // 数据寄存器地址
    
    // LCD参数
    uint16_t width;
    uint16_t height;
    uint16_t id;
    uint8_t dir;
    
    // 颜色格式
    uint16_t textColor;
    uint16_t backColor;
    uint16_t fillColor;
} FSMC_LCD_TypeDef;

// FSMC初始化
void FSMC_LCD_Init(void) {
    FSMC_NORSRAM_TimingTypeDef Timing = {0};
    FSMC_NORSRAM_TimingTypeDef ExtTiming = {0};
    
    // 使能时钟
    __HAL_RCC_FSMC_CLK_ENABLE();
    __HAL_RCC_GPIOD_CLK_ENABLE();
    __HAL_RCC_GPIOE_CLK_ENABLE();
    __HAL_RCC_GPIOF_CLK_ENABLE();
    __HAL_RCC_GPIOG_CLK_ENABLE();
    
    // 初始化GPIO
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    
    // 数据线D0-D15
    GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_8 | GPIO_PIN_9 |
                         GPIO_PIN_10 | GPIO_PIN_14 | GPIO_PIN_15;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF12_FSMC;
    HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
    
    // 更多GPIO初始化代码...
    
    // 配置FSMC
    FSMC_NORSRAM_HandleTypeDef hsram1 = {0};
    
    hsram1.Instance = FSMC_NORSRAM_DEVICE;
    hsram1.Extended = FSMC_NORSRAM_EXTENDED_DEVICE;
    hsram1.Init.NSBank = FSMC_NORSRAM_BANK1;
    hsram1.Init.DataAddressMux = FSMC_DATA_ADDRESS_MUX_DISABLE;
    hsram1.Init.MemoryType = FSMC_MEMORY_TYPE_SRAM;
    hsram1.Init.MemoryDataWidth = FSMC_NORSRAM_MEM_BUS_WIDTH_16;
    hsram1.Init.BurstAccessMode = FSMC_BURST_ACCESS_MODE_DISABLE;
    hsram1.Init.WaitSignalPolarity = FSMC_WAIT_SIGNAL_POLARITY_LOW;
    hsram1.Init.WrapMode = FSMC_WRAP_MODE_DISABLE;
    hsram1.Init.WaitSignalActive = FSMC_WAIT_TIMING_BEFORE_WS;
    hsram1.Init.WriteOperation = FSMC_WRITE_OPERATION_ENABLE;
    hsram1.Init.WaitSignal = FSMC_WAIT_SIGNAL_DISABLE;
    hsram1.Init.ExtendedMode = FSMC_EXTENDED_MODE_ENABLE;
    hsram1.Init.AsynchronousWait = FSMC_ASYNCHRONOUS_WAIT_DISABLE;
    hsram1.Init.WriteBurst = FSMC_WRITE_BURST_DISABLE;
    hsram1.Init.PageSize = FSMC_PAGE_SIZE_NONE;
    
    // 时序配置
    Timing.AddressSetupTime = 3;
    Timing.AddressHoldTime = 0;
    Timing.DataSetupTime = 6;
    Timing.BusTurnAroundDuration = 1;
    Timing.CLKDivision = 0;
    Timing.DataLatency = 0;
    Timing.AccessMode = FSMC_ACCESS_MODE_A;
    
    ExtTiming.AddressSetupTime = 3;
    ExtTiming.AddressHoldTime = 0;
    ExtTiming.DataSetupTime = 6;
    ExtTiming.BusTurnAroundDuration = 1;
    ExtTiming.CLKDivision = 0;
    ExtTiming.DataLatency = 0;
    ExtTiming.AccessMode = FSMC_ACCESS_MODE_A;
    
    HAL_FSMC_NORSRAM_Init(&hsram1, &Timing, &ExtTiming);
}

// 写命令
void LCD_WriteCmd(FSMC_LCD_TypeDef* lcd, uint16_t cmd) {
    lcd->LCD_REG = cmd;
}

// 写数据
void LCD_WriteData(FSMC_LCD_TypeDef* lcd, uint16_t data) {
    lcd->LCD_RAM = data;
}

// 读数据
uint16_t LCD_ReadData(FSMC_LCD_TypeDef* lcd) {
    return lcd->LCD_RAM;
}

// 初始化LCD
void LCD_Init(FSMC_LCD_TypeDef* lcd) {
    // 硬件复位
    LCD_RST(0);
    HAL_Delay(100);
    LCD_RST(1);
    HAL_Delay(100);
    
    // 读取LCD ID
    LCD_WriteCmd(lcd, 0xD3);
    lcd->id = LCD_ReadData(lcd);
    lcd->id = LCD_ReadData(lcd);
    lcd->id = LCD_ReadData(lcd);
    lcd->id <<= 8;
    lcd->id |= LCD_ReadData(lcd);
    
    // 根据ID初始化不同LCD
    switch(lcd->id) {
        case LCD_ILI9341:
            LCD_ILI9341_Init(lcd);
            break;
        case LCD_ST7789V:
            LCD_ST7789V_Init(lcd);
            break;
        default:
            // 默认初始化
            break;
    }
    
    // 清屏
    LCD_Clear(lcd, lcd->fillColor);
    // 设置显示方向
    LCD_DisplayDir(lcd, lcd->dir);
}

// ILI9341初始化序列
void LCD_ILI9341_Init(FSMC_LCD_TypeDef* lcd) {
    LCD_WriteCmd(lcd, 0xCF);
    LCD_WriteData(lcd, 0x00);
    LCD_WriteData(lcd, 0xC1);
    LCD_WriteData(lcd, 0x30);
    
    LCD_WriteCmd(lcd, 0xED);
    LCD_WriteData(lcd, 0x64);
    LCD_WriteData(lcd, 0x03);
    LCD_WriteData(lcd, 0x12);
    LCD_WriteData(lcd, 0x81);
    
    // 更多初始化命令...
    
    // 设置显示方向
    LCD_WriteCmd(lcd, 0x36);
    LCD_WriteData(lcd, 0x48);
    
    // 打开显示
    LCD_WriteCmd(lcd, 0x29);
}
3.2.2 SPI接口驱动
c 复制代码
// SPI LCD控制器
typedef struct {
    SPI_HandleTypeDef* spi;      // SPI句柄
    GPIO_TypeDef* csPort;        // 片选端口
    uint16_t csPin;              // 片选引脚
    GPIO_TypeDef* dcPort;        // 数据/命令选择端口
    uint16_t dcPin;              // 数据/命令选择引脚
    GPIO_TypeDef* rstPort;       // 复位端口
    uint16_t rstPin;             // 复位引脚
    
    uint16_t width;
    uint16_t height;
    uint16_t rotation;
} SPI_LCD_TypeDef;

// SPI发送数据
void SPI_LCD_SendData(SPI_LCD_TypeDef* lcd, uint8_t* data, uint32_t size) {
    HAL_GPIO_WritePin(lcd->csPort, lcd->csPin, GPIO_PIN_RESET);
    HAL_SPI_Transmit(lcd->spi, data, size, HAL_MAX_DELAY);
    HAL_GPIO_WritePin(lcd->csPort, lcd->csPin, GPIO_PIN_SET);
}

// SPI发送命令
void SPI_LCD_SendCmd(SPI_LCD_TypeDef* lcd, uint8_t cmd) {
    HAL_GPIO_WritePin(lcd->dcPort, lcd->dcPin, GPIO_PIN_RESET); // 命令模式
    SPI_LCD_SendData(lcd, &cmd, 1);
}

// SPI发送数据
void SPI_LCD_SendDataByte(SPI_LCD_TypeDef* lcd, uint8_t data) {
    HAL_GPIO_WritePin(lcd->dcPort, lcd->dcPin, GPIO_PIN_SET); // 数据模式
    SPI_LCD_SendData(lcd, &data, 1);
}

// 初始化SPI LCD
void SPI_LCD_Init(SPI_LCD_TypeDef* lcd) {
    // 复位LCD
    HAL_GPIO_WritePin(lcd->rstPort, lcd->rstPin, GPIO_PIN_RESET);
    HAL_Delay(100);
    HAL_GPIO_WritePin(lcd->rstPort, lcd->rstPin, GPIO_PIN_SET);
    HAL_Delay(100);
    
    // ST7789V初始化序列
    SPI_LCD_SendCmd(lcd, 0x36);
    SPI_LCD_SendDataByte(lcd, 0x00);
    
    SPI_LCD_SendCmd(lcd, 0x3A);
    SPI_LCD_SendDataByte(lcd, 0x05);
    
    SPI_LCD_SendCmd(lcd, 0xB2);
    SPI_LCD_SendDataByte(lcd, 0x0C);
    SPI_LCD_SendDataByte(lcd, 0x0C);
    SPI_LCD_SendDataByte(lcd, 0x00);
    SPI_LCD_SendDataByte(lcd, 0x33);
    SPI_LCD_SendDataByte(lcd, 0x33);
    
    // 更多初始化命令...
    
    // 打开显示
    SPI_LCD_SendCmd(lcd, 0x29);
    HAL_Delay(100);
}

// 设置显示区域
void SPI_LCD_SetWindow(SPI_LCD_TypeDef* lcd, uint16_t x1, uint16_t y1, 
                      uint16_t x2, uint16_t y2) {
    SPI_LCD_SendCmd(lcd, 0x2A); // 列地址设置
    SPI_LCD_SendDataByte(lcd, x1 >> 8);
    SPI_LCD_SendDataByte(lcd, x1 & 0xFF);
    SPI_LCD_SendDataByte(lcd, x2 >> 8);
    SPI_LCD_SendDataByte(lcd, x2 & 0xFF);
    
    SPI_LCD_SendCmd(lcd, 0x2B); // 行地址设置
    SPI_LCD_SendDataByte(lcd, y1 >> 8);
    SPI_LCD_SendDataByte(lcd, y1 & 0xFF);
    SPI_LCD_SendDataByte(lcd, y2 >> 8);
    SPI_LCD_SendDataByte(lcd, y2 & 0xFF);
    
    SPI_LCD_SendCmd(lcd, 0x2C); // 写GRAM
}

// 绘制像素点
void SPI_LCD_DrawPixel(SPI_LCD_TypeDef* lcd, uint16_t x, uint16_t y, uint16_t color) {
    if(x >= lcd->width || y >= lcd->height) return;
    
    SPI_LCD_SetWindow(lcd, x, y, x, y);
    HAL_GPIO_WritePin(lcd->dcPort, lcd->dcPin, GPIO_PIN_SET);
    uint8_t data[2] = {color >> 8, color & 0xFF};
    SPI_LCD_SendData(lcd, data, 2);
}

// 快速填充矩形
void SPI_LCD_FillRect(SPI_LCD_TypeDef* lcd, uint16_t x, uint16_t y, 
                     uint16_t w, uint16_t h, uint16_t color) {
    if((x >= lcd->width) || (y >= lcd->height)) return;
    
    if((x + w - 1) >= lcd->width) w = lcd->width - x;
    if((y + h - 1) >= lcd->height) h = lcd->height - y;
    
    SPI_LCD_SetWindow(lcd, x, y, x + w - 1, y + h - 1);
    
    HAL_GPIO_WritePin(lcd->dcPort, lcd->dcPin, GPIO_PIN_SET);
    
    uint32_t pixels = w * h;
    uint8_t colorHigh = color >> 8;
    uint8_t colorLow = color & 0xFF;
    uint8_t buffer[256];
    
    // 准备缓冲区
    for(int i = 0; i < 256; i += 2) {
        buffer[i] = colorHigh;
        buffer[i + 1] = colorLow;
    }
    
    HAL_GPIO_WritePin(lcd->csPort, lcd->csPin, GPIO_PIN_RESET);
    
    // 发送数据
    while(pixels > 0) {
        uint32_t sendSize = pixels * 2;
        if(sendSize > 256) sendSize = 256;
        HAL_SPI_Transmit(lcd->spi, buffer, sendSize, HAL_MAX_DELAY);
        pixels -= sendSize / 2;
    }
    
    HAL_GPIO_WritePin(lcd->csPort, lcd->csPin, GPIO_PIN_SET);
}

3.3 LCD图形库实现

3.3.1 基本绘图函数
c 复制代码
// LCD图形库
typedef struct {
    FSMC_LCD_TypeDef* fsmcLcd;   // FSMC LCD
    SPI_LCD_TypeDef* spiLcd;     // SPI LCD
    uint8_t interfaceType;       // 接口类型
    uint16_t width;
    uint16_t height;
    uint16_t textColor;
    uint16_t bgColor;
    sFONT* font;                 // 字体
} LCD_GraphicLib;

// 绘制直线(Bresenham算法)
void LCD_DrawLine(LCD_GraphicLib* lib, uint16_t x1, uint16_t y1, 
                 uint16_t x2, uint16_t y2, uint16_t color) {
    int dx = abs(x2 - x1);
    int dy = abs(y2 - y1);
    int sx = (x1 < x2) ? 1 : -1;
    int sy = (y1 < y2) ? 1 : -1;
    int err = dx - dy;
    int e2;
    
    while(1) {
        if(lib->interfaceType == LCD_INTERFACE_FSMC) {
            FSMC_LCD_DrawPixel(lib->fsmcLcd, x1, y1, color);
        } else {
            SPI_LCD_DrawPixel(lib->spiLcd, x1, y1, color);
        }
        
        if(x1 == x2 && y1 == y2) break;
        
        e2 = 2 * err;
        if(e2 > -dy) {
            err -= dy;
            x1 += sx;
        }
        if(e2 < dx) {
            err += dx;
            y1 += sy;
        }
    }
}

// 绘制矩形
void LCD_DrawRectangle(LCD_GraphicLib* lib, uint16_t x1, uint16_t y1, 
                      uint16_t x2, uint16_t y2, uint16_t color) {
    LCD_DrawLine(lib, x1, y1, x2, y1, color); // 上边
    LCD_DrawLine(lib, x1, y2, x2, y2, color); // 下边
    LCD_DrawLine(lib, x1, y1, x1, y2, color); // 左边
    LCD_DrawLine(lib, x2, y1, x2, y2, color); // 右边
}

// 绘制圆形
void LCD_DrawCircle(LCD_GraphicLib* lib, uint16_t x0, uint16_t y0, 
                   uint16_t r, uint16_t color) {
    int x = 0;
    int y = r;
    int d = 3 - 2 * r;
    
    while(x <= y) {
        if(lib->interfaceType == LCD_INTERFACE_FSMC) {
            FSMC_LCD_DrawPixel(lib->fsmcLcd, x0 + x, y0 + y, color);
            FSMC_LCD_DrawPixel(lib->fsmcLcd, x0 - x, y0 + y, color);
            FSMC_LCD_DrawPixel(lib->fsmcLcd, x0 + x, y0 - y, color);
            FSMC_LCD_DrawPixel(lib->fsmcLcd, x0 - x, y0 - y, color);
            FSMC_LCD_DrawPixel(lib->fsmcLcd, x0 + y, y0 + x, color);
            FSMC_LCD_DrawPixel(lib->fsmcLcd, x0 - y, y0 + x, color);
            FSMC_LCD_DrawPixel(lib->fsmcLcd, x0 + y, y0 - x, color);
            FSMC_LCD_DrawPixel(lib->fsmcLcd, x0 - y, y0 - x, color);
        } else {
            SPI_LCD_DrawPixel(lib->spiLcd, x0 + x, y0 + y, color);
            SPI_LCD_DrawPixel(lib->spiLcd, x0 - x, y0 + y, color);
            SPI_LCD_DrawPixel(lib->spiLcd, x0 + x, y0 - y, color);
            SPI_LCD_DrawPixel(lib->spiLcd, x0 - x, y0 - y, color);
            SPI_LCD_DrawPixel(lib->spiLcd, x0 + y, y0 + x, color);
            SPI_LCD_DrawPixel(lib->spiLcd, x0 - y, y0 + x, color);
            SPI_LCD_DrawPixel(lib->spiLcd, x0 + y, y0 - x, color);
            SPI_LCD_DrawPixel(lib->spiLcd, x0 - y, y0 - x, color);
        }
        
        if(d < 0) {
            d = d + 4 * x + 6;
        } else {
            d = d + 4 * (x - y) + 10;
            y--;
        }
        x++;
    }
}

// 绘制三角形
void LCD_DrawTriangle(LCD_GraphicLib* lib, uint16_t x1, uint16_t y1,
                     uint16_t x2, uint16_t y2, uint16_t x3, uint16_t y3,
                     uint16_t color) {
    LCD_DrawLine(lib, x1, y1, x2, y2, color);
    LCD_DrawLine(lib, x2, y2, x3, y3, color);
    LCD_DrawLine(lib, x3, y3, x1, y1, color);
}
3.3.2 字体显示实现
c 复制代码
// 字体结构定义
typedef struct {
    uint8_t width;     // 字符宽度
    uint8_t height;    // 字符高度
    uint8_t firstChar; // 第一个字符
    uint8_t lastChar;  // 最后一个字符
    const uint8_t* table; // 字符数据表
} sFONT;

// 8x16 ASCII字体
const uint8_t Font8x16[] = {
    // 空格 ' ' (0x20)
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    
    // 数字 '0' (0x30)
    0x00,0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,
    0x00,0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,
    
    // 更多字符数据...
};

// 16x24中文字体(示例)
const uint8_t Font16x24_CN[] = {
    // 汉字"中"
    0x00,0x00,0x00,0x80,0x40,0x20,0x10,0x08,0x08,0x08,0x10,0x20,0x40,0x80,0x00,0x00,
    0x00,0x00,0x00,0x03,0x04,0x08,0x10,0x20,0x20,0x20,0x10,0x08,0x04,0x03,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    
    // 更多汉字数据...
};

// 显示字符
void LCD_DrawChar(LCD_GraphicLib* lib, uint16_t x, uint16_t y, 
                 char ch, sFONT* font, uint16_t color) {
    uint8_t i, j;
    uint8_t pixel;
    uint16_t charIndex;
    
    // 检查字符是否在字体范围内
    if(ch < font->firstChar || ch > font->lastChar) {
        ch = '?'; // 显示问号代替
    }
    
    // 计算字符在字体表中的位置
    charIndex = (ch - font->firstChar) * font->height * ((font->width + 7) / 8);
    
    // 逐行显示字符
    for(i = 0; i < font->height; i++) {
        for(j = 0; j < font->width; j++) {
            // 读取像素点
            pixel = font->table[charIndex + i * ((font->width + 7) / 8) + j / 8];
            pixel = (pixel >> (7 - (j % 8))) & 0x01;
            
            if(pixel) {
                if(lib->interfaceType == LCD_INTERFACE_FSMC) {
                    FSMC_LCD_DrawPixel(lib->fsmcLcd, x + j, y + i, color);
                } else {
                    SPI_LCD_DrawPixel(lib->spiLcd, x + j, y + i, color);
                }
            }
        }
    }
}

// 显示字符串
void LCD_DrawString(LCD_GraphicLib* lib, uint16_t x, uint16_t y, 
                   const char* str, sFONT* font, uint16_t color) {
    uint16_t currentX = x;
    
    while(*str) {
        // 检查是否换行
        if(*str == '\n') {
            y += font->height + 2; // 行间距
            currentX = x;
            str++;
            continue;
        }
        
        LCD_DrawChar(lib, currentX, y, *str, font, color);
        currentX += font->width + 1; // 字符间距
        
        str++;
        
        // 检查是否超出屏幕
        if(currentX + font->width > lib->width) {
            currentX = x;
            y += font->height + 2;
        }
    }
}

// 显示中文字符
void LCD_DrawChinese(LCD_GraphicLib* lib, uint16_t x, uint16_t y,
                    const uint8_t* chinese, uint16_t color) {
    uint8_t i, j;
    uint8_t pixel;
    const uint8_t* ptr = chinese;
    
    // 中文字体通常是16x16或24x24
    for(i = 0; i < 16; i++) { // 假设16行
        for(j = 0; j < 2; j++) { // 每行2字节
            uint8_t data = *ptr++;
            for(uint8_t k = 0; k < 8; k++) {
                if(data & (0x80 >> k)) {
                    if(lib->interfaceType == LCD_INTERFACE_FSMC) {
                        FSMC_LCD_DrawPixel(lib->fsmcLcd, x + j * 8 + k, y + i, color);
                    } else {
                        SPI_LCD_DrawPixel(lib->spiLcd, x + j * 8 + k, y + i, color);
                    }
                }
            }
        }
    }
}
3.3.3 高级图形功能
c 复制代码
// 绘制渐变矩形
void LCD_DrawGradientRect(LCD_GraphicLib* lib, uint16_t x, uint16_t y,
                         uint16_t w, uint16_t h, uint16_t colorStart,
                         uint16_t colorEnd, uint8_t direction) {
    uint16_t i, j;
    uint16_t currentColor;
    uint8_t r1, g1, b1, r2, g2, b2;
    
    // 提取颜色分量
    r1 = (colorStart >> 11) & 0x1F;
    g1 = (colorStart >> 5) & 0x3F;
    b1 = colorStart & 0x1F;
    
    r2 = (colorEnd >> 11) & 0x1F;
    g2 = (colorEnd >> 5) & 0x3F;
    b2 = colorEnd & 0x1F;
    
    if(direction == GRADIENT_HORIZONTAL) {
        // 水平渐变
        for(i = 0; i < w; i++) {
            uint8_t r = r1 + (r2 - r1) * i / w;
            uint8_t g = g1 + (g2 - g1) * i / w;
            uint8_t b = b1 + (b2 - b1) * i / w;
            
            currentColor = (r << 11) | (g << 5) | b;
            
            // 绘制垂直线
            if(lib->interfaceType == LCD_INTERFACE_FSMC) {
                for(j = 0; j < h; j++) {
                    FSMC_LCD_DrawPixel(lib->fsmcLcd, x + i, y + j, currentColor);
                }
            } else {
                for(j = 0; j < h; j++) {
                    SPI_LCD_DrawPixel(lib->spiLcd, x + i, y + j, currentColor);
                }
            }
        }
    } else {
        // 垂直渐变
        for(j = 0; j < h; j++) {
            uint8_t r = r1 + (r2 - r1) * j / h;
            uint8_t g = g1 + (g2 - g1) * j / h;
            uint8_t b = b1 + (b2 - b1) * j / h;
            
            currentColor = (r << 11) | (g << 5) | b;
            
            // 绘制水平线
            if(lib->interfaceType == LCD_INTERFACE_FSMC) {
                for(i = 0; i < w; i++) {
                    FSMC_LCD_DrawPixel(lib->fsmcLcd, x + i, y + j, currentColor);
                }
            } else {
                for(i = 0; i < w; i++) {
                    SPI_LCD_DrawPixel(lib->spiLcd, x + i, y + j, currentColor);
                }
            }
        }
    }
}

// 绘制圆角矩形
void LCD_DrawRoundRect(LCD_GraphicLib* lib, uint16_t x, uint16_t y,
                      uint16_t w, uint16_t h, uint16_t r, uint16_t color) {
    // 绘制四个角的圆弧
    LCD_DrawCircleArc(lib, x + r, y + r, r, 180, 270, color); // 左上角
    LCD_DrawCircleArc(lib, x + w - r - 1, y + r, r, 270, 360, color); // 右上角
    LCD_DrawCircleArc(lib, x + w - r - 1, y + h - r - 1, r, 0, 90, color); // 右下角
    LCD_DrawCircleArc(lib, x + r, y + h - r - 1, r, 90, 180, color); // 左下角
    
    // 绘制直线部分
    LCD_DrawLine(lib, x + r, y, x + w - r - 1, y, color); // 上边
    LCD_DrawLine(lib, x, y + r, x, y + h - r - 1, color); // 左边
    LCD_DrawLine(lib, x + w - 1, y + r, x + w - 1, y + h - r - 1, color); // 右边
    LCD_DrawLine(lib, x + r, y + h - 1, x + w - r - 1, y + h - 1, color); // 下边
}

// 绘制圆弧
void LCD_DrawCircleArc(LCD_GraphicLib* lib, uint16_t x0, uint16_t y0,
                      uint16_t r, uint16_t startAngle, uint16_t endAngle,
                      uint16_t color) {
    int x = 0;
    int y = r;
    int d = 3 - 2 * r;
    
    // 角度转换为弧度
    float startRad = startAngle * M_PI / 180.0;
    float endRad = endAngle * M_PI / 180.0;
    
    while(x <= y) {
        // 计算当前点的角度
        float angle1 = atan2(y, x);
        float angle2 = atan2(x, y);
        
        // 绘制8个对称点
        if(angle1 >= startRad && angle1 <= endRad) {
            LCD_DrawPixel(lib, x0 + x, y0 - y, color);
        }
        if(angle1 >= M_PI - endRad && angle1 <= M_PI - startRad) {
            LCD_DrawPixel(lib, x0 - x, y0 - y, color);
        }
        if(angle1 >= M_PI + startRad && angle1 <= M_PI + endRad) {
            LCD_DrawPixel(lib, x0 - x, y0 + y, color);
        }
        if(angle1 >= 2*M_PI - endRad && angle1 <= 2*M_PI - startRad) {
            LCD_DrawPixel(lib, x0 + x, y0 + y, color);
        }
        
        if(angle2 >= startRad && angle2 <= endRad) {
            LCD_DrawPixel(lib, x0 + y, y0 - x, color);
        }
        if(angle2 >= M_PI - endRad && angle2 <= M_PI - startRad) {
            LCD_DrawPixel(lib, x0 - y, y0 - x, color);
        }
        if(angle2 >= M_PI + startRad && angle2 <= M_PI + endRad) {
            LCD_DrawPixel(lib, x0 - y, y0 + x, color);
        }
        if(angle2 >= 2*M_PI - endRad && angle2 <= 2*M_PI - startRad) {
            LCD_DrawPixel(lib, x0 + y, y0 + x, color);
        }
        
        if(d < 0) {
            d = d + 4 * x + 6;
        } else {
            d = d + 4 * (x - y) + 10;
            y--;
        }
        x++;
    }
}

3.4 LCD图像处理与优化

3.4.1 DMA传输优化
c 复制代码
// DMA图形传输
typedef struct {
    DMA_HandleTypeDef* dma;      // DMA句柄
    uint32_t dmaChannel;         // DMA通道
    uint8_t* frameBuffer;        // 帧缓冲区
    uint32_t bufferSize;         // 缓冲区大小
    uint8_t transferComplete;    // 传输完成标志
} LCD_DMA_Controller;

// 初始化DMA传输
void LCD_DMA_Init(LCD_DMA_Controller* dmaCtrl) {
    // 配置DMA
    dmaCtrl->dma->Instance = DMA2_Stream0;
    dmaCtrl->dma->Init.Channel = DMA_CHANNEL_0;
    dmaCtrl->dma->Init.Direction = DMA_MEMORY_TO_MEMORY;
    dmaCtrl->dma->Init.PeriphInc = DMA_PINC_ENABLE;
    dmaCtrl->dma->Init.MemInc = DMA_MINC_ENABLE;
    dmaCtrl->dma->Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    dmaCtrl->dma->Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    dmaCtrl->dma->Init.Mode = DMA_NORMAL;
    dmaCtrl->dma->Init.Priority = DMA_PRIORITY_HIGH;
    dmaCtrl->dma->Init.FIFOMode = DMA_FIFOMODE_ENABLE;
    dmaCtrl->dma->Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
    dmaCtrl->dma->Init.MemBurst = DMA_MBURST_SINGLE;
    dmaCtrl->dma->Init.PeriphBurst = DMA_PBURST_SINGLE;
    
    HAL_DMA_Init(dmaCtrl->dma);
    
    // 使能DMA中断
    HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
    
    // 分配帧缓冲区
    dmaCtrl->frameBuffer = (uint8_t*)malloc(dmaCtrl->bufferSize);
    dmaCtrl->transferComplete = 1;
}

// DMA传输回调函数
void HAL_DMA_XferCpltCallback(DMA_HandleTypeDef* hdma) {
    // 传输完成处理
    LCD_DMA_Controller* dmaCtrl = GetDMAControllerFromHandle(hdma);
    if(dmaCtrl) {
        dmaCtrl->transferComplete = 1;
    }
}

// 使用DMA传输图像
void LCD_DMA_TransferImage(SPI_LCD_TypeDef* lcd, LCD_DMA_Controller* dmaCtrl,
                          uint16_t x, uint16_t y, uint16_t w, uint16_t h,
                          const uint8_t* image) {
    // 等待上一次传输完成
    while(!dmaCtrl->transferComplete);
    
    dmaCtrl->transferComplete = 0;
    
    // 设置显示区域
    SPI_LCD_SetWindow(lcd, x, y, x + w - 1, y + h - 1);
    
    // 准备数据
    uint32_t dataSize = w * h * 2; // 16位颜色
    
    // 使用DMA传输
    HAL_GPIO_WritePin(lcd->dcPort, lcd->dcPin, GPIO_PIN_SET); // 数据模式
    HAL_GPIO_WritePin(lcd->csPort, lcd->csPin, GPIO_PIN_RESET);
    
    // 启动DMA传输
    HAL_DMA_Start_IT(dmaCtrl->dma, (uint32_t)image, 
                    (uint32_t)&lcd->spi->DR, dataSize);
    
    // 等待传输完成(异步方式)
}
3.4.2 双缓冲技术
c 复制代码
// 双缓冲显示控制器
typedef struct {
    uint8_t* frontBuffer;    // 前台缓冲区
    uint8_t* backBuffer;     // 后台缓冲区
    uint32_t bufferSize;     // 缓冲区大小
    uint8_t activeBuffer;    // 当前活动缓冲区(0:前台, 1:后台)
    uint8_t vsync;           // 垂直同步标志
} LCD_DoubleBuffer;

// 初始化双缓冲
void LCD_DoubleBuffer_Init(LCD_DoubleBuffer* db, uint32_t width, uint32_t height) {
    db->bufferSize = width * height * 2; // 16位颜色
    db->frontBuffer = (uint8_t*)malloc(db->bufferSize);
    db->backBuffer = (uint8_t*)malloc(db->bufferSize);
    db->activeBuffer = 0;
    db->vsync = 0;
    
    // 清空缓冲区
    memset(db->frontBuffer, 0, db->bufferSize);
    memset(db->backBuffer, 0, db->bufferSize);
}

// 交换缓冲区
void LCD_DoubleBuffer_Swap(LCD_DoubleBuffer* db) {
    // 等待VSync
    while(!db->vsync);
    db->vsync = 0;
    
    // 交换缓冲区指针
    uint8_t* temp = db->frontBuffer;
    db->frontBuffer = db->backBuffer;
    db->backBuffer = temp;
    
    // 切换活动缓冲区
    db->activeBuffer = !db->activeBuffer;
}

// 获取当前绘图缓冲区
uint8_t* LCD_DoubleBuffer_GetDrawBuffer(LCD_DoubleBuffer* db) {
    return db->backBuffer;
}

// 更新显示(从后台缓冲区复制到前台)
void LCD_DoubleBuffer_UpdateDisplay(SPI_LCD_TypeDef* lcd, 
                                   LCD_DoubleBuffer* db) {
    // 设置显示区域
    SPI_LCD_SetWindow(lcd, 0, 0, lcd->width - 1, lcd->height - 1);
    
    // 传输数据
    HAL_GPIO_WritePin(lcd->dcPort, lcd->dcPin, GPIO_PIN_SET);
    HAL_GPIO_WritePin(lcd->csPort, lcd->csPin, GPIO_PIN_RESET);
    
    // 使用SPI传输后台缓冲区数据
    HAL_SPI_Transmit(lcd->spi, db->backBuffer, db->bufferSize, HAL_MAX_DELAY);
    
    HAL_GPIO_WritePin(lcd->csPort, lcd->csPin, GPIO_PIN_SET);
    
    // 设置VSync标志
    db->vsync = 1;
}

4. 数码管与LCD对比分析

4.1 技术特性对比

特性 数码管 LCD
显示原理 LED发光 液晶分子偏转
显示内容 数字、简单字符 图形、文字、图像
颜色 单色(通常) 彩色/单色
视角 较窄(需背光)
响应时间 快(ns级) 慢(ms级)
功耗 较高 较低
成本 中等
驱动复杂度 简单 复杂
适用场景 数字显示 图形界面

4.2 应用场景选择建议

  1. 数码管适用场景

    • 工业仪表显示
    • 电梯楼层显示
    • 温度计/计时器
    • 简单状态指示
  2. LCD适用场景

    • 图形用户界面
    • 数据显示图表
    • 菜单系统
    • 图像显示

5. 综合应用实例

5.1 智能仪表显示系统

c 复制代码
// 智能仪表显示控制器
typedef struct {
    DynamicDigitalTube digitalTube;  // 数码管显示
    LCD_GraphicLib lcd;              // LCD显示
    uint8_t displayMode;             // 显示模式
    float temperature;               // 温度值
    float pressure;                  // 压力值
    float flowRate;                  // 流量值
    uint32_t updateTime;             // 更新时间
} SmartMeterDisplay;

// 初始化智能仪表
void SmartMeter_Init(SmartMeterDisplay* meter) {
    // 初始化数码管
    DynamicDigitalTube_Init(&meter->digitalTube);
    
    // 初始化LCD
    LCD_GraphicLib_Init(&meter->lcd);
    
    meter->displayMode = 0;
    meter->updateTime = HAL_GetTick();
    
    // 初始化显示内容
    meter->digitalTube.buffer[0] = 0;
    meter->digitalTube.buffer[1] = 0;
    meter->digitalTube.buffer[2] = 0;
    meter->digitalTube.buffer[3] = 0;
}

// 更新显示
void SmartMeter_UpdateDisplay(SmartMeterDisplay* meter) {
    uint32_t currentTime = HAL_GetTick();
    
    if(currentTime - meter->updateTime < 100) { // 100ms更新一次
        return;
    }
    
    meter->updateTime = currentTime;
    
    // 更新数码管显示
    uint16_t value = 0;
    switch(meter->displayMode) {
        case 0: // 显示温度
            value = (uint16_t)(meter->temperature * 10);
            break;
        case 1: // 显示压力
            value = (uint16_t)(meter->pressure);
            break;
        case 2: // 显示流量
            value = (uint16_t)(meter->flowRate * 100);
            break;
    }
    
    DynamicDigitalTube_SetNumber(&meter->digitalTube, value);
    
    // 更新LCD显示
    LCD_Clear(&meter->lcd, BLACK);
    
    // 绘制标题
    const char* titles[] = {"Temperature", "Pressure", "Flow Rate"};
    LCD_DrawString(&meter->lcd, 10, 10, titles[meter->displayMode], 
                   &Font16x24, WHITE);
    
    // 绘制数值
    char valueStr[20];
    switch(meter->displayMode) {
        case 0:
            sprintf(valueStr, "%.1f °C", meter->temperature);
            break;
        case 1:
            sprintf(valueStr, "%.0f kPa", meter->pressure);
            break;
        case 2:
            sprintf(valueStr, "%.2f L/min", meter->flowRate);
            break;
    }
    
    LCD_DrawString(&meter->lcd, 50, 100, valueStr, &Font24x32, GREEN);
    
    // 绘制单位
    const char* units[] = {"°C", "kPa", "L/min"};
    LCD_DrawString(&meter->lcd, 200, 100, units[meter->displayMode], 
                   &Font16x24, YELLOW);
    
    // 绘制进度条
    float normalizedValue = 0;
    switch(meter->displayMode) {
        case 0:
            normalizedValue = meter->temperature / 100.0f;
            break;
        case 1:
            normalizedValue = meter->pressure / 1000.0f;
            break;
        case 2:
            normalizedValue = meter->flowRate / 50.0f;
            break;
    }
    
    if(normalizedValue > 1.0f) normalizedValue = 1.0f;
    if(normalizedValue < 0.0f) normalizedValue = 0.0f;
    
    // 绘制背景条
    LCD_DrawRectangle(&meter->lcd, 10, 200, 230, 220, GRAY);
    LCD_FillRect(&meter->lcd, 10, 200, 220, 20, DARK_GRAY);
    
    // 绘制进度
    uint16_t progressWidth = (uint16_t)(220 * normalizedValue);
    uint16_t color;
    if(normalizedValue < 0.3) {
        color = GREEN;
    } else if(normalizedValue < 0.7) {
        color = YELLOW;
    } else {
        color = RED;
    }
    
    LCD_FillRect(&meter->lcd, 10, 200, progressWidth, 20, color);
    
    // 绘制刻度
    for(int i = 0; i <= 10; i++) {
        uint16_t x = 10 + i * 22;
        LCD_DrawLine(&meter->lcd, x, 220, x, 225, WHITE);
    }
    
    // 绘制数据趋势图(简化)
    static float history[20] = {0};
    static uint8_t historyIndex = 0;
    
    history[historyIndex] = normalizedValue;
    historyIndex = (historyIndex + 1) % 20;
    
    // 绘制趋势线
    for(int i = 0; i < 19; i++) {
        uint16_t x1 = 10 + i * 12;
        uint16_t x2 = 10 + (i + 1) * 12;
        uint16_t y1 = 250 - (uint16_t)(history[i] * 40);
        uint16_t y2 = 250 - (uint16_t)(history[(i + 1) % 20] * 40);
        
        LCD_DrawLine(&meter->lcd, x1, y1, x2, y2, CYAN);
    }
}

// 切换显示模式
void SmartMeter_SwitchMode(SmartMeterDisplay* meter) {
    meter->displayMode = (meter->displayMode + 1) % 3;
}

// 主循环处理
void SmartMeter_MainLoop(SmartMeterDisplay* meter) {
    // 扫描数码管
    DynamicDigitalTube_Scan(&meter->digitalTube);
    
    // 更新LCD显示
    SmartMeter_UpdateDisplay(meter);
    
    // 处理按键输入
    if(Button_Pressed(BUTTON_MODE)) {
        SmartMeter_SwitchMode(meter);
    }
}

5.2 多功能工业控制器界面

c 复制代码
// 工业控制器界面
typedef struct {
    LCD_GraphicLib lcd;              // LCD显示
    uint8_t currentScreen;           // 当前屏幕
    uint8_t selectedItem;            // 选中项
    float processValues[8];          // 过程值
    uint8_t alarmStatus[8];          // 报警状态
    uint32_t lastUpdate;             // 最后更新时间
} IndustrialControllerUI;

// 屏幕枚举
typedef enum {
    SCREEN_MAIN = 0,
    SCREEN_TREND,
    SCREEN_ALARMS,
    SCREEN_SETTINGS,
    SCREEN_COUNT
} ScreenType;

// 初始化工业控制器界面
void IndustrialController_Init(IndustrialControllerUI* ctrl) {
    LCD_GraphicLib_Init(&ctrl->lcd);
    ctrl->currentScreen = SCREEN_MAIN;
    ctrl->selectedItem = 0;
    ctrl->lastUpdate = HAL_GetTick();
    
    // 初始化过程值
    for(int i = 0; i < 8; i++) {
        ctrl->processValues[i] = 0.0f;
        ctrl->alarmStatus[i] = 0;
    }
}

// 绘制主屏幕
void DrawMainScreen(IndustrialControllerUI* ctrl) {
    // 清屏
    LCD_Clear(&ctrl->lcd, DARK_BLUE);
    
    // 绘制标题栏
    LCD_FillRect(&ctrl->lcd, 0, 0, ctrl->lcd.width, 30, BLUE);
    LCD_DrawString(&ctrl->lcd, 10, 5, "Industrial Controller", &Font16x24, WHITE);
    
    // 绘制当前时间
    char timeStr[20];
    RTC_TimeTypeDef time;
    RTC_DateTypeDef date;
    HAL_RTC_GetTime(&hrtc, &time, RTC_FORMAT_BIN);
    HAL_RTC_GetDate(&hrtc, &date, RTC_FORMAT_BIN);
    
    sprintf(timeStr, "%02d:%02d:%02d", time.Hours, time.Minutes, time.Seconds);
    LCD_DrawString(&ctrl->lcd, ctrl->lcd.width - 100, 5, timeStr, &Font12x16, WHITE);
    
    // 绘制过程值显示区域
    for(int i = 0; i < 4; i++) {
        uint16_t x = 10 + (i % 2) * 115;
        uint16_t y = 40 + (i / 2) * 70;
        
        // 绘制背景框
        LCD_DrawRoundRect(&ctrl->lcd, x, y, 100, 60, 5, 
                         ctrl->selectedItem == i ? YELLOW : GRAY);
        LCD_FillRect(&ctrl->lcd, x + 2, y + 2, 96, 56, DARK_GRAY);
        
        // 绘制标签
        char label[20];
        sprintf(label, "PV%d:", i + 1);
        LCD_DrawString(&ctrl->lcd, x + 10, y + 10, label, &Font12x16, WHITE);
        
        // 绘制数值
        char valueStr[20];
        sprintf(valueStr, "%.1f", ctrl->processValues[i]);
        LCD_DrawString(&ctrl->lcd, x + 10, y + 30, valueStr, &Font16x24, GREEN);
        
        // 绘制报警指示
        if(ctrl->alarmStatus[i]) {
            LCD_FillCircle(&ctrl->lcd, x + 85, y + 15, 5, RED);
        }
    }
    
    // 绘制底部状态栏
    LCD_FillRect(&ctrl->lcd, 0, ctrl->lcd.height - 30, 
                 ctrl->lcd.width, 30, BLUE);
    
    // 绘制操作提示
    LCD_DrawString(&ctrl->lcd, 10, ctrl->lcd.height - 25, 
                   "F1:Trend F2:Alarms F3:Settings", &Font12x16, WHITE);
}

// 绘制趋势图屏幕
void DrawTrendScreen(IndustrialControllerUI* ctrl) {
    // 清屏
    LCD_Clear(&ctrl->lcd, BLACK);
    
    // 绘制标题
    LCD_DrawString(&ctrl->lcd, 10, 10, "Process Trend", &Font16x24, WHITE);
    
    // 绘制坐标系
    LCD_DrawRectangle(&ctrl->lcd, 20, 50, 280, 160, WHITE);
    
    // 绘制坐标轴标签
    LCD_DrawString(&ctrl->lcd, 10, 60, "Value", &Font12x16, WHITE);
    LCD_DrawString(&ctrl->lcd, 150, 220, "Time (s)", &Font12x16, WHITE);
    
    // 绘制网格
    for(int i = 0; i <= 10; i++) {
        uint16_t y = 50 + i * 16;
        LCD_DrawLine(&ctrl->lcd, 20, y, 300, y, DARK_GRAY);
    }
    
    for(int i = 0; i <= 10; i++) {
        uint16_t x = 20 + i * 28;
        LCD_DrawLine(&ctrl->lcd, x, 50, x, 210, DARK_GRAY);
    }
    
    // 绘制趋势线(模拟数据)
    static float history[100];
    static uint8_t historyIndex = 0;
    
    // 生成模拟数据
    static uint32_t lastUpdate = 0;
    if(HAL_GetTick() - lastUpdate > 100) { // 每100ms更新一次
        lastUpdate = HAL_GetTick();
        
        // 模拟正弦波叠加噪声
        float time = HAL_GetTick() / 1000.0f;
        history[historyIndex] = 50.0f + 30.0f * sin(time) + 
                              5.0f * sin(time * 2.0f) +
                              2.0f * ((rand() % 100) / 100.0f - 0.5f);
        
        historyIndex = (historyIndex + 1) % 100;
    }
    
    // 绘制趋势线
    uint16_t colors[] = {RED, GREEN, BLUE, YELLOW, CYAN, MAGENTA};
    
    for(int ch = 0; ch < 2; ch++) { // 显示2个通道
        uint16_t color = colors[ch % 6];
        
        for(int i = 0; i < 99; i++) {
            uint16_t index1 = (historyIndex - 100 + i) % 100;
            uint16_t index2 = (historyIndex - 100 + i + 1) % 100;
            
            // 归一化到显示范围
            float minVal = 0.0f;
            float maxVal = 100.0f;
            float val1 = (history[index1] - minVal) / (maxVal - minVal);
            float val2 = (history[index2] - minVal) / (maxVal - minVal);
            
            // 计算屏幕坐标
            uint16_t x1 = 20 + i * 2.8f;
            uint16_t x2 = 20 + (i + 1) * 2.8f;
            uint16_t y1 = 210 - (uint16_t)(val1 * 160);
            uint16_t y2 = 210 - (uint16_t)(val2 * 160);
            
            // 限制在显示区域内
            if(y1 < 50) y1 = 50;
            if(y1 > 210) y1 = 210;
            if(y2 < 50) y2 = 50;
            if(y2 > 210) y2 = 210;
            
            LCD_DrawLine(&ctrl->lcd, x1, y1, x2, y2, color);
        }
    }
    
    // 绘制图例
    LCD_DrawString(&ctrl->lcd, 20, 220, "CH1", &Font12x16, RED);
    LCD_DrawString(&ctrl->lcd, 80, 220, "CH2", &Font12x16, GREEN);
    
    // 绘制当前值
    char valueStr[50];
    sprintf(valueStr, "Current: %.1f", history[(historyIndex - 1) % 100]);
    LCD_DrawString(&ctrl->lcd, 150, 220, valueStr, &Font12x16, YELLOW);
}

// 绘制报警屏幕
void DrawAlarmScreen(IndustrialControllerUI* ctrl) {
    // 清屏
    LCD_Clear(&ctrl->lcd, BLACK);
    
    // 绘制标题
    LCD_DrawString(&ctrl->lcd, 10, 10, "Alarm Status", &Font16x24, RED);
    
    // 绘制报警列表
    char alarmText[8][50] = {
        "PV1: High Temperature",
        "PV2: Low Pressure",
        "PV3: Flow Rate High",
        "PV4: Level Low",
        "PV5: Motor Overload",
        "PV6: Communication Fail",
        "PV7: Sensor Error",
        "PV8: System Fault"
    };
    
    for(int i = 0; i < 8; i++) {
        uint16_t y = 50 + i * 25;
        
        if(ctrl->alarmStatus[i]) {
            // 报警项
            LCD_FillRect(&ctrl->lcd, 10, y - 2, ctrl->lcd.width - 20, 22, DARK_RED);
            LCD_DrawString(&ctrl->lcd, 15, y, alarmText[i], &Font12x16, WHITE);
            
            // 绘制报警图标
            LCD_FillCircle(&ctrl->lcd, ctrl->lcd.width - 30, y + 8, 5, RED);
            
            // 闪烁效果
            static uint8_t blink = 0;
            if((HAL_GetTick() / 500) % 2) {
                LCD_DrawCircle(&ctrl->lcd, ctrl->lcd.width - 30, y + 8, 5, YELLOW);
            }
        } else {
            // 正常项
            LCD_DrawString(&ctrl->lcd, 15, y, alarmText[i], &Font12x16, GRAY);
        }
    }
    
    // 绘制统计信息
    uint8_t activeAlarms = 0;
    for(int i = 0; i < 8; i++) {
        if(ctrl->alarmStatus[i]) activeAlarms++;
    }
    
    char statStr[50];
    sprintf(statStr, "Active Alarms: %d", activeAlarms);
    LCD_DrawString(&ctrl->lcd, 10, ctrl->lcd.height - 30, 
                   statStr, &Font12x16, activeAlarms > 0 ? RED : GREEN);
}

// 绘制设置屏幕
void DrawSettingsScreen(IndustrialControllerUI* ctrl) {
    // 清屏
    LCD_Clear(&ctrl->lcd, DARK_GREEN);
    
    // 绘制标题
    LCD_DrawString(&ctrl->lcd, 10, 10, "System Settings", &Font16x24, WHITE);
    
    // 设置选项
    char* menuItems[] = {
        "Communication Settings",
        "Alarm Configuration",
        "Display Settings",
        "Calibration",
        "System Information",
        "Save Configuration",
        "Load Defaults",
        "Exit Settings"
    };
    
    for(int i = 0; i < 8; i++) {
        uint16_t y = 50 + i * 25;
        
        if(i == ctrl->selectedItem) {
            // 选中项
            LCD_FillRect(&ctrl->lcd, 10, y - 2, ctrl->lcd.width - 20, 22, GREEN);
            LCD_DrawString(&ctrl->lcd, 15, y, menuItems[i], &Font12x16, BLACK);
            
            // 绘制选中标记
            LCD_DrawString(&ctrl->lcd, ctrl->lcd.width - 40, y, ">", &Font12x16, BLACK);
        } else {
            // 未选中项
            LCD_DrawString(&ctrl->lcd, 15, y, menuItems[i], &Font12x16, WHITE);
        }
    }
    
    // 绘制说明
    LCD_DrawString(&ctrl->lcd, 10, ctrl->lcd.height - 30,
                   "Use UP/DOWN to navigate, ENTER to select", 
                   &Font12x16, YELLOW);
}

// 更新显示
void IndustrialController_UpdateDisplay(IndustrialControllerUI* ctrl) {
    if(HAL_GetTick() - ctrl->lastUpdate < 50) { // 20Hz刷新率
        return;
    }
    
    ctrl->lastUpdate = HAL_GetTick();
    
    switch(ctrl->currentScreen) {
        case SCREEN_MAIN:
            DrawMainScreen(ctrl);
            break;
        case SCREEN_TREND:
            DrawTrendScreen(ctrl);
            break;
        case SCREEN_ALARMS:
            DrawAlarmScreen(ctrl);
            break;
        case SCREEN_SETTINGS:
            DrawSettingsScreen(ctrl);
            break;
    }
}

// 处理用户输入
void IndustrialController_HandleInput(IndustrialControllerUI* ctrl, 
                                     uint8_t key) {
    switch(key) {
        case KEY_F1:
            ctrl->currentScreen = SCREEN_TREND;
            break;
        case KEY_F2:
            ctrl->currentScreen = SCREEN_ALARMS;
            break;
        case KEY_F3:
            ctrl->currentScreen = SCREEN_SETTINGS;
            ctrl->selectedItem = 0;
            break;
        case KEY_UP:
            if(ctrl->currentScreen == SCREEN_SETTINGS) {
                if(ctrl->selectedItem > 0) ctrl->selectedItem--;
            }
            break;
        case KEY_DOWN:
            if(ctrl->currentScreen == SCREEN_SETTINGS) {
                if(ctrl->selectedItem < 7) ctrl->selectedItem++;
            }
            break;
        case KEY_ENTER:
            if(ctrl->currentScreen == SCREEN_SETTINGS) {
                // 执行选中的设置项
                ExecuteSetting(ctrl->selectedItem);
            }
            break;
        case KEY_ESC:
            ctrl->currentScreen = SCREEN_MAIN;
            break;
    }
}

// 主循环
void IndustrialController_MainLoop(IndustrialControllerUI* ctrl) {
    // 更新显示
    IndustrialController_UpdateDisplay(ctrl);
    
    // 处理输入
    uint8_t key = ReadKeypad();
    if(key != KEY_NONE) {
        IndustrialController_HandleInput(ctrl, key);
    }
    
    // 模拟数据更新
    static uint32_t lastDataUpdate = 0;
    if(HAL_GetTick() - lastDataUpdate > 1000) { // 每秒更新一次
        lastDataUpdate = HAL_GetTick();
        
        // 更新过程值(模拟)
        for(int i = 0; i < 8; i++) {
            ctrl->processValues[i] = 50.0f + 30.0f * sin(HAL_GetTick() / 1000.0f + i);
            
            // 随机触发报警
            if(rand() % 100 < 5) { // 5%概率
                ctrl->alarmStatus[i] = !ctrl->alarmStatus[i];
            }
        }
    }
}

6. 性能优化与调试技巧

6.1 显示性能优化

c 复制代码
// 性能优化示例:快速区域填充算法
void LCD_FillRectOptimized(LCD_GraphicLib* lib, uint16_t x, uint16_t y,
                          uint16_t w, uint16_t h, uint16_t color) {
    // 检查边界
    if(x >= lib->width || y >= lib->height) return;
    if(x + w > lib->width) w = lib->width - x;
    if(y + h > lib->height) h = lib->height - y;
    
    // 使用DMA加速填充
    if(lib->interfaceType == LCD_INTERFACE_FSMC) {
        // 设置显示区域
        LCD_WriteCmd(lib->fsmcLcd, lib->fsmcLcd->setxcmd);
        LCD_WriteData(lib->fsmcLcd, x >> 8);
        LCD_WriteData(lib->fsmcLcd, x & 0xFF);
        LCD_WriteData(lib->fsmcLcd, (x + w - 1) >> 8);
        LCD_WriteData(lib->fsmcLcd, (x + w - 1) & 0xFF);
        
        LCD_WriteCmd(lib->fsmcLcd, lib->fsmcLcd->setycmd);
        LCD_WriteData(lib->fsmcLcd, y >> 8);
        LCD_WriteData(lib->fsmcLcd, y & 0xFF);
        LCD_WriteData(lib->fsmcLcd, (y + h - 1) >> 8);
        LCD_WriteData(lib->fsmcLcd, (y + h - 1) & 0xFF);
        
        LCD_WriteCmd(lib->fsmcLcd, lib->fsmcLcd->wramcmd);
        
        // 使用内存填充模式
        uint32_t pixelCount = w * h;
        uint16_t* buffer = (uint16_t*)lib->fsmcLcd->LCD_RAM;
        
        // 批量写入
        while(pixelCount--) {
            *buffer = color;
        }
    }
}

// 使用缓存优化频繁更新的显示
typedef struct {
    uint8_t* dirtyBuffer;      // 脏标记缓冲区
    uint16_t* pixelBuffer;     // 像素缓冲区
    uint16_t width;
    uint16_t height;
    uint8_t needsFullUpdate;   // 需要全屏更新
} LCD_CacheManager;

// 标记需要更新的区域
void LCD_MarkDirty(LCD_CacheManager* cache, uint16_t x, uint16_t y, 
                  uint16_t w, uint16_t h) {
    for(uint16_t j = y; j < y + h && j < cache->height; j++) {
        for(uint16_t i = x; i < x + w && i < cache->width; i++) {
            cache->dirtyBuffer[j * cache->width + i] = 1;
        }
    }
}

// 增量更新显示
void LCD_UpdateDirty(LCD_GraphicLib* lib, LCD_CacheManager* cache) {
    if(cache->needsFullUpdate) {
        // 全屏更新
        LCD_UpdateFull(lib, cache->pixelBuffer);
        memset(cache->dirtyBuffer, 0, cache->width * cache->height);
        cache->needsFullUpdate = 0;
        return;
    }
    
    // 查找脏区域
    uint16_t minX = cache->width, maxX = 0;
    uint16_t minY = cache->height, maxY = 0;
    uint8_t foundDirty = 0;
    
    for(uint16_t y = 0; y < cache->height; y++) {
        for(uint16_t x = 0; x < cache->width; x++) {
            if(cache->dirtyBuffer[y * cache->width + x]) {
                foundDirty = 1;
                if(x < minX) minX = x;
                if(x > maxX) maxX = x;
                if(y < minY) minY = y;
                if(y > maxY) maxY = y;
            }
        }
    }
    
    if(!foundDirty) return;
    
    // 更新脏区域
    uint16_t w = maxX - minX + 1;
    uint16_t h = maxY - minY + 1;
    
    // 设置显示区域
    if(lib->interfaceType == LCD_INTERFACE_FSMC) {
        LCD_WriteCmd(lib->fsmcLcd, lib->fsmcLcd->setxcmd);
        LCD_WriteData(lib->fsmcLcd, minX >> 8);
        LCD_WriteData(lib->fsmcLcd, minX & 0xFF);
        LCD_WriteData(lib->fsmcLcd, maxX >> 8);
        LCD_WriteData(lib->fsmcLcd, maxX & 0xFF);
        
        LCD_WriteCmd(lib->fsmcLcd, lib->fsmcLcd->setycmd);
        LCD_WriteData(lib->fsmcLcd, minY >> 8);
        LCD_WriteData(lib->fsmcLcd, minY & 0xFF);
        LCD_WriteData(lib->fsmcLcd, maxY >> 8);
        LCD_WriteData(lib->fsmcLcd, maxY & 0xFF);
        
        LCD_WriteCmd(lib->fsmcLcd, lib->fsmcLcd->wramcmd);
        
        // 复制脏区域数据
        for(uint16_t y = minY; y <= maxY; y++) {
            for(uint16_t x = minX; x <= maxX; x++) {
                if(cache->dirtyBuffer[y * cache->width + x]) {
                    LCD_WriteData(lib->fsmcLcd, 
                                 cache->pixelBuffer[y * cache->width + x]);
                    cache->dirtyBuffer[y * cache->width + x] = 0;
                }
            }
        }
    }
}

6.2 调试与故障排除

c 复制代码
// LCD调试工具
typedef struct {
    uint32_t frameCount;       // 帧计数
    uint32_t lastFrameTime;    // 上一帧时间
    uint32_t frameTime;        // 帧时间
    uint32_t minFrameTime;     // 最小帧时间
    uint32_t maxFrameTime;     // 最大帧时间
    uint32_t drawCallCount;    // 绘制调用计数
    uint8_t showStats;         // 显示统计信息
} LCD_DebugInfo;

// 初始化调试信息
void LCD_Debug_Init(LCD_DebugInfo* debug) {
    debug->frameCount = 0;
    debug->lastFrameTime = HAL_GetTick();
    debug->frameTime = 0;
    debug->minFrameTime = 0xFFFFFFFF;
    debug->maxFrameTime = 0;
    debug->drawCallCount = 0;
    debug->showStats = 1;
}

// 开始帧计时
void LCD_Debug_StartFrame(LCD_DebugInfo* debug) {
    debug->drawCallCount = 0;
    debug->lastFrameTime = HAL_GetTick();
}

// 结束帧计时
void LCD_Debug_EndFrame(LCD_DebugInfo* debug) {
    uint32_t currentTime = HAL_GetTick();
    debug->frameTime = currentTime - debug->lastFrameTime;
    
    if(debug->frameTime < debug->minFrameTime) {
        debug->minFrameTime = debug->frameTime;
    }
    if(debug->frameTime > debug->maxFrameTime) {
        debug->maxFrameTime = debug->frameTime;
    }
    
    debug->frameCount++;
}

// 记录绘制调用
void LCD_Debug_RecordDrawCall(LCD_DebugInfo* debug) {
    debug->drawCallCount++;
}

// 显示调试信息
void LCD_Debug_ShowStats(LCD_GraphicLib* lib, LCD_DebugInfo* debug) {
    if(!debug->showStats) return;
    
    char stats[100];
    
    // 绘制背景
    LCD_FillRect(lib, 0, 0, 200, 70, 0x0000); // 半透明黑色
    
    // 显示帧率
    float fps = 1000.0f / debug->frameTime;
    sprintf(stats, "FPS: %.1f", fps);
    LCD_DrawString(lib, 5, 5, stats, &Font12x16, GREEN);
    
    // 显示帧时间
    sprintf(stats, "Frame: %lums", debug->frameTime);
    LCD_DrawString(lib, 5, 25, stats, &Font12x16, YELLOW);
    
    // 显示绘制调用
    sprintf(stats, "Draw Calls: %d", debug->drawCallCount);
    LCD_DrawString(lib, 5, 45, stats, &Font12x16, CYAN);
    
    // 显示帧计数
    sprintf(stats, "Frames: %lu", debug->frameCount);
    LCD_DrawString(lib, 5, 65, stats, &Font12x16, MAGENTA);
}

// LCD自检程序
void LCD_SelfTest(LCD_GraphicLib* lib) {
    // 1. 清屏测试
    LCD_Clear(lib, BLACK);
    HAL_Delay(500);
    
    // 2. 颜色条测试
    uint16_t colors[] = {RED, GREEN, BLUE, YELLOW, CYAN, MAGENTA, WHITE};
    uint16_t barWidth = lib->width / 7;
    
    for(int i = 0; i < 7; i++) {
        LCD_FillRect(lib, i * barWidth, 0, barWidth, lib->height, colors[i]);
    }
    HAL_Delay(1000);
    
    // 3. 渐变测试
    LCD_Clear(lib, BLACK);
    for(uint16_t y = 0; y < lib->height; y++) {
        uint8_t r = y * 31 / lib->height;
        uint8_t g = (lib->height - y) * 63 / lib->height;
        uint8_t b = y * 31 / lib->height;
        uint16_t color = (r << 11) | (g << 5) | b;
        
        LCD_DrawLine(lib, 0, y, lib->width - 1, y, color);
    }
    HAL_Delay(1000);
    
    // 4. 文本测试
    LCD_Clear(lib, DARK_BLUE);
    LCD_DrawString(lib, 10, 10, "LCD Self Test", &Font16x24, YELLOW);
    LCD_DrawString(lib, 10, 50, "STM32 Graphics Test", &Font12x16, GREEN);
    LCD_DrawString(lib, 10, 80, "Resolution:", &Font12x16, WHITE);
    
    char resStr[50];
    sprintf(resStr, "%dx%d", lib->width, lib->height);
    LCD_DrawString(lib, 120, 80, resStr, &Font12x16, CYAN);
    
    // 5. 图形测试
    LCD_DrawCircle(lib, lib->width / 2, lib->height / 2, 50, RED);
    LCD_DrawRectangle(lib, lib->width / 2 - 60, lib->height / 2 - 60, 
                     lib->width / 2 + 60, lib->height / 2 + 60, GREEN);
    LCD_DrawTriangle(lib, lib->width / 2, lib->height / 2 - 80,
                    lib->width / 2 - 60, lib->height / 2 + 40,
                    lib->width / 2 + 60, lib->height / 2 + 40, BLUE);
    
    HAL_Delay(2000);
    
    // 6. 像素点测试
    LCD_Clear(lib, BLACK);
    for(int i = 0; i < 1000; i++) {
        uint16_t x = rand() % lib->width;
        uint16_t y = rand() % lib->height;
        uint16_t color = rand() & 0xFFFF;
        LCD_DrawPixel(lib, x, y, color);
    }
    HAL_Delay(1000);
    
    // 7. 最终状态
    LCD_Clear(lib, GREEN);
    LCD_DrawString(lib, lib->width / 2 - 40, lib->height / 2 - 10,
                   "PASS", &Font16x24, WHITE);
    HAL_Delay(500);
}

7. 总结

本文详细分析了STM32驱动数码管和LCD显示器的原理与技术实现,涵盖了从基础理论到高级应用的完整知识体系。主要内容总结如下:

7.1 数码管显示技术

  • 基本原理:介绍了数码管的结构、分类和驱动方式
  • 驱动实现:提供了静态和动态扫描的完整代码实现
  • 优化技术:包括PWM调光、消隐处理等高级技术

7.2 LCD显示技术

  • 工作原理:详细分析了LCD的显示原理和接口类型
  • 驱动实现:提供了FSMC并行接口和SPI串行接口的驱动代码
  • 图形库:实现了完整的图形绘制函数库
  • 优化技术:包括DMA传输、双缓冲、脏矩形更新等

7.3 综合应用

  • 智能仪表:结合数码管和LCD的混合显示系统
  • 工业控制器:完整的图形用户界面实现
  • 性能优化:各种显示性能优化技术
  • 调试工具:实用的调试和故障排除方法

7.4 技术展望

随着嵌入式系统的发展,显示技术也在不断进步。未来的发展方向包括:

  1. 高分辨率显示:支持更高分辨率的LCD显示屏
  2. 触摸屏集成:电容/电阻触摸屏的集成应用
  3. 3D图形加速:硬件加速的3D图形渲染
  4. 低功耗显示:节能显示技术的应用
  5. 无线显示:无线传输显示内容

通过本文的学习,读者可以掌握STM32显示系统开发的核心技术,为开发复杂的嵌入式图形界面应用奠定坚实基础。


注意:本文提供的代码示例需要在具体的STM32硬件平台上进行适配和测试。实际应用中需要考虑硬件资源限制、电源管理、电磁兼容性等因素。建议在理解原理的基础上,根据具体需求进行适当的修改和优化。

相关推荐
公众号:ITIL之家2 小时前
服务价值体系重构:在变化中寻找不变的运维本质
java·运维·开发语言·数据库·重构
d111111111d2 小时前
STM32 DMA传输配置详解:数据宽度与传输方向设置指南
笔记·stm32·单片机·嵌入式硬件·学习
橙汁味的风2 小时前
《数据库系统概论》陈红、卢卫 - 11 - 数据库恢复技术
数据库·数据库系统概论
qq_455760852 小时前
redis - 事务
数据库·redis·缓存
清风6666662 小时前
基于单片机的多路热电偶温度监测与报警器
数据库·单片机·mongodb·毕业设计·课程设计·期末大作业
大巨头2 小时前
SQL Server 完整锁类型详解
数据库
Archie_IT2 小时前
基于STM32F103C8T6标准库的OLED显示屏中文汉字显示实现_资料编号39
stm32·单片机·嵌入式硬件·mcu·硬件工程·信息与通信·信号处理
Damon小智2 小时前
NiFi实现数据存储到数据库
数据库·mysql·docker·postgresql·nifi
任子菲阳2 小时前
学JavaWeb第六天——JDBC & Mybatis
java·数据库·mybatis