嵌入式Linux驱动开发笔记

模拟vexpress-a9

使用QEMU模拟vexpress-a9开发板,网络上教程比较多,可以快速搭建好开发环境。

基本环境

项目信息
操作系统Ubuntu 22.04
内核linux-4.14.334
根文件系统busybox-1.36.1
引导程序u-boot-2022.10-rc5

安装依赖

1
sudo apt-get install -y make gcc bc libncurses5-dev bison flex libssl-dev u-boot-tools gcc-arm-linux-gnueabi g++-arm-linux-gnueabi qemu-system-arm

Linux内核

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 使用vexpress默认配置编译
cd linux-4.14.334
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- vexpress_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- menuconfig
# 开启NFS4支持。
# 位置:File System -> Network File Systems->NFS client support for NFS version 4 (相关的四项全勾上)

# 编译内核
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- zImage -j$(nproc)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- LOADADDR=0x60003000 uImage -j$(nproc)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- modules -j$(nproc)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- dtbs -j$(nproc)
cd ..

# 启动模拟器
qemu-system-arm -M vexpress-a9 -m 128M -kernel linux-4.14.334/arch/arm/boot/zImage -dtb linux-4.14.334/arch/arm/boot/dts/vexpress-v2p-ca9.dtb -nographic -append "console=ttyAMA0"
# 这里注意文件的路径,是在linux-4.14.334源码目录执行的。
# -M vexpress-a9 模拟vexpress-a9单板,你能够使用-M ?參数来获取该qemu版本号支持的全部单板
# -m 128M 单板执行物理内存128M
# -kernel xx/zImage 告诉qemu单板执行内核镜像路径
# -dtb xx/vexpress-v2p-ca9.dtb 告诉qemu单板的设备树(必须加入)
# -nographic 不使用图形化界面,仅仅使用串口
# -append "console=ttyAMA0" 内核启动參数。这里告诉内核vexpress单板执行。串口设备是哪个tty。

提示没有根文件系统,接下来制作它。

根文件系统

配置编译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
cd busybox-1.36.1

# 配置
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- clean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- menuconfig
# 配置页面选择
# Settings->Build Options->Build static binary

# 编译
# 默认安装在_install路径中
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- -j$(nproc)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- install

cd ..

补充根文件系统

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# 拷贝基本文件系统
mkdir rootfs-arm32
cd rootfs-arm32
cp -rfd ../busybox-1.36.1/_install/* .

# 创建设备节点
mkdir dev
sudo mknod -m 666 dev/tty1 c 4 1
sudo mknod -m 666 dev/tty2 c 4 2
sudo mknod -m 666 dev/tty3 c 4 3
sudo mknod -m 666 dev/tty4 c 4 4
sudo mknod -m 666 dev/console c 5 1
sudo mknod -m 666 dev/null c 1 3

# 安装动态链接库
mkdir lib
cp -d /usr/arm-linux-gnueabi/lib/*.so* ./lib

# 配置初始化进程rcS
mkdir -p etc/init.d
touch etc/init.d/rcS
chmod 777 etc/init.d/rcS
cat > etc/init.d/rcS <<EOF
#!/bin/sh
PATH=/bin:/sbin:/usr/bin:/usr/sbin
export LD_LIBRARY_PATH=/lib:/usr/lib
/bin/mount -n -t ramfs ramfs /var
/bin/mount -n -t ramfs ramfs /tmp
/bin/mount -n -t sysfs none /sys
/bin/mount -n -t ramfs none /dev
/bin/mkdir /var/tmp
/bin/mkdir /var/modules
/bin/mkdir /var/run
/bin/mkdir /var/log
/bin/mkdir -p /dev/pts
/bin/mkdir -p /dev/shm
/sbin/mdev -s
/bin/mount -a
echo "-----------------------------------"
echo "*****welcome to vexpress board*****"
echo "-----------------------------------"
EOF

# 配置文件系统fstab
cat > etc/fstab <<EOF
proc /proc proc defaults 0 0
none /dev/pts devpts mode=0622 0 0
mdev /dev ramfs defaults 0 0
sysfs /sys sysfs defaults 0 0
tmpfs /dev/shm tmpfs defaults 0 0
tmpfs /dev tmpfs defaults 0 0
tmpfs /mnt tmpfs defaults 0 0
var /dev tmpfs defaults 0 0
ramfs /dev ramfs defaults 0 0
EOF

# 配置初始化脚本
cat > etc/inittab <<EOF
::sysinit:/etc/init.d/rcS
::askfirst:-/bin/sh
::restart:/sbin/init
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r
EOF

# 配置环境变量,'EOF'可以防止内容被解析
cat > etc/profile <<'EOF'
#!/bin/sh
USER="root"
LOGNAME=$USER
# export HOSTNAME=vexpress-a9
export HOSTNAME=`cat /etc/sysconfig/HOSTNAME`
export USER=root
export HOME=root
export PS1="[$USER@$HOSTNAME:\w]\#"
PATH=/bin:/sbin:/usr/bin:/usr/sbin
LD_LIBRARY_PATH=/lib:/usr/lib:$LD_LIBRARY_PATH
export PATH LD_LIBRARY_PATH
EOF

# 增加主机名
mkdir etc/sysconfig
cat > etc/sysconfig/HOSTNAME <<EOF
vexpress-a9
EOF

# 创建其他文件夹
mkdir mnt proc root sys tmp var

cd ..

接下来需要制作根文件系统,有两种方式

方式一:模拟SD卡方式启动

这种方式非常简单,但不适合需要经常修改文件系统中的文件的场景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 创建SD卡根文件系统镜像
sudo mkdir /mnt/rootfs
sudo chmod 777 /mnt/rootfs
dd if=/dev/zero of=rootfs-arm32.ext3 bs=1M count=64
mkfs.ext3 rootfs-arm32.ext3
sudo mount -t ext3 rootfs-arm32.ext3 /mnt/rootfs -o loop
sudo cp -rf rootfs-arm32/* /mnt/rootfs/
sudo umount /mnt/rootfs/

# 启动模拟器
qemu-system-arm -M vexpress-a9 \
-m 512M \
-kernel linux-4.14.334/arch/arm/boot/zImage \
-dtb linux-4.14.334/arch/arm/boot/dts/vexpress-v2p-ca9.dtb \
-nographic \
-append "root=/dev/mmcblk0 rw console=ttyAMA0" \
-sd rootfs-arm32.ext3

方式二:通过NFS挂载根文件系统

首先,宿主机需要安装配置nfs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 安装依赖
sudo apt-get install -y nfs-kernel-server

# 配置nfs
sudo mkdir -p /sync/rootfs
sudo chmod 777 -R /home/johnny/project/ldd4/rootfs-arm32
sudo cat >>/etc/exports <<EOF
/home/johnny/project/ldd4/rootfs-arm32 *(rw,sync,no_root_squash,no_subtree_check)
EOF
# 为解决Linux内核与NFS服务器的兼容问题
# 设置Ubuntu20.04的NFS,使之兼容NFS-V2和NFS-V3并增加调试功能。
sudo sed -i 's/\(^RPCSVCGSSDOPTS="\).*/\1--nfs-version 2,3,4 --debug --syslog"/g' /etc/default/nfs-kernel-server

