STM32F469有个分辨率很高的4寸屏幕,无论显示图片还是字体都很清晰细腻,下面的测试就模仿大家经常见到的“弹幕”效果进行测试。:lol
板载的LCD是使用MIPI接口(DSI)来实现的,配合LTDC和DMA2D使用更加功能强大。LCD驱动部分总的结构框图如下:
DSI接口配置较为复杂,还好的是ST给出了参考的配置过程,分别初始化SDRAM,DSI,LTDC这些接口即可,使用时直接写SDRAM用来更新显示。除此之外也可以使用CubeMX配置更加简单方便。
主要的LCD初始化代码如下:
uint8_t BSP_LCD_InitEx(LCD_OrientationTypeDef orientation)
{
DSI_PLLInitTypeDef dsiPllInit;
static RCC_PeriphCLKInitTypeDef PeriphClkInitStruct;
uint32_t LcdClock = 27429; /*!< LcdClk = 27429 kHz */
uint32_t Clockratio = 0;
uint32_t laneByteClk_kHz = 0;
uint32_t VSA; /*!< Vertical start active time in units of lines */
uint32_t VBP; /*!< Vertical Back Porch time in units of lines */
uint32_t VFP; /*!< Vertical Front Porch time in units of lines */
uint32_t VACT; /*!< Vertical Active time in units of lines = imageSize Y in pixels to display */
uint32_t HSA; /*!< Horizontal start active time in units of lcdClk */
uint32_t HBP; /*!< Horizontal Back Porch time in units of lcdClk */
uint32_t HFP; /*!< Horizontal Front Porch time in units of lcdClk */
uint32_t HACT; /*!< Horizontal Active time in units of lcdClk = imageSize X in pixels to display */
/* Toggle Hardware Reset of the DSI LCD using
* its XRES signal (active low) */
BSP_LCD_Reset();
/* Call first MSP Initialize only in case of first initialization
* This will set IP blocks LTDC, DSI and DMA2D
* - out of reset
* - clocked
* - NVIC IRQ related to IP blocks enabled
*/
BSP_LCD_MspInit();
/*************************DSI Initialization***********************************/
/* Base address of DSI Host/Wrapper registers to be set before calling De-Init */
hdsi_eval.Instance = DSI;
HAL_DSI_DeInit(&(hdsi_eval));
#if !defined(USE_STM32469I_DISCO_REVA)
dsiPllInit.PLLNDIV = 125;
dsiPllInit.PLLIDF = DSI_PLL_IN_DIV2;
dsiPllInit.PLLODF = DSI_PLL_OUT_DIV1;
#else
dsiPllInit.PLLNDIV = 100;
dsiPllInit.PLLIDF = DSI_PLL_IN_DIV5;
dsiPllInit.PLLODF = DSI_PLL_OUT_DIV1;
#endif
laneByteClk_kHz = 62500; /* 500 MHz / 8 = 62.5 MHz = 62500 kHz */
/* Set number of Lanes */
hdsi_eval.Init.NumberOfLanes = DSI_TWO_DATA_LANES;
/* TXEscapeCkdiv = f(LaneByteClk)/15.62 = 4 */
hdsi_eval.Init.TXEscapeCkdiv = laneByteClk_kHz/15620;
HAL_DSI_Init(&(hdsi_eval), &(dsiPllInit));
Clockratio = laneByteClk_kHz/LcdClock;
/* Timing parameters for all Video modes
* Set Timing parameters of LTDC depending on its chosen orientation
*/
if(orientation == LCD_ORIENTATION_PORTRAIT)
{
VSA = OTM8009A_480X800_VSYNC; /* 12 */
VBP = OTM8009A_480X800_VBP; /* 12 */
VFP = OTM8009A_480X800_VFP; /* 12 */
HSA = OTM8009A_480X800_HSYNC; /* 120 */
HBP = OTM8009A_480X800_HBP; /* 120 */
HFP = OTM8009A_480X800_HFP; /* 120 */
lcd_x_size = OTM8009A_480X800_WIDTH; /* 480 */
lcd_y_size = OTM8009A_480X800_HEIGHT; /* 800 */
}
else
{
/* lcd_orientation == LCD_ORIENTATION_LANDSCAPE */
VSA = OTM8009A_800X480_VSYNC; /* 12 */
VBP = OTM8009A_800X480_VBP; /* 12 */
VFP = OTM8009A_800X480_VFP; /* 12 */
HSA = OTM8009A_800X480_HSYNC; /* 120 */
HBP = OTM8009A_800X480_HBP; /* 120 */
HFP = OTM8009A_800X480_HFP; /* 120 */
lcd_x_size = OTM8009A_800X480_WIDTH; /* 800 */
lcd_y_size = OTM8009A_800X480_HEIGHT; /* 480 */
}
HACT = lcd_x_size;
VACT = lcd_y_size;
hdsivideo_handle.VirtualChannelID = LCD_OTM8009A_ID;
hdsivideo_handle.ColorCoding = LCD_DSI_PIXEL_DATA_FMT_RBG888;
hdsivideo_handle.VSPolarity = DSI_VSYNC_ACTIVE_HIGH;
hdsivideo_handle.HSPolarity = DSI_HSYNC_ACTIVE_HIGH;
hdsivideo_handle.DEPolarity = DSI_DATA_ENABLE_ACTIVE_HIGH;
hdsivideo_handle.Mode = DSI_VID_MODE_BURST; /* Mode Video burst ie : one LgP per line */
hdsivideo_handle.NullPacketSize = 0xFFF;
hdsivideo_handle.NumberOfChunks = 0;
hdsivideo_handle.PacketSize = HACT; /* Value depending on display orientation choice portrait/landscape */
hdsivideo_handle.HorizontalSyncActive = HSA*Clockratio;
hdsivideo_handle.HorizontalBackPorch = HBP*Clockratio;
hdsivideo_handle.HorizontalLine = (HACT + HSA + HBP + HFP)*Clockratio; /* Value depending on display orientation choice portrait/landscape */
hdsivideo_handle.VerticalSyncActive = VSA;
hdsivideo_handle.VerticalBackPorch = VBP;
hdsivideo_handle.VerticalFrontPorch = VFP;
hdsivideo_handle.VerticalActive = VACT; /* Value depending on display orientation choice portrait/landscape */
/* Enable or disable sending LP command while streaming is active in video mode */
hdsivideo_handle.LPCommandEnable = DSI_LP_COMMAND_ENABLE; /* Enable sending commands in mode LP (Low Power) */
/* Largest packet size possible to transmit in LP mode in VSA, VBP, VFP regions */
/* Only useful when sending LP packets is allowed while streaming is active in video mode */
hdsivideo_handle.LPLargestPacketSize = 64;
/* Largest packet size possible to transmit in LP mode in HFP region during VACT period */
/* Only useful when sending LP packets is allowed while streaming is active in video mode */
hdsivideo_handle.LPVACTLargestPacketSize = 64;
/* Specify for each region of the video frame, if the transmission of command in LP mode is allowed in this region */
/* while streaming is active in video mode */
hdsivideo_handle.LPHorizontalFrontPorchEnable = DSI_LP_HFP_ENABLE; /* Allow sending LP commands during HFP period */
hdsivideo_handle.LPHorizontalBackPorchEnable = DSI_LP_HBP_ENABLE; /* Allow sending LP commands during HBP period */
hdsivideo_handle.LPVerticalActiveEnable = DSI_LP_VACT_ENABLE; /* Allow sending LP commands during VACT period */
hdsivideo_handle.LPVerticalFrontPorchEnable = DSI_LP_VFP_ENABLE; /* Allow sending LP commands during VFP period */
hdsivideo_handle.LPVerticalBackPorchEnable = DSI_LP_VBP_ENABLE; /* Allow sending LP commands during VBP period */
hdsivideo_handle.LPVerticalSyncActiveEnable = DSI_LP_VSYNC_ENABLE; /* Allow sending LP commands during VSync = VSA period */
/* Configure DSI Video mode timings with settings set above */
HAL_DSI_ConfigVideoMode(&(hdsi_eval), &(hdsivideo_handle));
/* Enable the DSI host and wrapper : but LTDC is not started yet at this stage */
HAL_DSI_Start(&(hdsi_eval));
/*************************End DSI Initialization*******************************/
/************************LTDC Initialization***********************************/
/* Timing Configuration */
hltdc_eval.Init.HorizontalSync = (HSA - 1);
hltdc_eval.Init.AccumulatedHBP = (HSA + HBP - 1);
hltdc_eval.Init.AccumulatedActiveW = (lcd_x_size + HSA + HBP - 1);
hltdc_eval.Init.TotalWidth = (lcd_x_size + HSA + HBP + HFP - 1);
/* Initialize the LCD pixel width and pixel height */
hltdc_eval.LayerCfg->ImageWidth = lcd_x_size;
hltdc_eval.LayerCfg->ImageHeight = lcd_y_size;
/* LCD clock configuration */
/* PLLSAI_VCO Input = HSE_VALUE/PLL_M = 1 Mhz */
/* PLLSAI_VCO Output = PLLSAI_VCO Input * PLLSAIN = 384 Mhz */
/* PLLLCDCLK = PLLSAI_VCO Output/PLLSAIR = 384 MHz / 7 = 54.857 MHz */
/* LTDC clock frequency = PLLLCDCLK / LTDC_PLLSAI_DIVR_2 = 54.857 MHz / 2 = 27.429 MHz */
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LTDC;
PeriphClkInitStruct.PLLSAI.PLLSAIN = 384;
PeriphClkInitStruct.PLLSAI.PLLSAIR = 7;
PeriphClkInitStruct.PLLSAIDivR = RCC_PLLSAIDIVR_2;
HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);
/* Background value */
hltdc_eval.Init.Backcolor.Blue = 0;
hltdc_eval.Init.Backcolor.Green = 0;
hltdc_eval.Init.Backcolor.Red = 0;
hltdc_eval.Init.PCPolarity = LTDC_PCPOLARITY_IPC;
hltdc_eval.Instance = LTDC;
/* Get LTDC Configuration from DSI Configuration */
HAL_LTDC_StructInitFromVideoConfig(&(hltdc_eval), &(hdsivideo_handle));
/* Initialize the LTDC */
HAL_LTDC_Init(&hltdc_eval);
#if !defined(DATA_IN_ExtSDRAM)
/* Initialize the SDRAM */
BSP_SDRAM_Init();
#endif /* DATA_IN_ExtSDRAM */
/* Initialize the font */
BSP_LCD_SetFont(&LCD_DEFAULT_FONT);
/************************End LTDC Initialization*******************************/
/***********************OTM8009A Initialization********************************/
/* Initialize the OTM8009A LCD Display IC Driver (KoD LCD IC Driver)
* depending on configuration set in 'hdsivideo_handle'.
*/
OTM8009A_Init(hdsivideo_handle.ColorCoding, orientation);
/***********************End OTM8009A Initialization****************************/
return LCD_OK;
}
然后实现一个简单的字符串滚动函数:
unsigned int Colorbuff[] = {LCD_COLOR_BLUE,LCD_COLOR_GREEN,LCD_COLOR_RED,LCD_COLOR_CYAN,\
LCD_COLOR_MAGENTA,LCD_COLOR_YELLOW,LCD_COLOR_LIGHTBLUE,LCD_COLOR_DARKBLUE,LCD_COLOR_ORANGE,\
LCD_COLOR_ORANGE,LCD_COLOR_WHITE,LCD_COLOR_RED,LCD_COLOR_BLUE,LCD_COLOR_GREEN};
//滚动显示一个字符串
void BSP_LCD_SlideShow(uint16_t x,uint16_t y,uint16_t num)
{
BSP_LCD_ShowStr(x,y,LCD_COLOR_BLACK,LCD_COLOR_BLACK,TextBuff[num],0);
BSP_LCD_ShowStr(x+1,y,Colorbuff[num],LCD_COLOR_BLACK,TextBuff[num],0);
}
最后就是在main函数中来滚动显示想要显示的字符串了:
int main(void)
{
uint8_t LedSta = LED_ON;
uint16_t x0,t;
HAL_Init();
SystemClock_Config();
LED_Init();
BSP_LCD_Init();
BSP_LCD_Layer();
while(1)
{
for(x0 = 0;x0 < 800;x0++)
{
for(t = 0;t < 14;t++)
{
BSP_LCD_SlideShow(x0,10 + 30*t,t);
}
GREEN_STA(LedSta);ORANGE_STA(LedSta);RED_STA(LedSta);BLUE_STA(LedSta);
LedSta = !LedSta;
}
}
}
效果大概如下:
总体来说显示滚动还很流畅,但是可能是优化不是很好,有的字符串有轻微的闪烁。通过简单的“弹幕”测试,可以发现虽然整个LCD显示配置过程比较复杂,但是一旦配置就可以较为简单的使用,而且显示效果也较为理想。
|