某Hi3516EV300摄像头折腾笔记

最近因工作需要买了某款HI3516DV300开发板,但是价格死贵,于是在国内某著名电商网站上瞎逛,很巧发现一家店铺买摄像头模组,主控HI3516EV300,cmos是IMX335,价格不到200元,然后非常巧我又找到了H3516EV300的SDK,正好IMX335是海思SDK里适配好的cmos,也省去我自己适配(cmos没玩过,我也不会适配),啥也不说了,买!!!

先说一下我为什么会买
1.价格不贵,玩坏了也不心疼
2.先备份flash固件,哪怕不能二次开发,烧回原来的固件也可以做一个普通的IPC摄像头用
3.没有电路图,但是我赌厂商没有修改GPIO的复用功能,大部分GPIO的功能是按照默认引脚功能开发的,因为这样开发最稳妥。(其实能复用的IO也不多,比如网线、USB这种的轻易不会复用,一般都是复用普通GPIO做按键之类的)

接下来就是想办法做二次开发了,这种模组的资料非常有限,只告诉你电源、网线、喇叭等接口,电路图和源码肯定没有,但是没关系,有这些接口简单玩玩已经足够了。
以下内容我默认你已经安装好海思的sdk,包括交叉编译器、各种依赖工具、软件包、uboot源码、kernel源码等。有关安装的详细内容看sdk文档。

第零步,备份固件

这一步是你的救命稻草,如果不小心把固件玩崩了,这是你最后的保险,买到摄像头先上电试试,验证基本功能正常就行,接下来准备备份固件。
有烧录夹的就不用拆芯片了,没有烧录夹的就拆芯片,有烧录器的用烧录器,没烧录器的用单片机,把固件读出来。最好多读几次,避免出错,若这几次读出来的固件一致,保存,归档。不一致就自己想办法,比如缩短连线(一般来说TTL电平的线不要太长),降低时钟频率等。

第一步,找串口

正式出货的商品一般串口是不会引出的,但是板子上通常会预留串口焊盘或测试点(硬件工程师要是敢不预留串口肯定会被软件工程师打死)。所以,这是一道送分题,串口很好找,玩嵌入式的应该都有经验。

波特率设置为115200,非常顺利上电就有打印,内容如下(已删减包含厂家信息的部分内容)

System startup

Uncompress Ok!

U-Boot 2016.11  (Sep 01 2020 - 11:06:10 +0800)hi3516ev300

Relocation Offset is: 07719000
Relocating to 47f19000, new gd at 47ed8ef0, sp at 47ed8ed0
SPI Nor:  hifmc_ip_ver_check(44): Check Flash Memory Controller v100 ...hifmc_ip_ver_check(50):  Found
hifmc_spi_nor_probe(1843): SPI Nor ID Table Version 1.0
hifmc_spi_nor_probe(), SPI Nor(cs 0) ID: 0xb 0x40 0x18 <Found>
hifmc100_spi_nor_probe(152): SPI Nor total size: 16MB
In:    serial
Out:   serial
Err:   serial
Net:   eth0
Hit ctrl+c to stop autoboot:  0 
device 0 offset 0x40000, size 0x540000

SF: 5505024 bytes @ 0x40000 Read: OK
## Booting kernel from Legacy Image at 42000000 ...
   Image Name:   Linux-4.9.37
   Image Type:   ARM Linux Kernel Image (uncompressed)
   Data Size:    2132934 Bytes = 2 MiB
   Load Address: 40008000
   Entry Point:  40008000
   Loading Kernel Image ... OK

Starting kernel ...

Uncompressing Linux... done, booting the kernel.

但是也就到此为止了,内核的打印是关掉的,通过uboot环境变量可以拿到内核地址、rootfs地址等信息,但其实我根本用不到,我是打算自己进行开发的,没必要研究它的原版固件,直接用海思sdk的uboot、kernel最省事

第二步,烧写uboot

前面备份了固件我可以为所欲为,所以简单粗暴一点,我直接把海思sdk中编译好的uboot烧写进flash启动一下试试,日志如下

System startup

Uncompress Ok!

U-Boot 2016.11 (Sep 27 2019 - 11:05:20 +0800)hi3516ev300

Relocation Offset is: 07734000
Relocating to 47f34000, new gd at 47e93ef0, sp at 47e93ed0
SPI Nor:  hifmc_ip_ver_check(44): Check Flash Memory Controller v100 ...hifmc_ip_ver_check(50):  Found
hifmc_spi_nor_probe(1664): SPI Nor ID Table Version 1.0
hifmc_spi_nor_probe(1689): SPI Nor(cs 0) ID: 0xc2 0x20 0x18
hifmc_spi_nor_probe(1754): Block:64KB hifmc_spi_nor_probe(1755): Chip:16MB hifmc_spi_nor_probe(1756): Name:"MX25L128XX"
hifmc100_spi_nor_probe(147): SPI Nor total size: 16MB
NAND:  0 MiB
MMC:   
*** Warning - bad CRC, using default environment

In:    serial
Out:   serial
Err:   serial
Net:   eth0
Warning: eth0 (eth0) using random MAC address - 86:4e:da:cf:fc:e7

Hit any key to stop autoboot:  2 1 0
hisilicon #

非常顺利,可以启动,但是注意这个警告 *** Warning - bad CRC, using default environment
网上搜索一番给出的答案是,一般第一次烧写是肯定没有环境变量的,所以随便设置一个环境变量,保存重启就ok了
然而事情并没有这么简单,我设置环境变量保存重启后这个警告依然存在,用 printenv 命令打印发现环境变量根本没有修改

我排查了以下几点
1.是不是flash兼容性问题?
我拆掉原来的flash,换上自己手头其他品牌的flash问题依旧
2.是不是硬件有特殊设计?比如WP引脚写保护?
在保存环境变量时测量WP引脚,发现是高电平,也就是无写保护
3.uboot问题?
随便给哪个flash烧回原来的uboot,保存环境变量都是正常的,初步可以确定是uboot有改动

到这里就有基本思路了,一般来说海思官方的uboot是正常的,至少在海思的demo板上是正常的。但是我这个模组的电路就不能保证和海思demo板一致了,应该是flash的电路有改动,而且uboot也有改动,肯定是和spi flash读写相关的代码,但是具体出哪些代码有问题,要怎么改,毫无头绪。

在各种折腾几天之后实在没办法,只能祭出逻辑分析仪了,其实原本我是不打算用逻辑分析仪的,因为如果是spi时序有问题根本起不来,我这也是没办法瞎折腾,结果还真的把问题解决了。

逻辑分析仪比较垃圾,国产山寨货,采样率比较低,但也能发现一些线索。
1.uboot启动阶段spi时序正常,时钟12MHz左右
2.保存环境变量时,前面的通信即读取芯片型号、读取寄存器、擦除、忙等是正常的,但开始写入时信号就乱了,很明显是信号频率超过采样率,我的逻辑分析出抓不出来了
3.原版固件在保存时MISO和MOSI信号线上都有数据,海思uboot保存时MISO、MOSI、RST、WP上全都有数据!

现在问题很明显了,写入方式有问题!其实就是两个问题:时钟频率可能太高,不能用4线模式读写

接下来很简单,改uboot就行,为了保险,我把时钟改到最低,同时删除了所有双线、4线读写的接口
后面我把flash换成了w25q128fv,所以在uboot中找到相应flash的代码,改成我这样
没有你的flash?换个flash或自己加,具体怎么加参考其他人写的文档
要修改的文件是 drivers\mtd\spi\hifmc100\hifmc_spi_nor_ids.c

