打印
[蓝牙芯片]

沁恒CH585开发板的2种TFT显示屏驱动方式及比较

[复制链接]
371|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 jinglixixi 于 2025-7-7 08:39 编辑

#申请原创#

@21小跑堂

CH585是沁恒出品的一款集成了 BLE 无线通讯和高速 USB 及 NFC 的 RISC-V MCU 微控制器。并在片上集成了 2Mbps 低功耗蓝牙 BLE 通讯模块、USB 全速控制器及收发器、USB 高速控制器及收发器(480Mbps)、NFC 近场通信无线接口、段式 LCD 驱动模块、LED 点阵屏接口、2 个 SPI、4 个串口、14 路 ADC、触摸按键检测模块等丰富的外设资源。
其内核框架如图1所示,与以前产品不同的是减少了RTC的介绍。

图1 内核框架

开发板的外观如图2和图3所示,可见它采用的是单面器件布局。

图2 开发板正面


图3 开发板背面

为了便于显示,就为其配置了一个SPI接口的2.2’ TFT显示屏,其分辨率为240*320像素点。
在显示驱动方面,有2种驱动可供选择,一种是采用模拟的方式,另一种则是以硬件的方式。
对于模拟的方式,其好处就是易于实现,不受硬件在引脚方面的制约;而对于硬件方式,其好处就是处理速度快,可以充分发挥SPI接口的作用。此外,相对其他的开发板,似乎在CH585上也更易于实现,通过后面的对比会加深大家对这种想法的认识。

1. 模拟方式
要以模拟的方式来实现TFT显示屏的驱动,先要定义引脚的连接关系并对所用引脚加以配置。
这里为显示屏所建立的连接关系为:
CS  -----PA0
RST  ----PA1
DC   ----PA2
MOSI-----PA3
SCK  -----PA5
其引脚配置的函数为:
void TFT_CONFIG()
{
GPIOA_ModeCfg( GPIO_Pin_4, GPIO_ModeOut_PP_5mA );
GPIOA_ModeCfg( GPIO_Pin_0, GPIO_ModeOut_PP_5mA );
GPIOA_ModeCfg( GPIO_Pin_1, GPIO_ModeOut_PP_5mA );
GPIOA_ModeCfg( GPIO_Pin_2, GPIO_ModeOut_PP_5mA );
GPIOA_ModeCfg( GPIO_Pin_3, GPIO_ModeOut_PP_5mA );
GPIOA_ModeCfg( GPIO_Pin_5 , GPIO_ModeOut_PP_5mA );
}

实现引脚输出高低电平的语句定义为:
#define LCD_CS_High()   GPIOA_SetBits(GPIO_Pin_0)
#define LCD_CS_Low()    GPIOA_ResetBits(GPIO_Pin_0)
#define LCD_REST_High() GPIOA_SetBits(GPIO_Pin_1)
#define LCD_REST_Low()  GPIOA_ResetBits(GPIO_Pin_1)
#define LCD_DC_High()   GPIOA_SetBits(GPIO_Pin_2)
#define LCD_DC_Low()    GPIOA_ResetBits(GPIO_Pin_2)
#define LCD_SDI_High()  GPIOA_SetBits(GPIO_Pin_3)
#define LCD_SDI_Low()   GPIOA_ResetBits(GPIO_Pin_3)
#define LCD_SCK_High()  GPIOA_SetBits(GPIO_Pin_5)
#define LCD_SCK_Low()   GPIOA_ResetBits(GPIO_Pin_5)

在模拟方式下,最为关键的是模拟SPI方式进行字节数据的方式,其函数内容为:
void LCD_Writ_Bus(unsigned char com)
{
unsigned char uci;
for(uci=0;uci<8;uci++)
{
if(com & 0x80)
{
LCD_SDI_High();
}
else
{
LCD_SDI_Low();
}
com = com << 1;
DelayUs(1);
LCD_SCK_Low();
LCD_SCK_High();
}
}

其他一些支持显示驱动的辅助函数有:
单字节数据发送函数LCD_WR_DATA8(char da)
双字节数据发送函数LCD_WR_DATA(int da)
寄存器发送函数LCD_WR_REG(char da)
向寄存器发送双字节数据函数LCD_WR_REG_DATA(int reg,int da)

