×

基于FPGA的RPi摄像头设计

消耗积分:0 | 格式:rar | 大小:10.33 MB | 2022-12-23

香香技术员

分享资料个

方案介绍

FPGA 非常适合低延迟、高吞吐量的成像应用。最好使用直接连接到 FPGA 的相机(而不是 USB/网络相机)来实现这一点。然而,对于业余爱好者、修补匠和小型初创企业来说,可用的相机模块选项数量有限,而且并非所有可用的选项都很便宜(相对而言)。最重要的是,许多都有自己的物理接口,所以通常你只能将相机与它的开发套件一起使用。没有相当大的努力,就是这样。

然而,由于带有 15 针 FFC 摄像头接口 (MIPI-CSI) 的 Raspberry Pi 等低成本单板计算机的普及,情况已经好转。随着时间的推移,RPi 摄像头接口变得非常流行,如今许多单板计算机都可以使用相同的摄像头连接器(Jetson 套件、RPi 替代板等)。甚至一些 FPGA 板现在也配备了相同的 15 针连接器(Zybo-Z7、Kria 等)。由于流行,现在可以从各个供应商(RPi 基金会、Arducam、VEYE 等等)获得许多低成本图像传感器。

不幸的是,一旦我们开始在我们的 FPGA 中使用这些图像传感器之一,我们就会遇到一个大问题:大多数图像传感器的数据表都不公开!

您会看到,在使用任何现代图像传感器之前,您需要根据您的用例对其进行配置。虽然高吞吐量数据通过 MIPI-CSI 等协议传输,但配置通常通过 I2C 进行。但是,要知道通过 I2C 接口发送哪些命令需要知道传感器的寄存器映射以及这些寄存器的每一位的功能。但如果没有公开的数据表,这是一个大问题。在某些情况下,如果您足够幸运,您将能够在互联网上找到泄露的特定图像传感器的数据表,但情况并非总是如此。例如,在撰写本文时(2021 年 9 月),RPi“高质量”相机没有公开可用的数据表。

有时即使有数据表,事情也不会太乐观。现代图像传感器是复杂的硅片,配置大多数图像传感器需要正确设置数百个寄存器,然后才能以正确的格式输出正确的图像数据。有时钟/PLL 配置、分辨率、分级、曝光、增益、白平衡、输出接口格式设置——仅举几例。至少获得一个参考或起点来构建不是很好吗?

所以,我们能做些什么?
“嗅探”或“窃听”是软件/硬件黑客攻击中常用的一种技术。这个想法是在不干扰原始对话的情况下监听两个设备之间的通信,并保留细致的记录以备后用。我们可能不明白对话的每个部分的含义,但有时我们可以重播相同的序列,并设法欺骗目标设备给我们需要的东西。

我的设置

在这种特殊情况下,我有一块我之前购买的Zybo-z7-20板以及PCAM-5C板相机。我能够使用Digilent 提供的演示项目非常快速地让两者协同工作,该项目运行“裸机”。但是,对于未来的项目,我希望有更多的相机选项,而不仅仅是 PCAM-5C。许多 RPi 接口兼容传感器都带有自己的 Linux 驱动程序。我希望能够在运行裸机时使用相同的传感器。

我的计划如下:

  • 选择具有匹配物理/电子接口的相机
  • 使用我需要的设置(分辨率、帧率等)使用它的原生平台(RPi、Jetson 等)
  • 使用逻辑分析器设置 I2C 嗅探器并记录从处理器到相机的所有命令。
  • 使用我自己的 FPGA 板清理、适当格式化和重新播放命令。

对于这个特定的实验,我将使用我已有的:- Raspberry Pi v2.1 相机- RaspberryPi 4B 作为“本机”平台- Saleae Logic 16 pro 作为我的逻辑分析仪- Zybo-Zy-20 作为我的“目标” FPGA 板。

步骤 1:验证接口兼容性
N. 通过查看电路板原理图来检查引脚配置和电压电平。

我们可以看到连接器将为连接的相机提供 3.3V 电压,并且 I2C 也在 3.3V 电压下工作。

接下来,我们看一下摄像头模块。从RPi v2.1 相机模块的原理图,我们可以验证引脚配置和电压确实匹配。

