LiCheePi Zero底层开发

GPIO引脚图

各种文件

烧录工具:sunxi-tools-spi-rebase.zip

根文件系统:buildroot-2019.08.tar.bz2

交叉编译器:gcc-linaro-6.3.1-2017.02-x86_64_arm-linux-gnueabihf.tar.xz

Uboot:u-boot-3s-current.zip

  • spi nor flash启动:u-boot-3s-spi-experimental.zip

主线Linux内核:linux-zero-4.10.y.zip、linux-zero-4.13.y.zip

BSP内核:v3s_lichee.zip( camdriod ,需要从中剥离出内核)

一、编译下载镜像

1. 编译及相关配置

1.1编译uboot

1
2
3
4
5
6
#480x272LCD:LicheePi_Zero_480x272LCD_defconfig
#800x480LCD:LicheePi_Zero_800x480LCD_defconfig
#make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- LicheePi_Zero_480x272LCD_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- LicheePi_Zero_defconfig
make ARCH=arm menuconfig#配置
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j8

编译完成后,在当前目录下生成了u-boot-sunxi-with-spl.bin

自定义uboot配置

该部分属于自定义部分,可以直接采用默认配置

  • Architecture select架构选择:ARM架构
  • ARM architecture
  • DDR相关配置
  • LCD相关配置
  • Boot images
  • CPU clock frequency:时钟频率配置
  • delay in seconds before automatically booting:开机等待时间的秒数
  • SPL/TPL
  • MMC raw mode: by sector 按扇区
  • Address on the MMC to load U-Boot from mmc加载uboot的地址
  • Support GPIO 支持GPIO
  • Support I2C 支持I2C
  • Support common libraries 支持通用lib
  • Support disk paritions 支持分区
  • Support generic libraries 支持一般lib库
  • Support MMC 支持MMC
  • Support power drivers 支持电源驱动
  • Support serial 支持串口
修改代码支持SD卡

源码:u-boot-3s-current

增加uboot源码中include/configs/sun8i.h

1
2
3
4
5
#define CONFIG_BOOTCOMMAND	  "load mmc 0:1 0x41000000 zImage;"  \
"load mmc 0:1 0x41800000 sun8i-v3s-licheepi-zero.dtb;" \
"bootz 0x41000000 - 0x41800000;"

#define CONFIG_BOOTARGS "console=ttyS0,115200 panic=5 rootwait root=/dev/mmcblk0p2 earlyprintk rw vt.global_cursor_default=0"
修改代码支持SPI FLASH

源码:u-boot-3s-spi-experimental

如果flash大于16M,需要修改uboot配置,勾选flash bank支持选项( CONFIG_SPI_FLASH_BAR )

增加uboot源码中include/configs/sun8i.h

1
2
3
4
5
6
7
#define CONFIG_BOOTCOMMAND   "sf probe 0; "                           \
"sf read 0x41800000 0x100000 0x10000; " \/*从flash0x100000(1MB)位置读取dtb放到内存0x41800000偏移处。 //如果是bsp的bin,则是0x41d00000*/
"sf read 0x41000000 0x110000 0x400000; " \
"bootz 0x41000000 - 0x41800000"

#define CONFIG_BOOTARGS "console=ttyS0,115200 earlyprintk panic=5 rootwait " \
"mtdparts=spi32766.0:1M(uboot)ro,64k(dtb)ro,4M(kernel)ro,-(rootfs) root=31:03 rw rootfstype=jffs2"

1.2编译Linux内核

1.2.1 主线内核
1
2
3
4
5
6
7
#bsp内核中用到是arm-linux-gnueabi-,工具链为自带的4.6.3
make ARCH=arm licheepi_zero_defconfig
make ARCH=arm menuconfig #add bluethooth, etc.
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16 modules
#INSTALL_MOD_PATH:把指定为M的驱动安装到指定文件系统根目录下面的/lib/modules目录下面
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16 INSTALL_MOD_PATH=指定文件系统根目录 modules_install

编译完成后,zImage在arch/arm/boot/下,驱动模块在INSTALL_MOD_PATH指定的目录下

使用SPI的uboot时需要配置
1
2
3
4
5
6
7
Device Drivers
<*> Memory Technology Device (MTD) support --->
<*> Command line partition table parsing//解析uboot传递过来的flash分区信息。
<*> SPI-NOR device support --->//支持SPI-NOR 设备
File systems
[*] Miscellaneous filesystems --->
<*> Journalling Flash File System v2 (JFFS2) support//添加对jffs2文件系统的支持

设备树中添加spi flash节点信息

1
2
3
4
5
6
7
8
9
10
&spi0 {
status ="okay";
xt25f128b:xt25f128b@0 {
compatible = "jedec,spi-nor";
reg = <0x0>;
spi-max-frequency = <50000000>;
#address-cells = <1>;
#size-cells = <1>;
};
};

内核代码drivers/mtd/devices/m25p80.c中添加flash型号

1
2
3
4
5
6
static const struct spi_device_id m25p_ids[] = {
...
{"w25q80bl"}, {"w25q128"},/* add xt25f128b --by jz */{"xt25f128b"},/* add xt25f128b --by jz */ {"w25q256"},
...
{ },
};

内核代码drivers/mtd/spi-nor/spi-nor.c中添加flash型号