{
    "W25Q128(B/F)V", {0xEF, 0x40, 0x18}, 3, _16M, _64K, 3,
    {
        &READ_STD(0, INFINITE, 33),
        // &READ_FAST(1, INFINITE, 60),
        // &READ_FAST(1, INFINITE, 104),
        // &READ_DUAL(1, INFINITE, 104),
        // &READ_QUAD(1, INFINITE, /* 70 */80),
        0
    },

    {
        &WRITE_STD(0, 256, 33),
        // &WRITE_STD(0, 256, 104),
        // &WRITE_QUAD(0, 256, /* 70 */80),
        0
    },

    {
        &ERASE_SECTOR_64K(0, _64K, 33),
        // &ERASE_SECTOR_64K(0, _64K, 104),
        0
    },
    &spi_driver_w25q256fv,
},

编译、烧写、重启……
还有*** Warning - bad CRC, using default environment
没关系,随便保存一下环境变量,再重启,成功!!!!!!

注意这里有
Error: Disable Quad failed! reg:0x2
不用管,不影响使用

第三步,烧写kernel和rootfs

kernel和rootfs当然先用sdk中已经编译好的最稳妥,但是应该烧到哪个地址呢?乱烧可能导致uboot环境变量覆盖内核,查看uboot源码可知uboot环境变量的保存地址,具体方法略,总之我这里是将kernel烧写在1M的位置,rootfs烧写在5M的位置

uboot的环境变量应该如何设置?从海思《裸烧及非裸烧升级使用手册》中找到的环境变量设置如下

setenv bootargs 'mem=512M console=ttyAMA0,115200 clk_ignore_unused rw root=/dev/mtdblock2 rootfstype=jffs2 mtdparts=hi_sfc:1M(u-boot.bin),9M(kernel),16M(rootfs.jffs2)'
setenv bootcmd 'sf probe 0; sf read 4a000000 100000 900000; bootm 4a000000'

很显然这款芯片内存只有128M,可用的内存地址也不是4a000000,根据实际情况修改成这样,mem=64M不是写错了,这是给内核的内存,其余的内存供海思的多媒体系统使用

setenv bootargs 'mem=64M console=ttyAMA0,115200 clk_ignore_unused rw root=/dev/mtdblock2 rootfstype=jffs2 mtdparts=hi_sfc:1M(u-boot.bin),4M(kernel),9M(rootfs.jffs2)'
setenv bootcmd 'sf probe 0; sf read 42000000 100000 400000; bootm 42000000'

保存重启,然后……


System startup

Uncompress Ok!

U-Boot 2016.11 (Oct 30 2021 - 19:08:33 +0800)hi3516ev300

Relocation Offset is: 07734000
Relocating to 47f34000, new gd at 47e93ef0, sp at 47e93ed0
SPI Nor:  hifmc_ip_ver_check(44): Check Flash Memory Controller v100 ...hifmc_ip_ver_check(50):  Found
hifmc_spi_nor_probe(1667): SPI Nor ID Table Version 1.0
hifmc_spi_nor_probe(1692): SPI Nor(cs 0) ID: 0xef 0x40 0x18
spi_w25q256fv_qe_enable(193): Error: Disable Quad failed! reg:0x2
hifmc_spi_nor_probe(1757): Block:64KB hifmc_spi_nor_probe(1758): Chip:16MB hifmc_spi_nor_probe(1759): Name:"W25Q128(B/F)V"
hifmc100_spi_nor_probe(147): SPI Nor total size: 16MB
NAND:  0 MiB
MMC:   
In:    serial
Out:   serial
Err:   serial
Net:   eth0
Warning: eth0 (eth0) using random MAC address - 16:29:bd:47:91:f4

Hit any key to stop autoboot:  0 
device 0 offset 0x100000, size 0x400000

SF: 4194304 bytes @ 0x100000 Read: OK
## Booting kernel from Legacy Image at 42000000 ...
   Image Name:   Linux-4.9.37
   Image Type:   ARM Linux Kernel Image (uncompressed)
   Data Size:    3398021 Bytes = 3.2 MiB
   Load Address: 40008000
   Entry Point:  40008000
   Loading Kernel Image ... OK

