OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 14138|回复: 9

[F4开发板通用] 阿波罗STM32F4/F7开发板如何修改OV5640摄像头的输出帧率?以及撕裂现象说明

[复制链接]

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165352
金钱
165352
注册时间
2010-12-1
在线时间
2108 小时
发表于 2017-3-8 15:39:24 | 显示全部楼层 |阅读模式
最近,很多客户问阿波罗STM32F429或者阿波罗STM32F767开发板,接OV5640和RGB屏(4.3或7寸)的时候,有如下两个问题:
1,默认帧率只有15帧,我要30帧怎么办?
2,RGB屏显示,有撕裂现象,而且离摄像头越近,撕裂效果越明显。
今天我特地花时间研究了一下这个。这里统一做一个解答。

我们以阿波罗STM32F429,寄存器版本例程,摄像头实验,为基础进行修改。

首先,对第一个问题,帧率低。其原因为:
1,采用的竖屏,每次只能填充一列数据,填充效率低下。
2,输出图像帧率低(15帧)
要解决这个问题,我们得从这两方面着手。
首先,改为横屏,这个很简单,在lcd.c里面,LCD_Init函数的最后,修改
[mw_shl_code=c,true]//初始化lcd
//该初始化函数可以初始化各种型号的LCD(详见本.c文件最前面的描述)
void LCD_Init(void)
{         
        lcddev.id=LTDC_PanelID_Read();        //检查是否有RGB屏接入
        if(lcddev.id!=0)
        {
                ...//省略部分代码        
        }
        LCD_Display_Dir(1);                //默认为横屏
        LCD_LED=1;                                //点亮背光
        LCD_Clear(WHITE);
}  [/mw_shl_code]

修改LCD_Display_Dir函数的参数为1,即可改为横屏。


其次,在test.c函数里面,对RGB屏,我们默认用的1列一列的填充方式(改为横屏以后,即一行一行的填充),这种方式效率较低
我们修改核心代码如下:
[mw_shl_code=c,true]#define RGBLCD_TYPE           0                                //0,480*272(45帧);1,800*480(25帧);2,1024*600(25帧);

#if RGBLCD_TYPE==0                                                //对于480*272的屏幕
#define LCD_WID_VAL                480
#define LCD_LINE_INC        34

#elif RGBLCD_TYPE==1                                        //对于800*480的屏幕

#define LCD_WID_VAL                800
#define LCD_LINE_INC        30

#else                                                                        //对于1024*600的屏幕
#define LCD_WID_VAL                1024
#define LCD_LINE_INC        20

#endif


#define jpeg_line_size        LCD_WID_VAL*LCD_LINE_INC        //定义DMA接收数据时,一行数据的最大值

u32 dcmi_line_buf[2][jpeg_line_size];                                //RGB屏时,摄像头采用一行一行读取,定义行缓存
[/mw_shl_code]
原来的代码,jpeg_line_size的值是2*1024,即2K*2=4K字节,只做了1行缓存的设计,算上双缓冲,也才8K字节,存储容量不够大。
这里我们针对STM32F429内部内存最大为192K(不算CCM),再根据RGB屏的横向分辨率,来计算最大可以做多少行的双缓冲(LCD_LINE_INC*4,因为dcmi_line_buf是u32的,且为双缓冲数组,故*4)。
比如对480*272的RGB屏,最大可以做34*4行的双缓冲,即单个缓冲有68行,整个屏幕4个缓冲就可以缓冲完。

