位置: IT常识 - 正文

基于imx8m plus开发板全体系开发教程4:Linux系统开发(imx6ul开源项目)

编辑:rootadmin
基于imx8m plus开发板全体系开发教程4:Linux系统开发

推荐整理分享基于imx8m plus开发板全体系开发教程4:Linux系统开发(imx6ul开源项目),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:imx8mmini,imx8mmini,imx8mmini,imx8x,imx8x,imx8 安卓,imx8开发板,imx8 gpu,内容如对您有帮助,希望把文章链接给更多的朋友!

前言:

i.MX8M Plus 开发板是一款拥有 4 个 Cortex-A53 核心,运行频率 1.8GHz;1 个 Cortex-M7 核心,运行频率 800MHz;此外还集成了一个 2.3 TOPS 的 NPU,大大加速机器学习推理。

全文所使用的开发平台均为与NXP官方合作的FS-IMX8MPCA开发板(华清远见imx8mp开发板),支持Weston、ubuntu20.04、Android11 等操作系统;同时支持 Xenomai 硬实时内核、EtherCAT 总线、TSN 时间敏感网络、ROS1.0、ROS2.0 等工业与机器人领域应用;可以用于工业互联网、人工智能、边缘计算、多屏异显等应用方向。华清远见研发中心编写了大量开发教程并录制了丰富视频教学资源免费提供给大家!

开发板更多资料可在下方评论区留言领取~~~

Linux 系统开发TF-A 编译配置交叉编译工具链

在进行源码编译之前需要先导入交叉编译工具链,之前章节已经介绍过具体安装过程,这里不再赘述,如果还没有安装可参考《交叉编译工具链安装》章节

linux@ubuntu:$ source /opt/fsl-imx-xwayland/5.4-zeus/environment-setup-aarch64-poky-lin

ux

linux@ubuntu:$ $CC --version

编译 TF-A 编译

将当前工作目录切换到 TF-A 源码目录下,这里为“~/workdir/imx8mp/imx-yocto-bsp/bsp

_source/imx-atf”

linux@ubuntu:$ cd ~/workdir/imx8mp/imx-yocto-bsp/bsp_source/imx-atf

编译之前可以先清除之前的缓存

linux@ubuntu:$ make clean PLAT=imx8mp

编译

linux@ubuntu:$ LDFLAGS="" make PLAT=imx8mp

编译成功后在 build/imx8mp/release/目录下生成相关镜像

Bootloader 的编译与运行配置交叉编译工具链

在进行源码编译之前需要先导入交叉编译工具链,之前章节已经介绍过具体安装过程,

这里不再赘述,如果还没有安装可参考《交叉编译工具链安装》章节

linux@ubuntu:$ source /opt/fsl-imx-xwayland/5.4-zeus/environment-setup-aarch64-poky-lin

ux

linux@ubuntu:$ $CC --version

Bootloader编译

将当前工作目录切换到 bootloader 源码目录下,这里为“~/workdir/imx8mp/bsp_source/u

boot-imx”

linux@ubuntu:$ cd ~/workdir/imx8mp/bsp_source/uboot-imx

⚫ 配置

linux@ubuntu:$ make imx8mp_ai_robot_defconfig

⚫ 编译

linux@ubuntu:$ make

编译成功后在 spl 目录下生成 u-boot-spl.bin 相关镜像,在根目录下生成 u-boot.img 镜像

制作 imx-boot

在我们前面编译的标准 u-boot 无法自动启动设备,imx8mp 需要通过 imx-mkimage 构建

imx-boot。

imx-boot 镜像包括 U-boot、tf-a、uboot spl 和 ddr 固件。因此我们需要将前面编译的 uboot-imx 镜像和 imx-atf 以及 ddr 固件复制到 imx-boot/iMX8M 目录下来制作 imx-boot 镜像。

linux@ubuntu:$ cd ~/workdir/imx8mp/bsp_source/imx-boot

⚫ 复制 u-boot 镜像

linux@ubuntu:$ cp ../u-boot-imx/u-boot-nodtb.bin iMX8M/

linux@ubuntu:$ cp ../u-boot-imx/spl/u-boot-spl.bin iMX8M/

linux@ubuntu:$ cp ../u-boot-imx/tools/mkimage iMX8M/mkimage_uboot

linux@ubuntu:$ cp ../u-boot-imx/arch/arm/dts/imx8mp-ai-robot.dtb iMX8M/

⚫ 复制 DDR 固件

linux@ubuntu:$ cp ../firmware-imx-8.10/firmware/ddr/synopsys/ddr4_*_202006* iMX8M/

⚫ 复制 tf-a 镜像

linux@ubuntu:$ cp ../imx-atf/build/imx8mp/release/bl31.bin iMX8M/

⚫ 编译生成 imx-boot

linux@ubuntu:$ make SOC=iMX8MP flash_ai_robot

编译成功之后会在 iMX8M 目录下生成 flash.bin 文件,使用此文件通过 uuu 工具烧录到

开发板便可使用自己编译的 u-boot 和 tf-a 镜像启动开发板。

dos@windows:$ uuu -b emmc flash.bin

u-boot 常用命令介绍linux 系统环境变量

linux 下的环境变量可以通过在 u-boot 阶段进行设置,在 u-boot 启动过程中倒计时结束之

前按任意键停在 u-boot 进行环境变量设置。

如果要查看当前环境变量可以使用“print”命令

如果想要恢复至出厂时的环境变量可以使用“env default -f -a”命令进行重置

如果想要保存当前的环境变量可以使用“saveenv”进行保存。

a) 设置 linux 内核加载地址

⚫ linux 内核加载地址

