1647 lines
64 KiB
C
1647 lines
64 KiB
C
/******************************************************************************
|
||
* @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 显示按键运行状态指示,屏幕的左上方显示3个点
|
||
* @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色模式 写入整个屏幕
|
||
* 图像数据格式:每字节包含8个像素点
|
||
* 写入数据:| D7 D6 D5 D4 | D3 D2 D1 D0 |
|
||
* └ 第一个像素 ┘ └ 第二个像素 ┘
|
||
* 写入数据:| 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 无
|
||
*/
|
||
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个字节(每字节 8 个像素点,共 160 个像素点) */
|
||
for(j = 0; j < 20; j++)
|
||
{
|
||
/* 将每字节的 4 个 2 位像素值转换为显示数据 */
|
||
WriteData(disp_point[(ptr[i*20 + j] >> 6) & 0x03]); /* bit0-1 */
|
||
WriteData(disp_point[(ptr[i*20 + j] >> 4) & 0x03]); /* bit2-3 */
|
||
WriteData(disp_point[(ptr[i*20 + j] >> 2) & 0x03]); /* bit4-5 */
|
||
WriteData(disp_point[(ptr[i*20 + j] >> 0) & 0x03]); /* bit6-7 */
|
||
}
|
||
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);
|
||
}
|