随后,修改rgblcd_dcmi_rx_callback和rgb565_test函数,以匹配填充方式的变化,修改后代码如下:
[mw_shl_code=c,true]//RGB屏数据接收回调函数
void rgblcd_dcmi_rx_callback(void)
{  
        u16 *pbuf;
        if(DMA2_Stream1->CR&(1<<19))//DMA使用buf1,读取buf0
        {
                pbuf=(u16*)dcmi_line_buf[0];
        }else                                                 //DMA使用buf0,读取buf1
        {
                pbuf=(u16*)dcmi_line_buf[1];
        }         
        LTDC_Color_Fill(0,curline,lcddev.width-1,curline+2*LCD_LINE_INC-1,pbuf);//DM2D填充
        if(curline<lcddev.height)curline+=2*LCD_LINE_INC;
}
//RGB565测试
//RGB数据直接显示在LCD上面
void rgb565_test(void)
{
        u8 key;
        u8 effect=0,contrast=2,fac;
        u8 scale=1;                //默认是全尺寸缩放
        u8 msgbuf[15];        //消息缓存区
        u16 outputheight=0;
        
        LCD_Clear(WHITE);
    POINT_COLOR=RED;
        LCD_ShowString(30,50,200,16,16,"Apollo STM32F4/F7");
        LCD_ShowString(30,70,200,16,16,"OV5640 RGB565 Mode");
        LCD_ShowString(30,100,200,16,16,"KEY0:Contrast");                        //对比度
        LCD_ShowString(30,120,200,16,16,"KEY1:Auto Focus");                 //执行自动对焦
        LCD_ShowString(30,140,200,16,16,"KEY2:Effects");                         //特效
        LCD_ShowString(30,160,200,16,16,"KEY_UP:FullSize/Scale");        //1:1尺寸(显示真实尺寸)/全尺寸缩放
        //自动对焦初始化
        OV5640_RGB565_Mode();        //RGB565模式        
        OV5640_Focus_Init();
        OV5640_Light_Mode(0);        //自动模式
        OV5640_Color_Saturation(3);//色彩饱和度0
        OV5640_Brightness(4);        //亮度0
        OV5640_Contrast(3);                //对比度0
        OV5640_Sharpness(33);        //自动锐度
        OV5640_Focus_Constant();//启动持续对焦
        DCMI_Init();                        //DCMI配置
        if(lcdltdc.pwidth!=0)        //RGB屏
        {
                dcmi_rx_callback=rgblcd_dcmi_rx_callback;//RGB屏接收数据回调函数
                DCMI_DMA_Init((u32)dcmi_line_buf[0],(u32)dcmi_line_buf[1],lcddev.width*LCD_LINE_INC,1,1);//DCMI DMA配置  
        }else                                        //MCU 屏
        {
                DCMI_DMA_Init((u32)&LCD->LCD_RAM,0,1,1,0);                        //DCMI DMA配置,MCU屏,竖屏
        }
        //TIM3->CR1&=~(0x01);                //关闭定时器3,关闭帧率统计,打开的话,RGB屏,在串口打印的时候,会抖89
        yoffset=0;
        outputheight=lcddev.height;
        if(lcddev.width>800)                //1024*600
        {
                OV5640_WR_Reg(0x3035,0X41);                //降低输出帧率,否则可能抖动         
        }else if(lcddev.width>480)        //800*480
        {
                OV5640_WR_Reg(0x3036,0x83);                //降低输出帧率,否则可能抖动
        }else                                                //480*270
        {        
                OV5640_WR_Reg(0x3035,0X21);                //提高输出帧率,到45帧
        }
        curline=yoffset;                //行数复位
        OV5640_OutSize_Set(4,0,lcddev.width,outputheight);                //满屏缩放显示
        DCMI_Start();                         //启动传输
        LCD_Clear(BLACK);
        while(1)
        {
                key=KEY_Scan(0);
                if(key)
                {
                      ......//省略部分代码
                }
                delay_ms(10);               
        }   
}[/mw_shl_code]
这样,代码修改就完成了,原理性的东西,大家参考代码自己理解即可。

然后,由于我们的RGB565输出,使用的是ov5640_rgb565_reg_tbl配置数组,默认设置的是15帧输出。
那么我们要提高帧率到30帧,可以通过提高频率来实现。
将原来的:
[mw_shl_code=c,true]//RGB565配置.15帧
//最大支持1280*800的RGB565图像输出
const u16 ov5640_rgb565_reg_tbl[][2]=
{
        0x4300, 0X6F,
        0X501F, 0x01,
        // 1280x800, 15fps
        // input clock 24Mhz, PCLK 42Mhz
        0x3035, 0x41, // PLL
        0x3036, 0x69, // PLL[/mw_shl_code]
改为:
[mw_shl_code=c,true]//RGB565配置.30帧
//最大支持1280*800的RGB565图像输出
const u16 ov5640_rgb565_reg_tbl[][2]=
{
        0x4300, 0X6F,
        0X501F, 0x01,
        // 1280x800, 15fps
        // input clock 24Mhz, PCLK 42Mhz
        0x3035, 0x31, // PLL
        0x3036, 0x9C, // PLL[/mw_shl_code]
即可以输出30帧的1280*800图像了。
这样,我们就提高了帧率。
修改0x3035这个寄存器的高四位,可以修改PLL的分频,值越小,帧率越大(前提是MCU可以正常读取,STM32F4的DCMI最大可以接收54M左右的PCLK,再大就要丢数据了)。
经过以上修改,对于不同的RGB屏,可以获得以下帧率:
480*272  RGB屏    45帧(默认是30帧,不过我们在rgb565_test函数,修改了0x3035寄存器,提高了帧率)
800*480  RGB屏    25帧
1024*600 RGB屏    25帧


不过很遗憾,即便帧率上到了45帧,也还是会看到撕裂现象,这就可以肯定,不是摄像头输出帧率慢,导致的撕裂了,而是SDRAM速度限制。
这就到了第二个问题,这是因为SDRAM的速度限制了。一旦一帧图像,你需要分几次传输到RGB屏的GRAM(即SDRAM),那么肯定就可以看到撕裂现象。
而且撕裂位置,就是每次传输的终点行(也可以称为下一次传输的起点行)。我们将480*272的RGB屏分成4块传输,这样,大范围移动拍摄目标的时候,
就会有3个(3条线,将屏幕分成4块)明显的撕裂位置。


拍摄物体靠的越近,撕裂越明显,这是因为拍摄物体靠近摄像头,一单物体位置发生变化,那么屏幕内变化的内容越多,看起来就越明显。


这样,问题原因找到了,那解决办法,就是必须有内存,可以缓存一帧的图像(然后再把这帧图像DMA传输给RGBLCD的GRAM),才不会出现撕裂现象,
在429上面,是不可以缓存一帧480*272图像的,但是F767是可以的,有兴趣的朋友,在我们代码的基础上,稍微修改,应该就可以实现F767驱动480*272不撕裂了。

最后,上传测试代码。 实验37 摄像头实验_横屏_帧率提高.rar (542.14 KB, 下载次数: 1117)
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

0

主题

5

帖子

0

精华

初级会员

Rank: 2

积分
61
金钱
61
注册时间
2019-8-9
在线时间
15 小时
发表于 2020-4-3 08:28:24 | 显示全部楼层
lzh771055370 发表于 2020-4-1 17:55
原子哥啊,我用H743照着你的方式改了,屏幕黑屏了,自己又修改了一下,设置数组大小480*34就是四分屏,480* ...

@@正点原子
回复 支持 1 反对 0

使用道具 举报

1

主题

4

帖子

0

精华

初级会员

Rank: 2

积分
72
金钱
72
注册时间
2014-7-15
在线时间
18 小时
发表于 2017-3-8 15:57:14 | 显示全部楼层
顶起!谢谢原子哥~
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165352
金钱
165352
注册时间
2010-12-1
在线时间
2108 小时
 楼主| 发表于 2017-3-22 11:36:12 | 显示全部楼层
后面发现,对4.3寸RGB屏,提高RGB屏的像素时钟,可以显著降低撕裂现象。
也就是RGB屏的刷屏速度,实际上导致了这个撕裂现象。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

4

主题

9

帖子

0

精华

新手入门

积分
13
金钱
13
注册时间
2017-4-3
在线时间
5 小时
发表于 2017-6-28 22:02:38 | 显示全部楼层
原子哥,按照你的方法修改完寄存器之后花屏,怎么办?
回复 支持 反对

使用道具 举报

8

主题

11

帖子

0

精华

初级会员

Rank: 2

积分
71
金钱
71
注册时间
2017-8-25
在线时间
17 小时
发表于 2017-10-28 21:47:26 | 显示全部楼层
如何改变输出的数据格式  想输出RAW RGB
回复 支持 反对

使用道具 举报

109

主题

5562

帖子

0

精华

资深版主

Rank: 8Rank: 8

积分
10541
金钱
10541
注册时间
2017-2-18
在线时间
1908 小时
发表于 2017-12-11 12:25:18 | 显示全部楼层
谢谢分享
回复 支持 反对

使用道具 举报

1

主题

4

帖子

0

精华

新手上路

积分
26
金钱
26
注册时间
2017-9-11
在线时间
3 小时
发表于 2018-2-2 14:38:48 | 显示全部楼层
我之前使用F407的时候,把图像窗口开小,再调成30帧的,用两个buf(一个buf存一帧)去缓冲,得到的效果虽然不是平行断层的那种效果,变成有斜纹的断层,而不是像楼主说的不会出现撕裂的现象
回复 支持 反对

使用道具 举报

2

主题

46

帖子

0

精华

初级会员

Rank: 2

积分
146
金钱
146
注册时间
2017-12-14
在线时间
31 小时
发表于 2018-10-9 21:08:08 | 显示全部楼层
OV2640能用相似的方法,去设置吗?
回复 支持 反对

使用道具 举报

0

主题

5

帖子

0

精华

初级会员

Rank: 2

积分
61
金钱
61
注册时间
2019-8-9
在线时间
15 小时
发表于 2020-4-1 17:55:23 | 显示全部楼层
原子哥啊,我用H743照着你的方式改了,屏幕黑屏了,自己又修改了一下,设置数组大小480*34就是四分屏,480*68就是如图二分屏了,再大编译就报错了,求指点现在该怎么改啊
QQ图片20200401175152.jpg
回复 支持 反对

使用道具 举报

2

主题

6

帖子

0

精华

新手上路

积分
32
金钱
32
注册时间
2019-10-28
在线时间
8 小时
发表于 2021-10-29 08:33:13 | 显示全部楼层
H7按照上面的方法试了一下,显示异常
回复 支持 反对

使用道具 举报

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

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-24 22:57

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表