SDI

概述

本示例演示了通过 LCDC 外设的 SDI 接口驱动 ICNA3311 LCD 面板的示例。

环境需求

该示例的环境需求,可参考 环境需求

硬件连线

  1. 将 P3_6 连接到Reset引脚。

  2. 将 P4_2 连接到 D0 引脚。

  3. 将 P4_1 连接到 D1 引脚。

  4. 将 P3_2 连接到 D2 引脚。

  5. 将 P3_3 连接到 D3 引脚。

  6. 将 P4_3 连接到 CS 引脚。

  7. 将 P4_0 连接到 CLK 引脚。

  8. 将 P0_5 连接到 TE 引脚。

配置选项

可以通过修改函数 rtk_lcd_hal_init() 中的参数 ABGR_color 来改变 LCD 屏幕上显示的颜色。

uint32_t ABGR_color = 0x0000FF00;
rtk_lcd_hal_rect_fill(0, 0, 280, 456, ABGR_color);

当前默认值代表纯绿色。

编译和下载

该示例的编译和下载流程,可参考 编译和下载

测试验证

屏幕刷新成功

屏幕将以 配置选项 中确定的特定颜色刷新。

代码介绍

该章节主要介绍示例中的初始化和相应功能实现的代码和流程说明。

源码路径

工程文件和源码路径如下:

  • 工程路径: sdk\samples\peripheral\lcdc\lcdc_qspi\proj

  • 源码路径: sdk\samples\peripheral\lcdc\lcdc_qspi\src

初始化

初始化流程包含在 rtk_lcd_hal_init() 中。

  1. 启用 LCDC 时钟并选择合适的时钟源。

/* config LCD dev info */
RCC_PeriphClockCmd(APBPeriph_DISP, APBPeriph_DISP_CLOCK, ENABLE);

//from XTAL SOURCE = 40M
PERIBLKCTRL_PERI_CLK->u_324.BITS_324.disp_ck_en = 1;
PERIBLKCTRL_PERI_CLK->u_324.BITS_324.disp_func_en = 1;
PERIBLKCTRL_PERI_CLK->u_324.BITS_324.r_disp_mux_clk_cg_en = 1;

//From PLL1, SOURCE = 100M
PERIBLKCTRL_PERI_CLK->u_324.BITS_324.r_disp_div_en = 1;
PERIBLKCTRL_PERI_CLK->u_324.BITS_324.r_disp_clk_src_sel0 = 0; //pll1_peri(0) or pll2(1, pll2 = 160M)
PERIBLKCTRL_PERI_CLK->u_324.BITS_324.r_disp_clk_src_sel1 = 1; //pll(1) or xtal(0)
PERIBLKCTRL_PERI_CLK->u_324.BITS_324.r_disp_div_sel = 1; //div
  1. 初始化所有将要使用的引脚。

Pad_Config(LCD_QSPI_RST, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_HIGH);
Pad_Config(LCD_QSPI_D0, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_HIGH);
Pad_Config(LCD_QSPI_D1, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_HIGH);
Pad_Config(LCD_QSPI_D2, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_HIGH);
Pad_Config(LCD_QSPI_D3, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_HIGH);
Pad_Config(LCD_QSPI_CS, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_HIGH);
Pad_Config(LCD_QSPI_CLK, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_HIGH);
Pad_Config(LCD_QSPI_TE, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_HIGH);

Pad_Dedicated_Config(LCD_QSPI_RST, ENABLE);
Pad_Dedicated_Config(LCD_QSPI_D0, ENABLE);
Pad_Dedicated_Config(LCD_QSPI_D1, ENABLE);
Pad_Dedicated_Config(LCD_QSPI_D2, ENABLE);
Pad_Dedicated_Config(LCD_QSPI_D3, ENABLE);
Pad_Dedicated_Config(LCD_QSPI_CS, ENABLE);
Pad_Dedicated_Config(LCD_QSPI_CLK, ENABLE);
Pad_Dedicated_Config(LCD_QSPI_TE, ENABLE);
  1. 确定输入和输出颜色格式,并选择 SDI 接口。启用 TE 信号。

LCDC_InitTypeDef lcdc_init = {0};
lcdc_init.LCDC_Interface = LCDC_IF_DBIC;
lcdc_init.LCDC_GroupSel = 1;
lcdc_init.LCDC_PixelInputFormat = LCDC_INPUT_RGB565;
lcdc_init.LCDC_PixelOutputFormat = LCDC_OUTPUT_RGB565;
lcdc_init.LCDC_PixelBitSwap = LCDC_SWAP_BYPASS;
lcdc_init.LCDC_TeEn = ENABLE;
lcdc_init.LCDC_TePolarity = LCDC_TE_EDGE_FALLING;
lcdc_init.LCDC_TeInputMux = LCDC_TE_LCD_INPUT;
lcdc_init.LCDC_DmaThreshold = 112;
LCDC_Init(&lcdc_init);
  1. 初始化 SDI 接口,选择时钟分频器、极性和相位。

LCDC_DBICCfgTypeDef dbic_init = {0};
dbic_init.DBIC_SPEED_SEL         = 1;
dbic_init.SCPOL                  = DBIC_SCPOL_LOW;
dbic_init.SCPH                   = DBIC_SCPH_1Edge;
DBIC_Init(&dbic_init);
  1. 控制复位引脚完成 LCD 面板复位过程。

