本帖最后由 sujingliang 于 2025-2-13 16:18 编辑
STM32H747I-DISCO、STM32H747I-EVAL的例程中有播放视频的例程,如STM32H747I-EVAL\Examples\JPEG\JPEG_MJPEG_VideoDecodingFromQSPI
本文用STM32H745I-DISCO实现一下
一、工程中需要添加的文件
AVI_parser.c AVI解码文件,官方提供的文件不做解释
decode_dma.c JPGE 解码文件,官方提供的文件不做解释
AVI_Player.c AVI例程文件
Retarget.c printf重定向
必要的BSP文件
硬件驱动文件mt48:SRAM;mt25:FLASH
stm32_lcd.c STM32的图形库文件
二、main.c
BSP_QSPI_Init(0, &init);
BSP_QSPI_EnableMemoryMappedMode(0);
MX_JPEG_Init();
MX_MDMA_Init();
BSP_LCD_Init(0, LCD_ORIENTATION_LANDSCAPE);
UTIL_LCD_SetFuncDriver(&LCD_Driver);
UTIL_LCD_SetLayer(0);
BSP_LED_Init(LED_GREEN);
BSP_LED_Init(LED_RED);
AVI_player_demo();
初始化flash、JPEG、MDMA、LCD
运行AVI DEMO程序
JPEG初始化
static void MX_JPEG_Init(void)
{
/* USER CODE BEGIN JPEG_Init 0 */
/* USER CODE END JPEG_Init 0 */
/* USER CODE BEGIN JPEG_Init 1 */
/* USER CODE END JPEG_Init 1 */
hjpeg.Instance = JPEG;
if (HAL_JPEG_Init(&hjpeg) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN JPEG_Init 2 */
/* USER CODE END JPEG_Init 2 */
}
MDMA初始化:
static void MX_MDMA_Init(void)
{
/* MDMA controller clock enable */
__HAL_RCC_MDMA_CLK_ENABLE();
/* Local variables */
/* MDMA interrupt initialization */
/* MDMA_IRQn interrupt configuration */
HAL_NVIC_SetPriority(MDMA_IRQn, 0x08, 0x0F);
HAL_NVIC_EnableIRQ(MDMA_IRQn);
}
MPU_Config
void MPU_Config(void)
{
MPU_Region_InitTypeDef MPU_InitStruct = {0};
/* Disables the MPU */
HAL_MPU_Disable();
/** Initializes and configures the Region and the memory to be protected
*/
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER0;
MPU_InitStruct.BaseAddress = 0x0;
MPU_InitStruct.Size = MPU_REGION_SIZE_4GB;
MPU_InitStruct.SubRegionDisable = 0x87;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.AccessPermission = MPU_REGION_NO_ACCESS;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/** Initializes and configures the Region and the memory to be protected
*/
MPU_InitStruct.Number = MPU_REGION_NUMBER1;
MPU_InitStruct.BaseAddress = 0xD0000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_32MB;
MPU_InitStruct.SubRegionDisable = 0x0;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/* Configure the MPU QSPI flash */
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.BaseAddress = 0x90000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_128MB;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER2;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
MPU_InitStruct.SubRegionDisable = 0x0;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/* Enables the MPU */
HAL_MPU_Enable(MPU_HFNMI_PRIVDEF);
}
三、AVI_Player.c
1、定义常量
//AVI文件地址 位于flash,首地址0x90000000
#define AVI_FILE_ADDRESS ((uint32_t)(0x90000000)) /*Address of the MJPEG AVI file located on the QSPI */
//LCD显示地址
#define LCD_FRAME_BUFFER 0xD0000000
//JPEG解码双缓冲地址
#define JPEG_OUTPUT_DATA_BUFFER0 0xD0100000
#define JPEG_OUTPUT_DATA_BUFFER1 0xD0200000
2、AVI_player_demo
主要功能是利用AVI_parser解析AVI文件(0x90000000),得到每一帧画面,用硬件JPEG双缓存解码,通过DMA2D将解码后内容显示到LCD上。
void AVI_player_demo(void)
{
uint32_t isfirstFrame =0 , startTime = 0;
uint32_t jpegOutDataAdreess = JPEG_OUTPUT_DATA_BUFFER0;
uint32_t FrameType = 0;
BSP_LCD_GetXSize(0, &LCD_X_Size);
BSP_LCD_GetYSize(0, &LCD_Y_Size);
/*##-3- Initialize the AVI Parser ##########################################*/
if(AVI_ParserInit(&AVI_Handel, AVI_FILE_ADDRESS) == 0)
{
isfirstFrame = 1;
FrameRate = 0;
startTime = HAL_GetTick();
do
{
/*##-4- Get a Frame from the AVI file ##################################*/
FrameType = AVI_GetFrame(&AVI_Handel);
if(FrameType == AVI_VIDEO_FRAME)
{
AVI_Handel.CurrentImage ++;
/*##-5- Start decoding the current JPEG frame with DMA (Not Blocking ) Method ################*/
JPEG_Decode_DMA(&hjpeg,(uint32_t) AVI_Handel.pVideoBuffer ,AVI_Handel.FrameSize, jpegOutDataAdreess );
/*##-6- Wait till end of JPEG decoding ###############################*/
while(Jpeg_HWDecodingEnd == 0)
{
}
if(isfirstFrame == 1)
{
isfirstFrame = 0;
/*##-7- Get JPEG Info #############################################*/
HAL_JPEG_GetInfo(&hjpeg, &JPEG_Info);
/*##-8- Initialize the DMA2D #######################################*/
DMA2D_Init(JPEG_Info.ImageWidth, JPEG_Info.ImageHeight, JPEG_Info.ChromaSubsampling);
}
/*##-9- Copy the Decoded frame to the display frame buffer using the DMA2D #*/
DMA2D_CopyBuffer((uint32_t *)jpegOutDataAdreess, (uint32_t *)LCD_FRAME_BUFFER, JPEG_Info.ImageWidth, JPEG_Info.ImageHeight);
//DMA2D_CopyBuffer((uint32_t *)jpegOutDataAdreess, (uint32_t*)LCD_FRAME_BUFFER, 0 , 0, JPEG_Info.ImageWidth, JPEG_Info.ImageHeight, JPEG_Info.ChromaSubsampling);
jpegOutDataAdreess = (jpegOutDataAdreess == JPEG_OUTPUT_DATA_BUFFER0) ? JPEG_OUTPUT_DATA_BUFFER1 : JPEG_OUTPUT_DATA_BUFFER0;
#ifdef USE_FRAMERATE_REGULATION
/* Regulate the frame rate to the video native frame rate by inserting delays */
FrameRate = (HAL_GetTick() - startTime) + 1;
if(FrameRate < ((AVI_Handel.aviInfo.SecPerFrame/1000) * AVI_Handel.CurrentImage))
{
HAL_Delay(((AVI_Handel.aviInfo.SecPerFrame /1000) * AVI_Handel.CurrentImage) - FrameRate);
}
#endif /* USE_FRAMERATE_REGULATION */
}
}while(AVI_Handel.CurrentImage < AVI_Handel.aviInfo.TotalFrame);
HAL_DMA2D_PollForTransfer(&DMA2D_Handle, 50); /* wait for the Last DMA2D transfer to ends */
if(AVI_Handel.CurrentImage > 0)
{
/*##-10- Calc the average decode frame rate #*/
FrameRate = (AVI_Handel.CurrentImage * 1000)/(HAL_GetTick() - startTime);
/* Display decoding info */
LCD_BriefDisplay();
}
}
else /* Can't Open avi file*/
{
printf("Can't Open avi file\r\n");
}
}
3、DMA2D_CopyBuffer
用DMA2D显示在LCD上
static void DMA2D_CopyBuffer(uint32_t *pSrc, uint32_t *pDst, uint16_t ImageWidth, uint16_t ImageHeight)
{
//uint16_t timeout=0;
uint32_t xPos, yPos, destination;
/*##-1- calculate the destination transfer address ############*/
xPos = (LCD_X_Size - JPEG_Info.ImageWidth)/2;
yPos = (LCD_Y_Size - JPEG_Info.ImageHeight)/2;
destination = (uint32_t)pDst + ((yPos * LCD_Y_Size) + xPos) * 4;
HAL_DMA2D_PollForTransfer(&DMA2D_Handle, 25); /* wait for the previous DMA2D transfer to ends */
/* copy the new decoded frame to the LCD Frame buffer*/
HAL_DMA2D_Start(&DMA2D_Handle, (uint32_t)pSrc, destination, ImageWidth, ImageHeight);
}
4、DMA2D_Init
DMA2D初始化
static void DMA2D_Init(uint16_t xsize, uint16_t ysize, uint32_t ChromaSampling)
{
uint32_t cssMode = JPEG_420_SUBSAMPLING, inputLineOffset = 0;
if(ChromaSampling == JPEG_420_SUBSAMPLING)
{
cssMode = DMA2D_CSS_420;
inputLineOffset = xsize % 16;
if(inputLineOffset != 0)
{
inputLineOffset = 16 - inputLineOffset;
}
}
else if(ChromaSampling == JPEG_444_SUBSAMPLING)
{
cssMode = DMA2D_NO_CSS;
inputLineOffset = xsize % 8;
if(inputLineOffset != 0)
{
inputLineOffset = 8 - inputLineOffset;
}
}
else if(ChromaSampling == JPEG_422_SUBSAMPLING)
{
cssMode = DMA2D_CSS_422;
inputLineOffset = xsize % 16;
if(inputLineOffset != 0)
{
inputLineOffset = 16 - inputLineOffset;
}
}
/*##-1- Configure the DMA2D Mode, Color Mode and output offset #############*/
DMA2D_Handle.Init.Mode = DMA2D_M2M_PFC;
DMA2D_Handle.Init.ColorMode = DMA2D_OUTPUT_ARGB8888;
DMA2D_Handle.Init.OutputOffset = LCD_X_Size - xsize;
DMA2D_Handle.Init.AlphaInverted = DMA2D_REGULAR_ALPHA; /* No Output Alpha Inversion*/
DMA2D_Handle.Init.RedBlueSwap = DMA2D_RB_REGULAR; /* No Output Red & Blue swap */
/*##-2- DMA2D Callbacks Configuration ######################################*/
DMA2D_Handle.XferCpltCallback = NULL;
/*##-3- Foreground Configuration ###########################################*/
DMA2D_Handle.LayerCfg[1].AlphaMode = DMA2D_REPLACE_ALPHA;
DMA2D_Handle.LayerCfg[1].InputAlpha = 0xFF;
DMA2D_Handle.LayerCfg[1].InputColorMode = DMA2D_INPUT_YCBCR;
DMA2D_Handle.LayerCfg[1].ChromaSubSampling = cssMode;
DMA2D_Handle.LayerCfg[1].InputOffset = inputLineOffset;
DMA2D_Handle.LayerCfg[1].RedBlueSwap = DMA2D_RB_REGULAR; /* No ForeGround Red/Blue swap */
DMA2D_Handle.LayerCfg[1].AlphaInverted = DMA2D_REGULAR_ALPHA; /* No ForeGround Alpha inversion */
DMA2D_Handle.Instance = DMA2D;
/*##-4- DMA2D Initialization ###########################################*/
HAL_DMA2D_Init(&DMA2D_Handle);
HAL_DMA2D_ConfigLayer(&DMA2D_Handle, 1);
}
四、stm32h7xx_hal_msp.c
extern MDMA_HandleTypeDef hmdma_jpeg_infifo_th;
extern MDMA_HandleTypeDef hmdma_jpeg_outfifo_th;
void HAL_JPEG_MspInit(JPEG_HandleTypeDef* hjpeg)
{
if(hjpeg->Instance==JPEG)
{
/* USER CODE BEGIN JPEG_MspInit 0 */
/* USER CODE END JPEG_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_JPEG_CLK_ENABLE();
/* JPEG MDMA Init */
/* JPEG_INFIFO_TH Init */
hmdma_jpeg_infifo_th.Instance = MDMA_Channel7;
hmdma_jpeg_infifo_th.Init.Request = MDMA_REQUEST_JPEG_INFIFO_TH;
hmdma_jpeg_infifo_th.Init.TransferTriggerMode = MDMA_BUFFER_TRANSFER;
hmdma_jpeg_infifo_th.Init.Priority = MDMA_PRIORITY_HIGH;
hmdma_jpeg_infifo_th.Init.Endianness = MDMA_LITTLE_ENDIANNESS_PRESERVE;
hmdma_jpeg_infifo_th.Init.SourceInc = MDMA_SRC_INC_BYTE;
hmdma_jpeg_infifo_th.Init.DestinationInc = MDMA_DEST_INC_DISABLE;
hmdma_jpeg_infifo_th.Init.SourceDataSize = MDMA_SRC_DATASIZE_BYTE;
hmdma_jpeg_infifo_th.Init.DestDataSize = MDMA_DEST_DATASIZE_WORD;
hmdma_jpeg_infifo_th.Init.DataAlignment = MDMA_DATAALIGN_PACKENABLE;
hmdma_jpeg_infifo_th.Init.BufferTransferLength = 32;
hmdma_jpeg_infifo_th.Init.SourceBurst = MDMA_SOURCE_BURST_32BEATS;
hmdma_jpeg_infifo_th.Init.DestBurst = MDMA_DEST_BURST_16BEATS;
hmdma_jpeg_infifo_th.Init.SourceBlockAddressOffset = 0;
hmdma_jpeg_infifo_th.Init.DestBlockAddressOffset = 0;
if (HAL_MDMA_Init(&hmdma_jpeg_infifo_th) != HAL_OK)
{
Error_Handler();
}
if (HAL_MDMA_ConfigPostRequestMask(&hmdma_jpeg_infifo_th, 0, 0) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(hjpeg,hdmain,hmdma_jpeg_infifo_th);
/* JPEG_OUTFIFO_TH Init */
hmdma_jpeg_outfifo_th.Instance = MDMA_Channel6;
hmdma_jpeg_outfifo_th.Init.Request = MDMA_REQUEST_JPEG_OUTFIFO_TH;
hmdma_jpeg_outfifo_th.Init.TransferTriggerMode = MDMA_BUFFER_TRANSFER;
hmdma_jpeg_outfifo_th.Init.Priority = MDMA_PRIORITY_VERY_HIGH;
hmdma_jpeg_outfifo_th.Init.Endianness = MDMA_LITTLE_ENDIANNESS_PRESERVE;
hmdma_jpeg_outfifo_th.Init.SourceInc = MDMA_SRC_INC_DISABLE;
hmdma_jpeg_outfifo_th.Init.DestinationInc = MDMA_DEST_INC_BYTE;
hmdma_jpeg_outfifo_th.Init.SourceDataSize = MDMA_SRC_DATASIZE_WORD;
hmdma_jpeg_outfifo_th.Init.DestDataSize = MDMA_DEST_DATASIZE_BYTE;
hmdma_jpeg_outfifo_th.Init.DataAlignment = MDMA_DATAALIGN_PACKENABLE;
hmdma_jpeg_outfifo_th.Init.BufferTransferLength = 32;
hmdma_jpeg_outfifo_th.Init.SourceBurst = MDMA_SOURCE_BURST_32BEATS;
hmdma_jpeg_outfifo_th.Init.DestBurst = MDMA_DEST_BURST_32BEATS;
hmdma_jpeg_outfifo_th.Init.SourceBlockAddressOffset = 0;
hmdma_jpeg_outfifo_th.Init.DestBlockAddressOffset = 0;
if (HAL_MDMA_Init(&hmdma_jpeg_outfifo_th) != HAL_OK)
{
Error_Handler();
}
if (HAL_MDMA_ConfigPostRequestMask(&hmdma_jpeg_outfifo_th, 0, 0) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(hjpeg,hdmaout,hmdma_jpeg_outfifo_th);
/* JPEG interrupt Init */
HAL_NVIC_SetPriority(JPEG_IRQn, 0x07, 0x0F);
HAL_NVIC_EnableIRQ(JPEG_IRQn);
/* USER CODE BEGIN JPEG_MspInit 1 */
/* USER CODE END JPEG_MspInit 1 */
}
}
void HAL_JPEG_MspDeInit(JPEG_HandleTypeDef* hjpeg)
{
if(hjpeg->Instance==JPEG)
{
/* USER CODE BEGIN JPEG_MspDeInit 0 */
/* USER CODE END JPEG_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_JPEG_CLK_DISABLE();
/* JPEG MDMA DeInit */
HAL_MDMA_DeInit(hjpeg->hdmain);
HAL_MDMA_DeInit(hjpeg->hdmaout);
/* JPEG interrupt DeInit */
HAL_NVIC_DisableIRQ(JPEG_IRQn);
/* USER CODE BEGIN JPEG_MspDeInit 1 */
/* USER CODE END JPEG_MspDeInit 1 */
}
}
五、中断处理
extern JPEG_HandleTypeDef hjpeg;
extern DMA2D_HandleTypeDef DMA2D_Handle;
void JPEG_IRQHandler(void)
{
/* USER CODE BEGIN JPEG_IRQn 0 */
/* USER CODE END JPEG_IRQn 0 */
HAL_JPEG_IRQHandler(&hjpeg);
/* USER CODE BEGIN JPEG_IRQn 1 */
/* USER CODE END JPEG_IRQn 1 */
}
void MDMA_IRQHandler(void)
{
/* USER CODE BEGIN MDMA_IRQn 0 */
HAL_MDMA_IRQHandler(hjpeg.hdmain);
HAL_MDMA_IRQHandler(hjpeg.hdmaout);
/* USER CODE END MDMA_IRQn 0 */
/* HAL_MDMA_IRQHandler(&hmdma_jpeg_outfifo_th);
HAL_MDMA_IRQHandler(&hmdma_jpeg_infifo_th);*/
/* USER CODE BEGIN MDMA_IRQn 1 */
/* USER CODE END MDMA_IRQn 1 */
}
void DMA2D_IRQHandler(void)
{
/* USER CODE BEGIN DMA2D_IRQn 0 */
HAL_DMA2D_IRQHandler(&DMA2D_Handle);
/* USER CODE END DMA2D_IRQn 0 */
/* USER CODE BEGIN DMA2D_IRQn 1 */
/* USER CODE END DMA2D_IRQn 1 */
}
六、将AVI文件烧录到0x90000000
打开STM32 ST-LINK Utility
External Loader->add External Loader 增加外部FLASH:
擦除flash内容:
将AVI(改名.bin)文件烧录到0x90000000
这样视频文件就准备好了。
C:\Users\自己的用户名\STM32Cube\Repository\STM32Cube_FW_H7_V1.12.0\Utilities\Media\Video下有一些AVI文件
七、源码
AVI_TEST.part01.rar
(10 MB)
AVI_TEST.part02.rar
(10 MB)
AVI_TEST.part03.rar
(10 MB)
AVI_TEST.part04.rar
(10 MB)
AVI_TEST.part05.rar
(7.32 MB)
|