264|1

171

帖子

0

资源

一粒金砂(中级)

【STM32F769Discovery开发板试用】SD卡文件系统应用&让人自闭的硬解JPEG解码代码

本帖最后由 donatello1996 于 2020-7-25 16:41 编辑

        在移植官方硬解JPEG代码的时候出现了严重问题,首先官方JPEG解码能够正常显示的文件跟BMP数组同样只支持16位和24位颜色,也就是只能成功显示图片格式为16位和24位颜色的JPEG文件,然后就是官方的JPEG解码代码工程只是用于Demo展示,没有进一步完善,官方代码中仅仅是显示一次JPEG图片,什么为之显示一次呢,如果把GPU显存看成容器,把JPEG硬件解码看成从扎着结的米袋中往容器倒米,那么目前移植的代码就是只能将米袋的结解开倒一次米,不能将米从容器中倒出来再装一次,也就是从头到尾容器只能使用一次,即使是同一张图片的数据也只能刷一次,归结到底,一是官方的代码没有做容器的复位,导致想要显示多次图片的时候会出现BUG,我目前是修复了这个BUG,不过有更多的BUG,官方的代码问题实在是多,而且显示也不稳定,不想搞了,明天有空移植一份原子的硬解JPEG代码算了,免得造轮子,一个试用活动搞得自己这么累,不值得,直接移植能用的代码不香吗?

       要想实现JPEG解码,第一步当然是搭建文件系统,这步非常简单,即使是从ST官方那晦涩难懂的代码中直接移植也不会有什么大问题,步骤如下:
第一,将所有FATFS文件系统相关驱动库函数全部移植,并修复其中的#include包含报错和stdint语法报错,能用volatile就不用__IO关键字,并尽量将core_cm7.h中一些关键定义用第三方头文件替代,减少工程对KEIL安装目录ARMCC交叉工具链的的依赖:

4.jpg

-其中ff.c是定义了一些文件系统常用函数,如f_open(),f_read(),f_close()之类的,用法跟stdio/stdlib的同名函数差不多,这个代码是抽象层代码,是前辈写好供大家直接调用的,不需要关心实现;ff_gen_dev.c是定义上述操作的几个相关接口;diskio.c是对于可访问磁盘设备的结构体函数抽象;所有支持文件系统的单片机都用同样的这三份文件;

-真正要根据每个不同单片机平台要修改的文件就是sd_diskio.c,当然名字可以变,比如用U盘的就叫usbms_diskio.c,用SPI FLASH的就叫spiflash_diskio.c,用NAND的就叫nand_diskio.c,里面就是将具体操作存储设备的底层函数挂载到diskio.c和ff_gen_dev.c的抽象层中。

然后是头文件,ffconf.h等。

成功挂载SD卡文件系统之后就可以进行一些简单的文件读写操作了,读写文件之前需要打开文件,f_open()函数的目标操作结构体是FIL:

typedef struct {
	_FDID	obj;			/* Object identifier (must be the 1st member to detect invalid object pointer) */
	BYTE	flag;			/* File status flags */
	BYTE	err;			/* Abort flag (error code) */
	FSIZE_t	fptr;			/* File read/write pointer (Zeroed on file open) */
	DWORD	clust;			/* Current cluster of fpter (invalid when fptr is 0) */
	DWORD	sect;			/* Sector number appearing in buf[] (0:invalid) */
#if !_FS_READONLY
	DWORD	dir_sect;		/* Sector number containing the directory entry */
	BYTE*	dir_ptr;		/* Pointer to the directory entry in the win[] */
#endif
#if _USE_FASTSEEK
	DWORD*	cltbl;			/* Pointer to the cluster link map table (nulled on open, set by application) */
#endif
#if !_FS_TINY
	BYTE	buf[_MAX_SS];	/* File private data read/write window */
#endif
} FIL;

然后使用f_read就可以读取文件内容了。举一个非常简单的栗子,读取TXT文件内容:

if(f_open(&TXT_File, "1txt.txt", FA_READ) == FR_OK)
{
  f_read(&TXT_File,TXT_File.buf,10,&b);
  printf("TXT_File:%d %d %s\n",b,TXT_File.buf);
  f_close(&TXT_File);
}

JPEG硬件解码,官方代码采用的方式是双通道DMA,一个DMA用于从JPEG文件中读取数据流到内存中,另一个DMA用于从内存中解码完毕的BMP数组搬运到GPU显存中显示,所以需要两个DMA中断服务函数:

void DMA2_Stream3_IRQHandler(void)
{
  HAL_DMA_IRQHandler(JPEG_Handle.hdmain);
}

void DMA2_Stream4_IRQHandler(void)
{
  HAL_DMA_IRQHandler(JPEG_Handle.hdmaout);
}

然后JPEG硬解码也需要一个中断服务函数:

void JPEG_IRQHandler(void)
{
  HAL_JPEG_IRQHandler(&JPEG_Handle);
}

至此,JPEG文件就可以正常解码并输出到GPU显存中了,STM32F769的JPEG硬件解码库是怎么工作的,我们不需要关心,我们需要操作的是JPEG原始数据和JPEG成功解码数据的输送,特别注意的是我在官方的代码中加了一行,以便支持:

int JPEG_OutputHandler(JPEG_HandleTypeDef *hjpeg)
{
  int ConvertedDataCount;
  
  if(Jpeg_OUT_BufferTab[JPEG_OUT_Read_BufferIndex].State == JPEG_BUFFER_FULL)
  {  
    MCU_BlockIndex += pConvert_Function(Jpeg_OUT_BufferTab[JPEG_OUT_Read_BufferIndex].DataBuffer, (unsigned char *)FrameBufferAddress, MCU_BlockIndex, Jpeg_OUT_BufferTab[JPEG_OUT_Read_BufferIndex].DataBufferSize, &ConvertedDataCount);   
    
    Jpeg_OUT_BufferTab[JPEG_OUT_Read_BufferIndex].State = JPEG_BUFFER_EMPTY;
    Jpeg_OUT_BufferTab[JPEG_OUT_Read_BufferIndex].DataBufferSize = 0;
    
    JPEG_OUT_Read_BufferIndex++;
    if(JPEG_OUT_Read_BufferIndex >= NB_OUTPUT_DATA_BUFFERS)
    {
      JPEG_OUT_Read_BufferIndex = 0;
    }
    
    if(MCU_BlockIndex == MCU_TotalNb)
    {
      MCU_BlockIndex=0;
      //添加的一行
      return 1;
    }
  }
  else if((Output_Is_Paused == 1) && \
          (Jpeg_OUT_BufferTab[JPEG_OUT_Write_BufferIndex].State == JPEG_BUFFER_EMPTY) &&\
          (Jpeg_OUT_BufferTab[JPEG_OUT_Read_BufferIndex].State == JPEG_BUFFER_EMPTY))
  {
		
    Output_Is_Paused = 0;
    HAL_JPEG_Resume(hjpeg, JPEG_PAUSE_RESUME_OUTPUT);            
  }
  return 0;  
}

void JPEG_InputHandler(JPEG_HandleTypeDef *hjpeg)
{
  if(Jpeg_IN_BufferTab[JPEG_IN_Write_BufferIndex].State == JPEG_BUFFER_EMPTY)
  {
    if(f_read (pFile, Jpeg_IN_BufferTab[JPEG_IN_Write_BufferIndex].DataBuffer , CHUNK_SIZE_IN, (UINT*)(&Jpeg_IN_BufferTab[JPEG_IN_Write_BufferIndex].DataBufferSize)) == FR_OK)
    {  
      Jpeg_IN_BufferTab[JPEG_IN_Write_BufferIndex].State = JPEG_BUFFER_FULL;
    }
    else
    {
      
    }
    
    if((Input_Is_Paused == 1) && (JPEG_IN_Write_BufferIndex == JPEG_IN_Read_BufferIndex))
    {
      Input_Is_Paused = 0;
      HAL_JPEG_ConfigInputBuffer(hjpeg,Jpeg_IN_BufferTab[JPEG_IN_Read_BufferIndex].DataBuffer, Jpeg_IN_BufferTab[JPEG_IN_Read_BufferIndex].DataBufferSize);    
  
      HAL_JPEG_Resume(hjpeg, JPEG_PAUSE_RESUME_INPUT); 
    }
    
    JPEG_IN_Write_BufferIndex++;
    if(JPEG_IN_Write_BufferIndex >= NB_INPUT_DATA_BUFFERS)
    {
      JPEG_IN_Write_BufferIndex = 0;
    }            
  }
}

我自己封装了一个JPEG解码的函数:

int Show_JPEG_File(JPEG_HandleTypeDef *hjpeg,unsigned char *path,JPEG_ConfTypeDef* hjinfo)
{
	unsigned char jpeg_done = 0;
	int xpos = 0, ypos = 0;
	uint32_t width_offset = 0;
	FIL fil;
	hjpeg->Instance = JPEG;
  HAL_JPEG_Init(hjpeg);  
	if(f_open(&fil,(const char*)path, FA_READ) == FR_OK)
	{
		JPEG_Decode_DMA(hjpeg, &fil, 0xC0200000);
		do
		{
			JPEG_InputHandler(hjpeg);
			jpeg_done = JPEG_OutputHandler(hjpeg);
		}while(jpeg_done == 0);
		
		HAL_JPEG_GetInfo(hjpeg,hjinfo);       
		
		xpos = (800 - hjinfo->ImageWidth)/2;
		ypos = (480 - hjinfo->ImageHeight)/2;        

		if(hjinfo->ChromaSubsampling == JPEG_420_SUBSAMPLING)
		{
			if((hjinfo->ImageWidth % 16) != 0)
			width_offset = 16 - (hjinfo->ImageWidth % 16);
		}

		if(hjinfo->ChromaSubsampling == JPEG_422_SUBSAMPLING)
		{
			if((hjinfo->ImageWidth % 16) != 0)
			width_offset = 16 - (hjinfo->ImageWidth % 16);
		}

		if(hjinfo->ChromaSubsampling == JPEG_444_SUBSAMPLING)
		{
			if((hjinfo->ImageWidth % 8) != 0)
			width_offset = (hjinfo->ImageWidth % 8);
		}

		DMA2D_CopyBuffer((uint32_t *)0xC0200000, (uint32_t *)0xC0000000, 
		xpos , ypos, hjinfo->ImageWidth, hjinfo->ImageHeight, width_offset);
		
		f_close(&fil);  
	}
}

