开发板

K210开发板

基于K210开发板LCD显示图片测试_初始化

实验目的

本实验主要学习 K210 驱动 LCD 显示图片和字符串。

实验准备

实验元件

LCD 显示屏

基于K210开发板LCD显示图片测试_初始化_02

元件特性

LCD 显示为 2.0 寸,分辨率为 320*240,驱动芯片是 st7789,体积小,厚度薄,耗能低,工作电压为 3.3V,显示屏的材料为 TFT。TFT(Thin Film Transistor)是指薄膜晶体管,使每个液晶像素点都是由集成在像素点后面的薄膜晶体管来驱动。从而可以做到高速度、高亮度、高对比度显示屏幕信息。

硬件连接

K210 开发板出厂默认已经安装好 LCD 显示屏,其中 LCD_D0~D7 总共八个引脚连接到 SPI0_D0~D7 上,LCD_CS 连接到 IO36 上,LCD_RST 连接到 IO37 上,LCD_RS连接 IO3 上,LCD_WR 连接 IO39 上。

基于K210开发板LCD显示图片测试_初始化_03

SDK 中对应 API 功能

以头文件 spi.h为例,SPI 模块具有以下功能: 独立的 SPI 设备封装外设相关参数,自动处理多设备总线争用,支持标准、双线、四线、八线模式,支持先写后读和全双工读写,支持发送一串相同的数据帧,常见与清屏、填充储存扇区等场景

• spi_init:设置 SPI 工作模式、多线模式和位宽。
• spi_init_non_standard:多线模式下设置指令长度、地址长度、等待时钟数、指令地址
传输模式。
• spi_send_data_standard:SPI 标准模式传输数据。
• spi_send_data_standard_dma:SPI 标准模式下使用 DMA 传输数据。
• spi_receive_data_standard:标准模式下接收数据。
• spi_receive_data_standard_dma:标准模式下通过 DMA 接收数据。
• spi_send_data_multiple:多线模式发送数据。
• spi_send_data_multiple_dma:多线模式使用 DMA 发送数据。
• spi_receive_data_multiple:多线模式接收数据。
• spi_receive_data_multiple_dma:多线模式通过 DMA 接收。
• spi_fill_data_dma:通过 DMA 始终发送同一个数据,可以用于刷新数据。
• spi_send_data_normal_dma:通过 DMA 发送数据。不用设置指令地址。
• spi_set_clk_rate:设置 SPI 的时钟频率。
• spi_handle_data_dma:SPI 通过 DMA 传输数据。
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

实验原理

LCD 显示的基本原理是将液晶至于两片导电玻璃基板之间,在上下玻璃基板的两个电极作用下,引起液晶分子扭曲变形,改变通过液晶盒光束的偏振状态,实现对背光源光束的开关控制。 **如果液晶盒上不施加外电场,由于 TN 型液晶显示器件中液晶分子在盒中的扭曲螺距远比可见光波长大得多,所以当入射直线偏振光的偏振方向与玻璃表面液晶分子的排列方向一致时,其偏光方向在通过整个液晶层后会随着液晶分子扭曲变形而被扭曲 90。由另一侧射出,呈透光状态。如果这时在液晶盒上施加一个电压并达到一定值后,液晶分子长轴将开始沿电场方向倾斜,除电极表面的液晶分子外,所有液晶盒内两电极之间的液晶分子都变成沿电场方向的再排列。液晶显示屏技术是根据电压的大小来改变亮度,每个液晶显示屏的子图元显示的颜色取决于色彩筛检程序。SPI 是一种高速、高效率的串行接口技术。通常由一个主模块和一个或多个从模块组成,主模块选择一个从模块进行同步通信,从而完成数据的交换。SPI是一个环形结构,通信时需要至少 4 根线(事实上在单向传输时 3 根线也可以),它们是 MISO(主设备数据输入)、MOSI(主设备数据输出)、SCLK(时钟)、CS(片选)。

  1. MISO– Master Input Slave Output,主设备数据输入,从设备数据输出;
  2. MOSI– Master Output Slave Input,主设备数据输出,从设备数据输入;
  3. SCLK – Serial Clock,时钟信号,由主设备产生;
  4. CS – Chip Select,从设备使能信号,由主设备控制。当有多个从设备的时候,因为每个从设备上都有一个片选引脚接入到主设备机中,当我们的主设备和某个从设备通信时将需要将从设备对应的片选引脚电平拉低或者是拉高。

基于K210开发板LCD显示图片测试_初始化_04

实验过程

  1. 首先初始化 K210 的硬件引脚和软件功能使用的是 FPIOA 映射关系。
