特征
- 三个独立芯片选择引脚,可设置极性,支持三个设备,时序独立设置
- 数据总线和地址总线分开。
- 每个设备支持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
生产焊接
华秋DFM正好在搞活动,可以减免30打样双面板,所以实际只花了8块钱的运费打样。
三四天就生产完到货了,非常快。
在生产时刚好淘宝下单将BOM表的物料采购下,我手里大部分都有就买了个FFC的座子,和2.54的排母。
打样加上物料30块多点就搞定了。
焊接手工,十来分钟就搞定了。
回来的PCB
焊接之后,由于手头没有洗板水所以焊接后残留很多松香渣滓,可以买点高浓度酒精97%以上清洗下。
插入到开发板
原理分析
用的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 /* 0~7 75m 26ns */
#define MCLK_DIVVAL 32 /* 1~128分频 */
#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
PC0~PC15 EBI_DATA0~EBI_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-testlcd-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
效果如下:
b7ccb72a8698f533e3306ed234cd55c9.mp4
源码
NK-980IOT-TEST: NK-980IOT-TEST
LCD相关资料在lcd-pcb目录下。
总结
EBI接口驱动8080接口的LCD非常方便,本次设计也比较顺利,一次就成功,其中逻辑分析仪非常重要,用来分析时序没有他不行,另外嘉立创的打样活动非常不错,可以去看看。
下次测试下性能,优化下时序,将时序尽可能提高到最快,尽可能提高刷屏速度。
后面再移植GUI,暂时选定lvgl或者emwin。
后面再添加ADC测评,添加驱动支持。