用户选择使用Digi嵌入式核心模块作为主控单元,是为了充分利用Digi的DEY简单易用的特点,以及各种验证过的BSP和软硬件功能模块。在开发过程中,用户可以仅维护少数几个自己改动过的配置文件,设备树文件等,利用这些文件就可以直接配合已有的或全新安装的DEY开发环境编译出自己定制的系统。利用这种方式开发对初学者非常有利,不论DEY或上游软件如何更新变化,只要大版本一致,都可以在任意时刻快速复原之前的开发成果。

本文以一个cc6ul项目为例,针对Yocto的初学者,希望以最接近传统开发方式来进行项目开发和维护。在熟练掌握本文的方法后,还可以meta-custom来代替相关的文件和操作。这是熟练掌握Yocto开发方式之后的事,在此之前,先用本文的方法来开发和维护自己的工作。

根据项目需求设计第一版硬件,只需配合该硬件设计更改编译相关的设备树文件。硬件电路设计时,请注意参考cc6ul官方硬件参考手册,特别注意cc6ul硬件设计指南

  • 串口

用到uart2,uart3设计为两线RS232接口,uart4为485接口,uart5

  • SPI

SPI1接MC33996

  • 其它

I2C1,CAN1,CAN2

请参考CC6UL上手指南固件升级教程,学会卡刷和TFTP刷固件等操作

了解Digi Embedded Yocto的基本知识并搭好DEY开发环境,编译出最基本的镜像dey-image-qt和core-image-base,请参考本wiki上的DEY系统开发教程

熟悉单板机设备树,DEY2.6的设备树源文件在github上的位置:

https://github.com/digi-embedded/linux/tree/v4.14/dey-2.6/maint/arch/arm/boot/dts

我们可以先在github上浏览它,并确定将来需要变更的地方,做好笔记,然后复制单板板级设备树并重新定义一个名字,并编译测试它。Digi的单板机有SBC EXPRESS(ID 129)和 SBC PRO (ID 135)两种,我们可以打开它们对应的HRM来查看对应的板载接口和资源分配。对应的设备树文件在:arch/arm/boot/dts/imx6ul-ccimx6ulsbc-(board_ID).dts。

以单板机设备树为模板,修改为自己底板的设备树,并建立和维护相关工作的版本控制。 以arch/arm/boot/dts/imx6ul-ccimx6ulsbc-id135.dts为例,我们可以在这个板级文件基础上更改设备树以匹配我们的硬件平台。复制一份并重新命名为i.mx6ul-ccimx6ulsbc-id201.dts。

为了更好的维护自己的工作,采用git的方式来维护自己的开发工作。

  • 配置git帐户

请先注册一个git服务帐户,以ECCEE的git为例,到http://gitlab.eccee.com:11080/申请一个帐户,然后把相关帐户信息配置到开发电脑中。你还需要生成ssh-key并配置到帐户中,参考利用Eccee的免费gitlab进行版本控制和管理工作

git config --global user.name "my-username"
git config --global user.email "my-email"
  • 初始化git项目tdmk-dey-2.6

先在Ecceegit上新建好一个项目,比如命名为tdmkv1,然后在DEY的项目文件夹下,初始化git项目,并新建一个README.rd,输入下面相关备注信息:

git init
git remote add ecceegit ssh://git@gitlab.eccee.com:11022/<myusername>/myproject.git
#请用你项目的git地址替掉上面URL
nano README.rd
****This is custom board DEY project of dey-2.6************
2019-11-1  V1 board release, board ID 201

接着添加README.rd,local.conf和i.mx6ul-ccimx6ulsbc-id201.dts到git中,先将这几个文件commit,并提交到git服务器上。为了能编译自定义ID的板子设备树,我们还需要添加tmp/work-shared/ccimx6ulsbc/kernel-source/arch/arm/boot/dts/Makefile到git,并添加id201这个dts文件到Makefile对应位置中。

接下来就可以慢慢修改和调试相关文件了。