LCDC_LCD_SET_RST(false);
platform_delay_ms(100);
LCDC_LCD_SET_RST(true);
platform_delay_ms(120);
LCDC_LCD_SET_RST(false);
platform_delay_ms(50);
  1. 使用基本 SPI 协议发送命令和数据,这些命令和数据应参考 LCD 生产商的指导手册。

incna3311_cmd_param1(0xFE, 0x00);
incna3311_cmd_param1(0xC4, 0x80);           /*set quad SPI mode*/
incna3311_cmd_param1(0x35, 0x00);           /*set tear on*/
incna3311_cmd_param1(0x51, 0xff);           /*write display brightness*/
incna3311_cmd_param1(0x53, 0x20);           /*write display ctrl ,dimming*/
incna3311_cmd_param1(0x63, 0xFF);           /*write HBM display brightness*/

功能实现

设置更新窗口

函数 rtk_lcd_hal_set_window 用于设置要更新的屏幕窗口。本示例演示了一个从点 (0,0) 开始,宽度为 280,高度为 456 的窗口:

  1. 通过向 LCD 面板发送命令设置窗口。使用窗口范围与命令一起构建数据包。

//水平范围
data[0] = xStart >> 8;
data[1] = xStart & 0xff;
data[2] = xEnd >> 8;
data[3] = xEnd & 0xff;
incna3311_cmd_param4(0x2A, data);
//垂直范围
data[0] = yStart >> 8;
data[1] = yStart & 0xff;
data[2] = yEnd >> 8;
data[3] = yEnd & 0xff;
incna3311_cmd_param4(0x2B, data);
  1. 通过 API LCDC_AXIMUXMode() 将 LCDC 的 AXI 模式切换到 LCDC_FW_MODE ,从而使能对 SDI 进行写入修改。

LCDC_AXIMUXMode(LCDC_FW_MODE);
  1. 设置帧数据传输的数据大小、地址和命令,然后将 LCDC 切换回 LCDC_HW_MODE

DBIC_CmdLength(1);
DBIC_AddrLength(3);
DBIC_TX_NDF(len_byte);
/* must push cmd and address to handler before SDI enable */
LCDC_SPICCmd(0x32);
LCDC_SPICAddr(0x002c00);
LCDC_AXIMUXMode(LCDC_HW_MODE);

DMA 配置

在初始化 SDI 接口后,还应初始化 DMA 以启用数据传输。该过程在 rtk_lcd_hal_clear_screen 中演示。

  1. 初始化 DMA 参数,包括数据大小、突发大小和源增量,通过初始化的 SDI 接口向 LCD 面板传输数据。在本示例中,使用单块模式。

LCDC_DMA_InitTypeDef LCDC_DMA_InitStruct = {0};
LCDC_DMA_StructInit(&LCDC_DMA_InitStruct);
LCDC_DMA_InitStruct.LCDC_DMA_ChannelNum          = LCDC_DMA_CHANNEL_NUM;
LCDC_DMA_InitStruct.LCDC_DMA_DIR                 = LCDC_DMA_DIR_PeripheralToMemory;
LCDC_DMA_InitStruct.LCDC_DMA_SourceInc           = LCDC_DMA_SourceInc_Inc;
LCDC_DMA_InitStruct.LCDC_DMA_DestinationInc      = LCDC_DMA_DestinationInc_Fix;
LCDC_DMA_InitStruct.LCDC_DMA_SourceDataSize      = LCDC_DMA_DataSize_Word;
LCDC_DMA_InitStruct.LCDC_DMA_DestinationDataSize = LCDC_DMA_DataSize_Word;
LCDC_DMA_InitStruct.LCDC_DMA_SourceMsize         = LCDC_DMA_Msize_8;
LCDC_DMA_InitStruct.LCDC_DMA_DestinationMsize    = LCDC_DMA_Msize_8;
LCDC_DMA_Init(LCDC_DMA_CHANNEL_INDEX, &LCDC_DMA_InitStruct);

数据传输

在初始化内部 DMA 后,数据传输准备就绪。该过程在 rtk_lcd_hal_clear_screen 中演示。

  1. 使用 API LCDC_SetTxPixelLen() 确定每帧内应发送多少像素。

LCDC_SetTxPixelLen(280 * 456);
  1. 使用 LCDC_DmaCmd() 让 DMA 开始向内部 FIFO 搬运数据。

LCDC_DmaCmd(ENABLE);
  1. 通过调用 API LCDC_AutoWriteCmd() 让 LCDC 开始从内部 FIFO 向选定的接口传输数据。

LCDC_AutoWriteCmd(ENABLE);
  1. 轮询 LCDC 的状态。在 DMA 和 SDI 返回空闲状态后,停止 LCDC 的使能状态。

while ((LCDC_HANDLER->DMA_FIFO_CTRL & LCDC_DMA_ENABLE) != RESET);//wait dma idle
LCDC_HANDLER_OPERATE_CTR_t handler_reg_0x14;
do
{
    handler_reg_0x14.d32 = LCDC_HANDLER->OPERATE_CTR;
}
while (handler_reg_0x14.b.auto_write_start != RESET);
LCDC_Cmd(DISABLE);
LCDC_ClearDmaFifo();
LCDC_ClearTxPixelCnt();
LCDC_AXIMUXMode(LCDC_FW_MODE);