ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

EFM32 EBI驱动ILI9342并行LCD:时序配置与驱动代码详解

EFM32 EBI驱动ILI9342并行LCD:时序配置与驱动代码详解 简介一套面向嵌入式开发者的EFM32与ILI9342驱动资源围绕EBI外部总线接口与TFT彩屏连接展开涵盖ILI9342的引脚定义、寄存器配置及基本驱动框架适合在EFM32平台快速移植或调试320×240分辨率显示屏。压缩包体积仅4KB包含2个文件分别为C源码与配套头文件代码量精简便于直接查阅与二次开发。资源对ILI9342的D0-D15数据线、CS片选、RS命令/数据选择及读写使能等关键引脚做了清晰梳理并给出初始化序列、显示控制命令与底层读写函数的实现参考可帮助理解高速、低功耗的EBI总线如何操作LCD同时避免与ILI9431型号混淆导致的接线错误。目前已有278人学习适合物联网、低功耗显示终端等项目的嵌入式开发者参考借鉴也可作为相关课程设计的入门模板。1. 一开是给 SPI 屏结果被 ILI9342 的并行总线卡住了很多人第一次接触 ILI9342 是因为从压缩包里翻到了ili9342.c和ili9342.h然后照着网上的 ILI9342 引脚图把 16 根数据线、WR、RD、CS、RS 全部接好复位也拉过了屏幕上却一个字都不出。问题往往不在接线而在总线本身GPIO 模拟的 8080 时序可以靠延时强撑但 EFM32 的 EBIExternal Bus Interface把外部设备当作内存来访问地址建立时间、等待状态、数据保持时间的配置但凡有一点偏差后续所有命令都进不了控制器屏自然白得干净。这篇文章围绕 EFM32 通过 EBI 驱动 ILI9342 这条主线展开内容覆盖信号对接、bank 映射、等待状态参数、初始化序列和实际驱动代码。适合已经把屏点亮过、但想摆脱 SPI 刷屏速度瓶颈的嵌入式工程师也适合第一次接手并行接口 LCD 的新手跟着寄存器配置走一遍比直接套驱动例程更不容易踩坑。2. EFM32 EBI 的 Bank 映射与 ILI9342 信号对接2.1 把 LCD 当成 SRAMEBI 的核心工作方式网上能搜到的 stlink v2 引脚图、树莓派 4B 引脚功能图这类资料按图接线基本就能跑但 EBI 这类并行总线不一样引脚图只是起点真正决定设备能否工作的是总线时序和地址映射。EFM32 的 EBI 接口把外部设备映射到统一地址空间每个 bank 对应一个固定基地址读写操作在 CPU 看来就是向某个内存地址写入数据而芯片会自动产生片选、读使能、写使能信号。这个特性对 ILI9342 特别有用。ILI9342 是 16 位并行接口的 TFT-LCD 控制器支持 8080 时序信号线包括数据线 D0-D15、片选 CS、命令/数据选择 RS、写使能 WR 和读使能 RD。如果把 LCD 映射到 EBI 的一个 bank那么只要改变写入的地址位RS 信号就会自动变化编程时不需要手动翻转 GPIO 来切换命令和数据类型。常见做法是把 EBI 的一根地址线接到 RS 引脚用地址的最低位区分命令周期和数据周期这样驱动代码看起来就像是在操作一块 SRAM。以 EFM32LG 系列为例EBI 提供 4 个 bankBank0 基地址为 0x60000000Bank1 为 0x64000000每个 bank 空间足够容纳多个外设。要把 ILI9342 挂上去需要先确认芯片型号对应的 EBI 引脚组再配置映射关系。需要注意EBI 的地址、数据、控制信号是复用引脚初始化时不能按普通 GPIO 的 push-pull 方式去配置必须交给 EBI 外设接管这部分最容易出错后面代码里会给出完整的配置顺序。2.2 16 位模式下的引脚对接与地址线选择实际接线时我会把 ILI9342 配置成 16 位数据模式这样一次写入就能完成一个像素的 RGB565 数据刷屏效率比 8 位模式高一倍。下表是常见的对接关系适用于 EFM32 系列中带有 EBI 的型号具体引脚组以参考手册的 EBI 引脚连接表为准。EBI 信号ILI9342 引脚说明EBI_AD15..0DB15..DB016 位像素数据线传输 RGB565 格式EBI_CS0CSBank0 片选低有效EBI_A1RS (D/CX)地址线复用为命令/数据选择EBI_WEnWR写使能上升沿锁存数据EBI_REnRD读使能用于读 ID 和状态GPIO 输出RESET复位信号低有效可由任意 GPIO 控制RS 引脚不建议接 EBI_A0因为 EBI 按字节地址编址而 16 位数据访问要求地址按 2 字节对齐挂在 A0 上会产生奇数地址访问处理不当会触发对齐异常或导致数据错位。把 RS 接到 EBI_A1 是更稳妥的方案写命令时访问基地址 0x60000000写数据时访问 0x60000002两个地址相差 2既满足了 16 位对齐又正好让 A1 在高低电平之间切换。复位引脚单独用一个 GPIO 控制是有意为之。很多引脚图资料里把 RESET 和系统复位直接并在一起这样上电顺序没有被严格保证ILI9342 内部上电复位电路对 VCI 和 IOVCC 的时序有要求独立控制可以让软件在电源稳定后再拉高复位尽量减少初始化失败的概率。2.3 EBI 初始化参数先给足等待状态跑通后再压缩EBI 初始化涉及地址建立时间、地址保持时间、读/写周期和等待状态这几个关键参数。常见做法是先设置比较宽松的时序确保屏幕能够响应然后再逐步压缩等待状态提升刷屏速度。下面这段代码基于 emlib 库给出基本配置框架寄存器名字会因为芯片系列不同略有差异以工程中实际使用的头文件为准。EBI_Init_TypeDef ebiInit EBI_INIT_DEFAULT; ebiInit.mode ebiModeD16A8; /* 16 位数据线8 位地址线 */ ebiInit.aleMode ebiALEInactive; /* 不使用地址锁存 */ ebiInit.bankEnable EBI_BANK0; /* 启用 Bank0 映射 */ ebiInit.addrSetup ebiAddrSetup1; /* 地址建立时间 1 个 EBI 时钟周期 */ ebiInit.addrHold ebiAddrHold2; /* 地址保持时间 2 个周期 */ ebiInit.readSetup ebiReadSetup2; /* 读信号建立时间 2 个周期 */ ebiInit.readHold ebiReadHold1; /* 读信号保持时间 1 个周期 */ ebiInit.writeSetup ebiWriteSetup1; /* 写信号建立时间 1 个周期 */ ebiInit.writeHold ebiWriteHold2; /* 写信号保持时间 2 个周期 */ ebiInit.waitStates 4; /* 等待状态 4 个周期先慢后快 */ EBI_Init(ebiInit); GPIO_PinModeSet(gpioPortF, 0, gpioModePushPull, 0); /* 其余 EBI 引脚按相同方式配置 */代码里的关键是把 waitStates 和读写保持时间先放大。EBI 外设的时钟来自 HFCORECLK如果系统主频是 48MHz一个周期大约是 20ns4 个等待状态加上地址建立和保持总共能给 WR 信号留出约 120ns 的有效时间满足 ILI9342 对写周期不小于 100ns 的典型要求。此时屏幕应该能正常响应命令。之后再逐步减小 waitStates每次改完都刷一屏测试图案直到出现噪点或花屏再回退一个档位作为稳定运行的值。初始化完成后不要急着画像素先用逻辑分析仪观察 EBI_WEn 和 EBI_CS0 的波形确认每一次写访问都产生了完整的低电平脉冲。很多人在这里忘记配置引脚复用功能GPIO 还在普通输出模式结果是总线信号根本没有被驱动到引脚上后面所有调试都是浪费时间。3. ILI9342 初始化序列与读写时序读 ID 识别真实屏芯片3.1 初始化序列不是复制粘贴每条命令都必须能解释ILI9342 的初始化序列和 ILI9341 很接近但寄存器地址和默认值并不完全相同直接搬 9341 的模板不一定能点亮。正规的初始化流程应当包括软件复位、Sleep Out、像素格式、内存访问控制和伽马校正。下面是我在一颗常见 ILI9342 模组上使用的基础命令序列共 6 条足以让屏正常显示颜色。命令参数延时作用0x01 Soft Reset无120ms软件复位控制器恢复到上电默认状态0x11 Sleep Out无150ms退出睡眠模式内部振荡器启动0x36 MADCTL0x480ms设置扫描方向0x48 对应竖屏 RGB 顺序0x3A Pixel Format0x550ms16 位像素格式RGB565 每像素 2 字节0x2A Column Address0x00 0x00 0x00 0xEF0ms列地址 0 到 239实际宽 240 像素0x2B Page Address0x00 0x00 0x01 0x3F0ms页地址 0 到 319实际高 320 像素这些命令通过 EBI 总线发送时有一个共同规则命令字节写入命令地址参数写入数据地址每一帧字节的长短由控制器内部寄存器自动判断软件只需要保证在正确的地址写入正确的值。0x2A 命令后必须连续写入 4 个字节的列起始和结束地址如果中间插入其他操作窗口设置就会错位画面会出现颜色串行或图像偏移。像素格式设置为 0x55 时后续传输的每个像素占 16 位EBI 的 16 位数据总线正好一次传输一个像素。如果把像素格式设成 0x66 即 RGB666控制器会按 3 字节解析总线宽度和数据包不吻合写在代码里就会出现颜色通道错位这也是花屏的常见来源之一排查时优先检查这条命令的数值。3.2 读 ID 区分 ILI9342、ILI9341 与 ILI9431ILI9342、ILI9341、ILI9431 这三个型号在网络上经常被混在一起下载的引脚图可能标注的是 ILI9342实际买到手却是 ILI9341 或者 ILI9431 的模组。区分的方法不是靠目测走线而是通过 0x04 Read ID 命令读取控制器型号。读取流程如下写入命令 0x04然后从数据地址连续读 3 个字节分别对应制造商 ID、驱动版本和模块 ID具体响应因屏厂封装而异但版本号一般能区分出不同型号。uint8_t lcd_read_id(void) { uint8_t id[3] {0}; LCD_REG 0x04; /* 向命令地址写入 Read ID 命令 */ delay_us(50); /* 给控制器内部准备时间典型值 20us 以上 */ id[0] LCD_RAM; /* 连续读回三个字节 */ id[1] LCD_RAM; id[2] LCD_RAM; return id[1]; /* 驱动版本号通常可以区分型号 */ }读取时序里有几个值得注意的细节。第一命令写入后必须等待一段稳定时间不能立刻读部分控制器在收到 Read ID 命令后需要几十微秒才会在数据线上输出有效电平。第二每次读回一个字节EBI 会主动产生一个 RD 脉冲控制器内部的数据指针自动递增因此连续读三个数据地址即可。第三读回结果如果全为 0xFF 或全为 0x00说明控制器可能没有响应优先检查 RD 信号是否真正被 EBI 驱动以及 CS 是否被选中的 bank 拉低。识别出真实型号后初始化序列要做相应调整。如果是 ILI9341可以把第 3 章的 6 条命令原样保留如果是 ILI9431需要向屏厂索取初始化代码因为列地址和页地址的范围可能不同。网上搜索 ILI9342 引脚图时经常混入多个型号的资料下载驱动文件时要核对文件内部的寄存器定义优先使用与实物 ID 匹配的那一份代码。4. 驱动实现用地址差代替 GPIO 翻转来点亮 320x2404.1 命令/数据地址的 C 宏设计驱动代码的基础是两个地址宏分别代表命令寄存器和数据寄存器。按照第 2 章的接线RS 接到 EBI_A1命令地址是 0x60000000数据地址是 0x60000002这两个地址在物理上是同一个外设只是因为地址线 A1 的电平不同而访问到控制器的不同内部区域。#define LCD_BASE ((volatile uint16_t *)0x60000000) #define LCD_REG (*(volatile uint16_t *)(LCD_BASE 0x0000)) /* 命令地址 */ #define LCD_RAM (*(volatile uint16_t *)(LCD_BASE 0x0001)) /* 数据地址偏移 1 个 16 位单位 */ static void lcd_write_cmd(uint16_t cmd) { LCD_REG cmd; /* 命令写入命令地址A1 保持低电平 */ } static void lcd_write_data(uint16_t data) { LCD_RAM data; /* 数据写入数据地址A1 被拉高 */ }这里的地址偏移计算要看得仔细。指针类型是uint16_t偏移 1 对应的实际字节地址是 2也就是 0x60000002正好让 EBI_A1 输出高电平。如果指针类型换成uint32_t或者偏移写错访问到的实际地址就会偏离预期RS 信号不翻转命令和数据全部写进同一个寄存器屏幕的表现通常是不显示任何内容或者显示出完全混乱的色块。写命令和写数据函数内部的单行赋值语句被编译成一次 16 位总线写操作。EBI 外设会自动把这次访问转换成 WR 低脉冲数据在上升沿被 ILI9342 锁存。整个过程没有 GPIO 翻转也没有延时循环CPU 开销只有一条写指令这也是 EBI 方案相对于 IO 模拟最大的优势。4.2 窗口设置与画点函数驱动第二部分实现的是窗口设置和像素填充。ILI9342 的工作方式是在设定窗口内顺序写入像素数据写完一个像素后地址指针自动加一因此画一个点其实分三步设置列地址范围、设置页地址范围、写入 RGB565 颜色值。static void lcd_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) { lcd_write_cmd(0x2A); /* 设置列地址 */ lcd_write_data(x0 8); /* 起始列高 8 位 */ lcd_write_data(x0 0xFF); /* 起始列低 8 位 */ lcd_write_data(x1 8); /* 结束列高 8 位 */ lcd_write_data(x1 0xFF); /* 结束列低 8 位 */ lcd_write_cmd(0x2B); /* 设置页地址 */ lcd_write_data(y0 8); /* 起始页高 8 位 */ lcd_write_data(y0 0xFF); /* 起始页低 8 位 */ lcd_write_data(y1 8); /* 结束页高 8 位 */ lcd_write_data(y1 0xFF); /* 结束页低 8 位 */ lcd_write_cmd(0x2C); /* 内存写命令后续数据进入显存 */ } void lcd_draw_pixel(uint16_t x, uint16_t y, uint16_t color) { if (x 240 || y 320) return; /* 坐标越界保护 */ lcd_set_window(x, y, x 1, y 1); /* 开 1x1 窗口 */ LCD_RAM color; /* 写入 RGB565 颜色值 */ }参数x0、y0是窗口左上角坐标x1、y1是右下角坐标含端点在内。每次画点都要调用lcd_set_window虽然会产生 9 次总线写操作但在调试阶段这个代价可以接受因为代码逻辑直观任何一根数据线接错都能在画点时快速暴露出来。窗口起始和结束地址的字节序是高字节在前写反了会导致显示区域完全错乱常见症状是颜色铺满全屏但位置完全不对。清屏函数可以复用窗口函数把整个屏幕设成一个窗口然后用循环连续写入 240 乘 320 个像素。优化方法是把颜色值拆成两个字节直接写入数据地址因为控制器在窗口模式下会连续递增地址中间不需要再发命令总线利用率明显高于逐点设置窗口。4.3 缓冲与 DMA性能瓶颈在哪里不使用 DMA 时清一屏 240x320 的 RGB565 数据需要写入 76800 次每次写操作大约消耗几个系统时钟周期屏看起来是一行一行刷上去的。要提升刷新率最直接的方法是把一整行像素放到 RAM 缓冲区中然后让 DMA 从缓冲区把数据搬运到LCD_RAM地址CPU 只负责准备下一行数据。static uint16_t line_buffer[240]; void lcd_fill_line(uint16_t y, uint16_t color) { for (int i 0; i 240; i) { line_buffer[i] color; /* 填充一行缓冲区 */ } lcd_set_window(0, y, 239, y); /* 只开一行窗口 */ DMA_Transfer(EBI_DATA_ADDR, line_buffer, 240); /* DMA 写入数据地址示例函数 */ }DMA 传输方向是从内存到外设目标地址就是数据寄存器地址传输宽度设置为 16 位传输次数为 240。使用 DMA 后一次行填充只需要等待 DMA 完成中断CPU 可以在传输期间处理其他任务。需要注意ILI9342 的数据写入是连续的一旦窗口设置完毕后续所有总线写入都会按照控制器内部的地址递增顺序落入显存因此 DMA 传输期间不能插入任何其他 EBI 访问否则会破坏当前窗口内的数据流。缓冲区大小可以根据 RAM 资源调整常见做法是准备两个 240 字的缓冲区用 ping-pong 方式交替填充和传输让 DMA 和 CPU 并行工作。如果 RAM 足够大可以直接准备整帧缓冲区代价是 76800 字节的存储空间在 EFM32 这类低功耗 MCU 上一般只用于静态图片显示动画场景还是用逐行传输更现实。5. 白屏、花屏、颜色错乱的定位与等待状态校准5.1 症状对应的排查方向我经手过的 ILI9342 调试问题最后大多集中在下面这张表列出的几个方向。屏幕不亮优先查硬件屏幕亮了但内容不对优先查命令序列和时序参数。现象最可能原因检查方式全白屏初始化命令未生效用示波器看 CS 和 WR 是否在复位后有脉冲全黑屏背光未打开或像素格式错误检查背光引脚确认 0x3A 参数为 0x55雪花噪点EBI 等待状态过小把 waitStates 增大到 4 再观察颜色偏绿或偏红RGB 顺序或 RGB565 字节序错误修改 MADCTL 的 BGR 位图像上下颠倒扫描方向配置错误调整 0x36 命令的 MX/MY 位画面整体错位窗口地址命令字节序写反检查 0x2A/0x2B 高 8 位和低 8 位顺序颜色错乱这一项容易被误判为硬件接错线。16 位数据模式下如果 D0-D15 中有两根线接反颜色偏差会呈现出固定的规律而不是随机噪点。RGB565 格式中高 5 位是红色中间 6 位是绿色低 5 位是蓝色当字节序写反时红蓝通道会交换画面整体偏蓝或偏红这时候改 MADCTL 的 BGR 位不一定有效必须检查数据线。5.2 用逻辑分析仪核对单次总线写周期示波器或逻辑分析仪应挂在 CS、WR、A1 三根信号上触发条件设为 CS 下降沿观察写入命令地址和写入数据地址时 A1 的电平是否分别为低和高。如果 A1 始终为低或者始终为高说明地址偏移宏写错了如果 WR 脉冲宽度明显小于数据手册要求的 100ns说明等待状态配置过小。ILA9342 对写周期的最小脉宽要求可以在具体模组的数据手册里查到不同封装批次略有差异预留 20% 余量比较保险。5.3 扫描色块法找到稳定的等待状态边界一个可行的校准方法是写一个渐变色填充函数从暗到亮逐列变化然后在不同的 waitStates 值下反复刷屏。肉眼能明显看出色带断层、横向噪点或亮度闪烁的位置出现这些现象时说明当前等待状态不够。记录下稳定运行的最小值把它再加上 1作为最终配置写入初始化代码。void lcd_fill_gradient_test(void) { for (int x 0; x 240; x) { uint16_t color (x 3) | ((x 0x07) 5); /* 低字节渐变保留高位色彩 */ for (int y 0; y 320; y) { lcd_draw_pixel(x, y, color); } } }色块扫描的判定标准是渐变过渡是否平滑。如果屏幕在高亮度区域出现横向细线或闪烁点说明数据传输在部分周期内没有满足建立时间要求。常见做法是从 waitStates 等于 4 开始每次减 1直到渐变出现噪点再回退一个档位。如果回退之后仍然偶发花屏还要检查 HFCORECLK 频率是否有动态变化低功耗模式下时钟切换会让 EBI 时序随主频漂移这种情况必须固定系统时钟源不能只改等待状态。本文还有配套的精品资源点击获取
返回列表