• 内部 SRAM:0x20000000 起,大小 0x140000 = 1.25MB。片上 RAM。

  • 外部 SDRAM:0x60000000 起 = 8MB。

LTDC

LTDC = LCD-TFT 显示控制器,一个专用显示外设,用它来配置屏幕的各项连接数据。他还能直接将一段内存通过DMA直接渲染到界面上。

它:

  • 用内部计数器自己产生 VSYNC/HSYNC/DE/PCLK 时序
  • 作为总线主设备,按像素时钟不断从显存(CFBAR)取像素发到屏
  • 做图层混合(Layer1/Layer2)、透明度、RGB565/RGB888 格式
  • 用影子寄存器 + 消隐期重载实现无撕裂换帧

屏幕参数

enum {
    LCD_WIDTH = 240U, //屏幕像素
    LCD_HEIGHT = 320U,//屏幕像素
    
    LCD_HSYNC = 10U, //行同步信号
    LCD_VSYNC = 4U,//帧同步信号
    
    LCD_HBACK_PORCH = 10U, //
    LCD_VBACK_PORCH = 4U,
    
    LCD_HFRONT_PORCH = 38U,
    LCD_VFRONT_PORCH = 8U
};

配置时钟

LTDC提供了多个4时钟源:PLL1R,PLL2P,PLL2Q,PLL2R

LTDC-clock-tree

static void pixel_clock_init(void)
{
    /*
 	* PLL2复用PLL1M预分频后的1MHz参考时钟:
 	* PLL2 VCO = 1MHz × 84 = 84MHz;
 	* PLL2R    = 84MHz ÷ 12 = 7MHz。
 	*/
    ClockEnable(ClockNodePLL2G, false);//先关闭时钟
    ClockMultiply(ClockNodePLL2, 84U); //ClockMultiply 倍频x84 = 84MHz
    ClockDivide(ClockNodePLL2R, 12U); //ClockDivide 分频为÷12 = 7MHz
    ClockEnable(ClockNodePLL2G, true);//配置好后再开启时钟
    while (RCC_GetFlagStatus(RCC_FLAG_PLL2RDY) == RESET) {//等待PLL2锁定
    }
    ClockSelect(ClockNodeDPC, ClockNodePLL2R);//选择ClockNodePLL2R作为LTDC时钟输入
}

配置io脚

static void ltdc_gpio_init(void)
{
    GPIO_InitTypeDef gpio;

    PeripheralEnable(PeripheralGPIOA, true);
    /*省略GPIOB-H*/
    PeripheralEnable(PeripheralGPIOI, true);
    PeripheralEnable(PeripheralPCFG, true);
	//配置电气特性
    gpio.GPIO_Speed = GPIO_Speed_50MHz;
    gpio.GPIO_Mode = GPIO_Mode_AF;
    gpio.GPIO_OType = GPIO_OType_PP;
    gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
	
    /*配置DE PCLK VSYNC HSYNC*/
    GPIO_PinAFConfig(GPIOF, GPIO_PinSource10, GPIO_AF_LTDC); /* DE */
    gpio.GPIO_Pin = GPIO_Pin_10;
    GPIO_Init(GPIOF, &gpio);

    GPIO_PinAFConfig(GPIOG, GPIO_PinSource7, GPIO_AF_LTDC);  /* PCLK */
    gpio.GPIO_Pin = GPIO_Pin_7 ;
    GPIO_Init(GPIOG, &gpio);
    
    GPIO_PinAFConfig(GPIOI, GPIO_PinSource9, GPIO_AF_LTDC);  /* VSYNC */
    GPIO_PinAFConfig(GPIOI, GPIO_PinSource10, GPIO_AF_LTDC); /* HSYNC */
    gpio.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
    GPIO_Init(GPIOI, &gpio);
    
    /*配置18线RGB的引脚*/
    /*省略*/
}

初始化LTDC

