DPI

概述

本示例演示了通过 LCDC 外设的 DPI 接口驱动 EK9716 LCD 面板的应用。

环境需求

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

硬件连线

  1. 将 P0_2 连接到 WRCLK 引脚。

  2. 将 P1_5 连接到 HSYNC 引脚。

  3. 将 P0_1 连接到 VSYNC 引脚。

  4. 将 P0_0 连接到 DE 引脚。

  5. 对于其他数据引脚,请参考 EK9716_800480_rgb.h

配置选项

可以通过修改以下参数 pixel[i] 来改变 LCD 屏幕上显示的颜色。

for (uint32_t i = 0; i < EK9716_800480_LCD_WIDTH * EK9716_800480_LCD_HEIGHT; i++)
{
    pixel[i] = 0x001F;
}

当前默认值在 RGB565 格式中为红色。

编译和下载

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

测试验证

屏幕刷新成功

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

代码介绍

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

源码路径

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

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

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

初始化

初始化流程包含在 lcd_pad_and_clk_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 = 0; //div
  1. 初始化所有将要使用的引脚。

Pad_Config(LCDC_RGB_WRCLK, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE,
           PAD_OUT_HIGH);
Pad_Config(LCDC_HSYNC, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_HIGH);
Pad_Config(LCDC_VSYNC, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_HIGH);
Pad_Config(LCDC_CSN_DE, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_HIGH);

Pad_Dedicated_Config(LCDC_RGB_WRCLK, ENABLE);
Pad_Dedicated_Config(LCDC_HSYNC, ENABLE);
Pad_Dedicated_Config(LCDC_VSYNC, ENABLE);
Pad_Dedicated_Config(LCDC_CSN_DE, ENABLE);
  1. 确定输入和输出颜色格式,并选择 DPI 接口。启用infinite模式以持续向 LCD 面板传输数据。

LCDC_InitTypeDef lcdc_init = {0};
lcdc_init.LCDC_Interface = LCDC_IF_DPI;
lcdc_init.LCDC_PixelInputFormat = LCDC_INPUT_RGB565;
lcdc_init.LCDC_PixelOutputFormat = LCDC_OUTPUT_RGB888;
lcdc_init.LCDC_PixelBitSwap = LCDC_SWAP_BYPASS; //lcdc_handler_cfg->LCDC_TeEn = LCDC_TE_DISABLE;
lcdc_init.LCDC_GroupSel = 1;

lcdc_init.LCDC_DmaThreshold = 64;   //DMA MSize + Dma Threshold = 128
lcdc_init.LCDC_InfiniteModeEn = 1;
LCDC_Init(&lcdc_init);
  1. 初始化 DPI 接口,包括时钟分频和同步信号的参数。

uint32_t HSA = 48, HFP = 40, HBP = 40, HACT = EK9716_800480_LCD_WIDTH;
uint32_t VSA = 1, VFP = 13, VBP = 31, VACT = EK9716_800480_LCD_HEIGHT;

LCDC_eDPICfgTypeDef eDPICfg;
eDPICfg.eDPI_ClockDiv = 4;

eDPICfg.eDPI_HoriSyncWidth = HSA;
eDPICfg.eDPI_VeriSyncHeight = VSA;
eDPICfg.eDPI_AccumulatedHBP = HSA + HBP;
eDPICfg.eDPI_AccumulatedVBP = VSA + VBP;
eDPICfg.eDPI_AccumulatedActiveW = HSA + HBP + HACT;
eDPICfg.eDPI_AccumulatedActiveH = VSA + VBP + VACT;
eDPICfg.eDPI_TotalWidth = HSA + HBP + HACT + HFP;
eDPICfg.eDPI_TotalHeight = VSA + VBP + VACT + VFP;
eDPICfg.eDPI_DataEnPolarity = 1;
eDPICfg.eDPI_LineIntMask = 1;   //Mask interrupt
eDPICfg.eDPI_ColorMap = EDPI_PIXELFORMAT_RGB888;//For RGB888
eDPICfg.eDPI_OperateMode = 0;//Video mode
eDPICfg.eDPI_LcdArc = 0;  //Architecture 1,2,3
eDPICfg.eDPI_CmdMaxLatency = 0;
eDPICfg.eDPI_LineBufferPixelThreshold = eDPICfg.eDPI_TotalWidth / 2;