对显示屏的初始化函数为:
void TFT_Init(void)
{   
    LCD_REST_Low();
    DelayMs(20);
    LCD_REST_High();
    DelayMs(20);
    LCD_CS_Low();  

    LCD_WR_REG(0xCB);   
    LCD_WR_DATA8(0x39);
    LCD_WR_DATA8(0x2C);
    LCD_WR_DATA8(0x00);
    LCD_WR_DATA8(0x34);
    LCD_WR_DATA8(0x02);

    LCD_WR_REG(0xCF);  
    LCD_WR_DATA8(0x00);
    LCD_WR_DATA8(0XC1);
    LCD_WR_DATA8(0X30);

    LCD_WR_REG(0xE8);  
    LCD_WR_DATA8(0x85);
    LCD_WR_DATA8(0x00);
    LCD_WR_DATA8(0x78);

    LCD_WR_REG(0xEA);  
    LCD_WR_DATA8(0x00);
    LCD_WR_DATA8(0x00);

    LCD_WR_REG(0xED);  
    LCD_WR_DATA8(0x64);
    LCD_WR_DATA8(0x03);
    LCD_WR_DATA8(0X12);
    LCD_WR_DATA8(0X81);

    LCD_WR_REG(0xF7);  
    LCD_WR_DATA8(0x20);
  
    LCD_WR_REG(0xC0);  
    LCD_WR_DATA8(0x23);

    LCD_WR_REG(0xC1);
    LCD_WR_DATA8(0x10);

    LCD_WR_REG(0xC5);
    LCD_WR_DATA8(0x3e);
    LCD_WR_DATA8(0x28);

    LCD_WR_REG(0xC7);
    LCD_WR_DATA8(0x86);

    LCD_WR_REG(0x36);
    LCD_WR_DATA8(0xE8);
  
    LCD_WR_REG(0x3A);   
    LCD_WR_DATA8(0x55);

    LCD_WR_REG(0xB1);   
    LCD_WR_DATA8(0x00);  
    LCD_WR_DATA8(0x18);

    LCD_WR_REG(0xB6);
    LCD_WR_DATA8(0x08);
    LCD_WR_DATA8(0x82);
    LCD_WR_DATA8(0x27);  

    LCD_WR_REG(0xF2);
    LCD_WR_DATA8(0x00);

    LCD_WR_REG(0x26);
    LCD_WR_DATA8(0x01);

    LCD_WR_REG(0xE0);
    LCD_WR_DATA8(0x0F);
    LCD_WR_DATA8(0x31);
    LCD_WR_DATA8(0x2B);
    LCD_WR_DATA8(0x0C);
    LCD_WR_DATA8(0x0E);
    LCD_WR_DATA8(0x08);
    LCD_WR_DATA8(0x4E);
    LCD_WR_DATA8(0xF1);
    LCD_WR_DATA8(0x37);
    LCD_WR_DATA8(0x07);
    LCD_WR_DATA8(0x10);
    LCD_WR_DATA8(0x03);
    LCD_WR_DATA8(0x0E);
    LCD_WR_DATA8(0x09);
    LCD_WR_DATA8(0x00);

    LCD_WR_REG(0XE1);
    LCD_WR_DATA8(0x00);
    LCD_WR_DATA8(0x0E);
    LCD_WR_DATA8(0x14);
    LCD_WR_DATA8(0x03);
    LCD_WR_DATA8(0x11);
    LCD_WR_DATA8(0x07);
    LCD_WR_DATA8(0x31);
    LCD_WR_DATA8(0xC1);
    LCD_WR_DATA8(0x48);
    LCD_WR_DATA8(0x08);
    LCD_WR_DATA8(0x0F);
    LCD_WR_DATA8(0x0C);
    LCD_WR_DATA8(0x31);
    LCD_WR_DATA8(0x36);
    LCD_WR_DATA8(0x0F);

    LCD_WR_REG(0x11);
    DelayMs(120);
               
    LCD_WR_REG(0x29);
    LCD_WR_REG(0x2c);
}

实现色彩清屏处理的函数为:
void LCD_Clear(unsigned int Color)
{
    char VH,VL;
    unsigned int i,j;
    VH=Color>>8;
    VL=Color;
    Address_set(0,0,LCD_H-1,LCD_W-1);
    for(i=0;i<LCD_H;i++)
    {
        for (j=0;j<LCD_W;j++)
        {
            LCD_WR_DATA8(VH);
            LCD_WR_DATA8(VL);
        }
    }
}

有了以上函数的支持,进行驱动测试的主程序为:
int main()
{
    uint8_t i;
    HSECFG_Capacitance(HSECap_18p);
    SetSysClock(CLK_SOURCE_HSE_PLL_62_4MHz);
    TFT_CONFIG();
    DelayMs(1);
    TFT_Init();
    LCD_Clear(RED);
    while(1);
}

经编译和选择,其测试效果如图4所示,说明驱动有效。

图4 驱动测试