#define PIN_LCD_CS              (36)
#define PIN_LCD_RST             (37)
#define PIN_LCD_RS              (38)
#define PIN_LCD_WR              (39)

/*****************************SOFTWARE-GPIO********************************/
// 软件GPIO口,与程序对应
#define LCD_RST_GPIONUM         (0)
#define LCD_RS_GPIONUM          (1)


/*****************************FUNC-GPIO************************************/
// GPIO口的功能,绑定到硬件IO口
#define FUNC_LCD_CS             (FUNC_SPI0_SS3)
#define FUNC_LCD_RST            (FUNC_GPIOHS0 + LCD_RST_GPIONUM)
#define FUNC_LCD_RS             (FUNC_GPIOHS0 + LCD_RS_GPIONUM)
#define FUNC_LCD_WR             (FUNC_SPI0_SCLK)
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
void hardware_init(void)
{
    /**
    *PIN_LCD_CS	    36
    *PIN_LCD_RST	37
    *PIN_LCD_RS	    38
    *PIN_LCD_WR 	39
    **/
    fpioa_set_function(PIN_LCD_CS,  FUNC_LCD_CS);
    fpioa_set_function(PIN_LCD_RST, FUNC_LCD_RST);
    fpioa_set_function(PIN_LCD_RS,  FUNC_LCD_RS);
    fpioa_set_function(PIN_LCD_WR,  FUNC_LCD_WR);
    
    /* 使能SPI0和DVP数据 */
    sysctl_set_spi0_dvp_data(1);

}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  1. 设置 LCD 的 IO 口电平电压为 1.8V。
void io_set_power(void)
{
    sysctl_set_power_mode(SYSCTL_POWER_BANK6, SYSCTL_POWER_V18);
}
  • 1.
  • 2.
  • 3.
  • 4.
  1. 初始化 LCD,主要的功能就是激活 LCD 显示屏,设置显示方向和显示颜色,并使能显示和清空显示屏。关于详细的 ST7789 的初始化寄存器对应和功能可以查看硬件资料中的 LCD 资料,查看 ST7789 开发手册。