注意还要msp初始化,千万别漏了,这个是weak弱函数,由HAL主控代码调用:

void HAL_JPEG_MspInit(JPEG_HandleTypeDef *hjpeg)
{
  static DMA_HandleTypeDef   hdmaIn;
  static DMA_HandleTypeDef   hdmaOut;
  
  /* Enable JPEG clock */
  __HAL_RCC_JPEG_CLK_ENABLE();
  
    /* Enable DMA clock */
  __HAL_RCC_DMA2_CLK_ENABLE();

  HAL_NVIC_SetPriority(JPEG_IRQn, 0x06, 0x0F);
  HAL_NVIC_EnableIRQ(JPEG_IRQn);
  
  /* Input DMA */    
  /* Set the parameters to be configured */
  hdmaIn.Init.Channel = DMA_CHANNEL_9;
  hdmaIn.Init.Direction = DMA_MEMORY_TO_PERIPH;
  hdmaIn.Init.PeriphInc = DMA_PINC_DISABLE;
  hdmaIn.Init.MemInc = DMA_MINC_ENABLE;
  hdmaIn.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
  hdmaIn.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
  hdmaIn.Init.Mode = DMA_NORMAL;
  hdmaIn.Init.Priority = DMA_PRIORITY_HIGH;
  hdmaIn.Init.FIFOMode = DMA_FIFOMODE_ENABLE;         
  hdmaIn.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
  hdmaIn.Init.MemBurst = DMA_MBURST_INC4;
  hdmaIn.Init.PeriphBurst = DMA_PBURST_INC4;      
  
  hdmaIn.Instance = DMA2_Stream3;
  
  /* Associate the DMA handle */
  __HAL_LINKDMA(hjpeg, hdmain, hdmaIn);
  
  HAL_NVIC_SetPriority(DMA2_Stream3_IRQn, 0x07, 0x0F);
  HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn);    
  
  /* DeInitialize the DMA Stream */
  HAL_DMA_DeInit(&hdmaIn);  
  /* Initialize the DMA stream */
  HAL_DMA_Init(&hdmaIn);
  
  
  /* Output DMA */
  /* Set the parameters to be configured */ 
  hdmaOut.Init.Channel = DMA_CHANNEL_9;
  hdmaOut.Init.Direction = DMA_PERIPH_TO_MEMORY;
  hdmaOut.Init.PeriphInc = DMA_PINC_DISABLE;
  hdmaOut.Init.MemInc = DMA_MINC_ENABLE;
  hdmaOut.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
  hdmaOut.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
  hdmaOut.Init.Mode = DMA_NORMAL;
  hdmaOut.Init.Priority = DMA_PRIORITY_VERY_HIGH;
  hdmaOut.Init.FIFOMode = DMA_FIFOMODE_ENABLE;         
  hdmaOut.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
  hdmaOut.Init.MemBurst = DMA_MBURST_INC4;
  hdmaOut.Init.PeriphBurst = DMA_PBURST_INC4;

  
  hdmaOut.Instance = DMA2_Stream4;
  /* DeInitialize the DMA Stream */
  HAL_DMA_DeInit(&hdmaOut);  
  /* Initialize the DMA stream */
  HAL_DMA_Init(&hdmaOut);

  /* Associate the DMA handle */
  __HAL_LINKDMA(hjpeg, hdmaout, hdmaOut);
  
  HAL_NVIC_SetPriority(DMA2_Stream4_IRQn, 0x07, 0x0F);
  HAL_NVIC_EnableIRQ(DMA2_Stream4_IRQn);   
}

本来前两天还能正常显示图片的,现在又花屏了,都不知道什么原因,心累了,草(一种植物),放出代码工程让大佬们批评一下:

STM32F769.zip (3.46 MB, 下载次数: 4)


回复

3716

帖子

0

资源

版主

好的,正打算尝试这部分

回复
您需要登录后才可以回帖 登录 | 注册

关闭
站长推荐上一条 1/6 下一条

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区知春路23号集成电路设计园量子银座1305 电话:(010)82350740 邮编:100191

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号 电信业务审批[2006]字第258号函 京公海网安备110108001534 Copyright © 2005-2020 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表