一、前述
ART-PI是RTT官方退出的STM32H7系列的开发板,照理说用RTT开发即可,简洁易上手(如果已经熟悉RTT)。但也可基于STM32CUBEMX开发,生成带FreeRTOS以及FATFS文件系统的工程。相对来说,用ST官方工具生成的代码用起来跟原汁原味一些,但RTT则具有更多的中间件,代码使用起来也更加简洁。个人习惯于使用STM32CUBEMX,所以先记录一下基于CUBE的开发方式,后续会加入RTT。
二、硬件配置
如图的ART-PI + LCD转接板 + 野火5寸RGB屏
三、STM32CUBEMX的配置
进入正题,创建一个LTDC的显示工程。需要用到以下几个基本外设,FMC、MPU、DMA2D、LTDC。FMC用于驱动SDRAM,作为LTDC的缓存。如果是480*272的RGB显示屏,也可使用内部的AXI-SRAM以获得更大的数据带宽。但AXI-SRAM只有512KB,最多作双FB缓冲。有的GUI如AWTK需要作三FB缓冲才可流畅显示而不产生撕裂画面。MPU,字面上意思是用于保护内存单元。但实际上,MPU是用来协调芯片内部的I-Cashe、D-Cashe与存储设备的一个外设。具体自行搜索:“STM32F7 MPU Cache浅析”。具体作用就是可加速RAM、ROM数据的读写速度,当然,加速也是存在一定的局限性的。DMA2D与LTDC就不赘述了。
(1)时钟树
内核倍频到480M,LTDC时钟如下图红框,20M。FMC,则采用48M的HSI倍频到240M。所以SDRAM时钟频率是240M/2 = 120M
图3.1.1 时钟树的配置
(2)GPIO的配置
ART-PI还是进行了不少的GPIO映射的,估计是为了画板走线方便吧。LTDC的IO都进行了大量的映射,这里还需要注意背光控制引脚的初始化。
图3.2.1 GPIO的配置
(3)MPU的配置
如下图,配置了AXI-SRAM以及SDRAM的缓冲模式。SDRAM为读缓冲,写不缓冲。而AXI-SRAM则是读写缓冲。
图3.3.1 MPU的配置
(4)FMC的配置
SDRAM是使用FMC驱动的,我们找到SDRAM1处进行配置。具体的时序参考SDRAM的数据手册。ART-PI使用的是:W9825G6KH-6。
图 3.4.1 FMC配置
图 3.4.2 SDRAM时序
(5)LTDC的配置
我使用的是野火的800*480-5寸电容触摸显示模组,LTDC驱动RGB屏的参数也应自行参考屏的数据手册。
图 3.5.1 LTDC配置1
图 3.5.2 LTDC配置2
(6)DMA2D的配置
这个只需要在CUBE里面勾选这个选项就好,这样“main.h”就会包含DMA2D库的头文件。因为后续在使用DMA2D的时候,会先对DMA2D进行配置再使用,所以在CUBE里面的配置无意义。
四、代码
用stm32cubemx生成代码后,并不能直接烧录到ART-PI运行。还需要添加一些代码。
(1)SDRAM的初始化,参考了安富莱的教程。
#define SDRAM_TIMEOUT ((uint32_t)0xFFFF)
#define REFRESH_COUNT ((uint32_t)917) /* SDRAM ReFlash Count */
#define SDRAM_MODEREG_BURST_LENGTH_1 ((uint16_t)0x0000)
#define SDRAM_MODEREG_BURST_LENGTH_2 ((uint16_t)0x0001)
#define SDRAM_MODEREG_BURST_LENGTH_4 ((uint16_t)0x0002)
#define SDRAM_MODEREG_BURST_LENGTH_8 ((uint16_t)0x0004)
#define SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL ((uint16_t)0x0000)
#define SDRAM_MODEREG_BURST_TYPE_INTERLEAVED ((uint16_t)0x0008)
#define SDRAM_MODEREG_CAS_LATENCY_2 ((uint16_t)0x0020)
#define SDRAM_MODEREG_CAS_LATENCY_3 ((uint16_t)0x0030)
#define SDRAM_MODEREG_OPERATING_MODE_STANDARD ((uint16_t)0x0000)
#define SDRAM_MODEREG_WRITEBURST_MODE_PROGRAMMED ((uint16_t)0x0000)
#define SDRAM_MODEREG_WRITEBURST_MODE_SINGLE ((uint16_t)0x0200)
/* This function is used to initialize SDRAM */
void SDRAM_Initialization_Sequence(SDRAM_HandleTypeDef *hsdram)
{
__IO uint32_t tmpmrd =0;
FMC_SDRAM_CommandTypeDef command1 = {0},*Command = &command1;
/*##-1- Clock enable command ##################################################*/
Command->CommandMode = FMC_SDRAM_CMD_CLK_ENABLE;
Command->CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;;
Command->AutoRefreshNumber = 1;
Command->ModeRegisterDefinition = 0;
/* SendCommand */
HAL_SDRAM_SendCommand(hsdram, Command, SDRAM_TIMEOUT);
/*##-2- delay ##################################################*/
HAL_Delay(1);
/*##-3- SDRAM pre charging(precharge all) #############################*/
Command->CommandMode = FMC_SDRAM_CMD_PALL;
Command->CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
Command->AutoRefreshNumber = 1;
Command->ModeRegisterDefinition = 0;
/* SendCommand */
HAL_SDRAM_SendCommand(hsdram, Command, SDRAM_TIMEOUT);
/*##-4- Auto refresh command ##################################################*/
Command->CommandMode = FMC_SDRAM_CMD_AUTOREFRESH_MODE;
Command->CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
Command->AutoRefreshNumber = 8;
Command->ModeRegisterDefinition = 0;
/* SendCommand */
HAL_SDRAM_SendCommand(hsdram, Command, SDRAM_TIMEOUT);
/*##-5- Configure SDRAM mode register ######################################*/
tmpmrd = (uint32_t)SDRAM_MODEREG_BURST_LENGTH_1 |
SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL |
SDRAM_MODEREG_CAS_LATENCY_3 |
SDRAM_MODEREG_OPERATING_MODE_STANDARD |
SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;
Command->CommandMode = FMC_SDRAM_CMD_LOAD_MODE;
Command->CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
Command->AutoRefreshNumber = 1;
Command->ModeRegisterDefinition = tmpmrd;
/* SendCommand */
HAL_SDRAM_SendCommand(hsdram, Command, SDRAM_TIMEOUT);
/*##-6- Set self refresh rate ############################################*/
/*
= 64ms / 8192 *120MHz*1000 - 20
= 917
*/
HAL_SDRAM_ProgramRefreshRate(hsdram, REFRESH_COUNT);
}
(2)DMA2D功能代码,这部分代码来自安富莱的教程
/*
*********************************************************************************************************
* 函 数 名: _DMA2D_Fill
* 功能说明: DMA2D颜色填充功能
* 形 参: pDst 颜色数据目的地址
* xSize 色块X轴大小,即每行像素数
* ySize 色块Y轴大小,即行数
* OffLine 前景层图像的行偏移
* ColorIndex 色块颜色值
* PixelFormat 目标区颜色格式
* 返 回 值: 无
*********************************************************************************************************
*/
void _DMA2D_Fill(void * pDst,
uint32_t xSize,
uint32_t ySize,
uint32_t OffLine,
uint32_t ColorIndex,
uint32_t PixelFormat)
{
/* DMA2D采用寄存器到存储器模式, 这种模式用不到前景层和背景层 */
DMA2D->CR = 0x00030000UL | (1 << 9);
DMA2D->OCOLR = ColorIndex;
DMA2D->OMAR = (uint32_t)pDst;
DMA2D->OOR = OffLine;
DMA2D->OPFCCR = PixelFormat;
DMA2D->NLR = (uint32_t)(xSize << 16) | (uint16_t)ySize;
/* 启动传输 */
DMA2D->CR |= DMA2D_CR_START;
/* 等待DMA2D传输完成 */
while (DMA2D->CR & DMA2D_CR_START) {}
}
/*
*********************************************************************************************************
* 函 数 名: _DMA2D_Copy
* 功能说明: 通过DMA2D从前景层复制指定区域的颜色数据到目标区域
* 形 参: pSrc 颜色数据源地址
* pDst 颜色数据目的地址
* xSize 目的区域的X轴大小,即每行像素数
* ySize 目的区域的Y轴大小,即行数
* OffLineSrc 前景层图像的行偏移
* OffLineDst 输出的行偏移
* PixelFormat 目标区颜色格式
* 返 回 值: 无
*********************************************************************************************************
*/
void _DMA2D_Copy(void * pSrc,
void * pDst,
uint32_t xSize,
uint32_t ySize,
uint32_t OffLineSrc,
uint32_t OffLineDst,
uint32_t PixelFormat)
{
/* DMA2D采用存储器到存储器模式, 这种模式是前景层作为DMA2D输入 */
DMA2D->CR = 0x00000000UL | (1 << 9);
DMA2D->FGMAR = (uint32_t)pSrc;
DMA2D->OMAR = (uint32_t)pDst;
DMA2D->FGOR = OffLineSrc;
DMA2D->OOR = OffLineDst;
/* 前景层和输出区域都采用的RGB565颜色格式 */
DMA2D->FGPFCCR = LTDC_PIXEL_FORMAT_RGB565;
DMA2D->OPFCCR = LTDC_PIXEL_FORMAT_RGB565;
DMA2D->NLR = (uint32_t)(xSize << 16) | (uint16_t)ySize;
/* 启动传输 */
DMA2D->CR |= DMA2D_CR_START;
/* 等待DMA2D传输完成 */
while (DMA2D->CR & DMA2D_CR_START) {}
}
/*
*********************************************************************************************************
* 函 数 名: _DMA2D_MixColorsBulk
* 功能说明: 前景层和目标区域的颜色混合
* 形 参: pColorFG 前景层数据源地址
* OffLineSrcFG 前景层图像的行偏移
* pColorDst 目标区数据地址
* OffLineDst 目标区的行偏移
* xSize 目的区域的X轴大小,即每行像素数
* ySize 目的区域的Y轴大小,即行数
* Intens 设置前景层的透明度,255表示完全不透明,0表示完全透明
* 返 回 值: 无
*********************************************************************************************************
*/
void _DMA2D_MixColorsBulk(uint32_t * pColorFG,
uint32_t OffLineSrcFG,
uint32_t * pColorDst,
uint32_t OffLineDst,
uint32_t xSize,
uint32_t ySize,
uint8_t Intens)
{
/* DMA2D采用存储器到存储器模式, 这种模式前景层和背景层作为DMA2D输入,且支持颜色格式转换和颜色混合 */
DMA2D->CR = 0x00020000UL | (1 << 9);
DMA2D->FGMAR = (uint32_t)pColorFG;
DMA2D->BGMAR = (uint32_t)pColorDst;
DMA2D->OMAR = (uint32_t)pColorDst;
DMA2D->FGOR = OffLineSrcFG;
DMA2D->BGOR = OffLineDst;
DMA2D->OOR = OffLineDst;
/* 前景层,背景层和输出区都是用的RGB565格式 */
DMA2D->FGPFCCR = LTDC_PIXEL_FORMAT_RGB565
| (1UL << 16)
| ((uint32_t)Intens << 24);
DMA2D->BGPFCCR = LTDC_PIXEL_FORMAT_RGB565;
DMA2D->OPFCCR = LTDC_PIXEL_FORMAT_RGB565;
DMA2D->NLR = (uint32_t)(xSize << 16) | (uint16_t)ySize;
/* 启动传输 */
DMA2D->CR |= DMA2D_CR_START;
/* 等待DMA2D传输完成 */
while (DMA2D->CR & DMA2D_CR_START) {}
}
/*
*********************************************************************************************************
* 函 数 名: _DMA2D_AlphaBlendingBulk
* 功能说明: 前景层和背景层的颜色混合
* 形 参: pColorFG 前景层源数据地址
* OffLineSrcFG 前景层源数据行偏移
* pColorBG 背景层源数据地址
* OffLineSrcBG 背景层源数据行偏移
* pColorDst 目标区地址
* OffLineDst 目标区行偏移
* xSize 目标区域的X轴大小,即每行像素数
* ySize 目标区域的Y轴大小,即行数
* 返 回 值: 无
*********************************************************************************************************
*/
void _DMA2D_AlphaBlendingBulk(uint32_t * pColorFG,
uint32_t OffLineSrcFG,
uint32_t * pColorBG,
uint32_t OffLineSrcBG,
uint32_t * pColorDst,
uint32_t OffLineDst,
uint32_t xSize,
uint32_t ySize)
{
/* DMA2D采用存储器到存储器模式, 这种模式前景层和背景层作为DMA2D输入,且支持颜色格式转换和颜色混合 */
DMA2D->CR = 0x00020000UL | (1 << 9);
DMA2D->FGMAR = (uint32_t)pColorFG;
DMA2D->BGMAR = (uint32_t)pColorBG;
DMA2D->OMAR = (uint32_t)pColorDst;
DMA2D->FGOR = OffLineSrcFG;
DMA2D->BGOR = OffLineSrcBG;
DMA2D->OOR = OffLineDst;
/* 前景层,背景层采用ARGB8888格式,输出区采用RGB565格式 */
DMA2D->FGPFCCR = LTDC_PIXEL_FORMAT_ARGB8888;
DMA2D->BGPFCCR = LTDC_PIXEL_FORMAT_ARGB8888;
DMA2D->OPFCCR = LTDC_PIXEL_FORMAT_RGB565;
DMA2D->NLR = (uint32_t)(xSize << 16) | (uint16_t)ySize;
/* 启动传输 */
DMA2D->CR |= DMA2D_CR_START;
/* 等待DMA2D传输完成 */
while (DMA2D->CR & DMA2D_CR_START) {}
}
/*
*********************************************************************************************************
* 函 数 名: _DMA2D_DrawAlphaBitmap
* 功能说明: ARGB8888格式位图显示
* 形 参: pDst 目标区地址
* pSrc 源数据地址,即位图首地址
* xSize 目标区域的X轴大小,即每行像素数
* ySize 目标区域的Y轴大小,即行数
* OffLineSrc 源数据行偏移
* OffLineDst 目标区行偏移
* PixelFormat 目标区颜色格式
* 返 回 值: 无
*********************************************************************************************************
*/
void _DMA2D_DrawAlphaBitmap(void * pDst,
void * pSrc,
uint32_t xSize,
uint32_t ySize,
uint32_t OffLineSrc,
uint32_t OffLineDst,
uint32_t PixelFormat)
{
/* DMA2D采用存储器到存储器模式, 这种模式前景层和背景层作为DMA2D输入,且支持颜色格式转换和颜色混合 */
DMA2D->CR = 0x00020000UL | (1 << 9);
DMA2D->FGMAR = (uint32_t)pSrc;
DMA2D->BGMAR = (uint32_t)pDst;
DMA2D->OMAR = (uint32_t)pDst;
DMA2D->FGOR = OffLineSrc;
DMA2D->BGOR = OffLineDst;
DMA2D->OOR = OffLineDst;
/* 前景层颜色格式是LTDC_PIXEL_FORMAT_ARGB8888,即位图的颜色格式,背景层和输出区颜色格式可配置 */
DMA2D->FGPFCCR = LTDC_PIXEL_FORMAT_ARGB8888;
DMA2D->BGPFCCR = PixelFormat;
DMA2D->OPFCCR = PixelFormat;
DMA2D->NLR = (uint32_t)(xSize << 16) | (uint16_t)ySize;
/* 启动传输 */
DMA2D->CR |= DMA2D_CR_START;
/* 等待DMA2D传输完成 */
while (DMA2D->CR & DMA2D_CR_START) {}
}
(3)主函数
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MPU Configuration--------------------------------------------------------*/
MPU_Config();
/* Enable I-Cache---------------------------------------------------------*/
SCB_EnableICache();
/* Enable D-Cache---------------------------------------------------------*/
SCB_EnableDCache();
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_FMC_Init();
MX_LPUART1_UART_Init();
MX_TIM6_Init();
MX_LTDC_Init();
MX_DMA2D_Init();
/* USER CODE BEGIN 2 */
SDRAM_Initialization_Sequence(&hsdram1);
_DMA2D_Fill((uint16_t*)0xc0000000,
800,
480,
0,
0x07e0,
LTDC_PIXEL_FORMAT_RGB565) ;
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
五、效果展示
(1) 主函数中使用DMA2D刷了全屏的绿色,显存位置如LTDC的设置,地址为:0xC0000000。这里屏幕没亮的话注意检查一下背光是否有打开。
(2)移植了LittleVGL7.9版本到板子上,并跑了一个例程。需要代码的可自行去下载:https://download.csdn.net/download/a3748622/14039358
(3)LittleVGL双帧缓冲代码
上述工程里默认是使用AXI RAM做240ROW的缓冲区。也可以使用SDRAM作双帧缓冲,通过“lv_port_disp.c”的一个宏定义更改:
#define TWO_SCREEN_SIZE 0
|