【北京迅为】《STM32MP157开发板嵌入式开发指南》-第六十九章 linux内核移植
iTOP-STM32MP157开发板采用ST推出的双核cortex-A7+单核cortex-M4异构处理器,既可用Linux、又可以用于STM32单片机开发。开发板采用核心板+底板结构,主频650M、1G内存、8G存储,核心板采用工业级板对板连接器,高可靠,牢固耐用,可满足高速信号环境下使用。共240PIN,CPU功能全部引出:底板扩展接口丰富底板板载4G接口(选配)、千兆以太网、WIFI蓝牙模块HDMI、CAN、RS485、LVDS接口、温湿度传感器(选配)光环境传感器、六轴传感器、2路USB OTG、3路串口,CAMERA接口、ADC电位器、SPDIF、SDIO接口等
第六十九章 linux内核移植
在“第十二章 编译Linux内核”已经对Linix内核进行了编译以及介绍,且对编译出的文件作用做了简单的说明,下面我们对官方提供的源码来进行移植,以此来适配我们自己的开发板。
官方提供的系统源码存放路径为“iTOP-STM32MP157开发板网盘资料汇总\07_系统移植\01_官方源码\linux-stm32mp-5.4.31-r0”文件夹。
移植好的系统源码存放路径为“iTOP-STM32MP157开发板网盘资料汇总\07_系统移植\02_移植好的源码\linux”。
本小节所使用的测试文件系统为buildroot构建好的镜像,存放路径为“iTOP-STM32MP157开发板网盘资料汇总\03_文件系统源码和镜像\02_buildroot文件系统\03_buildroot制作好的镜像”。
69.1 源码的导入以及打补丁
将官方提供的系统源码文件夹拷贝到ubuntu上,如下图所示
使用命令“ cd linux-stm32mp-5.4.31-r0/”,进入源码文件夹如下图所示:
然后使用命令“tar -vxf linux-5.4.31.tar.xz”,对源码的压缩文件进行解压,如下图所示:
解压完成之后,使用命令“ cd linux-5.4.31”进入linux-5.4.31源码文件如下图所示:
接下来需要使用以下命令将 ST 官方提供的源码补丁添加到标准内核中,如下图所示:
for p in `ls -1 ../*.patch`; do patch -p1 < $p; done
69.2 编译内核源码
69.2.1 增设自己的平台
使用命令“cd linux-5.4.31”,进入内核目录,如下图所示:
使用以下命令来生成multi_v7_defconfig的默认配置,如下图所示:
make ARCH=arm multi_v7_defconfig "fragment*.config"
然后使用以下两条命令在默认 multi_v7_defconfig 配置中加入 ST 官方提供的 fragment config,生成我们最终需要的.config文件,两条命令执行完成之后如下图所示:
for f in `ls -1 ../fragment*.config`; do scripts/kconfig/merge_config.sh -m -r .config $f; done
yes '' | make ARCH=arm oldconfig
然后使用以下命令创建自己的平台的默认配置文件,如下图所示:
cp .config arch/arm/configs/stm32_itop_mp1a_defconfig
创建自己的的默认配置文件,如下图所示:
然后我们使用命令“cd arch/arm/boot/dts”进入设备树文件存放目录,可以看到ST官方开发板的设备树文件,而我们的板子同样也是参照官方开发板来进行设计的,所以为了方便我们直接通过修改官方开发板的设备树文件来适配我们自己的开发板。
使用以下命令,将官方的设备树文件进行复制并修改文件名,如下图所示
cp stm32mp15xx-dkx.dtsi stm32mp15xx-itop.dtsi
cp stm32mp157a-dk1.dts stm32mp157a-itop.dts
使用命令“vim stm32mp157a-itop.dts”进入stm32mp157a-itop.dts文件,将头文件中的
#include "stm32mp15xx-dkx.dtsi"
修改为
#include "stm32mp15xx-itop.dtsi"
修改完成如下图所示:
并将model从STMicroelectronics STM32MP157A-DK1 Discovery Board修改为STMicroelectronics STM32MP157A-iTOP Discovery Board,修改完成如下D图所示:
保存退出之后使用以下命令进入stm32mp15xx-itop.dtsi文件夹
vim stm32mp15xx-itop.dtsi
由于官方的DK1开发板内存默认为512MB而我们的内存为1G,所以我们将 memory属性值由
0xc0000000 0x20000000
修改为
0xc0000000 0x40000000
修改完成如下图所示:
保存退出之后,继续在设备树目录下使用命令“ vim Makefile”对编译配置文件进行修改,打开之后如下图所示:
使用查找命令查找“stm32mp157a-dk1.dtb”,然后在下方添加我们自己的设备树文件
stm32mp157a-itop.dtb \
添加完成之后如下图所示:
保存退出,至此我们自己的平台就增加完成了。
69.2.2 编译内核
回到源码目录下,然后我们使用命令“vim create.sh”来创建一个编译脚本,并添加以下内容,添加完成如下图所示:
#!/bin/sh
export ARCH=arm
export CROSS_COMPILE=arm-none-linux-gnueabihf-
make stm32_itop_mp1a_defconfig
make -j8 ARCH=arm uImage vmlinux LOADADDR=0xC2000040
make dtbs
保存退出之后,使用命令“chmod 777 create.sh”,赋予文件可执行权限,如下图所示:
然后使用命令“./create.sh”对源码进行一个初步的编译,编译完成如下图所示:
我们使用命令“cd arch/arm/boot/”进入boot文件夹,该文件夹下的uImage文件正是我们之后要烧写的内核镜像。如下图所示:
然后我们再使用命令“cd dts”,来进入设备树目录,该目录下的stm32mp157a-itop.dtb就是我们最后要烧写进开发板的设备树二进制文件,如下图所示:
69.2.3 制作内核设备树镜像
使用命令“mkdir bootfs”创建一个文件夹bootfs用来挂载之后制作制作出来的 bootfs.ext4,如下图所示:
然后使用命令
dd if=/dev/zero of=bootfs.ext4 bs=1M count=20
mkfs.ext4 -L bootfs bootfs.ext4
建立一个大小为10M的ext4 磁盘,如下图所示:
接下来使用 mount 命令将 bootfs.ext4 挂载到bootfs 目录下,如下图所示:
mount ./bootfs.ext4 bootfs
然后我们在内核目录下使用以下命令拷贝我们编译生成的内核与设备树文件到bootfs目录里面,如下图所示:
cp linux-stm32mp-5.4.31-r0/linux-5.4.31/arch/arm/boot/uImage ./bootfs
cp linux-stm32mp-5.4.31-r0/linux-5.4.31/arch/arm/boot/dts/stm32mp157a-itop.dtb ./bootfs
建立一个大小为10M的ext4 磁盘,如下图所示:
接下来使用 mount 命令将 bootfs.ext4 挂载到bootfs 目录下,如下图所示:
mount ./bootfs.ext4 bootfs
然后我们在内核目录下使用以下命令拷贝我们编译生成的内核与设备树文件到bootfs目录里面,如下图所示:
cp linux-stm32mp-5.4.31-r0/linux-5.4.31/arch/arm/boot/uImage ./bootfs
cp linux-stm32mp-5.4.31-r0/linux-5.4.31/arch/arm/boot/dts/stm32mp157a-itop.dtb ./bootfs
拷贝完成后,在对应的目录下使用命令“umount bootfs”,进行解除挂载,如下图所示:
至此我们的内核设备树镜就制作完成了,然后只需要将我们制作的bootfs.ext4替代image烧写目录下的bootfs.ext4文件然后进行烧写即可(不论是烧写到EMMC还是TF卡都可以)。
烧写完成之后,将拨码开关设置为对应的启动模式,在这里设置的为emmc启动,启动之后在uboot倒计时的时候按下任意键,进入uboot的命令行,使用以下命令设置环境变量为hdmi,保存之后重启,如下图所示:
setenv lcdtype hdmi
saveenv
reset
为什么使用hdmi的环境变量呢,在上一章节之中21.3.3.1小节和21.3.4.1小节,找到我们hdmi环境变量设置源码如下:
setenv("bootcmd", "ext4load mmc 1:2 c2000000 uImage;ext4load mmc 1:2 c4000000 stm32mp157a-itop.dtb;bootm c2000000 - c4000000");
可以看到这里加载进去的正是我们上一小节编译出来对应的内核镜像和设备树二进制文件。重新启动进入内核之后打印信息如下图所示:
如果我们没有将文件系统一起烧写进去,那么就会卡在以下部分,这是由于默认的配置之中没有配置EMMC,而配置的TF卡热插拔检测引脚不匹配,所以都会卡在以下内容。
至此我们的内核移植的第一步就结束了。下面将会是一些外设功能的添加和部分内容的完善。
69.3 从零开始适配内核
在上一章节之中我们直接使用了官方的设备树,但其中有好多功能与我们自身的板子不匹配。同时为了让大家更好的了解移植的过程,我们接下来我们将回对设备树的内容进行裁剪,裁剪成最小系统,然后一点点的开始适配我们的外设和功能。
69.3.1设备树的裁剪
我们将裁剪完成的设备树文件存放在了“iTOP-STM32MP157开发板网盘资料汇总\07_系统移植\03_移植过程中用到的文件\01_裁剪过的最小设备树”路径下。由于pdf版本对于复制不太友好,大家可以直接将对应的文件进行拷贝覆盖。
首先,我们进入到内核源码路径下,如下图所示:
然后使用以下命令,对stm32mp15xx-itop.dtsi文件夹进行修改和裁剪
vim arch/arm/boot/dts/stm32mp15xx-itop.dtsi
我们将不适配我们的功能全部删除,只留下能确保正常启动的部分,裁剪完成之后,文件内容如下图所示:
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
/*
* Copyright (C) STMicroelectronics 2019 - All Rights Reserved
* Author: Alexandre Torgue <alexandre.torgue@st.com> for STMicroelectronics.
*/
#include "stm32mp157-m4-srm.dtsi"
#include "stm32mp157-m4-srm-pinctrl.dtsi"
#include <dt-bindings/mfd/st,stpmic1.h>
/ {
/*总内存*/
memory@c0000000 {
device_type = "memory";
reg = <0xc0000000 0x40000000>;
};
/*所保留内存*/
reserved-memory {
#address-cells = <1>;
#size-cells = <1>;
ranges;
mcuram2: mcuram2@10000000 {
compatible = "shared-dma-pool";
reg = <0x10000000 0x40000>;
no-map;
};
vdev0vring0: vdev0vring0@10040000 {
compatible = "shared-dma-pool";
reg = <0x10040000 0x1000>;
no-map;
};
vdev0vring1: vdev0vring1@10041000 {
compatible = "shared-dma-pool";
reg = <0x10041000 0x1000>;
no-map;
};
vdev0buffer: vdev0buffer@10042000 {
compatible = "shared-dma-pool";
reg = <0x10042000 0x4000>;
no-map;
};
mcuram: mcuram@30000000 {
compatible = "shared-dma-pool";
reg = <0x30000000 0x40000>;
no-map;
};
retram: retram@38000000 {
compatible = "shared-dma-pool";
reg = <0x38000000 0x10000>;
no-map;
};
};
vin: vin {
compatible = "regulator-fixed";
regulator-name = "vin";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
regulator-always-on;
};
};
/*HDMI CEC 控制器*/
&cec {
pinctrl-names = "default", "sleep";
pinctrl-0 = <&cec_pins_b>;
pinctrl-1 = <&cec_pins_sleep_b>;
status = "okay";
};
/*双核心*/
&cpu0{
cpu-supply = <&vddcore>;
};
&cpu1{
cpu-supply = <&vddcore>;
};
/*循环冗余校验计算单元*/
&crc1 {
status = "okay";
};
/*直接内存操作*/
&dma1 {
sram = <&dma_pool>;
};
&dma2 {
sram = <&dma_pool>;
};
/*数字钟温度传感器*/
&dts {
status = "okay";
};
/*图像处理单元*/
&gpu {
contiguous-area = <&gpu_reserved>;
status = "okay";
};
/*哈希处理器*/
&hash1 {
status = "okay";
};
/*处理器间通信控制器*/
&ipcc {
status = "okay";
};
/*看门狗*/
&iwdg2 {
timeout-sec = <32>;
status = "okay";
};
/*随机数发生器*/
&rng1 {
status = "okay";
};
/*实时时钟*/
&rtc {
status = "okay";
};
/*静态随机存取存储器*/
&sram {
dma_pool: dma_pool@0 {
reg = <0x50000 0x10000>;
pool;
};
};
/*电源基准缓冲器*/
&vrefbuf {
regulator-min-microvolt = <2500000>;
regulator-max-microvolt = <2500000>;
vdda-supply = <&vdd>;
status = "okay";
};
&i2c4 {
pinctrl-names = "default", "sleep";
pinctrl-0 = <&i2c4_pins_a>;
pinctrl-1 = <&i2c4_pins_sleep_a>;
i2c-scl-rising-time-ns = <185>;
i2c-scl-falling-time-ns = <20>;
clock-frequency = <400000>;
status = "okay";
/* spare dmas for other usage */
/delete-property/dmas;
/delete-property/dma-names;
/*电源管理芯片*/
pmic: stpmic@33 {
compatible = "st,stpmic1";
reg = <0x33>;
interrupts-extended = <&exti_pwr 55 IRQ_TYPE_EDGE_FALLING>;
interrupt-controller;
#interrupt-cells = <2>;
status = "okay";
regulators {
compatible = "st,stpmic1-regulators";
buck1-supply = <&vin>;
buck2-supply = <&vin>;
buck3-supply = <&vin>;
buck4-supply = <&vin>;
ldo1-supply = <&v3v3>;
ldo2-supply = <&vin>;
ldo3-supply = <&vdd_ddr>;
ldo4-supply = <&vin>;
ldo5-supply = <&vin>;
ldo6-supply = <&v3v3>;
vref_ddr-supply = <&vin>;
boost-supply = <&vin>;
pwr_sw1-supply = <&bst_out>;
pwr_sw2-supply = <&bst_out>;
vddcore: buck1 {
regulator-name = "vddcore";
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <1350000>;
regulator-always-on;
regulator-initial-mode = <0>;
regulator-over-current-protection;
};
vdd_ddr: buck2 {
regulator-name = "vdd_ddr";
regulator-min-microvolt = <1350000>;
regulator-max-microvolt = <1350000>;
regulator-always-on;
regulator-initial-mode = <0>;
regulator-over-current-protection;
};
vdd: buck3 {
regulator-name = "vdd";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
st,mask-reset;
regulator-initial-mode = <0>;
regulator-over-current-protection;
};
v3v3: buck4 {
regulator-name = "v3v3";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
regulator-over-current-protection;
regulator-initial-mode = <0>;
};
v1v8_audio: ldo1 {
regulator-name = "v1v8_audio";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
regulator-always-on;
interrupts = <IT_CURLIM_LDO1 0>;
};
v3v3_hdmi: ldo2 {
regulator-name = "v3v3_hdmi";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
interrupts = <IT_CURLIM_LDO2 0>;
};
vtt_ddr: ldo3 {
regulator-name = "vtt_ddr";
regulator-min-microvolt = <500000>;
regulator-max-microvolt = <750000>;
regulator-always-on;
regulator-over-current-protection;
};
vdd_usb: ldo4 {
regulator-name = "vdd_usb";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
interrupts = <IT_CURLIM_LDO4 0>;
regulator-always-on;
};
vdda: ldo5 {
regulator-name = "vdda";
regulator-min-microvolt = <2900000>;
regulator-max-microvolt = <2900000>;
interrupts = <IT_CURLIM_LDO5 0>;
regulator-boot-on;
};
v1v2_hdmi: ldo6 {
regulator-name = "v1v2_hdmi";
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <1200000>;
regulator-always-on;
interrupts = <IT_CURLIM_LDO6 0>;
};
vref_ddr: vref_ddr {
regulator-name = "vref_ddr";
regulator-always-on;
regulator-over-current-protection;
};
bst_out: boost {
regulator-name = "bst_out";
interrupts = <IT_OCP_BOOST 0>;
};
vbus_otg: pwr_sw1 {
regulator-name = "vbus_otg";
interrupts = <IT_OCP_OTG 0>;
};
vbus_sw: pwr_sw2 {
regulator-name = "vbus_sw";
interrupts = <IT_OCP_SWOUT 0>;
regulator-active-discharge = <1>;
};
};
onkey {
compatible = "st,stpmic1-onkey";
interrupts = <IT_PONKEY_F 0>, <IT_PONKEY_R 0>;
interrupt-names = "onkey-falling", "onkey-rising";
power-off-time-sec = <10>;
status = "okay";
};
watchdog {
compatible = "st,stpmic1-wdt";
status = "disabled";
};
};
};
/*TF卡*/
&sdmmc1 {
pinctrl-names = "default", "opendrain", "sleep";
pinctrl-0 = <&sdmmc1_b4_pins_a>;
pinctrl-1 = <&sdmmc1_b4_od_pins_a>;
pinctrl-2 = <&sdmmc1_b4_sleep_pins_a>;
cd-gpios = <&gpiob 7 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
disable-wp;
st,neg-edge;
bus-width = <4>;
vmmc-supply = <&v3v3>;
status = "okay";
};
/*终端所用到的串口*/
&uart4 {
pinctrl-names = "default", "sleep", "idle";
pinctrl-0 = <&uart4_pins_a>;
pinctrl-1 = <&uart4_sleep_pins_a>;
pinctrl-2 = <&uart4_idle_pins_a>;
pinctrl-3 = <&uart4_pins_a>;
/delete-property/dmas;
/delete-property/dma-names;
status = "okay";
};
修改完成之后,保存退出,然后使用命令“vim stm32mp157a-itop.dts”,来对stm32mp157a-itop.dts文件进行修改和裁剪,这里注意在引用的第四个设备树头文件要修改为#include "stm32mp15xxaa-pinctrl.dtsi",裁剪完成内容如下所示:
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
/*
* Copyright (C) STMicroelectronics 2019 - All Rights Reserved
* Author: Alexandre Torgue <alexandre.torgue@st.com> for STMicroelectronics.
*/
/dts-v1/;
#include "stm32mp157.dtsi"
#include "stm32mp15xa.dtsi"
#include "stm32mp15-pinctrl.dtsi"
#include "stm32mp15xxaa-pinctrl.dtsi"
#include "stm32mp15xx-itop.dtsi"
/ {
model = "STMicroelectronics STM32MP157A-iTOP Discovery Board";
compatible = "st,stm32mp157a-dk1", "st,stm32mp157";
aliases {
serial0 = &uart4;
};
chosen {
stdout-path = "serial0:115200n8";
};
reserved-memory {
gpu_reserved: gpu@da000000 {
reg = <0xda000000 0x4000000>;
no-map;
};
optee_memory: optee@0xde000000 {
reg = <0xde000000 0x02000000>;
no-map;
};
};
};
&optee {
status = "okay";
};
保存退出,至此我们的裁剪就完成了,回到源码目录下,可以使用“./create.sh”或者“make dtbs”来进行设备树的编译(内核可以不编译,因为我们在这里并没有对内核进行修改)。将生成的设备树文件进行少些,烧写成功之后,重启开发版,得到的结果和上一小结相同,因为我们还并未对TF卡和EMMC功能进行完善,下面我们将正式开始功能的完善。
69.3.2 TF卡和EMMC功能完善
回到设备树目录下,使用命令“vim stm32mp15xx-itop.dtsi”,对stm32mp15xx-itop.dtsi文件进行修改,进入文件之后如下图所示:
然后我们使用查找命令对“sdmmc1”进行查找,查找完成如下图所示:
sdmmc1对应的为TF卡,其中cd-gpios为热插拔检测引脚,在这里设置的为Pb7,由下图TF卡原理图可知我们开发板对应的引脚为PC0
所以我们将
cd-gpios = <&gpiob 7 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
修改为
cd-gpios = <&gpioc 0 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
修改完成如下图所示:
在该内容下方添加以下内容。以此来适配EMMC。添加完成之后如下图所示:
&sdmmc2 {
pinctrl-names = "default", "opendrain", "sleep";
pinctrl-0 = <&sdmmc2_b4_pins_a &sdmmc2_d47_pins_a>;
pinctrl-1 = <&sdmmc2_b4_od_pins_a &sdmmc2_d47_pins_a>;
pinctrl-2 = <&sdmmc2_b4_sleep_pins_a &sdmmc2_d47_sleep_pins_a>;
non-removable;
no-sd;
no-sdio;
st,neg-edge;
bus-width = <8>;
vmmc-supply = <&v3v3>;
vqmmc-supply = <&vdd>;
mmc-ddr-3_3v;
status = "okay";
};
保存退出,回到内核源码目录下,使用“make dtbs”,生成对应的设备树,根据之前章节的内容来将设备树的二进制镜像烧写(由于我们对TF卡和EMMC都进行了配置,所以我们在这里都可以进行烧写),烧写完成之后,重新启动开发板,在uboot命令行之中设置环境变量为“hdmi”之后,重启开发版,进入系统之后终端打印如下图所示:
69.3.3 USB适配
在进入文件系统之后,插入U盘,发现USB口并无法使用,所以我们对USB的功能进行添加。
首先来到内核源码文件夹,如下图所示:
然后使用命令“vim arch/arm/boot/dts/stm32mp15xx-itop.dtsi”,进入stm32mp15xx-itop.dtsi文件夹,如下图所示:
在根节点添加以下内容
usb_phy_tuning: usb-phy-tuning {
st,hs-dc-level = <2>;
st,fs-rftime-tuning;
st,hs-rftime-reduction;
st,hs-current-trim = <15>;
st,hs-impedance-trim = <1>;
st,squelch-level = <3>;
st,hs-rx-offset = <2>;
st,no-lsfs-sc;
};
对USB_PHY进行功能设定,添加完成之后如下图所示:
然后来到该文件的最下方,追加以下内容:
&usbh_ehci {
phys = <&usbphyc_port0>;
status = "okay";
};
&usbotg_hs {
pinctrl-0 = <&usbotg_hs_pins_a>;
pinctrl-names = "default";
phys = <&usbphyc_port1 0>;
phy-names = "usb2-phy";
status = "okay";
};
&usbphyc {
status = "okay";
};
&usbphyc_port0 {
st,phy-tuning = <&usb_phy_tuning>;
};
&usbphyc_port1 {
st,phy-tuning = <&usb_phy_tuning>;
};
以上添加的内容为分别为将端口0分配给USBH,将端口2分配给OTG,添加完成之后如下图所示:
保存退出,回到内核源码目录下,如下图所示
我们使用以下命令进入配置菜单,配置菜单打开之后如下图所示:
export ARCH=arm
export CROSS_COMPILE=arm-none-linux-gnueabihf-
make stm32_itop_mp1a_defconfig
make menuconfig
在以下位置,对Support for uevent helper进行勾选,
> Device Drivers
> Generic Driver Options
[*] Support for uevent helper
将配置保存到.config文件里,如下图所示:
退出,回到源码目录下。使用以下命令将.config 复制成我们的默认配置文件,如下图所示:
cp .config arch/arm/configs/stm32_itop_mp1a_defconfig
使用“./create.sh”命令,生成对应的设备树,根据之前章节的内容来将设备树的二进制镜像烧写(由于我们对TF卡和EMMC都进行了配置,所以我们在这里都可以进行烧写),烧写完成之后,重新启动开发板,在uboot命令行之中设置环境变量为“hdmi”之后,重启开发版,进入系统之后终端打印如下图所示:
插入U盘之后,显示如下打印信息:
这证明我们的USB已经可以使用了,使用命令“mount /dev/sda1 /mnt/”,对U盘进行挂载,然后查看其中的内容,如下图所示:
至此,我们的USB适配章节就结束了。
69.3.4网卡适配
在上一章节之中,我们对USB功能进行了添加,然后在本小节之中将会对网卡功能进行适配。当开发板进入系统之后,使用命令“ifconfig -a”查看所有的网络设备,如下图所示:
可以看到我们的内部回环网络和wifi对应的两个网络,内部回环网络设备是默认都有的,那为什么我们没有配置wifi却有wifi的网络呢,这是因为我们所使用的蓝牙WIFI模块,也是USB设备,在上一小节之中,我们配置好了USB,WIFI在加载对应的KO文件之后也产生了对应的设备(KO文件的加载为了方便我们直接放在了开机启动项之中,大家不用理会)。而这里并没有我们本小节所要配置的有线网络,下面来让我们进行适配。
回到虚拟机Ubuntu的源码目录下,如下图所示:
然后我们使用以下命令
vim arch/arm/boot/dts/stm32mp15xx-itop.dtsi
来对stm32mp15xx-itop.dtsi文件进行修改,进入该文件夹之后如下图所示:
由于网络节点已经在其他设备树文件中定义过了,而我们引用了该设备树文件,我们只需要重写该节点,配置不同的功能将会合并,配置相同的功能将会覆盖之前的内容(这里大家可以去了解设备树基础)。我们来到文件最下方,添加以下内容,添加完成如下图所示:
ðernet0 {
status = "okay";
pinctrl-0 = <ðernet0_rgmii_pins_a>;
pinctrl-1 = <ðernet0_rgmii_pins_sleep_a>;
pinctrl-names = "default", "sleep";
phy-mode = "rgmii-id";
/*phy-mode = "rgmii";*/
max-speed = <1000>;
phy-handle = <&phy0>;
mdio0 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "snps,dwmac-mdio";
phy0: ethernet-phy@0 {
reg = <0>;
};
};
};
参加完成如下图所示:
我们在设备树中的修改就已经完成了,但是还需要在内核之中进行一下配置,保存退出之后,回到内核源码目录下,使用以下命令
export ARCH=arm
export CROSS_COMPILE=arm-none-linux-gnueabihf-
make stm32_itop_mp1a_defconfig
make menuconfig
进入内核功能配置菜单,如下图所示:
由于官方已经进行了一定的配置,我们只需要将以下路径的内容选中即可,
> Device Drivers
> Network device support
> Ethernet driver support
[*] Support for STMMAC Selftests
选中之后如下图所示:
将配置保存到.config文件里,如下图所示:
退出,回到源码目录下。使用以下命令将.config 复制成我们的默认配置文件,如下图所示:
cp .config arch/arm/configs/stm32_itop_mp1a_defconfig
将我们所提供的“iTOP-STM32MP157开发板网盘资料汇总\07_系统移植\03_移植过程中用到的文件\11_网卡替换文件”路径下的stmmac_main.c文件对内核源码“drivers/net/ethernet/stmicro/stmmac/”目录下的stmmac_main.c进行替换,替换完成之后使用命令“./create.sh”对内核进行编译,编译成功如下图所示:
将我们新生成的设备树和内核,根据之前章节的内容生成镜像文件并烧写,烧写完成之后,重新启动开发板,在uboot命令行之中设置环境变量为“hdmi”之后,重启开发版,进入系统之后终端打印如下图所示:
再次使用命令“ifconfig -a”,会发现已经有了eth0,如下图所示:
然后我们使用命令“udhcpc eth0”,为eth0自动分配IP地址,如下所示:
然后我们使用命令“ping www.topeetboard.com”来ping迅为的官网,可以正常ping通,如下图所示:
至此我们的网卡就适配成功了。
69.3.5串口适配
在我们留下的默认配置中,我们留下了串口4作为终端的打印输出,而在我们的开发板上还有一个232外设,所使用的为USART3,原理图如下图所示:
根据底板和核心板的原理图可以得到我们对应的引脚,引脚对应列表如下图所示:
引脚名称 | 对应的管脚 |
USART3_CTS | PI10 |
USART3_RTS | PG8 |
USART3_RX | PD9 |
USART3_TX | PD8 |
下面我们对串口3进行适配,回到内核源码目录下,如下图所示:
然后使用以下命令
vim arch/arm/boot/dts/stm32mp15-pinctrl.dtsi
进入stm32mp15-pinctrl.dtsi文件, stm32mp15-pinctrl.dtsi文件是对ST官方对于引脚功能的定义,使用查找命令查找“usart3”,查找完成如下图所示:
在看过关于USART3的引脚定义后,发现和我们的并不符合,所以我们要添加适合我们开发板的引脚定义,而一般情况下我们不会在stm32mp15-pinctrl.dtsi 文件直接添加,而是在我们自己的设备树中进行添加,下面我们退出该文件,然后使用以下命令进入到我们的设备树文件之中:
vim arch/arm/boot/dts/stm32mp15xx-itop.dtsi
来到我们文件的最下方,添加以下内容:
&pinctrl {
usart3_pins_c: uart3-c-0 {
pins1 {
pinmux = <STM32_PINMUX('D', 8, AF7)>, /* USART3_TX */
<STM32_PINMUX('G', 8, AF8)>; /* USART3_RTS */
bias-disable;
drive-push-pull;
slew-rate = <0>;
};
pins2 {
pinmux = <STM32_PINMUX('D', 9, AF7)>, /* USART3_RX */
<STM32_PINMUX('I', 10, AF8)>; /* USART3_CTS_NSS */
bias-disable;
};
};
usart3_idle_pins_c: uart3-idle-c-0{
pins1 {
pinmux = <STM32_PINMUX('D', 8, ANALOG)>, /* USART3_TX */
<STM32_PINMUX('G', 8, ANALOG)>, /* USART3_RTS */
<STM32_PINMUX('I', 10, ANALOG)>; /* USART3_CTS_NSS */
};
pins2 {
pinmux = <STM32_PINMUX('D', 9, AF7)>; /* USART3_RX */
bias-disable;
};
};
usart3_sleep_pins_c: uart3-sleep-c-0 {
pins {
pinmux = <STM32_PINMUX('D', 8, ANALOG)>, /* USART3_TX */
<STM32_PINMUX('G', 8, ANALOG)>, /* USART3_RTS */
<STM32_PINMUX('I', 10, ANALOG)>, /* USART3_CTS_NSS */
<STM32_PINMUX('D', 9, ANALOG)>; /* USART3_RX */
};
};
};
添加完成如下图所示:
&pinctrl 就是引脚功能定义的节点,我们对于自身引脚功能的定义都要会在该节点中添加。至此我们的串口3引脚就定义完成了。
同时还有有着一个485接口,原理图如下图所示:
引脚名称 | 对应的管脚 |
UART5_TX | PB13 |
UART5_RX | PB12 |
和上述的串口3功能引脚定义添加方法相同,将以下符合我们板子的引脚定义添加到&pinctrl 节点中:
uart5_pins_a: uart5-a-0 {
pins1 {
pinmux = <STM32_PINMUX('B', 13, AF14)>; /* USART5_TX */
bias-disable;
drive-push-pull;
slew-rate = <0>;
};
pins2 {
pinmux = <STM32_PINMUX('B', 12, AF14)>; /* USART3_RX */
bias-disable;
};
};
uart5_idle_pins_a: uart5-idle-a-0 {
pins1 {
pinmux = <STM32_PINMUX('B', 13, ANALOG)>; /* USART5_TX */
};
pins2 {
pinmux = <STM32_PINMUX('B', 12, AF14)>; /* USART5_RX */
bias-disable;
};
};
uart5_sleep_pins_a: uart5-sleep-a-0 {
pins {
pinmux = <STM32_PINMUX('B', 13, ANALOG)>, /* USART5_TX */
<STM32_PINMUX('B', 12, ANALOG)>; /* USART5_RX */
};
};
添加完成如下图所示:
至此我们的串口5引脚就定义完成了。然后在&pinctrl 节点下方追加&usart3,&usart5两个节点相关的内容如下:
&usart3 {
pinctrl-names = "default", "sleep", "idle";
pinctrl-0 = <&usart3_pins_c>;
pinctrl-1 = <&usart3_idle_pins_c>;
pinctrl-2 = <&usart3_sleep_pins_c>;
uart-has-rtscts;
status = "okay";
};
&uart5 {
pinctrl-names = "default", "sleep", "idle";
pinctrl-0 = <&uart5_pins_a>;
pinctrl-1 = <&uart5_sleep_pins_a>;
pinctrl-2 = <&uart5_idle_pins_a>;
/delete-property/dmas;
/delete-property/dma-names;
status = "okay";
};
添加完成如下图所示:
在之前的内容之中,我么只是对管脚进行了定义,而在这里我们使能了两个串口功能且使用我们自己定义的引脚。
保存退出。然后使用以下命令
vim arch/arm/boot/dts/stm32mp157a-itop.dts
打开stm32mp157a-itop.dts文件,可以看到aliases 节点。如下图所示:
aliases节点的作用就是用来设置别名,而在我们的设备树之中,仅仅对UART4赋予了一个别名,在系统之中,串口4即serial0就是终端信息打印接口ttySTM0,而在这里我们添加串口3和串口5的别名信息。内容如下:
serial3 = &usart3;
serial5 = &uart5;
将串口3赋予serial3别名,将串口5赋予serial5别名,进入系统之后相对应的接口分别为ttySTM3和接口ttySTM5.
添加完成之后,保存退出。回到内核源码目录下,使用“make dtbs”,生成对应的设备树,根据之前章节的内容来将设备树的二进制镜像烧写,烧写完成之后,重新启动开发板,在uboot命令行之中设置环境变量为“hdmi”之后,重启开发版,进入系统之后终端打印如下图所示:
使用命令“ls /dev/ttySTM*”查看对应的串口设备,如下图所示:
至此我们的串口功能就适配成功了,如果有想要测试的小伙伴可以分别根据“3.8 UART测试和扩展”和“3.14 485接口测试”进行串口测试。
69.3.6 RTC实时时钟适配
我们板载了一个纽扣电池电路,RX8010为实时时钟芯片,用来读取和设置年、月、日、小时、分钟、秒、星期等数据。年份的记录范围到 2099 年,能自动识别闰年。在通讯开始的时候,时钟和日历的数据保持不变,直到通讯结束后会被自动刷新。由3.3v电源和纽扣电池来供电,当3.3v断开后,纽扣电池仍然可以使该模块继续工作。再次上电之后会仍为正确的时间。原理图如下图所示:
当我们不对外部RTC进行适配时,会发现系统启动之后可以使用以下命令设置和查看时间,如下图所示:
date -s "2021-12-24 12:58:00"
date
但是重启上电之后会发现, 我们写入的时间信息并没有保存到RTC芯片之中,如下图所示:
所以我们需要对其进行适配。回到我们的内核源码目录下,如下图所示:
然后我们使用以下命令
vim arch/arm/boot/dts/stm32mp15xx-itop.dtsi
进入stm32mp15xx-itop.dtsi 文件,对其进行内容的添加。我们的RX8010实时时钟芯片的通信使用的为I2C协议,根据原理图可知我们使用的为I2C5。我们的电源管理芯片使用的为I2C4,为了严谨和之后设备树的阅读,我们在I2C4节点下添加I2C5节点内容,添加内容如下
&i2c5 {
pinctrl-names = "default", "sleep";
pinctrl-0 = <&i2c5_pins_a>;
pinctrl-1 = <&i2c5_pins_sleep_a>;
i2c-scl-rising-time-ns = <100>;
i2c-scl-falling-time-ns = <7>;
clock-frequency = <100000>;
/* spare dmas for other usage */
/delete-property/dmas;
/delete-property/dma-names;
status = "okay";
};
以上内容为i2C5的一些管脚和功能设置,然后我们在该节点中添加我们关于rx8010芯片相关的配置,添加内容如下:添加完成如下图所示:
rtc@32 {
compatible = "rx8010";
reg = <0x32>;
status = "okay";
};
然后我们需要添加RTC对应的头文件,添加到文件头部,添加内容如下
#include <dt-bindings/rtc/rtc-stm32.h>
添加完成如下图所示:
由于在内核源码里面,关于该芯片的驱动文件已经存在,我们只需要在配置菜单中进行勾选即可,保存退出,回到内核源码目录,如下图所示:
我们使用以下命令进入配置菜单,配置菜单打开之后如下图所示:
export ARCH=arm
export CROSS_COMPILE=arm-none-linux-gnueabihf-
make stm32_itop_mp1a_defconfig
make menuconfig
在以下位置,对Epson RX8010SJ 进行勾选,勾选完成如下图所示:
> Device Drivers
> Real Time Clock
<*> Epson RX8010SJ
将配置保存到.config文件里,如下图所示:
退出,回到源码目录下。使用以下命令将.config 复制成我们的默认配置文件,如下图所示:
cp .config arch/arm/configs/stm32_itop_mp1a_defconfig
然后使用命令“./create.sh”对内核进行编译,编译成功如下图所示:
将我们新生成的设备树和内核,根据之前章节的内容生成镜像文件并烧写,烧写完成之后,重新启动开发板,在uboot命令行之中设置环境变量为“hdmi”之后,重启开发版,进入系统之后终端打印如下图所示:
输入以下命令
date -s "2021-10-23 12:58:00"
hwclock -w
对时间进行调整如下图所示
安装上纽扣电池,断电再开机,使用命令“date”,如下图所示,时间仍然是 2021 年,说明实时时钟起作用了。
至此我们的外部RTC实时时钟适配就完成了。
69.3.7 HDMI适配
在我们的开发板上带有一个两个HDMI接口,如下图所示:
这两个HDMI不可以搞混,标准HDMI接口为左侧稍大的接口,该信号和下方的LVDS信号为同一信号,只能用来接迅为提供的7寸塑胶壳触摸屏、9.7寸塑胶壳触摸屏和10.1寸金属框触摸屏,不能用来接其他屏幕更不能将其他信号通过该接口进行输入(如有违规操作,很可能会烧坏开发板),而旁边那个稍小的接口正是我们本章节要用的接口,该接口可以通过迅为提供的HDMI线接到我们常用的电脑显示屏上。而该接口的信号是RGB信号通过sii9022芯片转换出来的,所以当我们使用HDMI的时候无法使用RGB屏幕。
LTDC 是 STM32MP1 自带的液晶屏幕接口,用于连接 RGB LCD 接口的屏幕。
从图可以看出 LTDC 的信号可以分为两类:4 个控制信号(LCD_CLK 像素时钟、 LCD_HSYNC 水平同步、LCD_VSYNC 垂直同步、LCD_DE 数据有效)和 3 个 RGB 数据信号 (8bit x 3)。而我们在适配HDMI的时候,不需要深入了解,只需要知道我们的HDMI信号是RGB通过sii9022转换而来的即可,下面开始我们的适配步骤。
首先回到我们的内核源码。如下图所示:
然后使用命令
vim arch/arm/boot/dts/stm32mp15xx-itop.dtsi
对stm32mp15xx-itop.dtsi文件进行修改,根据原理图,可以看到sim9022芯片通信的是通过i2c协议实现的,且为i2c2,为了严谨,我们添加i2c2对应的内容到i2c4节点上方,添加内容如下:
&i2c2 {
pinctrl-names = "default", "sleep";
pinctrl-0 = <&i2c2_pins_a>;
pinctrl-1 = <&i2c2_pins_sleep_a>;
i2c-scl-rising-time-ns = <100>;
i2c-scl-falling-time-ns = <7>;
/delete-property/dmas;
/delete-property/dma-names;
status = "okay";
};
添加完成如下图所示
保存退出。然后继续使用命令
vim arch/arm/boot/dts/stm32mp157a-itop.dts
对stm32mp157a-itop.dts文件进行修改。来到最下方,我们首先添加以下内容,使能ltdc。
<dc {
pinctrl-names = "default", "sleep";
pinctrl-0 = <<dc_pins_b>;
pinctrl-1 = <<dc_pins_sleep_b>;
status = "okay";
};
添加完成如下图所示:
然后我们在ltdc节点之中添加port子节点,添加内容如下所示:
port {
#address-cells = <1>;
#size-cells = <0>;
ltdc_ep0_out: endpoint@0 {
reg = <0>;
remote-endpoint = <&sii9022_in>;
};
};
添加完成如下图所示:
该子接口的作用就是将LTDC 接口的数据输出到 HDMI。
然后我们在该节点的上方中添加sim9022芯片相关的内容,添加内容如下图所示:
hdmi-transmitter@39 {
compatible = "sil,sii9022";
reg = <0x39>;
iovcc-supply = <&v3v3_hdmi>;
cvcc12-supply = <&v1v2_hdmi>;
reset-gpios = <&gpioa 14 GPIO_ACTIVE_LOW>;
interrupts = <15 IRQ_TYPE_EDGE_FALLING>;
interrupt-parent = <&gpioa>;
#sound-dai-cells = <0>;
status = "okay";
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
sii9022_in: endpoint {
remote-endpoint = <<dc_ep0_out>;
};
};
};
};
添加完成如下图所示:
该子节点的内容主要是对一些引脚和功能进行设置。然后引用ltdc_ep0_out节点,通过port 节点来接收 LTDC 接口的数据。
添加完成之后,保存退出。回到内核源码目录,如下图所示:
我们使用以下命令进入配置菜单,配置菜单打开之后如下图所示:
export ARCH=arm
export CROSS_COMPILE=arm-none-linux-gnueabihf-
make stm32_itop_mp1a_defconfig
make menuconfig
官方已经默认给我们勾选上HDMI的相关配置了,而我们现在要做的是使能DRM 驱动的 FB。并让屏幕显示内核logo。
首先在以下位置选中Enable legacy fbdev support for your modesetting driver ,选中之后如下图所示:
Device Drivers
> Graphics support
> Direct Rendering Manager (XFree86 4.1.0 and higher DRI support)
[*] Enable legacy fbdev support for your modesetting driver
然后我们在以下位置,对ARM PrimeCell PL110 support 进行勾选,支持frame buffer驱动,勾选完成如下图所示:
> Device Drivers
> Graphics support
>Frame buffer Devices
> Support for frame buffer devices
<*> ARM PrimeCell PL110 support
然后我们在以下位置,对Bootup logo进行勾选,支持内核logo显示,勾选完成如下图所示
> Device Drivers
> Graphics support
[*] Bootup logo --->
将配置保存到.config文件里,如下图所示:
退出,回到源码目录下。使用以下命令将.config 复制成我们的默认配置文件,如下图所示:
cp .config arch/arm/configs/stm32_itop_mp1a_defconfig
然后使用命令“./create.sh”对内核进行编译,编译成功如下图所示:
将我们新生成的设备树和内核,根据之前章节的内容生成镜像文件并烧写,烧写完成之后,断电,使用我们提供的HDMI线连接开发板和显示屏,连接完成之后,重新启动开发板,在uboot命令行之中设置环境变量为“hdmi”之后,重启开发版,进入系统之后终端打印如下图所示:
这时可以看到显示屏上会有两只小企鹅出现,大家也可以使用QT程序来进行测试。
69.3.8 LCD屏幕适配
在上一个章节之中我们对HDMI进行了适配,由于STM32MP157只有一条RGB的通道,而上一小节之中我们的HDMI信号是通过sii9022芯片转换出来的,本章节我们将会对RGB和LVDS接口的LCD进行适配,其中LVDS信号其实也是RGB信号转换出来的,转换芯片原理图如下图所示:
该芯片的使用更为简单,不需要我们来进行控制,该芯片会自动将ltdc输出的RGB信号转换为LVDS的差分信号,所以我们所有屏幕的适配过程几乎相同,只是每个屏幕的参数或有不同。下面在我们开始适配之前,对每个屏幕的参数进行一下陈列。
我们提供了六款屏幕,屏幕尺寸分别为10.1寸金属框、9.7寸塑胶壳、7寸塑胶壳、7寸金属框(我们也会称之1024x600金属框)、5寸金属框和4.3寸金属框。具体的展示可以去第二章进行具体的查看。每款屏幕的参数如下:
款式 | 参数 | 值 |
4.3寸金属框 | 水平显示区域 | 480(tCLK) |
HSPW(thp) | 41(tCLK) | |
HBP(thb) | 4(tCLK) | |
HFP(thf) | 8(tCLK) | |
垂直显示区域 | 272(th) | |
VSPW(tvp) | 10(th) | |
VBP(tvb) | 2(th) | |
VFP(tvf) | 4(th) | |
像素时钟 | 9200(KHz) | |
5寸金属框 | 水平显示区域 | 800(tCLK) |
HSPW(thp) | 32(tCLK) | |
HBP(thb) | 80(tCLK) | |
HFP(thf) | 48(tCLK) | |
垂直显示区域 | 480 (th) | |
VSPW(tvp) | 5(th) | |
VBP(tvb) | 14(th) | |
VFP(tvf) | 3(th ) | |
像素时钟 | 20000(KHz) | |
7寸金属框(1024x600) | 水平显示区域 | 1024(tCLK) |
HSPW(thp) | 20(tCLK) | |
HBP(thb) | 80(tCLK) | |
HFP(thf) | 48(tCLK) | |
垂直显示区域 | 600(th ) | |
VSPW(tvp) | 5(th ) | |
VBP(tvb) | 14(th ) | |
VFP(tvf) | 3(th ) | |
像素时钟 | 70000(KHz) | |
7寸塑胶壳 | 水平显示区域 | 800(tCLK) |
HSPW(thp) | 32(tCLK) | |
HBP(thb) | 80(tCLK) | |
HFP(thf) | 48(tCLK) | |
垂直显示区域 | 1280(th ) | |
VSPW(tvp) | 5(th ) | |
VBP(tvb) | 14(th ) | |
VFP(tvf) | 3(th ) | |
像素时钟 | 58000(KHz) | |
9.7寸塑胶壳 | 水平显示区域 | 1024(tCLK) |
HSPW(thp) | 32(tCLK) | |
HBP(thb) | 80(tCLK) | |
HFP(thf) | 48(tCLK) | |
垂直显示区域 | 768(th ) | |
VSPW(tvp) | 5(th ) | |
VBP(tvb) | 14(th ) | |
VFP(tvf) | 3(th ) | |
像素时钟 | 70000(KHz) | |
10.1寸塑胶壳 | 水平显示区域 | 1024(tCLK) |
HSPW(thp) | 32(tCLK) | |
HBP(thb) | 80(tCLK) | |
HFP(thf) | 48(tCLK) | |
垂直显示区域 | 600(th ) | |
VSPW(tvp) | 5(th ) | |
VBP(tvb) | 14(th ) | |
VFP(tvf) | 3(th ) | |
像素时钟 | 51200(KHz) |
以上就是我们屏幕的参数,下面我们将进行每个屏幕的适配,首先回到我们的内核源码目录下如下图所示:
然后我们使用命令
cd arch/arm/boot/dts/
进入设备树目录下,如下图所示:
在之前的uboot的移植章节,我们关于设置屏幕环境变量章节,是通过加载不同的设备树来对不同的屏幕进行适配的,每个屏幕对应的设备树名称如下:
种类 | 对应设备树二进制文件名称 |
4.3寸金属框 | stm32mp157a-itop-rgb-043.dts |
5寸金属框 | stm32mp157a-itop-rgb-050.dts |
1024x600金属框 | stm32mp157a-itop-rgb-070.dts |
7寸塑胶壳 | stm32mp157a-itop-lvds-070.dts |
9.7寸塑胶壳 | stm32mp157a-itop-lvds-097.dts |
10.1寸金属框 | stm32mp157a-itop-lvds-101.dts |
HDMI | stm32mp157a-itop.dts |
我们以stm32mp157a-itop.dts文件为模板,分别来适配其他的屏幕,首先使用以下命令将stm32mp157a-itop.dts拷贝成我们需求的文件名称。
由于我们在上一小节中,在stm32mp157a-itop.dts文件中添加了hdmi显示相关的内容,而我们适配的其他屏幕不需要hdmi显示功能,所以我们需要将“iTOP-STM32MP157开发板网盘资料汇总\07_系统移植\03_移植过程中用到的文件\01_裁剪过的最小设备树”路径下将stm32mp157a-itop-rgb-043.dtb文件拷贝到 dtb目录下,拷贝完成如下图所示:
然后我们stm32mp157a-itop-rgb-043.dts为模板,复制为其他屏幕的设备树名称,复制命令如下,拷贝完成如下图所示:
cp stm32mp157a-itop-rgb-043.dts stm32mp157a-itop-rgb-050.dts
cp stm32mp157a-itop-rgb-043.dts stm32mp157a-itop-rgb-070.dts
cp stm32mp157a-itop-rgb-043.dts stm32mp157a-itop-lvds-070.dts
cp stm32mp157a-itop-rgb-043.dts stm32mp157a-itop-lvds-097.dts
cp stm32mp157a-itop-rgb-043.dts stm32mp157a-itop-lvds-101.dts
拷贝完成之后使用命令“ls stm32mp157a*itop*.dts”来进行初步的检查如下图所示:
然后我们使用命令“ vim Makefile ”,进入Makefile文件添加我们要编译的设备树文件。在我们之前添加的“stm32mp157a-itop.dtb \”下添加以下内容:
stm32mp157a-itop-rgb-043.dtb \
stm32mp157a-itop-rgb-050.dtb \
stm32mp157a-itop-rgb-070.dtb \
stm32mp157a-itop-lvds-070.dtb\
stm32mp157a-itop-lvds-097.dtb\
stm32mp157a-itop-lvds-101.dtb\
添加完成如下图所示:
保存退出之后,回到内核源码路径下,如下图所示:
然后我们使用以下命令来
export ARCH=arm
make dtbs
编译我们新生成的设备树,编译正常如下图所示:
至此我们每个屏幕的设备树就添加完成了,然后我们将开始我们的设备树内容的添加。我们首先要将屏幕供电使能,供电相关的原理图内容如下图所示:
首先分析分析一下原理图,其中 VSYS_LCD 是LCD的供电引脚,LCD电源使能有两种方案:
1. 当 R76 为 0 欧姆电阻接上时 VSYS_LCD 直接系统电源VIN供电。上电时 LCD 就可以亮。
2. 当去掉 R76 时,VSYS_LCD 由系统控制提供电源,Q6 NPN 三极管基极(CHG_EN)为低电平时, 三极管不导通,PMOS 管的 G 极与 S 极都为高电平,不导通。反之,当 CHG_EN 给高电平时,PMOS的 G 极为低点平,满足|Vg-Vs| > |Vtp| ,PMOS 导通,LCD供电使能。
而在这里我们的解决方法为第二种,给首先LCD_BL_CTRL为高电平,通过软件来使能LCD供电。通过原理图我们可以查找到LCD_BL_CTRL对应的引脚为PD13,如下图所示:
我们在内核源码目录下使用命令
vim arch/arm/boot/dts/stm32mp15xx-itop.dtsi
对stm32mp15xx-itop.dtsi内容进行修改,进入stm32mp15xx-itop.dtsi后如下图所示:
我们只需要将LCD_BL_CTRL设置为高电平即可,我们在这里直接使用内核已经写好的LED驱动,我们在根节点之中添加以下内容,添加完成如下图所示:
led {
compatible = "gpio-leds";
lcdpower{
label = "lcdpower";
gpios = <&gpiod 13 GPIO_ACTIVE_HIGH>;
default-state = "on";
};
};
以上内容所完成的就是至此我们对于LCD的供电就成功使能了。再回到我们的原理图,每一个LCD接口还会有着一个背光接口,相应的引脚名称为ADC119_DAC2,如下图所示:
我们只是列出了RGB接口的原理图,LVDS接口也是同样的引脚来控制背光的,同样的该背光也有两种控制方法,第一种为使该引脚作为LED设备,这样屏幕只有两种状态,亮和灭,第二种方式为使用PWM脉冲宽度调制,这样我们就可以控制LED背光的强弱。在这里我们使用第二种方式。
通过查看原理图可知,ADC119_DAC2对应的CPU引脚为PA5,如下图所示
然后我们查找STM32MP153A数据手册,可以看到PA5对应的为定时器的通道2来实现PWM功能。
下面我们开始完善设备树,我们首先在&pinctrl节点之中添加PWM的引脚定义,添加内容如下:
pwm2_pins_b: pwm2-0 {
pins {
pinmux = <STM32_PINMUX('A', 5, AF1)>; /* TIM2_CH1 */
bias-pull-down;
drive-push-pull;
slew-rate = <0>;
};
};
pwm2_sleep_pins_b: pwm1-sleep-0 {
pins {
pinmux = <STM32_PINMUX('A', 5, ANALOG)>; /* TIM2_CH1 */
};
};
添加完成之后如下图所示:
然后我们在文件最后添加&timers2节点(在上级头文件已经定义过了,所以我们没写在根节点之中),添加内容如下
&timers2 {
/* spare dmas for other usage */
/delete-property/dmas;
/delete-property/dma-names;
status = "okay";
pwm2: pwm {
pinctrl-0 = <&pwm2_pins_b>;
pinctrl-1 = <&pwm2_sleep_pins_b>;
pinctrl-names = "default", "sleep";
#pwm-cells = <2>;
status = "okay";
};
timer@2 {
status = "disabled";
};
};
添加完成如下图所示:
以上内容主要为定义了pwm节点且取别名为pwm2,然后应用了我们上一步的引脚定义,确定为PA5。
然后我们在根节点种添加背光节点 panel-backlight,添加内容如下:
panel_backlight: panel-backlight {
compatible = "pwm-backlight";
pwms = <&pwm2 0 5000000>;
brightness-levels = <0 4 8 16 32 64 128 255>;
default-brightness-level = <6>;
status = "okay";
};
添加完成如下图所示:
上述内容确定了背光等级为7级,默认为第六级,且引用了pwm2节点。
保存退出。至此,我们的背光就设置完成了,我们对于屏幕的基本通用设置就完成了。下面我们将对每一个屏幕独有的设备树进行修改,由于每个屏幕设备树的内容大体相同,只是最后匹配的名称不同(根据名称会匹配不同的参数),在这里我们以七寸LVDS屏幕为例,进行讲解。最后我们会提供单独的设备树文件,存放路径为“iTOP-STM32MP157开发板网盘资料汇总\07_系统移植\03_移植过程中用到的文件\02_每个屏幕对应的设备树文件”。
然后我们使用以下命令
vim arch/arm/boot/dts/stm32mp157a-itop-lvds-070.dts
进入七寸LVDS屏幕对应的设备树之中,进入之后如下图所示:
接下来我们在该节点下方重写ltdc节点。添加内容如下图所示:
<dc {
pinctrl-names = "default", "sleep";
pinctrl-0 = <<dc_pins_b>;
pinctrl-1 = <<dc_pins_sleep_b>;
status = "okay";
port {
#address-cells = <1>;
#size-cells = <0>;
ltdc_ep0_out: endpoint@0 {
reg = <0>;
remote-endpoint = <&panel_in_rgb>;
};
};
};
添加完成如下图所示:
添加内容所实现的功能是使ltdc输出信号输出到&panel_in_rgb节点。
Ltdc节点添加完成之后,我们在根节点添加我们LCD屏幕对应的panel-rgb节点,添加内容如下图所示:
panel: panel-rgb {
compatible = "itop,topeet-lvds-070";
pinctrl-names = "default", "sleep";
backlight = <&panel_backlight>;
status = "okay";
port {
panel_in_rgb: endpoint {
remote-endpoint = <<dc_ep0_out>;
};
};
};
添加完成之后如下图所示:
在这里最先注意的是compatible匹配值,稍后我们会进行说明,然后是在这里引用了我们之前写的背光引脚,最后是添加了一个port节点,和hdmi添加的port节点作用相同,通过port 节点来接收 LTDC 接口的数据。
在以上添加的内容之中,所有屏幕的设备树只有最后的compatible匹配值不相同,每块屏幕对应的compatible匹配值如下:
屏幕 | 对应的compatible匹配值 |
4.3寸金属框 | itop,topeet-rgb-043 |
5寸金属框 | itop,topeet-rgb-050 |
1024x600金属框 | itop,topeet-rgb-070 |
7寸塑胶壳 | itop,topeet-lvds-070 |
9.7寸塑胶壳 | itop,topeet-lvds-097 |
10.1寸金属框 | itop,topeet-lvds-101 |
修改完成的设备树文件存放路径为“iTOP-STM32MP157开发板网盘资料汇总\07_系统移植\03_移植过程中用到的文件\02_每个屏幕对应的设备树文件”,如下图所示:
然后我们需要修改对应的驱动文件,添加我们屏幕的参数和匹配值,回到内核源码路径下,如下下图所示
使用以下命令
vim drivers/gpu/drm/panel/panel-simple.c
来对panel-simple.c驱动文件进行内容的添加,进入panel-simple.c文件之后如下图所示:
使用搜索命令查找“platform_of_match”,查找完成如下图所示:
根据以上格式添加以下内容,添加完成如下图所示:
{
.compatible = "itop,topeet-rgb-043",
.data = &topeet_rgb_043,
}, {
.compatible = "itop,topeet-rgb-050",
.data = &topeet_rgb_050,
}, {
.compatible = "itop,topeet-rgb-070",
.data = &topeet_rgb_070,
}, {
.compatible = "itop,topeet-lvds-070",
.data = &topeet_lvds_070,
}, {
.compatible = "itop,topeet-lvds-097",
.data = &topeet_lvds_097,
}, {
.compatible = "itop,topeet-lvds-101",
.data = &topeet_lvds_101,
},
添加完成如下图所示:
添加完成之后,在该结构上方添加以下内容,该内容为每个屏幕对应的参数,内核会根据不同的设备树传递的设备值,来匹配不同的参数,屏幕的参数解释如下,可以根据之前提供的屏幕参数来确定下列各个参数值:
hdisplay:有效显示区水平像素数量,对应 Active Width
hsync_start:水平同步开始,对应 hdispay + HFP
hsync_end:水平同步结束,对应 hdisplay + HFP + HSYNC width(HPW)
htotal:水平总像素,对应 hdisplay + HFP + HSYNC width + HBP
vdisplay:垂直显示像素,对应 Active Height
vsync_start:垂直同步开始,对应 vdispay + VFP
vsync_end:垂直像素结束,对应 vdisplay + VFP + VSYNC width(VPW)
vtotal:垂直总像素,对应 vdisplay + VFP + VSYNC width + VBP
vrefresh:刷新率
/*4.3寸RGB屏*/
static const struct drm_display_mode topeet_rgb_043_mode = {
.clock = 9200,
.hdisplay = 480,
.hsync_start = 480 + 4,
.hsync_end = 480 + 4 + 41,
.htotal = 480 + 4 + 41 + 8,
.vdisplay = 272,
.vsync_start = 272 + 2,
.vsync_end = 272+ 2 + 10,
.vtotal = 272 +2 + 10 + 4,
.vrefresh = 60,
};
static const struct panel_desc topeet_rgb_043 = {
.modes = &topeet_rgb_043_mode,
.num_modes = 1,
.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
};
/*5寸RGB屏*/
static const struct drm_display_mode topeet_rgb_050_mode = {
.clock = 20000,
.hdisplay = 800,
.hsync_start = 800 + 80,
.hsync_end = 800 + 80 + 32,
.htotal = 800 + 80 + 32 + 48,
.vdisplay = 480,
.vsync_start = 480 + 14,
.vsync_end = 480+ 14 + 5,
.vtotal = 480 + 14 + 5 + 3,
.vrefresh = 60,
};
static const struct panel_desc topeet_rgb_050 = {
.modes = &topeet_rgb_050_mode,
.num_modes = 1,
.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
};
/*7寸RGB屏*/
static const struct drm_display_mode topeet_rgb_070_mode = {
.clock = 70000,
.hdisplay = 1024,
.hsync_start = 1024 + 80,
.hsync_end = 1024 + 80 + 20,
.htotal = 1024 + 80 + 20 + 48,
.vdisplay = 600,
.vsync_start = 600 + 14,
.vsync_end = 600 + 14 + 5,
.vtotal = 600 + 14 + 5 + 3,
.vrefresh = 60,
};
static const struct panel_desc topeet_rgb_070 = {
.modes = &topeet_rgb_070_mode,
.num_modes = 1,
.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
};
/*7寸LVDS屏*/
static const struct drm_display_mode topeet_lvds_070_mode = {
.clock = 53000,
.hdisplay = 800,
.hsync_start = 800 + 40,
.hsync_end = 800 + 40 + 60,
.htotal = 800 + 40 + 60 + 220,
.vdisplay = 1280,
.vsync_start = 1280 + 7,
.vsync_end = 1280 + 7+10 ,
.vtotal = 1280 + 7 + 10 + 21,
.vrefresh = 60,
};
static const struct panel_desc topeet_lvds_070 = {
.modes = &topeet_lvds_070_mode,
.num_modes = 1,
.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
};
/*9.7寸LVDS屏*/
static const struct drm_display_mode topeet_lvds_097_mode = {
.clock = 70000,
.hdisplay = 1024,
.hsync_start = 1024 + 80,
.hsync_end = 1024 + 80 + 32,
.htotal = 1024 + 80 + 32 + 48,
.vdisplay = 768,
.vsync_start = 768 + 14,
.vsync_end = 768 + 14 + 5,
.vtotal = 768 + 14 + 5 + 3,
.vrefresh = 60,
};
static const struct panel_desc topeet_lvds_097 = {
.modes = &topeet_lvds_097_mode,
.num_modes = 1,
.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
};
/*10.1寸LVDS屏*/
static const struct drm_display_mode topeet_lvds_101_mode = {
.clock = 51200,
.hdisplay = 1024,
.hsync_start = 1024 + 80,
.hsync_end = 1024 + 80 + 32,
.htotal = 1024 + 80 + 32 + 48,
.vdisplay = 600,
.vsync_start = 600 + 14,
.vsync_end = 600 + 14 + 3,
.vtotal = 600 + 14 + 5 + 3,
.vrefresh = 60,
};
static const struct panel_desc topeet_lvds_101 = {
.modes = &topeet_lvds_101_mode,
.num_modes = 1,
.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
};
添加完成之后,保存退出。为了方便起见我们将修改好的panel-simple.c 文件存放在了“iTOP-STM32MP157开发板网盘资料汇总\07_系统移植\03_移植过程中用到的文件\03_panel-simple.c文件”路径下,如下图所示:
回到源码目录下。然后使用命令“./create.sh”对内核进行编译,编译成功如下图所示:
将我们新生成的设备树和内核,根据之前章节的内容生成镜像文件并烧写,这里要注意的是,内核设备树镜像中应该会有七个设备树文件和一个内核文件,如下图所示:
每一个设备树分别对应一种屏幕,烧写完成之后,重新启动开发板,在uboot命令行之中屏幕设置环境变量为“7.0”之后,重启开发版,进入系统之后终端打印如下图所示:
在内核的加载工程之中我们会看到对应的小企鹅logo,然后我们可以运行一个QT程序来进行屏幕的显示,在这里我们使用日历程序来进行检测(在这里我们使用buildroot系统进行测试,当然大家也可以使用QT系统来测试)。将iTOP-STM32MP157开发板网盘资料汇总\03_文件系统源码和镜像\02_buildroot文件系统\06_QT测试路径下的压缩包解压,将可执行文件QCalender拷贝到虚拟机上,使用“./QCalender”来运行,如下下图所示:
七寸LVDS显示如下,这就证明我们对于屏幕的适配成功了。
69.3.9 LCD触摸适配
在上一小节之中我们已经将每个LCD的显示进行了适配,但可以发现我们的屏幕并不能进行触摸,在本章节之中我们将会对触摸进行适配,
首先我们先了解每个LCD的触摸芯片的型号,见如下表:
LCD类型 | 触摸芯片型号 |
4.3寸金属框 | Tsc2007(内核中已提供) |
5寸金属框 | ft5x06 |
1024x600金属框 | ft5x06 |
7寸塑胶壳 | ft5x06 |
9.7寸塑胶壳 | ft5x06 |
10.1寸金属框 | gt911 |
我们以RGB接口为例进行引脚的讲解(LVDS的触摸引脚和RGB触摸引脚相同)。根据原理图可知我们的触摸芯片是通过i2c协议来进行通信的,且QSPI_BK2_IO2和QSPI_BK2_IO3分别作为触摸复位引脚和触摸初始化引脚,如下图所示:
每个引脚所对应的CPU的IO口如下
引脚名称 | 对应的CPU IO口 |
QSPI_BK2_IO2 | PG10 |
QSPI_BK2_IO3 | PG7 |
下面我们在设备树之中进行适配,回到内核源码目录下,如下图所示:
在上面我们已经列出了每个屏幕用到的触摸芯片,所以我们分别在每个屏幕对应的设备树中添加对应的内容,每个触摸芯片对应的内容如下:
tsc2007:
&i2c2{
tsc2007: tsc2007@48 {
compatible = "ti,tsc2007";
reg = <0x48>;
interrupt-parent = <&gpiog>;
interrupts = <7 IRQ_TYPE_EDGE_FALLING>;
interrupt-controller;
gpios = <&gpiog 7 GPIO_ACTIVE_LOW>;
ti,x-plate-ohms = <660>;
wakeup-source;
};
};
ft5x06:
&i2c2{
ft5x06: ft5x06@38 {
compatible = "edt,edt-ft5406";
reg = <0x38>;
interrupts = <7 IRQ_TYPE_EDGE_FALLING>;
interrupt-parent = <&gpiog>;
interrupt-controller;
irq-gpios = <&gpiog 7 (GPIO_ACTIVE_HIGH | GPIO_PULL_UP)>;
reset-gpios = <&gpiog 10 GPIO_ACTIVE_LOW>;
status = "okay";
};
};
gt911:
&i2c2{
gt911: gt911_ts@5d{
compatible = "goodix,gt911";
reg = <0x5d>;
irq-gpios = <&gpiog 7 (GPIO_ACTIVE_HIGH | GPIO_PULL_UP)>;
reset-gpios = <&gpiog 10 GPIO_ACTIVE_HIGH>;
interrupt-parent = <&gpiog>;
interrupts = <7 IRQ_TYPE_EDGE_FALLING>;
status = "okay";
};
};
我们在这里以7寸lvds添加为例子,首先使用以下命令进入对应的设备树文件
vim arch/arm/boot/dts/stm32mp157a-itop-lvds-070.dts
然后我们来到完成如下图所示
来到文件底部,添加对应的触摸芯片内容,添加完成,如下图所示:
保存退出,以上内容为添加触摸芯片对应的节点,并设置一些列引脚功能和中断设置。保存退出,回到内核源码目录下,如下图所示:
使用以下命令,进入触摸屏驱动存放目录,如下图所示:
cd drivers/input/touchscreen/
由于官方默认提供的触摸驱动会存在不匹配的情况,我们将我们适配好的驱动拷贝到该目录下,驱动存放路径为“iTOP-STM32MP157开发板网盘资料汇总\07_系统移植\03_移植过程中用到的文件\04_触摸驱动”。如下图所示:
拷贝完成之后,在touchscreen目录下使用命令“vim Kconfig”,修改Kconfig文件,打开Kconfig文件之后如下图所示:
来到文件的最下端(注意要在endif上方,否则会添加到上一级目录下),添加以下内容
config TOUCHSCREEN_FT5X0X_SELECT
tristate "select FT5X0X based touchscreens"
depends on I2C
help
Say Y here if you have a FT5X0X based touchscreen
controller.
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called FT5X0X.
choice
depends on TOUCHSCREEN_FT5X0X_SELECT
prompt "select lcd type"
default RGB_7_0_1024x600
config LVDS_7_0_800x1280
bool "lvds 7.0"
config LVDS_9_7_1024x768
bool "lvds 9.7"
config RGB_7_0_1024x600
bool "RGB 7.0"
config RGB_5_0_800x480
bool "RGB 5.0"
endchoice
添加完成如下图所示:
保存退出。在touchscreen目录继续下使用命令“vim Makefile”,修改Makefile文件,打开Makefile文件之后如下图所示:
来到文件最下端,添加以下内容,使编译的工程中编译我们新添加的驱动文件,添加完成如下图所示:
obj-$(CONFIG_TOUCHSCREEN_FT5X0X_SELECT) += ft5x06_ts.o
保存退出,然后回到我们的内核源码目录下,使用以下命令进行菜单界面,打开之后如下图所示:
export ARCH=arm
export CROSS_COMPILE=arm-none-linux-gnueabihf-
make stm32_itop_mp1a_defconfig
make menuconfig
然后我们进入以下路径
> Device Drivers
> Input device support
> Touchscreens
进入 Touchscreens目录之后如下图所示:
我们取消勾选以下内容:
< > EDT FocalTech FT5x06 I2C Touchscreen support
取消勾选之后选中以下选项
<*> TSC2007 based touchscreens
<*> GTP911 based touchscreens
<*> select FT5X0X based touchscreens
完成上述步骤之后,将配置保存到.config文件里,如下图所示:
退出,回到源码目录下。使用以下命令将.config 复制成我们的默认配置文件,如下图所示:
cp .config arch/arm/configs/stm32_itop_mp1a_defconfig
然后使用命令“./create.sh”对内核进行编译,编译成功如下图所示:
我们这里以七寸LVDS屏幕来进行触摸测试(如果使用的是4.3寸或者10.1寸屏幕需要先加载对应的KO文件)。将我们新生成的设备树和内核,根据之前章节的内容生成镜像文件并烧写,烧写完成之后,重新启动开发板,在uboot命令行之中设置环境变量为“7.0”之后,重启开发版,进入系统之后终端打印如下图所示:
然后我们使用命令“ts_calibrate”,来进行屏幕的触摸测试。或者我们可以使用上一章节之中的LCD显示测试的QT历程来测试,在这里就不对测试进行展示了。
至此我们的触摸就适配完成了。
69.3.10音频适配
在上一个章节之中我们对LCD屏幕的触摸进行了适配,在本小节之中我们将对音频进行适配。
首先我们使用的音频芯片为WM8960,底板对应的原理图如下图所示:
下面我们将会对该芯片的引脚做一个解释:
下面我们来对部分引脚进行功能分析。
- 我们模拟信号的输入引脚为MICL和MICR,分贝代表麦克风的左声道输入和右声道输入。MICL接入了WM8960芯片的LINPUT1和LINPUT2 ,MICR接入了WM8960芯片的RINPUT1和RINPUT2,作为外部的输入。
- 我们模拟信号的输出接口分为扬声器输出接口和耳机输出接口,扬声器对应的引脚有四个,分别为SPKOUTLN、SPKOUTLP、SPKOUTRN、SPKOUTRP,分别代表左通道的正负接入引脚和右通道的正负接入引脚。而耳机对应的引脚为HPOUT_FL和HPOUT_FR,代表耳机的左通道和右通道输出。
- 而SAI2_MCLKA 、SAI2_SCKA 、SAI2_FSA、SAI2_SDA 、PF3、SAI2_SDB 六个引脚分别连接到了WM8960芯片的MCLK、BCLK、DACLRC、 DATDAT、 ADCLRC/GPIO1 、ADCDAT。每个引脚的定义如下
MCLK:主时钟,
BCLK:位时钟,用于同步。
DACLRC:DAC 数据对齐时钟,功能和 ADCLRC 一样,都是帧时钟(LRCK),用于切换左右声道数据,此信号的频率等于采样率。
DACDAT:DAC 数据输入引脚,主控器通过此引脚将数字信号输入给 WM8960 的 DAC。
ADCLRC:ADC 数据对齐时钟,也就是帧时钟(LRCK),用于切换左右声道数据,此信号的频率就是采样率。此引脚可以配置为 GPIO 功能,配置为 GPIO 以后 ADC 就会使用 DACLRC引脚作为帧时钟。
ADCDAT:ADC 数据输出引脚,采集到的音频数据转换为数字信号以后通过此引脚传输给主控制器。
- 我们通过i2c2来对WM8960进行功能的配置。
然后我们将进行该芯片的适配,首先回到内核源码目录下,如下图所示:
然后使用以下命令
vim arch/arm/boot/dts/stm32mp15xx-itop.dtsi
对stm32mp15xx-itop.dtsi文件进行修改,打开stm32mp15xx-itop.dtsi文件之后如下图所示:
然后来到文件最下端,添加&sai2节点相关的内容,添加内容如下:
&sai2 {
clocks = <&rcc SAI2>, <&rcc PLL3_Q>, <&rcc PLL3_R>;
clock-names = "pclk", "x8k", "x11k";
pinctrl-names = "default", "sleep";
pinctrl-0 = <&sai2a_pins_a>, <&sai2b_pins_b>;
pinctrl-1 = <&sai2a_sleep_pins_a>, <&sai2b_sleep_pins_b>;
status = "okay";
sai2a: audio-controller@4400b004 {
#clock-cells = <0>;
dma-names = "tx";
clocks = <&rcc SAI2_K>;
clock-names = "sai_ck";
status = "okay";
sai2a_port: port {
sai2a_endpoint: endpoint {
remote-endpoint = <&wm8960_tx_endpoint>;
format = "i2s";
mclk-fs = <256>;
dai-tdm-slot-num = <2>;
dai-tdm-slot-width = <16>;
};
};
};
sai2b: audio-controller@4400b024 {
dma-names = "rx";
st,sync = <&sai2a 2>;
clocks = <&rcc SAI2_K>, <&sai2a>;
clock-names = "sai_ck", "MCLK";
status = "okay";
sai2b_port: port {
sai2b_endpoint: endpoint {
remote-endpoint = <&wm8960_rx_endpoint>;
format = "i2s";
mclk-fs = <256>;
dai-tdm-slot-num = <2>;
dai-tdm-slot-width = <16>;
};
};
};
};
添加完成如下下图所示:
以上内容对sai2 节点做了四个方面描述:SAI2 接口引脚的 pinctrl设置、时钟配置、修改 status 为“okay”以及指定sai2a为发送端口,指定sai2b为接收端口。
然后我们添加关于i2c2对wm8960芯片设置相关的功能。使用查找命令来查找i2c2,查找完成如下图所示:
然后我们在该节点中添加以下内容:
codec: wm8960@1a {
compatible = "wlf,wm8960";
reg = <0x1a>;
#sound-dai-cells = <0x0>;
status = "okay";
hp-det = <3 0>;
clocks = <&sai2a>;
clock-names = "mclk";
wlf,shared-lrclk;
wlf,double-dai;
port {
#address-cells = <0x1>;
#size-cells = <0x0>;
wm8960_tx_endpoint: endpoint@0 {
8reg = <0x0>;
remote-endpoint = <&sai2a_endpoint>;
frame-master;
bitclock-master;
};
wm8960_rx_endpoint: endpoint@1 {
reg = <0x1>;
remote-endpoint = <&sai2b_endpoint>;
frame-master;
bitclock-master;
};
};
};
添加完成如下图所示:
wm8960_tx_endpoint端口指示wm8960芯片从 sai2a_endpoint 获取音频数据,用来播放音频。wm8960_rx_endpoint端口用来从外面获取的音频数据传输到 sai2b_endpoint,用来录音。然后我们在根节点的最下方添加 sound 节点,添加内容如下图所示:
sound {
compatible = "audio-graph-card";
label = "STM32MP1-iTOP";
dais = <&sai2a_port &sai2b_port>;
routing =
"LINPUT1", "MICB",
"RINPUT1", "MICB",
"RINPUT2", "MICB";
status = "okay";
};
添加完成如下图所示:
该节点的作用就是匹配相应的声卡驱动文件,然后通过dais设置音频信息的输入输出端口,通过routing对一系列的连接进行设置。
保存退出,回到源码目录下。我们使用以下命令进入配置菜单,配置菜单打开之后如下图所示:
export ARCH=arm
export CROSS_COMPILE=arm-none-linux-gnueabihf-
make stm32_itop_mp1a_defconfig
make menuconfig
由于官方配置文件没有对WM8960进行勾选,所以需要我们自己进行手动勾选,路径如下,对 Wolfson Microelectronics WM8960 CODEC进行勾选:
-> Device Drivers
-> Sound card support (SOUND [=y])
-> Advanced Linux Sound Architecture (SND [=y])
-> ALSA for SoC audio support (SND_SOC [=y])
-> CODEC drivers
<*> Wolfson Microelectronics WM8960 CODEC
将配置保存到.config文件里,如下图所示:
退出,回到源码目录下。使用以下命令将.config 复制成我们的默认配置文件,如下图所示:
cp .config arch/arm/configs/stm32_itop_mp1a_defconfig
由于我们的一些配置相较于官方提供的wm8960.c源码不同,所以我们对sound/soc/codecs目录下的wm8960.c文件进行替换,我们提供的wm8960.c文件存放路径为“iTOP-STM32MP157开发板网盘资料汇总\07_系统移植\03_移植过程中用到的文件\05_wm8960.c替换文件”。
替换完成之后,使用命令“./create.sh”对内核进行编译,编译成功如下图所示:
将我们新生成的设备树和内核,根据之前章节的内容生成镜像文件并烧写,烧写完成之后,重新启动开发板,启动之后如下图所示:
输入命令“ls /dev/snd”,来查看声卡设备,如下图所示:
下面是对于这四个设备的解释:
controlC0:用于声卡控制,C0 表示声卡 0。
pcmC0D0p:用于播放的 pcm 设备,最后面的“p”是 playback 的缩写,表示放音。
pcmC0D1c:用于录音的 pcm 设备,最后面的“c”是 capture 的缩写,表示录音。
timer:定时器。
至此我们的声卡就适配成功了,大家可以根据“3.11.2 耳机播放测试”小节进行测试。
69.3.11 ov5640摄像头适配
本小节我们将对ov5640摄像头进行适配。摄像头用到的接口为DCMI(Digital Camera Interface)接口,DCMI 是一个同步并行接口,能够从外部 8bit、10bit、12bit 或 14bit 的 CMOS 摄像头接收高速数据流,支持不同的数据格式:YCbCr4:2:2/RGB565渐进式视频和压缩数据(JPEG)。
从原理图可以知道我们外接的为 8bit COM Camera。下面我们将对该摄像头进行适配:
回到内核源码目录下,如下图所示:
然后使用以下命令对stm32mp15xx-itop.dtsi文件进行内容的添加
vim arch/arm/boot/dts/stm32mp15xx-itop.dtsi
首先添加dcmi对应的节点内容,添加内容如下图所示:
&dcmi {
status = "okay";
pinctrl-names = "default", "sleep";
pinctrl-0 = <&dcmi_pins_a>;
pinctrl-1 = <&dcmi_sleep_pins_a>;
port {
dcmi_0: endpoint {
remote-endpoint = <&ov5640_0>;
bus-width = <8>;
hsync-active = <0>;
vsync-active = <0>;
pclk-sample = <1>;
pclk-max-frequency = <77000000>;
};
};
};
添加完成如下图所示:
添加内容用来接收摄像头所传递的信号,然后我们根据原理图可以得知ov5640的功能设置使用的为i2c5,我们使用查找命令对i2c5进行查找,查找完成如下图所示:
在该节点之中添加以下内容。
ov5640: camera@3c {
compatible = "ovti,ov5640";
reg = <0x3c>;
clocks = <&clk_ext_camera>;
clock-names = "xclk";
//DOVDD-supply = <&v2v8>;
powerdown-gpios = <&gpioa 13 (GPIO_ACTIVE_HIGH | GPIO_PUSH_PULL)>;
reset-gpios = <&gpioa 3 (GPIO_ACTIVE_LOW | GPIO_PUSH_PULL)>;
rotation = <180>;
status = "okay";
port {
ov5640_0: endpoint {
remote-endpoint = <&dcmi_0>;
bus-width = <8>;
data-shift = <2>; /* lines 9:2 are used */
hsync-active = <0>;
vsync-active = <0>;
pclk-sample = <1>;
pclk-max-frequency = <77000000>;
};
};
};
添加完成如下图所示:
该内容为对ov4540进行了功能的设置,通过port 节点来接收 DCMI 接口的数据。这里需要注意的是始终的设置,引用了节点&clk_ext_camera,而我们并没有设置该节点,所以我们需要在根节点中添加以下内容
clocks {
clk_ext_camera: clk-ext-camera {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <24000000>;
};
};
添加完成如下图所示:
保存退出之后,回到我们的内核源码目录下,使用以下命令进行菜单界面,打开之后如下图所示:
export ARCH=arm
export CROSS_COMPILE=arm-none-linux-gnueabihf-
make stm32_itop_mp1a_defconfig
make menuconfig
然后我们进入以下路径,对下列内容进行勾选
> Device Drivers
<*> Multimedia support --->
I2C Encoders, decoders, sensors and other helper chips --->
<M> OmniVision OV5640 sensor support
然后我们进入以下路径,对下列内容进行勾选,勾选完成如下图所示:
> Device Drivers
<*> Multimedia support --->
[*] V4L platform devices --->
<*> STM32 Digital Camera Memory Interface (DCMI) support
将配置保存到.config文件里,如下图所示:
退出,回到源码目录下。使用以下命令将.config 复制成我们的默认配置文件,如下图所示:
cp .config arch/arm/configs/stm32_itop_mp1a_defconfig
使用命令“./create.sh”对内核进行编译,编译成功如下图所示:
退出,回到源码目录下。使用以下命令将.config 复制成我们的默认配置文件,如下图所示:
cp .config arch/arm/configs/stm32_itop_mp1a_defconfig
使用命令“./create.sh”对内核进行编译,编译成功如下图所示:
将我们新生成的设备树和内核,根据之前章节的内容生成镜像文件并烧写,烧写完成之后,安装好摄像头,要注意摄像头的朝向是开发板,然后对开发板进行上电
然后我们使用以下命令来查看摄像头支持的格式
v4l2-ctl --device=/dev/video0 --list-formats-ext
至此,我们的摄像头模块就适配成功了。
69.3.12 蓝牙WIFI适配
我们使用的芯片为蓝牙WIFI芯片二合一的芯片,具体型号为RTL8723BU。原理图如下图所示:
69.3.12.1 WIFI适配
我们首先对WIFI进行适配,回到内核源码目录下,如下图所示:
然后使用以下命令进入wireless目录下
cd drivers/net/wireless/
将“iTOP-STM32MP157开发板网盘资料汇总\07_系统移植\03_移植过程中用到的文件\06_蓝牙WIFI文件”路径下的rtl8723bu.tar.bz2文件拷贝到该目录下,使用命令
tar -vxf rtl8723bu.tar.bz2
对 rtl8723bu.tar.bz2文件进行解压,解压完成如下图所示:
在该目录下使用命令“vim Kconfig”,对 Kconfig文件进行内容的添加,在文件最下方(endif 上方)添加以下内容:
source "drivers/net/wireless/rtl8723bu/Kconfig"
保存退出之后,在该目录下使用命令“vim Makefile”对Makefile文件进行内容的添加,添加内容如下图所受:
obj-$(CONFIG_RTL8723BU) += rtl8723bu/
保存退出之后,回到我们的内核源码目录下,使用以下命令进行菜单界面,打开之后如下图所示:
export ARCH=arm
export CROSS_COMPILE=arm-none-linux-gnueabihf-
make stm32_itop_mp1a_defconfig
make menuconfig
然后我们进入以下路径,对下列内容进行勾选
> Device Drivers
> Network device support
> Wireless LAN
<*> IEEE 802.11 for Host AP (Prism2/2.5/3 and WEP/TKIP/CCMP)
[*] Support downloading firmware images with Host AP driver
[*] Support for non-volatile firmware download
然后对以下路径下的内容进行勾选,这里要注意的是cfg80211 - wireless configuration API和 Realtek 8723B USB WiFi 是编译成模块的形式。
> Networking support
> Wireless
<M> cfg80211 - wireless configuration API
> Device Drivers
[*] Staging drivers --->
> Device Drivers
> Network device support
> Wireless LAN
<M> Realtek 8723B USB WiFi
将配置保存到.config文件里,如下图所示:
退出,回到源码目录下。使用以下命令将.config 复制成我们的默认配置文件,如下图所示:
cp .config arch/arm/configs/stm32_itop_mp1a_defconfig
使用命令“./create.sh”对内核进行编译,编译成功如下图所示:
编译完成之后,由于我们的cfg80211 和rtl8723设置成的为编译成模块,所以我们要执行命令“make modules”,来将模块进行编译,编译完成之后我们所需要的模块存放路径为net/wireless/cfg80211.ko和drivers/net/wireless/rtl8723bu/8723bu.ko。如下图所示:
将我们新生成的设备树和内核,根据之前章节的内容生成镜像文件并烧写,烧写完成之后,根据“3.16.1 WIFI测试”来对WIFI进行测试,需要注意的是这里要用的ko文件是我们刚刚编译出来的。
至此我们的wifi适配就结束了。
69.3.12.2 蓝牙适配
下面开始我们的蓝牙适配,首先回到我们的内核源码目录下,如下图所示:
然后使用以下命令进入bluetooth目录下。
cd drivers/bluetooth/
然后将“iTOP-STM32MP157开发板网盘资料汇总\07_系统移植\03_移植过程中用到的文件\06_蓝牙WIFI文件\蓝牙”目录下的rtk_btusb.c和rtk_btusb.h文件拷贝到当前目录下,拷贝完成如下图所示:
在该目录下使用命令“vim Kconfig”,对 Kconfig文件进行内容的添加,在文件最下方(endif 上方)添加以下内容:
vim Kconfig
config BT_RTKBTUSB
tristate "RTK HCI USB driver"
depends on USB
help
RTK Bluetooth HCI USB driver
保存退出之后,在该目录下使用命令“vim Makefile”对Makefile文件进行内容的添加,添加内容如下图所受:
obj-$(CONFIG_BT_RTKBTUSB) += rtk_btusb.o
保存退出之后,回到我们的内核源码目录下,使用以下命令进行菜单界面,
export ARCH=arm
export CROSS_COMPILE=arm-none-linux-gnueabihf-
make stm32_itop_mp1a_defconfig
make menuconfig
打开之后如下图所示:
来到以下路径,对下列选项进行勾选,如下所示:
[*] Networking support --->
<*> Bluetooth subsystem support --->
Bluetooth device drivers --->
<*> HCI USB driver
[*] Enable USB autosuspend for Bluetooth USB devices by default
<*> RTK HCI USB driver
将配置保存到.config文件里,如下图所示:
退出,回到源码目录下。使用以下命令将.config 复制成我们的默认配置文件,如下图所示:
cp .config arch/arm/configs/stm32_itop_mp1a_defconfig
使用命令“./create.sh”对内核进行编译,编译成功如下图所示:
将我们新生成的设备树和内核,根据之前章节的内容生成镜像文件并烧写,烧写完成之后,根据“3.16.2蓝牙测试”来对蓝牙进行测试,至此我们的蓝牙适配就结束了。
69.3.13 CAN总线适配
下面我们对CAN总线进行适配,CAN接口原理图如下图所示:
然后我们回到内核源码目录下,如下图所示:
然后使用以下命令对stm32mp15xx-itop.dtsi文件进行内容的添加
vim arch/arm/boot/dts/stm32mp15xx-itop.dtsi
来到文件最下方添加m_can1对应的节点内容,添加内容如下图所示:
&m_can1 {
pinctrl-names = "default", "sleep";
pinctrl-0 = <&m_can1_pins_a>;
pinctrl-1 = <&m_can1_sleep_pins_a>;
status = "okay";
};
添加完成如下图所示:
保存退出,回到源码目录下,然后使用命令
cd drivers/net/can/m_can/
进入m_can目录下,由于官方所提供的源码经过测试不能进行收发,所以我们要将“iTOP-STM32MP157开发板网盘资料汇总\07_系统移植\03_移植过程中用到的文件\07_can替换文件”目录下的m_can_platform.c文件对m_can目录下的m_can_platform.c文件进行替换。
然后我们回到源码目录下,由于官方在内核中已经为我们配置好了,默认编译进内核,所以我们不需要再进行适配,接下来我们使用命令“ ./create.sh”进行整体的编译,编译完成如下图所示:
将我们新生成的设备树和内核,根据之前章节的内容生成镜像文件并烧写,烧写完成之后,根据“3.13 CAN接口测试”小节进行测试。
至此我们的can适配就完成了。
69.3.14 ap3216c光环境传感器适配
下面我们对 ap3216c光环境传感器进行适配,原理图如下图所示:
回到内核源码目录下,如下图所示:
然后使用以下命令对stm32mp15xx-itop.dtsi文件进行内容的添加
vim arch/arm/boot/dts/stm32mp15xx-itop.dtsi
首先根据原理图可知,该传感器所使用的为i2c5。我们使用查找命令对i2c5节点进行查询,查找完成如下图所示,
在该节点之中,添加以下内容:
ap3216c@1e {
compatible = "LiteOn,ap3216c";
reg = <0x1e>;
};
添加完成如下图所示:
保存,退出,回到内核源码目录下,然后将我们提供的“iTOP-STM32MP157开发板网盘资料汇总\07_系统移植\03_移植过程中用到的文件\08_ap3216c光环境传感器源码”路径下的ap3216c.c文件和ap3216c.h文件分别拷贝到drivers/char目录下和include/linux目录下,拷贝完成如下图所示:
然后在drivers/char目录下使用命令“vim Kconfig”,对 Kconfig文件进行内容的添加,使用查找命令查找“config PPDEV”查找完成如下图所示:
在该内容上方,添加以下内容:
config LITEON_AP3216C
tristate "Lite-On Three-in-one Environmental Sensor ap3216c Support"
help
To compile this driver as a module, choose M here: the module
will be called ap3216c.
If unsure, it is safe to say Y.
添加完成如下
保存退出之后,在该目录下使用命令“vim Makefile”对Makefile文件进行内容的添加,添加内容如下图所受:
obj-$(CONFIG_LITEON_AP3216C) += ap3216c.o
保存退出之后,回到我们的内核源码目录下,使用以下命令进行菜单界面,
export ARCH=arm
export CROSS_COMPILE=arm-none-linux-gnueabihf-
make stm32_itop_mp1a_defconfig
make menuconfig
打开之后如下图所示:
在以下路径,对Lite-On Three-in-one Environmental Sensor ap3216c Suppor选中,编译进内核:
> Device Drivers
> Character devices
<*> Lite-On Three-in-one Environmental Sensor ap3216c Suppor
将配置保存到.config文件里,如下图所示:
退出,回到源码目录下。使用以下命令将.config 复制成我们的默认配置文件,如下图所示:
cp .config arch/arm/configs/stm32_itop_mp1a_defconfig
使用命令“./create.sh”对内核进行编译,编译成功如下图所示:
将我们新生成的设备树和内核,根据之前章节的内容生成镜像文件并烧写,烧写完成之后,根据“3.17 AP3216C测试”小节进行测试。
至此我们的光环境传感器的适配就完成了。
69.3.15 ADC适配
下面我们对ADC进行适配,我们总共引出了两路ADC,一路被用做了ADC电位器,一路被GPIO所引出,原理图如下图所示:
我们回到内核源码目录下,如下图所示:
然后使用以下命令对stm32mp15xx-itop.dtsi文件进行内容的添加
vim arch/arm/boot/dts/stm32mp15xx-itop.dtsi
来到文件底部,添加首先添加&adc 对应的节点内容,添加内容如下图所示:
&adc {
pinctrl-names = "default";
vdd-supply = <&vdd>;
vdda-supply = <&vdda>;
vref-supply = <&vdda>;
status = "okay";
adc1: adc@0 {
st,adc-channels = <0 1>;
/* 16.5 ck_cycles sampling time */
st,min-sample-time-nsecs = <400>;
status = "okay";
};
};
添加完成如下图所示
保存退出,回到我们的内核源码目录下。因为官方默认已经为我们配置好驱动,所以我们不需要进行任何修改。使用命令“./create.sh”对内核进行编译,编译成功如下图所示:
将我们新生成的设备树和内核,根据之前章节的内容生成镜像文件并烧写,烧写完成之后,根据“3.3 ADC电位器测试”小节进行测试。
至此我们的ADC的适配就完成了。
经过我们的以上步骤,我们的对于内核的移植就结束了,这其中很多知识点知识涉及到了步骤,并没有对这些进行详细的说明和解释,在我们有关驱动的章节,会进行详细的描述。