git add README.rd
git add conf/local.conf
cp tmp/work-shared/ccimx6ulsbc/kernel-source/arch/arm/boot/dts/imx6ul-ccimx6ulsbc-id135.dts tmp/work-shared/ccimx6ulsbc/kernel-source/arch/arm/boot/dts/imx6ul-ccimx6ulsbc-id201.dts
git add tmp/work-shared/ccimx6ulsbc/kernel-source/arch/arm/boot/dts/imx6ul-ccimx6ulsbc-id201.dts
git add tmp/work-shared/ccimx6ulsbc/kernel-source/arch/arm/boot/dts/Makefile
nano tmp/work-shared/ccimx6ulsbc/kernel-source/arch/arm/boot/dts/Makefile
添加id201这个设备树文件在id136那一行下,然后保存 
git commit -a
注意这是第一次提交,远程库没任何版本,我们先用本地master分支提交并设置到远程库上
git push -u ecceegit master
这里-u也可以用--set-upstream,这样远程库的主版本建立好后,下次提交只需直接git push了。

由于tmp下的目录比较长,我们又经常需要nano,cat或ls来查看相关的文件或文件夹,掌握一些linux小技巧很重要。比如用“history |grep work”之类的快速来找到之前打过的命令,减少一些出错机率。对于git命令,常用的还有“git status|more “,来查看哪些文件是否有变动,而忽略那些我们不需要管理的文件。

  • 串口相关的设备树源码调整

从搜索串口UART开始,默认已经有uart2和uart5,uart3是注释掉的状态,先将对应的注释取消。因为我们只需要两线的uart,所以uart-has-rtscts;这一句在对应的串口下都要注释掉。还需加一个uart4:

&uart4 {
	status = "okay";
};

串口对应的设备树源码修改就完成了。
uart3作为485接口的设备树修改目标:uart3的RTS作为485的方向使能脚。
因为uart3的RTS和CAN1复用,为了用作485的使能脚,需禁掉can1。在id201设备树中搜索uart3相关项,做如下修改:

1、注释掉can1
//&can1 {
//	status = "okay";
//};

2、启用uart3的485接口RTS使能。 参考BSP中的串口/485接口说明: 注意,RTS是请求发送的意思,在i.mx6ul的串口中,CTS线是输出引脚,用于控制发送请求(RTS功能线),因此应该使用CTS线来连接方向脚,输出高电平时(DE为高),表示请求发送,当CTS为低时(/RE为低),接收有效。

pinctrl-0 = <&pinctrl_uart3_4wires>;
uart-has-rtscts;
linux,rs485-enabled-at-boot-time;
rs485-rts-active-high;
rs485-rx-during-tx;
rs485-rts-delay = <1 1>;
status = "okay";
  • SPI

其它接口待续。。 请用git commit 和git push ecceegit来保存您的工作

第二版资源简述:这一版中uart3用作485,方向控制用GPIO1 2
首先,把can的注释去掉,并把uart2,3都改成两线。
在uart2中
pinctrl-0 = <&pinctrl_uart2_2wires>;

如果不用uart的CTS作为RTS使能控制,而是指定gpio,至少需要下面两行

uart3中用下面改法:
&uart3 {
pinctrl-0 = <&pinctrl_uart3_2wires>;
linux,rs485-enabled-at-boot-time;
cts-gpios = <&gpio1 2>;
rts-gpios;
rs485-rts-active-high;
rs485-rx-during-tx;
rs485-rts-delay = <1 1>;
status = "okay";
}; 
为了编译自定义的设备树,您需要进入devshell来运行make dtbs。一些人喜欢在DEY默认的设备树上更改,比如直接改id135的设备树,然后用bitbake -C compile virtual/kernel来编译它。注意-C是大写,这种方法编译并不会生成自定义的设备树,它只能编译本来就有的设备树。 但要注意的是,如果您用小写的-c并使用bitbake编译镜像,因为每一次编译镜像或是内核,会用DEY默认安装版本重新生成内核相关文件(也包括设备树以及相关的Makefile)。为了保留自己的变更,如果需要重新编译内核或系统原有但改动过的设备树,请使用
bitbake -C compile virtual/kernel
命令,

如果您不小心没用(-C)重新编译了内核或整个镜像,或是用cleanall,即 bitbake -c cleanall virtual/kernel
该命令会清空tmp/work-shared/ccimx6ulsbc/kernel-source下的文件(包括自定义的id设备树文件和Makefile,所以还要用
git reset –hard
回退到上一次commit之后和改动前的状态(把删除的设备树文件和Makefile恢复)。

设备树的编译方法
bitbake -c devshell virtual/kernel    
然后在devshell里编译自定义板子的设备树文件
make dtbs
exit
编译结果在:tmp/work/ccimx6ulsbc-dey-linux-gnueabi/linux-dey/4.14-r0/build/arch/arm/boot/dts/
新建DEY项目,并bitbake出基本镜像后,我们可以用git clone把自己的相关改动放到项目文件夹中。然后进行编译。注意使用非空项目文件夹,需要用–no-checkout参数
cd 项目文件夹
git clone --no-checkout ssh://git@my-project-url tempgit
mv tempgit/.git .
rmdir tempgit
git reset –hard HEAD
bitbake -c devshell virtual/kernel
####在devshell内,直接运行设备树编译命令####
  >make dtbs
  >exit

退出后,就可以在tmp/work/ccimx6ulsbc-dey-linux-gnueabi/linux-dey/4.14-r0/build/arch/arm/boot/dts/目录下找到自定义板子的设备树

如果你有bitbake dey-image-qt,则就已经编译出linux-kernel,但是bitbake -c cleanall dey-image-qt并不会删除编译的linux-kernel。如果想清除已经编译的linux-kernel,需要用bitbake -c cleanall virtual/kernel,只有这个命令会清空我们git版本维护的东西,因此当我们不小心清除时,需要用bitbake -c compile virtual/kernel来重新编译出相关的内核,然后再用git pull, bitbbake -C compile virtual/kernel来编译出我们自己修改的内核和设备树文件等。

测试设备树,您无需每次都重刷固件,设备树只需拷贝到内核分区,然后在启动时注意启用的是它即可。flas系统启动时,会打印加载的设备树文件名,它是由启动脚本和模块上的参数共同决定的。我们在测试设备树时,可以绕过该脚本,直接用dboot linux nand命令来启动系统,这时会采用uboot参数fdt_file定义的设备树文件。但您的内核必须有你要测试的设备树文件。比如我们想测试my-device-tree.dtb,可以先拷到u盘或是用网络传到linux文件系统上

进入系统后,加载linux分区,一些固件会自动加载linux分区为只读文件系统,可用mount命令查看。
ls /mnt
如果没有linux文件夹,可以创建一个再加载linux分区,如果已经存在,说明已经加载了linux内核分区。
mkdir -p /mnt/linux
mount -t ubifs /dev/ubi0_0 /mnt/linux
如果系统已经加载过,一般是只读分区,用下面命令修改为可读写分区:
mount -o remount,rw /dev/ubi0_0 /mnt/linux
拷入my-device-tree.dtb,如果你从sd卡拷入,要先加载sd卡
mkdir -p /media/tfcard
mount -t vfat /dev/mmcblk1p1 /media/tfcard
cp /media/tfcard/my-device-tree.dtb /mnt/linux
然后reset,即可
启动时停在uboot界面,用setenv设置fdt_file参数
setenv fdt_file my-device-tree.dtb
dboot linux nand

如果上面这个设备树文件名想多次测试,可以在bootcmd里改相应的设备树文件名

setenv fdt_file my-device-tree.dtb
setenv bootcmd dboot linux nand
saveenv
reset 

DEY在编译过程中,会下载Linux源码树和软件包到downloads目录下,在编译系统的过程中,它还用到两个目录work和work-shared,其中work就是编译内核时的工作目录,它会用到linux的源码树,这个是在tmp/work/<平台>-dey-linux-gnueabi/<内核版本号>/git,而实际上这个git是链接,指向work-shared/<平台>/kernel-source。

如果我们已经编译了一个镜像,那么编译的结果是在tmp/work/<平台>-dey-linux-gnueabi/<内核版本号>/build下。当我们 注意编译结果除了在tmp/work/ccimx6ulsbc-dey-linux-gnueabi/linux-dey/4.14-r0/相关目录下,也会更新tmp/deploy/images/ccimx6ulsbc下的设备树和内核二进制文件,但不会更新内核文件系统固件。

要修改内核选项:

bitbake -c menuconfig virtual/kernel
在出来的图形化配置窗口勾选所需的选项,然后按默认文件名保存(.config),并退出,注意bitbake并不直接引用该配置文件,而是使用defconfig配置文件,在build的父目录中有默认的defconfig,我们需要在build目录中生成一个新的defconfig,它的优先权比默认的高
bitbake -c savedefconfig virtual/kernel
上述命令会在build目录内生成defconfig,这样再编译出来的内核,就带有更改好的内核选项配置文件。

要查看对应的内核选项开关是否起作用,进入linux用下面命令

zcat /proc/config.gz | grep DP83848
上面这个命令查询DP83848在当前运行内核的配置情况

如果需要编译到镜像,请先用bitbake -c cleanall core-image-base(或dey-image-qt)来删除旧镜像,然后再bitbake core-image-base生成新的内核镜像即可。

请将tmp/work/ccimx6ulsbc-dey-linux-gnueabi/linux-dey/4.14-r0/build/defconfig也添加到版本管理中

git add tmp/work/ccimx6ulsbc-dey-linux-gnueabi/linux-dey/4.14-r0/build/defconfig
git commit -a
git push
  • 设备树集成到内核前的测试方法

其实您无需更改启动脚本boot.scr就可以测试自己的内核,只需将bootcmd命令改为直接绕过boot.scr启动,这样就可以更改并利用fdt_file参数来加载设备树。不过您仍先设置一个内核已经有的设备树,在可以在进入linux系统后,加载内核分区,将对应的设备树拷入到内核分区中,再重新启动即可。

第一次启动,先用默认镜像中的imx6ul-ccimx6ulsbc-id135.dtb
setenv fdt_file imx6ul-ccimx6ulsbc-id135.dtb
dboot linux nand
进入系统后,加载linux分区,一些固件会自动加载linux分区为只读文件系统,可用mount命令查看。
ls /mnt
如果没有linux文件夹,可以创建一个再加载linux分区,如果已经存在,说明已经加载了linux内核分区。
mkdir -p /mnt/linux
mount -t ubifs /dev/ubi0_0 /mnt/linux
如果系统已经加载过,一般是只读分区,用下面命令修改为可读写分区:
mount -o remount,rw /dev/ubi0_0 /mnt/linux
拷入imx6ul-ccimx6ulsbc-id201.dtb,如果你从sd卡拷入,要先加载sd卡
mkdir -p /media/tfcard
mount -t vfat /dev/mmcblk1p1 /media/tfcard
cp /media/tfcard/imx6ul-ccimx6ulsbc-id201.dtb /mnt/linux
然后reset,即可
  • 设备树集成到内核后

当您集成好设备树到内核固件后,可更改启动脚本boot.scr,以便系统启动时可以加载自己的设备树。
启动脚本和卡刷脚本源码,默认在dey安装目录meta-digi/meta-digi-arm/recipes-bsp/u-boot/u-boot-dey/ccimx6ulsbc/ 下,有两个文件 boot.txt和install_linux_fw_sd.txt ,经bitbake编译出来后,后缀会变成.scr

当编译镜像后,这两个文件会被拷在tmp/work/ccimx6ulsbc-dey-linux-gnueabi/u-boot-dey/2017.03-r0/目录下,因此我们可以添加到git中

git add tmp/work/ccimx6ulsbc-dey-linux-gnueabi/u-boot-dey/2017.03-r0/boot.txt
git add tmp/work/ccimx6ulsbc-dey-linux-gnueabi/u-boot-dey/2017.03-r0/install_linux_fw_sd.txt
#手工修改boot.txt
nano tmp/work/ccimx6ulsbc-dey-linux-gnueabi/u-boot-dey/2017.03-r0/boot.txt
#将第一个else if语句中的内容全注释掉,然后加上下面这句
    setenv fdt_file imx6ul-ccimx6ulsbc-id201.dtb
#按ctrl+o保存后,ctrl+s退出
git commit -a
git push ecceegit

用bitbake编译的方法是:
bitbake -C compile virtual/bootloader
编译的结果在: tmp/work/ccimx6ulsbc-dey-linux-gnueabi/u-boot-dey/2017.03-r0/deploy-u-boot-dey目录下

如果我们使用的不是dey-image-qt,而是core-image-base,可以修改卡刷脚本install_linux_fw_sd.txt

nano tmp/work/ccimx6ulsbc-dey-linux-gnueabi/u-boot-dey/2017.03-r0/install_linux_fw_sd.txt
#在脚本开头,将定义镜像的文件名由dey-image-qt-x11开头变成core-image-base开头。
#保存,退出
git commit -a
git push

这个脚本在DEY2.6-r3下测试已经是用bitbake -C compile virtual/bootloader编译,结果同样在tmp/work/ccimx6ulsbc-dey-linux-gnueabi/u-boot-dey/2017.03-r0/deploy-u-boot-dey目录下。事实上,tmp/deploy/images/下uboot相关的脚本和uboot也是在这个命令后更新的。

文件系统裁剪定制后,原本的Linux分区可能不够用,我们除了可以在uboot编辑分区大小外,还可以用卡刷脚本自动定义所需的分区,update分区对DEY来说并非必须,所以我们可以增大rootfs,改小update,来满足分区大小的需求。在install_linux_fw_sd.txt中增加mtdparts一行:

...
setenv bootcmd "
	env default -a;
	setenv mtdparts mtdparts=gpmi-nand:3m(bootloader),1m(environment),1m(safe),12m(linux),14m(recovery),150m(rootfs),-(update);
	saveenv;
	echo \"\";
	echo \"\";
	echo \">> Installing Linux kernel and device tree files\";
	echo \"\";
...

然后重编译uboot,得到新的卡刷脚本,并应用到卡刷包中,就可以实现卡刷前自动改分区大小。

卡刷脚本的另一种编译方法

卡刷脚本的另一种编译方法

早期的DEY版本可以用SDK中的mkimage来单独编译它,但它使用了不同的脚本环境,因此做个脚本来完成相关环境变量切换和编译,注意,您需要先安装对应版本的DEY的SDK,然后在脚本中用该SDK中的mkimage来编译卡刷脚本,下面以cc6ul的dey-2.6r2为例:

>nano installer_compiler.sh
#以下为脚本内容:
#!/bin/bash
source /opt/dey/2.6-r2/environment-setup-cortexa7t2hf-neon-dey-linux-enugabi
mkimage -T script -n "DEY firmware install script" -C none -d ./release/install_linux_fw_sd.txt install_linux_fw_sd.scr
source dey-setup-environment
#保存并退出
>chmod a+x installer_compiler.sh
>.installer_compiler.sh
#将该脚本添加到git中
git add installer_compiler.sh
git commit -a
git push

上面我们用git的方式建立了源码版本管理项目,通常我们还可以在ecceegit上建立一个发布版本,比如项目名称为tdmkv1_release,然后在项目文件夹中新建一个release项目 mkdir release #创建一个shell脚本来打包各种release的文件 nano copyrelease.sh

cp ../tmp/work/ccimx6ulsbc-dey-linux-gnueabi/linux-dey/4.14-r0/build/arch/arm/boot/dts/imx6ul-ccimx6ulsbc-id201.dtb ./
cp ../tmp/work/ccimx6ulsbc-dey-linux-gnueabi/u-boot-dey/2017.03-r0/deploy-u-boot-dey/boot.scr ./
cp ../tmp/work/ccimx6ulsbc-dey-linux-gnueabi/u-boot-dey/2017.03-r0/deploy-u-boot-dey/install_linux_fw_sd.scr ./
cp ../tmp/deploy/images/ccimx6ulsbc/core-image-base-ccimx6ulsbc.ubifs ./
cp ../tmp/deploy/images/ccimx6ulsbc/core-image-base-ccimx6ulsbc.boot.ubifs ./
cp ../tmp/deploy/images/ccimx6ulsbc/core-image-base-ccimx6ulsbc.recovery.ubifs ./
cp ../tmp/deploy/images/ccimx6ulsbc/u-boot.imx ./
sleep 5
zip my_sd_installer.zip ./* -x copyrelease.sh

保存后,记得更改为可执行权限,然后运行,就可以自动把想要发布的镜像或脚本复制到release目录中

chmode a+x copyrelease.sh
./copyrelease.sh

然后在release目录内初始化git项目

git init
git remote add ecceegit ssh://git@gitlab.eccee.com:11022/<myusername>/tdmkv1_release.git
nano README.rd
####README.rd内容,主要记录发布信息####
Release Note:
20191114  initial release
---core-image-base images added
---imx6ul-ccimx6ulsbc-id201.dtb added
---boot.scr change to boot into id201 if board_id does exist
---install_linux_fw_sd change to use core-image-base
####
git add *
git commit -a
注意这是第一次提交,远程库版本为空,我们要用本地源去设置远程库版本
git push --set-upstream ecceegit master

注意本文我们学习了在ecceegit上新建源码项目和发布项目,对于设备树等需要自行管理版本控制的文件,重大修改请及时git commit -a并git push上去。当某个版本测试得差不多时,请用git tag打上标签,例如:

git tag -a v0.1 -m "basic IO function work"
git push ecceegit --tags

同样地,定期管理发布的内容,并打上版本号,添加注释以示区别,形成良好的产品版本管理习惯。