/******************************************************************************
 * @file        160160D.C
 * @brief       UC1698U控制器驱动的 160x160 像素 LCD 显示屏驱动文件
 * @details     本文件实现了基于 UC1698U 控制器的 160x160 像素彩色 LCD 显示屏的底层驱动功能，
 *              包括初始化、显示控制、字符显示、图形显示等功能。
 * @author      阜阳师范大学物电学院
 * @version     V0.1
 * @date        2026.1.19
 * @note        控制器: UC1698U
 *              显示屏: 160x160像素
 *              段地址: SEG112~SEG271
 *              外部MPU晶振: 20MHz
 ******************************************************************************/

#include "160160D.h"
#include "GraphicsLibrary.h"
#include "ChineseLibraryCrossWise12x12.h" 
#include "math.h"
#include <string.h>
#include <stdio.h>

/* ============================================================================
 * LCD偏置电压配置宏定义
 * ============================================================================ */
#define LCD_MTPM_MAX        31   /**< MTP（多次可编程）最大值 */
#define LCD_PM_NORMAL       190  /**< 正常偏置电压值 */
#define LCD_PM_MAX          (LCD_PM_NORMAL + LCD_MTPM_MAX)  /**< 最大偏置电压值 */
#define LCD_PM_MIN          (LCD_PM_NORMAL - LCD_MTPM_MAX)  /**< 最小偏置电压值 */

/* ============================================================================
 * 颜色模式配置宏定义
 * ============================================================================ */
#define COLOR_64K_565       0x02  /**< 64K色模式（RGB565格式） */
#define COLOR_4K_444        0x01  /**< 4K色模式（RGB444格式） */

/** @brief 设置颜色模式
 *  @param n 颜色模式值（0x01=4K色，0x02=64K色）
 */
#define set_color_mode(n)            do{WriteCMD(0xD4 | ((n) & 0x03));}while(0)


/* ============================================================================
 * 功能宏定义
 * ============================================================================ */
/** @brief 设置偏置电压电位器
 *  @param n 偏置电压值（0-255）
 */
#define set_vbias_potentiometer(n)   do{WriteCMD(0x81); WriteCMD((n));}while(0)



/* ============================================================================
 * UC1698U控制器寄存器命令定义
 * ============================================================================ */
#define UC1698U_CAL     (0x00)  /**< 列地址低4位（Column Address Low） */
#define UC1698U_CAM     (0x10)  /**< 列地址高3位（Column Address Middle） */
#define UC1698U_WA      /**< 窗口地址（Window Address，未定义值） */
#define UC1698U_TC      (0x24)  /**< 温度补偿控制（Temp. Compensation），低2位有效 */
#define UC1698U_PC      (0x28)  /**< 电源控制（Power Control），低2位有效 */
#define UC1698U_SLL     (0x40)  /**< 滚动行低4位（Scroll Line Low） */
#define UC1698U_SLM     (0x50)  /**< 滚动行高4位（Scroll Line Middle） */
#define UC1698U_RAL     (0x60)  /**< 行地址低4位（Row Address Low） */
#define UC1698U_RAM     (0x70)  /**< 行地址高4位（Row Address Middle） */
#define UC1698U_PM      (0x81)  /**< 偏置电压电位器（Vbias Potentiometer） */
#define UC1698U_LC8     (0x84)  /**< 部分显示控制（Partial Display Control），低1位有效 */
#define UC1698U_AC20    (0x88)  /**< RAM地址控制（RAM Address Control），低3位有效 */
#define CURSOR_R        (0x01)  /**< 光标方向：向右（Cursor Right） */
#define CURSOR_D        (0x03)  /**< 光标方向：向下（Cursor Down） */
#define CURSOR_U        (0x07)  /**< 光标方向：向上（Cursor Up） */

#define UC1698U_FLT_FLB (0x90)  /**< 固定行设置（Fixed Lines Top/Bottom） */
#define UC1698U_LC43    (0xA0)  /**< 行速率控制（Line Rate），低2位有效 */
#define UC1698U_DC1     (0xA4)  /**< 全像素开启（All-Pixel-ON），低1位有效 */
#define UC1698U_DC0     (0xA6)  /**< 反色显示控制（Inverse Display），低1位有效 */
#define UC1698U_DC42    (0xA8)  /**< 显示使能控制（Display Enable），低3位有效 */
#define UC1698U_LC20    (0xC0)  /**< LCD映射控制（LCD Mapping Control），低3位有效 */
#define UC1698U_NIV     (0xC8)  /**< N线反转（N-Line Inversion） */
#define UC1698U_LC5     (0xD0)  /**< 颜色模式（Color Pattern），低1位有效 */
#define UC1698U_LC76    (0xD4)  /**< 颜色模式选择（Color Mode），低2位有效 */
#define UC1698U_CSF20   (0xD8)  /**< COM扫描功能（COM Scan Function），低3位有效 */
#define UC1698U_RST     (0xE2)  /**< 复位命令（RESET） */
#define UC1698U_NOP     (0xE3)  /**< 空操作（NOP - No Operation） */
#define UC1698U_BR10    (0xE8)  /**< LCD偏置比率（LCD Bias Ratio），低2位有效 */
#define UC1698U_CEN60   (0xF1)  /**< COM结束地址（COM End） */
#define UC1698U_DST60   (0xF2)  /**< 部分显示起始地址（Partial Display Start） */
#define UC1698U_DEN60   (0xF3)  /**< 部分显示结束地址（Partial Display End） */
#define UC1698U_WPC0    (0xF4)  /**< 窗口程序起始列地址（Window Program Starting Column Address） */
#define UC1698U_WPP0    (0xF5)  /**< 窗口程序起始行地址（Window Program Starting Row Address） */
#define UC1698U_WPC1    (0xF6)  /**< 窗口程序结束列地址（Window Program Ending Column Address） */
#define UC1698U_WPP1    (0xF7)  /**< 窗口程序结束行地址（Window Program Ending Row Address） */
#define UC1698U_AC3     (0xF8)  /**< 窗口程序控制（Window Program），低1位有效 */
#define UC1698U_MTPC40  (0xB8)  /**< MTP操作控制（MTP Operation Control） */
#define UC1698U_MTPM    (0xB9)  /**< MTP写掩码（MTP Write Mask） */

/* ============================================================================
 * MTP（多次可编程）相关寄存器定义
 * ============================================================================ */
#define UC1698U_MTP1    (0xF4)  /**< MTP1电位器（Vmtp1 Potentiometer） */
#define UC1698U_MTP2    (0xF5)  /**< MTP2电位器（Vmtp2 Potentiometer） */
#define UC1698U_MTP3    (0xF6)  /**< MTP写定时器（MTP Write Timer） */
#define UC1698U_MTP4    (0xF7)  /**< MTP读定时器（MTP Read Timer） */


/* ============================================================================
 * GPIO控制宏定义（硬件接口层）
 * 注意：以下宏定义中部分引脚的高低电平设置可能存在问题，需要根据实际硬件连接进行修正
 * ============================================================================ */
/** @brief 设置CD（Command/Data）引脚为高电平
 *  @note CD引脚用于区分命令和数据：高电平=数据，低电平=命令
 */
#define UC1698U_CD_H()     do{HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, GPIO_PIN_SET);}while(0)

/** @brief 设置CD（Command/Data）引脚为低电平 */
#define UC1698U_CD_L()     do{HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, GPIO_PIN_RESET);}while(0)

/** @brief 设置WR（Write）写信号引脚为高电平 */
#define UC1698U_WR_H()     do{HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_SET);}while(0)

/** @brief 设置WR（Write）写信号引脚为低电平 */
#define UC1698U_WR_L()     do{HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_RESET);}while(0)

/** @brief 设置RD（Read）读信号引脚为高电平 */
#define UC1698U_RD_H()     do{HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET);}while(0)

/** @brief 设置RD（Read）读信号引脚为低电平 */
#define UC1698U_RD_L()     do{HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET);}while(0)

/** @brief 设置CS（Chip Select）片选引脚为高电平（取消选中） */
#define UC1698U_CS_H()     do{HAL_GPIO_WritePin(GPIOB, GPIO_PIN_11, GPIO_PIN_SET);}while(0)

/** @brief 设置CS（Chip Select）片选引脚为低电平（选中芯片） */
#define UC1698U_CS_L()     do{HAL_GPIO_WritePin(GPIOB, GPIO_PIN_11, GPIO_PIN_RESET);}while(0)

/** @brief 设置RST（Reset）复位引脚为高电平（正常工作） */
#define UC1698U_RST_H()    do{HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_SET);}while(0)

/** @brief 设置RST（Reset）复位引脚为低电平（复位状态） */
#define UC1698U_RST_L()    do{HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET);}while(0)

/** @brief 设置背光引脚为关闭 */
#define LCD_BackLight_OFF()       do{HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);}while(0)

/** @brief 设置背光引脚为打开 */
#define LCD_BackLight_ON()        do{HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);}while(0)

volatile static uint32_t BackLightCount = 0; 
#define BackLightTimeMax        (20000)         /* 100s */

/**
 * @brief  背光处理函数
 * @note   背光超时，关闭背光
 * @retval 无
 */
void BackLight_Close(void)
{
    BackLightCount++;
    if(BackLightCount >= BackLightTimeMax)
    {
        BackLightCount = BackLightTimeMax;  //防止参数溢出
        LCD_BackLight_OFF();
    }
}
/**
 * @brief  背光处理函数
 * @note   开启背光，重置背光计时
 * @retval 无
 */
void BackLight_ON(void)
{
    LCD_BackLight_ON();
    BackLightCount = 0;  
}