1
2
3
4
5
6
7
8
static const struct flash_info spi_nor_ids[] = {
...
{ "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) },
{"xt25f128b",INFO(0x0b4018, 0, 64 * 1024, 256, 0) },/*add xt25f128b by j.z.*/
{ "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K) },
...
{ },
};
  • mkfs.jffs2 使用的最小擦除尺寸是8KB,而spi flash的扇区大小是4KB,所以按照扇区擦除的话,会无法使用,所以必须使用块擦除。 所以这里应该去掉SECT_4K
wifi使能配置

linux-zero-4.10.y.zip 内核才有rtl8723bswifi的驱动(默认支持,编译成模块)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
-> Device Drivers
-> Staging drivers (STAGING [=y])
<M> Realtek RTL8723BS SDIO Wireless LAN NIC driver (CONFIG_RTL8723BS)


//尝试去支持ov2640 但是貌似不起效 先记着
-> Device Drivers
-> Multimedia support (MEDIA_SUPPORT [=y])
[*] Cameras/video grabbers support
[*] V4L platform devices --->
[ ] Autoselect ancillary drivers (tuners, sensors, i2c, spi, frontends)
-> I2C Encoders, decoders, sensors and other helper chips
<M> OmniVision OV2640 sensor support (CONFIG_VIDEO_OV2640)
<M> OmniVision OV2659 sensor support
<M> OmniVision OV7640 sensor support
<M> OmniVision OV7670 sensor support
//尝试去支持lcd
-> Device Drivers
Graphics support --->
[*] Backlight & LCD device support --->//LCD和背光控制
Console display driver support --->//控制台Framebuffer支持
[*] Framebuffer Console Rotation
[*] Bootup logo --->//linux启动的开机界面

使能之后还需要rtl8723bs_nic.binwifi固件,放到 /lib/firmware/rtlwifi/ 中,没有的话要自己建立。

1.2.2 BSP内核
  • 解压v3s_lichee.zip, BSP内核源码在lichee/linux-3.4下

    • 采用WhyCan上大佬的配置文件lichee_BSP_config :cp lichee_BSP_config .config

    • 配置内核 make ARCH=arm menuconfig (下面这些均基于默认配置)

      • 使能USB摄像头驱动(已经使能):

        1
        2
        3
        4
        5
        -> Device Drivers
        -> Multimedia support (MEDIA_SUPPORT [=y])
        -> Video capture adapters (VIDEO_CAPTURE_DRIVERS [=y])
        -> V4L USB devices (V4L_USB_DRIVERS [=y])
        -><M> USB Video Class (UVC) CONFIG_USB_VIDEO_CLASS
      • 使能DVP/MIPI摄像头(需选上ov2640):

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        -> Device Drivers
        -> Multimedia support (MEDIA_SUPPORT [=y])
        -> Video capture adapters (VIDEO_CAPTURE_DRIVERS [=y])
        -> V4L platform devices (V4L_PLATFORM_DRIVERS [=y])
        -> SoC camera support (SOC_CAMERA [=y])
        <M> ov2640 camera support(默认没有选上)
        <M> ov5642 camera support
        <M> sunxi video front end (camera and etc)driver
        <M> v4l2 driver for SUNXI
        <*> sunxi video encoder and decoder support
      • 由于camdriod原始的内核配置是为了在spi nor flash上运行而配置的,没有ext4支持,所以需要额外添加ext4支持(已经使能):

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        File systems  --->
        <*> The Extended 4 (ext4) filesystem
        [*] Use ext4 for ext2/ext3 file systems (NEW)
        [*] Ext4 extended attributes (NEW)
        [ ] Ext4 POSIX Access Control Lists (NEW)
        [ ] Ext4 Security Labels (NEW)
        [ ] EXT4 debugging support (NEW)
        [ ] JBD2 (ext4) debugging support (NEW)
        #支持大文件,以便于挂载文件系统
        [*] Enable the block layer --->
        [*] Support for large (2TB+) block devices and files
        #加上CGROUPS支持
        -> General setup
        [*] Control Group support --->
        #开启SWAP
        -> General setup
        [*] Support for paging of anonymous memory (swap)
      • 开启 FHANDLE 特性( 否则debian下会出现报错)(已经使能):

        1
        2
        -> General setup
        [*] open by fhandle syscalls (EXPORTFS [=y])
      • 开启wifi功能(AW_RF_PM功能未使能)

        1
        2
        3
        4
        5
        6
        7
        8
        9
        #开启RTL8723BS的支持
        -> Device Drivers
        -> Network device support (NETDEVICES [=y])
        -> Wireless LAN (WLAN [=y])
        <M> Realtek 8723B SDIO WiFi
        #开启AW_RF_PM功能
        -> Device Drivers
        -> Misc devices
        [*] Allwinner rf module pm driver
  • 编译并提取bsp内核

    • 解压buildroot/dl/gcc-linaro.tar.bz2到lichee/out/sun8iw8p1/linux/common/buildroot/external-toolchain

      1
      2
      3
      4
      5
      6
      7
      #在lichee目录下
      mkdir -p out/sun8iw8p1/linux/common/buildroot/external-toolchain
      tar --strip-components=1 -jxf buildroot/dl/gcc-linaro.tar.bz2 -C out/sun8iw8p1/linux/common/buildroot/external-toolchain
      touch out/sun8iw8p1/linux/common/buildroot/external-toolchain/.installed

      mv out/sun8iw8p1/linux/common/buildroot/external-toolchain/gcc-linaro/* out/sun8iw8p1/linux/common/buildroot/external-toolchain/
      rm -rf out/sun8iw8p1/linux/common/buildroot/external-toolchain/gcc-linaro/
    • 安装lib32z1解决64位的操作系统中没有32位的类库的问题

    1
    2
    sudo apt-get update
    sudo apt-get install lib32z1
    • 在linux-3.4目录下执行:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
        #使用解压的工具链
    export PATH=../out/sun8iw8p1/linux/common/buildroot/external-toolchain/bin:../tools/pack/pctools/linux/android:$PATH
    #清除上次编译
    ./scripts/build_tiger-cdr.sh clean
    #直接编译
    #后面的选项根据lichee\linux-3.4\arch\arm\configs\sun8iw8p1smp_defconfig来配置
    make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- -j16
    make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- -j16 modules
    make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- -j16 INSTALL_MOD_PATH=/home/null/Code/LiCheePi_Zero/Image/cam_image/rootfs modules_install

    #LICHEE_JLEVEL:-j${LICHEE_JLEVEL}
    #LICHEE_JLEVEL=8 ./scripts/build_tiger-cdr.sh
    #./scripts/build_sun8iw8p1.sh all
1.2.3 单独编译设备树
1
2
3
4
5
make ARCH=arm licheepi_zero_defconfig
make dtbs ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j8
#bsp内核编译设备树:
dtc -I dts -O dtb -o ../sun8i-v3s-blueberrypi-csi-ov2640-480x272LCD.dtb ../sun8i-v3s-blueberrypi-csi-ov2640-480x272LCD.dts
./scripts/dtc/dtc -I dts -O dtb -o ../sun8i-v3s-blueberrypi-csi-ov2640-480x272LCD.dtb ./arch/arm/boot/dts/sun8i-v3s-blueberrypi-csi-ov2640-480x272LCD.dts

如果设备树中有include包含关系,是不能够直接用dtc编译的,会报include相关的错。

DTC本身不支持#include语法,其正确语法为/include/

对于以下稍微复杂一点(包含#include,宏,*.h等)的设备树,以上的方法不免有些笨拙。

由于“#include”“宏”等都是C的特征,因此可以使用CPP(C Preprocessor)命令对dts源文件进行处理,完成文件包含与宏置换的工作。

用下面的脚本dts2dtb.sh可以实现目的:

1
2
3
4
5
6
7
8
9
10
#/bin/bash
#set -vx
device="your_device_name"
src_dts=$device.dts
tmp_dts=$device.tmp.dts
dst_dtb=$device.dtb

cpp -nostdinc -I. -undef -x assembler-with-cpp $src_dts > $tmp_dts
dtc -O dtb -b 0 -o $dst_dtb $tmp_dts
rm $tmp_dts
1.2.4 反编译设备树
1
2
3
4
./scripts/dtc/dtc -I dtb -O dts -o ../sun8i-v3s-licheepi-zero.dts ./arch/arm/boot/dts/sun8i-v3s-licheepi-zero.dtb
./scripts/dtc/dtc -I dtb -O dts -o ../sun8i-v3s-licheepi-zero-dock.dts ./arch/arm/boot/dts/sun8i-v3s-licheepi-zero-dock.dtb
#bsp内核反汇编:
dtc -I dtb -O dts -o ../sun8i-v3s-blueberrypi-csi-ov2640.dts ../sun8i-v3s-blueberrypi-csi-ov2640.dtb

1.3buildroot编译根文件系统

1
make menuconfig
配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
target options 
-> Target Architecture = ARM (little endian)
-> Target Binary Format = ELF
-> Target Architecture Variant = cortex-A7
-> Target ABI = EABIhf
-> Floating point strategy = VFPv4
-> ARM instruction set = ARM
Toolchain
-> Toolchain type = External toolchain
-> Toolchain = Custom toolchain //用户自己的交叉编译器
-> Toolchain origin = Pre-installed toolchain //预装的编译器
-> Toolchain path =/home/null/Toolchain/6.3.1/
-> Toolchain prefix = $(ARCH)-linux-gnueabihf //前缀
-> External toolchain gcc version = 6.x
-> External toolchain kernel headers series = 4.6.x//可在内核文件夹根目录Makefile文件中查看相应内核版本
-> External toolchain C library = glibc/eglibc
-> [*] Toolchain has SSP support? (NEW) //选中
-> [*] Toolchain has RPC support? (NEW) //选中
-> [*] Toolchain has C++ support? //选中
-> [*] Enable MMU support (NEW) //选中
System configuration
-> System hostname = LiCheePiZero //平台名字,自行设置
-> System banner = Welcome to Buildroot //欢迎语
-> Init system = BusyBox //使用 busybox
-> /dev management = Dynamic using devtmpfs + mdev //使用 mdev
-> [*] Enable root login with password (NEW) //使能登录密码
-> Root password = 55555 //登录密码为 55555
Filesystem images
-> [*] ext2/3/4 root filesystem //如果是 EMMC 或 SD 卡的话就用 ext3/ext4
-> ext2/3/4 variant = ext4 //选择 ext4 格式
-> [*] ubi image containing an ubifs root filesystem //如果使用 NAND 的话就用 ubifs
编译
1
sudo make -j8

编译完成后会在 buildroot/output/images 下生成根文件系统

修改控制台颜色

编辑/etc/profile文件,修改如下语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if [ "$PS1" ]; then
if [ "`id -u`" -eq 0 ]; then
export PS1='# '
else
export PS1='$ '
fi
fi
#改为
if [ "$PS1" ]; then
if [ "`id -u`" -eq 0 ]; then
export PS1='\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\# '
else
export PS1='\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
fi
fi

使能配置

1
source /etc/profile
构建完后,想要取消登录密码

(在buildroot的menuconfig中可以配置取消密码,这里是假设是配置完毕后想要取消)

  • 方法一(参考官方手册)

修改/etc/inittab:

1
ttyS0::respawn:/root/logintest -L ttyS0 115200 vt100

新建logintest:

1
2
#!/bin/sh
/bin/login -f root

自启动任务在/etc/init.d/rcS中加入即可

export 相关环境变量在/etc/profile中加入。

  • 方法二

找到 /etc/inittab 文件的

1
console::respawn:/sbin/getty -L console 0 vt100 # GENERIC_SERIAL

修改为:

1
console::respawn:-/bin/sh
flash启动

flash启动时需要jffs2格式的文件系统, 所以需要使用此rootfs制作jffs2文件系统镜像

下载jffs2文件系统制作工具

1
apt-get install mtd-utils

解压 rootfs.tar

1
2
mkdir rootfs
tar xvf rootfs.tar -C rootfs

安装内核模块

1
2
3
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16 modules
#INSTALL_MOD_PATH:把指定为M的驱动安装到指定文件系统根目录下面的/lib/modules目录下面
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16 INSTALL_MOD_PATH=/home/null/Code/LiCheePi_Zero/Image/spi_image_10_28/rootfs modules_install

计算好jffs的大小,总空间是16M-1M-64K-4M=0xAF0000

1
2
3
4
5
6
#页大小0x100 256字节
#块大小0x10000 64k
#jffs2分区总空间0xAF0000=16M-1M-64K-4M
#jffs2.img是生成的文件系统镜像
du -h ./rootfs#测试文件夹大小
mkfs.jffs2 -s 0x100 -e 0x10000 -p 0xAF0000 -d rootfs/ -o jffs2.img

删除rootfs文件夹

1
rm -rf rootfs/

或者重新打包回rootfs.tar

1
tar -cvf rootfs.tar ./rootfs
添加wifi工具库(这里有好几个库,推荐用buildroot )
单独编译wireless tools工具集

修改Makefile中的 CC、 AR 和 RANLIB 这三个变量:

1
2
3
4
5
## Compiler to use (modify this for cross compile).
CC = arm-linux-gnueabihf-gcc
## Other tools you need to modify for cross compile (static lib only).
AR = arm-linux-gnueabihf-ar
RANLIB = arm-linux-gnueabihf-ranlib
  • 清理:make clean

  • 编译:make

编译完成以后就会在当前目录下生成 iwlist、 iwconfig、 iwspy、 iwpriv、 ifrename 这 5 个工
具,另外还有很重要的 libiw.so.29 这个库文件。将这 5 个工具拷贝到开发板根文件系统下的
/usr/bin 目录中 ,将 libiw.so.29 这个库文件拷贝到开发板根文件系统下的/usr/lib 目录中

1
2
sudo cp iwlist iwconfig iwspy iwpriv ifrename /home/null/Code/LiCheePi_Zero/Image/spi_image_wifi/rootfs/usr/bin -f
sudo cp libiw.so.29 /home/null/Code/LiCheePi_Zero/Image/spi_image_wifi/rootfs/usr/lib -f
编译。。。。工具集(以后再写)
buildroot 编译

配置使能

1
2
3
4
5
-> make menuconfig
-> Target packages
-> Networking applications
[*] wireless tools//其目录下全选中
[*] wpa_supplicant//其目录下全选中

2. 下载

2.1 SD卡

2.1.1 修改uboot源码设置启动命令

参考1.1节修改uboot源码后可以直接启动,无需boot.scr和script.bin这两个文件

2.1.2 利用boot.scr和script.bin

在不修改uboot源码的情况下,可以通过两个启动参数文件:boot.scrscript.bin来配置

boot.src是什么

根据资料描述 https://github.com/linux-sunxi/u-boot-sunxi/wiki#bootscr-support,u-boot在启动的时候会在第一个分区(FAT/extX格式)寻找/boot.scr或者/boot/boot.scr文件,boot.scr中可以包含用于载入script.bin,kernel,initrd(可选)以及设置内核启动参数的uboot命令。 (相当于uboot的bootcmd启动命令)

boot.src如何生成

在工作目录新建 boot.cmd 文件,添加以下内容(即uboot启动命令):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
setenv bootargs console=ttyS0,115200 panic=5 rootwait root=/dev/mmcblk0p2 earlyprintk rw vt.global_cursor_default=0
setenv bootm_boot_mode sec
setenv machid 1029
load mmc 0:1 0x41000000 zImage
#以下两句二选一
#load mmc 0:1 0x41d00000 script.bin#13M偏移
load mmc 0:1 0x41800000 sun8i-v3s-licheepi-zero-dock.dtb#8M偏移
#bootm 0x41000000
bootz 0x41000000 - 0x41800000

#fatload mmc 0 0x43000000 boot/script.bin
#fatload mmc 0 0x48000000 boot/uImage

#也可以不用这么麻烦,用以下命令直接写死在uboot中也行
setenv bootcmd 'load mmc 0:1 0x41000000 zImage;load mmc 0:1 0x41800000 sun8i-v3s-blueberrypi-csi-ov2640-480x272LCD.dtb;bootz 0x41000000 - 0x41800000;'
saveenv

详细解释:

1.上述第一行设置uboot的bootargs启动参数,格式为 参数=值,不同参数使用空格分开,其中

  • console=ttyS0,115200 含义为使用特定的串口ttyS0,波特率为 115200
  • noinitrd 含义为不使用ramdisk(内存磁盘)
  • init=/init 含义为内核启起来后,进入系统中运行的第一个脚本
  • root=/dev/mmcblk0p2 含义为指定rootfs的位置为TF卡第二个分区
  • rootfstype=ext4 含义为根文件系统类型
  • rootwait 含义为等待设备/dev/mmcblk0p2就绪后才尝试挂载rootfs
  • panic=5 传递内核参数,当遇到panic(内核严重错误)时等待5秒后重启

更多的参数可以通过查看Linux内核源码目录下Documentation/kernel-parameters.txt文件了解

2.第二行和第三行为将script.bin和内核uImage加载到指定内存地址。fatload是U-Boot中装载linux kernel 到内存的指令(这里用到oad)。

基本用法:fatload <interface> <dev[:part]> <addr> <filename> <bytes>

  • interface:所用到接口,如:MMC、USB
  • dev [:part]: 文件存放的设备 如:ide 0:1
  • addr: 装载到内存的开始地址。
  • filename: 装载的文件名称。
  • bytes: copy的字节数.

3.第四行bootm 用于将内核映像加载到指定的地址

保存文件后,执行以下命令生成boot.scr:

1
2
3
4
5
mkimage -C none -A arm -T script -d boot.cmd boot.scr
#-C ==> set compression type 'comp'
#-A ==> set architecture to 'arch'
#-T ==> set image type to 'type'
#-d ==> use image data from 'datafile'
script.bin是什么

script.bin是被全志SOC内核驱动或LiveSuit使用的针对特定目标板的二进制配置文件,包含如何设置基于芯片的各种外设,端口,I/O针脚信息。 (相当于设备树,有该信息后就不需要设备树文件了,具体由script.bin还是设备树指定由boot.src中的命令决定)

其对应的可读文本文件格式为FEX,可以利用 Sunxi-tools在二进制和文本文件之间进行转换。更多关于FEX配置的信息可以参考 http://linux-sunxi.org/Fex_Guide

script.bin如何生成

首先需要编译sunxi-tools,得到fex2binbin2fex等文件,其中fex2bin能把 *.fex 文件生成 *.bin文件。反之bin2fex可以将得到的*.bin文件生成可读的*.fex文件。

1
fex2bin sys_config.fex script.bin
2.1.3 分区及下载

ubuntu上商店中下载 gparted ,利用该软件对SD卡进行格式化并分区

如果SD卡上已经有分区需要先umount卸载文件系统,再格式整个SD卡后才能确保操作无误

  • 将uboot写入到sd卡8k偏移处:

    1
    sudo dd if=u-boot-sunxi-with-spl.bin of=/dev/sdb bs=1024 seek=8
  • 建立第一个分区,大小32M(可以随意填写),格式FAT16,把zImage,sun8i-v3s-licheepi-zero.dtb, boot.src(如果有),script.bin(如果有) 拷贝到这个分区

  • 建立第二个分区,用尽剩余空间,格式ext4,把buildroot产生的rootfs.tar解压到该分区根目录

    1
    2
    3
    sudo tar -xvf output/images/rootfs.tar -C /挂载的tf卡第二个分区目录
    #如:
    sudo tar -xvf output/images/rootfs.tar -C /media/null/rootfs/

2.2 SPI FLASH

分区情况
分区序号 起始地址 长度 镜像名字 内容
mtd0 0x0 0x0100000(1M) u-boot-sunxi-with-spl.bin uboot
mtd1 0x0100000 0x0010000(64k) sun8i-v3s-licheepi-zero.dtb dtb
mtd2 0x0110000 0x0400000(4M) zImage kernel
mtd3 0x0510000 0x100 0000-0x0510000=0xAF 0000剩余 jffs2.img rootfs
生成img镜像
1
2
3
4
5
6
7
8
9
10
11
# !/bin/sh
#生成一个空文件,大小是32MB
dd if=/dev/zero of=flashimg.bin bs=1M count=32
#将uboot添加到文件开头
dd if=u-boot-sunxi-with-spl.bin of=flashimg.bin bs=1K conv=notrunc
#将dtb放到1M偏移处
dd if=sun8i-v3s-licheepi-zero.dtb of=flashimg.bin bs=1K seek=1024 conv=notrunc
#将kernel放到1M+64K偏移处
dd if=zImage of=flashimg.bin bs=1K seek=1088 conv=notrunc
#将rootfs放到1M+64K+4M偏移处
dd if=jffs2.img of=flashimg.bin bs=1K seek=5184 conv=notrunc

执行完毕后生成镜像文件 flashimg.bin

烧写镜像
安装sunxiflash烧写工具
  • 下载

    1
    git clone -b spi-rebase https://github.com/Icenowy/sunxi-tools.git
  • 编译安装

    1
    2
    3
    #安装 libusb-1.0-0-dev 以防止报错
    sudo apt-get install libusb-1.0-0-dev
    make && sudo make install
进入下载(fel)模式

Zero有一个usb下载模式称为fel模式,进入fel模式有下面几种方式:

  • TF卡和spi flash 同时没有可启动镜像;
  • TF卡中有进入fel模式的特殊固件 fel-sdboot.sunxi
    • 如果spiflash已经有了启动镜像,那么需要在TF卡中烧入一个sunxi提供的启动工具 ( dd if=fel-sdboot.sunxi of=/dev/mmcblk0 bs=1024 seek=8 ), 插入TF卡后就会启动会进入fel模式;
  • 上电时SPI_MISO拉低到地(记得松开,否则一直检测不到flash)
下载
1
2
3
4
5
6
7
8
9
10
sudo sunxi-fel version              #查看连接的cpu信息
sudo sunxi-fel spiflash-info #显示flash信息
sudo sunxi-fel -p spiflash-write 0 flashimg.bin #烧写镜像flashimg.bin
# -p 显示进度条
# spiflash-info Retrieves basic information
# spiflash-hex[dump] addr length Dumps SPI flash region in hex
# spiflash-read addr length file Write SPI flash contents into file
# spiflash-write addr file Store file contents into SPI flash
# clear address length Clear memory
# fill address length value Fill memory
dd命令详解

dd:用指定大小的块拷贝一个文件,并在拷贝的同时进行指定的转换。

  • if=文件名:输入文件名,缺省为标准输入。即指定源文件。<if=inputfile>
  • of=文件名:输出文件名,缺省为标准输出。即指定目的文件。< of=output file >
  • ibs=bytes:一次读入bytes个字节,即指定一个块大小为bytes个字节。
    obs=bytes:一次输出bytes个字节,即指定一个块大小为bytes个字节。
    bs=bytes:同时设置读入/输出的块大小为bytes个字节。
  • cbs=bytes:一次转换bytes个字节,即指定转换缓冲区大小。
  • skip=blocks:从输入文件开头跳过blocks个块后再开始复制。
  • seek=blocks:从输出文件开头跳过blocks个块后再开始复制。
    (注意:通常只用当输出文件是磁盘或磁带时才有效,即备份到磁盘或磁带时才有效。
  • count=blocks:仅拷贝blocks个块,块大小等于ibs指定的字节数。
  • conv=conversion:用指定的参数转换文件。
    • ascii:转换ebcdic为ascii
    • ebcdic:转换ascii为ebcdic
    • ibm:转换ascii为alternateebcdic
    • block:把每一行转换为长度为cbs,不足部分用空格填充
    • unblock:使每一行的长度都为cbs,不足部分用空格填充
    • lcase:把大写字符转换为小写字符
    • ucase:把小写字符转换为大写字符
    • swab:交换输入的每对字节
    • noerror:出错时不停止
    • notrunc:不截短输出文件
    • sync:将每个输入块填充到ibs个字节,不足部分用空(NUL)字符补齐。
/dev/null和/dev/zero的区别
  • /dev/null,外号叫无底洞,你可以向它输出任何数据,它通吃,并且不会撑着!
  • /dev/zero,是一个输入设备,你可你用它来初始化文件。该设备无穷尽地提供0,可以使用任何你需要的数目——设备提供的要多的多。他可以用于向设备或文件写入字符串0。

二、开发环境搭建

Root用户:

1
2
用户名: root
密码: 55555

存储空间查看

1
2
df -h
du -h 要查看的文件夹或文件

rtl8723bs无线网卡搭建

前提条件:

  • rtl8723bs_nic.binwifi固件
  • wifi工具库
  • r8723bs.ko驱动(4.13主线内核自带,默认以模块方式安装)

步骤:

  • ifconfig -a命令查看当前系统中的全部网卡

  • 安装驱动后执行 ifconfig wlan0 up 启动网卡

  • 修改或创建配置文件 /etc/wpa_supplicant.conf

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    ctrl_interface=/var/run/wpa_supplicant 
    ctrl_interface_group=0
    ap_scan=1
    network={
    #wifi名字
    ssid="debugdump"
    scan_ssid=1
    key_mgmt=WPA-EAP WPA-PSK IEEE8021X NONE
    pairwise=TKIP CCMP
    group=CCMP TKIP WEP104 WEP40
    #wifi秘密
    psk="13800138000"
    priority=5
    }
  • 使能配置wpa_supplicant -B -d -i wlan0 -c /etc/wpa_supplicant.conf

  • 开启wifiudhcpc -i wlan0

三、移植OPENCV(3.4.1)

默认使用的是6.3.1的gcc

下载烧录V3s_linux_4_2_0_ov2640_debugdump.bin镜像

1
sudo dd of=/dev/sdX if=V3s_linux_4_2_0_ov2640_debugdump.bin

用户名root,密码root

该镜像中rootfs只有90M左右,需要用到linux主机下gparted 软件将rootfs进行扩展

测试相机

1
2
3
4
5
6
7
8
9
10
11
12
13
#ov2640可用
fswebcam -d /dev/video0 --no-banner -p UYVY -S 10 -r 800x600 capture.jpg
#-d配置使用哪个摄像设备
#–no-banner不添加水印
#-p摄像头输出格式
#-r图片大小(本人手上的ov2640貌似只支持800x600的格式)
#-S曝光度设置,越大越不刺眼

#开启web服务器
mjpg_streamer -i "input_uvc.so -d /dev/video0 -r 640x480" -o "output_http.so -p 8080 -w /usr/local/share/mjpg-streamer/www"
mjpg_streamer -i "input_uvc.so -f 10 -r 320*240 -y" -o "output_http.so -c "ruoyun:liufeng" -w www -p 8888" -o "output_file.so -d 1000 -f /mnt "

mjpg_streamer -i "input_uvc.so -d /dev/video0 -r QVGA" -o "output_http.so -p 8080 -w /usr/local/www"

配置编译OPENCV

  • linux主机下打开cmake的gui

    1
    sudo cmake-gui
  • 填写配置信息

  • sudo make编译

  • make install将opencv编译出的库文件安装到CMAKE_INSTALL_PREFIX设置的目录下

  • 将安装目录下的/lib/下的所有lib文件拷贝至开发板/lib下

    1
    2
    3
    4
    5
    cp -a /home/null/Code/OpenCV/nfs_code/opencvlib/* /home/null/Code/LiCheePi_Zero/Image/cam_image/rootfs/lib
    #-a :相当于 -pdr 的意思(参数pdr分别为:保留权限,复制软链接本身,递归复制);
    #-p :连同档案的属性一起复制过去,而非使用预设属性;
    #-d :若来源文件为连结文件的属性(link file),则复制连结文件属性而非档案本身;
    #-r :递归持续复制,用于目录的复制行为;

OPENCV开发

makefile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
CC  = arm-linux-gnueabihf-g++
LFLAGS = -Wno-psabi
#lib库位置
LIBS = -L/home/null/Code/OpenCV/install_opencv/lib/
#各种库
#-libopencv_imgcodecs -lopencv_features2d -std=c++11 -fopenmp
CPPFLAGS = -lpthread -lopencv_core -lopencv_highgui -lrt -lopencv_imgproc -lopencv_imgcodecs
#包含的头文件目录
LINC += -I/home/null/Code/OpenCV/install_opencv/include/
#输入文件名
objs := opencv
out := opencv

$(out):$(objs).cpp
${CC} ${LFLAGS} ${LIBS} ${LINC} ${CPPFLAGS} -o $@ $^
clean:
rm $(out)

测试文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include<opencv2/core/core.hpp>  
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/opencv.hpp>
#include<iostream>

using namespace cv;
using namespace std;

int main( )
{
//载入图片
Mat image= imread("girl.jpg");
Mat logo= imread("fly.jpg");
//定义一个Mat类型,用于存放,图像的ROI
Mat imageROI;

imageROI= image(Rect(10,10,logo.cols,logo.rows));//定义一个左上角点坐标为(_x, _y)的cols*rows矩形窗口

//将logo加到原图
//参数:图、权重、图、权重、添加的常数项、输出图
addWeighted(imageROI,0.5,logo,0.3,0.,imageROI);

cout << "start add picture......\n" << endl;
//输出一张jpg图片到工程目录下
imwrite("fly_girl.jpg",image);

//waitKey();

return 0;
}
  • 由于编译的时候没有加入WITH_GTK选项,因而在板卡上执行的程序无法使用函数imshowwaitKey等函数。

上面例子中的girl.jpg:

girl

fly.jpg:

fly

合成后的图片:

fly_girl

四、驱动编写

暂时采用官方镜像,镜像名字为nanopi-neo-core_sd_friendlycore-xenial_4.14_armhf_20190823.img,该镜像中采用的rootfs为UbuntuCore 18.04

写在前面

内核版本查看方法

1
2
cat /proc/version
uname -a

挂载pc上文件系统

1
2
3
4
5
mount -t nfs -o nolock 192.168.2.101:/home/null/Code/NanoPi_NEO_Core/nfs /mnt

mount -t nfs -o nolock 192.168.2.101:/home/null/nfs_root /mnt
mount -t nfs -o nolock 192.168.2.101:/home/null/Code/OpenCV/nfs_code /mnt
mount -t nfs -o nolock 192.168.2.101:/home/null/Code/LiCheePi_Zero/nfs /mnt

挂载报错尝试安装nfs-common

报错:

1
2
3
4
5
6
7
mount: wrong fs type, bad option, bad superblock on 192.168.2.101:/home/null/nfs_root,
missing codepage or helper program, or other error
(for several filesystems (e.g. nfs, cifs) you might
need a /sbin/mount.<type> helper program)

In some cases useful info is found in syslog - try
dmesg | tail or so.

安装nfs-common:

1
sudo apt-get install nfs-common

pc端检查是否配置成功(自己挂接自己)(绝对路径)

1
2
3
4
5
6
sudo mount -t nfs -o nolock localhost://home/null/Code/NanoPi_NEO_Core/nfs /mnt/nfs#挂接
sudo mount -t nfs -o nolock localhost://home/null/nfs_root /mnt/nfs_root#挂接
ls -l /mnt/nfs#检查
ls -l /mnt/nfs_root#检查
sudo umount /mnt/nfs#卸载
sudo umount /mnt/nfs_root#卸载
一键挂载脚本
1
2
3
4
5
6
7
8
#!/bin/bash
if [ $(id -u) -ne 0 ]; then
echo "Re-running script under sudo..."
sudo "$0" "$@"
exit
fi
mount -t nfs -o nolock 192.168.2.101:/home/null/Code/NanoPi_NEO_Core/nfs /mnt
echo "mount -> /mnt"

记得chmod +x

卸载脚本
1
2
3
4
5
6
7
8
#!/bin/bash
if [ $(id -u) -ne 0 ]; then
echo "Re-running script under sudo..."
sudo "$0" "$@"
exit
fi
umount /mnt
echo "umount /mnt OK"

记得chmod +x

更换源

参考: http://mirrors.ustc.edu.cn/help/ubuntu-ports.html

修改 /etc/apt/sources.list 文件中镜像源服务器地址 http://ports.ubuntu.com/http://mirrors.ustc.edu.cn/ubuntu-ports/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
deb http://mirrors.ustc.edu.cn/ubuntu-ports/ bionic main restricted universe multiverse
#deb http://ports.ubuntu.com/ bionic main restricted universe multiverse
# deb-src http://ports.ubuntu.com/ xenial main restricted universe multiverse

deb http://mirrors.ustc.edu.cn/ubuntu-ports/ bionic-security main restricted universe multiverse
#deb http://ports.ubuntu.com/ bionic-security main restricted universe multiverse
# deb-src http://ports.ubuntu.com/ xenial-security main restricted universe multiverse

deb http://mirrors.ustc.edu.cn/ubuntu-ports/ bionic-updates main restricted universe multiverse
#deb http://ports.ubuntu.com/ bionic-updates main restricted universe multiverse
# deb-src http://ports.ubuntu.com/ xenial-updates main restricted universe multiverse

deb http://mirrors.ustc.edu.cn/ubuntu-ports/ bionic-backports main restricted universe multiverse
#deb http://ports.ubuntu.com/ bionic-backports main restricted universe multiverse
# deb-src http://ports.ubuntu.com/ xenial-backports main restricted universe multiverse

更新源:

1
sudo apt update

1.按键驱动程序

1.1修改设备树

编译设备树
1
make dtbs ARCH=arm CROSS_COMPILE=arm-linux- -j8
查看系统中是否存在对应节点
1
2
3
4
5
6
7
ls /proc/device-tree/

cat /proc/device-tree/key/compatible

# 注意:hexdump查看的是dtb文件原始数据,其中的数据是按照大字节序排放的(低地址放高位)
hexdump /proc/device-tree/key/key-gpio
busybox hexdump /proc/device-tree/key/key-gpio
设备树中的GPIO节点

假设PA6和PA7上接入两个LED,通过编写一个GPIO的驱动程序来控制LED的亮灭,在设备树文件sun8i-h3-nanopi-m1.dts的根节点增加一个myleds子节点 :

1
2
3
4
myleds {       
compatible = "usr,myleds";
led-gpios = <&pio 0 7 GPIO_ACTIVE_HIGH>,<&pio 0 6 GPIO_ACTIVE_HIGH>;/* 0表示PA组的io口, 因h3里第0组的io口就是以PA开始命名的 */
};

节点中的led-gpios属性,引用了pinctrl信息,sunxi-h3-h5.dtsi中有关于该控制器的描述,

pio: pinctrl@01c20800 {
    /* compatible is in per SoC .dtsi file */
    reg = <0x01c20800 0x400>;
    interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>,
    <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&ccu CLK_BUS_PIO>, <&osc24M>, <&osc32k>;
    clock-names = "apb", "hosc", "losc";
    gpio-controller;
    #gpio-cells = <3>;
    interrupt-controller;
    #interrupt-cells = <3>;
    ......
};

myleds子节点描述了两个GPIO信息,具有多个gpio口信息的属性值: <&gpio控制器节点名 具体gpio口的标识符>,<&gpio控制器节点名 具体gpio口的标识符> …;具体gpio口的标识符是由多个数字组成, 数字的个数由所用的gpio控制器节点里的#gpio-cells属性值指定.。全志H3 共有七组GPIO ,也即七个bank,分别为PA (PA0-21),PC(PC0-16) PD (PD0-17), PE(PE0-15),PF(PF0-6) PG(PG0-13) ,PL(PL0-PL11),在内核中PA-PG是一个pinctrl控制器,而PL是另一个pinctrl控制器 。

由#gpio-cells = <3>可以推出GPIO属性需要用3个u32的数据描述,第一个参数代表该GPIO位于哪个bank,第二个参数代表该bank下的序号,第三个参数代表默认电平,myleds节点下的GPIO_ACTIVE_HIGH这个宏就定义于include/dt-bindings/gpio/gpio.h

1.2编写驱动

1.2.1检查注册的驱动
  • 驱动安装好后,会在/dev/目录下创建同名目录

  • 或者通过cat /proc/devices命令查看多出来的驱动

1.2.2gpiolib及gpio操作
经典接口
  • gpio_request:驱动中要想使用某一个gpio,就必须先调用gpio_request接口来向内核申请,得到允许后才可以去使用这个gpio
  • gpio_free: 对应gpio_request,用来释放申请后用完了的gpio
  • gpiochip_is_requested: 接口用来判断某一个gpio是否已经被申请了
  • gpio_direction_input/gpio_direction_output: 接口用来设置GPIO为输入/输出模式(不推荐直接设置寄存器)
  • 一般来说,gpio的资源申请和释放操作应该放在驱动模块的加载与卸载函数内
  • gpio_request的第一个参数是需要申请的gpio号。第二个参数是我们给该gpio起个名字
  • 申请完了之后对这个gpio进行模式设置,我们这里用gpio_direction_output 设置成输出模式,并且默认输出1让led灭
新接口
  • devm_gpio_request_one: 自带初始化电平功能,并且会在模块卸载时自动释放gpio,十分简便
读写gpio
  • gpio_get_value:读gpio
  • gpio_set_value:写gpio
控制台中查看当前gpio占用情况的方法

内核中提供了虚拟文件系统debugfs,里面有一个gpio文件,提供了gpio的使用信息

  • 使用 mount -t debugfs debugfs /tmp 把debugfs挂接到/tmp下,再重新进入/tmp后就能看到一个名为gpio的文件
  • cat /tmp/gpio即可得到gpio的所有信息,使用完后umount /tmp卸载掉debugfs

1.3调试

printk
  • 查看printk输出的日志:

    通过dmesgmoretaillessgrep的结合查看日志

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #输出全部信息
    dmesg
    #输出前20行
    dmesg | head -20
    #输出后20行
    dmesg | tail -20
    #输出包含usb的信息(-i:忽略大小写)
    dmesg | grep -i usb
    #清空dmesg环形缓冲区中的日志
    dmesg -c
  • 或者直接设置日志的输出的等级:

    1
    2
    3
    echo $level > /proc/sys/kernel/printk#默认为4 4 1 7
    #如:
    echo 8 4 1 7 > /proc/sys/kernel/printk#所有级别日志均可打印输出

    /proc/sys/kernel/printk中对应的四个数分别对应于:

    • ①控制台日志级别:优先级高于该值的消息将被打印至控制台。
    • ②缺省的消息日志级别:将用该值来打印没有优先级的消息。
    • ③最低的控制台日志级别:控制台日志级别可能被设置的最小值。
    • ④缺省的控制台:控制台日志级别的缺省值。

    日志缓冲区的每一行文本开头具有级别标记, 级别值越小则优先级越高.

    系统定义了8个消息级别, 级别号从0到7分别为:

    • 致命级(KERN_EMESG),

    • 警戒级(KERN_ALERT),

    • 临界级(KERN_CRIT),

    • 错误级(KERN_ERR),

    • 告警级(KERN_WARN),

    • 注意级(KERN_NOTICE),

    • 通知级(KERN_INFO),

    • 调试级(KERN_DEBUG).

后台运行
  • &

    当在前台运行某个作业时,终端被该作业占据;可以在命令后面加上& 实现后台运行。例如:sh test.sh &

    当成功地提交进程以后,就会显示出一个进程号,可以用它来监控该进程,或杀死它。(ps -ef | grep 进程号 或者 kill -9 进程号

  • nohup

    使用&命令后,作业被提交到后台运行,当前控制台没有被占用,但是一但把当前控制台关掉(退出帐户时),作业就会停止运行。nohup命令可以在你退出帐户之后继续运行相应的进程。nohup就是不挂起的意思( no hang up)。该命令的一般形式为:

    1
    nohup command &

    如果使用nohup命令提交作业,那么在缺省情况下该作业的所有输出都被重定向到一个名为nohup.out的文件中,除非另外指定了输出文件:

    1
    nohup command > myout.file 2>&1 &

    使用了nohup之后,很多人就这样不管了,其实这样有可能在当前账户非正常退出或者结束的时候,命令还是自己结束了。所以在使用nohup命令后台运行命令之后,需要使用exit正常退出当前账户,这样才能保证命令一直在后台运行。

  • ctrl + z

    可以将一个正在前台执行的命令放到后台,并且处于暂停状态。

  • jobs
    查看当前有多少在后台运行的命令。
    jobs -l选项可显示所有任务的PID,jobs的状态可以是running, stopped, Terminated。但是如果任务被终止了(kill),shell 从当前的shell环境已知的列表中删除任务的进程标识。

2.内核定时器

3.中断

上半部和下半部

  • 软中断

    软中断必须在编译的时候静态注册

    • open_sotfirq:打开软中断
    • raise_sotfirq:触发软中断
  • tasklet:利用软中断来实现的另一种下半部机制,在软中断和tasklet之间建议选tasklet

    • tasklet_init:初始化tasklet_struct结构体,或直接使用宏DECLARE_TASKLET定义+初始化
    • tasklet_schedule:一般在上半部中调用,使要调度的tasklet在合适的时候运行
  • 工作队列:在进程上下文执行,允许睡眠或重新调度(下半部可以睡眠就可以交给工作队列),要执行的工作会交给一个内核工作者线程去执行

    • 宏INIT_WORK:初始化工作
    • 宏DECLARE_WORK:定义+初始化工作
    • schedule_work:类似tasklet_schedule,调度传入的工作

3.1修改设备树

参考内核绑定信息:Documentation/devicetree/bindings/arm/gic.txt

对于一般的ARM处理器来说,#interrupt-cells=<3>对应的三个cell分别为:

  1. 中断类型
    • 0:SPI中断
    • 1:PPI中断
  2. 中断号
    • SPI:0~987
    • PPI:0~15
  3. 标志
    • bit[3:0]:中断触发类型
      • 1:上升沿
      • 2:下降沿
      • 4:高电平
      • 8:低电平
    • bit[15:8]:PPI中断的CPU掩码
相关函数
  • irq_of_parse_and_map:从设备树中中断节点中的interrupts属性提取对应的中断号
  • gpio_to_irq:获取gpio对应的中断号
编译设备树
1
make dtbs ARCH=arm CROSS_COMPILE=arm-linux- -j8
查看系统中是否存在对应节点
1
2
3
4
5
6
7
ls /proc/device-tree/

cat /proc/device-tree/key/compatible

# 注意:hexdump查看的是dtb文件原始数据,其中的数据是按照大字节序排放的(低地址放高位)
hexdump /proc/device-tree/key/interrupts
busybox hexdump /proc/device-tree/key/interrupts
设备树中的中断节点
  • interrupt-parent = <&pio>;//属于pio中断控制器
  • interrupts = <0 7 IRQ_TYPE_EDGE_BOTH>;
    • 第一个参数:第几个GPIO(这里是GPIOA)
    • 第二个参数:第几个外部中断
    • 第三个参数:触发沿

3.2编写驱动

3.3调试

中断注册成功后可以通过以下命令来查看是否注册到系统中:

1
cat /proc/interrupts

4.input子系统

4.1修改设备树

如果需要使用内核自带的gpio_keys.c驱动,可以参考内核中Documentation/devicetree/bindings/input/gpio-keys.txt,需要设备树中的节点满足以下要求:

  • 节点名字为“gpio-keys”

  • gpio-keys节点的compatible属性必须为“gpio-keys”

  • 所有的按键都是gpio-keys的子节点,并可以使用如下属性描述自己:

    • gpios:按键连接的gpio信息
    • interrupts:按键使用的中断信息(可选)
    • lable:按键名字
    • linux,code:要模拟的按键值
  • 如果要支持连按的话需要加入autorepeat属性

    例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    gpio_keys {
    compatible = "gpio-keys";
    autorepeat;//支持连按
    k1 {
    label = "k1";//按键名称
    linux,code = <KEY_POWER>;//要模拟的按键值
    gpios = <&pio 0 7 GPIO_ACTIVE_LOW>;//按键连接的gpio信息
    interrupts = <0 7 IRQ_TYPE_EDGE_BOTH>;//中断信息 PA7 --> EINT7
    };
    };

4.2编写驱动

Linux中已经实现了input输入子系统框架,因此无需再次注册设备,系统中input框架中的所有设备的主设备号均为13

  • input设备结构体input_dev
    • evbit成员:输入事件类型,可选事件见下文
    • keybit成员:按键值
    • relbit成员:相对坐标
    • absbit成员:绝对坐标

设置input_dev结构体中成员的方法:

  • __set_bit
  • BIT_MASK宏
  • input_set_capability:仅仅设置keybit成员
相关函数
  • input_allocate_device:申请一个input_dev结构体
  • input_free_device:释放申请到的input_dev结构体
  • input_register_device:向内核注册input_dev结构体
  • input_unregister_device:注销input_dev结构体
  • input_event:上报指定事件及对应的值
    • input_report_key:封装一层,专门上报按键事件
    • input_report_rel
    • input_report_abs
    • input_report_ff_status
    • input_report_switch
    • input_mt_sync
  • input_sync:同步事件
input_dev的相关事件
  • EV_SYN:同步
  • EV_KEY:按键
  • EV_REL:相对坐标
  • EV_ABS:绝对坐标
  • EV_MSC:杂项(其他)
  • EV_SW:开关
  • EV_LED:LED
  • EV_SND:声音(sound)
  • EV_REP:重复事件
  • EV_FF:压力
  • EV_PWR:电源
  • EV_FF_STATUS:压力状态

4.3调试

input设备注册成功后可以通过以下命令来查看是否注册到系统中:

1
ls /dev/input/ -l

通过以下命令查看是否有效:

1
busybox hexdump /dev/input/event1

5.IIC框架驱动

5.1设备树

  • clock-frequency属性:iic总线的速度(默认100000,即100k)
  • status:是否使能该总线
    • okay
    • disabled
  • iic设备节点需要写到iic总线节点中,作为总线节点的子节点
  • 总线节点需要利用pinctrl-names = "default";pinctrl-0指定iic引脚(已经设置好)
  • iic控制器节点compatible属性必须为allwinner,sun6i-a31-i2c或allwinner,sun4i-a10-i2c,这里在sunxi-h3-h5.dtsi中已经设置好,无需再次设置

设备树修改成功后可以通过以下命令来查看是否注册到系统中:

1
2
3
#查看设备节点,该目录下放着所有的i2c设备
#节点是以i2c地址结尾的文件夹
ls /sys/bus/i2c/devices/

5.2驱动

基本数据结构
  • IIC总线结构体i2c_bus_type
    • match函数:驱动和设备匹配是调用该函数来决定是否匹配
  • IIC适配器(控制器)驱动结构体i2c_adapter
    • algo:总线访问算法成员
      • master_xfer:IIC适配器的传输函数
      • smbus_xfer:SMBUS总线传输函数
  • IIC设备结构体i2c_client
    • addr:芯片地址,存在低7位
    • name:名字
    • adapter:对应的IIC适配器
    • dev:设备结构体
    • irq:中断
  • IIC设备驱动结构体i2c_driver
    • probe函数:IIC设备和驱动匹配后调用,初始化用
    • remove函数:同理,卸载用
    • id_table:传统方式匹配ID列表
    • driver:
      • owner:所有者,一般为宏THIS_MODULE
      • name:名字
      • of_match_table:设备树匹配列表
  • IIC发送数据结构体i2c_msg
    • addr:从机地址
    • flags:标志
    • len:消息长度
    • buf:消息数据
相关函数
  • IIC适配器(控制器)
    • i2c_add_adapter:向系统注册i2c_adapter成员,使用动态总线号
    • i2c_add_numbered_adapter:向系统注册i2c_adapter成员,使用静态总线号
    • i2c_del_adapter:删除IIC适配器
  • SPI设备
    • i2c_register_driver / i2c_add_driver:注册i2c_driver
    • i2c_del_driver:卸载i2c_driver
  • 传输数据
    • i2c_transfer:传输i2c_msg类型数据
    • i2c_master_send:IIC发送缓存中的数据,最后会调用i2c_transfer
    • i2c_master_recv:IIC接收数据到缓存中,最后会调用i2c_transfer

5.3调试

i2c-tools工具

得益于ubuntucore,直接运行sudo apt install i2c-tools安装i2c-tools工具

  • 查询i2c总线sudo i2cdetect -l
  • 检测i2c地址sudo i2cdetect -r -y 0(最后的0为i2c-0总线)

6.SPI框架驱动

6.1设备树

  • spi-max-frequency属性:spi总线的最大速度(默认100000,即100k)
  • status:是否使能该总线
    • okay
    • disabled
  • spi设备节点需要写到spi总线节点中,作为总线节点的子节点
  • 通过cs-gpios属性指定CS引脚
  • spi设备节点中reg和节点@后的数字均为该设备所使用的spi通道(一般0即可)
  • 总线节点需要利用pinctrl-names = "default";pinctrl-0指定spi引脚(已经设置好)
  • spi控制器节点compatible属性必须为allwinner,sun8i-h3-spi或allwinner,sun6i-a31-spi,这里在sunxi-h3-h5.dtsi中已经设置好,无需再次设置

设备树修改成功后可以通过以下命令来查看是否注册到系统中:

1
2
3
#查看设备节点,该目录下放着所有的i2c设备
#节点是以i2c地址结尾的文件夹
ls /sys/bus/spi/devices/

6.2驱动

基本数据结构
  • SPI总线结构体spi_bus_type
    • match函数:驱动和设备匹配是调用该函数来决定是否匹配
  • SPI主机驱动结构体spi_master
    • transfer函数:控制器数据传输函数
    • transfer_one_message函数:控制器数据传输函数,一次发一个spi_message包
  • SPI设备驱动结构体spi_driver
    • probe函数:SPI设备和驱动匹配后调用,初始化用
    • remove函数:同理,卸载用
    • id_table:传统方式匹配ID列表
    • driver:
      • owner:所有者,一般为宏THIS_MODULE
      • name:名字
      • of_match_table:设备树匹配列表
  • SPI传输信息结构体spi_transfer
    • tx_buf:发送数据
    • rx_buf:接收数据
    • len:数据长度
  • SPI消息结构体spi_message,由spi_transfer组成
    • complete函数:异步传输完成时调用
相关函数
  • SPI主机
    • spi_alloc_master:申请spi_master结构体
    • spi_master_put:释放spi_master结构体
    • spi_register_master:注册
    • spi_unregister_master:注销
  • SPI设备
    • spi_register_driver:注册
    • spi_unregister_driver:卸载
  • 数据传输
    • spi_message_init:初始化spi_message结构体
    • spi_message_add_tail:将spi_transfer添加到spi_message队列中
    • spi_sync:同步传输(会阻塞并等待SPI数据传输完成)
    • spi_async:异步传输(传输完成后会调用spi_message.complete函数)

6.3调试

7.RGBLCD驱动

Zero默认支持800x480和480x272这两种常见分辨率的的RGB屏幕。这两种分辨率的屏幕,直接在编译时候选择对应的分辨率即可。如果需要修改,可以参考官方手册 https://www.kancloud.cn/lichee/lpi0/519472 修改uboot。这里采用的是480*242的RGBLCD。

前提条件:Uboot中需要配置好LCD驱动。设备树种会规定chose节点用于uboot和linux之间传递参数,simple-fb的参数也是在这里传递。 所以只需要uboot中设置屏幕没问题,内核即可正常显示

7.1设备树

1.添加framebuffer

参考内核中的设备树绑定文件:\Documentation\devicetree\bindings\display\simple-framebuffer-sunxi.txt

需要写到chosen节点中

必要属性:

  • compatible: “allwinner,simple-framebuffer”
  • allwinner,pipeline为以下几个中的一个
    • “de_be0-lcd0”
    • “de_be1-lcd1”
    • “de_be0-lcd0-hdmi”
    • “de_be1-lcd1-hdmi”

例子:

  1. 官方例子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    chosen {
    #address-cells = <1>;
    #size-cells = <1>;
    ranges;

    framebuffer@0 {
    compatible = "allwinner,simple-framebuffer", "simple-framebuffer";
    allwinner,pipeline = "de_be0-lcd0-hdmi";
    clocks = <&pll5 1>, <&ahb_gates 36>, <&ahb_gates 43>,
    <&ahb_gates 44>;
    status = "disabled";
    };
    };
  2. zero例子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    chosen {
    #address-cells = <1>;
    #size-cells = <1>;
    ranges;

    simplefb_lcd: framebuffer@0 {
    compatible = "allwinner,simple-framebuffer",
    "simple-framebuffer";
    allwinner,pipeline = "de0-lcd0";
    clocks = <&ccu CLK_BUS_TCON0>, <&display_clocks 0>,
    <&display_clocks 6>, <&ccu CLK_TCON0>;
    status = "disabled";
    };
    };
2.添加屏幕节点

不知道从哪个版本开始,linux内核需要专门的 panel 节点(在根节点下)才行,具体的配置文件的选取可以从 linux ‣ drivers ‣ gpu ‣ drm ‣ panel 文件夹下选取,具体的 compatible 属性从上述文件夹下的panel-simple.c文件中的匹配表platform_of_match中选取

具体的配置可以参考内核帮助文档:\Documentation\devicetree\bindings\display\panel\simple-panel.txt\Documentation\devicetree\bindings\display\panel\panel-common.txt\Documentation\devicetree\bindings\graph.txt

这里用的是480*272的分辨率的屏幕,具体的 panel 节点配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
panel: panel {
compatible = "qiaodian,qd43003c0-40", "simple-panel";//480*272的屏
//compatible = “lg,lb070wv8”, “simple-panel”;//800X480的屏
#address-cells = <1>;
#size-cells = <0>;
enable-gpios = <&pio 4 6 GPIO_ACTIVE_HIGH>;//使能引脚 PB4脚为高使能 这里为pwm信号

port@0 {
reg = <0>;
#address-cells = <1>;
#size-cells = <0>;

panel_input: endpoint@0 {
reg = <0>;
remote-endpoint = <&tcon0_out_lcd>;
};
};
};

这里解释下ports、port和endpoint(\Documentation\devicetree\bindings\graph.txt)

Ports are described by child ‘port’ nodes contained in the device node.
Each port node contains an ‘endpoint’ subnode for each remote device port connected to this port. If a single port is connected to more than one remote device, an ‘endpoint’ child node must be provided for each link.
If more than one port is present in a device node or there is more than one endpoint at a port, or a port node needs to be associated with a selected hardware interface, a common scheme using ‘#address-cells’, ‘#size-cells’ and ‘reg’ properties is used to number the nodes.

端口s由设备节点中包含的子“端口”节点描述。
每个端口节点都为连接到该端口的每个远程设备端口包含一个“端点”子节点。如果单个端口连接到多个远程设备,则必须为每个链接提供一个“端点”子节点。
如果设备节点中存在多个端口,或者某个端口处有多个端点,或者需要将端口节点与选定的硬件接口相关联,则使用“#address-cells”,“#size-cells”和’reg’属性的通用方案用于编号节点。

All ‘port’ nodes can be grouped under an optional ‘ports’ node, which allows to specify #address-cells, #size-cells properties for the ‘port’ nodes independently from any other child device nodes a device might have.

所有“端口”节点都可以分组在一个可选的“端口s”节点下,该节点允许独立于设备可能具有的任何其他子设备节点,为“端口”节点指定#address-cells,#size-cells属性。

tcon0_out_lcd节点:

1
2
3
4
5
6
7
//tcon0_out节点是液晶控制器节点tcon0的一个输出端口(port)
&tcon0_out {
tcon0_out_lcd: endpoint@0 {
reg = <0>;
remote-endpoint = <&panel_input>;
};
};

同时这里的tcon节点需要绑定上lcd对应的io口

1
2
3
4
5
&tcon0 {
pinctrl-names = "default";
pinctrl-0 = <&lcd_rgb666_pins>;//绑定到lcd_rgb666_pins
status = "okay";
};

lcd_rgb666_pins节点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pio: pinctrl@1c20800 {
compatible = "allwinner,suniv-pinctrl";
......
lcd_rgb666_pins: lcd-rgb666-pins {
pins = "PE4", "PE5", "PE6", "PE7", "PE8", "PE9",
"PE10", "PE11", "PE12", "PE13" , "PE14", "PE15",
"PE16", "PE17", "PE18", "PE19", "PE23", "PE24",//data信号
"PE3",//VSYNC
"PE2",//HSYNC
"PE1",//DE
"PE0";//CLK
function = "lcd";
};
......
};

7.3调试

设备树可以通过以下方式检测:

1
2
3
4
#会发现framebuffer节点后面被填充了实际地址
ls /proc/device-tree/chosen/
#framebuffer节点一般在:
ls /dev/fb0

屏幕测试方法:

  1. linux系统启动后,存在/dev/fb0节点:

    1
    cat /dev/urandom > /dev/fb0
  2. 改变uboot的启动参数bootargs,从屏幕输出启动信息

    1
    2
    setenv bootargs 'console=tty1 console=ttyS0,115200 panic=5 rootwait root=/dev/mmcblk0p2 earlyprintk rw vt.global_cursor_default=0'
    boot