小孩瘦小不长肉是什么原因| 现是什么生肖| 女人吃什么补肾| 认栽是什么意思| 头孢主要治什么病| 3月什么星座| 心脏增大吃什么药| 三查八对的内容是什么| 同学群取什么名字好| 什么样的小手| 生殖疱疹吃什么药不复发| 直肠炎是什么症状| 梦见吃樱桃是什么意思| 红是什么意思| 大枣吃多了有什么危害| 五劳七伤什么生肖| 一什么篮子| 夏天喝什么解暑| 低血钾吃什么| 什么是刮痧| 颈椎病用什么药最好| 压力是什么| 夏天可以做什么| 黑鱼又叫什么鱼| 什么是手机号| 为什么叫深圳| 黑茶色是什么颜色| 火和什么相生| 1994年属什么生肖| 查肝肾功能挂什么科| 头孢是治什么的| 低密度脂蛋白偏高是什么意思| a醇对皮肤有什么作用| 收获颇丰什么意思| 顾问是什么意思| ca什么意思| 子宫内膜增厚吃什么药| 刷牙出血是什么原因| 黄加红是什么颜色| 办幼儿园需要什么证| 什么是脂肪肝| 证监会是干什么的| 尿失禁是什么意思| 今天什么节日| 疯马皮是什么皮| 中国发明了什么| 圣诞节送什么好| 颈椎病用什么药最好| 治疗阳痿早泄什么药最好| 情趣是什么| 脚底疼是什么原因引起的| 女大十八变是什么意思| 新生的什么| 94年是什么命| 精液是什么组成的| 为什么说有钱难买孕妇B| 免疫力是什么意思| 琼瑶剧是什么意思| 子宫内膜厚吃什么食物好| 家里有小蜘蛛预示什么| 嗓子疼流鼻涕吃什么药| 茶水洗脸有什么好处和坏处| 96年属鼠的是什么命| 36属什么| 7o年属什么生肖| 哦哦是什么意思| 脾胃不好吃什么药好| 亭台楼阁是什么意思| 生命科学专业学什么| 衣原体支原体感染有什么症状| 水可以加什么偏旁| 3月29日是什么星座| hcg下降是什么原因| 八成是什么意思| 重阳节干什么| 什么叫做靶向治疗| 苯磺酸氨氯地平片什么时候吃| 打鸡血是什么意思| 创伤急救的原则是什么| 妨子痣是什么意思| 断掌是什么意思| 翡翠什么样的好| 两只小船儿孤孤零零是什么歌| 孕吐吃什么| 什么进曹营一言不发| 头疼恶心吃什么药| 牛蛙不能和什么一起吃| 暑假是什么时候| 肚胀是什么原因| 生活因什么而精彩| 晚上喝牛奶有什么好处和坏处| 男性雄激素低吃什么药| 痛风什么感觉| 谷雨是什么季节| 脖子上长疣是什么原因| 粘膜慢性炎是什么意思| hb是什么意思| 什么是乳糖不耐受| 包皮真菌感染用什么药| 小猫咪能吃什么| 腺病毒阳性是什么意思| 肠道功能紊乱吃什么药效果好| 麦昆牌子是什么档次| 7.1是什么星座| 脊髓损伤有什么症状| 尼姑是什么生肖| 梦见蛇代表什么| 肠道易激惹综合征的症状是什么| 足字旁的有什么字| 做蛋糕用什么面粉| 芭乐什么味道| 云南属于什么地区| 行代表什么生肖| p4是什么意思| 最大的沙漠是什么| 晚上睡觉脚酸痛什么原因| 阿华田是什么饮料| 10.1什么星座| 木属于五行属什么| 郑中基为什么叫太子基| 乳腺彩超挂什么科| 黄豆可以和什么一起打豆浆| 军长是什么级别| 镜子碎了有什么征兆吗| 放浪形骸是什么意思| 五月天主唱叫什么名字| 舰长是什么级别| 肾功能不全吃什么药| 新生儿什么时候可以喝水| 葡萄不能和什么一起吃| 头伏吃什么| 为什么嘴唇会发紫| 孕妇感冒吃什么药| 兰若是什么意思| adr是什么| 砥砺什么意思| 乙型肝炎表面抗体高是什么意思| 什么颜色最显白| 铂字五行属什么| 驾驶证c1和c2有什么区别| 昏昏欲睡是什么意思| 梦见好多动物是什么意思| 号外是什么意思| 长相厮守是什么意思| 肠胃炎输液用什么药| 维生素b12又叫什么| 正县级是什么级别| 舌头变黑是什么原因| 巽代表什么| 红细胞偏高是什么原因| 咽颊炎吃什么药| 什么东西能吃能喝又能坐| 舌根部淋巴滤泡增生吃什么药| cas是什么| 面瘫吃什么药| 附耳是什么| 躺平是什么意思| 关联词是什么意思| 七活八不活是什么意思| 酷暑难当是什么意思| 什么充电宝可以带上飞机| 你的美丽让你带走是什么歌| 什么是平舌音| chick什么意思| 一步两步三步四步望着天是什么歌| 什么是硬下疳| 伏天吃什么| 什么是吸附性义齿| 花柳病是什么病| 甲流乙流吃什么药| 老年人适合吃什么| 阴道炎有些什么症状| 副高是什么级别| 脑梗阻有什么症状| 口腔医学是干什么的| 什么是收缩压和舒张压| 胸口疼痛是什么原因| 感想是什么意思| 喝黑芝麻糊有什么好处| cartoon什么意思| 柠檬水喝了有什么好处| 耄耋读什么| 嗡阿吽是什么意思| 排卵期后是什么期| 妍字属于五行属什么| 无能为力是什么意思| 酒后头疼什么原因| 胸口疼痛吃什么药| cb是什么意思| nt什么意思| 手上为什么会有小水泡| 破伤风有什么作用| 中统和军统有什么区别| 后脑勺出汗是什么原因| 一凉就咳嗽是什么原因| 移花接木的意思是什么| 毛囊炎用什么药膏| 容易流鼻血是什么原因| 吃什么清肝火最快| 冒是什么意思| 流产挂什么科| 不自觉摇头是什么病| 介入医学科是什么科室| cpi是什么意思啊| rh血型鉴定阳性是什么意思| 孕妇梦见自己出轨是什么意思| 什么食物可以降血糖| 扑朔迷离是什么意思| 惊厥是什么原因引起的| 女大十八变是什么意思| 发光免疫是检查什么的| 脚趾头抽筋是什么原因引起的| 有个马的标志是什么车| 肤色不均匀是什么原因| 痔疮什么感觉| 马的守护神是什么菩萨| 朝鲜韩国什么时候分开的| 减肥的原理是什么| 血糖高吃什么| 怕热的人是什么体质| 生气胸口疼是什么原因| 人为什么要穿衣服| 香椿是什么| 驳是什么动物| 盛世美颜是什么意思| 为什么健身后体重反而重了| 黄色加蓝色等于什么颜色| 甲状腺双叶回声欠均匀是什么意思| 梦见自己大便是什么意思| 护士是干什么的| 孕早期头晕是什么原因| 额头长闭口是什么原因| c代表什么| 怀孕吃什么好| 红色加蓝色等于什么颜色| 薄荷有什么功效| 什么私语| 富士康是做什么的| 手发胀是什么原因造成的| 怀孕初期吃什么菜| 澎湃的什么| 12378是什么电话| 小觑是什么意思| 总是口腔溃疡是什么原因| 富贵命是什么生肖| 什么年马月| 子宫萎缩是什么原因| 什么地蹲着| 金鱼藻属于什么植物| 礼是什么意思| 腺肌症有什么症状表现| 终结者是什么意思| 孕晚期为什么会脚肿| 什么公主| 财神是什么生肖| 八月三十日是什么星座| 之一的意思是什么| 法令纹上的痣代表什么| 一毛不拔指什么生肖| 小211是什么意思| 小人痣代表什么意思| 什么叫上门女婿| 骨癌有什么症状有哪些| 标准工资指什么| 百度
打印
[其他ST产品]

ofs是什么意思

[复制链接]
2834|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
第十九章 摄像头图像捕获实验

本章将介绍如何运用Kendryte K210通过数字视频接口(DVP)来驱动摄像头,以实现实时采集摄像头捕获的图像数据,并对这些数据进行后续处理。通过学习本章内容,读者将掌握利用SDK编程技术实现摄像头实时图像数据捕获的方法。
本章分为如下几个小节:
19.1 OV2640DVP简介
19.2 硬件设计
19.3 程序设计
19.4 运行验证


19.1 OV2640DVP简介
本节将分为两个部分,分别介绍OV2640简介和Kendryte K210 DVP接口简介。另外,所有OV2640的相关资料,都在光盘:A盘à硬件资料à摄像头资料àOV2640资料 文件夹里面。
19.1.1 OV2640简介
OV2640OVOmniVision)公司生产的一颗1/4寸的CMOS UXGA1632*1232)图像传感器。该传感器体积小、工作电压低,提供单片UXGA摄像头和影像处理器的所有功能。通过SCCB 总线控制,可以输出整帧、子采样、缩放和取窗口等方式的各种分辨率8/10位影像数据。该产品UXGA图像最高达到15/秒(SVGA可达30帧,CIF可达60帧)。用户可以完全控制图像质量、数据格式和传输方式。所有图像处理功能过程包括伽玛曲线、白平衡、对比度、色度等都可以通过SCCB接口编程。OmmiVision 图像传感器应用独有的传感器技术,通过减少或消除光学或电子缺陷如固定图案噪声、拖尾、浮散等,提高图像质量,得到清晰的稳定的彩色图像。
OV2640的特点有:
l 高灵敏度、低电压适合嵌入式应用
l 标准的SCCB接口,兼容IIC接口
l 支持RawRGBRGB(RGB565/RGB555)GRB422YUV(422/420)YCbCr422)输出格式
l 支持UXGASXGASVGA以及按比例缩小到从SXGA40*30的任何尺寸
l 支持自动曝光控制、自动增益控制、自动白平衡、自动消除灯光条纹、自动黑电平校准等自动控制功能。同时支持色饱和度、色相、伽马、锐度等设置。
l 支持闪光灯
l 支持图像缩放、平移和窗口设置
l 支持图像压缩,即可输出JPEG图像数据
l 自带嵌入式微处理器
OV2640的功能框图如图19.1.1.1所示:
图19.1.1.1 OV2640功能框图
OV2640传感器包括如下一些功能模块。
1.感光整列(Image Array
OV2640总共有1632*1232个像素,最大输出尺寸为UXGA1600*1200),即200W像素。
2.模拟信号处理(Analog Processing
模拟信号处理所有模拟功能,并包括:模拟放大(AMP)、增益控制、通道平衡和平衡控制等。
3.10A/D 转换(A/D
原始的信号经过模拟放大后,分GBR两路进入一个10 位的A/D 转换器,A/D 转换器工作频率高达20M,与像素频率完全同步(转换的频率和帧率有关)。除A/D转换器外,该模块还有黑电平校正(BLC)功能。
4.数字信号处理器(DSP
这个部分控制由原始信号插值到RGB信号的过程,并控制一些图像质量:
l 边缘锐化(二维高通滤波器)
l 颜色空间转换(原始信号到RGB或者YUV/YCbYCr)
l RGB色彩矩阵以消除串扰
l 色相和饱和度的控制
l /白点补偿
l 降噪
l 镜头补偿
l 可编程的伽玛
l 十位到八位数据转换
5.输出格式模块(Output Formatter
该模块按设定优先级控制图像的所有输出数据及其格式。
6.压缩引擎(Compression Engine
压缩引擎框图如下图所示:
图19.1.1.2 压缩引擎框图
从图可以看出,压缩引擎主要包括三部分:DCTQZentropy encoder(熵编码器),将原始的数据流,压缩成jpeg数据输出。
7.微处理器(Microcontroller
OV2640自带了一个8位微处理器,该处理器有512字节SRAM4KBROM,它提供一个灵活的主机到控制系统的指令接口,同时也具有细调图像质量的功能。
8.SCCB接口(SCCB Interface
SCCB接口控制图像传感器芯片的运行,详细使用方法参照光盘的《OmniVision Technologies Seril Camera Control Bus(SCCB) Specification》这个文档
9.数字视频接口(Digital Video Port
OV2640拥有一个10位数字视频接口(支持8位接法),其MSBLSB可以程序设置先后顺序,ALIENTEK OV2640模块采用默认的8位连接方式,如下图所示:
图19.1.1.3 OV2640默认8位连接方式
OV2640的寄存器通过SCCB时序访问并设置,SCCB时序和IIC时序十分类似,在本章我们不做介绍,请大家参考光盘《OmniVision Technologies Seril Camera Control Bus(SCCB) Specification》这个文档。
接下来,我们介绍一下OV2640的传感器窗口设置、图像尺寸设置、图像窗口设置和图像输出大小设置,这几个设置与我们的正常使用密切相关,有必要了解一下。其中,除了传感器窗口设置是直接针对传感器阵列的设置,其他都是DSP部分的设置了,接下来我们一个个介绍。
传感器窗口设置,该功能允许用户设置整个传感器区域(1632*1220)的感兴趣部分,也就是在传感器里面开窗,开窗范围从2*2~1632*1220都可以设置,不过要求这个窗口必须大于等于随后设置的图像尺寸。传感器窗口设置,通过:0X03/0X19/0X1A/0X07/0X17/0X18等寄存器设置,寄存器定义请看OV2640_DS(1.6).pdf这个文档(下同)。
图像尺寸设置,也就是DSP输出(最终输出到LCD的)图像的最大尺寸,该尺寸要小于等于前面我们传感器窗口设置所设定的窗口尺寸。图像尺寸通过:0XC0/0XC1/0X8C等寄存器设置。
图像窗口设置,这里起始和前面的传感器窗口设置类似,只是这个窗口是在我们前面设置的图像尺寸里面,再一次设置窗口大小,该窗口必须小于等于前面设置的图像尺寸。该窗口设置后的图像范围,将用于输出到外部。图像窗口设置通过:0X51/0X52/0X53/0X54/0X55/0X57等寄存器设置。
图像输出大小设置,这是最终输出到外部的图像尺寸。该设置将图像窗口设置所决定的窗口大小,通过内部DSP处理,缩放成我们输出到外部的图像大小。该设置将会对图像进行缩放处理,如果设置的图像输出大小不等于图像窗口设置图像大小,那么图像就会被缩放处理,只有这两者设置一样大的时候,输出比例才是1 : 1的。
因为OmniVision 公司公开的文档对这些设置实在是没有详细介绍。只能从他们提供的初始化代码(还得去linux源码里面移植过来)里面去分析规律,所以,这几个设置,都是作者根据OV2640的调试经验,以及相关文档总结出来的,不保证百分比正确,如有错误,还请大家指正。
以上几个设置,光看文字可能不太清楚,这里我们画一个简图有助于大家理解,如图19.1.1.4所示:
图19.1.1.4 OV2640图像窗口设置简图
上图,最终红色框所示的图像输出大小,才是OV2640输出给外部的图像尺寸,也就是显示在LCD上面的图像大小。当图像输出大小与图像窗口不等时,会进行缩放处理,在LCD上面看到的图像将会变形。
最后,我们介绍一下OV2640的图像数据输出格式。首先我们简单介绍一些定义:
UXGA,即分辨率位1600*1200的输出格式,类似的还有:SXGA(1280*1024)WXGA+(1440*900)XVGA(1280*960)WXGA(1280*800)XGA(1024*768)SVGA(800*600)VGA(640*480)CIF(352*288)WQVGA(400*240)QCIF(176*144)QQVGA(160*120)等。
PCLK,即像素时钟,一个PCLK时钟,输出一个像素(或半个像素)
VSYNC,即帧同步信号。
HREF /HSYNC,即行同步信号。
OV2640的图像数据输出(通过Y[9:0])就是在PCLKVSYNCHREF/ HSYNC的控制下进行的。首先看看行输出时序,如图19.1.1.5所示:
图19.1.1.5 OV2640行输出时序
从上图可以看出,图像数据在HREF为高的时候输出,当HREF变高后,每一个PCLK时钟,输出一个8/10位数据。我们采用8位接口,所以每个PCLK输出1个字节,且在RGB/YUV输出格式下,每个tp=2Tpclk,如果是Raw格式,则一个tp=1Tpclk。比如我们采用UXGA时序,RGB565格式输出,每2个字节组成一个像素的颜色(高低字节顺序可通过0XDA寄存器设置),这样每行输出总共有1600*2PCLK周期,输出1600*2个字节。
再来看看帧时序(UXGA模式),如图19.1.1.6所示:
图19.1.1.6 OV2640帧时序
上图清楚的表示了OV2640UXGA模式下的数据输出。我们按照这个时序去读取OV2640的数据,就可以得到图像数据。
最后说一下OV2640的图像数据格式,我们一般用2种输出方式:RGB565JPEG。当输出RGB565格式数据的时候,时序完全就是上面两幅图介绍的关系。以满足不同需要。而当输出数据是JPEG数据的时候,同样也是这种方式输出(所以数据读取方法一模一样),不过PCLK数目大大减少了,且不连续,输出的数据是压缩后的JPEG数据,输出的JPEG数据以:0XFF,0XD8开头,以0XFF,0XD9结尾,且在0XFF,0XD8之前,或者0XFF,0XD9之后,会有不定数量的其他数据存在(一般是0),这些数据我们直接忽略即可,将得到的0XFF,0XD8~0XFF,0XD9之间的数据,保存为.jpg/.jpeg文件,就可以直接在电脑上打开看到图像了。
OV2640自带的JPEG输出功能,大大减少了图像的数据量,使得其在网络摄像头、无线视频传输等方面具有很大的优势。OV2640我们就介绍到这,关于OV2640更详细的介绍,请参考:A盘à硬件资料à摄像头资料àOV2640资料à OV2640_DS(1.6).pdf。
19.1.2 Kendryte K210 DVP接口简介
DVP是摄像头接?模块,支持把摄像头输入图像数据转发给AI模块或者内存。主要功能包括:
1. 支持DVP接?的摄像头
2. 支持SCCB协议配置摄像头寄存器
3. ?支持640x480每帧的图像输入,每帧图像??可配置
4. 支持YUV422RGB565格式的图像输入
5. 支持图像同时输出到AI模块和显示屏
6. 输出到AI模块的格式可选RGB888,或YUV422输入时的Y分量
7. 输出到显示屏的格式为RGB565
8. 图像输出接?为AXI总线,支持4 beats burst和1 beat burst
9. 检测到一帧开始或一帧图像传输完成时可向CPU发送中断
Kendryte K210芯片内置的摄像头接口模块功能强大,能够同时输出AI模型所需的数据以及RGB565格式的图像信息,从而充分满足开发板在AI应用和图像显示方面的需求。通过学习本章内容,读者将掌握如何利用Kendryte K210DVP接口驱动摄像头实时捕获图像数据,并将其显示出来。
Kendryte K210官方SDK提供了多个操作DVP模块的函数,这里我们只讲述部分用到的函数,这些函数介绍如下:
1, dvp_init函数
该函数主要用于DVP初始化,如下代码所示:
void dvp_init(uint8_t reg_len)
{
    g_sccb_reg_len = reg_len;
    sysctl_clock_enable(SYSCTL_CLOCK_DVP);
    sysctl_reset(SYSCTL_RESET_DVP);
    dvp->cmos_cfg &= (~DVP_CMOS_CLK_DIV_MASK);
    dvp->cmos_cfg |= DVP_CMOS_CLK_DIV(3) | DVP_CMOS_CLK_ENABLE;
    dvp_sccb_clk_init();
    dvp_reset();
}
首先我们要使能DVP时钟,然后重新配置系统DVP,最后初始化SCCBDVP配置,简单来说,这个函数用于完成DVP驱动的必要初始化配置。
2dvp_set_xclk_rate函数
该函数用于设置DVP时钟频率,如下代码所示:
uint32_t dvp_set_xclk_rate(uint32_t xclk_rate);
函数只有一个参数,用于设置DVP的时钟频率,设置成功后函数返回实际的DVP时钟频率值。
3dvp_start_convert函数
该函数用于启动DVP转换,如下代码所示:
void dvp_start_convert(void)
{
    dvp->sts = DVP_STS_DVP_EN | DVP_STS_DVP_EN_WE;
}
当图像缓存buf未满时,启动图像捕获。
4dvp_set_image_size函数
该函数用于设置图像分辨率大小,如下代码所示:
void dvp_set_image_size(uint32_t width, uint32_t height)
{
    uint32_t tmp;
    tmp = dvp->dvp_cfg & (~(DVP_CFG_HREF_BURST_NUM_MASK | DVP_CFG_LINE_NUM_MASK));
    tmp |= DVP_CFG_LINE_NUM(height);
    if(dvp->dvp_cfg & DVP_CFG_BURST_SIZE_4BEATS)
        tmp |= DVP_CFG_HREF_BURST_NUM(width / 8 / 4);
    else
        tmp |= DVP_CFG_HREF_BURST_NUM(width / 8 / 1);
    dvp->dvp_cfg = tmp;
}
该函数设置捕获图片分辨率大小,有两个参数,第一个参数设置图像宽的大小,第二个参数设置图像高的大小
5dvp_set_ai_addr函数
该函数用于设置AI图像的缓存地址,用于AI模块的算法处理,如下代码所示:
void dvp_set_ai_addr(uint32_t r_addr, uint32_t g_addr, uint32_t b_addr)
{
#if FIX_CACHE
    configASSERT(!is_memory_cache((uintptr_t)r_addr));
    configASSERT(!is_memory_cache((uintptr_t)g_addr));
    configASSERT(!is_memory_cache((uintptr_t)b_addr));
#endif
    dvp->r_addr = r_addr;
    dvp->g_addr = g_addr;
    dvp->b_addr = b_addr;
}
该函数用于设置AIRGB地址,AI图像常用RGB888格式,图像的RG、B三个颜色分量数据分别存放在连续的内存中(不同于RGB565)。函数有三个参数,第一个参数为存放R分量的内存地址,第二个参数存放G分量的内存地址,第三个参数存放B分量的内存地址。
6dvp_set_ai_addr函数
该函数用于设置显示图像的内存地址,用于LCD显示,如下代码所示:
void dvp_set_display_addr(uint32_t addr)
{
#if FIX_CACHE
    configASSERT(!is_memory_cache((uintptr_t)addr));
#endif
    dvp->rgb_addr = addr;
}
该函数用于设置LCD显示的RGB地址,图像使用RGB565格式,即图像的RG、B三个颜色分量占用16bit,可用一个uint16_t类型数据表示一个颜色,故RGB565格式的图像是以16bit长度为单位连续存放在内存中。函数的参数是RGB565图像的首地址。
Kendryte K210DVP接口提供了多达18API函数,而除此之外的其他相关API函数介绍,则可以在官方提供的裸机SDK编程指南手册中查阅获取。
19.2 硬件设计
19.2.1 例程功能
1. 使用SDKDVP接口驱动OV2640摄像头模块,并配置摄像头的输出帧大小以及输出格式等进行配置,最后获取摄像头采集到的图像数据,并在LCD进行显示。
19.2.2 硬件资源
1. 摄像头
        SCCB_SDA - IO40
        SCCB_SCL - IO41
        DVP_RESET - IO42
        DVP_VSYNC - IO43
        DVP_PWDN - IO44
        DVP_HREF - IO45
        DVP_XCLK - IO46
        DVP_PCLK - IO47
        D0~D7 - DVP_D0~DVP_D7
2. LCD
        LCD_RD - IO34
        LCD_BL - IO35
        LCD_CS - IO36
        LCD_RST - IO37
        LCD_RS - IO38
        LCD_WR - IO39
        LCD_D0~LCD_D7 - SPI0_D0~SPI0_D7
19.2.3 原理图
本章实验内容,需要使用到板载的摄像头接口,在正点原子DNK210开发板上有两处摄像头接口分别为位于正点原子DNK210开发板底板上的ATK-MC2640摄像头模块接口,该接口用于连接正点原子的ATK-MC2640模块,另一个摄像头接口位于正点原子CNK210F核心板,该接口同于直接连接OV2640等摄像头模组,但需要特别注意的是,这两个摄像头接口不能同时使用,否则可能导致硬件损坏。
正点原子DNK210开发板上的ATK-MC2640摄像头模块接口的连接原理图,如下图所示:
19.2.3.1 ATK-MC2640摄像头模块接口原理图
正点原子CNK210F核心板上的OV2640等摄像头模组接口的连接原理图,如下图所示:
19.2.3.2 OV2640等摄像头模组接口原理图
19.3 程序设计
19.3.1 OV2640驱动代码
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。OV2640驱动源码包括四个文件:ov2640.c、ov2640.h、camera.ccamera.h。这四个文件可以做个简单的区分,ov2640.cov2640.h是用于OV2640摄像头得底层驱动,而camera.ccamera.h文件用于摄像头得上层应用,对外提供函数接口方便读取摄像头数据和配置摄像头等。我们这里仅介绍部分camera.ccamera.h文件的函数。
下面,首先介绍camera.h头文件中摄像头接口的宏定义,其定义情况如下:
#if !defined(CAMERA_SDA_PIN)
#define CAMERA_SDA_PIN 40
#endif
#if !defined(CAMERA_SCL_PIN)
#define CAMERA_SCL_PIN 41
#endif
#if !defined(CAMERA_RST_PIN)
#define CAMERA_RST_PIN 42
#endif
#if !defined(CAMERA_VSYNC_PIN)
#define CAMERA_VSYNC_PIN 43
#endif
#if !defined(CAMERA_PWDN_PIN)
#define CAMERA_PWDN_PIN 44
#endif
#if !defined(CAMERA_HSYNC_PIN)
#define CAMERA_HSYNC_PIN 45
#endif
#if !defined(CAMERA_XCLK_PIN)
#define CAMERA_XCLK_PIN 46
#endif
#if !defined(CAMERA_PCLK_PIN)
#define CAMERA_PCLK_PIN 47
#endif
这里是SCCB的硬件引脚和一些控制引脚的宏。OV2640摄像头模块需要连接Kendryte K210 16个引脚,除了上述8个引脚外还有8DVP专用引脚,专用引脚初始化后便可使用,无需绑定。
下面介绍camera.c文件的内容。
/**
* @brief       摄像头初始化
* @param       xclk_rate DVP时钟速率
* @retval      0,操作成功
*/
int camera_init(uint32_t xclk_rate)
{
    /* Reset framebuffer */
    camera_fb_reset();
    /* Automatically detect and initialize sensor */
    if (camera_sensor_init(xclk_rate) != 0)
    {
        return 1;
    }
    /* Configure DVP and disable DVP capture */
    camera_dvp_init();
    camera_dvp_run(0);
    return 0;
}
可以看到,函数首先对摄像头进行重置,然后初始化传感器,如果初始化出现问题则退出函数并返回1,传感器初始化完成后重新配置摄像头,最后关闭DVP中断暂停捕获图像数据。
/**
* @brief       设置摄像头输出的图像格式
* @param       format :像素格式结构体参数
* @retval      0,操作成功
*/
int camera_set_pixformat(pixformat_t format)
{
    if (format == PIXFORMAT_INVLAID)
    {
        return 1;
    }
    /* Reconfigure sensor's pixel format if that is changed */
    if (fb.pixformat != format)
    {
        if (sensor->set_pixformat(format) != 0)
        {
            return 1;
        }
        /* Recode frame parameters */
        fb.pixformat = format;
        switch (fb.pixformat)
        {
            case PIXFORMAT_RGB565:
            {
                fb.bpp = 2;
                break;
            }
            default:
            {
                break;
            }
        }
    }
    return 0;
}
这个函数是设置输出到显示器的图像格式,我们设置为RGB565格式。
/**
* @brief       设置图像分辨率大小
* @param       width  :宽度
* @param       height :高度
* @retval      0,设置成功 1,设置失败
*/
int camera_set_framesize(uint16_t width, uint16_t height)
{
    uint8_t index;
    uint8_t rec_index;
    /* Reset sensor frame size */
    if (sensor->set_framesize(width, height) != 0)
    {
        return 1;
    }
    /* Realloc framebuffer memory if frame size is changed */
    if ((fb.width != width) || (fb.height != height))
    {
        for (index=0; index<CAMERA_FRAMEBUFFER_NUM; index++)
        {
            /* Free current framebuffer memory */
            if (fb.disp[index] != NULL)
            {
                iomem_free(fb.disp[index]);
                fb.disp[index] = NULL;
            }
            if (fb.ai[index] != NULL)
            {
                iomem_free(fb.ai[index]);
                fb.ai[index] = NULL;
            }
            /* Alloc memory for display framebuffer */
            fb.disp[index] = (uint8_t *)iomem_malloc(width * height * fb.bpp);
            if (fb.disp[index] == NULL)
            {
                for (rec_index=0; rec_index<index; rec_index++)
                {
                    iomem_free(fb.disp[rec_index]);
                    fb.disp[rec_index] = NULL;
                    fb.disp[rec_index] = (uint8_t *)iomem_malloc(fb.width * fb.height * fb.bpp);
                    iomem_free(fb.ai[rec_index]);
                    fb.ai[rec_index] = NULL;
                    fb.ai[rec_index] = (uint8_t *)iomem_malloc(fb.width * fb.height * 3);
                }
                fb.disp[index] = (uint8_t *)iomem_malloc(fb.width * fb.height * fb.bpp);
                sensor->set_framesize(fb.width, fb.height);
                return 1;
            }
            /* Alloc memory for AI framebuffer */
            fb.ai[index] = (uint8_t *)iomem_malloc(width * height * 3);
            if (fb.ai[index] == NULL)
            {
                for (rec_index=0; rec_index<index; rec_index++)
                {
                    iomem_free(fb.disp[rec_index]);
                    fb.disp[rec_index] = NULL;
                    fb.disp[rec_index] = (uint8_t *)iomem_malloc(fb.width * fb.height * fb.bpp);
                    iomem_free(fb.ai[rec_index]);
                    fb.ai[rec_index] = NULL;
                    fb.ai[rec_index] = (uint8_t *)iomem_malloc(fb.width * fb.height * 3);
                }
                iomem_free(fb.disp[index]);
                fb.disp[index] = NULL;
                fb.disp[index] = (uint8_t *)iomem_malloc(fb.width * fb.height * fb.bpp);
                fb.ai[index] = (uint8_t *)iomem_malloc(fb.width * fb.height * 3);
                sensor->set_framesize(fb.width, fb.height);
                return 1;
            }
        }
        /* Recode frame parameters */
        fb.width = width;
        fb.height = height;
    }
    /* Reconfigure DVP base parameter */
    dvp_set_image_size(width, height);
    dvp_set_ai_addr((uint32_t)fb.ai[fb.write_index] + (0 * fb.width * fb.height),
                    (uint32_t)fb.ai[fb.write_index] + (1 * fb.width * fb.height),
                    (uint32_t)fb.ai[fb.write_index] + (2 * fb.width * fb.height));
    dvp_set_display_addr((uint32_t)fb.disp[fb.write_index]);
    /* Enable DVP capture */
    camera_dvp_run(1);
    return 0;
}
这个函数比较长,不过并不难理解,我们首先要释放原先申请的内存和清空内存地址,再重新申请一片内存和设置内存的大小,防止原先内存不足或者内存过多导致浪费,然后重新设置AI图像和显示图像的内存地址,最后开启DVP中断开始捕获图像数据。
/**
* @brief       传递图像的地址
* @param       display RGB565数据指针的地址
* @param       ai      RGB888数据指针的地址
* @retval      0,捕获成功 1,捕获失败
*/
int camera_snapshot(uint8_t **display, uint8_t **ai)
{
    /* Return error if framebuffer is empty */
    if (fb.empty == 1)
    {
        return 1;
    }
    /* Get data */
    if (display != NULL)
    {
        if (fb.pixformat == PIXFORMAT_RGB565)
        {
            uint32_t index;
            uint32_t size = fb.width * fb.height * fb.bpp;
            uint32_t loop = size >> 2;
            uint32_t *data = (uint32_t *)fb.disp[fb.read_index];
            for (index=0; index<loop; index++)
            {
                data[index] = ((data[index] & 0x0000FFFF) << 16) | ((data[index] & 0xFFFF0000) >> 16);
            }
        }
        *display = fb.disp[fb.read_index];
    }
    if (ai != NULL)
    {
        // uint32_t index;
        // uint32_t size = fb.width * fb.height * 3;
        // uint32_t loop = size >> 1;
        // uint16_t *data = (uint16_t *)fb.ai[fb.read_index];
        // for (index=0; index<loop; index++)
        // {
        //     data[index] = ((data[index] & 0x00FF) << 8) | ((data[index] & 0xFF00) >> 8);
        // }
        *ai = fb.ai[fb.read_index];
    }
    return 0;
}
这个函数的主要功能是将AI图像和显示图像的指针地址分别赋值给一个新的指针变量,以便我们能方便地访问图像数据。操作完成该函数会返回0作为成功标识。特别指出的是,鉴于Kendryte K210芯片的SPI接口与LCD显示器之间的兼容性问题,对于RGB565格式的图像数据,在传递之前需要将每个像素点相邻的两个16位数据进行交换处理,否则在LCD上显示会出现条纹现象。
/**
* @brief       释放当前捕获的图像数据
* @param      
* @retval      0,成功 1,失败
*/
int camera_snapshot_release(void)
{
    /* Return error if framebuffer is empty */
    if (fb.empty == 1)
    {
        return 1;
    }
    /* Update framebuffer read index for next framebuffer read */
    fb.read_index++;
    if (fb.read_index == CAMERA_FRAMEBUFFER_NUM)
    {
        fb.read_index = 0;
    }
    /* Check framebuffer is empty or not */
    if (fb.read_index == fb.write_index)
    {
        /*
         * If read index is increased to equal read index,
         * it means framebuffer is empty
         */
        fb.empty = 1;
    }
    /* Set next capture's framebuffer and clean framebuffer's full flag,
     * cause there is a framebuffer to be released when framebuffer is full
     */
    if (fb.full == 1)
    {
        dvp_set_ai_addr((uint32_t)fb.ai[fb.write_index] + (0 * fb.width * fb.height),
                        (uint32_t)fb.ai[fb.write_index] + (1 * fb.width * fb.height),
                        (uint32_t)fb.ai[fb.write_index] + (2 * fb.width * fb.height));
        dvp_set_display_addr((uint32_t)fb.disp[fb.write_index]);
        fb.full = 0;
    }
    return 0;
}
此函数是用于释放当前捕获的图像缓存,清空后便可开启下一次中断捕获。摄像头相关驱动代码就介绍到这,接下来介绍main.c文件。
19.3.2 main.c代码
main.c中的代码如下所示:
#define CAMERA_WIDTH    320
#define CAMERA_HEIGHT   240
int main(void)
{
    uint8_t *disp;
   
    sysctl_pll_set_freq(SYSCTL_PLL0, 800000000);
    sysctl_pll_set_freq(SYSCTL_PLL1, 400000000);
    sysctl_pll_set_freq(SYSCTL_PLL2, 45158400);
    sysctl_set_power_mode(SYSCTL_POWER_BANK6, SYSCTL_POWER_V18);
    sysctl_set_power_mode(SYSCTL_POWER_BANK7, SYSCTL_POWER_V18);
    sysctl_set_spi0_dvp_data(1);
    lcd_init();
    lcd_set_direction(DIR_YX_LRUD);
    camera_init(0);
    camera_set_pixformat(PIXFORMAT_RGB565);
    camera_set_framesize(CAMERA_WIDTH, CAMERA_HEIGHT);
   
    while (1)
    {
        if (camera_snapshot(&disp, NULL) == 0)
        {
            lcd_draw_picture(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT, (uint16_t *)disp);
            camera_snapshot_release();
        }
    }
}
可以看到首先对LCD和摄像头进行了初始化。
接着是配置摄像头输出的帧大小以及输出格式。
最后就是在一个循环中不断地获取摄像头输出的图像数据,然后将图像在LCD显示屏上进行显示,显示完成之后释放当前捕获的图像数据便可重新进入下一帧图像采集。
19.4 运行验证
DNK210开发板连接到电脑主机,通过VSCode将固件烧录到开发板中,可以看到LCD上实时地显示这摄像头采集到的画面,如下图所示:
19.4.1 LCD显示摄像头采集图像

沙发
Stahan| | 2025-08-04 22:50 | 只看该作者
正点原子的资料是很不错的
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

125

主题

126

帖子

3

粉丝
病毒感染咳嗽吃什么药 牙齿经常出血是什么原因 北京大栅栏有什么好玩的 什么胆什么心 olay是什么档次
什么牙什么嘴 自卑是什么意思 o型血阳性是什么意思 瞳距是什么意思 涵字五行属什么
人乳头瘤病毒51型阳性是什么意思 我是舅舅的什么人 中国的国球是什么 为什么会无缘无故长痣 古惑仔是什么
这是什么颜色 r表示什么 什么样的西瓜 脚底疼是什么原因引起的 夫妻都是b型血孩子是什么血型
网球肘吃什么药hcv8jop8ns4r.cn 牛肉饺子馅配什么蔬菜travellingsim.com 六月初八是什么日子hcv9jop1ns2r.cn 火字旁的有什么字hcv7jop6ns3r.cn 疤痕憩室是什么意思hcv9jop6ns0r.cn
梦见自己大肚子快生了是什么意思hcv8jop8ns5r.cn 白细胞满视野是什么意思hcv8jop4ns5r.cn 瘪是什么意思yanzhenzixun.com 梦到前夫什么意思96micro.com 子宫小结节是什么意思hcv7jop6ns3r.cn
pacu是什么意思hcv8jop2ns2r.cn 金银满堂是什么生肖hcv8jop0ns1r.cn 甜醋是什么醋hcv8jop0ns3r.cn 肾结石检查什么项目hcv9jop6ns9r.cn 南京有什么好吃的hcv7jop6ns6r.cn
楚门的世界是什么意思hcv7jop6ns1r.cn 什么李子品种最好吃hcv8jop3ns1r.cn 难以启齿是什么意思luyiluode.com 什么是皮质醇hcv8jop4ns8r.cn 人格魅力是什么意思hcv8jop6ns5r.cn
百度