本篇文章为车道线检测模型系列文章的第三篇,第一篇介绍了模型所使用的单片机和开发板,第二篇介绍了实时操作系统RT-Thread,想了解的朋友点击:
(一)https://blog.csdn.net/weixin_42967006/article/details/106687245
(二)https://blog.csdn.net/weixin_42967006/article/details/106688601
本篇将为大家介绍本次用到的两个外设:OV2640摄像头和3.2寸LCD触摸屏
OV2640有200W像素,可选广角,带补光灯,不支持自动调焦,本次买的模块使用3.3V供电。
OV2640在通讯上有两种方式,一种是写指令寄存器的时候,用SCCB协议,SCCB是一种和IIC类似但又不完全相同的通讯协议,因为只在初始化的时候对寄存器进行配置时使用,所以通常就用GPIO模拟了,浪费一点单片机资源也影响不大。另一种通讯方式就是传输图像像素的时候使用的DCMI接口,采用8bit并口,结合帧同步、行同步和像素同步等信号来完成图像的传输。STM32F4系列单片机集成了DCMI接口,但我这次用到的AT32F403和STM32F1系列都不支持DCMI接口,只能用GPIO模拟。
下面链接中是我用到的OV2640的驱动,供参考,在使用的时候只需要根据单片机不同更改初始化函数中的GPIO配置函数,以及ov2640.h和SCCB.H中的引脚配置即可:
https://download.csdn.net/download/weixin_42967006/12514367
OV2640输出图像大小的设置涉及到驱动中这样几个函数:
SCCB_WR_Reg(0XFF,0X00);
SCCB_WR_Reg(0X50,0X89);
有关这个问题更详细的可以参考下面这个帖子:
http://www.openedv.com/posts/list/0/63099.htm
在初始化完成后,开始接受图像数据时,可以参考下面的时序:
while(1)
{
while(OV2640_VSYNC == 1) //帧同步信号,高代表正在传输帧数据
{
while(OV2640_HREF == 0 && OV2640_VSYNC == 1);
while(OV2640_HREF)//行同步信号,高代表正在传输行数据
{
while(OV2640_PCLK == 0);
while(OV2640_PCLK == 1);//像素同步信号,高代表正在传输一个8位像素值
//在此处读取并存储像素值
ImageData = OV2640_DATA;
}
}
}
在车道线检测中通常只关心图像的轮廓,颜色意义不大,所以都以灰度图像作为源数据。OV2640支持直接输出YUV422格式的灰度图像,很方便,还节省了存储空间。设置方法如下:
void OV2640_YUV422_Mode(void)
{
SCCB_WR_Reg(0xFF, 0x00);
SCCB_WR_Reg(0xDA, 0x01);
}
设置完成后OV2640输出图像时会输出16bit的灰度值,我们只关心高字节即可(传输时低字节在前,高字节在后),接收函数如下:
while(1)
{
ImagePtr_W = 0;
ImagePtr_H = 0;
while(OV2640_VSYNC == 1);//丢弃传输到一半的图像
while(OV2640_VSYNC == 0);
while(OV2640_VSYNC == 1) //开始采集jpeg数据
{
ImagePtr_W = 0;
while(OV2640_HREF == 0 && OV2640_VSYNC == 1);
while(OV2640_HREF)
{
while(OV2640_PCLK == 0);
while(OV2640_PCLK == 1);
while(OV2640_PCLK == 0);
ImageData(ImagePtr_H, ImagePtr_W) = OV2640_DATA;
while(OV2640_PCLK == 1);
++ImagePtr_W;
}
++ImagePtr_H;
}
顺便研究了一下RBG565和YUV422格式之间的转化,注意这里的YUV422是16位的,和我们前文直接取的高字节不是一个概念。
RGB565转化为YUV422:
#define RGB565_RED 0xf800
#define RGB565_GREEN 0x07e0
#define RGB565_BLUE 0x001f
u16 RGB565_to_Gray(u16 RGB)
{
u16 Gray;
u16 R = (RGB & RGB565_RED) >> 3+8;
u16 G = (RGB & RGB565_GREEN) >> 5;
u16 B = (RGB & RGB565_BLUE) ;
Gray=(u16)(( R*77 + G * 150 + B * 29 +128 )/256);
return Gray;
}
YUV422转化为RGB565:
u16 yuv422_to_RGB565Gray(u16 yuv422)
{
u16 Gray;
Gray =(((yuv422>>(8+3))<<11)|((yuv422>>(8+2))<<5)|((yuv422>>(8+3))<<0));
return Gray;
}
因为通常LCD显示还是需要RGB565格式,所以可能还需要将YUV422转化为RGB565格式,虽然格式不同但也还是灰色的。
有关OV2640的内容就是这些了,本文最后会和LCD一起贴一个本项目的引脚定义,可以结合驱动代码一起参考。
本次使用的是3.2寸触摸屏,LCD驱动芯片是ILI9341,通信接口采用16位8080接口,触摸屏控制芯片是XPT2046,通讯接口是SPI,驱动中是用GPIO模拟的SPI。驱动程序如下:
https://download.csdn.net/download/weixin_42967006/12514444
LCD的横竖屏和8位数据线还是16位数据线可以通过LCD.h中的以下两个宏定义来设置:
#define USE_HORIZONTAL 0 //定义是否使用横屏 0,不使用.1,使用.
#define LCD_USE8BIT_MODEL 0 //定义数据总线是否使用8位模式 0,使用16位模式.1,使用8位模式
在显示图像前需要先初始化,在之后的显示过程中主要有以下几个函数比较常用:
//设置显示图像的窗口大小,必须先设置大小再发送像素数据
void LCD_SetWindows(u16 xStar, u16 yStar,u16 xEnd,u16 yEnd);
//设置当前坐标值,设置完成后,再写入一个像素值,LCD就会在设置的坐标位置显示写入的像素
void LCD_SetCursor(u16 Xpos, u16 Ypos);
//向当前坐标处写一个像素值
void LCD_DrawPoint_16Bit(u16 color);
//显示一幅16位真彩图像
void LCD_Drawbmp16(u16 x,u16 y,u16 width,u16 length,const unsigned char *p);
这里分享一个图片取模软件,可以直接生成.h文件中的数组,作为上面最后一个函数的*p输入即可,下载链接:
https://download.csdn.net/download/weixin_42967006/12514757
使用方法:
有关LCD没有太多需要注意的地方,就是模块引脚太多了,接线的时候要十分细心,我当时就是因为接线接错了被卡了一段时间。只要硬件连接没问题,用上面的驱动程序应该是可以正常显示和触摸的。关于触摸有一点需要注意的是按下屏幕时会存在类似于按键的抖动问题,如果想避免这种情况,就需要像按键一样做一个消斗的步骤。
贴一张本项目的简要引脚图,绿色是LCD的引脚,黄色是OV2640的引脚,供参考:
最后来几张集成好的图吧,接了摄像头和LCD之后,电流可能较大,最好采用外部电源供电,避免用AT-Link供电: