打印

【F769_DISCOVERY】官方JPEG Demo学习笔记

[复制链接]
1916|5
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
拿到F769_DISCOVERY好一阵子了,刚好到年底,事情又非常多,这两天终于抽于时间,好好学习一下。

首先想学习的是F769_DISCOVERY的特色JPEG硬解,但是看了论坛上几个关于JPEG硬件的帖子,都只是蜻蜓点水,没有深入讲解。

所以,我花了两个晚上的时间,尝试去弄懂官方的demo是如何运作,并记录了相关的学习笔记。

希望那些想尝试使用JPEG硬解,但是又无从下手的朋友们,可以从我这个帖子获得帮助。

demo 位于以下路径:STM32Cube_FW_F7_V1.5.1\Projects\STM32F769I-Discovery\Examples\JPEG\JPEG_DecodingUsingFs_DMA

该Demo从大的框架上分为以下个步骤:
1.硬件初始化,声明4个JPEG解码图像buffer,2个作为JPEG解码图像的输入buffer,2个作为JPEG解码图像的输出buffer


2.注册FatFs文件系统


3.从SD卡内读取image.jpg图片,分别读取4096字节数据填入输入的buffer


4.调用JPEG_Decode_DMA,开始对输入buffer1进行解码,输出的数据源填入输出buffer1


5.解码完成调用HAL_JPEG_DataReadyCallback()中断回调函数,该回调里输出buffer1被填满,切换图像输出buffer2,继续解码。


6.在解码的同时,在main()函数里有个小循环,在不断判断输出buffer是否填满,如果满了,则将解码出来的数据填入
#define JPEG_OUTPUT_DATA_BUFFER  0xC0200000 这段内存当中。


7.当图像buffer1的数据完全解码完成后,则系统调用HAL_JPEG_GetDataCallback()中断回调函数,函数内把输入buffer1状态标志高为空,并切换输入buffer2作为解码缓存。


8.重复5、6,7,直到解码全部完成。


9.把JPEG_OUTPUT_DATA_BUFFER 内存当中的图像送入到lcd显示



进行分析代码前,需要了解以下几点。
1.首先需要知道几个知识点ARGB8888,RGB888,RGB565的区别:
ARGB8888:比RGB888多了一个A,这个A是透明度Alpha,R是红色,G是绿色,B是蓝色,每个通道点一个字节,所以一个像素4个字节。
RGB888:和上面一样,不同点是每个像素占3个字节。
RGB565:比较特殊,但是原理差不多,就是R占5bit,G占3bit,组成一个字节,然后是G点3bit,B点5bit又组成1个字节,共2个字节。
RGB565的排列如下:RRRRRGGG GGGBBBBB,共两个字节(如果想提取其中一种颜色,很简单,作移位操作便可以了)

2.该Demo使用的屏幕色彩格式是ARGB8888,所以解码格式也为ARGB8888

3.很多朋友不知道图像大小是怎么计算的,这里也介绍一下:
拿320*240分辨率为例子,说明宽为320,长为240,意思是图像宽有320个像素,长是240行,所以总共有76800个像素,所以对于不同图像格式,所占的大小也不一样
ARGB8888:  76800*4=307200字节
RGB888:      76800*3=230400字节
RGB565:      76800*2=153600字节