/* ============================================================================
 * 底层硬件接口函数
 * ============================================================================ */

/**
 * @brief  向LCD屏幕写入8位数据
 * @param  Data 要写入的8位数据（0x00-0xFF）
 * @note   硬件连接使用了PE8-PE15，即GPIOE数据线的高8位
 *          此函数直接操作GPIOE的ODR寄存器，保留低8位不变，更新高8位
 * @retval 无
 */
static void UC1698U_Write(uint8_t Data)
{
    uint16_t itmp = 0;
    
    /* 读取ODR寄存器的低8位，保留原有值 */
    itmp = GPIOE->ODR & 0xFF;
    /* 将数据写入ODR寄存器的高8位（PE8-PE15） */
    GPIOE->ODR = (Data<<8) | itmp;
}

/**
 * @brief  向LCD控制器写入命令
 * @param  data 要写入的命令字节
 * @note   命令写入时序：
 *          1. 设置CD为低电平（表示命令）
 *          2. 设置RD为高电平（读禁止）
 *          3. 写入数据
 *          4. 拉低CS（片选有效）
 *          5. 拉低WR（写有效）
 *          6. 拉高WR（写完成）
 *          7. 拉高CS（片选无效）
 * @retval 无
 */
static void WriteCMD(uint8_t data)
{
    UC1698U_CD_L();        /* 设置为命令模式 */
    UC1698U_RD_H();        /* 禁止读操作 */
    UC1698U_Write(data);   /* 写入命令数据 */
    UC1698U_CS_L();        /* 片选有效 */
    UC1698U_WR_L();        /* 写信号有效 */
    UC1698U_WR_H();        /* 写信号无效，完成写入 */
    UC1698U_CS_H();        /* 片选无效 */
}

/**
 * @brief  向 LCD 控制器写入显示数据
 * @param  data 要写入的数据字节
 * @note   数据写入时序与命令写入类似，但 CD 引脚设置为高电平（表示数据）
 * @retval 无
 */
static void WriteData(uint8_t data)
{
    UC1698U_CD_H();        /* 设置为数据模式 */
    UC1698U_RD_H();        /* 禁止读操作 */
    UC1698U_Write(data);   /* 写入显示数据 */
    UC1698U_CS_L();        /* 片选有效 */
    UC1698U_WR_L();        /* 写信号有效 */
    UC1698U_WR_H();        /* 写信号无效，完成写入 */
    UC1698U_CS_H();        /* 片选无效 */
}

/**
 * @brief  LCD硬件复位函数
 * @note   复位时序：
 *          1. 拉高RST引脚（确保初始状态）
 *          2. 延时10ms
 *          3. 拉低RST引脚（复位有效）
 *          4. 延时2ms（复位保持时间）
 *          5. 拉高RST引脚（复位释放）
 *          6. 延时150ms（等待芯片稳定）
 * @retval 无
 */
void LCD_Reset(void)
{
    UC1698U_RST_H();       /* 复位引脚拉高 */
    delay_ms(10);          /* 延时10ms */
    UC1698U_RST_L();       /* 复位引脚拉低，开始复位 */
    delay_ms(2);           /* 复位保持时间2ms */
    UC1698U_RST_H();       /* 复位引脚拉高，复位完成 */
    delay_ms(150);         /* 等待芯片稳定150ms */
}
/**
 * @brief  LCD初始化函数（XRD版本，黑白模式）
 * @note   本函数按照 UC1698U 控制器的初始化序列进行配置
 *          初始化顺序：
 *          1. 偏置比率设置
 *          2. 电源控制设置
 *          3. 温度补偿设置
 *          4. 偏置电压设置
 *          5. 显示控制设置
 *          6. LCD控制设置
 *          7. 扫描功能设置
 *          8. 窗口显示区域设置
 *          9. RAM 地址控制设置
 *          10. 显示使能
 *          11. 滚动行设置
 *          12. 固定行设置
 *          13. 部分显示设置
 *          14. COM结束地址设置
 * @retval 无
 */
void LCD_InitXRD(void)
{
    /* ========== 偏置和电源控制 ========== */
    WriteCMD(UC1698U_BR10 | 0x01);     /* 设置偏置比率为1/10 */
    WriteCMD(UC1698U_PC | 0x03);       /* 设置电源控制：内部Vlcd */
    WriteCMD(UC1698U_TC | 0x00);       /* 设置温度补偿为0.15% */
    WriteCMD(UC1698U_PM);              /* 设置电子电位器命令 */
    WriteCMD(190);                      /* 设置偏置电压值为190（正常值） */
    
    /* ========== 显示控制 ========== */
    WriteCMD(UC1698U_DC1 | 0x00);      /* 所有像素关闭 */
    WriteCMD(UC1698U_DC0 | 0x00);      /* 反色显示关闭 */
    
    /* ========== LCD控制 ========== */
    WriteCMD(UC1698U_LC20 | 0x00);      /* 部分显示和MX禁用，MY使能 */
    WriteCMD(UC1698U_LC43 | 0x03);     /* 设置行速率为15.2klps */
    WriteCMD(UC1698U_LC5 | 0x01);      /* 设置颜色模式为RGB-RGB */
    WriteCMD(UC1698U_LC76 | 0x01);     /* 设置颜色模式为4K色模式 */
    
    /* ========== 扫描功能 ========== */
    WriteCMD(UC1698U_CSF20 | 0x02);    /* 使能FRC、PWM、LRM序列 */
    
    /* ========== 窗口显示区域设置 ========== */
    /* 说明：列和行设置固定显示区域
     * 当AC[0]=1, AC[1]=0时（自动换行，列优先）
     * 列自动递增将在结束地址后重新开始
     */
    WriteCMD(UC1698U_WPC0);            /* 窗口程序起始列地址 */
    WriteCMD(37);                       /* 列起始地址：37（硬件偏移） */
    WriteCMD(UC1698U_WPC1);            /* 窗口程序结束列地址 */
    WriteCMD(90);                       /* 列结束地址：90（总长53，160*160，X寻址以3位为单位） */

    WriteCMD(UC1698U_WPP0);            /* 窗口程序起始行地址 */
    WriteCMD(0x00);                     /* 行起始地址：0 */
    WriteCMD(UC1698U_WPP1);            /* 窗口程序结束行地址 */
    WriteCMD(159);                      /* 行结束地址：159（共160行） */

    WriteCMD(UC1698U_AC3 | 0x00);      /* 窗口程序模式：内部模式 */
    WriteCMD(UC1698U_AC20 | CURSOR_R); /* RAM 地址控制：X自动加1，加满到53则回0，同时Y加1 */
    WriteCMD(UC1698U_DC42 | 0x05);     /* 显示开启，选择开/关模式，绿色增强模式禁用 */
    
    /* ========== 滚动行设置 ========== */
    WriteCMD(UC1698U_SLL | 0x00);      /* 滚动行低4位 */
    WriteCMD(UC1698U_SLM | 0x00);      /* 滚动行高4位 */
    WriteCMD(UC1698U_LC20 | 0x04);     /* 使能FLT和FLB（固定行顶部和底部） */
    
    WriteCMD(UC1698U_FLT_FLB);         /* 设置FLT和FLB：设置屏幕固定区域大小，配合图像动态显示的命令 */
    WriteCMD(0xFF);                     /* 固定行数据 */
    
    /* ========== 部分显示设置 ========== */
    WriteCMD(UC1698U_LC8 | 0x00);      /* 设置部分显示控制：关闭，DST/DEN不使用 */
    WriteCMD(UC1698U_CEN60);           /* COM结束地址命令 */
    WriteCMD(0x9F);                    /* COM结束地址：159（0x9F） */
}
/**
 * @brief  设置 LCD 显示地址（行列坐标）
 * @param  x 行地址（X坐标，0-53，实际显示时会加37偏移）
 * @param  y 列地址（Y坐标，0-159）
 * @note   X 坐标会自动加上 37 的偏移量（可能是硬件布局导致的）
 *         地址设置分为低4位和高位两部分分别写入
 * @retval 无
 */
static void SetAddress(uint8_t x, uint8_t y)
{   
    /* X 坐标偏移 37（可能是硬件布局要求） */
    x += 37;
    /* 设置行地址：低 4 位 */
    WriteCMD(UC1698U_CAL | (x & 0x0F));
    /* 设置行地址：高 3 位（右移 4 位后取低 3 位） */
    WriteCMD(UC1698U_CAM | ((x >> 4) & 0x07));
    
    /* 设置列地址：低4位 */
    WriteCMD(UC1698U_RAL | (y & 0x0F));
    /* 设置列地址：高4位（右移4位后取低4位） */
    WriteCMD(UC1698U_RAM | ((y >> 4) & 0x0F));
}
/* ============================================================================
 * 像素数据写入函数（支持不同位宽）
 * ============================================================================ */

/**
 * @brief  写入 6 位数据到指定坐标，也就是写入 6 个像素点
 * @param  x 列坐标（X 坐标）
 * @param  y 行坐标（Y 坐标）
 * @param  byte 要写入的8位数据（只使用高6位）
 * @note   将 8 位数据的高 6 位转换为 4K 色模式写入
 * 写入数据：| D7 D6 D5 D4 | D3 D2 D1 D0 |
 *           └ 第一个像素 ┘ └ 第二个像素 ┘ 
 * 写入数据：| D7 D6 D5 D4 | D3 D2 D1 D0 |
 *           └ 第三个像素 ┘ └ 第四个像素 ┘ 
 * 写入数据：| D7 D6 D5 D4 | D3 D2 D1 D0 |
 *           └ 第五个像素 ┘ └ 第六个像素 ┘       
 * @retval 无
 */
static void write_6bit(uint8_t x, uint8_t y, uint8_t byte)
{
    /* 4K 色模式下的像素值映射表，每个 bytes 需要映射 2 个 bit 的数据*/
    uint8_t disp_point[4] = {0x00, 0x0f, 0xf0, 0xff};
    
    SetAddress(x, y);
    /* 写入高2位（bit7-6）对应的像素值 */
    WriteData(disp_point[((byte>>6) & 0x03)]);
    /* 写入中间2位（bit5-4）对应的像素值 */
    WriteData(disp_point[((byte>>4) & 0x03)]);
    /* 写入低2位（bit3-2）对应的像素值 */
    WriteData(disp_point[((byte>>2) & 0x03)]);
}

/**
 * @brief  写入 3 位数据到指定坐标
 * @param  x 列坐标（X坐标）
 * @param  y 行坐标（Y坐标）
 * @param  byte 要写入的 8 位数据（只使用高 3 位）
 * @note   将 8 位数据的高 3 位转换为 4K 色模式写入
 * @retval 无
 */
static void write_3bit(uint8_t x, uint8_t y, uint8_t byte)
{
    /* 4K 色模式下的像素值映射表，每个 bytes 需要映射 2 个 bit 的数据*/
    uint8_t disp_point[4] = {0x00, 0x0f, 0xf0, 0xff};
    
    SetAddress(x, y);
    /* 写入高 2 位（bit7-6）对应的像素值 */
    WriteData(disp_point[((byte>>6) & 0x03)]);
    /* 写入 bit5 对应的像素值（左移1位后映射）*/
    WriteData(disp_point[((byte>>5) & 0x01) << 1]);
    /* 写入黑色像素（补齐） */
    WriteData(disp_point[0]);
}

/**
 * @brief  写入 8 位数据到指定坐标
 * @param  x 列坐标（X坐标）
 * @param  y 行坐标（Y坐标）
 * @param  byte 要写入的 8 位数据
 * @param  Flag 标志位：SET=反显（补齐0xFF），RESET=正常显示（补齐0x00）
 * @note   将 8 位数据转换为 4K 色模式
 *          每个字节对应4个像素点，最后补齐一个4位数据
 * @retval 无
 */
static void writebyte(uint8_t x, uint8_t y, uint8_t byte, FlagStatus Flag)
{
    /* 4K色模式下的像素值映射表 */
    uint8_t disp_point[4] = {0x00, 0x0f, 0xf0, 0xff};
    
    SetAddress(x, y);
    /* 写入高2位（bit7-6）对应的像素值 */
    WriteData(disp_point[((byte>>6) & 0x03)]);
    /* 写入中间高2位（bit5-4）对应的像素值 */
    WriteData(disp_point[((byte>>4) & 0x03)]);
    /* 写入中间低2位（bit3-2）对应的像素值 */
    WriteData(disp_point[((byte>>2) & 0x03)]);
    /* 写入低2位（bit1-0）对应的像素值 */
    WriteData(disp_point[((byte>>0) & 0x03)]);
    
    /* 根据标志位补齐最后一个4位数据 */
    if(Flag)
        WriteData(0xFF);  /* 反显模式：补齐白色 */
    else
        WriteData(0x00);   /* 正常模式：补齐黑色 */
}

/**
 * @brief  写入12位数据到指定坐标
 * @param  x 列坐标（X坐标）
 * @param  y 行坐标（Y坐标）
 * @param  bytes 要写入的16位数据（只使用高12位）
 * @note   将16位数据的高12位转换为4K色模式（2位/像素）写入
 *          每个12位数据对应6个像素点
 * @retval 无
 */
static void write_12bit(uint8_t x, uint8_t y, uint16_t bytes)
{
    /* 4K色模式下的像素值映射表 */
    uint8_t disp_point[4] = {0x00, 0x0f, 0xf0, 0xff};
    
    SetAddress(x, y);
    /* 写入bit15-14对应的像素值 */
    WriteData(disp_point[((bytes>>0x0E) & 0x03)]);
    /* 写入bit13-12对应的像素值 */
    WriteData(disp_point[((bytes>>0x0C) & 0x03)]);
    /* 写入bit11-10对应的像素值 */
    WriteData(disp_point[((bytes>>0x0A) & 0x03)]);
    /* 写入bit9-8对应的像素值 */
    WriteData(disp_point[((bytes>>8) & 0x03)]);
    /* 写入bit7-6对应的像素值 */
    WriteData(disp_point[((bytes>>6) & 0x03)]);
    /* 写入bit5-4对应的像素值 */
    WriteData(disp_point[((bytes>>4) & 0x03)]);
}

/**
 * @brief  显示单个图形（按键图标或连接单元图标）
 * @param  column 列坐标（X坐标）
 * @param  lin 行坐标（Y坐标）
 * @param  hzcode 图形代码索引
 * @param  tb_index 图形类型索引：1=按键图标（GRAPHICS_TB），其他=连接单元图标（CONNECT_UNIT）
 * @note   显示16x16像素的图形，每个图形由32字节数据组成
 *          图形数据按行扫描，每行16位，共16行
 * @retval 无
 */
void DisplayOneGraphics(uint8_t column, uint8_t lin, uint16_t hzcode, uint8_t tb_index)
{
    uint8_t i, t, row, colm;
    /* 4K色模式下的像素值映射表（注意顺序与writebyte不同） */
    uint8_t disp_point[4] = {0x00, 0xf0, 0x0f, 0xff};
    
    /* 循环显示16行像素 */
    for(i = 0; i < 16; i++) 
    {
        if(tb_index == 1)  /* 显示按键图标（GRAPHICS_TB） */
        {
            row = lin + i;
            colm = column;
            SetAddress(colm, row);
            
            if(i < 8)  /* 前8行：使用字节0-15 */
            {
                /* 每次写入 1 行的 16 个像素点 */
                WriteData(disp_point[((GRAPHICS_TB[hzcode][ 0]>>i) & 0x01) | (((GRAPHICS_TB[hzcode][ 1]>>i) & 0x01) << 1)]);
                WriteData(disp_point[((GRAPHICS_TB[hzcode][ 2]>>i) & 0x01) | (((GRAPHICS_TB[hzcode][ 3]>>i) & 0x01) << 1)]);
                WriteData(disp_point[((GRAPHICS_TB[hzcode][ 4]>>i) & 0x01) | (((GRAPHICS_TB[hzcode][ 5]>>i) & 0x01) << 1)]);
                WriteData(disp_point[((GRAPHICS_TB[hzcode][ 6]>>i) & 0x01) | (((GRAPHICS_TB[hzcode][ 7]>>i) & 0x01) << 1)]);
                WriteData(disp_point[((GRAPHICS_TB[hzcode][ 8]>>i) & 0x01) | (((GRAPHICS_TB[hzcode][ 9]>>i) & 0x01) << 1)]);
                WriteData(disp_point[((GRAPHICS_TB[hzcode][10]>>i) & 0x01) | (((GRAPHICS_TB[hzcode][11]>>i) & 0x01) << 1)]);
                WriteData(disp_point[((GRAPHICS_TB[hzcode][12]>>i) & 0x01) | (((GRAPHICS_TB[hzcode][13]>>i) & 0x01) << 1)]);
                WriteData(disp_point[((GRAPHICS_TB[hzcode][14]>>i) & 0x01) | (((GRAPHICS_TB[hzcode][15]>>i) & 0x01) << 1)]);
            }
            else  /* 后8行：使用字节16-31 */
            {
                /* 每次写入 1 行的 16 个像素点 */
                t = i - 8;  /* 计算后8行的相对索引 */
                WriteData(disp_point[((GRAPHICS_TB[hzcode][16]>>t) & 0x01) | (((GRAPHICS_TB[hzcode][17]>>t) & 0x01) << 1)]);
                WriteData(disp_point[((GRAPHICS_TB[hzcode][18]>>t) & 0x01) | (((GRAPHICS_TB[hzcode][19]>>t) & 0x01) << 1)]);
                WriteData(disp_point[((GRAPHICS_TB[hzcode][20]>>t) & 0x01) | (((GRAPHICS_TB[hzcode][21]>>t) & 0x01) << 1)]);
                WriteData(disp_point[((GRAPHICS_TB[hzcode][22]>>t) & 0x01) | (((GRAPHICS_TB[hzcode][23]>>t) & 0x01) << 1)]);
                WriteData(disp_point[((GRAPHICS_TB[hzcode][24]>>t) & 0x01) | (((GRAPHICS_TB[hzcode][25]>>t) & 0x01) << 1)]);
                WriteData(disp_point[((GRAPHICS_TB[hzcode][26]>>t) & 0x01) | (((GRAPHICS_TB[hzcode][27]>>t) & 0x01) << 1)]);
                WriteData(disp_point[((GRAPHICS_TB[hzcode][28]>>t) & 0x01) | (((GRAPHICS_TB[hzcode][29]>>t) & 0x01) << 1)]);
                WriteData(disp_point[((GRAPHICS_TB[hzcode][30]>>t) & 0x01) | (((GRAPHICS_TB[hzcode][31]>>t) & 0x01) << 1)]);
            }
        }
        else  /* 显示连接单元图标（CONNECT_UNIT） */
        {
            row = lin + i;
            colm = column;
            SetAddress(colm, row);
            
            if(i < 8)  /* 前8行：使用字节0-15 */
            {
                WriteData(disp_point[((CONNECT_UNIT[hzcode][ 0]>>i) & 0x01) | (((CONNECT_UNIT[hzcode][ 1]>>i) & 0x01) << 1)]);
                WriteData(disp_point[((CONNECT_UNIT[hzcode][ 2]>>i) & 0x01) | (((CONNECT_UNIT[hzcode][ 3]>>i) & 0x01) << 1)]);
                WriteData(disp_point[((CONNECT_UNIT[hzcode][ 4]>>i) & 0x01) | (((CONNECT_UNIT[hzcode][ 5]>>i) & 0x01) << 1)]);
                WriteData(disp_point[((CONNECT_UNIT[hzcode][ 6]>>i) & 0x01) | (((CONNECT_UNIT[hzcode][ 7]>>i) & 0x01) << 1)]);
                WriteData(disp_point[((CONNECT_UNIT[hzcode][ 8]>>i) & 0x01) | (((CONNECT_UNIT[hzcode][ 9]>>i) & 0x01) << 1)]);
                WriteData(disp_point[((CONNECT_UNIT[hzcode][10]>>i) & 0x01) | (((CONNECT_UNIT[hzcode][11]>>i) & 0x01) << 1)]);
                WriteData(disp_point[((CONNECT_UNIT[hzcode][12]>>i) & 0x01) | (((CONNECT_UNIT[hzcode][13]>>i) & 0x01) << 1)]);
                WriteData(disp_point[((CONNECT_UNIT[hzcode][14]>>i) & 0x01) | (((CONNECT_UNIT[hzcode][15]>>i) & 0x01) << 1)]);
            }
            else  /* 后8行：使用字节16-31 */
            {
                t = i - 8;  /* 计算后8行的相对索引 */
                WriteData(disp_point[((CONNECT_UNIT[hzcode][16]>>t) & 0x01) | (((CONNECT_UNIT[hzcode][17]>>t) & 0x01) << 1)]);
                WriteData(disp_point[((CONNECT_UNIT[hzcode][18]>>t) & 0x01) | (((CONNECT_UNIT[hzcode][19]>>t) & 0x01) << 1)]);
                WriteData(disp_point[((CONNECT_UNIT[hzcode][20]>>t) & 0x01) | (((CONNECT_UNIT[hzcode][21]>>t) & 0x01) << 1)]);
                WriteData(disp_point[((CONNECT_UNIT[hzcode][22]>>t) & 0x01) | (((CONNECT_UNIT[hzcode][23]>>t) & 0x01) << 1)]);
                WriteData(disp_point[((CONNECT_UNIT[hzcode][24]>>t) & 0x01) | (((CONNECT_UNIT[hzcode][25]>>t) & 0x01) << 1)]);
                WriteData(disp_point[((CONNECT_UNIT[hzcode][26]>>t) & 0x01) | (((CONNECT_UNIT[hzcode][27]>>t) & 0x01) << 1)]);
                WriteData(disp_point[((CONNECT_UNIT[hzcode][28]>>t) & 0x01) | (((CONNECT_UNIT[hzcode][29]>>t) & 0x01) << 1)]);
                WriteData(disp_point[((CONNECT_UNIT[hzcode][30]>>t) & 0x01) | (((CONNECT_UNIT[hzcode][31]>>t) & 0x01) << 1)]);
            }
        }
    }
}


/* ============================================================================
 * 字符显示函数（支持不同字体大小）
 * ============================================================================ */

/**
 * @brief  显示 6x12 像素的 ASCII 字符
 * @param  x 列坐标（X坐标）
 * @param  y 行坐标（Y坐标）
 * @param  CharCode ASCII字符代码（0x20-0x7E，即可打印字符）
 * @param  Flag 显示标志：SET=反显，RESET=正常显示
 * @note   字符数据存储在ASCII6x12字库中，每个字符12字节
 *          字符索引 = CharCode - 0x20（0x20为空格字符）
 *    0x00,0x00,0x20,0x60,0x20,0x20,0x20,0x20,0x20,0x70,0x00,0x00,  *** 1 
 *1      0 0 0 0 0 0   0x00  
 *2      0 0 0 0 0 0   0x00  
 *3      0 0 1 0 0 0   0x20
 *4      0 0 1 1 0 0   0x60
 *5      0 0 1 0 0 0   0x20
 *6      0 0 1 0 0 0   0x20
 *7      0 0 1 0 0 0   0x20
 *8      0 0 1 0 0 0   0x20
 *9      0 0 1 0 0 0   0x20
 *10     0 1 1 1 0 0   0x70
 *11     0 0 0 0 0 0   0x20
 *12     0 0 0 0 0 0   0x20
 * @retval 无
 */
void Char6_Write(uint8_t x, uint8_t y, uint8_t CharCode, FlagStatus Flag)
{
    uint8_t const *CodePtr;
    uint32_t i = 0, index;
    
    /* 计算字符在字库中的索引（ASCII码从0x20开始） */
    index = CharCode - 0x20;
    /* 获取字符数据指针（每个字符12字节） */
    CodePtr = ASCII6x12 + (index * 12); 
    
    /* 循环显示12行像素 */
    for(i = 0; i < 12; i++)
    {
        if(Flag)
            write_6bit(x, y + i, ~CodePtr[i]);  /* 反显：取反 */
        else
            write_6bit(x, y + i, CodePtr[i]);   /* 正常显示 */
    }  
}

/**
 * @brief  显示8x12像素的ASCII字符
 * @param  x 列坐标（X坐标）
 * @param  y 行坐标（Y坐标）
 * @param  CharCode ASCII字符代码（0x20-0x7E）
 * @param  Flag 显示标志：SET=反显，RESET=正常显示
 * @note   字符数据存储在ASCII8x12字库中，每个字符12字节
 * @retval 无
 */
void Char8_Write(uint8_t x, uint8_t y, uint8_t CharCode, FlagStatus Flag)
{
    uint8_t const *CodePtr;
    uint32_t i = 0, index = 0;
    
    /* 计算字符在字库中的索引 */
    index = CharCode - 0x20;
    /* 获取字符数据指针（每个字符12字节） */
    CodePtr = ASCII8x12 + (index * 12); 
    
    /* 循环显示12行像素 */
    for(i = 0; i < 12; i++)
    {
        if(Flag)
            writebyte(x, y + i, ~CodePtr[i], Flag);  /* 反显：取反 */
        else
            writebyte(x, y + i, CodePtr[i], Flag);   /* 正常显示 */
    }  
}
/**
 * @brief  显示12x12像素的ASCII字符
 * @param  x 列坐标（X坐标）
 * @param  y 行坐标（Y坐标）
 * @param  CharCode ASCII字符代码（0x20-0x7E）
 * @param  Flag 显示标志：SET=反显，RESET=正常显示
 * @note   字符数据存储在ASCII12x12字库中，每个字符24字节（12行x2字节/行）
 *          每行数据由2个字节组成（高字节+低字节）
 * @retval 无
 */
void Char12_Write(uint8_t x, uint8_t y, uint8_t CharCode, FlagStatus Flag)
{
    uint8_t const *CodePtr;
    uint32_t i = 0, index = 0;
    
    /* 计算字符在字库中的索引 */
    index = CharCode - 0x20;
    /* 获取字符数据指针（每个字符24字节：12行x2字节） */
    CodePtr = ASCII12x12 + (index * 24); 
    
    /* 循环显示12行像素 */
    for(i = 0; i < 12; i++)
    {
        if(Flag)
            /* 反显：将2字节组合后取反 */
            write_12bit(x, y+i, (uint16_t)(~(((uint16_t)CodePtr[i*2 ])<<8)|((uint16_t)CodePtr[i*2+1 ])));  
        else
            /* 正常显示：将2字节组合（高字节左移8位+低字节） */
            write_12bit(x, y+i, (uint16_t)(((uint16_t)CodePtr[i*2 ])<<8)|((uint16_t)CodePtr[i*2+1 ])); 
    }  
}
/**
 * @brief  显示12x12像素的汉字
 * @param  x 列坐标（X坐标）
 * @param  y 行坐标（Y坐标）
 * @param  HzCode 汉字 GB2312 编码指针（2字节，高字节在前）
 * @param  Flag 显示标志：SET=反显，RESET=正常显示
 * @note   支持 GB2312 编码的汉字显示，字库覆盖 16~55 区的常用汉字
 *          每个汉字 24 字节（12行x2字节/行）
 *          特殊字符映射：罗马数字Ⅰ、Ⅱ、Ⅲ、Ⅳ和符号∠等
 * @retval 无
 */
void HZ12_Write(uint8_t x, uint8_t y, const uint8_t *HzCode, FlagStatus Flag)
{
    uint8_t const *CodePtr;
    uint32_t GB2312Code = 0, HZKcode = 0, hzk_area = 0, hzk_index = 0, i = 0;

    /* 组合 GB2312 编码（高字节左移8位 + 低字节） */
    GB2312Code = (*HzCode << 8) | (*(HzCode + 1));
    
    /* 临时增加特殊字符映射（扩展字符集） */
    if(GB2312Code == 0xA2F1)  /* 罗马数字Ⅰ */
        GB2312Code = 0xB0A1 + (39 << 8 | 89);
    if(GB2312Code == 0xA2F2)  /* 罗马数字Ⅱ */
        GB2312Code = 0xB0A1 + (39 << 8 | 90);
    if(GB2312Code == 0xA2F3)  /* 罗马数字Ⅲ */
        GB2312Code = 0xB0A1 + (39 << 8 | 91);
    if(GB2312Code == 0xA2F4)  /* 罗马数字Ⅳ */
        GB2312Code = 0xB0A1 + (39 << 8 | 92);
    if(GB2312Code == 0xA1CF)  /* 角度符号∠ */
        GB2312Code = 0xB0A1 + (39 << 8 | 93);
    if(GB2312Code == 0xA6DA)  /* 其他特殊字符 */
        GB2312Code = 0xB0A1 + (40 << 8 | 0);
    
    /* 计算汉字在字库中的位置（GB2312从0xB0A1开始，对应16~55区） */
    HZKcode = GB2312Code - 0xB0A1;  /* 16~55区的常用汉字区域 */
    hzk_area = HZKcode >> 8;        /* 区号（高字节） */
    hzk_index = (HZKcode & 0x00FF);  /* 位号（低字节） */
    
    /* 获取汉字数据指针（12x12像素，一个汉字24字节） */
    CodePtr = &HZ12[hzk_area][hzk_index * 24];    
    
    /* 循环显示12行像素 */
    for(i = 0; i < 12; i++)
    {
        if(Flag)
            /* 反显：将2字节组合后取反 */
            write_12bit(x, y + i, (uint16_t)~((((uint16_t)CodePtr[i*2]) << 8) | ((uint16_t)CodePtr[i*2+1])));
        else
            /* 正常显示：将2字节组合（高字节左移8位+低字节） */
            write_12bit(x, y + i, (uint16_t)((((uint16_t)CodePtr[i*2]) << 8) | ((uint16_t)CodePtr[i*2+1])));
    }  
}
/**
 * @brief  混合显示汉字和ASCII字符字符串
 * @param  x 起始列坐标（X坐标）
 * @param  y 起始行坐标（Y坐标）
 * @param  ptr 字符串指针（支持GB2312汉字和ASCII字符混合）
 * @param  Flag 显示标志：SET=反显，RESET=正常显示
 * @note   自动识别汉字（GB2312编码，首字节>0xA0）和ASCII字符
 *          支持换行符'\n'，换行后X坐标回到起始位置
 *          自动换行：X坐标超过51时换行，Y坐标超过148时停止显示
 *          汉字宽度：4个像素单位，ASCII字符宽度：2个像素单位
 * @retval 无
 */
void HZ12AndChar_Printf(uint8_t x, uint8_t y, const uint8_t *ptr, FlagStatus Flag)
{
    uint8_t x_base = x;  /* 保存起始X坐标，用于换行后复位 */
    
    while(*ptr != '\0')
    {
        if(*ptr > 0xA0)  /* 汉字（GB2312编码首字节>0xA0） */
        {
            HZ12_Write(x, y, ptr, Flag);   /* 显示汉字 */
            x += 4;                        /* 汉字占4个像素单位 */
            ptr += 2;                      /* 汉字占2字节 */
        }
        else if(*ptr >= 0x20)  /* 可显示ASCII字符（0x20-0x7E） */
        {
            Char6_Write(x, y, *ptr, Flag);  /* 显示ASCII字符 */
            x += 2;                         /* ASCII字符占2个像素单位 */
            ptr += 1;                       /* ASCII字符占1字节 */
        }
        else if(*ptr == '\n')  /* 换行符 */
        {
            if(ptr[1] < 0x20)  /* 如果下一字节是控制字符，作为行间距 */
                y += ptr[1];
            else
                y += 12;        /* 默认行间距12像素 */
            x = x_base;         /* X坐标回到起始位置 */
            ptr += 1;
        }
        else  /* 其他控制字符，跳过 */
        {
            ptr += 1;
        }
        
        /* 自动换行处理 */
        if(x > 51)  /* X坐标超过屏幕宽度 */
        {
            x = 0;
            y += 12;  /* 换行，行间距12像素 */
        } 
        /* 超出屏幕高度，停止显示 */
        if(y > 148) return;
    }  
}

/**
 * @brief  混合显示汉字和ASCII字符字符串（支持指定位置反显）
 * @param  x 起始列坐标（X坐标）
 * @param  y 起始行坐标（Y坐标）
 * @param  ptr 字符串指针（支持GB2312汉字和ASCII字符混合）
 * @param  SignNUM 需要反显的字符位置索引（从0开始）
 * @note   功能与HZ12AndChar_Printf类似，但可以指定某个字符位置反显
 *          用于实现光标或选中效果
 *          字符位置索引从0开始，汉字和ASCII字符都算作一个字符
 * @retval 无
 */
void HZ12AndChar_SignPrintf(uint8_t x, uint8_t y, const uint8_t *ptr, uint32_t SignNUM)
{
    uint32_t i = 0;  /* 字符位置索引 */
    
    while(*ptr != '\0')
    {
        if(*ptr > 0xA0)  /* 汉字 */
        {
            if(i != SignNUM)
                HZ12_Write(x, y, ptr, RESET);  /* 正常显示 */
            else
                HZ12_Write(x, y, ptr, SET);     /* 反显 */
            ptr += 2;
            x += 4;
        }
        else if(*ptr >= 0x20)  /* ASCII字符 */
        {
            if(i != SignNUM)
                Char6_Write(x, y, *ptr, RESET);  /* 正常显示 */
            else
                Char6_Write(x, y, *ptr, SET);     /* 反显 */
            x += 2;
            ptr += 1;
        }
        else  /* 碰到非字符，指针继续加1，避免死循环 */
        {
            ptr += 1;
        }
        i++;  /* 字符位置索引递增 */
        
        /* 自动换行处理 */
        if(x > 51)
        {
            x = 0;
            y += 12;
        } 
        /* 超出屏幕高度，停止显示 */
        if(y > 148) return;
    }  
}

/**
 * @brief  以十六进制格式显示32位数据
 * @param  x 列坐标（X坐标）
 * @param  y 行坐标（Y坐标）
 * @param  data 要显示的32位数据
 * @param  Flag 显示标志：SET=反显，RESET=正常显示
 * @note   格式：0xXXXXXXXX（8位十六进制数，前面加"0x"前缀）
 * @retval 无
 */
void ASCII_Printf(uint8_t x, uint32_t y, uint32_t data, FlagStatus Flag)
{
    char ptr[11];
    memset(ptr, 0, sizeof(ptr));
    sprintf(ptr, "0x%08X", data);  /* 格式化为8位十六进制字符串 */
    HZ12AndChar_Printf(x, y, (const uint8_t *)ptr, Flag);	
}

/**
 * @brief  以十六进制格式显示32位数据（支持指定位置反显）
 * @param  x 列坐标（X坐标）
 * @param  y 行坐标（Y坐标）
 * @param  data 要显示的32位数据
 * @param  SignNUM 需要反显的字符位置索引（从0开始，不包括"0x"前缀）
 * @note   格式：0xXXXXXXXX
 *          SignNUM+2是因为字符串以"0x"开头，需要跳过前2个字符
 * @retval 无
 */
void ASCII_SignPrintf(uint8_t x, uint32_t y, uint32_t data, uint32_t SignNUM)
{
    char ptr[11];
    memset(ptr, 0, sizeof(ptr));
    sprintf(ptr, "0x%08X", data);
    HZ12AndChar_SignPrintf(x, y, (const uint8_t *)ptr, SignNUM + 2);  /* +2跳过"0x"前缀 */
}

/**
 * @brief  将32位IP地址数据格式化为字符串
 * @param  buf 输出缓冲区指针（至少17字节）
 * @param  IPdata IP地址数据（32位，格式：高字节为最高位IP段）
 * @note   IP地址格式：XXX.XXX.XXX.XXX（每个段3位数字，不足补空格）
 *          IPdata格式：高8位=第一段，次8位=第二段，次8位=第三段，低8位=第四段
 * @retval 无
 */
void IP_Sprintf(uint8_t *buf, uint32_t IPdata)
{
    uint8_t temp = 0;
    
    memset(buf, 0, 17);  /* 清空缓冲区（IP地址字符串最多16字节+结束符） */
    
    /* 提取并格式化第一段IP地址（高8位） */
    temp = (uint8_t)(IPdata >> 24);
    sprintf((char *)buf, "%3d", temp);
    buf[3] = '.';
    
    /* 提取并格式化第二段IP地址（次高8位） */
    temp = (uint8_t)(IPdata >> 16);
    sprintf((char *)buf + 4, "%3d", temp); 
    buf[7] = '.';
    
    /* 提取并格式化第三段IP地址（次低8位） */
    temp = (uint8_t)(IPdata >> 8);
    sprintf((char *)buf + 8, "%3d", temp);
    buf[11] = '.';
    
    /* 提取并格式化第四段IP地址（低8位） */
    temp = (uint8_t)(IPdata >> 0);
    sprintf((char *)buf + 12, "%3d", temp);
}
/**
 * @brief  显示IP地址（支持光标位置反显）
 * @param  x 列坐标（X坐标）
 * @param  y 行坐标（Y坐标）
 * @param  IPdata IP地址数据（32位）
 * @param  SetFlag 是否启用光标：SET=启用光标，RESET=正常显示
 * @param  cursor 光标位置索引（当SetFlag=SET时有效）
 * @note   先调用IP_Sprintf格式化IP地址，然后根据SetFlag选择显示方式
 * @retval 无
 */
void IP_Printf(uint8_t x, uint32_t y, uint32_t IPdata, FlagStatus SetFlag, uint32_t cursor)
{
    uint8_t buf[17];
    memset(buf, 0, sizeof(buf));  
    IP_Sprintf(buf, IPdata);  /* 格式化IP地址 */
    
    if(SetFlag)
    {
        HZ12AndChar_SignPrintf(x, y, buf, cursor);  /* 带光标显示 */
    }
    else
    {
        HZ12AndChar_Printf(x, y, buf, RESET);        /* 正常显示 */
    }
}
/**
 * @brief  显示整数数值
 * @param  x 列坐标（X坐标）
 * @param  y 行坐标（Y坐标）
 * @param  data 要显示的32位有符号整数
 * @param  Flag 显示标志：SET=反显，RESET=正常显示
 * @note   使用标准格式"%d"格式化整数
 * @retval 无
 */