2. 硬件方式
有了前面模拟驱动的基础,下面再将其改为硬件方式的驱动。
SPI 是一种全双工串行接口,总线上连接有一个主机和若干从机,同一时刻,仅有一对主从在通讯。通常 SPI 接口由 4 个引脚组成:SPI 片选引脚 SCS、SPI 时钟引脚 SCK、SPI 串行数据引脚 MISO(主机输入/从机输出引脚)和 SPI 串行数据引脚 MOSI(主机输出/从机输入引脚)。
CH585 芯片提供 2 个 SPI 接口(SPI0 和 SPI1)其特性如下:
SPI0 支持主机模式(Master)和从机模式(Slave),SPI1 只支持主机模式(Master)
兼容串行外设接口(SPI)规范
支持模式 0 和模式 3 数据传输方式
8 位数据传输方式,数据位序可选:字节低位在前或者高位在前
时钟频率最高可达系统主频 Fsys 的一半
8 字节 FIFO
SPI0 从机模式支持首字节为命令模式或数据流模式
SPI0 支持 DMA,数据传输效率更高
SPI 支持模式 0 和模式 3 两种传输格式,通过设置 R8_SPIx_CTRL_MOD 的 RB_SPI_MST_SCK_MOD 进
行选择。总是在 SCK 上升沿采样串行数据输入,在下降沿输出串行数据。

SPI 的工作时序如图5所示:
5 工作时序


替代模拟方式所选取的是SPI0,其所用的引脚如图6所示。

图6 所用接口

因此TFT显示屏新的引脚连接关系为:
CS  -----PA12
SCK -----PA13
MOSI-----PA14
RST  ----PA1
DC   ----PA2

也就是说在硬件驱动方式下,只需所用2个辅助引脚进行配合,其他则是通过SPI接口来完成。
辅助引脚的配置函数为:
void TFT_CONFIG()
{
    GPIOA_ModeCfg( GPIO_Pin_4, GPIO_ModeOut_PP_5mA );
    GPIOA_ModeCfg( GPIO_Pin_1, GPIO_ModeOut_PP_5mA );
    GPIOA_ModeCfg( GPIO_Pin_2, GPIO_ModeOut_PP_5mA );
}

配置SPI0的语句为:
GPIOA_ModeCfg(GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14, GPIO_ModeOut_PP_5mA);
SPI0_MasterDefInit();

在此基础上,新的SPI模式下方式字节数据的函数变为:
void LCD_Writ_Bus(unsigned char com)
{
    GPIOA_ResetBits(GPIO_Pin_12);
    SPI0_MasterSendByte(com);
}

这样就完成了模拟方式向硬件方式的转换,看上去是不是非常简单!
这里引用到2个函数,其内容为:
void SPI0_MasterDefInit(void)
{
R8_SPI0_CLOCK_DIV = 4; // 主频时钟4分频
R8_SPI0_CTRL_MOD = RB_SPI_ALL_CLEAR;
R8_SPI0_CTRL_MOD = RB_SPI_MOSI_OE | RB_SPI_SCK_OE;
R8_SPI0_CTRL_CFG |= RB_SPI_AUTO_IF;
R8_SPI0_CTRL_CFG &= ~RB_SPI_DMA_ENABLE; // 不启动DMA方式
}

void SPI0_MasterSendByte(uint8_t d)
{
    R8_SPI0_CTRL_MOD &= ~RB_SPI_FIFO_DIR;
    R16_SPI0_TOTAL_CNT = 1;         // 设置要发送的数据长度
    R8_SPI0_FIFO = d;
    while(!(R8_SPI0_INT_FLAG & RB_SPI_FREE));
}

在确保硬件驱动有效的情况下,通过添加字符及字符串显示函数,实现显示测试的主程序为:
int main()
{
    uint8_t i;
    HSECFG_Capacitance(HSECap_18p);
    SetSysClock(CLK_SOURCE_HSE_PLL_62_4MHz);
    TFT_CONFIG();
    GPIOA_SetBits(GPIO_Pin_12);
    GPIOA_ModeCfg(GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14, GPIO_ModeOut_PP_5mA);
    SPI0_MasterDefInit();
    DelayMs(1);
    TFT_Init();
    LCD_Clear(RED);
    BACK_COLOR=RED;
    POINT_COLOR=WHITE;
    LCD_ShowChar(10,10,'V',0);
    LCD_ShowString(10,30,"CH585");
    while(1);
}

经编译和下载,其测试效果如图7所示,说明硬件驱动是成功的,且明显地加快了显示的速度。

图7 测试效果

通过对模拟和硬件2种驱动方式的对比,可以发现模拟方式向硬件方式的转换是十分方便的,此外也发现CH585在SPI的使用方面也有了新的变化,且更加易于使用啦!










使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

496

主题

2890

帖子

39

粉丝