Site Overlay

嵌入式调试快速起步

前言

我目前使用的是Ubuntu 20.04作为上位机的系统,其他的系统可能会在部分不一致的步骤,比如不同的配置文件位置等等,但是大体上都还是一样的。

串口通信

我使用的是minicom作为串口通信的软件,这个软件在绝大多数的发行版软件源中都应该已经包含了。

普通用户的权限不足以打开串口,你有三种方法可以解决这个问题:

  1. 将当前用户添加到dialout用户组,即sudo usermod -a -G dialout $USER
  2. 每次sudo
  3. 使用sudo chmod +s /usr/bin/minicom命令,使得用户始终以/usr/bin/minicom的拥有者的身份(即root)运行minicom

你只需要选择其中一个方法就可以,我个人的推荐偏好为依次递减。

接下来,你需要配置minicom,使得上位机能够与开发板正确通信,使用命令minicom -s可以直接打开minicom的配置界面,或者,你也可以在minicom的界面里,使用Ctrl + A然后按O的组合键打开同样的界面。

minicom的界面里,使用Ctrl + A然后按Z的组合键可以打开帮助菜单,帮助菜单内,按对应的按键也能够打开对应的功能。

在配置界面中,选择Serial port setup进入串口配置页面,敲击每个配置项前的大写字母即可分别修改该配置项。一般而言,你只需要注意以下几个设置项:

  • Serial Device:这是你想要连接到的设备,你需要事先到/dev目录下寻找设备,一般会是/dev/ttyUSBx/dev/ttySx的形式,其中x为数字,一般从0开始编号。
  • Bps/Par/Bits: 这是串口通信的参数配置,不同的开发板有不同的配置,需要参考手册设定,但是绝大多数情况下,应当设置为115200 8N1,其代表波特率为115200bps,数据位8位,没有奇偶校验位,停止位1位。
  • Hardware Flow Control:不同的开发板有不同的配置,需要参考手册设定,但是绝大多数情况下,应当设置为No。
  • Software Flow Control:不同的开发板有不同的配置,需要参考手册设定,但是绝大多数情况下,应当设置为No。

在完成设置后,返回上级菜单,选择Save setup as dfl即可将配置保存为默认值。

如果需要临时指定一个不同的设备进行连接,不需要每次更改串口配置,可以使用minicom -D <设备文件>的方式打开一个新的minicom

minicom的界面里,使用Ctrl + A然后按X的组合键即可退出。

ZMODEM

选用minicom的好处是其支持使用ZMODEM直接通过终端传输文件,在2022年5月1日及更新的根文件系统中,都已经包含了ZMODEM的二进制文件szrz,可以用来发送和接收文件。注意,这里的“发送”与“接收”是从开发板的视角出发的。

事实上,ZMODEM并不局限于minicom或者串口,只要有一个支持的终端软件就可以通过终端收发文件,比如使用Xshell通过SSH也可以实现同样的目的。

  • 发送文件:使用sz <文件名>命令发送,你的终端模拟器应当会处理剩下的工作,比如保存这个文件到上位机,如果你没有特别配置,那么minicom应该会把文件保存在你运行minicom时所处的目录。

你可以使用sz --help命令查看其支持的更多的配置参数。

  • 接收文件:使用rz命令接收文件,输入命令后,开发板会进入等待阶段,在minicom的界面里,使用Ctrl + A然后按S的组合键打开文件发送功能,然后选择zmodem,并在弹出的列表中用Tag标记你需要发送的文件,再用Okay将文件发送到开发板。

tftp服务器

tftp服务器在嵌入式调试中很重要,因为许多的关键的文件传输都使用的tftp,比如在U-boot中,几乎所有的文件都是使用tftp从上位机传输到开发板上的。

在Ubuntu 20.04中,我推荐使用tftpd-hpa软件包实现tftp服务器功能。你需要使用apt安装tftpd-hpatftp-hpa两个软件包。

在安装完成软件包后,你需要对上位机进行一些简单的配置:

  • 你需要编辑/etc/hosts.allow文件,这个文件控制了网络上的哪些主机能够访问本机上的INET服务,为了简单考虑,我们允许所有的主机访问tftp服务:
tftpd:ALL
in.tftpd:ALL
  • 你需要检查tftpd服务的配置文件,以Ubuntu系统上的tftpd-hpa软件包为例,其配置文件位于/etc/default/tftpd-hpa,你可以修改配置以满足自己的需要,或者保持不动:
# /etc/default/tftpd-hpa

TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/var/lib/tftpboot"
TFTP_ADDRESS=":69"
TFTP_OPTIONS="--secure"

TFTP_DIRECTORY项目对应的/var/lib/tftpboot即为tftp服务器工作的根目录,为了方便,你可以考虑将这个目录用软链接的方式链接到常用的目录,比如桌面上。

  • 最后,使用systemd启动tftpd服务,这样,tftp服务器就配置完成了。
sudo systemctl start tftpd.socket tftpd.service
sudo systemctl enable tftpd.socket

NFS服务器

NFS文件系统需要内核的支持。

NFS是网络文件系统的缩写,在Linux上,你可以将一个NFS target挂在到本地的文件系统上,实现一种“挂载盘”一样的使用体验,你甚至可以将整个NFS target作为一个Linux的根文件系统进行使用。

NFS可以极大程度地降低嵌入式调试的工作量,不再需要反复插拔SD卡或者调用各种FTP、MODEM,你只需要在上位机的文件系统中进行文件操作,嵌入式系统中挂载的NFS便会同步更新。

在Ubuntu 20.04中,你需要安装nfs-kernel-server软件包来安装NFS服务器。

在安装完成软件包后,你需要对上位机进行一些简单的配置:

  • 你需要编辑/etc/exports文件,这个文件控制了NFS会将哪些目录以什么样的方式暴露给网络上的哪些主机:
/srv/nfs4 *(rw,sync,no_subtree_check,no_root_squash)
/srv/nfs4/nfsboot_rootfs *(rw,sync,no_subtree_check,no_root_squash)

对于每一行记录,第一部分代表共享的目录,这里的两个目录分别用作NFS文件共享和NFS启动根文件系统。
第二部分的括号前部分代表允许连接的客户端,*号代表允许所有人连接。
括号内的部分代表共享属性配置,rw指允许读写,sync指文件更改将实时在所有挂载客户端同步,no_subtree_check指不检查父目录的权限,no_root_squash指连接的客户端如果是以超级用户身份连接的,则其对于NFS目录也是超级用户身份。

为了方便,你可以考虑将NFS的工作目录用软链接的方式链接到常用的目录,比如桌面上。

  • 最后,使用systemd启动nfs-kernel-server服务,这样,NFS服务器就配置完成了。

U-boot相关

读取uEnv.txt需要配置U-boot支持。

U-boot是一个在嵌入式系统中广泛使用的第二阶段Bootloader(SSBL),可以以多种方式引导Linux系统启动。U-boot支持以文本文件的方式读入启动配置,实现自动无干预的启动。下面是当前的嵌入式系统使用的uEnv.txt

modeboot=minaduki_sdboot
uenvcmd=run minaduki_sdboot

adi_sdboot=echo Copying Linux from SD to RAM... && fatload mmc 0 0x3000000 ${kernel_image} && fatload mmc 0 0x2A00000 ${devicetree_image} && if fatload mmc 0 0x2000000 ${ramdisk_image}; then bootm 0x3000000 0x2000000 0x2A00000; else bootm 0x3000000 - 0x2A00000; fi
bootargs=console=ttyPS0,115200 root=/dev/mmcblk0p2 rw earlycon rootfstype=ext4 rootwait cpuidle.off=1 

localip=192.168.3.27
remoteip=192.168.3.102
gwip=192.168.3.1
mask=255.255.255.0
nfsroot=/srv/nfs/zynq-ubuntu

tftpsetenv=setenv ipaddr ${localip}; setenv gatewayip ${gwip}; setenv netmask ${mask}; setenv serverip ${remoteip};
tftpgetimage=tftpboot 0x3000000 ${kernel_image}; tftpboot 0x2A00000 ${devicetree_image};

nfssetenv=setenv nfsargs nfsaddrs=${localip}:${remoteip}:${gwip}:${mask}
nfssetrootfs=setenv rootfs root=/dev/nfs rw nfsroot=${remoteip}:${nfsroot},vers=3

setbootargs=setenv bootargs console=ttyPS0,115200 $rootfs $nfsargs earlycon rootwait cpuidle.off=1

minaduki_netboot=run tftpsetenv; run tftpgetimage; run nfssetenv; run nfssetrootfs; run setbootargs; bootm 0x3000000 - 0x2A00000;

set_minaduki_sdboot_bootargs=setenv bootargs console=ttyPS0,115200 root=/dev/mmcblk0p3 rw earlycon rootfstype=ext4 rootwait cpuidle.off=1
minaduki_sdboot=run set_minaduki_sdboot_bootargs; run adi_sdboot;

minaduki_rescue=run adi_sdboot

Linux配置文件相关

Linux的配置文件基本位于/etc目录下,这里仅介绍几个最基础、最essential的文件。想要了解详细的Linux启动与工作的流程可以参考方元老师的书。

初始化表

初始化表位于/etc/inittab,系统启动程序init在系统启动时会读取并解释执行文件的内容,下面是一个最简单的初始化表:

::sysinit:/etc/init.d/rcS
::askfirst:-/bin/sh
::once:/usr/sbin/telnetd -l /bin/login
::ctrlaltdel:/sbin/reboot
::shutdown:/bin/umount -a -r

其中,第一行sysinit对应的/etc/init.d/rcS就是系统启动是运行的脚本文件,其他行对应的是在不同的情况下(比如按下Ctrl+Alt+Delete)时执行的命令。

Run Command

Run command即为系统启动时运行的脚本,一般而言位于/etc/init.d/目录下,随着systemd的逐渐流行,整个Linux的启动流程也日渐复杂化了,不过,在嵌入式系统上,因为需要运行的服务往往不是很多,所以Run command一般可以放在同一个文件内。

我一般习惯在/etc/rc文件内撰写Run command脚本,并将其软链接到/etc/init.d/rcS。下面是一个简单的脚本示例:

#! /bin/sh
hostname NJURadio

mount -a
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s
mkdir -p /dev/pts
mkdir -p /dev/i2c
mount -t devpts devpts /dev/pts

cat /etc/motd

Message Of ToDay

今日信息位于/etc/motd,一般保存一些特定的自定义内容,MOTD一般在终端登陆时显示,用于提示操作者。

下面是处于工作状态和救援状态的NJURadio的不同MOTD,作为示例:

工作状态:

#  .88b  d88. d888888b d8b   db  .d8b.  d8888b. db    db db   dD d888888b
#  88'YbdP`88   `88'   888o  88 d8' `8b 88  `8D 88    88 88 ,8P'   `88'
#  88  88  88    88    88V8o 88 88ooo88 88   88 88    88 88,8P      88
#  88  88  88    88    88 V8o88 88~~~88 88   88 88    88 88`8b      88
#  88  88  88   .88.   88  V888 88   88 88  .8D 88b  d88 88 `88.   .88.
#  YP  YP  YP Y888888P VP   V8P YP   YP Y8888D' ~Y8888P' YP   YD Y888888P
# ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
#              Buildroot 2021.11.3 rootfs built for NJURadio.
#       Powered by MINADUKI Technologies 2022. All rights reserved.
# ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

救援状态:

#  .88b  d88. d888888b d8b   db  .d8b.  d8888b. db    db db   dD d888888b
#  88'YbdP`88   `88'   888o  88 d8' `8b 88  `8D 88    88 88 ,8P'   `88'
#  88  88  88    88    88V8o 88 88ooo88 88   88 88    88 88,8P      88
#  88  88  88    88    88 V8o88 88~~~88 88   88 88    88 88`8b      88
#  88  88  88   .88.   88  V888 88   88 88  .8D 88b  d88 88 `88.   .88.
#  YP  YP  YP Y888888P VP   V8P YP   YP Y8888D' ~Y8888P' YP   YD Y888888P
# ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
#            Busybox 1.35.stable rootfs compiled for NJURadio. 
#       Powered by MINADUKI Technologies 2022. All rights reserved.
# ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

# ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
#   You are in rescue mode. Think twice before performing any opeations!
# ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

文件系统表

文件系统表的自动挂载需要init程序的支持。

文件系统表位于/etc/fstab,系统启动时会读取这个文件,并根据文件的内容自动挂载文件系统到对应的挂载点上,省去手动挂载的麻烦。下面是一个文件系统表的示例,文件系统表中可以指定不同类型的文件系统进行挂载。

# <file system>   <mount pt>    <type>    <options> <dump>    <pass>
proc            /proc           proc    defaults        0       0
tmpfs       /tmp        tmpfs   defaults    0   0
sysfs       /sys        sysfs   defaults    0   0
mdev        /dev        tmpfs   defaults    0   0
/dev/mmcblk0p1  /boot       vfat    defaults    0   2
/dev/mmcblk0p2  /           ext4    defaults,noatime 0  2
/dev/mmcblk0p4  /nju        ext4    defaults,noatime 0  2
192.168.3.102:/srv/nfs  /nfs    nfs defaults,noauto 0   0

环境文件

环境文件分为系统全局的和用户的,不同shell往往对应了不同的文件。系统全局的环境配置文件一般在/etc目录下,用户定义的环境配置文件一般在$HOME/目录下。

用户可以在环境配置文件中输入一些自定义的alias或者source其他的环境文件,实现简化的配置文件,比如,我在自己的.zshrc中加入了这两句alias,实现快速切换Vivado开发环境。

alias vivado18="source /tools/Xilinx/Vivado/2018.3/settings64.sh"
alias vivado21="source /tools/Xilinx/Vivado/2021.2/settings64.sh"

解释:内核与根文件系统

根文件系统是内核启动时挂载的第一个文件系统,文件系统内提供了运行时必要的可执行文件和挂载点,是用户与OS交互的主要工具。

一个常见的误区是,根文件系统经常被人和内核放在一起提及,可能的原因是,市面上常见的Linux发行版几乎都是将内核与根文件系统同时提供给用户的。然而这两者的关联并不紧密,根文件系统和系统内核可以由完全不同的厂家提供(在嵌入式领域尤为如此),可以具有完全不同的配置方法,同时根文件系统提供的是用户态的可执行程序,从本质上讲和用户自己编写的程序是一样的,区别于系统内核,而且一般来讲,根文件系统的“兼容性”相对更强,只要二进制格式正确,配置正确,同样的根文件系统可以在多种嵌入式平台上正常运行,而这显然是内核做不到的。

由于内核编译与rootfs制作有单独的文章进行解释,在此不再赘述。

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据