您可能会注意到 1.8V 而不是 3.3V 的上拉电压,但这没关系,因为组件 Q1 正在执行电压电平转换。它期待相同的 3.3V 电源输入。我还验证了 ENABLE 信号可以与来自 Zybo-Z7 的 3.3V 信号一起使用。MIPI 信号和地的所有位置都符合预期。

第 2 步:让相机与原生平台一起工作
首先,我们需要让相机与其本地支持的平台一起工作,在这种情况下是 Raspberry Pi 4。但是,对于不同的相机,它可以是任何其他嵌入式系统(Jetson Nano 等);平台没有太大关系。主要目标是能够使用可用的驱动程序命令相机进行正确的设置,以便我们可以监视 I2C 线路。

我不会详细介绍如何让 v2 ​​相机与 Raspberry Pi 一起工作,因为这在互联网上的很多地方都有介绍,包括官方文档。简而言之,我在 RPi 4 上启动了 Raspbian OS 映像,启用了摄像头,然后打开终端发出以下命令:

FpdFb2wDwhhC3aYu96YyKG5sRcWi

实时视频立即开始在连接的 HDMI 监视器上显示,表明相机正在按预期工作。Ctrl+C退出。注意上面gstreamer管道的使用,这是上面命令工作的先决条件。

第 3 步:嗅探 I2C 通信
现在我们有了一个工作系统,下一步是嗅探 RPi 和图像传感器之间的 I2C 通信。首先,我使用烙铁将三根细跳线连接到 FFC 上的接地、SDA、SCL 引脚,然后用 Kapton 胶带覆盖以作为连接线的机械释放。我这样做是因为与在焊接过程中弄乱 RPi 或相机板相比,FFC 电缆便宜且易于更换,如果您犯了错误。此外,这给了我一条“间谍电缆”,我也可以将其重新用于其他相机/平台。

在此之后,我将这些电线与我的逻辑分析仪连接起来。对于这一步,我使用 USB“逻辑分析仪”来嗅探 I2C 通道上的通信。我使用了 Saleae Logic 16 pro,因为这是我所拥有的,但任何便宜的逻辑分析仪都可以用于此(I2C 的数据速率相当慢)。最后,我为 I2C 解码配置了逻辑分析仪并点击了开始按钮。

现在我们有一些捕获的数据。接下来,我们需要清理和准备数据,然后才能将其与我们的 FPGA 板一起使用。我遵循的确切程序非常特定于我的设置,我将详细说明每个步骤的意图以供读者理解。

步骤 4:清理和准备 I2C 数据
现在我们有一些捕获的数据,我们需要清理和准备数据,然后才能将其与我们的 FPGA 板一起使用。由于我遵循的确切过程非常特定于我的设置,因此我将详细说明每个步骤的意图,以便读者理解。

我注意到的第一件事是,有一个初始的命令突发(左),然后是另一个密集的命令突发(中),然后经过一段时间的延迟,每约 15 毫秒(右)有一个 I2C 命令。

每 ~15ms 的命令对应于 60fps 的帧之间的时间。此外,命令被发送到相同的 I2C 地址并且都是写命令。

所以我得出结论,经过一些实验,这些必须是从 ISP 到相机的命令,用于在处理每一帧之后调整不断变化的光照条件(自动增益/曝光控制,可能还有自动白平衡)。这也意味着,就目前而言,我可以删除在这些命令的第一次实例之后发送的任何数据数据,因为发送一次曝光命令就足以开始了。另外,请注意这些重复的命令被发送到 I2C 地址 0x10。因此我们可以推断出我们相机的I2C 地址是 0x10 。

接下来,我查看了修剪后的数据,并注意到开头有一些读取命令,以及一些对 I2C 地址 0x71 的写入命令。

因为我们不知道如何处理从读取命令返回的数据,所以保留它们没有意义,所以我删除了它们。

寻址到 0x71 的命令最有可能用于位于 Rpi V2.1 cam 上的 EEPROM,Raspberry pi 固件使用它来查看是否连接了真正的相机,因此我们也不需要保留它们。因此我也删除了它们。

对于转储的数据,我只会提到一些细分。让我们随机选择一行转储数据:

FgmF-UDtNkJxv4QdUont96Y-fpIN

在这一行中,0x10 是相机的 I2C 地址,0x0162 是寄存器地址,0x0D、0xE8 是数据字节。通常一个寄存器是一个字节长,因此由于大多数传感器中的 I2C 地址自动递增功能,第二个字节实际上将转到下一个寄存器地址 (0x1063)。

无论如何,在此之后,我将剩余的修剪数据复制到 Excel 表中,删除了除 I2C 数据之外的所有文本并对其进行了格式化(通过一些手动操作),以便稍后在 Xilinx 内部将其用作 C 中的数组开发工具包。其中一部分包括在每行中添加一列,其中包含 I2C 字节数,因为转储数据中的不同行发送的 I2C 字节数不同。

步骤 5:使用 FPGA 重放命令
现在,为了使用我们的 FPGA 板测试我们的数据,我需要一个 Vivado &SDK 项目。幸运的是,我已经从Adam Taylor的优秀帖子中下载了一个 Vivado 项目,它为我提供了一个工作系统,可以从 PCAM-5C 相机以 720p60 分辨率获取数据并通过 HDMI 输出显示。

接下来,我为此转到了 SDK 项目,并添加了一个数组,其中包含前面步骤中转储和格式化的 I2C 数据,并将其设置为启动后发送到 v2 相机 I2C 地址(0x10)。其余什么都不必更改,事实上我什至在文件中保留了以前的 PCAM-5C 代码,因为当该地址上没有设备响应时,直接忽略对该地址的命令。我还配置了 Adam 的 vivado 项目以使用 CSI 输入(而不是 HDMI IN)作为源。包含所有修改的完整项目链接在帖子末尾。

软件修改完成后,剩下要做的就是将 RPi v2.1 摄像头连接到 Zybo-Z7 上的 MIPI 连接器,将 HDMI 电缆从 HDMI TX 端口连接到显示器,然后上传代码。

几秒钟后,现场视频出现在屏幕上!

然而,在成功之前,我会提到一个我立即观察到的问题:曝光和白平衡保持不变。公平地说,这是意料之中的,因为我们不会在初始配置后更新曝光或白平衡。将来,这可以通过构建一些 HLS 块来计算曝光、白平衡等,并在每一帧后将更新发送回传感器来完成。

结果和结论
我能够使用 Zybo-Z7-20 FPGA 从 RPi Cam v2.1 成功获得 720p-60fps 视频,甚至没有查看数据表(顺便说一句,它可用于 RPi v2.1 cam 上的 IMX219 传感器模块)。这证明这里采用的概念也适用于其他可用的传感器(尽管有一些警告),并且应该为我们提供更多用于 FPGA 项目的 MIPI 图像传感器选项。

在此过程中,我了解到一些注意事项:

如今越来越多的图像传感器没有内部 ISP(图像信号处理器),而是依靠芯片内部的 ISP 来接收视频以实现自动曝光/增益、白平衡等功能。这意味着,如果这些传感器之一是与 FPGA 一起使用,那么我们需要在 FPGA 中自己处理这些功能。在受控的光照条件变化下,通过查看每一帧之后发送到相机的数据,应该可以找到相关的寄存器。另一方面,一些相机模块还带有板载 ISP,在这种情况下,这不会成为问题。(看看来自 Arducam、VEYE 的一些传感器)
并非所有图像传感器本身都支持 720p 或 1080p 等格式。因此,在某些情况下,与用户通过命令行请求的相比,驱动程序会将传感器设置为高度/宽度(或两者)更大的模式。在这种情况下,Raspberry Pi/Jetson 等内部的 ISP(图像信号处理器)应将图像裁剪为用户要求的分辨率。这意味着如果我们使用 FPGA 来接收这个流,那么我们将需要在我们的 FPGA 中进行裁剪。通过为图像起始位置、活动像素和水平和垂直方向的步幅设置适当的值。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

评论(0)
发评论

下载排行榜

全部0条评论

快来发表一下你的评论吧 !

'+ '

'+ '