Starting kernel ...

Booting Linux on physical CPU 0x0
Linux version 4.9.37 (pub@BVT-SDK) (gcc version 6.3.0 (HC&C V1R3C00SPC200B005_20190606) ) #1 Fri Sep 27 11:06:38 CST 2019
CPU: ARMv7 Processor [410fc075] revision 5 (ARMv7), cr=10c53c7d
CPU: div instructions available: patching division code
CPU: PIPT / VIPT nonaliasing data cache, VIPT aliasing instruction cache
OF: fdt:Machine model: Hisilicon HI3516EV300 DEMO Board
Memory policy: Data cache writeback
CPU: All CPU(s) started in SVC mode.
Built 1 zonelists in Zone order, mobility grouping on.  Total pages: 16256
Kernel command line: mem=64M console=ttyAMA0,115200 clk_ignore_unused rw root=/dev/mtdblock2 rootfstype=jffs2 mtdparts=hi_sfc:1M(u-boot.bin),4M(kernel),9M(rootfs.jffs2)
PID hash table entries: 256 (order: -2, 1024 bytes)
Dentry cache hash table entries: 8192 (order: 3, 32768 bytes)
Inode-cache hash table entries: 4096 (order: 2, 16384 bytes)
Memory: 57916K/65536K available (5042K kernel code, 181K rwdata, 1260K rodata, 176K init, 249K bss, 7620K reserved, 0K cma-reserved)
Virtual kernel memory layout:
    vector  : 0xffff0000 - 0xffff1000   (   4 kB)
    fixmap  : 0xffc00000 - 0xfff00000   (3072 kB)
    vmalloc : 0xc4800000 - 0xff800000   ( 944 MB)
    lowmem  : 0xc0000000 - 0xc4000000   (  64 MB)
    modules : 0xbf000000 - 0xc0000000   (  16 MB)
      .text : 0xc0008000 - 0xc04f4d08   (5044 kB)
      .init : 0xc0632000 - 0xc065e000   ( 176 kB)
      .data : 0xc065e000 - 0xc068b460   ( 182 kB)
       .bss : 0xc068d000 - 0xc06cb448   ( 250 kB)
SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
NR_IRQS:16 nr_irqs:16 16
Gic dist init...
arm_arch_timer: Architected cp15 timer(s) running at 50.00MHz (phys).
clocksource: arch_sys_counter: mask: 0xffffffffffffff max_cycles: 0xb8812736b, max_idle_ns: 440795202655 ns
sched_clock: 56 bits at 50MHz, resolution 20ns, wraps every 4398046511100ns
Switching to timer-based delay loop, resolution 20ns
clocksource: arm,sp804: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 637086815595 ns
Console: colour dummy device 80x30
Calibrating delay loop (skipped), value calculated using timer frequency.. 100.00 BogoMIPS (lpj=500000)
pid_max: default: 32768 minimum: 301
Mount-cache hash table entries: 1024 (order: 0, 4096 bytes)
Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes)
CPU: Testing write buffer coherency: ok
Setting up static identity map for 0x40008200 - 0x40008258
devtmpfs: initialized
VFP support v0.3: implementor 41 architecture 2 part 30 variant 7 rev 5
clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns
futex hash table entries: 256 (order: -1, 3072 bytes)
pinctrl core: initialized pinctrl subsystem
NET: Registered protocol family 16
DMA: preallocated 256 KiB pool for atomic coherent allocations
Serial: AMBA PL011 UART driver
12040000.uart: ttyAMA0 at MMIO 0x12040000 (irq = 20, base_baud = 0) is a PL011 rev2
console [ttyAMA0] enabled
SCSI subsystem initialized
ssp-pl022 12070000.spi: ARM PL022 driver, device ID: 0x00800022
ssp-pl022 12070000.spi: mapped registers from 0x12070000 to c486b000
ssp-pl022 12071000.spi: ARM PL022 driver, device ID: 0x00800022
ssp-pl022 12071000.spi: mapped registers from 0x12071000 to c486f000
usbcore: registered new interface driver usbfs
usbcore: registered new interface driver hub
usbcore: registered new device driver usb
Linux video capture interface: v2.00
clocksource: Switched to clocksource arch_sys_counter
NET: Registered protocol family 2
TCP established hash table entries: 1024 (order: 0, 4096 bytes)
TCP bind hash table entries: 1024 (order: 0, 4096 bytes)
TCP: Hash tables configured (established 1024 bind 1024)
UDP hash table entries: 256 (order: 0, 4096 bytes)
UDP-Lite hash table entries: 256 (order: 0, 4096 bytes)
NET: Registered protocol family 1
RPC: Registered named UNIX socket transport module.
RPC: Registered udp transport module.
RPC: Registered tcp transport module.
RPC: Registered tcp NFSv4.1 backchannel transport module.
workingset: timestamp_bits=30 max_order=14 bucket_order=0
NFS: Registering the id_resolver key type
Key type id_resolver registered
Key type id_legacy registered
jffs2: version 2.2 (NAND) (ZLIB) (RTIME) (c) 2001-2006 Red Hat, Inc.
Block layer SCSI generic (bsg) driver version 0.4 loaded (major 252)
io scheduler noop registered
io scheduler deadline registered (default)
io scheduler cfq registered
pl061_gpio 120b0000.gpio_chip: PL061 GPIO chip @0x120b0000 registered
pl061_gpio 120b1000.gpio_chip: PL061 GPIO chip @0x120b1000 registered
pl061_gpio 120b2000.gpio_chip: PL061 GPIO chip @0x120b2000 registered
pl061_gpio 120b3000.gpio_chip: PL061 GPIO chip @0x120b3000 registered
pl061_gpio 120b4000.gpio_chip: PL061 GPIO chip @0x120b4000 registered
pl061_gpio 120b5000.gpio_chip: PL061 GPIO chip @0x120b5000 registered
pl061_gpio 120b6000.gpio_chip: PL061 GPIO chip @0x120b6000 registered
pl061_gpio 120b7000.gpio_chip: PL061 GPIO chip @0x120b7000 registered
pl061_gpio 120b8000.gpio_chip: PL061 GPIO chip @0x120b8000 registered
pl061_gpio 120b9000.gpio_chip: PL061 GPIO chip @0x120b9000 registered
brd: module loaded
hisi-sfc hisi_spi_nor.0: SPI Nor ID Table Version 1.2
hisi-sfc hisi_spi_nor.0: all blocks is unlocked.
hisi-sfc hisi_spi_nor.0: w25q128(b/f)v (Chipsize 16 Mbytes, Blocksize 64KiB)
3 cmdlinepart partitions found on MTD device hi_sfc
3 cmdlinepart partitions found on MTD device hi_sfc
Creating 3 MTD partitions on "hi_sfc":
0x000000000000-0x000000100000 : "u-boot.bin"
0x000000100000-0x000000500000 : "kernel"
0x000000500000-0x000000e00000 : "rootfs.jffs2"
SPI Nand ID Table Version 2.7
Cannot found a valid SPI Nand Device
hisi_spi_nand_probe(175): Error: driver probe, result: -19
FEPHY:addr=1, la_am=0xb, ldo_am=0x4, r_tuning=0x1e
libphy: hisi_femac_mii_bus: probed
libphy: Fixed MDIO Bus: probed
Generic PHY 10041100.mdio:01: attached PHY driver [Generic PHY] (mii_bus:phy_addr=10041100.mdio:01, irq=-1)
phy_id=0x20669903, phy_mode=mii
hisi-femac 10040000.ethernet: using random MAC address 26:68:c3:91:3f:a8
xhci-hcd xhci-hcd.0.auto: xHCI Host Controller
xhci-hcd xhci-hcd.0.auto: new USB bus registered, assigned bus number 1
xhci-hcd xhci-hcd.0.auto: hcc params 0x0220fe6c hci version 0x110 quirks 0x20010010
xhci-hcd xhci-hcd.0.auto: irq 132, io mem 0x10030000
hub 1-0:1.0: USB hub found
hub 1-0:1.0: 1 port detected
xhci-hcd xhci-hcd.0.auto: xHCI Host Controller
xhci-hcd xhci-hcd.0.auto: new USB bus registered, assigned bus number 2
usb usb2: We don't know the algorithms for LPM for this host, disabling LPM.
hub 2-0:1.0: USB hub found
hub 2-0:1.0: hub can't support USB3.0
usbcore: registered new interface driver usb-storage
mousedev: PS/2 mouse device common for all mice
hibvt_rtc 120e0000.rtc: rtc core: registered 120e0000.rtc as rtc0
hibvt_rtc 120e0000.rtc: RTC driver for hibvt enabled
i2c /dev entries driver
hibvt-i2c 12060000.i2c: hibvt-i2c0@100000hz registered
hibvt-i2c 12061000.i2c: hibvt-i2c1@100000hz registered
hibvt-i2c 12062000.i2c: hibvt-i2c2@100000hz registered
uvcvideo: Unable to create debugfs directory
usbcore: registered new interface driver uvcvideo
USB Video Class driver (1.1.1)
sdhci: Secure Digital Host Controller Interface driver
sdhci: Copyright(c) Pierre Ossman
sdhci-pltfm: SDHCI platform and OF driver helper
mmc0: SDHCI controller on 10010000.sdhci [10010000.sdhci] using ADMA in legacy mode
mmc1: SDHCI controller on 10020000.sdhci [10020000.sdhci] using ADMA in legacy mode
usbcore: registered new interface driver usbhid
usbhid: USB HID core driver
Initializing XFRM netlink socket
NET: Registered protocol family 17
NET: Registered protocol family 15
Key type dns_resolver registered
hibvt_rtc 120e0000.rtc: hctosys: unable to read the hardware clock
clk: Not disabling unused clocks
VFS: Mounted root (jffs2 filesystem) on device 31:2.
devtmpfs: error mounting -2
Freeing unused kernel memory: 176K (c0632000 - c065e000)
This architecture does not have kernel memory protection.
Kernel panic - not syncing: No working init found.  Try passing init= option to kernel. See Linux Documentation/init.txt for guidance.
CPU: 0 PID: 1 Comm: swapper Not tainted 4.9.37 #1
Hardware name: Generic DT based system
Backtrace: 
[<c0012f6c>] (dump_backtrace) from [<c0013250>] (show_stack+0x18/0x1c)
 r7:00000000 r6:c05a03c4 r5:00000000 r4:c068d2e8
[<c0013238>] (show_stack) from [<c025da5c>] (dump_stack+0x24/0x28)
[<c025da38>] (dump_stack) from [<c007867c>] (panic+0xe8/0x250)
[<c0078598>] (panic) from [<c04efd4c>] (__irq_alloc_descs+0x0/0x22c)
 r3:00000000 r2:00000000 r1:c3fee700 r0:c05a03c4
 r7:00000000
[<c04efc50>] (kernel_init) from [<c000fca8>] (ret_from_fork+0x14/0x2c)
 r5:c04efc50 r4:00000000
---[ end Kernel panic - not syncing: No working init found.  Try passing init= option to kernel. See Linux Documentation/init.txt for guidance.

启动失败了,最关键的打印信息是这个devtmpfs: error mounting -2
搜索一番发现最常见的原因是文件系统错误,比如设置的是jffs2,实际烧进去的是yaffs2;设置的块大小是64KB,实际编译的文件系统块大小是256KB等
不用怀疑,我这些设置都是对的,rootfs镜像也没有烧错,所以又一次陷入僵局

睡一觉起来以后我又想到了uboot出现的flash读写问题,没错我光改了uboot没改内核,这里读取rootfs出错的本质和uboot遇到的问题是一样的。当然改内核的过程中也遇到一些问题,具体过程就不说了。
先给出最重要的参考文件《基于Hifmcv100控制器的Flash移植指南》,不想看的直接看下面的总结

内核一共要改两个文件

第一个文件为drivers\mtd\spi-nor\spi-nor.c
找到你的flash型号,比如我这里的代码原来是这样的

{ "w25q128(b/f)v", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_QUAD_READ), PARAMS(winbond), CLK_MHZ_2X(104) },

