特征
三个独立芯片选择引脚,可设置极性,支持三个设备,时序独立设置
数据总线和地址总线分开。
每个设备支持1MB空间
8/16位数据宽度
支持8080模式LCD接口
支持PDMA
支持设置读写空闲时间
可选内部HCLK分频得到MCLK或者外部EBI_MCLK
支持地址总线和数据总线分开
配置
时钟使能
EBI (CLK_HCLKEN[9])
复位模块
EBIRST (SYS_AHBIPRST[3])
引脚
功能
地址映射
三个片选分别对应地址如下:
连接
地址总线和数据总线分开模式
读写时序
tASU:地址建立时间,CS拉低到RD或WR拉低的时间,连续读模式CACCESS=1,tASU不需要.
tACC:RD,WD拉低的时间,数据访问时间,即给对方去读数据的时间。
tAHD:数据保持时间,即RD,WR拉高后到CS拉高的时间。
寄存器
EBI_CTLx
设置时钟分频,使能EBI功能,总线宽度设置,CS极性等
EBI_TCTLx
设置时序参数
使用EBI驱动LCD设计
硬件设计
LCD选型
从原理图可以看出CON11为EBI接口,设计时实际就是预留给LCD使用的,可以使用EBI接口,接8080模式16bit的LCD屏。
对应如下:
从淘宝上挑选了一个便宜的2.4寸支持8080模式的触摸屏。使用的驱动芯片是ST7789V。
原理图和PCB设计
使用KiCAD设计了原理图和PCB
原理分析
用的CS0片选,所以地址空间为0x6000_0000 ~ 0x600F_FFFF
由于RS引脚接到了ADDR10,而且使用的是16bit模式,所以地址中的1<<11位对应ADDR10即RS。
如果是8位模式则一一对应,地址中的1<<10位对应ADDR10即RS
写地址0x60000000 | 1<<11则ADDR10为1,RS=1表示数据
写地址0x60000000 | 0<< 11则ADDR10为0,RS=0表示命令
所以读写命令或者数据只需要读写地址0x60000000 | 1<<11和0x60000000 | 0<< 11即可。
液晶屏的参数如下
根据以上
先确认HCLK时钟分频2^x次得到MCLK,x=0~7最大分频128。
sysGetClock(SYS_HCLK))可以获取HCLK单位为M
分频x则MCLK为x/HCLK uS = 1000*x/HCLK nS
nu_clocks可以打印当前时钟配置
代码编写
见lcd.c
#include <rtconfig.h>
#include <rtdevice.h>
#include "nuc980.h"
#include "nu_ebi.h"
#include "nu_sys.h"
#include <rthw.h>
#include <drv_gpio.h>
#define LCD_XSIZE 320
#define LCD_YSIZE 240
#define CMD_ADDR (0x60000000 | 0u<<11)
#define DAT_ADDR (0x60000000 | 1u<<11)
#define LLCD_WRITE_CMD(cmd) ((volatile uint16_t)CMD_ADDR)=cmd
#define LLCD_WRITE_DATA(dat) ((volatile uint16_t)DAT_ADDR)=dat
#define LLCD_READ_DATA() ((volatile uint16_t)DAT_ADDR)
#define MCLK_DIVSET 5 /* 07 75m 26ns /
#define MCLK_DIVVAL 32 / 1128分频 /
#define R2R 100 / >10nS /
#define W2X 100 / >10nS /
#define TAHD 100 / >10nS /
#define TACC 3000 / >355nS /
void lcd_init(void)
{
uint32_t hclk = sysGetClock(SYS_HCLK);
uint32_t r2r;
uint32_t w2x;
uint32_t tahd;
uint32_t tacc;
/ 1. 引脚配置 /
/ PA12背光 EBI_ADDR8 /
rt_pin_mode(NU_GET_PININDEX(NU_PA, 12), PIN_MODE_OUTPUT);
/ PA11复位 EBI_ADDR9 /
rt_pin_mode(NU_GET_PININDEX(NU_PA, 11), PIN_MODE_OUTPUT);
/ PA10 EBI_ADDR10 (LCD_RS) MFP1
PA9 EBI_nCS0 MFP1
PA8 EBI_nRE MFP1
PA7 EBI_nWE MFP1
PC0PC15 EBI_DATA0EBI_DATA15 MFP1
*/
M32(REG_SYS_GPC_MFPL) = 0x11111111; /*PC0~PC15 MFP1 /
M32(REG_SYS_GPC_MFPH) = 0x11111111;
M32(REG_SYS_GPA_MFPL) = (M32(REG_SYS_GPA_MFPL) & 0x0FFFFFFF) | 0x10000000; / PA7 MFP1 /
M32(REG_SYS_GPA_MFPH) = (M32(REG_SYS_GPA_MFPH) & 0xFFFFF000) | 0x00000111; / PA8 PA9 PA10 MFP1 /
/ 2. 时钟配置 */
M32(REG_CLK_HCLKEN) |= (1u<<9); /* 使能时钟 */
/* 3. 复位 */
M32(REG_SYS_AHBIPRST) |= (1u<<3); /* 复位EBI */
M32(REG_SYS_AHBIPRST) &= ~(1u<<3); /* 结束复位 */
/* 4. 时序 */
EBI->CTL0 |= 1u<<24; /* EBI write buffer Enabled */
EBI->CTL0 = (EBI->CTL0 & (~(0x07<<8))) | (MCLK_DIVVAL<<8);
EBI->CTL0 |= 0u<<4; /* Continuous data access mode tASU cycle is bypass. */
EBI->CTL0 |= 1u<<1; /* 16bit. */
r2r = R2R/(1000*MCLK_DIVVAL/hclk) & 0x0F;
if(r2r==0)
{
r2r=1;
}
w2x = W2X/(1000*MCLK_DIVVAL/hclk) & 0x0F;
if(w2x==0)
{
w2x=1;
}
tahd = TAHD/(1000*MCLK_DIVVAL/hclk) & 0x07;
if(tahd==0)
{
tahd=1;
}
tacc = TACC/(1000*MCLK_DIVVAL/hclk) & 0x1F;
if(tacc==0)
{
tacc=1;
}
EBI->TCTL0 = (r2r<<24) | (0 <<23) | (0<<22) | (w2x<<12) | ((tahd-1)<<8) | ((tacc-1)<<3);
EBI->CTL0 |= 1u<<0; /* EBI function Enabled. */
/* 5. LCD复位 */
rt_pin_write(NU_GET_PININDEX(NU_PA, 11), PIN_LOW);
/* 手册P48 延时大于10uS */
rt_thread_mdelay(1);
rt_pin_write(NU_GET_PININDEX(NU_PA, 11), PIN_HIGH);
/* LCD寄存器初始化 */
rt_thread_mdelay(120);
//---------------------------------------------------------------------------------------------------//
LLCD_WRITE_CMD (0x11);
rt_thread_mdelay(120); //Delay 120ms
//------------------------------display and color format setting--------------------------------//
LLCD_WRITE_CMD (0x36);
LLCD_WRITE_DATA (0x00);
LLCD_WRITE_CMD (0x3a);
LLCD_WRITE_DATA (0x05);
//--------------------------------ST7789V Frame rate setting----------------------------------//
LLCD_WRITE_CMD (0xb2);
LLCD_WRITE_DATA (0x0c);
LLCD_WRITE_DATA (0x0c);
LLCD_WRITE_DATA (0x00);
LLCD_WRITE_DATA (0x33);
LLCD_WRITE_DATA (0x33);
LLCD_WRITE_CMD (0xb7);
LLCD_WRITE_DATA (0x35);
//---------------------------------ST7789V Power setting--------------------------------------//
LLCD_WRITE_CMD (0xbb);
LLCD_WRITE_DATA (0x28);
LLCD_WRITE_CMD (0xc0);
LLCD_WRITE_DATA (0x2c);
LLCD_WRITE_CMD (0xc2);
LLCD_WRITE_DATA (0x01);
LLCD_WRITE_CMD (0xc3);
LLCD_WRITE_DATA (0x0b);
LLCD_WRITE_CMD (0xc4);
LLCD_WRITE_DATA (0x20);
LLCD_WRITE_CMD (0xc6);
LLCD_WRITE_DATA (0x0f);
LLCD_WRITE_CMD (0xd0);
LLCD_WRITE_DATA (0xa4);
LLCD_WRITE_DATA (0xa1);
//--------------------------------ST7789V gamma setting---------------------------------------//
LLCD_WRITE_CMD (0xe0);
LLCD_WRITE_DATA (0xd0);
LLCD_WRITE_DATA (0x01);
LLCD_WRITE_DATA (0x08);
LLCD_WRITE_DATA (0x0f);
LLCD_WRITE_DATA (0x11);
LLCD_WRITE_DATA (0x2a);
LLCD_WRITE_DATA (0x36);
LLCD_WRITE_DATA (0x55);
LLCD_WRITE_DATA (0x44);
LLCD_WRITE_DATA (0x3a);
LLCD_WRITE_DATA (0x0b);
LLCD_WRITE_DATA (0x06);
LLCD_WRITE_DATA (0x11);
LLCD_WRITE_DATA (0x20);
LLCD_WRITE_CMD (0xe1);
LLCD_WRITE_DATA (0xd0);
LLCD_WRITE_DATA (0x02);
LLCD_WRITE_DATA (0x07);
LLCD_WRITE_DATA (0x0a);
LLCD_WRITE_DATA (0x0b);
LLCD_WRITE_DATA (0x18);
LLCD_WRITE_DATA (0x34);
LLCD_WRITE_DATA (0x43);
LLCD_WRITE_DATA (0x4a);
LLCD_WRITE_DATA (0x2b);
LLCD_WRITE_DATA (0x1b);
LLCD_WRITE_DATA (0x1c);
LLCD_WRITE_DATA (0x22);
LLCD_WRITE_DATA (0x1f);
LLCD_WRITE_CMD (0x29);
/* 开背光 */
rt_pin_write(NU_GET_PININDEX(NU_PA, 12), PIN_HIGH);
#if 0 /* 逻辑分析仪测试波形使用 */
while(1)
{
LLCD_WRITE_CMD(0x00);
LLCD_WRITE_CMD(0x01);
LLCD_WRITE_CMD(0x02);
LLCD_WRITE_CMD(0x03);
LLCD_WRITE_DATA(0x00);
LLCD_WRITE_DATA(0x01);
LLCD_WRITE_DATA(0x02);
LLCD_WRITE_DATA(0x03);
LLCD_READ_DATA();
rt_thread_mdelay(1);
}
#endif
}
void lcd_setpoint(uint16_t x ,uint16_t y, uint16_t color)
{
LLCD_WRITE_CMD(0x2A);
LLCD_WRITE_DATA(x>>8);
LLCD_WRITE_DATA(x&0XFF);
LLCD_WRITE_CMD(0x2B);
LLCD_WRITE_DATA(y>>8);
LLCD_WRITE_DATA(y&0XFF);
LLCD_WRITE_CMD(0x2C);
LLCD_WRITE_DATA(color);
}
void lcd_clear(uint16_t color)
{
LLCD_WRITE_CMD(0x2A);
LLCD_WRITE_DATA(0);
LLCD_WRITE_DATA(0);
LLCD_WRITE_CMD(0x2B);
LLCD_WRITE_DATA(0);
LLCD_WRITE_DATA(0);
LLCD_WRITE_CMD(0x2C);
for(uint32_t i=0;i<LCD_XSIZE*LCD_YSIZE;i++)
{
LLCD_WRITE_DATA(color);
}
}
硬件/时序调试
焊接完后要先确认是否有短路,再上电。
首先先确认各引脚是不是对,对于RESET和BL引脚是作为IO的直接翻转用示波器或者逻辑分析仪看是否正确,
对于其他EBI引脚按照如下测试代码
#if 0 /* 逻辑分析仪测试波形使用 */
while(1)
{
LLCD_WRITE_CMD(0x00);
LLCD_WRITE_CMD(0x01);
LLCD_WRITE_CMD(0x02);
LLCD_WRITE_CMD(0x03);
LLCD_WRITE_DATA(0x00);
LLCD_WRITE_DATA(0x01);
LLCD_WRITE_DATA(0x02);
LLCD_WRITE_DATA(0x03);
LLCD_READ_DATA();
rt_thread_mdelay(1);
}
#endif
用逻辑分析仪去看是否信号线是否都对,首先确认信号线是否一一对应,不对就检查原理图和PCB和焊接。
然后确认信号是否符合要求,不符合就确认寄存器的配置是否正确。
\nk-980-iot-test\lcd-pcb\测试信号 使用Logic 2.3.47打开.sal 是我这里抓取到波形,可以打开回放,和上面的测试代码对应。
刷屏测试
确认时序都对后编写刷屏程序,按照RGB显示,
代码如下
#if LCD_TEST
lcd_clear(0xF800);
rt_thread_mdelay(1000);
lcd_clear(0x07E00);
rt_thread_mdelay(1000);
lcd_clear(0x0001F);
rt_thread_mdelay(1000);
#endif
总结
EBI接口驱动8080接口的LCD非常方便,本次设计也比较顺利,一次就成功,其中逻辑分析仪非常重要,用来分析时序没有他不行,另外嘉立创的打样活动非常不错,可以去看看。
下次测试下性能,优化下时序,将时序尽可能提高到最快,尽可能提高刷屏速度。
后面再移植GUI,暂时选定lvgl或者emwin。
后面再添加ADC测评,添加驱动支持。
原作者:qinyunti