打印
[STM32H7]

【STM32H745I-DISCO试用】12、播放视频

[复制链接]
257|7
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 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)




使用特权

评论回复
沙发
zhuomuniao110| | 2025-2-13 18:46 | 只看该作者
这个型号挺强的。

使用特权

评论回复
板凳
xionghaoyun| | 2025-2-14 09:10 | 只看该作者
兄弟 牛X 这个开发板有屏幕不错

使用特权

评论回复
地板
ningling_21| | 2025-2-15 13:33 | 只看该作者
这个搞的播放效果不错

使用特权

评论回复
5
xhackerustc| | 2025-2-15 16:59 | 只看该作者
你好,恰好也有一块这个板子,但发现个诡异的问题:SDRAM只能访问8MB,但按手册有16MB。你能否帮测一下你的板子能访问8MB以后的SDRAM么?

使用特权

评论回复
6
xhackerustc| | 2025-2-15 18:17 | 只看该作者
测试代码可见https://bbs.21ic.com/icview-3432362-1-1.html

使用特权

评论回复
7
sujingliang|  楼主 | 2025-2-16 10:38 | 只看该作者
xhackerustc 发表于 2025-2-15 16:59
你好,恰好也有一块这个板子,但发现个诡异的问题:SDRAM只能访问8MB,但按手册有16MB。你能否帮测一下你的 ...

我也遇到了内存地址不能大于0xD0800000,内存初始化用的BSP官方程序。

使用特权

评论回复
8
xhackerustc| | 2025-2-16 13:52 | 只看该作者
果然是共性问题,看来需要问下ST了

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

52

主题

103

帖子

0

粉丝