修改为

{ "w25q128(b/f)v", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) },

为什么这么改?首先要禁用双线和4线读写,SPI_NOR_QUAD_READ删掉
其次 PARAMS(winbond) 指的是使用内核自带的 winbond 读写接口,查看源码可知,自带的接口里面有双线和4线读写,所以不能用内核自带接口,最后一个是时钟,一起删掉吧

第二个文件为drivers\mtd\spi-nor\hisfc350\hisfc350_spi_ids.c
因为我们禁用了自带的读写接口,这时这个文件里面的读写接口才会生效,这个文件几乎和uboot一致,所以修改方法也是一样的,比如我就改成了这样

{
    "W25Q128(B/F)V", {0xEF, 0x40, 0x18}, 3, _16M, _64K, 3,
    {
        &READ_STD(0, INFINITE, 33),
        0
    },
    {
        &WRITE_STD(0, 256, 33),
        0
    },
    {
        &ERASE_SECTOR_64K(0, _64K, 33),
        0
    },
    &spi_driver_w25q256fv,
},

如果你想让启动速度快一点,可以把时钟改得更高,只要不超过芯片手册的说明且实际读写不会出错即可,记得uboot和kernel一起改。

最后编译、烧写、重启……

成功!!!!!!

第四步 hello world

这还用说吗?linux安装nfs服务端,摄像头挂载nfs,只要hello world能跑起来,后面的都不是问题

第五步 图像

还记得我前面说的吗?我赌它的电路没有改,特别是sensor相关电路,一般来说不会轻易修改。如何快速验证呢?编译sdk的sample例程,我这里编译的是 sample_venc.c。
具体编译方法就不说了,你可以直接用sdk的makefile,也可以自己提取相关文件,自己写makefile。
编译出来以后还是nfs挂载运行,程序跑起来以后按提示操作,如果顺利的话你会在同级目录中看到录制的文件,我的这个摄像头很顺利出图了。接下来就可以尽情发挥了。当然图像的坑其实很深,ISP调试没有专业的环境和设备根本玩不来,这个完全是我的知识盲区,所以为什么一定要选IMX335,因为SDK里面已经有一份调好的ISP参数了。

第六步 实际应用之电子显微镜

用它来做电子显微镜也是我在B站看到某位up主受到的启发,非常巧的是,他用的就是同款摄像头模组做的电子显微镜,不过有一个问题,就是延迟比较大,用起来很不舒服。摄像头自带的程序用的是rtsp推流,不论是用vlc、ffplay还是模组厂商提供的客户端,效果都不太理想。厂商的客户端应该是效果最好的,但是通过拍摄秒表并截图的方式测量的延迟在600ms到1s左右,有时候也会到2s左右。vlc和ffplay最好的情况也在1s以上,普遍在2s左右,我试过各种参数优化都没有将延迟控制在1s以内。所以现在的目标很明确了,就是做一个延迟尽可能低的推流观看方案。

简单说一下我的方案,首先个人精力有限,不会去做rtsp,肯定是怎么简单怎么来,而且ffplay非常强大可以直接播放h264的裸流,所以直接用udp发送h264裸流是最简单的方案,而且直接发送h264裸流也可以省去rtsp封装等的延迟。