'+ ''+ '
'+ ''+ ''+ '
'+ ''+ '' ); $.get('/article/vipdownload/aid/'+webid,function(data){ if(data.code ==5){ $(pop_this).attr('href',"/login/index.html"); return false } if(data.code == 2){ //跳转到VIP升级页面 window.location.href="//m.lene-v.com/vip/index?aid=" + webid return false } //是会员 if (data.code > 0) { $('body').append(htmlSetNormalDownload); var getWidth=$("#poplayer").width(); $("#poplayer").css("margin-left","-"+getWidth/2+"px"); $('#tips').html(data.msg) $('.download_confirm').click(function(){ $('#dialog').remove(); }) } else { var down_url = $('#vipdownload').attr('data-url'); isBindAnalysisForm(pop_this, down_url, 1) } }); }); //是否开通VIP $.get('/article/vipdownload/aid/'+webid,function(data){ if(data.code == 2 || data.code ==5){ //跳转到VIP升级页面 $('#vipdownload>span').text("开通VIP 免费下载") return false }else{ // 待续费 if(data.code == 3) { vipExpiredInfo.ifVipExpired = true vipExpiredInfo.vipExpiredDate = data.data.endoftime } $('#vipdownload .icon-vip-tips').remove() $('#vipdownload>span').text("VIP免积分下载") } }); }).on("click",".download_cancel",function(){ $('#dialog').remove(); }) var setWeixinShare={};//定义默认的微信分享信息,页面如果要自定义分享,直接更改此变量即可 if(window.navigator.userAgent.toLowerCase().match(/MicroMessenger/i) == 'micromessenger'){ var d={ title:'基于FPGA的RPi摄像头设计',//标题 desc:$('[name=description]').attr("content"), //描述 imgUrl:'https://'+location.host+'/static/images/ele-logo.png',// 分享图标,默认是logo link:'',//链接 type:'',// 分享类型,music、video或link,不填默认为link dataUrl:'',//如果type是music或video,则要提供数据链接,默认为空 success:'', // 用户确认分享后执行的回调函数 cancel:''// 用户取消分享后执行的回调函数 } setWeixinShare=$.extend(d,setWeixinShare); $.ajax({ url:"//www.lene-v.com/app/wechat/index.php?s=Home/ShareConfig/index", data:"share_url="+encodeURIComponent(location.href)+"&format=jsonp&domain=m", type:'get', dataType:'jsonp', success:function(res){ if(res.status!="successed"){ return false; } $.getScript('https://res.wx.qq.com/open/js/jweixin-1.0.0.js',function(result,status){ if(status!="success"){ return false; } var getWxCfg=res.data; wx.config({ //debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 appId:getWxCfg.appId, // 必填,公众号的唯一标识 timestamp:getWxCfg.timestamp, // 必填,生成签名的时间戳 nonceStr:getWxCfg.nonceStr, // 必填,生成签名的随机串 signature:getWxCfg.signature,// 必填,签名,见附录1 jsApiList:['onMenuShareTimeline','onMenuShareAppMessage','onMenuShareQQ','onMenuShareWeibo','onMenuShareQZone'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2 }); wx.ready(function(){ //获取“分享到朋友圈”按钮点击状态及自定义分享内容接口 wx.onMenuShareTimeline({ title: setWeixinShare.title, // 分享标题 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享给朋友”按钮点击状态及自定义分享内容接口 wx.onMenuShareAppMessage({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 type: setWeixinShare.type, // 分享类型,music、video或link,不填默认为link dataUrl: setWeixinShare.dataUrl, // 如果type是music或video,则要提供数据链接,默认为空 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ”按钮点击状态及自定义分享内容接口 wx.onMenuShareQQ({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到腾讯微博”按钮点击状态及自定义分享内容接口 wx.onMenuShareWeibo({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ空间”按钮点击状态及自定义分享内容接口 wx.onMenuShareQZone({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); }); }); } }); } function openX_ad(posterid, htmlid, width, height) { if ($(htmlid).length > 0) { var randomnumber = Math.random(); var now_url = encodeURIComponent(window.location.href); var ga = document.createElement('iframe'); ga.src = 'https://www1.elecfans.com/www/delivery/myafr.php?target=_blank&cb=' + randomnumber + '&zoneid=' + posterid+'&prefer='+now_url; ga.width = width; ga.height = height; ga.frameBorder = 0; ga.scrolling = 'no'; var s = $(htmlid).append(ga); } } openX_ad(828, '#berry-300', 300, 250);