NK-980IOT测评之使用EBI驱动LCD

特征

  • 三个独立芯片选择引脚,可设置极性,支持三个设备,时序独立设置
  • 数据总线和地址总线分开。
  • 每个设备支持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测评,添加驱动支持。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值