EDPI_Init(&eDPICfg);
  1. 控制复位引脚完成复位过程。

ek9716_reset_high();
platform_delay_ms(200);
ek9716_reset_low();
platform_delay_ms(200);
ek9716_reset_high();
platform_delay_ms(200);
  1. 初始化要显示的数据的内存空间。

for (uint32_t i = 0; i < EK9716_800480_LCD_WIDTH * EK9716_800480_LCD_HEIGHT; i++)
{
    pixel[i] = 0x001F;
}
  1. 初始化 DMA 参数,包括数据大小、burst大小和源地址的增长方式,以通过初始化后的 DPI 接口向 LCD 面板传输数据。在本示例中,选择了DNA的linklist模式。

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_64;
LCDC_DMA_InitStruct.LCDC_DMA_DestinationMsize    = LCDC_DMA_Msize_64;
LCDC_DMA_InitStruct.LCDC_DMA_Multi_Block_Mode   = LLI_TRANSFER;
LCDC_DMA_InitStruct.LCDC_DMA_Multi_Block_En     = ENABLE;
LCDC_DMA_InitStruct.LCDC_DMA_Multi_Block_Struct  = LCDC_DMA_LINKLIST_REG_BASE + 0x50;
LCDC_DMA_Init(LCDC_DMA_CHANNEL_INDEX, &LCDC_DMA_InitStruct);
  1. 初始化 DMA 配置的链表。在本示例中,两个缓冲区交替使用,它们的地址逐渐增加以完全覆盖整个显示区域。

LCDC_DMALLI_InitTypeDef LCDC_DMA_LLI_Init = {0};
LCDC_DMA_LLI_Init.g1_source_addr = (uint32_t)p_buffer;
LCDC_DMA_LLI_Init.g2_source_addr = (uint32_t)((uint32_t)p_buffer + EK9716_800480_LCD_WIDTH *
                                              EK9716_DRV_PIXEL_BITS / 8);
LCDC_DMA_LLI_Init.g1_sar_offset = EK9716_800480_LCD_WIDTH * EK9716_DRV_PIXEL_BITS / 8 * 2;
LCDC_DMA_LLI_Init.g2_sar_offset = EK9716_800480_LCD_WIDTH * EK9716_DRV_PIXEL_BITS / 8 * 2;
LCDC_DMA_LinkList_Init(&LCDC_DMA_LLI_Init, &LCDC_DMA_InitStruct);
  1. 每当整个帧传输到 LCD 面板后,源地址将重置为由 API LCDC_DMA_Infinite_Buf_Update() 预设的像素缓冲区地址。DMA 将再次读取步骤 8 中设置的链表,并开始无限传输数据。

LCDC_DMA_Infinite_Buf_Update((uint8_t *)p_buffer,
                             (uint8_t *)p_buffer + EK9716_800480_LCD_WIDTH *
                             EK9716_DRV_PIXEL_BITS / 8);
  1. 使用 API LCDC_SetTxPixelLen() 确定每帧内应发送多少像素。

LCDC_SetTxPixelLen(EK9716_800480_LCD_WIDTH * EK9716_800480_LCD_HEIGHT);

功能实现

LCDC的功能实现在函数 ek9716_dma_init 中展示。

  1. 通过调用 LCDC_SwitchDirect() 确定数据流的方向。

LCDC_SwitchDirect(LCDC_TX_MODE);
  1. 通过在 LCDC_SwitchMode() 中设置参数 LCDC_AUTO_MODE 让 LCDC 自动传输数据。

LCDC_SwitchMode(LCDC_AUTO_MODE);
  1. 通过调用 LCDC_Cmd() 启用 LCDC。

LCDC_Cmd(ENABLE);
  1. 使用 LCDC_DMA_MultiBlockCmd() 启用multi-block传输,并使用 LCDC_DMAChannelCmd() 启用内部 DMA 通道。

LCDC_DMA_MultiBlockCmd(ENABLE);
LCDC_DMAChannelCmd(LCDC_DMA_CHANNEL_NUM, ENABLE);
  1. 使用 LCDC_DmaCmd() 让 DMA 开始向内部 FIFO 搬运数据。

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

LCDC_AutoWriteCmd(ENABLE);