下面开始代码分析:
1.图像buffer
typedef struct
{
  uint8_t State;  
  uint8_t *DataBuffer;
  uint32_t DataBufferSize;

}JPEG_Data_BufferTypeDef;
这里的图像buffer使用的是一个结构体,里面有三个成员变量,分别是:
uint8_t state:当前buffer的状态(满或者空
uint8_t *DataBuffer:buffer的内存指针
uint32_t DataBufferSize; buffer内的数据大小



2.很多人看到demo里面,肯定对官方人员对buffer的大小设置感到非常奇怪,代码如下:
#define CHUNK_SIZE_IN  ((uint32_t)(4096))               //输入buffer大小为4096
#define CHUNK_SIZE_OUT ((uint32_t)(768))             //输出buffer大小为768

uint8_t MCU_Data_OutBuffer0[CHUNK_SIZE_OUT];    //输入buffer1
uint8_t MCU_Data_OutBuffer1[CHUNK_SIZE_OUT];   //输入buffer2

uint8_t JPEG_Data_InBuffer0[CHUNK_SIZE_IN];       //输出buffer1
uint8_t JPEG_Data_InBuffer1[CHUNK_SIZE_IN];      //输出buffer2
很奇怪吧,为什么输入buffer大小为4096,输出buffer大小为768,4096还能理解为4k个字节,但是768是什么鬼?
刚开始我也很奇怪,后来在选择图片分辨率的时候,突然想明白了:
通常的话,我们会选择以下三种分辨率图像:800*480、320*240、160*120
三者的像素点分别为:38400,76800 ,19200 ,而768是这三个数的最大公约数
这样做的好处是解码次数是整数,而且输出buffer也刚好填满


3.感觉论坛贴代码的显示太难看了,还是直接贴图片吧

这个函数有2个作用:
作用1:通过f_read函数,读取4096字节Image.jpg数据到buffer1,第二次循环再读取4096字节数据到buffer2
作用2:HAL_JPEG_Decode_DMA()调用硬件JPEG解码,通过DMA传输,这里几个参数的意义解释一下:
hjpeg:当前的jpeg结构体实例
Jpeg_IN_BufferTab[0].DataBuffer:输入buffer1的地址,里面是要解码的数据
Jpeg_IN_BufferTab[0].DataBufferSize:输入buffer1里的数据大小
peg_OUT_BufferTab[0].DataBuffer :     输出buffer1的地址
CHUNK_SIZE_OUT:                              要输出到输入buffer1的数据大小

4.以下这个是解码中断回调函数,那什么时候会产生这个中断呢?当解码了CHUNK_SIZE_OUT个数据后便产生这个中断。

作用1:当产生这个中断后,便说明输出buffer1被填满了,里面共有CHUNK_SIZE_OUT个数据,把输出buffer1的标志位设为满。
转换buffer2继续填。
作用2:当另外一个buffer已经是填满状态时,刚暂停当前解码转换。

5.以下while()的位于main,作用是一直读取输出buffer的状态,如果被填满,则把里面的读取送到FrameBufferAddress的内存当中

作用2是,当输出buffer数据已经转移到FrameBufferAddress,则判断当前JPEG转换是否被停止了,是的话就恢复解码


6.下面这个也是中断回调函数,什么时候调用呢?前面使用HAL_JPEG_Decode_DMA()开始解码时,传入输入buffer1的数据大小,如果输入buffer1的数据全部解码完成了,则调用这个中断。

作用1:当输入buffer的数据解码完成后, 则切换为输入buffer2,继续解码。把buffer1的标志高为空。
作用2:判断两个buffer标志,如果都为空,则暂停解码。

7.也在那个小while()里面调用,顺着上面6的步骤,如果输入buffer1标志为空,刚继续通过f_read()从sd的image.jpg读取数据到输入buffer1.

作用2:判断解码是否暂停了,是的话恢复解码。

8.最后解码完成,就调用最后的中断回调函数


解码的过程就是这样,后面代码是LCD显示,这里不作介绍了。

OK,基本的问题点在上面都有提到,相信对大家有一定的帮助,后面我会对这个demo进行改写,比如数据源获取,现在是通过SD卡,我想改成用串口获取,然后通过LCD直接显示。

相关帖子

沙发
巧克力娃娃| | 2017-1-16 14:14 | 只看该作者
非常地清晰条理~

使用特权

评论回复
板凳
dawei360| | 2017-2-4 13:56 | 只看该作者
解释的很清楚

使用特权

评论回复
地板
zhanzr21| | 2017-2-4 21:00 | 只看该作者
好,对buffer的来龙去脉讲的很清楚!

使用特权

评论回复
5
619888476| | 2017-2-7 12:05 | 只看该作者
非常不错!

使用特权

评论回复
6
colin2135|  楼主 | 2017-2-7 13:48 | 只看该作者
谢谢支持

使用特权

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

本版积分规则

144

主题

533

帖子

8

粉丝