u-boot=> setenv loadaddr 0x80080000

⚫ 设备树加载地址

u-boot=> setenv fdtaddr 0x80f00000

⚫ 设备树文件名称

u-boot=> setenv fdt_file imx8mp-evk.dtb

b) 显示设置

⚫ HDMI 显示

u-boot=> setenv fdt_file imx8mp-ai-robot.dtb

⚫ LVDS 和 HDMI 双屏显示

u-boot=> setenv fdt_file imx8mp-ai-robot-lvds070.dtb

⚫ MIPI 和 HDMI 双屏显示

u-boot=> setenv fdt_file imx8mp-ai-robot-mipi070.dtb

⚫ LVDS、MIPI 和 HDMI 三屏显示

u-boot=> imx8mp-ai-robot-mipi-lvds-dual.dts

c) 设置跟文件系统位置

⚫ 通过 NFS 挂载

u-boot=> setenv rootfsinfo 'root=/dev/nfs ip=dhcp nfsroot=${serverip}:${nfsroot},v3,tcp'

u-boot=> setenv rootfsinfo 'root=/dev/nfs ip=dhcp weim-nor nfsroot=${serverip}:${nfsroo

t},v3,tcp'

⚫ 通过 eMMC 挂载

u-boot=> setenv rootfsinfo 'root=/dev/mmcblk0p2 rootwait rw' /* eMMC */

常用命令介绍

uboot 命令类似于 linux 行缓冲命令行,当我们向终端命令行输入命令的时候,这些命令没有立即被系统识别,而是被缓冲到一个缓存区(也就是系统认为我换没有输入完),当我们按下回车键(换行)后,系统就认为输入完了,然后将缓冲区中所有刚才输入的命令拿去处理。

⚫ 第一个命令:printenv/print

printenv 命令不用带参数,作用是打印出系统中所有的环境变量。

printenv 环境变量名 查看指定的环境变量值。

⚫ 常用环境变量

bootdelay uboot 启动后,倒计时多少秒后自动执行环境变量 bootcmd 的语句

bootcmd 倒计时到 0 后,自动执行里面的语句

bootargs 是用于提供给内核的启动参数语句

ipaddr 当前开发板的 IP 地址

⚫ 设置添加/更改环境变量:setenv/set

用法:set name value

例如:set bootdelay 3

⚫ 保存环境变量的更改:saveenv/save

saveenv/save 作用是将内存中的环境变量的值同步保存到 flash 中环境变量的分区

⚫ 网络测试指令:ping

用法:ping IP 地址

此命令需要先设置好开发板的网络环境,包括网关,子网掩码,本机 IP 地址。之后才可以使用该命令。

⚫ tftp 下载命令

用于通过 tftp 协议从 tftp 服务器上下载文件。

eg: tftp 0x40480000 Image 将 tftp 服务器上的 Image 文件下载到本地内存的

0x40480000 地址处。

⚫ 内存操作指令:mw、md

md 内存地址 用于查看内存地址上的值

md.b 0x40008000 100 从内存地址 0x40008000 开始,查看 0x100 个字节并输出值

md.w 0x40008000 100 从内存地址 0x40008000 开始,查看 0x100 个 16 位值并输出值

md.l 0x40008000 100 从内存地址 0x40008000 开始,查看 0x100 个 32 位值并输出值

mw 用于修改内存地址上的值

mw.b x40008000 0xab 100 从内存地址 0x40008000 开始的 0x100 字节空间,设值为 0xab

mw.w 0x40008000 0xabcd 100 从内存地址 0x40008000 开始的 0x200 字节空间,每 16 位值设为 0xabcd

mw.l 0x40008000 0xabcdef88 100 从内存地址 0x40008000 开始的 0x400字节空间,每 32 位值设为 0xabcdef88

⚫ 帮助指令

上面介绍的指令为 bootloader 中常用的一些指令介绍,如果需要使用其它指令,可以通

过 help 命令来查看具体描述及用法。

Linux 内核源码编译配置交叉编译工具链

在进行源码编译之前需要先导入交叉编译工具链,之前章节已经介绍过具体安装过程,

这里不再赘述,如果还没有安装可参考《交叉编译工具链安装》章节

linux@ubuntu:$ source /opt/fsl-imx-xwayland/5.4-zeus/environment-setup-aarch64-poky-lin

ux

linux@ubuntu:$ $CC --version

linux 编译

将当前工作目录切换到 linux 内核源码目录下,这里为“~/workdir/imx8mp/imx-yocto-bsp

/bsp_source/ linux-imx” 

linux@ubuntu:$ cd ~/workdir/imx8mp/imx-yocto-bsp/bsp_source/linux-imx

添加 nxp 官方标准 imx_v8 配置

linux@ubuntu:$ make imx_v8_defconfig

添加自定义配置

linux@ubuntu:$ ./scripts/kconfig/merge_config.sh -m .config arch/arm64/configs/aicar.config

添加完配置后我们可以通过 menuconfig 进行内核配置和裁剪

linux@ubuntu:$ make menuconfig

编译内核文件

linux@ubuntu:$ make

编译成功后在 arch/arm64/boot/目录下生成 Image 内核镜像,在arch/arm64/boot/dts/freescale/目录下生成设备树镜像

单独编译设备树文件

linux@ubuntu:$ make dtbs

编译成功后在 arch/arm64/boot/dts/freescale/目录下生成设备树镜像

通过 tftp 服务器更新内核镜像

上面章节已经将 VMware 的桥接网络配置好了,本小节将要通过 TFTP 服务器来下载内核及设备树,我们分为两部分介绍,第一部分通过网线直连电脑的方式进行验收,第二部分通过路由器连接开发板和电脑。

网线直连电脑

这里我们采用的是直连网络,即开发板通过网线直连电脑。

首先启动 Ubuntu 虚拟机,由于我们采用直连方式,所以虚拟机无法自动获取到 IP 地址,

需要我们手动设置 IP 地址。

打开虚拟机的“/etc/network/interfaces”文件

linux@ubuntu:$ sudo vi /etc/network/interfaces

添加如下配置

auto ens33

iface ens33 inet static

address 192.168.100.240

netmask 255.255.255.0

gateway 192.168.100.1

dns-nameserver 192.168.100.1

这里“ens33”代表网卡名,可以通过 ifconfig 命令查看;

address 为要设置的静态 IP 地址;

netmask 为子网掩码

gateway 为网关地址

设置完成后重启网络服务

linux@ubuntu:$ sudo reboot

设置完成后使用 ifconfig 命令查看当前 ubuntu 的 IP 地址。

在开发板上电之前需将调试串口、网线接入网口 1。

之后给开发板上电,使程序停留在 u-boot 终端。

这里我们需要设置几个与网络相关的环境变量,以支持网络传输。

在上面我们已经设置好了 ubuntu 的网络配置,这里 ubuntu 充当 TFTP 服务器。我们需要

按照 ubuntu 的配置来设置开发板的环境变量。

设置服务器 IP 地址(ubuntu ip)

u-boot=> setenv serverip 192.168.100.240

设置本机 IP 地址(开发板 ip)

u-boot=> setenv ipaddr 192.168.100.241

设置网关

u-boot=> setenv gatewayip 192.168.100.1

设置子网掩码

u-boot=> setenv netmask 255.255.255.0

设置网卡 1 MAC 地址

u-boot=> setenv eth1addr 00:04:9f:07:0b:a5

保存环境变量

u-boot=> saveenv

设置完成后可以使用 ping 命令来进行测试

u-boot=> ping 192.168.100.240

下载 linux 内核与设备树

在进行内核下载之前,首先需要确认 TFTP 服务器是否已经安装完毕,如果还没有安装则需要根据前面章节的内容进行安装,此外还需要将之前编译好的 Image linux 内核程序与设备树文件 imx8mp-ai-robot.dtb 放入 TFTP 服务器,如果是按照前面教程搭建路径为【/tftpboot/】

下面在 u-boot 中设置要下的镜像名称和设备树名称

u-boot=> setenv image Image

u-boot=> setenv fdt_file imx8mp-ai-robot.dtb

u-boot=> saveenv

如果希望将下载下来的镜像文件存储到指定的存储设备,例如外部 sdcard 或者 eMMC,

则需要设置当前存储设备。

保存到 eMMC,在开发板执行如下指令

u-boot=> mmc dev 2 0

保存到 SDcard,在开发板执行如下指令

u-boot=> mmc dev 1 0

下载 linux 内核文件

u-boot=> tftpboot ${loadaddr} ${image}

下载设备树文件

u-boot=> tftpboot ${fdt_addr} ${fdt_file}

启动程序

u-boot=> run mmcargs

u-boot=> booti ${loadaddr} - ${fdt_addr}

通过路由器连接电脑

上小节讲述了如何通过网路直连进行 TFTP 服务器的数据传输,本小节则讲述如何通过路由器连接。

通过路由器连接与直连其实差异并不大,只是通过路由器可以使用 DHCP 服务器,进行自动 IP 地址获取。

打开虚拟机的“/etc/network/interfaces”文件

linux@ubuntu:$ sudo vi /etc/network/interfaces

添加如下配置

auto ens33

iface ens33 inet dhcp

这里将“ens33”设置成了通过 DHCP 自动获取 IP 地址

设置完成后重启网络服务

linux@ubuntu:$ sudo reboot

设置完成后使用 ifconfig 命令查看当前 ubuntu 的 IP 地址。

首先启动开发板,使程序停留在 u-boot 终端。

这里我们需要设置几个与网络相关的环境变量,以支持网络传输。

在上面我们已经设置好了 ubuntu 的网络配置,这里 ubuntu 充当 TFTP 服务器。我们需要按照 ubuntu 的配置来设置开发板的环境变量。

设置服务器 IP 地址(ubuntu ip) 

u-boot=> setenv serverip 192.168.101.47

设置网卡 1 MAC 地址

u-boot=> setenv eth1addr 00:04:9f:07:0b:a5

保存环境变量

u-boot=> saveenv

下载 linux 内核与设备树

在进行内核下载之前,首先需要确认 TFTP 服务器是否已经安装完毕,如果还没有安装则需要根据前面章节的内容进行安装,此外还需要将之前编译好的 Image linux 内核程序与设备树文件 imx8mp-ai-robot.dtb 放入 TFTP 服务器,如果是按照前面教程搭建路径为【/tftpboot/】

下面在 u-boot 中设置要下的镜像名称和设备树名称

u-boot=> setenv image Image

u-boot=> setenv fdt_file imx8mp-ai-robot.dtb

基于imx8m plus开发板全体系开发教程4:Linux系统开发(imx6ul开源项目)

u-boot=> saveenv

这里与直连网路不同的是,不再需要指定本机 ip、网关、子网掩码等环境变量,这些信

息只需要通过 dhcp 获取即可

下载 linux 内核文件

u-boot=> dhcp ${loadaddr} ${image}

下载设备树文件

u-boot=> dhcp ${fdt_addr} ${fdt_file}

启动程序

u-boot=> run mmcargs

u-boot=> booti ${loadaddr} - ${fdt_addr}

基于 busybox 的最小文件系统制作busybox 源码编译及安装

可以从 http://busybox.net/downloads/网站下载 busybox-1.29.3 源码用于制作 Linux 文件系

统,为了方便,已将源码放进了光盘。

安装交叉编译工具链。

linux@ubuntu:$ sudo apt-get install gcc-aarch64-linux-gnu

linux@ubuntu:$ sudo apt-get install g++-aarch64-linux-gnu

linux@ubuntu:$ sudo apt-get install libncurses5-dev libncursesw5-dev

验证开发工具是否安装正确,显示版本信息如下图所示。

linux@ubuntu:$ aarch64-linux-gnu-gcc -v

建立源码目录

将【华清远见-I.MX8M Plus 开发资料-2021-06-02\程序源码\busybox】下的 busybox-1.29.

3.tar.bz2 拷贝至该目录。

linux@ubuntu:$ tar xvf busybox-1.29.3.tar.bz2 //解压源码

linux@ubuntu:$ cd busybox-1.29.3

配置 busybox 源码:

将顶层目录下的 Makefile 文件中的 CROSS_COMPILE 字段修改为“aarch64-linux-gnu-”

可以使用如下命令配置源码

linux@ubuntu:$ make ARCH=arm64 menuconfig

选择退出,选择保存

编译源码:

linux@ubuntu:$ make

安装:

busybox 默认安装路径为源码目录下的_install

linux@ubuntu:$ make install

进入安装目录可看到如下目录:

linux@ubuntu:$ cd _install

linux@ubuntu:$ ls

bin linuxrc sbin usr

添加主要系统启动文件

创建其他需要的目录: 

linux@ubuntu:$ cd _install

linux@ubuntu:$ mkdir dev etc mnt proc var tmp sys root

添加库:

将工具链中的库拷贝到_install 目录下:

linux@ubuntu:$ cp –a /usr/aarch64-linux-gnu/lib/ .

删除静态库:

