/****************************************************************************** * @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 #include /* ============================================================================ * 编译选项配置 * ============================================================================ */ //#define STATIC_BOX /**< 静态框显示模式(已注释) */ #define DYNAMIC_BOX /**< 动态框显示模式 */ #define TEST_DELAY_TIME 600 /**< 测试延时时间(单位:毫秒) */ #define VERSION_CODE "Ver0.05(XRD)" /**< 版本号字符串 */ /* ============================================================================ * 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_SET);}while(0) /** @brief 设置背光引脚为打开 */ #define LCD_BackLight_ON() do{HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);}while(0) volatile static uint32_t BackLightCount = 0; #define BackLightTimeMax (20000) //五分钟 /** * @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初始化总函数 * @note 执行LCD初始化的完整流程: * 1. 硬件复位 * 2. 初始化配置(XRD版本) * 3. 清屏 * @retval 无 */ void LcdInit(void) { 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整除 */ } } /******************************************************************************* * FunctionName : Display_Picture() * Description : 图片显示程序 * EntryParameter : none * ReturnValue : none *******************************************************************************/ 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}; uint8_t x, y; uint32_t bytesNumOfRow; set_color_mode(COLOR_4K_444);//设置4.4.4.模式 if(Width>160 || Height>160) return ; //设置4.4.4.模式 x = ((160-Width)/2)/3; //x坐标为每3个像素点递增 y = (160-Height)/2 + Height; //x坐标为每3个像素点递增 bytesNumOfRow = Width/8; for(i=0;i>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); //补全每行末尾的数据,使总点数能被三整除 } } /******************************************************************************* * FunctionName : DisplayNANRUI_BMP() * Description : BMP格式显示测试 * EntryParameter : none * ReturnValue : none *******************************************************************************/ void DisplayNANRUI_BMP(void) { const uint32_t xmax=128, ymax=50; Display_BMP(xmax, ymax, NANRUI_BMP); } /******************************************************************************* * FunctionName : DisplayNANRUI_BMP() * Description : BMP格式显示测试 * EntryParameter : none * ReturnValue : none *******************************************************************************/ 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坐标) * @param len 方框宽度(像素单位) * @param high 方框高度(行数) * @note 绘制一个带3D阴影效果的方框 * 包括:左边框、上边框、下边框(带阴影)、右边框(带渐变阴影效果) * @retval 无 */ static void DrawBox(uint8_t x, uint8_t y, uint8_t len, uint8_t high) { /* 绘制左边框 */ DisplayVerticalLine(x, y, high, 0x80); /* 绘制上边框 */ DisplayHorizontalLine(x, y, len); /* 绘制下边框 */ DisplayHorizontalLine(x, y + high, len); /* 绘制第一层阴影 */ writebyte(x, y + high + 1, 0x7F, RESET); DisplayHorizontalLine(x + 1, y + high + 1, len); /* 绘制第二层阴影 */ writebyte(x, y + high + 2, 0x3F, RESET); DisplayHorizontalLine(x + 1, y + high + 2, len); /* 绘制右边框(带渐变阴影效果) */ DisplayVerticalLine(x + len, y, high + 3, 0x80); /* 主边框 */ DisplayVerticalLine(x + len, y + 1, high + 2, 0xC0); /* 第一层阴影 */ DisplayVerticalLine(x + len, y + 2, high + 1, 0xE0); /* 第二层阴影 */ } /** * @brief 绘制无阴影效果的方框(内部函数) * @param x 起始列坐标(X坐标) * @param y 起始行坐标(Y坐标) * @param len 方框宽度(像素单位) * @param high 方框高度(行数) * @note 绘制一个简单的方框,无阴影效果 * 包括:左边框、上边框、下边框、右边框 * @retval 无 */ static void DrawBox_NoShadow(uint8_t x, uint8_t y, uint8_t len, uint8_t high) { DisplayVerticalLine(x, y, high, 0x80); /* 左边框 */ DisplayHorizontalLine(x, y, len); /* 上边框 */ DisplayHorizontalLine(x, y + high, len); /* 下边框 */ DisplayVerticalLine(x + len - 1, y, high, 0x20); /* 右边框 */ } /** * @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); } /** * @brief 擦除线条(内部函数,用于动态框效果) * @param x 起始列坐标(X坐标) * @param y 起始行坐标(Y坐标) * @param len 线条长度(像素单位) * @param high 线条高度(行数) * @note 擦除指定区域的线条,用于实现动态框的擦除效果 * 擦除底部3行和右侧1列 * @retval 无 */ static void EraseLine(uint8_t x, uint8_t y, uint8_t len, uint8_t high) { FillBoxScreen(x, y + high, len + 1, 3, 0x00); /* 擦除底部3行 */ FillBoxScreen(x + len, y, 1, high + 1, 0x00); /* 擦除右侧1列 */ } /** * @brief 显示消息提示框(静态模式) * @note 在屏幕指定位置显示一个带阴影的消息提示框 * 位置:坐标(10,40),尺寸:33x50像素 * 先清除背景区域,然后绘制方框 * 动态框模式代码已注释(需要定义DYNAMIC_BOX) * @retval 无 */ void MessageBox(void) { /* 静态框模式 */ FillBoxScreen(9, 37, 35, 59, 0x00); /* 清除背景区域 */ DrawBox(10, 40, 33, 50); /* 绘制带阴影的方框 */ } /** * @brief 清除消息提示框 * @note 清除消息提示框的整个显示区域 * 位置和尺寸与MessageBox函数对应 * @retval 无 */ void ClrMessageBox(void) { FillBoxScreen(9, 37, 35, 59, 0x00); /* 清除消息框区域 */ } /** * @brief 清除消息提示框内容区域 * @note 只清除消息框内部的内容区域,保留边框 * 用于刷新消息内容而不重新绘制边框 * @retval 无 */ void ClrMessageBoxContent(void) { FillBoxScreen(11, 41, 31, 48, 0x00); /* 清除内容区域(保留边框) */ } /** * @brief 显示可编程消息提示框(支持静态和动态模式) * @param x 起始列坐标(X坐标) * @param y 起始行坐标(Y坐标) * @param size 方框大小参数(影响方框尺寸) * @note 根据编译选项显示静态或动态消息框 * 静态模式:直接绘制完整方框 * 动态模式:逐步绘制方框,产生动画效果 * 方框尺寸:宽度=size,高度=size*3/2+9 * @retval 无 */ void ProgramableMessageBox(uint8_t x, uint8_t y, uint8_t size) { #ifdef STATIC_BOX /* 静态框模式 */ FillBoxScreen(x - 1, y - 3, size + 2, size * 3 / 2 + 9, 0x00); /* 清除背景 */ DrawBox(x, y, size, size * 3 / 2 + 9); /* 绘制方框 */ #endif //#ifdef STATIC_BOX #ifdef DYNAMIC_BOX /* 动态框模式 */ FillBoxScreen(x - 1, y - 3, size * 2 + 2, size * 3 + 9, 0x00); /* 清除背景 */ /* 逐步绘制方框,产生动画效果 */ for(int i = 0; i < size; i++) { EraseLine(x, y, i * 2, i * 3); /* 擦除旧线条 */ DrawBox(x, y, (i + 1) * 2, (i + 1) * 3); /* 绘制新方框 */ delay_ms(5); /* 延时,产生动画效果 */ } #endif //#ifdef DYNAMIC_BOX } /** * @brief 绘制下拉菜单组合框 * @param x 起始列坐标(X坐标) * @param y 起始行坐标(Y坐标) * @param len 菜单框宽度(像素单位) * @param items 菜单项数量 * @note 根据编译选项显示静态或动态菜单框 * 静态模式:直接绘制完整菜单框 * 动态模式:逐步绘制菜单框,产生下拉动画效果 * 每个菜单项高度:20像素 * @retval 无 */ void DrawMenuComboBox(uint8_t x, uint8_t y, uint8_t len, uint8_t items) { #ifdef STATIC_BOX /* 静态框模式 */ FillBoxScreen(x - 1, y - 6, len + 3, items * 20 + 9, 0x00); /* 清除背景 */ DrawBox(x - 1, y - 3, len + 2, items * 20); /* 绘制菜单框 */ #endif //#ifdef STATIC_BOX #ifdef DYNAMIC_BOX /* 动态框模式 */ FillBoxScreen(x - 2, y - 6, len + 3, items * 20 + 9, 0x00); /* 清除背景 */ /* 逐步绘制菜单框,产生下拉动画效果 */ for(int i = 0; i < items; i++) { EraseLine(x - 1, y - 6, len + 2, i * 20 + 3); /* 擦除旧线条 */ DrawBox(x - 1, y - 3, len + 2, (i + 1) * 20); /* 绘制新菜单框 */ delay_ms(15); /* 延时,产生动画效果 */ } #endif //#ifdef DYNAMIC_BOX } /** * @brief 显示消息设置提示框(带标题栏,清除背景) * @param title 标题字符串指针(GB2312编码) * @note 显示一个带标题栏的消息设置框 * 先清除背景区域,然后绘制无阴影方框 * 标题栏区域填充白色,标题文字反显 * 位置:坐标(8,40),尺寸:37x70像素 * @retval 无 */ void MessageSetBox(const uint8_t *title) { FillBoxScreen(7, 37, 39, 79, 0x00); /* 清除背景区域 */ DrawBox_NoShadow(8, 40, 37, 70); /* 绘制无阴影方框 */ FillBoxScreen(8, 40, 37, 20, 0xFF); /* 填充标题栏区域(白色) */ HZ12AndChar_Printf(18, 43, title, SET); /* 显示标题(反显) */ } /** * @brief 显示消息设置提示框(带标题栏,不清除背景) * @param title 标题字符串指针(GB2312编码) * @note 功能与MessageSetBox类似,但不清除背景区域 * 用于在已有内容上叠加显示消息框 * @retval 无 */ void MessageSetBox_NoClear(const uint8_t *title) { DrawBox_NoShadow(8, 40, 37, 70); /* 绘制无阴影方框 */ FillBoxScreen(8, 40, 37, 20, 0xFF); /* 填充标题栏区域(白色) */ HZ12AndChar_Printf(18, 43, title, SET); /* 显示标题(反显) */ } /** * @brief 清除消息设置框的内容显示区域 * @note 只清除消息框内部的内容区域,保留标题栏和边框 * 用于刷新消息内容而不重新绘制整个框 * @retval 无 */ void ClrMessageSetBox(void) { FillBoxScreen(9, 61, 34, 48, 0x00); /* 清除内容区域(保留标题栏和边框) */ } /** * @brief 删除消息设置提示框 * @note 清除整个消息设置框的显示区域 * 包括标题栏、边框和内容区域 * @retval 无 */ void DeleteMessageSetBox(void) { FillBoxScreen(7, 37, 39, 79, 0x00); /* 清除整个消息框区域 */ } /** * @brief 绘制记录框(用于显示记录列表) * @param x 起始列坐标(X坐标) * @param y 起始行坐标(Y坐标) * @param len 框宽度(像素单位) * @param items 记录项数量 * @note 绘制一个带阴影的记录列表框 * 每个记录项高度:16像素(显示区域),19像素(包括间距) * 先清除背景,然后绘制方框 * @retval 无 */ void DrawRecordBox(uint8_t x, uint8_t y, uint8_t len, uint8_t items) { FillBoxScreen(x - 1, y - 3, len + 2, items * 16 + 6, 0x00); /* 清除背景 */ DrawBox(x - 1, y - 3, len + 2, items * 19); /* 绘制记录框 */ }