void IntValue_Printf(uint8_t x, uint32_t y, int32_t data, FlagStatus Flag)
{
    char ptr[11];
    memset(ptr, 0, sizeof(ptr));
    sprintf(ptr, "%d", data);  /* 格式化为整数字符串 */
    HZ12AndChar_Printf(x, y, (const uint8_t *)ptr, Flag);	
}
/**
 * @brief  显示菜单项编号（格式：XX.）
 * @param  x 列坐标（X坐标）
 * @param  y 行坐标（Y坐标）
 * @param  data 菜单项编号
 * @param  Flag 显示标志：SET=反显，RESET=正常显示
 * @note   格式：2位数字+点号（例如："01."、"12."）
 * @retval 无
 */
void MeunItem_Printf(uint8_t x, uint32_t y, int32_t data, FlagStatus Flag)
{
    char ptr[11];
    memset(ptr, 0, sizeof(ptr));
    sprintf(ptr, "%2d.", data);  /* 格式化为2位数字+点号 */
    HZ12AndChar_Printf(x, y, (const uint8_t *)ptr, Flag);	
}
/**
 * @brief  显示固定长度的整数（右对齐，左补0）
 * @param  x 列坐标（X坐标）
 * @param  y 行坐标（Y坐标）
 * @param  data 要显示的32位有符号整数
 * @param  len 显示长度（1-10位）
 * @param  Flag 显示标志：SET=反显，RESET=正常显示
 * @note   先格式化为10位右对齐字符串（左补0），然后截取指定长度显示
 *          例如：data=123, len=5，显示为"00123"
 * @retval 无
 */
void FixLenIntValue_Printf(uint32_t x, uint32_t y, int32_t data, uint32_t len, FlagStatus Flag)
{
    char ptr[11];
    memset(ptr, 0, sizeof(ptr));
    
    if(len > 10) return;  /* 长度限制检查 */
    
    sprintf(ptr, "%010d", data);  /* 右对齐，左不足补0，固定长度10 */
    /* 从字符串末尾截取len长度显示 */
    HZ12AndChar_Printf(x, y, (const uint8_t *)ptr + 10 - len, Flag);	
}

/**
 * @brief  将整数转换为固定长度的小数字符串（类似sprintf功能）
 * @param  str 目标字符串缓冲区指针
 * @param  data 要转换的32位有符号整数
 * @param  len 整数部分长度（1-10位）
 * @param  dot 小数部分位数（0-3位）
 * @note   将整数除以10^dot转换为小数，然后格式化为固定长度字符串
 *          例如：data=12345, len=3, dot=2，结果为"123.45"
 *          如果dot=0，则只显示整数部分
 * @retval 无
 */
void FixLenIToF_Sprintf(uint8_t* str, int32_t data, uint32_t len, uint32_t dot)
{
    char ptr[15];
    uint32_t i = 0, j = 0;
    float fvalue = 0;
    memset(ptr, 0, sizeof(ptr));
    
    /* 参数有效性检查 */
    if(str == NULL)
        return;  
    if((len + dot) > 10)
        return;   
    if(dot > 3)
        return;
    
    if(dot == 0)  /* 无小数部分，只显示整数 */
    {
        sprintf(ptr, "%011d", data);  /* 格式化为11位整数（右对齐，左补0） */
        /* 截取指定长度 */
        while(j < len)
        {
            *str++ = *(ptr + (11 - len) + j);
            j++;
        } 
    }
    else  /* 有小数部分 */
    {
        /* 将整数转换为小数（除以10^dot） */
        fvalue = (float)data / pow(10.0, dot);
        sprintf(ptr, "%011.3f", fvalue);  /* 格式化为11位小数（3位小数） */
        
        /* 查找小数点位置 */
        for(i = 0; i < 10; i++)
        {
            if(ptr[i] == '.')
            {
                ptr[i + dot + 1] = '\0';  /* 截取到指定小数位数 */
                break;
            }       
        } 
        /* 复制指定长度的字符串（整数部分+小数点+小数部分） */
        while(j < len + dot + 1)
        {
            *str++ = *(ptr + (i - len) + j);
            j++;
        }  
    } 
}


/**
 * @brief  显示固定长度的整数转小数字符串（支持光标位置反显）
 * @param  x 列坐标（X坐标）
 * @param  y 行坐标（Y坐标）
 * @param  data 要显示的32位有符号整数
 * @param  len 整数部分长度（1-10位）
 * @param  dot 小数部分位数（0-3位）
 * @param  Flag 是否启用光标：SET=启用光标，RESET=正常显示
 * @param  cursor 光标位置索引（当Flag=SET时有效）
 * @note   先调用FixLenIToF_Sprintf格式化字符串，然后根据Flag选择显示方式
 * @retval 无
 */
void FixLenIToF_Printf(uint32_t x, uint32_t y, int32_t data, uint32_t len, uint32_t dot, FlagStatus Flag, uint32_t cursor)
{
    uint8_t ptr[15];
    
    /* 参数有效性检查 */
    if((len + dot) > 10)
        return;
    if(dot > 3)
        return;
    
    FixLenIToF_Sprintf(ptr, data, len, dot);  /* 格式化字符串 */
    
    if(!Flag)
        HZ12AndChar_Printf(x, y, (const uint8_t *)ptr, RESET);  /* 正常显示 */
    else
        HZ12AndChar_SignPrintf(x, y, (const uint8_t *)ptr, cursor);  /* 带光标显示 */
}
/**
 * @brief  显示浮点数（保留2位小数）
 * @param  x 列坐标（X坐标）
 * @param  y 行坐标（Y坐标）
 * @param  data 要显示的浮点数
 * @param  Flag 显示标志：SET=反显，RESET=正常显示
 * @note   使用格式"%.2f"格式化浮点数，保留2位小数
 * @retval 无
 */
void FloatValue_Printf(uint8_t x, uint32_t y, float data, FlagStatus Flag)
{
    char ptr[11];
    memset(ptr, 0, sizeof(ptr));
    sprintf(ptr, "%.2f", data);  /* 格式化为2位小数 */
    HZ12AndChar_Printf(x, y, (const uint8_t *)ptr, Flag);	
}

/**
 * @brief  显示固定长度的浮点数
 * @param  x 列坐标（X坐标）
 * @param  y 行坐标（Y坐标）
 * @param  data 要显示的浮点数
 * @param  len 整数部分长度（1-10位）
 * @param  dot 小数部分位数（0-3位）
 * @param  Flag 显示标志：SET=反显，RESET=正常显示
 * @note   先格式化为11位小数（3位小数），然后截取指定长度显示
 * @retval 无
 */
void FixLenFloatValue_Printf(uint32_t x, uint32_t y, float data, uint32_t len, uint32_t dot, FlagStatus Flag)
{
    char ptr[15];
    int i = 0;
    memset(ptr, 0, sizeof(ptr));
    
    /* 参数有效性检查 */
    if((len + dot) > 10)
        return;
    if(dot > 3)
        return;
    
    sprintf(ptr, "%011.3f", data);  /* 格式化为11位小数（3位小数） */
    
    /* 查找小数点位置并截取到指定小数位数 */
    for(i = 0; i < 10; i++)
    {
        if(ptr[i] == '.')
        {
            ptr[i + dot + 1] = '\0';  /* 截取到指定小数位数 */
            break;
        }       
    }
    /* 从指定位置开始显示（整数部分+小数点+小数部分） */
    HZ12AndChar_Printf(x, y, (const uint8_t *)ptr + i - len, Flag);	
}

/* ============================================================================
 * 状态指示和屏幕操作函数
 * ============================================================================ */

/**
 * @brief  显示系统故障指示（闪烁效果）
 * @note   在屏幕右上角（坐标52,0）显示故障指示图标
 *          通过计数器实现闪烁效果：
 *          - count < 0x0280 (640)：显示故障图标
 *          - 0x0280 <= count < 0x1FFF (8191)：隐藏图标
 *          - count >= 0x1FFF：重置计数器
 * @retval 无
 */
void Fault_Disp(void)
{
    static uint32_t count = 0;  /* 静态计数器，保持状态 */
    const uint32_t x = 52, y = 0;  /* 故障指示位置 */
    count++;
    
    if(count < 0x0280)  /* 显示阶段 */
    {
        write_3bit(x, y, 0xC0);      /* 显示故障图标（上半部分） */
        write_3bit(x, y + 1, 0xC0);  /* 显示故障图标（下半部分） */
    }
    else if(count < 0x1FFF)  /* 隐藏阶段 */
    {
        write_3bit(x, y, 0x00);      /* 清除图标 */
        write_3bit(x, y + 1, 0x00);
    }
    else  /* 重置计数器 */
    {
        count = 0;
    }
}


/**
 * @brief  显示按键运行状态指示
 * @param  Flag 状态标志：非0=显示运行指示，0=清除指示
 * @note   在屏幕左上角（坐标0,0）显示按键运行状态
 *          显示3个像素点表示运行状态
 * @retval 无
 */
void KeyRun_Disp(uint32_t Flag)
{
    const uint32_t x = 0, y = 0;  /* 状态指示位置 */
    
    if(Flag != 0)  /* 显示运行指示 */
    {
        write_3bit(x, y, 0x80);      /* 显示指示点1 */
        write_3bit(x, y + 1, 0x80);  /* 显示指示点2 */
        write_3bit(x, y + 3, 0x80);  /* 显示指示点3 */
    }
    else  /* 清除指示 */
    {
        write_3bit(x, y, 0x00);
        write_3bit(x, y + 1, 0x00);
        write_3bit(x, y + 3, 0x00);
    }
}

