- from PIL import Image
- import binascii
- import re
- def jpg_to_hex(file_path):
- # 打开图像
- with open(file_path, 'rb') as image_file:
- # 读取文件的二进制数据
- binary_data = image_file.read()
- # 将二进制数据转换为十六进制
- hex_data = binascii.hexlify(binary_data)
- #return hex_data
- # 转换为字符串
- hex_string = hex_data.decode('utf-8')
- return hex_string
- # 示例用法
- file_path = 'example.jpg'
- hex_representation = jpg_to_hex(file_path)
- #print(hex_representation)
- split_str = [hex_representation[i:i+2] for i in range(0, len(hex_representation), 2)]
- print(split_str)
JPEG解码流程:
在图形化配置界面,首先使能JPEG模块,然后使能HPDMA模块:
LTDC显示还是使用上一个工程的配置方式:
主函数通过调用MX_JPEG_Init -> HAL_JPEG_Init(&hjpeg)来初始化硬件JPEG模块
LTDC模块初始化代码:
- /**
- * [url=home.php?mod=space&uid=247401]@brief[/url] LTDC Initialization Function
- * @param None
- * @retval None
- */
- static void MX_LTDC_Init(void)
- {
- /* USER CODE BEGIN LTDC_Init 0 */
- /* USER CODE END LTDC_Init 0 */
- LTDC_LayerCfgTypeDef pLayerCfg = {0};
- /* USER CODE BEGIN LTDC_Init 1 */
- /* USER CODE END LTDC_Init 1 */
- hltdc.Instance = LTDC;
- hltdc.Init.HSPolarity = LTDC_HSPOLARITY_AL;
- hltdc.Init.VSPolarity = LTDC_VSPOLARITY_AL;
- hltdc.Init.DEPolarity = LTDC_DEPOLARITY_AL;
- hltdc.Init.PCPolarity = LTDC_PCPOLARITY_IPC;
- hltdc.Init.HorizontalSync = 3;
- hltdc.Init.VerticalSync = 3;
- hltdc.Init.AccumulatedHBP = 11;
- hltdc.Init.AccumulatedVBP = 11;
- hltdc.Init.AccumulatedActiveW = 811;
- hltdc.Init.AccumulatedActiveH = 491;
- hltdc.Init.TotalWidth = 819;
- hltdc.Init.TotalHeigh = 499;
- hltdc.Init.Backcolor.Blue = 0;
- hltdc.Init.Backcolor.Green = 0;
- hltdc.Init.Backcolor.Red = 0;
- if (HAL_LTDC_Init(&hltdc) != HAL_OK)
- {
- Error_Handler();
- }
- pLayerCfg.WindowX0 = 0;
- pLayerCfg.WindowX1 = 320;
- pLayerCfg.WindowY0 = 0;
- pLayerCfg.WindowY1 = 240;
- pLayerCfg.PixelFormat = LTDC_PIXEL_FORMAT_ARGB8888;
- pLayerCfg.Alpha = 127;
- pLayerCfg.Alpha0 = 0;
- pLayerCfg.BlendingFactor1 = LTDC_BLENDING_FACTOR1_CA;
- pLayerCfg.BlendingFactor2 = LTDC_BLENDING_FACTOR2_CA;
- pLayerCfg.FBStartAdress = (uint32_t)&image_data_Cloud_320_240;
- pLayerCfg.ImageWidth = 320;
- pLayerCfg.ImageHeight = 240;
- pLayerCfg.Backcolor.Blue = 0;
- pLayerCfg.Backcolor.Green = 0;
- pLayerCfg.Backcolor.Red = 0;
- if (HAL_LTDC_ConfigLayer(&hltdc, &pLayerCfg, 0) != HAL_OK)
- {
- Error_Handler();
- }
- /* USER CODE BEGIN LTDC_Init 2 */
- /* USER CODE END LTDC_Init 2 */
- }
主函数代码:
- int main(void)
- {
- /* USER CODE BEGIN 1 */
- uint32_t toBottom = 0;
- uint32_t toTop = 0;
- /* STM32H7RSxx HAL library initialization:
- - Systick timer is configured by default as source of time base, but user
- can eventually implement his proper time base source (a general purpose
- timer for example or other time source), keeping in mind that Time base
- duration should be kept 1ms since PPP_TIMEOUT_VALUEs are defined and
- handled in milliseconds basis.
- - Set NVIC Group Priority to 4
- - Low Level Initialization
- */
- MPU_Config();
- /* USER CODE END 1 */
- /* Enable the CPU Cache */
- /* Enable I-Cache---------------------------------------------------------*/
- SCB_EnableICache();
- /* Enable D-Cache---------------------------------------------------------*/
- SCB_EnableDCache();
- /* MCU Configuration--------------------------------------------------------*/
- /* Update SystemCoreClock variable according to RCC registers values. */
- SystemCoreClockUpdate();
- /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
- HAL_Init();
- /* USER CODE BEGIN Init */
- /* Initialize LD1 and LD2*/
- BSP_LED_Init(LD1);
- BSP_LED_Init(LD2);
- BSP_LED_Init(LD3);
- BSP_LED_Init(LD4);
- /* USER CODE END Init */
- /* USER CODE BEGIN SysInit */
- /* USER CODE END SysInit */
- /* Initialize all configured peripherals */
- MX_GPIO_Init();
- MX_HPDMA1_Init();
- MX_ADC1_Init();
- MX_USART1_UART_Init();
- MX_JPEG_Init();
- MX_LTDC_Init();
- /* USER CODE BEGIN 2 */
- tickstart = HAL_GetTick();
- JPEG_Decode_DMA(&hjpeg, (uint32_t)image_320_240_jpg, IMAGE_320_240_JPG_SIZE, (uint32_t) rawBuffer);
- while((Jpeg_HWDecodingEnd == 0) && ((HAL_GetTick() - tickstart) < 5000))
- {
- }
- if (Jpeg_HWDecodingEnd != 0)
- {
- /* Test ends properly */
- BSP_LED_On(LD1);
- }
- else
- {
- /* Test error */
- BSP_LED_On(LD2);
- }
- /* USER CODE END 2 */
- /* Infinite loop */
- /* USER CODE BEGIN WHILE */
- while(1){
- HAL_LTDC_SetAddress(&hltdc, (uint32_t)rawBuffer, 0);
- HAL_LTDC_Reload( &hltdc,LTDC_RELOAD_IMMEDIATE);
- BSP_LED_Toggle(LD3);
- BSP_LED_Toggle(LD4);
- HAL_Delay(1000);
- HAL_LTDC_SetAddress(&hltdc, (uint32_t)&image_data_Cloud_320_240, 0);
- HAL_LTDC_Reload( &hltdc,LTDC_RELOAD_IMMEDIATE);
- BSP_LED_Toggle(LD3);
- BSP_LED_Toggle(LD4);
- HAL_Delay(1000);
- }
- }
此外烧录到板子后发现解码的图片没有正常显示,可能是LTDC配置参数与解码结果不兼容,目前还在调试中。解码后的数据在调试界面看到也有数据。
https://www.bilibili.com/video/BV1m61vYaEVX/?spm_id_from=333.999.0.0&vd_source=69f2c95df791924c208b48e010bc38a7
【更新10月8日】之所以出现上述视频中的解码图片不能正常显示的问题,在于JPEG硬件解码输出的图片格式为YCbCr,而LTDC控制器所需要的输入图片格式为ARGB8888.因此,结合相关资料【AN4996】,利用DMA2D模块完成图片格式的转换,进而成功的在TFT屏幕上显示出解码后的JPEG图片。
首先在图形化配置界面使能DMA2D:
这里发现一个问题,DMA2D选择像素格式转换模式,然而InputColorMode无法选择“DMA2D_INPUT_YCBCR”,只能手动修改代码。
- static void MX_DMA2D_Init(void)
- {
- /* USER CODE BEGIN DMA2D_Init 0 */
- /* USER CODE END DMA2D_Init 0 */
- /* USER CODE BEGIN DMA2D_Init 1 */
- /* USER CODE END DMA2D_Init 1 */
- hdma2d.Instance = DMA2D;
- hdma2d.Init.Mode = DMA2D_M2M_PFC;
- hdma2d.Init.ColorMode = DMA2D_OUTPUT_ARGB8888;
- hdma2d.Init.OutputOffset = 0;
- hdma2d.LayerCfg[1].InputOffset = 0;
- hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_YCBCR;
- hdma2d.LayerCfg[1].AlphaMode = DMA2D_NO_MODIF_ALPHA;
- hdma2d.LayerCfg[1].InputAlpha = 0;
- hdma2d.LayerCfg[1].AlphaInverted = DMA2D_REGULAR_ALPHA;
- hdma2d.LayerCfg[1].RedBlueSwap = DMA2D_RB_REGULAR;
- hdma2d.LayerCfg[1].ChromaSubSampling = DMA2D_NO_CSS;
- if (HAL_DMA2D_Init(&hdma2d) != HAL_OK)
- {
- Error_Handler();
- }
- if (HAL_DMA2D_ConfigLayer(&hdma2d, 1) != HAL_OK)
- {
- Error_Handler();
- }
- /* USER CODE BEGIN DMA2D_Init 2 */
- /* USER CODE END DMA2D_Init 2 */
- }
硬件解码完成后,通过调用如下API来获取解码后的图片信息,包括Width与Height。
- HAL_JPEG_GetInfo(&hjpeg, &MyJPEGInfo);
接下来是重头戏,即完成像素颜色格式转换:
- DMA2D_Copy_YCbCr_To_RGB((uint32_t *)rawBuffer, /* JEPG解码后的数据 */
- (uint32_t *)rawBuffer_DMA2D_ARGB8888, /* 这里是显存地址 */
- 0 ,
- 0,
- MyJPEGInfo.ImageWidth,
- MyJPEGInfo.ImageHeight,
- LTDC_PIXEL_FORMAT_ARGB8888,
- MyJPEGInfo.ChromaSubsampling);
而这个函数 DMA2D_Copy_YCbCr_To_RGB的具体实现如下(参考:安富莱的例程):
- /*
- *********************************************************************************************************
- * 函 数 名: DMA2D_Copy_YCbCr_To_RGB
- * 功能说明: YCbCr转RGB输出
- * 形 参: pSrc: 数据源地址
- * pDst: 数据目的地址
- * x: X轴首地址
- * y: Y轴首地址
- * xsize: 目的区域的X轴大小,即每行像素数
- * ysize: 目的区域的Y轴大小,即行数
- * PixelFormat: 目标区颜色格式
- * ChromaSampling : YCbCr Chroma sampling : 4:2:0, 4:2:2 or 4:4:4
- * 返 回 值: 无
- *********************************************************************************************************
- */
- void DMA2D_Copy_YCbCr_To_RGB(uint32_t *pSrc,
- uint32_t *pDst,
- uint16_t x,
- uint16_t y,
- uint16_t xsize,
- uint16_t ysize,
- uint32_t PixelFormat,
- uint32_t ChromaSampling)
- {
- uint32_t cssMode = DMA2D_CSS_420;
- uint32_t inputLineOffset = 0;
- uint32_t destination = 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;
- }
- }
- /* 输出地址,特别注意末尾乘以2的对RGB565,如果输出格式是ARGB8888,需要乘以4 */
- destination = (uint32_t)pDst + ((y * 320) + x) * 4;
- /* DMA2D采用存储器到存储器模式,并且执行FPC颜色格式转换, 这种模式是前景层作为DMA2D输入 */
- DMA2D->CR = 0x00010000UL | (1 << 9);
- DMA2D->OOR = 320 - xsize;
- /* 输出格式 */
- DMA2D->OPFCCR = PixelFormat
- | (DMA2D_REGULAR_ALPHA << 20)
- | (DMA2D_RB_REGULAR << 21);
- /* 前景层输入格式 */
- DMA2D->FGPFCCR = DMA2D_INPUT_YCBCR
- | (DMA2D_REPLACE_ALPHA << 16)
- | (DMA2D_REGULAR_ALPHA << 20)
- | (DMA2D_RB_REGULAR << 21)
- | (0xFFU << 24)
- | (cssMode << 18);
- DMA2D->FGOR = inputLineOffset;
- DMA2D->NLR = (uint32_t)(xsize << 16) | (uint16_t)ysize;
- DMA2D->OMAR = (uint32_t)destination;
- DMA2D->FGMAR = (uint32_t)pSrc;
- /* 启动传输 */
- DMA2D->CR |= DMA2D_CR_START;
- /* 等待DMA2D传输完成 */
- while (DMA2D->CR & DMA2D_CR_START) {}
- }
接下来主循环中显示这个图片即可:
- while(1){
- HAL_LTDC_SetAddress(&hltdc, (uint32_t)rawBuffer_DMA2D_ARGB8888, 0);
- HAL_LTDC_Reload( &hltdc,LTDC_RELOAD_IMMEDIATE);
- BSP_LED_Toggle(LD3);
- BSP_LED_Toggle(LD4);
- HAL_Delay(1000);
- }
最终解码出的照片如下所示:
https://www.bilibili.com/video/BV1iC2JYME3f/?spm_id_from=333.999.list.card_archive.click&vd_source=69f2c95df791924c208b48e010bc38a7
参考:
1. 整理了一个STM32H7的寄存器方式YCbCr转RGB函数并DMA2D到显示屏,支持采样比4:4:4,4:2:2和4:2:0https://www.armbbs.cn/forum.php?mod=viewthread&tid=93536
2. https://www.st.com/content/ccc/resource/training/technical/product_training/group0/d7/07/45/76/c2/f9/41/4f/STM32H7-Peripheral-JPEG_codec_JPEG/files/STM32H7-Peripheral-JPEG_codec_JPEG.pdf/_jcr_content/translations/en.STM32H7-Peripheral-JPEG_codec_JPEG.pdf#:~:text=The%20JPEG%20codec%20integrated%20inside%20STM32%20products%20is%20an%20hardware
3. How to use the JPEG codec peripheral on STM32 MCUs https://www.st.com/content/ccc/resource/technical/document/application_note/group0/a5/9d/22/46/61/6d/4a/ab/DM00356635/files/DM00356635.pdf/jcr:content/translations/en.DM00356635.pdf#:~:text=Using%20the%20STM32H743/53/45/55/47/57/50xx%20and%20STM32H7Rx/7Sx%20devices%20for%20JPEG%20decoding%20operations,