第一件事当然不是开发摄像头软件,而是测试ffplay的延迟,方法很简单,准备一段h264的裸流视频,视频的画面要有时间戳,最简单的方法就是录制一段秒表画面(电脑录屏或手机拍摄),然后用ffmpeg将h264裸流抽取出来(具体命令略),本地再简单写一个程序将h264一帧一帧通过udp发送出去,发送的同时打印帧序号之类的东西,最后使用ffplay从udp端口播放,播放的同时截图保存ffplay的画面和发送的帧序号来计算延迟。

总之经过我的一番调试,ffplay的播放延迟最低只有2帧,这应该是最低的播放延迟了,其他参数不管怎么调最少都是“2+cpu核心数”帧(为什么是这个数字?与ffplay的多线程播放机制有关,有兴趣的自己研究ffmpeg的源码)。

最终的播放命令是这样
./ffplay "udp://192.168.1.106:34543" -fflags nobuffer -flags low_delay -f h264 -threads 1 -thread_type frame -infbuf -an -sync video -fast -x 640 -y 360 -analyzeduration 100000 -framerate 30 -vf setpts=0.9*PTS
简单解释几个参数

  1. udp://127.0.0.1:1234 这个不用解释了吧
  2. -x、-y是图像大小,如果电脑屏幕分辨率小于图像分辨率,你的鼠标会点不到关闭按钮,对于0基础的人来说就傻眼了
  3. -analyzeduration 100000 码流探测的时间,单位微秒,可以设置在1秒以内,加快出图时间
  4. -framerate 30 不用解释吧?填写实际帧率
  5. -vf setpts=0.9*PTS 设置回放速率为真实速率的 10/9 倍,也就是略高于真实速率,一定程度上可以避免因为误差等原因导致延迟越来越大的问题,具体效果是不是有用也不好说,有时候不加这个参数也没问题

摄像头模组的编码过程略,总之经过我的一番努力,播放延迟被控制在了200-300ms左右,实际使用起来还是有点不舒服,但是比上面那些方案要好得多

当然这个代码还有优化空间,我现在使用的是VI离线VPSS离线模式,如果都设置为在线模式,并且开启低延迟相关的设置,再优化个100ms是完全有可能的,但是我对海思芯片的了解实在有限,照着手册的介绍的接口试着改了改各种报错,如果有懂行的朋友欢迎留言。

第七步 实际应用之家用摄像头

这个功能我没有实际开发,但是可以给大家提供一点思路

  1. 将视频(音频可选)封装为ts流并发送
  2. 使用树莓派(土豪可以用自己服务器、家用NAS等)之类的接收数据并保存

第八步 交给大家自由发挥吧

比如做成运动相机、航拍相机,或者自己开发wifi驱动(IPC常用的wifi模组RTL8188和RTL8189)做成无线图传什么的

Q&A

Q: 哪里买的摄像头?哪家店?什么牌子?
A: 就这么几家电商网站,你觉得谁最可能卖这种东西?哪家店?什么牌子?我只能告诉你关键字“摄像头模组”,其他的不知道,别问(狗头保命.jpg)

Q: 海思SDK哪里搞的?
A: 你猜我哪里搞的?百度是个不靠谱的搜索引擎,某些电商网站的搜索栏其实很好用,我这都明示了,懂?

Q: 海思的SDK能发我吗?
A: 不能,SDK写得很清楚“保密”,别人传播是他的事情,我转发就变成我的问题了。(智能水表不需要上门抄表、电表在楼道、物业费用手机交的从不拖欠、从不点外卖、快递放代收点)

附录:

开源链接
https://gitee.com/dma/hi3516ev300_camera
文章中提到的部分代码和编译好的固件都在这里,至于海思SDK、工具链、文档、代码等自己想办法。我没有、不知道、别找我(否认三连)

posted @ 2021-11-22 00:19  迷路的指针  阅读(1289)  评论(0编辑  收藏  举报