/**
 * @brief  全屏显示图像数据
 * @param  ptr 图像数据指针（160x160像素，每像素2位，共3200字节）
 * @note   将图像数据按4K色模式（2位/像素）写入整个屏幕
 *          图像数据格式：每字节包含4个像素点（bit7-6, bit5-4, bit3-2, bit1-0）
 *          每行160像素 = 40字节，共160行
 * @retval 无
 */
void ScreenPrintf(uint8_t* ptr)
{
    uint16_t i, j;
    set_color_mode(COLOR_4K_444);  /* 设置4K色模式（RGB444） */
    uint8_t disp_point[4] = {0x00, 0x0f, 0xf0, 0xff};  /* 像素值映射表 */
    
    SetAddress(0, 0);  /* 设置起始地址为屏幕左上角 */
    
    /* 循环显示160行 */
    for(i = 0; i < 160; i++)
    {
        /* 每行20个字节（每字节4个像素点，共80个像素点，但实际显示需要更多） */
        for(j = 0; j < 20; j++)
        {
            /* 将每字节的4个2位像素值转换为显示数据 */
            WriteData(disp_point[(ptr[i*20 + j] >> 6) & 0x03]);  /* bit7-6 */
            WriteData(disp_point[(ptr[i*20 + j] >> 4) & 0x03]);  /* bit5-4 */
            WriteData(disp_point[(ptr[i*20 + j] >> 2) & 0x03]);  /* bit3-2 */
            WriteData(disp_point[(ptr[i*20 + j] >> 0) & 0x03]);  /* bit1-0 */
        }
        WriteData(0x00);  /* 补全每行末尾的数据，使总点数能被3整除 */
    }
}

/**
 * @brief  显示公司LOGO图像
 * @note   调用ScreenPrintf函数全屏显示COMPANY_LOGO图像数据
 * @retval 无
 */
void DisplayGraphicsScreen(void)
{
    ScreenPrintf((uint8_t*)COMPANY_LOGO);
}
/**
 * @brief  清屏函数（全屏填充黑色）
 * @note   将整个160x160像素屏幕填充为黑色（0x00）
 *          设置4K色模式，然后逐行写入0x00数据
 * @retval 无
 */
void ClearScreen(void)
{
    uint16_t i, j;
    set_color_mode(COLOR_4K_444);  /* 设置4K色模式 */
    SetAddress(0, 0);  /* 设置起始地址为屏幕左上角 */
    
    /* 循环160行 */
    for(i = 0; i < 160; i++)
    {
        /* 每行写入20个字节的0x00数据 */
        for(j = 0; j < 20; j++)
        {
            WriteData(0x0);
            WriteData(0x0);
            WriteData(0x0);
            WriteData(0x0);
        }
        WriteData(0x00);  /* 补全每行末尾的数据，使总点数能被3整除 */
    }
}

/**
 * @brief  屏幕全显函数（全屏填充白色）
 * @note   将整个160x160像素屏幕填充为白色（0xFF）
 *          用于测试屏幕显示功能
 * @retval 无
 */
void ReverseScreen(void)
{
    uint16_t i, j;
    set_color_mode(COLOR_4K_444);  /* 设置4K色模式 */
    SetAddress(0, 0);  /* 设置起始地址为屏幕左上角 */
    
    /* 循环160行 */
    for(i = 0; i < 160; i++)
    {
        /* 每行写入20个字节的0xFF数据 */
        for(j = 0; j < 20; j++)
        {
            WriteData(0xFF);
            WriteData(0xFF);
            WriteData(0xFF);
            WriteData(0xFF);
        }
        WriteData(0x00);  /* 补全每行末尾的数据，使总点数能被3整除 */
    }
}

/**
 * @brief  清除菜单内容区域
 * @note   清除屏幕中菜单显示区域（从第21行开始，共139行）
 *          用于刷新菜单内容而不影响屏幕其他区域
 * @retval 无
 */
void ClearMenuScreen(void)
{
    uint16_t i, j;
    set_color_mode(COLOR_4K_444);  /* 设置4K色模式 */
    SetAddress(0, 21);  /* 设置起始地址为第21行 */
    
    /* 循环139行（从第21行到第159行） */
    for(i = 0; i < 139; i++)
    {
        /* 每行写入27个字节的0x00数据 */
        for(j = 0; j < 27; j++)
        {
            WriteData(0x0);
            WriteData(0x0);
            WriteData(0x0);
        }
    }
}

/**
 * @brief  填充指定矩形区域
 * @param  x 起始列坐标（X坐标）
 * @param  y 起始行坐标（Y坐标）
 * @param  len 矩形宽度（列数）
 * @param  high 矩形高度（行数）
 * @param  byte 填充数据值（用于write_3bit函数）
 * @note   使用3位写入模式填充指定矩形区域
 *          设置4K色模式，然后逐行填充数据
 * @retval 无
 */
void FillBoxScreen(uint8_t x, uint8_t y, uint8_t len, uint8_t high, uint8_t byte)
{
    uint16_t i, j;
    uint8_t x1 = x;  /* 保存起始X坐标 */
    set_color_mode(COLOR_4K_444);  /* 设置4K色模式 */
    
    /* 循环填充每一行 */
    for(i = 0; i < high; i++)
    {
        SetAddress(x, y);  /* 设置当前行起始地址 */
        /* 循环填充每一列 */
        for(j = 0; j < len; j++)
        {      
            write_3bit(x1, y, byte);  /* 写入3位数据 */
            x1++;
        }
        x1 = x;  /* 重置X坐标到起始位置 */
        y++;     /* 移动到下一行 */
    }
}

/**
 * @brief  LCD 控制与背光 GPIO 初始化
 * @note   初始化 UC1698U 控制器所需的 GPIO 引脚，分为两组：
 *          GPIOB 组（控制信号与背光）：
 *          | 引脚 | 功能     | 说明           |
 *          |------|----------|----------------|
 *          | PB0  | 背光     | 背光开关       |
 *          | PB10 | RST      | 复位信号       |
 *          | PB11 | CS       | 片选           |
 *          | PB12 | RD       | 读使能         |
 *          | PB13 | WR       | 写使能         |
 *          | PB14 | CD       | 命令/数据选择  |
 *          GPIOE 组（数据总线）：
 *          | 引脚      | 功能     | 说明                    |
 *          |-----------|----------|-------------------------|
 *          | PE8~PE15  | D0~D7    | 8 位并行数据总线       |
 *          配置为推挽输出、上拉、高速模式
 * @retval 无
 */
void LCD_GPIO_Init(void)
{
    GPIO_InitTypeDef gpio_init_struct;    
    
    __HAL_RCC_GPIOB_CLK_ENABLE();  /**< 使能 GPIOB 时钟 */
    __HAL_RCC_GPIOE_CLK_ENABLE();  /**< 使能 GPIOE 时钟 */

    /* ========== GPIOB：控制信号与背光 ========== */
    gpio_init_struct.Pin = GPIO_PIN_14 | GPIO_PIN_13 | GPIO_PIN_12 | GPIO_PIN_11 | GPIO_PIN_10 | GPIO_PIN_0;
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;   /**< 推挽输出 */
    gpio_init_struct.Pull = GPIO_PULLUP;           /**< 上拉 */
    gpio_init_struct.Speed = GPIO_SPEED_HIGH;      /**< 高速 */
    HAL_GPIO_Init(GPIOB, &gpio_init_struct);
    
    /* ========== GPIOE：8 位数据总线 ========== */
    gpio_init_struct.Pin = GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15;
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;   /**< 推挽输出 */
    gpio_init_struct.Pull = GPIO_PULLUP;           /**< 上拉 */
    gpio_init_struct.Speed = GPIO_SPEED_HIGH;      /**< 高速 */
    HAL_GPIO_Init(GPIOE, &gpio_init_struct);
}
/**
 * @brief  LCD初始化总函数
 * @note   执行LCD初始化的完整流程：
 *          1. 硬件复位
 *          2. 初始化配置（XRD版本）
 *          3. 清屏
 * @retval 无
 */
void LcdInit(void)
{
    LCD_GPIO_Init();
    LCD_BackLight_ON(); 
    LCD_Reset();      /* 硬件复位 */
    LCD_InitXRD();    /* 初始化配置（XRD版本） */
    ClearScreen();    /* 清屏 */
}

/**
 * @brief  显示能联LOGO图像
 * @note   在屏幕指定位置（从第40行开始）显示能联LOGO
 *          LOGO尺寸：70行x20字节/行，共1400字节
 *          使用4K色模式显示
 * @retval 无
 */
void DisplayNL_LOGO(void)
{
    uint8_t i, n;
    uint8_t disp_point[4] = {0x00, 0x0f, 0xf0, 0xff};  /* 4K色模式像素值映射表 */
    set_color_mode(COLOR_4K_444);  /* 设置4K色模式 */
    SetAddress(0, 40);  /* 设置起始地址：列0，行40 */
    
    /* 循环显示70行 */
    for(i = 0; i < 70; i++)
    {
        /* 每行20字节 */
        for(n = 0; n < 20; n++)
        {
            /* 将每字节的4个2位像素值转换为显示数据 */
            WriteData(disp_point[(NL_LOGO[i*20 + n] >> 6) & 0x03]);  /* bit7-6 */
            WriteData(disp_point[(NL_LOGO[i*20 + n] >> 4) & 0x03]);  /* bit5-4 */
            WriteData(disp_point[(NL_LOGO[i*20 + n] >> 2) & 0x03]);  /* bit3-2 */
            WriteData(disp_point[(NL_LOGO[i*20 + n] >> 0) & 0x03]);  /* bit1-0 */
        }
        WriteData(0x00);  /* 补全每行末尾的数据，使总点数能被3整除 */
    }
}
      
/**
 * @brief  显示南瑞LOGO图像
 * @note   在屏幕指定位置（坐标8,50）显示南瑞LOGO
 *          LOGO尺寸：50行x15字节/行，共750字节
 *          使用4K色模式显示
 * @retval 无
 */
void DisplayNANRUI_LOGO(void)
{
    uint8_t i, n;
    uint8_t disp_point[4] = {0x00, 0x0f, 0xf0, 0xff};  /* 4K色模式像素值映射表 */
    const uint32_t x = 8, y = 50;  /* LOGO显示位置 */
    set_color_mode(COLOR_4K_444);  /* 设置4K色模式 */
    
    /* 循环显示50行 */
    for(i = 0; i < 50; i++)
    {     
        SetAddress(x, y + i);  /* 设置当前行地址 */
        /* 每行15字节 */
        for(n = 0; n < 15; n++)
        {
            /* 将每字节的4个2位像素值转换为显示数据 */
            WriteData(disp_point[(NANRUI_LOGO[i*15 + n] >> 6) & 0x03]);  /* bit7-6 */
            WriteData(disp_point[(NANRUI_LOGO[i*15 + n] >> 4) & 0x03]);  /* bit5-4 */
            WriteData(disp_point[(NANRUI_LOGO[i*15 + n] >> 2) & 0x03]);  /* bit3-2 */
            WriteData(disp_point[(NANRUI_LOGO[i*15 + n] >> 0) & 0x03]);  /* bit1-0 */
        }
        WriteData(0x00);  /* 补全每行末尾的数据，使总点数能被3整除 */
    }
}


/**
 * @brief  在 LCD 上显示指定尺寸的位图
 * @param  Width   位图宽度（像素，不超过 160）
 * @param  Height  位图高度（像素，不超过 160）
 * @param  picture 位图数据指针（每像素 1 位，每行 Width/8 字节，行内取反显示）
 * @note   使用 4K 色（RGB444）模式；位图居中显示
 *          X 坐标按每 3 像素递增；每行末尾补 0 使点数能被 3 整除
 *          若 Width 或 Height 大于 160 则直接返回
 * @retval 无
 */
void Display_BMP(uint32_t Width, uint32_t Height, const uint8_t* picture)
{
    uint8_t i, n;
    uint8_t disp_point[4] = {0x00, 0x0f, 0xf0, 0xff};  /**< 4K 色像素值映射 */
    uint8_t x, y;
    uint32_t bytesNumOfRow;

    set_color_mode(COLOR_4K_444);  /**< 设置 4K 色模式 */

    if (Width > 160 || Height > 160)
        return;

    x = ((160 - Width) / 2) / 3;            /**< 居中 X，每 3 像素为一单位 */
    y = (160 - Height) / 2 + Height;        /**< 居中 Y，自下而上绘制 */

    bytesNumOfRow = Width / 8;
    for (i = 0; i < Height; i++)
    {
        SetAddress(x, y - i);
        for (n = 0; n < bytesNumOfRow; n++)
        {
            WriteData(disp_point[(~picture[i * bytesNumOfRow + n] >> 6) & 0x03]);
            WriteData(disp_point[(~picture[i * bytesNumOfRow + n] >> 4) & 0x03]);
            WriteData(disp_point[(~picture[i * bytesNumOfRow + n] >> 2) & 0x03]);
            WriteData(disp_point[(~picture[i * bytesNumOfRow + n] >> 0) & 0x03]);
        }
        WriteData(0x00);  /**< 补足使每行点数能被 3 整除 */
    }
}

/**
 * @brief  显示南瑞（NANRUI）Logo 位图
 * @note   调用 Display_BMP，尺寸 128×50 像素，数据源为 NANRUI_BMP
 * @retval 无
 */
void DisplayNANRUI_BMP(void)
{
    const uint32_t xmax = 128, ymax = 50;
    Display_BMP(xmax, ymax, NANRUI_BMP);
}

/**
 * @brief  显示 QQ Logo 位图
 * @note   调用 Display_BMP，尺寸 96×94 像素，数据源为 QQ_BMP
 * @retval 无
 */
void DisplayQQ_BMP(void)
{
    const uint32_t xmax = 96, ymax = 94;
    Display_BMP(xmax, ymax, QQ_BMP);
}
/**
 * @brief  绘制单条水平线（内部函数）
 * @param  x 列坐标（X坐标）
 * @param  y 行坐标（Y坐标）
 * @note   绘制一条3像素宽的水平线
 *          使用固定的像素模式：0xFF, 0xF0, 0x00
 * @retval 无
 */
static void DrawHorizontalLine(uint8_t x, uint8_t y)
{
    SetAddress(x, y);
    WriteData(0xFF);  /* 第一个像素：白色 */
    WriteData(0xF0);  /* 第二个像素：深色 */
    WriteData(0x00);  /* 第三个像素：黑色 */
}
/**
 * @brief  绘制指定长度的水平线
 * @param  x 起始列坐标（X坐标）
 * @param  y 行坐标（Y坐标）
 * @param  len 线条长度（像素单位）
 * @note   通过重复调用DrawHorizontalLine绘制多条水平线
 *          自动处理换行：当X坐标超过54时，换到下一行
 *          超出屏幕高度（160）时停止绘制
 * @retval 无
 */
void DisplayHorizontalLine(uint8_t x, uint8_t y, uint8_t len)
{
    uint8_t i;
    for(i = 0; i < len; i++)
    {     
        DrawHorizontalLine(x, y);  /* 绘制一条水平线 */
        x++;
        /* 自动换行处理 */
        if(x > 54)  /* X坐标超过屏幕宽度 */
        {
            x = 0;   /* X坐标回到起始位置 */
            y += 1;  /* 换到下一行 */
        } 
        /* 超出屏幕高度，停止绘制 */
        if(y >= 160) return;
    }
}

/**
 * @brief  绘制纵向线条
 * @param  x 列坐标（X坐标）
 * @param  y 起始行坐标（Y坐标）
 * @param  high 线条高度（行数）
 * @param  value 线条类型值（用于write_3bit函数）
 *               0x80=单边线，0xC0=双线，0xA0=间隔线，以此类推
 * @note   使用write_3bit函数逐行绘制纵向线条
 *          超出屏幕高度（160）时停止绘制
 * @retval 无
 */
void DisplayVerticalLine(uint8_t x, uint8_t y, uint8_t high, uint8_t value)
{
    uint8_t i;
    for(i = 0; i < high; i++)
    {     
        write_3bit(x, y++, value);  /* 写入3位数据，Y坐标递增 */
        /* 超出屏幕高度，停止绘制 */
        if(y >= 160) return;
    }
}

/**
 * @brief  显示矩形光标
 * @param  x 起始列坐标（X坐标）
 * @param  y 起始行坐标（Y坐标）
 * @param  len 光标宽度（像素单位）
 * @param  high 光标高度（行数）
 * @note   通过绘制多条水平线形成矩形光标
 *          用于指示当前选中位置
 * @retval 无
 */
void CursorPrintf(uint8_t x, uint8_t y, uint8_t len, uint8_t high)
{
    int i;
    for(i = 0; i < high; i++)
        DisplayHorizontalLine(x, y + i, len);  /* 逐行绘制水平线 */
}

/**
 * @brief  显示屏幕头部装饰图形
 * @note   绘制屏幕顶部的装饰边框和分隔线
 *          包括：
 *          - 左右两侧的垂直边框线（16行高）
 *          - 顶部的水平分隔线
 *          - 底部的水平分隔线
 *          - 左侧的垂直边框线（142行高）
 * @retval 无
 */
void DiaplayHeadGraph(void)
{
    /* 绘制左侧垂直边框（从第0行开始，高度16） */
    DisplayVerticalLine(20, 0, 16, 0x80);
    /* 绘制右侧垂直边框（从第0行开始，高度16） */
    DisplayVerticalLine(53, 0, 16, 0x80);
    /* 绘制顶部水平分隔线（第15行，长度20） */
    DisplayHorizontalLine(0, 15, 20);
    /* 绘制顶部水平分隔线（第15行，从第34列开始，长度20） */
    DisplayHorizontalLine(34, 15, 20);   
    
    /* 绘制底部水平分隔线（第17行，长度54） */
    DisplayHorizontalLine(0, 17, 54);
    /* 绘制左侧垂直边框（从第18行开始，高度142） */
    DisplayVerticalLine(0, 18, 142, 0x80); 
}
/**
 * @brief  绘制一个像素点（带渐变效果）
 * @param  x 列坐标（X坐标）
 * @param  y 行坐标（Y坐标）
 * @note   绘制一个2x6像素的点，带有渐变效果
 *          用于显示特殊标记或装饰
 * @retval 无
 */
void DrawPoint(uint8_t x, uint8_t y)
{
    /* 左列像素 */
    DisplayVerticalLine(x, y + 1, 1, 0x60);
    DisplayVerticalLine(x, y + 2, 1, 0x60);
    DisplayVerticalLine(x, y + 3, 1, 0x40);  /* 中间最亮 */
    DisplayVerticalLine(x, y + 4, 1, 0x60);
    DisplayVerticalLine(x, y + 5, 1, 0x60);
    
    /* 右列像素 */
    DisplayHorizontalLine(x + 1, y + 1, 1);
    DisplayVerticalLine(x + 1, y + 2, 1, 0x60);
    DisplayVerticalLine(x + 1, y + 3, 1, 0x20);  /* 中间较暗 */
    DisplayVerticalLine(x + 1, y + 4, 1, 0x60);
    DisplayHorizontalLine(x + 1, y + 5, 1); 
}
