ARTICLE DETAIL

资讯详情

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

EFM32 EBI驱动ILI9342:并口LCD当内存写,DMA刷屏优化

EFM32 EBI驱动ILI9342:并口LCD当内存写,DMA刷屏优化 简介一份面向嵌入式开发者的ILI9342彩屏驱动代码资源基于EFM32微控制器的EBI外部总线接口实现。资源围绕ILI9342液晶驱动芯片展开重点解决芯片初始化、寄存器配置、数据总线连接及常见引脚映射问题也适用于需要区分ILI9342与ILI9431型号差异的调试场景。压缩包共2个文件包含1个C源文件和1个头文件整体约4KB。C源文件提供显示初始化序列、方向设置和基础绘图命令头文件则定义数据线、命令/数据选择线、读写使能等引脚映射便于快速集成到现有板卡中。已有278人浏览学习适合正在使用EFM32、STM32等MCU驱动同系列TFT屏幕的开发者参考。通过这份精简驱动可快速获得可运行的底层代码理解EBI时序配置与数据宽度设置同时减少排查硬件连接和命令时序错误的时间辅助完成从点屏到图形界面的落地。1. 用 EFM32 的 EBI 驱动 ILI9342把屏幕当成一段内存来写第一次在 EFM32 上点亮 ILI9342很多人先踩一脚 GPIO 模拟 8080 时序的坑翻转 CS、WR逐位搬数据刷一帧 320x240 就要几十毫秒CPU 全程被拖住。换成 EBI 之后完全反转——外部设备被映射进内存地址空间写 LCD 数据寄存器和写普通变量是同一套指令刷屏只花在有效数据搬运上。下面要讲的正是这套组合EFM32 的 EBI 总线怎么和 ILI9342 的并行接口对接引脚图怎么画寄存器怎么设代码怎么落地以及最常栽的地址错位和时序余量问题怎么查。适合自己画板子接屏的人也适合拿着开发板想从 SPI 切到并口提速的人。网上那堆 ILI9342.rar 例程大多基于 51 或 STM32本文会把它们最值钱的那部分——初始化表和并口时序——用 EFM32 的 EBI 方式重写一遍。2. ILI9342 并口与 EFM32 EBI 的映射关系及引脚接线2.1 ILI9342 的 8080 时序和 SRAM 几乎同构ILI9342 支持 8080 和 6800 两种并行总线模式实用中基本都选 8080。在这种模式下控制器对外提供片选 CSX、写使能 WRX、读使能 RDX、命令/数据选择线 D/CX也叫 RS以及 DB0-DB15 数据引脚。写一条命令的过程很直接RS 拉低、CSX 拉低、数据线上放命令字节、WRX 给出一个下降沿写参数时 RS 拉高。这个时序和外部 SRAM 的写周期没有本质区别唯一多出来的就是 RS 这根地址选择线。EBI 在 EFM32 大容量系列里的定位就是接外部存储器和并行外设。把它配置成 SRAM 模式后EBI 会自动产生片选、写使能、读使能信号地址由 MCU 侧发起的读写操作决定。把 ILI9342 的 CSX、WRX、RDX 接到 EBI 的 CS0、WE、RE把 RS 接到一条 EBI 地址线上程序里访问不同地址时 RS 电平自然变化完全不需要 GPIO 去参与时序。这也是 EBI 和 SPI 方案最大的分水岭SPI 靠软件组织字节流EBI 把所有的握手逻辑下沉到硬件外设里。提示不是所有 EFM32 都带 EBI。Giant Gecko、Wonder Gecko 这类大容量时尚有但小容量的 Happy Gecko 等系列没有这个外设。选型时直接查数据手册外设列表别等到原理图都画完才发现不支持。2.2 引脚接线方案与 16 位数据线下面是一份按 8080 并口、16 位数据宽度设计的接线表。模块上的 IM[3:0] 引脚通常已经决定总线宽度接线前务必先对着模块原理图确认不要想当然。ILI9342 引脚方向EFM32 连接说明DB0-DB7双向EBI_D0-D7数据低 8 位DB8-DB15双向EBI_D8-D1516 位模式必接8 位模式可悬空CSX输入EBI_CS0bank0 片选WRX输入EBI_WE写使能低有效RDX输入EBI_RE读使能低有效D/CX输入EBI_A0命令/数据选择RESX输入任意 GPIO复位建议单独接VCI/VDDI电源3.3V注意模块要求的上下电顺序16 位模式下CPU 每次写一个uint16_t就是一次完整的 16 位并行传输刷屏效率比 8 位高一倍。ILI9342 的指令集本身是 8 位的所以命令和参数都放在低 8 位高 8 位写 0 即可。很多市售 2.8 寸、3.2 寸模块出厂就固定成 16 位接线直接把 EBI_D0-D15 对接 DB0-DB15省事也可靠。2.3 地址线偏移RS 接到 A0 时的命令/数据地址EBI 的这段映射是整个方案里最绕的地方。ARM 侧是字节编址但 EBI 在 16 位数据宽度时外部地址线 EBI_A0 实际对应的是 MCU 地址的 bit1所以相邻两个 16 位寄存器在内存里相差 2 字节。把 RS 接到 EBI_A0 时命令地址是 0x80000000数据地址就变成 0x80000002而不是直觉上的 0x80000001。/* RS 接 EBI_A016 位数据宽度 */ #define ILI9342_BASE 0x80000000UL #define ILI9342_CMD_ADDR (ILI9342_BASE 0x0UL) #define ILI9342_DATA_ADDR (ILI9342_BASE 0x2UL) #define ILI9342_CMD (*((volatile uint16_t *)ILI9342_CMD_ADDR)) #define ILI9342_DATA (*((volatile uint16_t *)ILI9342_DATA_ADDR))这里0x2的来历半字对齐访问下地址 bit1 翻转一次对应外部 A0 从 0 变 1也就是 RS 从命令电平切到数据电平。如果你的板子把 RS 接到了 A1那么数据地址要改成0x4。这个偏移不是固定的完全取决于 RS 接在哪根地址线上画原理图时最好在原理图里直接标清楚否则三个月后回来改驱动第一件事就是重新翻图纸。2.4 用一笔带宽账决定要不要上 EBISPI 不是不能用但 320x240 全屏刷新时差距非常直观。假设 SPI 时钟 36MHz考虑字节传输和命令开销有效数据率大约 4.5MB/s一帧 RGB565 是 150KB全屏刷新理论值 33ms。EBI 走 16 位并行总线即使按 24MHz 访问周期计算每两次访问完成一个 16 位像素吞吐也在 48MB/s 量级全屏理论值不到 4ms。实际受 LCD 内部 GRAM 写入速度和 DMA 启动开销影响8ms 到 15ms 刷一帧是很常见的数据。这个差距决定了使用场景。做菜单、静态表盘SPI 足够做滑动列表、动画过渡、或者想给 GUI 留出 CPU 余量EBI 几乎是必经之路。代价是多占一组 GPIO且 PCB 布线时 16 根数据线要尽量等长比 SPI 麻烦一些。3. EFM32 EBI 初始化与 ILI9342 驱动代码落地3.1 使能时钟、引脚路由和 EBI bank 配置EFM32 的 EBI 引脚不是普通 GPIO必须先通过引脚路由寄存器把对应引脚交给 EBI 外设接管再初始化 EBI 本身。下面的代码面向 EFM32GG 系列emlib 写法寄存器名在 GG11 上会有些差异务必以当前 SDK 的头文件定义为准。#include em_cmu.h #include em_ebi.h #include em_gpio.h void ebi_lcd_pin_setup(void) { CMU_ClockEnable(cmuClock_GPIO, true); CMU_ClockEnable(cmuClock_EBI, true); /* 将 EBI 数据线、地址线、CS0 和总线控制引脚交给 EBI 外设 */ GPIO-EBIROUTE GPIO_EBIROUTE_D0D15EN | GPIO_EBIROUTE_A0A23EN | GPIO_EBIROUTE_CS0EN | GPIO_EBIROUTE_BUSEN; } void ebi_lcd_bank_init(void) { EBI_Init_TypeDef ebi { 0 }; ebi.mode ebiModeSRAM; ebi.banks EBI_BANK0; ebi.bankConfig[0].enable true; ebi.bankConfig[0].baseAddress 0x80000000UL; ebi.bankConfig[0].bankSize ebiBankSize4MB; ebi.bankConfig[0].addressLatchEnable false; ebi.bankConfig[0].dataLines ebiDataSize16; ebi.bankConfig[0].mode ebiModeSRAM; EBI_Init(ebi); }GPIO_EBIROUTE_D0D15EN是让 EBI 接管 16 条数据线的总开关A0A23EN接管地址线CS0EN启用第一路片选BUSEN让 WR、RE 等控制信号进入 EBI 输出状态。这里没有对单个引脚做方向设置因为方向由 EBI 读写周期自动切换手工配 GPIO 反而会破坏总线状态。baseAddress决定 bank0 在 MCU 地址空间中的起点EFM32 外部总线空间通常在 0x80000000。bankSize定义了该片选区域的译码范围4MB 足够容纳命令/数据两个地址又不会覆盖到其他外设。addressLatchEnable必须保持 false它只用于地址/数据复用的 NAND 类设备打开后 ILI9342 的时序会整体错乱。3.2 从 .rar 例程里抄初始化表替换底层写入接口下载到的 ILI9342.rar 这类压缩包里面通常是一份基于 51 或 STM32 的屏厂例程。它的主循环和画点函数可以直接忽略但寄存器初始化表值得原样保留。ILI9342 的初始化序列分三段软复位和睡眠唤醒、像素格式与扫描方向、伽马与显示控制。伽马那组寄存器每个模组都有微调屏厂给的值就是标准答案。static void lcd_cmd(uint8_t cmd) { ILI9342_CMD cmd; } static void lcd_data8(uint8_t d) { ILI9342_DATA d; } static void lcd_reset(void) { GPIO_PinOutSet(gpioPortA, 5); delay_ms(10); GPIO_PinOutClear(gpioPortA, 5); delay_ms(15); GPIO_PinOutSet(gpioPortA, 5); delay_ms(120); } void ili9342_init_min(void) { lcd_reset(); lcd_cmd(0x11); /* 退出睡眠模式 */ delay_ms(150); lcd_cmd(0x3A); lcd_data8(0x55); /* RGB56516bit 像素 */ lcd_cmd(0x36); lcd_data8(0x00); /* 扫描方向按模组丝印调整 */ lcd_cmd(0x2A); /* 列地址范围 0-319 */ lcd_data8(0x00); lcd_data8(0x00); lcd_data8(0x01); lcd_data8(0x3F); lcd_cmd(0x2B); /* 行地址范围 0-239 */ lcd_data8(0x00); lcd_data8(0x00); lcd_data8(0x00); lcd_data8(0xEF); lcd_cmd(0x29); /* 开启显示 */ delay_ms(20); }0x11之后的 150ms 延时是硬性要求退出睡眠后立刻操作寄存器会出现偶发的首帧白屏。0x3A设为0x55表示每个像素占 16 位直接对应 EBI 的单次半字写入如果改成 18 位模式后续像素数据要按 3 字节排列效率和内存占用都会恶化一般不建议。0x36控制 RGB 方向和扫描顺序接反屏后字会倒过来这时候改它的值比改 PCB 快得多。3.3 窗口、画点和整屏填充窗口命令用来限定 GRAM 写入范围。0x2A 设置列区间0x2B 设置行区间0x2C 表示开始写显存。发送完 0x2C 后后续所有写操作都按顺序落入窗口内的像素地址。void lcd_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) { lcd_cmd(0x2A); lcd_data8(x0 8); lcd_data8(x0 0xFF); lcd_data8(x1 8); lcd_data8(x1 0xFF); lcd_cmd(0x2B); lcd_data8(y0 8); lcd_data8(y0 0xFF); lcd_data8(y1 8); lcd_data8(y1 0xFF); lcd_cmd(0x2C); } void lcd_fill(uint16_t color) { uint32_t i; lcd_set_window(0, 0, 319, 239); for (i 0; i 320UL * 240UL; i) { ILI9342_DATA color; } }窗口地址都是 16 位所以高字节和低字节要分两次发。lcd_fill里没有查表和函数调用编译器会把循环优化成连续的半字写入序列。EBI 模式下这个循环的耗时基本等于屏幕刷新的硬件下限再也没有 GPIO 翻转带来的额外开销。4. EBI 时序参数、地址错位与调试手法4.1 先用保守时序点亮再往里压EBI 在 SRAM 模式下有独立的时序参数常见的是地址建立时间、读周期长度、写周期长度。SDK 封装在EBI_Timing_TypeDef里不同版本的 emlib 枚举命名稍有出入编译报错就打开 em_ebi.h 对照。/* 保守起步值点亮后再逐项收紧 */ EBI_Timing_TypeDef timing { 0 }; timing.addrSetup 1; /* 地址建立时间单位 EBI 时钟周期 */ timing.readCycle 4; /* 读周期总长度 */ timing.writeCycle 3; /* 写周期总长度 */ /* timing 配置写到 ebi 初始化结构里再调用 EBI_Init */这三个参数的作用和调试方向可以整理成表参数控制对象调大后的表现调小后的风险addrSetup地址信号到 WR 有效之间的等待信号更从容地址未稳定就写命令串位readCycle整个读周期长度读数据更可靠RD 结束后总线还没释放writeCycle整个写周期长度WR 低脉冲更宽ILI9342 采样不到有效数据pageMode连续访问是否压缩地址周期小幅度提速屏控制器对页模式支持不稳ILI9342 的并行写时序在数据手册里会给最小写脉冲宽度一般是几十纳秒。EFM32 的 EBI 时钟通常几十 MHz一个周期约二三十纳秒所以从 writeCycle3 起步比较稳。点亮后可以慢慢降到 2看到整屏出现零散花点就说明写到边界了退回上一档再留一点余量。4.2 地址错位最常见的三种表现地址错位几乎每个 EBI 接屏项目都会遇到。第一种是命令/数据地址差算错典型表现是初始化后屏幕只亮一部分或整屏乱码但背光正常。把示波器探头点到 RS 引脚对比初始化序列如果 RS 的跳变次数远少于命令数基本就是命令地址和数据地址重叠了。第二种是 bank 空间溢出。bankSize 配置得多小地址译码范围就多大。比如 bank 只有 4MB代码里数据地址却写到 0x80400000写操作不会报错但读出来全是 0xFF示波器上看不到片选脉冲。解决办法是先确认 bankSize 和 baseAddress 的覆盖范围再把命令/数据地址限制在这个窗口内。第三种是引脚复用冲突。EBI 数据引脚可能和 SWD、USART 或定时器通道共用封装引脚GPIO-EBIROUTE打开后这些引脚不再响应普通 GPIO 配置。如果你的调试器正好占用同一组引脚调试时会看到莫名奇妙的波形甚至程序一进调试就死机。不管用的是 J-Link 还是兼容 ST-Link 的 ARM 仿真器先把调试器引脚和 EBI 引脚图对照一遍能省掉一整天的排错时间。4.3 用读 ID 命令验证数据线是否接对ILI9342 的 0x04 命令可以回读控制器 ID是验证数据线最直接的手段。uint16_t lcd_read_id(void) { uint16_t id; ILI9342_CMD 0x04; delay_us(5); id ILI9342_DATA; /* 第一次读到的是 ID 的低 16 位 */ return id; }注意读到的 ID 可能分布在多个字节里一次 16 位读不一定能覆盖完整 ID需要按数据手册把返回的寄存器拼起来。如果读回来全是 0xFF先检查 EBI 的数据线配置是不是真为 16 位如果全是 0x00重点查 RDX 和 CS0 的波形读周期没有建立起来时数据线就一直保持低电平。5. 用 DMAEBI 刷满 320x240验证帧率与降低 CPU 占用5.1 DMA 描述符怎么填CPU 循环写整屏已经能跑出不错的帧率但要想把 CPU 彻底解放出来需要让 DMA 把帧缓冲搬到 EBI 数据地址。DMA_CfgChannel_TypeDef ch { 0 }; ch.select DMAREQ_NONE; /* 软件触发 */ DMA_CfgChannel(DMA_CHANNEL_LCD, ch); DMA_CfgDescr_TypeDef d { 0 }; d.srcInc DMA_SRC_INC_1; /* 源地址按 16 位递增 */ d.dstInc DMA_DEST_INC_NONE; /* 目标地址固定 */ d.size DMA_DATA_SIZE_2; /* 每次传输 16 位 */ d.arbRate DMA_ARBITRATE_8; DMA_CfgDescr(DMA_CHANNEL_LCD, true, d); DMA_Transfer(DMA_CHANNEL_LCD, (void *)frame_buffer, (void *)ILI9342_DATA_ADDR, 320 * 240);这里最容易写错的是dstInc。EBI 外设地址是固定的寄存器映射不是 RAM 数组所以目标地址必须设成 NONE地址递增逻辑完全交给 ILI9342 内部的 GRAM 指针。size设为 2 是像素格式决定的RGB565 下每个像素正好一个 16 位半字和初始化时的0x3A0x55一一对应。5.2 帧率量法与优化顺序验证帧率不推荐用软件定时器去掐秒表最简单可靠的方法是在 DMA 完成中断里翻转一个 GPIO再拿逻辑分析仪量翻转频率。这样测到的是实际搬运时间加上 DMA 中断开销比任何估算都真实。优化顺序建议这样走先把 writeCycle 从 3 压到 2确认花点没有变多然后把 DMA 的 arbRate 调大让单次突发尽量长最后再考虑 EBI 页模式。页模式在连续读写时会压缩部分地址周期但在 320x240 全屏场景下收益通常不到 10%却可能引入屏控制器兼容问题优先级排在最后。如果跑 GUI建议在 RAM 里开一块完整的帧缓冲背景、控件、文字都先画到缓冲里再在垂直消隐期间用 DMA 整帧搬运。320x240x2 字节就是 150KBEFM32GG 的大容量型号能扛住小容量的型号只能退而求其次做局部窗口刷新每次只 DMA 一个脏矩形区域。本文还有配套的精品资源点击获取
返回列表