最近因工作需要买了某款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、工具链、文档、代码等自己想办法。我没有、不知道、别找我(否认三连)

某Hi3516EV300摄像头折腾笔记的更多相关文章

  1. MacBook外置显卡eGPU折腾笔记

    MacBook外置显卡eGPU折腾笔记 硬件选购 当今市场上个人电脑的独立显卡,基本上能选的只有NVIDIA和AMD了,如果你想买外置显卡来打游戏的话,NVIDIA和AMD的都可以,但如果是像我一样准 ...

  2. ASP.NET Core 折腾笔记二:自己写个完整的Cache缓存类来支持.NET Core

    背景: 1:.NET Core 已经没System.Web,也木有了HttpRuntime.Cache,因此,该空间下Cache也木有了. 2:.NET Core 有新的Memory Cache提供, ...

  3. ASP.NET Core 折腾笔记一

    前言: 在ASP.NET Core 1.0时,曾折腾过一次,后因发现不了System.Data而停止. 更因VS2015提示过期Delete掉VS了,其实主要还是笔记本的硬盘空间吃紧. 快双十一了,本 ...

  4. Ubuntu Gnome 14.04.2 lts 折腾笔记

    unity感觉不爽,于是来折腾gnome3 = = 首先去官网下载ubuntu gnome 14.04.2 lts的包(种子:http://cdimage.ubuntu.com/ubuntu-gnom ...

  5. systemtap折腾笔记

    在这货上花费了不少时间,都是受了@agentzh 大神的蛊惑:) 他写的nginx-systemtap-toolkit监测的数据很有价值,对于系统优化实在是利器. 最早折腾systemtap,是在Ub ...

  6. 【转】Kali Linux 新手折腾笔记

    原作者:http://defcon.cn/1618.html 最近在折腾Kali Linux 顺便做一简单整理,至于安装就不再多扯了,估计会出现的问题上一篇文章<VMware虚拟机安装Kali ...

  7. Kali Linux 新手折腾笔记

    http://defcon.cn/1618.html 2014年09月29日 渗透测试 暂无评论 阅读 55,052 次   最近在折腾Kali Linux 顺便做一简单整理,至于安装就不再多扯了,估 ...

  8. Kivy折腾笔记

    最近想用Python开发APP,选择kivy,记录过程 首先是源码安装,各种蛋疼的报错放弃了.cython高版本有问题. python3 -m pip install cython==0.23 pyt ...

  9. 黑苹果+win10双系统折腾笔记

    寒假趁机在家折腾一下黑苹果 笔记本配置:神船K610D I7 4600 ,其他配置思路一样,驱动要自己找 镜像和工具:OS X Yosemite 10.10.3 镜像 WIN10 TLSB 2016 ...

随机推荐

  1. 从函数计算到 Serverless 架构

    前言 随着 Serverless 架构的不断发展,各云厂商和开源社区都已经在布局 Serverless 领域,一方面表现在云厂商推出传统服务/业务的 Serverless 化版本,或者 Serverl ...

  2. 面试常问:HTTP 1.0 和 HTTP 1.1 有什么区别?

    这篇文章会从下面几个维度来对比 HTTP 1.0 和 HTTP 1.1: 响应状态码 缓存处理 连接方式 Host头处理 带宽优化 响应状态码 HTTP/1.0仅定义了16种状态码.HTTP/1.1中 ...

  3. java学习第一天.day01

    Java的编译和运行机制 java文件编译成字节码文件后加载到java缓存中jvm Java的基本语法 1.Java语言严格区分大小写 2.一个Java源文件里可以定义多个Java类,但不能存在多个p ...

  4. 一些有用的数学知识(Updating)

    文章目录 拉格朗日插值公式 微分中值定理 费马引理 拉格朗日中值定理 柯西中值定理 洛必达法则 连分数(NOI2021 D2T2 考点) 定义 结论 定理1 定理2 定理3 定理4 定理5 欧拉公式 ...

  5. Think PHP 完整的带富文本格式以及图片上传,并且在页面上分页展示

    Think php6.0官网网址:序言 · ThinkPHP6.0完全开发手册 · 看云 (kancloud.cn) 下面是基础配置 第一步:创建TP框架,命名为tp composer create- ...

  6. Linux之Samba服务器搭建

    一,samba的基本概念 SMB(Server Messages Block,信息服务块)是一种在局域网上共享文件和打印机的一种通信协议,它为局域网内的不同计算机之间提供文件及打印机等资源的共享服务. ...

  7. OSI模型 TCP/IP协议

    常见术语 网络相关的术语 1.拓扑:物理拓扑-----体现了设备之间的连接关系 逻辑拓扑----设备之间的通信关系 2.数据载荷:传递的实际信息 3.报文(PDU--协议数据单元) 4.数据头部的作用 ...

  8. 【设计模式】Java设计模式 - 动态代理

    [设计模式]Java设计模式 - 动态代理 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 一个有梦有戏的人 @怒放吧德德 最近工作比较忙,没啥时间学习 目录 [设计模 ...

  9. C语言001--hello world编译详解

    1.编写hello.c程序,并编译运行 book@100ask:~/linux/c01$ cat hello.c -n 1 #include <stdio.h> 2 3 int main( ...

  10. Windows下使用SSH连接到旧设备

    正好今天遇到一个旧设备有点问题,需要通过SSH的方式连接上去检查.Windows 10自带了SSH命令,可以直接连接而不必寻求其它工具的支持了.如果看不到图,请点我. 结果发现无法连接,显示协商错误. ...