# 重启nfs服务
sudo systemctl restart rpcbind
sudo systemctl restart nfs-kernel-server

配置主机网络

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 安装依赖
sudo apt-get install -y uml-utilities bridge-utils

# 配置网桥
cat > /etc/network/interfaces <<EOF
auto lo
iface lo inet loopback

auto eth0

auto br0
iface br0 inet dhcp
bridge_ports eth0
EOF

# 创建tap0网卡,用于连接qemu虚拟开发板
sudo tunctl -u root -t tap0
sudo ifconfig tap0 172.16.16.10 promisc up

启动模拟器

1
2
3
4
5
6
7
8
9
sudo qemu-system-arm \
-M vexpress-a9 \
-m 512M \
-kernel linux-4.14.334/arch/arm/boot/zImage \
-dtb linux-4.14.334/arch/arm/boot/dts/vexpress-v2p-ca9.dtb \
-net tap,ifname=tap0,script=no,downscript=no,id=net0 \
-net nic,macaddr=00:16:3e:00:00:01 \
-nographic \
-append "root=/dev/nfs rw nfsroot=172.16.16.10:/home/johnny/project/ldd4/rootfs-arm32,proto=tcp,nfsvers=3,nolock init=/linuxrc console=ttyAMA0 ip=172.16.16.20"

100ask-qemu

目前只支持ubuntu 16.04和18.04,需要拥有桌面环境。

已经支持模拟网卡、LCD显示、LED灯、按键、AT24CXX I2C存储芯片的功能

具体参考百问网嵌入式Linux

Linux驱动开发

与通用主机的差异是使用的内核与交叉编译工具不一样,详见Makefile文件

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
ifeq ($(KERNELRELEASE),)

ifeq ($(ARCH),arm)
KERNELDIR ?= /home/johnny/project/ldd4/linux-4.14.334
OBJECTDIR ?= /home/johnny/project/ldd4/objects/vexpress-v2p-ca9
ROOTFS ?= /home/johnny/project/ldd4/rootfs-arm32
CROSS_COMPILE ?= arm-linux-gnueabi-
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
endif
PWD := $(shell pwd)

modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) CROSS_COMPILE=$(CROSS_COMPILE) O=$(OBJECTDIR) modules

modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) CROSS_COMPILE=$(CROSS_COMPILE) O=$(OBJECTDIR) modules INSTALL_MOD_PATH=$(ROOTFS) modules_install

clean:
rm -rf *.o *.ko .*.cmd *.mod* modules.order Module.symvers .tmp_versions

else
obj-m += globalfifo.o

endif