linux@ubuntu:$ rm lib/*.a

添加系统启动文件:

在 etc 下添加文件 inittab,文件内容如下:

注意:修改文件均为_install 目录下

 etc/inittab

这里我们挂载的文件系统有三个 proc、sysfs 和 tmpfs。

回到创建的文件系统处,在 etc 下创建 init.d 目录,并在 init.d 下创建 rcS 文件,rcS 文件内容为:etc/init.d/rcS

为 rcS 添加可执行权限:

linux@ubuntu:$ chmod a+x init.d/rcS

在 etc 下添加 profile 文件,文件内容为:etc/profile

此时一个最小文件系统就制作完成了,可以将_install 目录下的文件复制到/source/rootfs 目

录下用于 NFS 挂载。

linux@ubuntu:$ cp * /source/rootfs -a 当前路径为_install 

通过 NFS 挂载文件系统

NFS 方式是开发板通过 NFS 挂载放在主机(PC )上的根文件系统。此时在主机在文件系统中进行的操作同步反映在开发板上;反之,在开发板上进行的操作同步反映在主机中的根文件系统上。实际工作中,我们经常使用 NFS 方式挂载系统,这种方式对于系统的调试非常便。

在进行本章实验前需首先确保 NFS 服务已经按照前面章节安装成功。本实验与 TFTP 实验相同,也分为直连和通过路由器两个部分。其中 ip 地址设置部分参考《通过 TFTP 服务器下载内核》章节设置即可,这里不在赘述。

在通过 NFS 启动网路文件系统之前,首先要确保 ubuntu 虚拟机目录下【/source/】存在

rootfs 文件系统,如果不存在该文件,则需要在【华清远见-I.MX8M Plus 开发资料-2021-06-

02\程序源码/文件系统源码】目录下提取 rootfs.tar.xz 压缩包,解压到/source/目录下,解压完成之后会在/source/目录下生成 rootfs 目录。

在开发板上电之前需将调试串口、网线接入网口 1。

配置好环境之后启动开发板,在使程序停留在 u-boot 终端。

使用 NFS 启动这里同样网路连接方式分为直连方式和路由器方式。

⚫ 直连方式

设置 serverip

这里假设服务器 ip 为 192.168.103.100 和开发板 ip 地址 192.168.103.1

u-boot=> setenv serverip 192.168.103.100

u-boot=> setenv ipaddr 192.168.103.101

设置 nfsroot

nfsroot 为我们在服务器上 nfs 服务器所在的工作目录,在前面章节我们已经将其设置为

“/source/rootfs”

u-boot=> setenv nfsroot /source/rootfs

设置 bootargs

u-boot=> setenv bootargs console=${console} root=/dev/nfs ip=${ipaddr}:::::eth0:off nfsr

oot=${serverip}:${nfsroot},v3,tcp

启动内核

u-boot=> run loadimage

u-boot=> run loadfdt

u-boot=> booti ${loadaddr} - ${fdt_addr}

⚫ 路由器方式

设置 serverip

这里假设服务器 ip 为 192.168.103.100

u-boot=> setenv serverip 192.168.103.100

设置 nfsroot

nfsroot 为我们在服务器上 nfs 服务器所在的工作目录,在前面章节我们已经将其设置为

“/source/rootfs”

u-boot=> setenv nfsroot /source/rootfs

设置 bootargs

u-boot=> setenv bootargs console=${console} root=/dev/nfs ip=dhcp nfsroot=${serverip}:

${nfsroot},v3,tcp

启动内核

u-boot=> run loadimage

u-boot=> run loadfdt

u-boot=> booti ${loadaddr} - ${fdt_addr}

这里需要注意的是直连方式和路由器方式,启动内核部分都是加载外部存储器中的程序进行启动的,如果需要通过 TFTP 启动参考《通过 TFTP 服务器下载内核》即可。

LED 驱动开发之设备树编写设备树结构分析

imx8mp uboot 设备树位于“arch/arm/dts/”目录下,linux 内核设备树位于

“arch/arm64/boot/dts/freescale/”目录下。

imx8mp 设备树结构如下图所示:

由上图可知“imx8mp.dtsi”为设备树的底层设备描述文件,主要描述了 SoC 级的控制器相关

信息,例如在开发中经常用到的“I2C 总线控制器”、“SPI 总线控制器”等都是在该设备树中描述,此外该文件中还添加了一些头文件,这些头文件是用于设备树中对一些宏定义的支持。在 imx8mp.dtsi 的下级分别有“imx8mp-evk.dts”、“imx8mp-ai-robot.dts”、“imx8mpevk-rpmsg.dts”、“imx8mp-evk-dsp.dts”四个文件,其中 imx8mp-evk.dts 是默认使用的设备树文件,可提供板载设备全功能驱动;imx8mp-ai-robot.dts 是作为“工业及机器人领域”的扩展设备树,可以支持 ROS、xenomai 及 EtherCAT 等功能扩展;imx8mp-evk-rpmsg.dts 主要用于 Cortex-A53 与 Cortex-M7 协同工作时使用的设备树,该设备树中将一部分外设资源分配给了 Cortex-A53 核心使用,一部分分配给 Cortex-M7 核心使用;imx8mp-evk-dsp.dts 主要是用于 DSP 相关功能支持。

IOMUXC 配置简介

我们在控制一个外部设备时,第一步通常都是在配置 SoC 上的引脚功能。对于 imx8mp

我们可以通过内部的 IOMUX 控制器来配置管脚的电压水平、驱动强度和迟滞等属性。具体引脚配置属性可以参考 NXP 官方提供的《i.MX 8M Plus Applications Processor Datasheet for Industrial Products》手册中的“External Signals and Pin Multiplexing”章节。IOMUX控制器的配置参考“IOMUX Controller (IOMUXC)”

IOMUX 控制器包含四组寄存器:

⚫ 通用寄存器(IOMUXC_GPRx):由控制锁相环频率、电压和其他通用配置的寄存器组成。

⚫ Daisy Chain 控制寄存器(IOMUXC_<Instance_port>_SELECT_INPUT):使 IC 可以在一

个 pad 上共享多个功能块。这种共享是通过复用 pad 的输入和输出信号来实现的。

⚫ MUX 控制寄存器(改变 pad 模式):

选择使用 pad 的 8 种不同功能(ALT 模式)中的哪一种功能。

使用下列寄存器中单独或分组设置 pad 功能:

IOMUXC_SW_MUX_CTL_PAD_<PAD NAME>

IOMUXC_SW_MUX_CTL_GRP_<GROUP NAME>

⚫ Pad 控制寄存器(改变 Pad 特性)涉及以下寄存器:

IOMUXC_SW_PAD_CTL_PAD_<PAD_NAME>

IOMUXC_SW_PAD_CTL_GRP_<GROUP NAME>

可设置的特性

SRE: 速率设置,可配置为 FAST 或者 SLOW。

DSE: 管脚驱动强度,可配置为 low, medium, high 或者 max

ODE: 可设置为开漏输出或者 CMOS 输出

HYS: 设置输入触发方式可配置为 CMOS 或者施密特

PUS: 设置管脚的内部上拉或者下拉

PUE: 设置在低功耗模式下默认的上拉或者下拉

PKE: 开启或关闭低功耗模式上拉或者下拉

下面的示例演示如何在设备树中使用 IOMUX XML Code

这里我们来分析第 7 行的配置,pinctrl 原理都是相同的这里以 7 行为例:

MX8MP_IOMUXC_UART1_RXD__UART1_DCE_RX 本质是一个宏定义,定义文件为 ar

ch/arm64/boot/dts/freescale/imx8mp-pinfunc.h,这里列出该定义的原型

#define MX8MP_IOMUXC_UART1_RXD__UART1_DCE_RX 0x220 0x480 0x5E8 0x0

0x4这里可以看到该宏的值为 0x220 0x480 0x5E8 0x0 0x4,那么这组数字是什么含义呢?

关于这组数字在 imx8mp-pinfunc.h 文件的第 11 行有解释“<mux_reg conf_reg input_reg mux_mode input_val>”这里的“mux_reg conf_reg input_reg”为三组 IOMUXC 控制器所对应的

寄存器的偏移量。

mux_reg=0x220 所对应的寄存器是 IOMUXC_SW_MUX_CTL_PAD_UART1_RXD

可以看到该寄存器的偏移量为 220h。同理我们可以得到另外两个寄存器 conf_reg=0x480

和 input_reg=0x5E8 分别对应 IOMUXC_SW_PAD_CTL_PAD_UART1_RXD 和 IOMUXC_UA

RT1_UART_RXD_MUX_SELECT_INPUT 寄存器。这里例如 input_reg 所对应的值为 0 则表

示没有使用。

接下来看 mux_mode input_val 这两个值,这两个值分别表示 mux_reg 和 input_reg 这两

个寄存器要设置的值。例如这里 mux_mode=0x0 则对应 IOMUXC_SW_MUX_CTL_PAD_UA

RT1_RXD 寄存器设置为 0;input_reg=0x4 对应IOMUXC_UART1_UART_RXD_MUX_SELE

CT_INPUT 设置为 0x4。这里我们就将 MX8MP_IOMUXC_UART1_RXD__UART1_DCE_RX

这个宏所对应的含义解释清楚了。

对于上面对 MX8MP_IOMUXC_UART1_RXD__UART1_DCE_RX 宏的解释其实还有一个问题,这个宏设置寄存器偏移量时我们设置了三个寄存器,但是这个宏里只指定了两个寄存器的值,那么有一个寄存器的值没有被设定,这个寄存器就是 conf_reg 所对应的 IOMUXC_SW_PAD_CTL_PAD_UART1_RXD 寄存器,该寄存器的值在设备树被指定,我们再来看设备树的第 7 行,MX8MP_IOMUXC_UART1_RXD__UART1_DCE_RX 0x140 这里的 0x140即为 IOMUXC_SW_PAD_CTL_PAD_UART1_RXD 寄存器要设置的值。

这部分内容可参考 linux 内核中对应的说明文档:

Documentation/devicetree/bindings/pinctrl/fsl,imx-pinctrl.txt

Documentation/devicetree/bindings/pinctrl/fsl,imx8mp-pinctrl.txt

LED 驱动开发之驱动程序设计platform 总线

要满足 Linux 设备模型,就必须要有总线、设备和驱动。但是有的设备并没有对应的物理总线,比如 LED、RTC 和蜂鸣器等。为此,内核专门开发了一种虚拟总线——platform 总线,用来连接这些没有物理总线的设备或者一些不支持热插拔的设备,接下来我们要用到的

LED 设备就是挂接在这条总线上的。

平台驱动是用 struct platform_driver 结构来表示的,他的定义如下。

C++ Code

驱动开发者关心得主要成员如下。

probe:总线发现有匹配的平台设备时调用。

remove:所驱动的平台设备被移除时或平台驱动注销时调用。

shutdown、suspend 和 resume:电源管理函数,在要求设备掉电、挂起和恢复时被调用。

内嵌的 struct device_driver 的 pm 成员也有对应的电源管理函数。

id_table:平台驱动可以驱动的平台设备 ID 列表,可用于和平台设备匹配。

向平台总线注册和注销的平台驱动的主要函数如下。

platform_driver_register(drv)

void platform_driver_unregister(struct platform_driver *);

因为在驱动中,经常在模块初始化函数中注册一个平台驱动,在清除函数中注销一个平台驱动,可以参考如下代码片段。

C++ Code

代码第 7 行至代码第 14 行定义了一个平台驱动,名字为 farsight-led;第 1 行至第 5 行是用于设备树匹配,匹配设备树中 compatible 的值为“farsight-led”的配置项当着两项匹配成功时执行 led_drv_probe 函数。代码第 19 行使用 platform_driver_register 向 platform 总线注册一个 platform driver 设备。

上面提到该驱动匹配设备树中 compatible 的值为“farsight-led”的配置项,这里列出完整设备树配置。

C++ Code

字符设备驱动编写

在正式学习字符设备驱动的编写之前,我们首先来看看相关的基础知识。在类 UNIX 系

统中,有一个众所周知的说法,即“一切皆文件”,当然网络设备是一个例外。这就意味着

设备最终也会体现为一个文件,应用程序要对设备进行访问,最终就会转化为对文件的访问,

这样做的好处是统一了对上层的接口。设备文件通常位于/dev 目录下,使用下面的命令可以看到很多设备文件及其相关的信息。

root@imx8mp:# ls -l /dev

total 0

……

brw-rw---- 1 root disk 8, 0 Jul 4 10:07 sda

brw-rw---- 1 root disk 8, 1 Jul 4 10:07 sda1

brw-rw---- 1 root disk 8, 2 Jul 4 10:07 sda2

brw-rw---- 1 root disk 8, 5 Jul 4 10:07 sda5

……

crw--w---- 1 root tty 4, 0 Jul 4 10:07 tty0

crw-rw---- 1 root tty 4, 1 Jul 4 10:07 tty1

……

​面列出的信息中,前面的字母“b”表示的是块设备,“c”表示的是字符设备。比如上面的 sda、sda1、sda2、sda5 就是块设备,实际上这些设备是笔者的 Ubuntu 主机上的一个硬盘和这个硬盘上的三个分区,其中 sda 表示的是整个硬盘,而 sda1、sda2、sda5 分别是三个分区。tty0、tty1 就是终端设备,shell 程序使用这些设备来同用户进行交互。从上面的打印信息来看,设备文件和普通文件有很多相似之处,都有相应的权限,所属的用户和组,修改时间和名字。但是设备文件会比普通文件多出两个数字,这两个数字分别叫主设备号和次设备号。这两个号是设备在内核中的身份或标志,是内核用于区分不同设备的唯一信息。通常内核用主设备号区别一类设备,次设备号用于区分同一类设备的不同的个体或不同的分区。而路径名则是用户层用于区别设备的信息。

现在的 Linux 系统中,设备文件通常是自动创建的。即便如此,我们还是可以通过 mknod

命令来手动创建一个设备文件,如下所示。

root@imx8mp:# mknod /dev/vser0 c 256 0

root@imx8mp:# ls -li /dev/vser0

126695 crw-r--r-- 1 root root 256, 0 Jul 13 10:03 /dev/vser0

要实现一个字符设备驱动,其中最重要的事就是要构造一个 cdev 结构对象,并让 cdev 同

设备号和设备的操作方法集合相关联,然后将该 cdev 结构对象添加到内核的 cdev_map 散列表中。下面我们逐步来实现这一过程,首先就是在驱动中注册设备号,代码如下(完整的代码请参见“”)。

在模块的初始化函数中,首先在代码第 38 行使用 MKDEV 宏将主设备号和次设备号合并成一个设备号。

第 5 行定义了一个 struct cdev 类型的变量 cdev,在第 12 行到第 16 行定义了一个 struct file_operations 类型的全局变量 dev_ops。我们知道,这两个数据结构是实现字符设备驱动的关键。其中 cdev 代表了一个具体的字符设备,而 dev_ops 是操作该设备的一些方法。代码第27 行调用了 cdev_init 函数初始化了 cdev 里面的部分成员,另外一个最重要的操作就是将 cdev里面的 ops 指针指向了 dev_ops,这样在通过设备号找到 cdev 对象后,就能找到相关的操作方法集合,并调用其中的方法。cdev_init 函数的原型如下,第一个参数是要初始化的 cdev 地址,第二个参数是设备操作方法集合的结构地址。

void cdev_init(struct cdev *cdev, const struct file_operations *fops);

代码第 29 行将一个 owner 成员赋值为 THIS_MUDULE,owner 是一个指向 struct module

类型变量的指针,THIS_MUDULE 是包含驱动的模块中的 struct module 类型对象的地址,类似于 c++中的 this 指针。这样就能通过 cdev 或 dev_ops 找到对应的模块,在对前面两个对象进行访问时都要调用类似于 try_module_get 的函数增加模块的引用计数,其目的是这两个对象在使用的过程中,模块是不能被卸载的,因为模块被卸载的前提条件是引用计数为 0。cdev 对象初始化好以后,就应该添加到内核中的 cdev_map 散列表当中,调用的函数是cdev_add,其函数原型如下。

int cdev_add(struct cdev *p, dev_t dev, unsigned count);

在初始化函数中添加了 cdev 对象,那么在清除函数中自然就应该删除该 cdev 对象,代码第 85 行演示了这一操作,实现的函数是 cdev_del,其函数原型如下。

void cdev_del(struct cdev *p);

操作 GPIO 管脚

GPIO 应该是每个嵌入式设备都避免不了的。现在内核里面多了 gpiod 的来控制 gpio 口,

相对于原来的形式,使用 gpiod 的好处是我们申请后不进行 free 也没有什么问题。

使用以下函数获取 GPIO 设备,多个设备时需要附带 index 参数。函数返回一个 GPIO 描

述符,或一个错误编码,可以使用 IS_ERR()进行检查:

struct gpio_desc *gpiod_get_index(struct device *dev,

 const char *con_id, unsigned int idx,

 enum gpiod_flags flags)

如果要设置 GPIO 的输入或者输出模式,可以使用如下两个函数实现:

int gpiod_direction_input(struct gpio_desc *desc)

int gpiod_direction_output(struct gpio_desc *desc, int value)

如果要读取 GPIO 口的电平状态则使用下面的函数:

int gpiod_get_value(const struct gpio_desc *desc);

如果要设置 GPIO 口的电平状态则使用下面的函数:

void gpiod_set_value(struct gpio_desc *desc, int value);

下面我们来分析 GPIO 控制逻辑。代码如下:

C++ Code

这部分代码比较简单,这里我们只关注与 GPIO 相关程序,程序第 6 行是在获取设备树中对 gpio 口的配置,第 21 行是将对应 IO 口设置为高电平,第 25 行的将对应 IO 口设置为低电平。

上层应用程序开发

该驱动我们通过 ioctl 函数来控制 LED 灯的状态。ioctl 函数为了处理设备非数据的操作(这些可以同过 read、write 接口来实现),内核将对设备的控制操作委派给了 ioctl 接口,ioctl 也是一个系统调用,其函数原型如下。

int ioctl(int d, int request, ...);

0x12345678 表示点灯,而 0x12345679 表示灭灯等。但是这个操作码,或者更符合内核的

说法是命令,应该具有一定的编码规则,这个我们在后面会介绍。…是 C 语言中实参个数可变的函数原型

本文链接地址:https://www.jiuchutong.com/zhishi/300522.html 转载请保留说明!

上一篇:最新版本 Stable Diffusion 开源AI绘画工具之部署篇(最新版本TVBox配置地址)

下一篇:React报错之map() is not a function(react错误处理)

  • 每天吃盐不超过多少克中国居民的推荐

    每天吃盐不超过多少克中国居民的推荐

  • 磁力链接怎么用

    磁力链接怎么用

  • 一般笔记本电脑功率大概多少(一般笔记本电脑功率)(一般笔记本电脑内存多大合适)

    一般笔记本电脑功率大概多少(一般笔记本电脑功率)(一般笔记本电脑内存多大合适)

  • 联通手机卡欠费多久会自动销号(联通手机卡欠费多久会销卡)

    联通手机卡欠费多久会自动销号(联通手机卡欠费多久会销卡)

  • 10086验证码能控制什么(10086验证码可以告诉别人吗)

    10086验证码能控制什么(10086验证码可以告诉别人吗)

  • 拼多多一刀砍成什么时候用(拼多多一刀砍成免费拿)

    拼多多一刀砍成什么时候用(拼多多一刀砍成免费拿)

  • 华为手环3e如何开机(华为手环3e如何使用)

    华为手环3e如何开机(华为手环3e如何使用)

  • iphone6s强制重启(iphone6s强制重启怎么操作)

    iphone6s强制重启(iphone6s强制重启怎么操作)

  • 华为手机怎样群发手机短信(华为手机怎样群删微信联系人)

    华为手机怎样群发手机短信(华为手机怎样群删微信联系人)

  • 探探怎么删除不了头像(怎样才能删除探探)

    探探怎么删除不了头像(怎样才能删除探探)

  • 华为畅享10系列什么时候出的(华为畅享10系列手机哪个好)

    华为畅享10系列什么时候出的(华为畅享10系列手机哪个好)

  • ml7e2ch/a是什么苹果几(ml7j2ch/a是什么版本的苹果)

    ml7e2ch/a是什么苹果几(ml7j2ch/a是什么版本的苹果)

  • 苹果手机充电头是几A的(苹果手机充电头多少钱1个)

    苹果手机充电头是几A的(苹果手机充电头多少钱1个)

  • 笔记本电脑的键盘可以拆下来吗(笔记本电脑的键盘有几个按键失灵了怎么办)

    笔记本电脑的键盘可以拆下来吗(笔记本电脑的键盘有几个按键失灵了怎么办)

  • nova6可以无线充电吗(nova6能否无线充电)

    nova6可以无线充电吗(nova6能否无线充电)

  • 有什么定位软件(手机有什么定位软件)

    有什么定位软件(手机有什么定位软件)

  • 云端软件平台是什么(云端软件是什么)

    云端软件平台是什么(云端软件是什么)

  • 晶面间距到底是什么(晶面间距能说明什么)

    晶面间距到底是什么(晶面间距能说明什么)

  • 手机照相机故障怎么办(手机照相机故障是什么原因)

    手机照相机故障怎么办(手机照相机故障是什么原因)

  • 竖排版的数字怎么排(竖排版数字怎么写)

    竖排版的数字怎么排(竖排版数字怎么写)

  • 手机照片怎么缩小尺寸(手机照片怎么缩放比例)

    手机照片怎么缩小尺寸(手机照片怎么缩放比例)

  • ARunit怎么用(arrive怎么用)

    ARunit怎么用(arrive怎么用)

  • 云空间怎么使用(换手机了云空间怎么使用)

    云空间怎么使用(换手机了云空间怎么使用)

  • 探探会员充值(探探会员充值方法)

    探探会员充值(探探会员充值方法)

  • 苹果7plus悬浮球在哪设置(苹果7plus悬浮球怎么设置里没有了)

    苹果7plus悬浮球在哪设置(苹果7plus悬浮球怎么设置里没有了)

  • 如何清除不必要的右键菜单?(清除不必要的内存)

    如何清除不必要的右键菜单?(清除不必要的内存)

  • 劳务公司一般纳税人可以抵扣进项税吗
  • 小规模纳税人增值税优惠政策
  • 凭证附件的粘法
  • 增值税和个人所得税都要交吗
  • 新公司开账户需要多少钱
  • 非居民企业直接投资居民企业取得股息
  • 缴纳社保的会计分录怎么做
  • 长期股权投资收益会计处理
  • 年化收益率的计算公式
  • 出租设备应计入什么科目
  • 购置房产按揭应注意事项
  • 法人 持股
  • 增值税留抵的原因
  • 企业可根据实际情况随意设置会计科目
  • 发票纳税人识别号错了能重新开吗
  • 土地增值税哪些可以抵扣
  • 不适用研究开发费用税前加计扣除政策的有
  • 如何确定连锁店的纳税地点?
  • 停车场会计科目设置
  • 企业职工教育经费
  • 联营企业分回的利润交企业所得税吗
  • 自建自用建筑物,其自建行为不是建筑业税目的征税范围
  • 一般纳税人所得税2023年税率
  • 退了货的发票还能用吗
  • 个人将房产无偿赠与他人应交个人所得税吗
  • 附有销售退回条款的递延所得税问题
  • 跨月发票冲红账怎么做
  • 贴现利息会计处理
  • 付给银行的手续费分录
  • 清除cookies有什么用
  • 公司出租房屋租金由承租方本人支付
  • windows未能正常启动
  • 政府性基金和行政事业性收费区别
  • php面向对象的理解
  • 收回前欠货款存入银行的会计分录
  • 尚融资本
  • 基于深度学习的图像超分辨率——综述
  • PyTorch深度学习实战 | 神经网络的优化难题
  • php中preg_replace_callback函数简单用法示例
  • 帝国cms安装教程
  • 出口退税未按期申报怎么办
  • mongodb基本使用
  • 应收处理的两大内容是什么
  • 投资款未备注
  • 专项附加可以叠加吗
  • mysql数据库死锁
  • 长期借款已经还了怎么办
  • 残保金用人单位在职职工人数
  • 应该是先付款还是先开发票
  • 交纳增值税的账务处理PPT
  • 收到的货款比实际货款多
  • 国有资产保值增值率
  • 财政补助结转余额在借方还是贷方
  • 每月增值税怎么做账
  • 主营业务成本如何做分录
  • 免征印花税的6个项目
  • 期末未缴税额为正是什么意思
  • 本月增加的固定资产本月可以进行部门转移
  • 管理费用怎么结转到本年利润未分配利润里了
  • 以前年度损益调整怎么做账
  • 物业服务企业管理
  • 预提业务
  • 苹果mac安装
  • win10预览版21277
  • ubuntu系统安装SSH服务
  • Red Hat Enterprise Linux 4+Nginx 0.7.47+PHP5.2+MYSQL5.0+Memcache+eAccelerator收
  • win1020h2无法重启
  • win7安装kb3170455失败
  • win8.1无线
  • jquery九宫格抽奖
  • jquery+ajax实现省市区三级联动效果简单示例
  • Linux中删除文件夹的正确方式
  • dos命令不能执行怎么回事
  • Python 正则表达式实现计算器功能
  • 详细解读退役军人优待政策
  • unity3d知乎
  • 央企收入归谁
  • 担保费属于什么服务
  • 税务注销相关文件
  • 减免所得税额怎么算的
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设