某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、工具鏈、文檔、代碼等自己想辦法。我沒有、不知道、別找我(否認三連)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM