【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交叉工具链的的依赖:
-其中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, 下载次数: 12)
|