void ltdc_init(void)
{
	LTDC_InitTypeDef controller; //配置LTDC结构体
    
    PeripheralEnable(PeripheralLTDC, true);//开启LTDC功能
    
    LTDC_StructInit(&controller); //初始化LTDC结构体主要是清零
    
    //配置信号极性 HSYNC低有效 VSYNC低有效 DE高有效 PCLK反相
    controller.LTDC_HSPolarity = LTDC_HSPolarity_AL;//(uint32_t)0x00000000
    controller.LTDC_VSPolarity = LTDC_VSPolarity_AL;//(uint32_t)0x00000000
    controller.LTDC_DEPolarity = LTDC_DEPolarity_AH; //(uint32_t)0x20000000
    controller.LTDC_PCPolarity = LTDC_PCPolarity_IIPC; //(uint32_t)0x10000000
    
    //配置时钟同步 LTDC计数器从0开始 所以要-1U
    controller.LTDC_HorizontalSync = LCD_HSYNC - 1U;
    controller.LTDC_VerticalSync = LCD_VSYNC - 1U;
    //配置后肩  LTDC计数器从0开始 所以要-1U
    controller.LTDC_AccumulatedHBP = LCD_HSYNC + LCD_HBACK_PORCH - 1U;
    controller.LTDC_AccumulatedVBP = LCD_VSYNC + LCD_VBACK_PORCH - 1U;
    //有效区结束位置 LTDC计数器从0开始 所以要-1U
    controller.LTDC_AccumulatedActiveW = LCD_HSYNC + LCD_HBACK_PORCH + LCD_WIDTH - 1U;
    controller.LTDC_AccumulatedActiveH = LCD_VSYNC + LCD_VBACK_PORCH + LCD_HEIGHT - 1U;
    //整行和整帧结束位置 LTDC计数器从0开始 所以要-1U
    controller.LTDC_TotalWidth = LCD_HSYNC + LCD_HBACK_PORCH +
                                 LCD_WIDTH + LCD_HFRONT_PORCH - 1U;
    controller.LTDC_TotalHeigh = LCD_VSYNC + LCD_VBACK_PORCH +
                                 LCD_HEIGHT + LCD_VFRONT_PORCH - 1U;
    
    LTDC_Init(&controller);//写入配置
    
    /*
    *	配置Layer
    */
    LTDC_Layer_InitTypeDef layer; //图层,支持多个图层
    LTDC_LayerStructInit(&layer);
    
    //图层水平起点终点
    layer.LTDC_HorizontalStart = LCD_HSYNC + LCD_HBACK_PORCH;
    layer.LTDC_HorizontalStop = layer.LTDC_HorizontalStart + LCD_WIDTH - 1U;
    
    //图层垂直起点终点
    layer.LTDC_VerticalStart = LCD_VSYNC + LCD_VBACK_PORCH;
    layer.LTDC_VerticalStop = layer.LTDC_VerticalStart + LCD_HEIGHT - 1U;
    
    //输入这个Layer的显存像素格式、alpha、首地址
    layer.LTDC_PixelFormat = LTDC_Pixelformat_ARGB8888;
    layer.LTDC_ConstantAlpha = 0xFFU;
    layer.LTDC_CFBStartAdress = framebuffer_address;
    
    //行有效显存数据 & 当前行到下一行跨过多少字节
    layer.LTDC_CFBLineLength = LCD_WIDTH * 4U;
    layer.LTDC_CFBPitch = LCD_WIDTH * 4U;
    
    //帧缓冲行数
    layer.LTDC_CFBLineNumber = LCD_HEIGHT;
    
    //混合因子1,混合因子2,每个图层混合情况
    layer.LTDC_BlendingFactor_1 = LTDC_BlendingFactor1_PAxCA;
    layer.LTDC_BlendingFactor_2 = LTDC_BlendingFactor2_PAxCA;
    
    //开始写入配置
    LTDC_LayerInit(LTDC_Layer1, &layer);
    
    //关闭Layer2,开启关闭Layer1
    LTDC_LayerCmd(LTDC_Layer2, DISABLE);
    LTDC_LayerCmd(LTDC_Layer1, ENABLE);
    
    //LTDC->AHBCFG |= 0x07U;
    LTDC->AHBCFG |= 0x07U;
    
    //Reload配置
    /*
     CPU写配置
   		↓
	Shadow Register
   		↓ Reload
	Active Register
   		↓
	LTDC实际使用
	*/
    LTDC_ReloadConfig(LTDC_IMReload);//LTDC_IMReload 立即加载
    
    //启动LTDC
    LTDC_Cmd(ENABLE);
}