管理员
- 积分
- 165352
- 金钱
- 165352
- 注册时间
- 2010-12-1
- 在线时间
- 2108 小时
|
最近,很多客户问阿波罗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)
|
|