void lcd_init(void)
{
    uint8_t data = 0;
    /* 硬件初始化 */
    tft_hard_init();
    /* 重置LCD */
    tft_write_command(SOFTWARE_RESET);
    usleep(100000);
    /* 关闭睡眠模式 */
    tft_write_command(SLEEP_OFF);
    usleep(100000);
    /* 设置像素格式:65K, 16bit/pixel */
    tft_write_command(PIXEL_FORMAT_SET);
    data = 0x55;  /* 0101 0101*/
    tft_write_byte(&data, 1);
    /* 打开显示反转 */
    tft_write_command(INVERSION_DISPLAY_ON);
    /* 设置LCD显示方向 */
    lcd_set_direction(DIR_YX_LRUD);

    /* 使能显示 */
    tft_write_command(DISPLAY_ON);
    /* 清空显示 */
    lcd_clear(WHITE);
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  1. 显示图片,其中 x1 和 y1 是起点位置,width 为图片的宽度(最大 320),height 为图片的高度(最大 240),ptr 指针指向要显示的图片。
void lcd_draw_picture(uint16_t x1, uint16_t y1, uint16_t width, uint16_t height, uint32_t *ptr)
{
    lcd_set_area(x1, y1, x1 + width - 1, y1 + height - 1);
    tft_write_word(ptr, width * height / 2, 0);
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  1. 显示字符串,其中 x 和 y 为起始坐标,str 指针指向要显示的字符串,color为字体的颜色。
void lcd_draw_string(uint16_t x, uint16_t y, char *str, uint16_t color)
{
    while (*str)
    {
        lcd_draw_char(x, y, *str, color);
        str++;
        x += 8;
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
void lcd_draw_char(uint16_t x, uint16_t y, char c, uint16_t color)
{
    uint8_t i = 0;
    uint8_t j = 0;
    uint8_t data = 0;

    for (i = 0; i < 16; i++)
    {
        data = ascii0816[c * 16 + i];
        for (j = 0; j < 8; j++)
        {
            if (data & 0x80)
                lcd_draw_point(x + j, y, color);
            data <<= 1;
        }
        y++;
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
void lcd_draw_point(uint16_t x, uint16_t y, uint16_t color)
{
    lcd_set_area(x, y, x, y);
    tft_write_half(&color, 1);
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  1. ST7789 底层 SPI 驱动程序: 开始传输命令、数据
/* 开始传输命令 */
static void set_start_cmd(void)
{
    gpiohs_set_pin(LCD_RS_GPIONUM, GPIO_PV_LOW);
}

/* 开始传输数据 */
static void set_start_data(void)
{
    gpiohs_set_pin(LCD_RS_GPIONUM, GPIO_PV_HIGH);
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

SPI 写命令、数据

/* SPI写命令 */
void tft_write_command(uint8_t cmd)
{
    set_start_cmd();
    spi_init(SPI_CHANNEL, SPI_WORK_MODE_0, SPI_FF_OCTAL, 8, 0);
    spi_init_non_standard(SPI_CHANNEL, 8 /*instrction length*/, 0 /*address length*/, 0 /*wait cycles*/,
                          SPI_AITM_AS_FRAME_FORMAT /*spi address trans mode*/);
    spi_send_data_normal_dma(DMAC_CHANNEL0, SPI_CHANNEL, SPI_SLAVE_SELECT, (uint8_t *)(&cmd), 1, SPI_TRANS_CHAR);
}

/* SPI写数据(uint8_t类型) */
void tft_write_byte(uint8_t *data_buf, uint32_t length)
{
    set_start_data();
    spi_init(SPI_CHANNEL, SPI_WORK_MODE_0, SPI_FF_OCTAL, 8, 0);
    spi_init_non_standard(SPI_CHANNEL, 8 /*instrction length*/, 0 /*address length*/, 0 /*wait cycles*/,
                          SPI_AITM_AS_FRAME_FORMAT /*spi address trans mode*/);
    spi_send_data_normal_dma(DMAC_CHANNEL0, SPI_CHANNEL, SPI_SLAVE_SELECT, data_buf, length, SPI_TRANS_CHAR);
}

/* SPI写数据(uint16_t类型) */
void tft_write_half(uint16_t *data_buf, uint32_t length)
{
    set_start_data();
    spi_init(SPI_CHANNEL, SPI_WORK_MODE_0, SPI_FF_OCTAL, 16, 0);
    spi_init_non_standard(SPI_CHANNEL, 16 /*instrction length*/, 0 /*address length*/, 0 /*wait cycles*/,
                          SPI_AITM_AS_FRAME_FORMAT /*spi address trans mode*/);
    spi_send_data_normal_dma(DMAC_CHANNEL0, SPI_CHANNEL, SPI_SLAVE_SELECT, data_buf, length, SPI_TRANS_SHORT);
}

/* SPI写数据(uint32_t类型) */
void tft_write_word(uint32_t *data_buf, uint32_t length, uint32_t flag)
{
    set_start_data();
    spi_init(SPI_CHANNEL, SPI_WORK_MODE_0, SPI_FF_OCTAL, 32, 0);

    spi_init_non_standard(SPI_CHANNEL, 0 /*instrction length*/, 32 /*address length*/, 0 /*wait cycles*/,
                          SPI_AITM_AS_FRAME_FORMAT /*spi address trans mode*/);
    spi_send_data_normal_dma(DMAC_CHANNEL0, SPI_CHANNEL, SPI_SLAVE_SELECT, data_buf, length, SPI_TRANS_INT);
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  1. main 函数里最后是一个 while(1)循环。
int main(void)
{
    /* 硬件引脚初始化 */
    hardware_init();
    
    /* 设置IO口电压 */
    io_set_power();
    
    /* 初始化LCD */
    lcd_init();

    /* 显示图片 */
    lcd_draw_picture_half(0, 0, 320, 240, gImage_logo);
    sleep(1);

    /* 显示字符 */
    lcd_draw_string(16, 40, "Hello Yahboom!", RED);
    lcd_draw_string(16, 60, "Nice to meet you!", BLUE);
    
    while (1);
    return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  1. 关于图片是如何转化成数据格式的方法:安装并打开图片转化工具Image2Lcd 然后再通过 extern 关键字在要使用的地方引用
const unsigned char gImage_logo[153608]
  • 1.
  1. 编译调试,烧录运行 进入自己项目 build目录,运行以下命令编译。
cmake .. -DPROJ=watchdog -G "MinGW Makefiles"
make
  • 1.
  • 2.

实验现象

LCD 屏幕会显示图片,一秒后打印出“Hello Yahboom!”“Nice to meet you!”的欢迎语。

基于K210开发板LCD显示图片测试_初始化_05

实验总结

  • LCD 显示屏的分辨率是 320240 的,显示图片前需要把图片转化成 320240 分辨率,然后通过图片转化工具把图片转成.c 文件,再引用图片的变量即可。
  • LCD 是基于 SPI 通讯的,传输速度快并且稳定。
  • 显示屏在显示前需要配置显示的方向以及显示的格式等参数。