ARM(14) - LCD(1)清屏和画图形
一、学习目标
- 理解 LCD 显示的核心指标与工作原理
- 掌握 I.MX6ULL 的 LCD 控制器(eLCDIF)硬件架构
- 熟练完成 I.MX6ULL LCD 裸机驱动开发(IO 配置、时钟配置、时序配置、显示控制)
- 实现 LCD 基础显示功能(清屏、颜色切换)
二、LCD 核心指标回顾(基础认知)
在进行硬件开发前,需先明确 LCD 的关键参数,这些参数直接决定驱动配置逻辑:
指标类型 | 核心参数 | 含义与细节 |
---|---|---|
分辨率 | 1K/720p | 1280×720(水平 1280 像素,垂直 720 像素),"1K" 为行业习惯称呼(非精确 1000) |
2K/1080p | 1920×1080(主流高清分辨率) | |
4K/2160p | 4096×2160(超高清分辨率,裸机开发中较少用,需更高带宽) | |
帧率 / 刷新率 | fps(帧 / 秒) | 每秒显示的画面数量,决定动态显示流畅度:- 30fps:基础静态显示- 60fps:主流动态显示(视频、UI 交互)- 120/144fps:高刷场景(游戏,裸机开发中极少用) |
色域 | RGB 加色模型 | 屏幕可显示的颜色范围,裸机开发常用RGB888格式:- 每个颜色通道(R/G/B)8 位深度(0~255)- 总颜色数:2⁸×2⁸×2⁸=16777216(约 1600 万色) |
ARGB 色彩模型 | 在 RGB 基础上增加 8 位不透明度(A 通道),总颜色数:2⁸×2⁸×2⁸×2⁸=4294967296(约 43 亿色),裸机 LCD 显示中 A 通道可忽略(默认不透明) |
三、I.MX6ULL LCD 硬件基础
1. I.MX6ULL 的 LCD 控制器:eLCDIF
I.MX6ULL 集成增强型 LCD 接口(eLCDIF),是连接 LCD 与 CPU 的核心模块,主要功能:
- 解析 CPU 下发的显示数据(帧缓冲区)
- 生成 LCD 所需的时序信号(HSYNC、VSYNC、DOTCLK)
- 控制数据传输速率(匹配 LCD 刷新率)
- 支持多种像素格式(RGB565、RGB888 等)
2. 硬件连接核心引脚
I.MX6ULL 与 LCD 的连接分为数据引脚和控制引脚,对应驱动代码中的 IO 配置:
引脚类型 | 数量 / 功能 | I.MX6ULL 引脚复用功能 | 作用 |
---|---|---|---|
数据引脚 | 24 位(D00~D23) | IOMUXC_LCD_DATAxx_LCDIF_DATAxx | 传输 RGB888 格式的颜色数据(R8+G8+B8) |
控制引脚 | LCD_ENABLE | IOMUXC_LCD_ENABLE_LCDIF_ENABLE | LCD 使能信号(高电平有效,控制屏幕是否显示) |
LCD_CLK | IOMUXC_LCD_CLK_LCDIF_CLK | 像素时钟(DOTCLK),决定每像素的传输时间 | |
LCD_HSYNC | IOMUXC_LCD_HSYNC_LCDIF_HSYNC | 水平同步信号(行同步),标记一行数据的开始 / 结束 | |
LCD_VSYNC | IOMUXC_LCD_VSYNC_LCDIF_VSYNC | 垂直同步信号(场同步),标记一帧数据的开始 / 结束 |
eg. RGB565 的核心:位深分配逻辑 是一种16 位 RGB 色彩模式
“565” 代表将 16 个二进制位(总位深 16 位)按以下比例分配给 R、G、B 三个通道:
- R(红色):5 位 → 二进制位数为 5,可表示的数值范围是
0~31
(共2^5 = 32
种亮度等级);- G(绿色):6 位 → 二进制位数为 6,可表示的数值范围是
0~63
(共2^6 = 64
种亮度等级);- B(蓝色):5 位 → 与红色一致,数值范围
0~31
(32 种亮度等级)。关键设计原因:人眼对绿色更敏感
之所以给绿色多 1 位(6 位),是因为人眼视网膜对绿色光的感知能力最强(含更多对绿光敏感的视锥细胞)。在总位深有限(16 位)的情况下,给绿色分配更多位数,能让颜色过渡更自然,视觉上减少 “色阶断层”(颜色突变的卡顿感),比平均分配(如 RGB555)的效果更好。
四、时钟与寄存器配置
1、时钟
1.配置对应引脚的功能
2. 配置LCD的时钟
3. 配置LCD参数----行场同步等等
1. 行同步(HSYNC):水平扫描的「指挥官」
- 作用:控制电子束(CRT)或像素刷新(LCD)在水平方向的移动,标记单行像素的开始与结束。
- 工作机制:
- 每行扫描结束后,HSYNC 信号触发电子束从屏幕右侧「回扫」到左侧起始位置,同时进入水平消隐期(不显示内容)。
- 一个完整的行扫描周期包括:同步脉冲(Sync)、后沿(Back Porch)、有效图像(Addressable Video)、前沿(Front Porch)等阶段,单位为像素时钟周期。
- 频率计算:
- 例如,640×480 分辨率下,水平总像素数为 800(含消隐),像素时钟 25MHz 时,行频约 31.5kHz。
2. 场同步(VSYNC):垂直刷新的「触发器」
- 作用:控制电子束或像素刷新在垂直方向的移动,标记整帧图像的开始与结束。
- 工作机制:
- 所有行扫描完成后,VSYNC 信号触发电子束从屏幕右下角「回扫」到左上角,同时进入垂直消隐期(不显示内容)。
- 一个完整的场扫描周期包括:同步脉冲、后沿、有效图像、前沿等阶段,单位为行周期。
- 频率计算:
- 例如,640×480 分辨率下,垂直总像素数为 525(含消隐),行频 31.5kHz 时,场频约 60Hz。
2、寄存器配置
以上参照 IMAX6ULL裸机驱动 手册 进行配置
以下是直接查询手册所得
1. 配置引脚功能
24根数据线 + DE / VSYNC / HSYNC / PCLK
(RGB:2^8 * 2^8 * 2^8,即3*8 = 24根)
电气配置为1011 1001 = 0xB9
2. 配置LCD时钟
1. 配置分频器
将24M配置为31MHz的时钟
24 * 31 = 744
即倍频至744M,然后使两个分频器相乘为24即可配置成功
CSCDR2② 和 CDCMR③ 均可配置到 8 (divide by)
① CSCDR2[LCDIF1_PRE_CLK_SEL] 选择PLL5, 配置为010
② CSCDR2[LCDIF1_PRED] 设置为 4 分频, 配置为011
③ CBCMR[CLDIF1_PORE] 设置为 6 分频, 配置为101
④ CSCDR2[LCDIF1_CLK_SEL] 选择CLDIF1 clock, 配置为000
2. 时钟计算
以下配置 LCD 寄存器(不完全)
五、函数详解与注释
------- 主函数 -----
1. LCD IO 初始化函数:lcd_io_init(void)
功能:配置 LCD 数据引脚和控制引脚的复用功能(复用为 LCDIF 外设)和电气属性(驱动能力、信号稳定性),是 LCD 硬件初始化的第一步。
void lcd_io_init(void)
{// -------------------------- 1. 配置LCD数据引脚(24位)复用为LCDIF功能 --------------------------// IMX6引脚需通过IOMUXC配置复用:第二个参数=0表示禁用GPIO功能,直接复用为指定外设(LCDIF)IOMUXC_SetPinMux(IOMUXC_LCD_DATA00_LCDIF_DATA00, 0); // 数据引脚0 → LCDIF_DATA00IOMUXC_SetPinMux(IOMUXC_LCD_DATA01_LCDIF_DATA01, 0); // 数据引脚1 → LCDIF_DATA01IOMUXC_SetPinMux(IOMUXC_LCD_DATA02_LCDIF_DATA02, 0); // 数据引脚2 → LCDIF_DATA02IOMUXC_SetPinMux(IOMUXC_LCD_DATA03_LCDIF_DATA03, 0); // 数据引脚3 → LCDIF_DATA03IOMUXC_SetPinMux(IOMUXC_LCD_DATA04_LCDIF_DATA04, 0); // 数据引脚4 → LCDIF_DATA04IOMUXC_SetPinMux(IOMUXC_LCD_DATA05_LCDIF_DATA05, 0); // 数据引脚5 → LCDIF_DATA05IOMUXC_SetPinMux(IOMUXC_LCD_DATA06_LCDIF_DATA06, 0); // 数据引脚6 → LCDIF_DATA06IOMUXC_SetPinMux(IOMUXC_LCD_DATA07_LCDIF_DATA07, 0); // 数据引脚7 → LCDIF_DATA07IOMUXC_SetPinMux(IOMUXC_LCD_DATA08_LCDIF_DATA08, 0); // 数据引脚8 → LCDIF_DATA08IOMUXC_SetPinMux(IOMUXC_LCD_DATA09_LCDIF_DATA09, 0); // 数据引脚9 → LCDIF_DATA09IOMUXC_SetPinMux(IOMUXC_LCD_DATA10_LCDIF_DATA10, 0); // 数据引脚10 → LCDIF_DATA10IOMUXC_SetPinMux(IOMUXC_LCD_DATA11_LCDIF_DATA11, 0); // 数据引脚11 → LCDIF_DATA11IOMUXC_SetPinMux(IOMUXC_LCD_DATA12_LCDIF_DATA12, 0); // 数据引脚12 → LCDIF_DATA12IOMUXC_SetPinMux(IOMUXC_LCD_DATA13_LCDIF_DATA13, 0); // 数据引脚13 → LCDIF_DATA13IOMUXC_SetPinMux(IOMUXC_LCD_DATA14_LCDIF_DATA14, 0); // 数据引脚14 → LCDIF_DATA14IOMUXC_SetPinMux(IOMUXC_LCD_DATA15_LCDIF_DATA15, 0); // 数据引脚15 → LCDIF_DATA15IOMUXC_SetPinMux(IOMUXC_LCD_DATA16_LCDIF_DATA16, 0); // 数据引脚16 → LCDIF_DATA16IOMUXC_SetPinMux(IOMUXC_LCD_DATA17_LCDIF_DATA17, 0); // 数据引脚17 → LCDIF_DATA17IOMUXC_SetPinMux(IOMUXC_LCD_DATA18_LCDIF_DATA18, 0); // 数据引脚18 → LCDIF_DATA18IOMUXC_SetPinMux(IOMUXC_LCD_DATA19_LCDIF_DATA19, 0); // 数据引脚19 → LCDIF_DATA19IOMUXC_SetPinMux(IOMUXC_LCD_DATA20_LCDIF_DATA20, 0); // 数据引脚20 → LCDIF_DATA20IOMUXC_SetPinMux(IOMUXC_LCD_DATA21_LCDIF_DATA21, 0); // 数据引脚21 → LCDIF_DATA21IOMUXC_SetPinMux(IOMUXC_LCD_DATA22_LCDIF_DATA22, 0); // 数据引脚22 → LCDIF_DATA22IOMUXC_SetPinMux(IOMUXC_LCD_DATA23_LCDIF_DATA23, 0); // 数据引脚23 → LCDIF_DATA23// -------------------------- 2. 配置LCD控制引脚复用为LCDIF功能 --------------------------IOMUXC_SetPinMux(IOMUXC_LCD_ENABLE_LCDIF_ENABLE, 0); // LCD使能引脚 → LCDIF_ENABLE(控制LCD显示开关)IOMUXC_SetPinMux(IOMUXC_LCD_CLK_LCDIF_CLK, 0); // LCD像素时钟 → LCDIF_CLK(同步数据传输)IOMUXC_SetPinMux(IOMUXC_LCD_HSYNC_LCDIF_HSYNC, 0); // 水平同步引脚 → LCDIF_HSYNC(行切换同步)IOMUXC_SetPinMux(IOMUXC_LCD_VSYNC_LCDIF_VSYNC, 0); // 垂直同步引脚 → LCDIF_VSYNC(帧切换同步)// -------------------------- 3. 配置所有LCD引脚的电气属性 --------------------------// 0xB9(二进制10111001):配置引脚驱动能力、 slew rate(信号边沿速度)、上下拉// 含义:高驱动能力(适应LCD长排线)、快速slew rate(匹配像素时钟)、无上下拉(LCD信号为推挽输出)IOMUXC_SetPinConfig(IOMUXC_LCD_DATA00_LCDIF_DATA00 , 0xB9);IOMUXC_SetPinConfig(IOMUXC_LCD_DATA01_LCDIF_DATA01 , 0xB9);IOMUXC_SetPinConfig(IOMUXC_LCD_DATA02_LCDIF_DATA02 , 0xB9);IOMUXC_SetPinConfig(IOMUXC_LCD_DATA03_LCDIF_DATA03 , 0xB9);IOMUXC_SetPinConfig(IOMUXC_LCD_DATA04_LCDIF_DATA04 , 0xB9);IOMUXC_SetPinConfig(IOMUXC_LCD_DATA05_LCDIF_DATA05 , 0xB9);IOMUXC_SetPinConfig(IOMUXC_LCD_DATA06_LCDIF_DATA06 , 0xB9);IOMUXC_SetPinConfig(IOMUXC_LCD_DATA07_LCDIF_DATA07 , 0xB9);IOMUXC_SetPinConfig(IOMUXC_LCD_DATA08_LCDIF_DATA08 , 0xB9);IOMUXC_SetPinConfig(IOMUXC_LCD_DATA09_LCDIF_DATA09 , 0xB9);IOMUXC_SetPinConfig(IOMUXC_LCD_DATA10_LCDIF_DATA10 , 0xB9);IOMUXC_SetPinConfig(IOMUXC_LCD_DATA11_LCDIF_DATA11 , 0xB9);IOMUXC_SetPinConfig(IOMUXC_LCD_DATA12_LCDIF_DATA12 , 0xB9);IOMUXC_SetPinConfig(IOMUXC_LCD_DATA13_LCDIF_DATA13 , 0xB9);IOMUXC_SetPinConfig(IOMUXC_LCD_DATA14_LCDIF_DATA14 , 0xB9);IOMUXC_SetPinConfig(IOMUXC_LCD_DATA15_LCDIF_DATA15 , 0xB9);IOMUXC_SetPinConfig(IOMUXC_LCD_DATA16_LCDIF_DATA16 , 0xB9);IOMUXC_SetPinConfig(IOMUXC_LCD_DATA17_LCDIF_DATA17 , 0xB9);IOMUXC_SetPinConfig(IOMUXC_LCD_DATA18_LCDIF_DATA18 , 0xB9);IOMUXC_SetPinConfig(IOMUXC_LCD_DATA19_LCDIF_DATA19 , 0xB9);IOMUXC_SetPinConfig(IOMUXC_LCD_DATA20_LCDIF_DATA20 , 0xB9);IOMUXC_SetPinConfig(IOMUXC_LCD_DATA21_LCDIF_DATA21 , 0xB9);IOMUXC_SetPinConfig(IOMUXC_LCD_DATA22_LCDIF_DATA22 , 0xB9);IOMUXC_SetPinConfig(IOMUXC_LCD_DATA23_LCDIF_DATA23 , 0xB9);// 控制引脚同样配置为0xB9,确保时序信号稳定IOMUXC_SetPinConfig(IOMUXC_LCD_ENABLE_LCDIF_ENABLE, 0XB9);IOMUXC_SetPinConfig(IOMUXC_LCD_CLK_LCDIF_CLK, 0XB9);IOMUXC_SetPinConfig(IOMUXC_LCD_HSYNC_LCDIF_HSYNC, 0XB9);IOMUXC_SetPinConfig(IOMUXC_LCD_VSYNC_LCDIF_VSYNC, 0XB9);
}
2. LCD 时钟初始化函数:lcd_clk_init(void)
功能:配置 LCDIF 的时钟源(PLL_VIDEO)及分频系数,确保 LCD 像素时钟(DOTCLK)符合 LCD 屏的时序要求(如 800x480@60Hz 需~27MHz 时钟)。
IMX6 时钟路径:PLL_VIDEO
(视频专用 PLL)→ 分频器 → LCDIF 时钟。
void lcd_clk_init(void)
{unsigned int t; // 临时变量,用于寄存器读写(避免直接多次操作寄存器)// -------------------------- 1. 暂存LCDIF时钟源配置(CSCDR2) --------------------------// CCM(时钟控制模块)-> CSCDR2(时钟状态控制寄存器2):控制LCDIF时钟源选择t = CCM->CSCDR2;t &= ~(0x7<<15); // 清除bit15~17(LCDIF_CLK_SEL):选择时钟源前先清零t |= (0x4<<15); // 设置bit15~17=0100:暂选备用时钟(避免配置PLL时时钟异常)CCM->CSCDR2 = t;// -------------------------- 2. 配置PLL_VIDEO(LCDIF的核心时钟源) --------------------------CCM_ANALOG->PLL_VIDEO &= ~(1 << 16); // 清除bit16(PLL_VIDEO_POWERDOWN):唤醒PLLCCM_ANALOG->PLL_VIDEO &= ~(1 << 12); // 清除bit12(PLL_VIDEO_BYPASS):禁用PLL旁路(使用PLL输出)// 设置PLL_VIDEO的倍频系数(NUM/denom):PLL输出 = 输入时钟 * (NUM + 1) / denom// 此处NUM=0、denom=1 → 倍频系数=1(默认输入时钟为24MHz晶振,暂不倍频)CCM_ANALOG->PLL_VIDEO_NUM = 0; // PLL倍频分子(+1后生效)CCM_ANALOG->PLL_VIDEO_DENOM = 1; // PLL倍频分母// 配置PLL_VIDEO后分频(POST_DIV)和锁定参数t = CCM_ANALOG->PLL_VIDEO;t |= (0x2 << 19); // bit19~20(POST_DIV_SELECT)=010:PLL输出后分频系数=3t |= (1 << 13); // bit13(PLL_VIDEO_LOCK_EN)=1:使能PLL锁定检测t &= ~(0x7F << 0); // 清除bit0~6(PLL_VIDEO_FRAC):禁用小数分频(整数分频模式)t |= (31 << 0); // bit0~6=31:配置PLL环路滤波器(确保锁定稳定性)CCM_ANALOG->PLL_VIDEO = t;// 等待PLL_VIDEO锁定:bit31(PLL_VIDEO_LOCK)=1表示PLL稳定while((CCM_ANALOG->PLL_VIDEO & (1 << 31)) == 0);// -------------------------- 3. 配置LCDIF最终时钟分频 --------------------------CCM->CSCDR2 &= ~(0x7 << 15); // 清除时钟源选择位CCM->CSCDR2 |= (0x2 << 15) | (0x3 << 12); // 配置:// bit15~17=0010 → 时钟源=PLL_VIDEO// bit12~14=011 → LCDIF时钟4分频(PLL输出/4)// 配置LCDIF时钟父时钟选择(CBCMR):bit23~25=0101 → 选择PLL_VIDEO作为父时钟CCM->CBCMR |= (0x5 << 23);// 清除LCDIF额外分频(CSCDR2 bit9~11):禁用额外分频,确保时钟仅受上述4分频控制CCM->CSCDR2 &= ~(0x7 << 9);
}
3. LCD 主初始化函数:lcd_init(void)
功能:整合 IO、时钟初始化,配置 LCD 核心参数(分辨率、时序)和 LCDIF 控制器,是 LCD 驱动的入口函数。
// 全局变量:存储LCD关键参数(分辨率、时序、帧缓冲区地址等)
lcd_param_t lcd_info;void lcd_init(void)
{// -------------------------- 1. 初始化IO和时钟 --------------------------lcd_io_init(); // 初始化LCD引脚(复用+电气属性)lcd_clk_init(); // 初始化LCDIF时钟// -------------------------- 2. 设置LCD核心参数(需与LCD屏 datasheet匹配) --------------------------lcd_info.width = 800; // LCD水平分辨率(像素)lcd_info.height = 480; // LCD垂直分辨率(像素)lcd_info.hbp = 88; // 水平后沿(HSYNC后到有效数据的像素数)lcd_info.hfp = 40; // 水平前沿(有效数据后到下一个HSYNC的像素数)lcd_info.hspw = 48; // 水平同步脉宽(HSYNC信号的有效宽度,像素数)lcd_info.vbp = 32; // 垂直后沿(VSYNC后到有效数据的行数)lcd_info.vfp = 13; // 垂直前沿(有效数据后到下一个VSYNC的行数)lcd_info.vspw = 3; // 垂直同步脉宽(VSYNC信号的有效宽度,行数)lcd_info.byte_per_pix = 4; // 每个像素的字节数(ARGB8888格式,4字节)lcd_info.cur_frame_addr = FRAME_ADDR; // 当前帧缓冲区地址(定义在SDRAM中,需足够大:800*480*4=1.5MB)// -------------------------- 3. 配置LCDIF控制器寄存器 --------------------------// -------------------------- 3.1 核心控制寄存器(CTRL):配置工作模式 --------------------------LCDIF->CTRL |= (1 << 31); // bit31(SOFT_RESET)=1:LCDIF软复位(初始化控制器状态)delay_ms(10); // 延时10ms,等待复位完成LCDIF->CTRL &= ~(0x3 << 30); // 清除bit30~31(RESET状态):退出复位// 配置CTRL关键位:LCDIF->CTRL |= (1 << 19) // bit19(DATA_FORMAT_SEL)=1:使用ARGB格式(而非RGB)| (1 << 17) // bit17(LCDIF_ENABLE)=1:使能LCDIF数据传输| (0x3 << 10) // bit10~11(PIXEL_FORMAT)=11:像素格式=ARGB8888(32位)| (0x3 << 8) // bit8~9(DATA_WIDTH)=11:数据总线宽度=32位(匹配24位数据+8位控制)| (1 << 5); // bit5(BURST_MODE)=1:使用突发传输(提高效率,适配SDRAM)// -------------------------- 3.2 数据有效字节配置(CTRL1) --------------------------LCDIF->CTRL1 &= ~(0xf << 16); // 清除bit16~19(VALID_DATA_MASK):先清零LCDIF->CTRL1 |= (0x7 << 16); // bit16~18=111:32位总线中低24位为有效数据(LCD为24位RGB)// -------------------------- 3.3 传输像素数配置(TRANSFER_COUNT) --------------------------// 高16位:垂直方向传输行数(height);低16位:水平方向传输像素数(width)LCDIF->TRANSFER_COUNT = (lcd_info.height << 16) | (lcd_info.width);// -------------------------- 3.4 垂直同步控制0(VDCTRL0):同步信号极性 --------------------------LCDIF->VDCTRL0 |= (1<<28) // bit28(HSYNC_POL)=1:HSYNC高电平有效| (1 <<24) // bit24(VSYNC_POL)=1:VSYNC高电平有效| (1<<21) // bit21(DOTCLK_POL)=1:DOTCLK上升沿采样数据| (1<< 20) // bit20(ENABLE_POL)=1:LCD_ENABLE高电平显示| (0x3<< 0); // bit0~1(PERIOD_UNIT)=11:时序参数单位=像素(水平)/行(垂直)// -------------------------- 3.5 垂直同步控制1(VDCTRL1):垂直总周期 --------------------------// 垂直总周期 = 有效行数(height) + 垂直同步脉宽(vspw) + 垂直前沿(vfp) + 垂直后沿(vbp)LCDIF->VDCTRL1 = lcd_info.height + lcd_info.vspw + lcd_info.vfp + lcd_info.vbp;// -------------------------- 3.6 垂直同步控制2(VDCTRL2):水平总周期+垂直同步脉宽 --------------------------// 高16位:垂直同步脉宽(vspw);低16位:水平总周期(有效像素+hspw+hfp+hbp)LCDIF->VDCTRL2 = (lcd_info.vspw << 18) | (lcd_info.width + lcd_info.hspw + lcd_info.hfp + lcd_info.hbp);// -------------------------- 3.7 垂直同步控制3(VDCTRL3):同步后沿配置 --------------------------// 高16位:垂直同步后沿(vspw + vbp);低16位:水平同步后沿(hspw + hbp)// 作用:同步信号后多久开始传输有效数据LCDIF->VDCTRL3 |= (lcd_info.hspw + lcd_info.hbp) << 16 | (lcd_info.vspw + lcd_info.vbp);// -------------------------- 3.8 垂直同步控制4(VDCTRL4):有效像素配置 --------------------------LCDIF->VDCTRL4 |= (1 << 18) // bit18(SYNC_SIGNALS_EN)=1:使能HSYNC/VSYNC信号输出| (lcd_info.width << 0); // bit0~15:水平有效像素数(width)// -------------------------- 3.9 配置帧缓冲区地址 --------------------------LCDIF->CUR_BUF = lcd_info.cur_frame_addr; // 当前帧缓冲区地址(LCDIF读取数据的地址)LCDIF->NEXT_BUF = lcd_info.cur_frame_addr; // 下一帧缓冲区地址(双缓冲用,此处单缓冲暂设为同一地址)// -------------------------- 3.10 使能LCDIF控制器 --------------------------LCDIF->CTRL |= (1 << 0); // bit0(LCDIF_MASTER_EN)=1:使能LCDIF主控制器,开始数据传输
}
4. LCD 清屏函数:lcd_clear(unsigned int color)
功能:填充 LCD 屏幕像素,实现清屏效果(特殊逻辑:上半屏用指定颜色,下半屏用青色0xffff
)。
void lcd_clear(unsigned int color)
{int i = 0; // 水平方向像素索引(x轴)int j = 0; // 垂直方向像素索引(y轴)// 遍历所有像素(y从0到height-1,x从0到width-1)for(j = 0; j < lcd_info.height; j++){for(i = 0; i < lcd_info.width; i++){// 逻辑:上半屏(j < 高度/2)用指定颜色,下半屏用0xffff(ARGB8888=0x0000FFFF,青色)if(j < (lcd_info.height / 2))draw_point(i, j, color); // 调用画点函数填充上半屏elsedraw_point(i, j, 0xffff); // 填充下半屏为青色}}
}
5. 基础绘图函数
5.1 画点函数:draw_point(unsigned int x, unsigned int y, unsigned int color)
功能:在指定坐标(x,y)
绘制一个像素,是所有绘图函数的基础。
// -------------------------- 画点函数 --------------------------
// 功能:在(x,y)坐标绘制指定颜色的点(ARGB8888格式)
// 参数:x-水平坐标(0~width-1),y-垂直坐标(0~height-1),color-颜色(0xRRGGBBAA或0xAARRGGBB,取决于格式)
void draw_point(unsigned int x, unsigned int y, unsigned int color)
{unsigned int *p; // 像素地址指针(指向帧缓冲区中该像素的内存地址)// -------------------------- 坐标越界检查(注释待启用,避免内存访问错误) --------------------------// if (x >= lcd_info.width || y >= lcd_info.height)// {// return; // 坐标超出屏幕范围,直接返回// }// -------------------------- 计算像素的内存地址 --------------------------// 公式:帧缓冲区基地址 + (y行号 * 每行像素数 + x列号) * 每个像素字节数// 例:(x=100,y=50) → 地址=FRAME_ADDR + (50*800 + 100)*4p = (unsigned int *)(lcd_info.cur_frame_addr + (y * lcd_info.width + x) * lcd_info.byte_per_pix);// -------------------------- 写入颜色值 --------------------------*p = color; // 将颜色值写入帧缓冲区,LCDIF会自动读取并显示
}
5.2 画横线函数:draw_h_line(unsigned int x0, unsigned int y0, unsigned int len, unsigned int color)
功能:从(x0,y0)
开始,绘制长度为len
的水平直线(固定 y,x 递增)。
void draw_h_line(unsigned int x0, unsigned int y0, unsigned int len, unsigned int color)
{int i = 0;// 循环绘制len个像素:x从x0到x0+len-1,y固定为y0for(i = 0; i < len; i++){draw_point(x0 + i, y0, color);}
}
5.3 画竖线函数:draw_v_line(unsigned int x0, unsigned int y0, unsigned int len, unsigned int color)
功能:从(x0,y0)
开始,绘制长度为len
的垂直直线(固定 x,y 递增)。
void draw_v_line(unsigned int x0, unsigned int y0, unsigned int len, unsigned int color)
{int i = 0;// 循环绘制len个像素:y从y0到y0+len-1,x固定为x0for(i = 0; i < len; i++){draw_point(x0, y0 + i, color);}
}
5.4 画矩形框函数:draw_recv(unsigned int x0, unsigned int y0, unsigned int w, unsigned int h, unsigned int color)
功能:绘制一个左上角为(x0,y0)
、宽度为w
、高度为h
的矩形边框(仅画四条边)。
void draw_recv(unsigned int x0, unsigned int y0, unsigned int w, unsigned int h, unsigned int color)
{draw_h_line(x0, y0, w, color); // 上边框:(x0,y0) → (x0+w,y0)draw_h_line(x0, y0 + h, w, color); // 下边框:(x0,y0+h) → (x0+w,y0+h)draw_v_line(x0, y0, h, color); // 左边框:(x0,y0) → (x0,y0+h)draw_v_line(x0 + w, y0, h, color); // 右边框:(x0+w,y0) → (x0+w,y0+h)
}
5.5 填充矩形函数:draw_fill_recv(unsigned int x0, unsigned int y0, unsigned int w, unsigned int h, unsigned int color)
功能:绘制一个左上角为(x0,y0)
、宽度为w
、高度为h
的实心矩形(填充所有像素)。
void draw_fill_recv(unsigned int x0, unsigned int y0, unsigned int w, unsigned int h, unsigned int color)
{int i = 0;// 循环绘制h行横线:y从y0到y0+h-1,每行宽度为wfor(i = 0; i < h; i++){draw_h_line(x0, y0 + i, w, color);}
}