这里从今天开始给自己挖坑然后慢慢填坑......
打算基于自己买的正点原子的stm32mp157开发板还有OV5640摄像头,完成一个视觉检测的项目,最基础的版本是直接OpenCV打开摄像头然后完成简单的检测;进阶版把sklearn训练的模型通过cpp读取然后显示;最终目标是跑之前训练好的yolo模型。
那么问题就可以分成以下几步走:
- 驱动移植:包括linux的基础驱动,OV5640驱动,LCD驱动等等
- OpenCV+QT的库移植
- Python的机器学习库sklearn的相关模型保存还有C++读取方法
- 深度学习yolo的C++读取方法
那么第一篇显然就是基础的驱动移植,正式内容就此展开!
linux驱动移植
这里直接看正点原子开源的教程就可以了,里面很详细的有讲解linux驱动的移植过程,总结一下就是以下几步骤:
- TF-A:这个是imx这个最常见初学嵌入式linux的开发板芯片所没有的,是一个安全系统,需要完成移植
- U_Boot:经典的导引加载系统
- Linux内核:可剪裁的操作系统
- 根文件系统:这里主要使用Buildroot制作加载
可以直接跟着教程一步步完成。
LCD驱动
完成基础的Linux操作系统搭建后,视觉检测项目就需要一个显示器,买的开发板上就有LCD显示器,可以同样跟着教程完成LCD的驱动移植用于显示图像。
OV5640驱动
这个是这篇文章的重点!!!虽然开发板的出产系统是自带驱动的,但是那个驱动太多了,QT的界面也是已经搭建好的,对于我想从头自己做项目的话,起不到学习作用,所以也是对着出厂设置来自行移植!
这里的话,我搜了网上的很多资料,因为stm32mp1系列的开发板,根据正点原子工作人员的讲法,当时ST公司是限制了其产量,所以用的人很少,大多数都是用imx或者直接rk系列芯片,mp1系列的相关资料就很少 ,我网上就只找到了华清有几个相关的摄像头驱动教程,但是是直接加载到内核,而且写的文件跟正点原子的风格还是不太一样的 ,本着自我学习的精神,我也是自己研究出来了怎么移植,而不是网上搜到的大多数基于正点原子出厂系统的使用!
进入正题,看看如何完成OV5640驱动移植。
设备树修改
需要在相关的设备树dts和dtsi文件中加入相关的内容,涉及到摄像头接口dcmi节点,ov5640节点以及相关的电源和时钟节点信息。
dcmi接口
首先需要添加dcmi节点,这是一个摄像头接口所使用的节点,这里根据正点原子的GPIO口需要在stm32mp157d-atk.dtsi文件中加入以下内容:
c
&pinctrl {
dcmi_pins_b: dcmi-1 {
pins {
pinmux = <STM32_PINMUX('H', 8, AF13)>,/* DCMI_HSYNC */
<STM32_PINMUX('B', 7, AF13)>,/* DCMI_VSYNC */
<STM32_PINMUX('A', 6, AF13)>,/* DCMI_PIXCLK */
<STM32_PINMUX('H', 9, AF13)>,/* DCMI_D0 */
<STM32_PINMUX('H', 10, AF13)>,/* DCMI_D1 */
<STM32_PINMUX('H', 11, AF13)>,/* DCMI_D2 */
<STM32_PINMUX('H', 12, AF13)>,/* DCMI_D3 */
<STM32_PINMUX('H', 14, AF13)>,/* DCMI_D4 */
<STM32_PINMUX('I', 4, AF13)>,/* DCMI_D5 */
<STM32_PINMUX('B', 8, AF13)>,/* DCMI_D6 */
<STM32_PINMUX('E', 6, AF13)>;/* DCMI_D7 */
bias-disable;
};
};
dcmi_sleep_pins_b: dcmi-sleep-1 {
pins {
pinmux = <STM32_PINMUX('H', 8, ANALOG)>,/* DCMI_HSYNC */
<STM32_PINMUX('B', 7, ANALOG)>,/* DCMI_VSYNC */
<STM32_PINMUX('A', 6, ANALOG)>,/* DCMI_PIXCLK */
<STM32_PINMUX('H', 9, ANALOG)>,/* DCMI_D0 */
<STM32_PINMUX('H', 10, ANALOG)>,/* DCMI_D1 */
<STM32_PINMUX('H', 11, ANALOG)>,/* DCMI_D2 */
<STM32_PINMUX('H', 12, ANALOG)>,/* DCMI_D3 */
<STM32_PINMUX('H', 14, ANALOG)>,/* DCMI_D4 */
<STM32_PINMUX('I', 4, ANALOG)>,/* DCMI_D5 */
<STM32_PINMUX('B', 8, ANALOG)>,/* DCMI_D6 */
<STM32_PINMUX('E', 6, ANALOG)>;/* DCMI_D7 */
};
};
};
这一部分相当于给定了dcmi接口所使用的GPIO口的定义。
然后在stm32mp157d-atk.dts中加入如下内容:
c
&dcmi {
status = "okay";
pinctrl-names = "default", "sleep";
pinctrl-0 = <&dcmi_pins_b>;
pinctrl-1 = <&dcmi_sleep_pins_b>;
port {
dcmi_0: endpoint {
remote-endpoint = <&ov5640_0>;
bus-width = <8>;
hsync-active = <0>;
vsync-active = <0>;
pclk-sample = <1>;
pclk-max-frequency = <77000000>;
};
};
};
这里就是在设备树中添加了dcmi的节点,连接到了ov5640的节点,并且定义了一些属性内容,这一部分只要复制过来就可以了。
ov5640
需要在设备树节点中添加ov5640节点,这里可以去参考Documentation/devicetree/bindings/media/i2c/ov5640.txt,里面有通过i2c驱动ov5640需要在设备树中添加内容的示例,基于示例以及正点原子开发板的出厂配置,可以在stm32mp157d-atk.dts添加如下内容:
c
&i2c5 {
pinctrl-names = "default", "sleep";
pinctrl-0 = <&i2c5_pins_a>;
pinctrl-1 = <&i2c5_pins_sleep_a>;
i2c-scl-rising-time-ns = <100>;
i2c-scl-falling-time-ns = <7>;
status = "okay";
/delete-property/dmas;
/delete-property/dma-names;
ov5640: camera@3c {
compatible = "ovti,ov5640";
reg = <0x3c>;
clocks = <&clk_ext_camera>;
clock-names = "xclk";
DOVDD-supply = <&v2v8>;
powerdown-gpios = <&gpioe 11 (GPIO_ACTIVE_HIGH | GPIO_PUSH_PULL)>;
reset-gpios = <&gpioe 1 (GPIO_ACTIVE_LOW | GPIO_PUSH_PULL)>;
rotation = <180>;
status = "okay";
port {
ov5640_0: endpoint {
remote-endpoint = <&dcmi_0>;
bus-width = <8>;
data-shift = <2>;
hsync-active = <0>;
vsync-active = <0>;
pclk-sample = <1>;
pclk-max-frequency = <77000000>;
};
};
};
};
这里就是把txt的内容拉过来,然后适配一下gpio的节点就可以了。
辅助节点
通过对ov5640的节点,可以发现需要时钟定义以及2.8V的电源定义,所以在stm32mp157d-atk.dts的根节点添加如下内容:
c
clocks {
clk_ext_camera: clk-ext-camera {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <24000000>;
};
};
v2v8: regulator-v2v8 {
compatible = "regulator-fixed";
regulator-name = "v2v8";
regulator-min-microvolt = <2800000>;
regulator-max-microvolt = <2800000>;
regulator-always-on;
regulator-over-current-protection;
};
之后就顺利的完成了设备树的内容一致啦。
Linux内核
这里的话其实也不用改,我按照教程学习Linux驱动的时候,按照教程移植过来的内核里面,menuconfig里面是已经把改配置的都已经配置好了。这里就我截个图,如果没配置就按照下面配置一下:
|-----------------------------------------------------------------------------------------------------------------------------------------|
| -> Device Drivers -> <M> Multimedia support -> V4L platform devices -> <M> STM32 Digital Camera Memory Interface (DCMI) support |
|-------------------------------------------------------------------------------------------------------------------------------------------------------|
| -> Device Drivers -> <M> Multimedia support -> I2C Encoders, decoders, sensors and other helper chips -> <M> OmniVision OV5640 sensor support |
我这边是把他都编译成模块化去生成动态加载的ko文件,之后去drivers文件夹里面复制到开发板就可以了。
以上操作均完成后就重新编译,命令如下:
|-------------------------------------------|
| make dtbs uImage LOADADDR=0XC2000040 -j16 |
最终移植加载
这一步也是很重要的,需要把如下的ko文件全部移动到开发板的/lib/modules/5.4.31中:
|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| /drivers/media/i2c/ov5640.ko /drivers/media/mc/mc.ko /drivers/media/v4l2-core/vediodev.ko /drivers/media/v4l2-core/v4l2-fwnode.ko /drivers/media/v4l2-core/videobuf2-common.ko /drivers/media/v4l2-core/videobuf2-memops.ko /drivers/media/v4l2-core/videobuf2-v4l2.ko /drivers/media/v4l2-core/videobuf2-dma-contig.ko /drivers/media/platform/stm32/stm32-dcmi.ko |
然后进去之后就只要输入以下命令就可以:
|------------------------------------------------------------------|
| cd /lib/modules/5.4.31 modprobe stm32-dcmi.ko modprobe ov5640.ko |
大功告成!!