开发板上共有三个按键:KEY0、KEY1 和 WK_UP。

注意原理图上 R14 标记了DNP ,表示板子上并未焊接这个 10K 上拉电阻 。👉 这块板子实际是利用 STM32 内部上拉电阻来实现的。
上拉电阻 R14 的一端接VCC3.3(3.3V 电源),另一端接到按键的一端,也就是 PG3 引脚;按键的另一端接到 GND(地,0V)。
- 按键没有按下(断开状态) :此时按键处于断开状态,引脚 PG3 没有接到地。电源 VCC3.3 通过 10k 上拉电阻将 PG3 节点直接拉高到 3.3V,即高电平。回路为:VCC3.3 → 10k 电阻 → PG3 引脚;由于按键断开,与 GND 不导通,因此 IO 为高电平。
- 按键按下(闭合状态) :此时按键接通,PG3 直接连接到 GND(0V)。电阻一端为 3.3V,另一端被按键短路到地,PG3 变为低电平 0V。

[root@ATK-stm32mp1]:/lib/modules/5.4.31$ modprobe atomic
[ 1044.647202] gpioled node not find!
[ 1044.776954] gpioled node not find!
modprobe: can't load module atomic (atomic.ko): Invalid argument
[root@ATK-stm32mp1]:/lib/modules/5.4.31$
修改设备树文件:在 stm32mp157d-atk.dts 文件的根节点"/"下创建 LED 灯节点,节点名为"gpioled",节点内容如下:
gpioled {
compatible = "alientek,led";
status = "okay";
led-gpio = <&gpioi 0 GPIO_ACTIVE_LOW>;
};
第一步先确认板子当前设备树里面到底有没有节点
ls /proc/device-tree/gpioled
ATK-stm32mp1 login: root
[root@ATK-stm32mp1]:~$ ls /proc/device-tree/gpioled
compatible led-gpio name status
[root@ATK-stm32mp1]:~$


[root@ATK-stm32mp1]:/lib/modules/5.4.31$ cat /proc/device-tree/gpioled/led-gpio
Z[root@ATK-stm32mp1]:/lib/modules/5.4.31$
d/led‑gpio直接乱码输出 Z,这是正常的!gpios 属性是二进制数组,不是文本,不能直接 cat 看。 用 hexdump 读取原始数据:
hexdump -C /proc/device-tree/gpioled/led-gpio
用 hexdump 读取原始数据:
hexdump -C /proc/device-tree/gpioled/led-gpio
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ hexdump -C /proc/device-tree/gpioled/le
d-gpio
00000000 00 00 00 5a 00 00 00 00 00 00 00 01 |...Z........|
0000000c
[root@ATK-stm32mp1]:/lib/modules/5.4.31$
正点原子 ATK‑STM32MP157 底板(DK1)板载 LED0

原理图:LED0 = PI0(GPIOI0) ,低电平点亮
gpioled {
compatible = "alientek,led";
status = "okay";
led-gpio = <&gpioi 0 GPIO_ACTIVE_LOW>;
};
❗但是你刚才遇到的问题:PI0 已经被系统内核占用!gpio_request 报‑EBUSY(Device or resource busy)!
📌两个选择
方案 1:坚持使用板上 LED0 (PI0)
为什么 PI0 被占用?内核自带设备树里面已经定义了 led,内核驱动默认把 PI0 拿过去用作系统心跳灯。 想要自己驱动使用 PI0,必须把系统自带那个 led 节点 status 改成 "disabled" 关闭! 在 dts 里面找到系统自带 led 节点:
&leds {
status = "disabled"; /*关闭系统自带LED,释放PI0引脚资源*/
};
然后再用上面 gpioled 节点,&gpioi 0。
⚠️做完之后重新编译 dtb,更新 SD 卡 dtb 断电重启。否则依旧资源忙!
方案 2:改用 LED1 (PF3)(你之前改的那个)
板上 LED1:PF3,低电平有效,内核默认没有占用,直接就可以用,不用关闭系统 led。
gpioled {
compatible = "alientek,led";
status = "okay";
led-gpio = <&gpiof 3 GPIO_ACTIVE_LOW>;
};
linux-5.4.31
user@user-virtual-machine:~/linux/atk-mp1/linux/my_linux$ cd linux-5.4.31/
user@user-virtual-machine:~/linux/atk-mp1/linux/my_linux/linux-5.4.31$ ls
arch CONTRIBUTING.md crypto fs ipc kernel MAINTAINERS modules.builtin.modinfo README security System.map virt
block COPYING Documentation include Kbuild lib Makefile Module.symvers samples sound tools vmlinux
certs CREDITS drivers init Kconfig LICENSES mm net scripts stm32mp157d_atk.sh usr vmlinux.o
user@user-virtual-machine:~/linux/atk-mp1/linux/my_linux/linux-5.4.31$ make dtbs
DTC arch/arm/boot/dts/stm32mp157d-atk.dtb
user@user-virtual-machine:~/linux/atk-mp1/linux/my_linux/linux-5.4.31$ cp arch/arm/boot/dts/stm32mp157d-atk.dtb /home/user/linux/tftpboot/
user@user-virtual-machine:~/linux/atk-mp1/linux/my_linux/linux-5.4.31$

设备树 改成
gpioled {
compatible = "alientek,led";
status = "okay";
led-gpio = <&gpiof 3 GPIO_ACTIVE_LOW>;
};
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ ls
aaaa beep.ko dtsled.ko modules.alias
atomic.ko beepApp led modules.dep
atomicApp chrdevbase.ko led.ko modules.symbols
bbb chrdevbaseApp ledApp newchrled.ko
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ modprobe atomic.ko
[ 53.387504] led-gpio num = 83
[ 53.389107] gpioled: Failed to request led-gpio
[ 53.505983] led-gpio num = 83
[ 53.507586] gpioled: Failed to request led-gpio
modprobe: can't load module atomic.ko (atomic.ko): Device or resource busy
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ [root@ATK-stm32mp1]:/lib/modules/5.4.31
$ modprobe atomic.ko
先排查:到底谁占用 PF3。 开发板上执行下面命令查看引脚占用:
cat /sys/kernel/debug/gpio
或者看 pinctrl 状态:
cat /sys/kernel/debug/pinctrl/soc\:pin-controller@50002000/pinmux-pins
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ cat /sys/kernel/debug/gpio
cat: can't open '/sys/kernel/debug/gpio': No such file or directory
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ cat /sys/kernel/debug/pinctrl/soc\:pin-
controller@50002000/pinmux-pins
cat: can't open '/sys/kernel/debug/pinctrl/soc:pin-controller@50002000/pinmux-pins': No such file or directory
[root@ATK-stm32mp1]:/lib/modules/5.4.31$
两种可能性
可能性 1:设备树 dtsi 里面已经把 PF3 定义为系统 LED1!
ST 官方 dtsi 里面自带&leds节点,已经使用 PF3 做 led1。
DK1 底板: led0 → PI0 led1 → PF3 两个 LED 在内核默认设备树全部已经注册! 👉所以不光 PI0,PF3 同样被系统 led 驱动占用!!! 这就是你现在 busy 的根源!
✅解决方法:把系统自带的 & leds 整个禁用掉! 修改你的 dts (stm32mp157d‑atk.dts),在里面增加:
&leds {
status = "disabled";
};
gpioled {
compatible = "alientek,led";
status = "okay";
led-gpio = <&gpiof 3 GPIO_ACTIVE_LOW>;
};
stm32mp157d-atk.dtsi

这里就是根源! stm32mp157d‑atk.dtsi里面已经定义好了:
leds {
compatible = "gpio-leds";
led1 {
label = "sys-led";
gpios = <&gpioi 0 GPIO_ACTIVE_LOW>; //LED0 PI0
linux,default-trigger = "heartbeat";
default-state = "on";
status = "okay";
};
led2 {
label = "user-led";
gpios = <&gpiof 3 GPIO_ACTIVE_LOW>; //LED1 PF3
linux,default-trigger = "none";
default-state = "on";
status = "okay";
};
};
✅PI0、PF3 两个板载 LED 全部在这里被内核 gpio‑led 驱动占用。
⚠️千万不要直接修改 dtsi 文件!!不要改这个头文件!! dtsi 是被
.dts文件包含进来的,我们只需要在**stm32mp157d‑atk.dts(不是 dtsi)** 里面用结点覆盖,把&leds禁用即可。
在 stm32mp157d‑atk.dts 添加下面内容
&leds {
status = "disabled";
};
gpioled {
compatible = "alientek,led";
status = "okay";
led-gpio = <&gpiof 3 GPIO_ACTIVE_LOW>;
};

原理:dts 里面对
&leds重新赋值 status,会覆盖 dtsi 里面原来的设置,把整个 leds 节点关闭,释放 PI0、PF3 两个引脚资源。不需要改动 dtsi!
完整步骤
-
打开
stm32mp157d‑atk.dts(dts,不是 dtsi)粘贴上面片段;保存。 -
内核源码编译设备树
make dtbs
报错 :

// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
/*
- Copyright (C) STMicroelectronics 2019 - All Rights Reserved
- Author: Alexandre Torgue [alexandre.torgue@st.com](mailto:alexandre.torgue@st.com) for STMicroelectronics.
*/
/dts-v1/;
#include "stm32mp157.dtsi"
#include "stm32mp15xc.dtsi"
#include "stm32mp15-pinctrl.dtsi"
#include "stm32mp15xxaa-pinctrl.dtsi"
#include "stm32mp157-m4-srm.dtsi"
#include "stm32mp157-m4-srm-pinctrl.dtsi"
#include "stm32mp157d-atk.dtsi"
/ {
model = "STMicroelectronics STM32MP157C eval daughter";
compatible = "st,stm32mp157c-ed1", "st,stm32mp157";
chosen {
stdout-path = "serial0:115200n8";
};
aliases {
serial0 = &uart4;
};
reserved-memory {
gpu_reserved: gpu@f6000000 {
reg = <0xf6000000 0x8000000>;
no-map;
};
optee_memory: optee@fe000000 {
reg = <0xfe000000 0x02000000>;
no-map;
};
};
stm32mp1_led {
compatible = "atkstm32mp1-led";
status = "okay";
reg = <0X50000A28 0X04 /* RCC_MP_AHB4ENSETR */
0X5000A000 0X04 /* GPIOI_MODER */
0X5000A004 0X04 /* GPIOI_OTYPER */
0X5000A008 0X04 /* GPIOI_OSPEEDR */
0X5000A00C 0X04 /* GPIOI_PUPDR */
0X5000A018 0X04 >; /* GPIOI_BSRR */
};
beep {
compatible = "alientek,beep";
status = "okay";
beep-gpio = <&gpioc 7 GPIO_ACTIVE_HIGH>;
};
&leds {
status = "disabled";
};
gpioled {
compatible = "alientek,led";
status = "okay";
led-gpio = <&gpiof 3 GPIO_ACTIVE_LOW>;
};
};
&cpu1{
cpu-supply = <&vddcore>;
};
&cryp1 {
status="okay";
};
&gpu {
contiguous-area = <&gpu_reserved>;
status = "okay";
};
&optee {
status = "okay";
};

user@user-virtual-machine:~/linux/atk-mp1/linux/my_linux/linux-5.4.31$ make dtbs
DTC arch/arm/boot/dts/stm32mp157d-atk.dtb
Error: arch/arm/boot/dts/stm32mp157d-atk.dts:65.1-6 Label or path leds not found
FATAL ERROR: Syntax error parsing input tree
scripts/Makefile.lib:285: recipe for target 'arch/arm/boot/dts/stm32mp157d-atk.dtb' failed
make[1]: *** [arch/arm/boot/dts/stm32mp157d-atk.dtb] Error 1
Makefile:1243: recipe for target 'dtbs' failed
make: *** [dtbs] Error 2
user@user-virtual-machine:~/linux/atk-mp1/linux/my_linux/linux-5.4.31$
报错原因
Error: arch/arm/boot/dts/stm32mp157d‑atk.dts:65.1‑6 Label or path leds not found
Label or path leds not found
含义:&leds 这个标签找不到!
你直接写
&leds想去引用节点,但是当前包含的头文件(dtsi)里面没有叫 leds 的 label。
❗不是语法括号错了!是正点原子这块板子的 dtsi 里面板载 LED 节点名字不是 label=leds 。 STM32MP157D‑ATK 开发板,它的 LED 是定义在stm32mp157d‑atk.dtsi里面。

解决方法:给它补 label(二选一)
方法 1:修改 stm32mp157d‑atk.dtsi(头文件)
原来:
leds {
改成,增加 label:
leds: leds {
保存头文件之后,你原来 dts 里面的&leds {status="disabled";};就可以正常识别编译通过。

user@user-virtual-machine:~/linux/atk-mp1/linux/my_linux/linux-5.4.31$ make dtbs
DTC arch/arm/boot/dts/stm32mp157d-atk.dtb
user@user-virtual-machine:~/linux/atk-mp1/linux/my_linux/linux-5.4.31$
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ modprobe atomic.ko
[ 35.333202] led-gpio num = 83
[ 35.334854] gpioled major=241,minor=0
[root@ATK-stm32mp1]:/lib/modules/5.4.31$
现象分析
[ 35.333202] led‑gpio num = 83
[ 35.334854] gpioled major=241,minor=0
✅modprobe atomic.ko驱动加载成功!
- GPIO 编号打印:83
先确认引脚编号换算:GPIOF3 F 组:F 是第 5 组 (A=0,B=1,C=2,D=3,E=4,F=5)
5*16 +3 = 83✔完全对上!说明设备树 of 解析读取 gpio 属性是正确拿到 PF3 (83)!
- 自动分配主设备号
major=241,次设备号 0;字符设备已经创建。
编译测试 APP 输入如下命令编译测试 atomicApp.c
这个测试程序: arm-none-linux-gnueabihf-gcc atomicApp.c -o atomicApp 编译成功以后就会生成 atomicApp 这个应用程序。
3、运行测试 将上一小节编译出来的 atomic.ko 和 atomicApp 这两个文件拷贝到 rootfs/lib/modules/5.4.31 目录中,重启开发板,进入到目录 lib/modules/5.4.31 中,输入如下命令加载 atomic.ko 驱动模 块:
depmod //第一次加载驱动的时候需要运行此命令
modprobe atomic.ko //加载驱动
驱动加载成功以后就可以使用 atomicApp 软件来测试驱动是否工作正常,输入如下命令以 后台运行模式打开 LED 灯,"&"表示在后台运行 atomicApp 这个软件: ./atomicApp /dev/gpioled 1 & //打开 LED 灯 输入上述命令以后观察开发板上的红色 LED 灯是否点亮,然后每隔 5 秒都会输出一行"App running times "
App running finished![1]+ Done ./atomicApp /dev/gpioled 1
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ ./atomicApp /dev/gpioled 1 &
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ ./atomicApp /dev/gpioled 0
file /dev/gpioled open failed[root@ATK-stm32mp1]:/lib/modules/5.4.31$ App running times:-1091150688
App running times:-1091150688
App running times:-1091150688
App running times:-1091150688
App running times:-1091150688
App running finished!
[1]+ Done ./atomicApp /dev/gpioled 1
[root@ATK-stm32mp1]:/lib/modules/5.4.31$
[root@ATK-stm32mp1]:/lib/modules/5.4.31$
./atomicApp /dev/gpioled 0
,打开/dev/gpioled 失败!原因是在图 28.1.2.1 中运行的 atomicAPP 软件正在占用/dev/gpioled,如果再次运行 atomicApp 软件去操作/dev/gpioled 肯定会失败。必须 等待图28.1.2.1中的atomicApp运行结束,也就是25S结束以后其他软件才能去操作/dev/gpioled。 这个就是采用原子变量实现一次只能有一个应用程序访问 LED 灯。 如果要卸载驱动的话输入如下命令即可: rmmod atomic.ko
附代码
stm32mp157d-atk.dtsi
增加leds:标签 leds: leds {
leds: leds {
compatible = "gpio-leds";
led1 {
label = "sys-led";
gpios = <&gpioi 0 GPIO_ACTIVE_LOW>;
linux,default-trigger = "heartbeat";
default-state = "on";
status = "okay";
};
led2 {
label = "user-led";
gpios = <&gpiof 3 GPIO_ACTIVE_LOW>;
linux,default-trigger = "none";
default-state = "on";
status = "okay";
};
/*
beep {
label = "beep";
gpios = <&gpioc 7 GPIO_ACTIVE_LOW>;
default-state = "off";
};
*/
};
根节点/添加 LED3 节点
gpioled {
compatible = "alientek,led";
status = "okay";
led-gpio = <&gpiof 3 GPIO_ACTIVE_LOW>;
};
&leds {
status = "disabled";
};
&leds写在 根节点外
// 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 "stm32mp15xc.dtsi"
#include "stm32mp15-pinctrl.dtsi"
#include "stm32mp15xxaa-pinctrl.dtsi"
#include "stm32mp157-m4-srm.dtsi"
#include "stm32mp157-m4-srm-pinctrl.dtsi"
#include "stm32mp157d-atk.dtsi"
/ {
model = "STMicroelectronics STM32MP157C eval daughter";
compatible = "st,stm32mp157c-ed1", "st,stm32mp157";
chosen {
stdout-path = "serial0:115200n8";
};
aliases {
serial0 = &uart4;
};
reserved-memory {
gpu_reserved: gpu@f6000000 {
reg = <0xf6000000 0x8000000>;
no-map;
};
optee_memory: optee@fe000000 {
reg = <0xfe000000 0x02000000>;
no-map;
};
};
stm32mp1_led {
compatible = "atkstm32mp1-led";
status = "okay";
reg = <0X50000A28 0X04 /* RCC_MP_AHB4ENSETR */
0X5000A000 0X04 /* GPIOI_MODER */
0X5000A004 0X04 /* GPIOI_OTYPER */
0X5000A008 0X04 /* GPIOI_OSPEEDR */
0X5000A00C 0X04 /* GPIOI_PUPDR */
0X5000A018 0X04 >; /* GPIOI_BSRR */
};
beep {
compatible = "alientek,beep";
status = "okay";
beep-gpio = <&gpioc 7 GPIO_ACTIVE_HIGH>;
};
gpioled {
compatible = "alientek,led";
status = "okay";
led-gpio = <&gpiof 3 GPIO_ACTIVE_LOW>;
};
};/* 根节点结束位置*/
&leds {/*根节点外引用 **/
status = "disabled";
};
&cpu1{
cpu-supply = <&vddcore>;
};
&cryp1 {
status="okay";
};
&gpu {
contiguous-area = <&gpu_reserved>;
status = "okay";
};
&optee {
status = "okay";
};
atomitc.c
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
/***************************************************************
Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
文件名 : gpioled.c
作者 : 正点原子Linux团队
版本 : V1.0
描述 : 原子操作实验,使用原子变量来实现对实现设备的互斥访问。
其他 : 无
论坛 : www.openedv.com
日志 : 初版V1.0 2021/1/4 正点原子Linux团队创建
***************************************************************/
#define GPIOLED_CNT 1 /* 设备号个数 */
#define GPIOLED_NAME "gpioled" /* 名字 */
#define LEDOFF 0 /* 关灯 */
#define LEDON 1 /* 开灯 */
/* gpioled设备结构体 */
struct gpioled_dev{
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
struct device_node *nd; /* 设备节点 */
int led_gpio; /* led所使用的GPIO编号 */
atomic_t lock; /* 原子变量 */
};
static struct gpioled_dev gpioled; /* led设备 */
/*
* @description : 打开设备
* @param - inode : 传递给驱动的inode
* @param - filp : 设备文件,file结构体有个叫做private_data的成员变量
* 一般在open的时候将private_data指向设备结构体。
* @return : 0 成功;其他 失败
*/
static int led_open(struct inode *inode, struct file *filp)
{
/* 通过判断原子变量的值来检查LED有没有被别的应用使用 */
if (!atomic_dec_and_test(&gpioled.lock)) {
atomic_inc(&gpioled.lock); /* 小于0的话就加1,使其原子变量等于0 */
return -EBUSY; /* LED被使用,返回忙 */
}
filp->private_data = &gpioled; /* 设置私有数据 */
return 0;
}
/*
* @description : 从设备读取数据
* @param - filp : 要打开的设备文件(文件描述符)
* @param - buf : 返回给用户空间的数据缓冲区
* @param - cnt : 要读取的数据长度
* @param - offt : 相对于文件首地址的偏移
* @return : 读取的字节数,如果为负值,表示读取失败
*/
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
return 0;
}
/*
* @description : 向设备写数据
* @param - filp : 设备文件,表示打开的文件描述符
* @param - buf : 要写给设备写入的数据
* @param - cnt : 要写入的数据长度
* @param - offt : 相对于文件首地址的偏移
* @return : 写入的字节数,如果为负值,表示写入失败
*/
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
int retvalue;
unsigned char databuf[1];
unsigned char ledstat;
struct gpioled_dev *dev = filp->private_data;
retvalue = copy_from_user(databuf, buf, cnt); /* 接收APP发送过来的数据 */
if(retvalue < 0) {
printk("kernel write failed!\r\n");
return -EFAULT;
}
ledstat = databuf[0]; /* 获取状态值 */
if(ledstat == LEDON) {
gpio_set_value(dev->led_gpio, 0); /* 打开LED灯 */
} else if(ledstat == LEDOFF) {
gpio_set_value(dev->led_gpio, 1); /* 关闭LED灯 */
}
return 0;
}
/*
* @description : 关闭/释放设备
* @param - filp : 要关闭的设备文件(文件描述符)
* @return : 0 成功;其他 失败
*/
static int led_release(struct inode *inode, struct file *filp)
{
struct gpioled_dev *dev = filp->private_data;
/* 关闭驱动文件的时候释放原子变量 */
atomic_inc(&dev->lock);
return 0;
}
/* 设备操作函数 */
static struct file_operations gpioled_fops = {
.owner = THIS_MODULE,
.open = led_open,
.read = led_read,
.write = led_write,
.release = led_release,
};
/*
* @description : 驱动出口函数
* @param : 无
* @return : 无
*/
static int __init led_init(void)
{
int ret = 0;
const char *str;
/* 1、初始化原子变量 */
gpioled.lock = (atomic_t)ATOMIC_INIT(0);
/* 2、原子变量初始值为1 */
atomic_set(&gpioled.lock, 1);
/* 设置LED所使用的GPIO */
/* 1、获取设备节点:gpioled */
gpioled.nd = of_find_node_by_path("/gpioled");
if(gpioled.nd == NULL) {
printk("gpioled node not find!\r\n");
return -EINVAL;
}
/* 2.读取status属性 */
ret = of_property_read_string(gpioled.nd, "status", &str);
if(ret < 0)
return -EINVAL;
if (strcmp(str, "okay"))
return -EINVAL;
/* 3、获取compatible属性值并进行匹配 */
ret = of_property_read_string(gpioled.nd, "compatible", &str);
if(ret < 0) {
printk("gpioled: Failed to get compatible property\n");
return -EINVAL;
}
if (strcmp(str, "alientek,led")) {
printk("gpioled: Compatible match failed\n");
return -EINVAL;
}
/* 4、 获取设备树中的gpio属性,得到LED所使用的LED编号 */
gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);
if(gpioled.led_gpio < 0) {
printk("can't get led-gpio");
return -EINVAL;
}
printk("led-gpio num = %d\r\n", gpioled.led_gpio);
/* 5.向gpio子系统申请使用GPIO */
ret = gpio_request(gpioled.led_gpio, "LED-GPIO");
if (ret) {
printk(KERN_ERR "gpioled: Failed to request led-gpio\n");
return ret;
}
/* 6、设置PI0为输出,并且输出高电平,默认关闭LED灯 */
ret = gpio_direction_output(gpioled.led_gpio, 1);
if(ret < 0) {
printk("can't set gpio!\r\n");
}
/* 注册字符设备驱动 */
/* 1、创建设备号 */
if (gpioled.major) { /* 定义了设备号 */
gpioled.devid = MKDEV(gpioled.major, 0);
ret = register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME);
if(ret < 0) {
pr_err("cannot register %s char driver [ret=%d]\n", GPIOLED_NAME, GPIOLED_CNT);
goto free_gpio;
}
} else { /* 没有定义设备号 */
ret = alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME); /* 申请设备号 */
if(ret < 0) {
pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n", GPIOLED_NAME, ret);
goto free_gpio;
}
gpioled.major = MAJOR(gpioled.devid); /* 获取分配号的主设备号 */
gpioled.minor = MINOR(gpioled.devid); /* 获取分配号的次设备号 */
}
printk("gpioled major=%d,minor=%d\r\n",gpioled.major, gpioled.minor);
/* 2、初始化cdev */
gpioled.cdev.owner = THIS_MODULE;
cdev_init(&gpioled.cdev, &gpioled_fops);
/* 3、添加一个cdev */
cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);
if(ret < 0)
goto del_unregister;
/* 4、创建类 */
gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);
if (IS_ERR(gpioled.class)) {
goto del_cdev;
}
/* 5、创建设备 */
gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);
if (IS_ERR(gpioled.device)) {
goto destroy_class;
}
return 0;
destroy_class:
device_destroy(gpioled.class, gpioled.devid);
del_cdev:
cdev_del(&gpioled.cdev);
del_unregister:
unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);
free_gpio:
gpio_free(gpioled.led_gpio);
return -EIO;
}
/*
* @description : 驱动出口函数
* @param : 无
* @return : 无
*/
static void __exit led_exit(void)
{
/* 注销字符设备驱动 */
cdev_del(&gpioled.cdev);/* 删除cdev */
unregister_chrdev_region(gpioled.devid, GPIOLED_CNT); /* 注销设备号 */
device_destroy(gpioled.class, gpioled.devid);/* 注销设备 */
class_destroy(gpioled.class);/* 注销类 */
gpio_free(gpioled.led_gpio); /* 释放GPIO */
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ALIENTEK");
MODULE_INFO(intree, "Y");
atomicApp.c
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
/***************************************************************
Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
文件名 : atomicApp.c
作者 : 正点原子Linux团队
版本 : V1.0
描述 : 原子变量测试APP,测试原子变量能不能实现一次
只允许一个应用程序使用LED。
其他 : 无
使用方法 :./atomicApp /dev/gpioled 0 关闭LED灯
./atomicApp /dev/gpioled 1 打开LED灯
论坛 : www.openedv.com
日志 : 初版V1.0 2021/1/04 正点原子Linux团队创建
***************************************************************/
#define LEDOFF 0
#define LEDON 1
/*
* @description : main主程序
* @param - argc : argv数组元素个数
* @param - argv : 具体参数
* @return : 0 成功;其他 失败
*/
int main(int argc, char *argv[])
{
int fd, retvalue;
char *filename;
unsigned char cnt = 0;
unsigned char databuf[1];
if(argc != 3){
printf("Error Usage!\r\n");
return -1;
}
filename = argv[1];
/* 打开beep驱动 */
fd = open(filename, O_RDWR);
if(fd < 0){
printf("file %s open failed!\r\n", argv[1]);
return -1;
}
databuf[0] = atoi(argv[2]); /* 要执行的操作:打开或关闭 */
/* 向/dev/gpioled文件写入数据 */
retvalue = write(fd, databuf, sizeof(databuf));
if(retvalue < 0){
printf("LED Control Failed!\r\n");
close(fd);
return -1;
}
/* 模拟占用25S LED */
while(1) {
sleep(5);
cnt++;
printf("App running times:%d\r\n", cnt);
if(cnt >= 5) break;
}
printf("App running finished!");
retvalue = close(fd); /* 关闭文件 */
if(retvalue < 0){
printf("file %s close failed!\r\n", argv[1]);
return -1;
}
return 0;
}
查看板子 status 板子执行这条命令看实际设备树里面 status 值!
cat /proc/device-tree/gpioled/status
echo
cat /proc/device-tree/gpioled/compatible
echo
自旋锁
spinlock.c
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
/***************************************************************
Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
文件名 : gpioled.c
作者 : 正点原子Linux团队
版本 : V1.0
描述 : 自旋锁实验,使用自旋锁来实现对实现设备的互斥访问。
其他 : 无
论坛 : www.openedv.com
日志 : 初版V1.0 2021/1/4 正点原子Linux团队创建
***************************************************************/
#define GPIOLED_CNT 1 /* 设备号个数 */
#define GPIOLED_NAME "gpioled" /* 名字 */
#define LEDOFF 0 /* 关灯 */
#define LEDON 1 /* 开灯 */
/* gpioled设备结构体 */
struct gpioled_dev{
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
struct device_node *nd; /* 设备节点 */
int led_gpio; /* led所使用的GPIO编号 */
int dev_stats; /* 设备使用状态,0,设备未使用;>0,设备已经被使用 */
spinlock_t lock; /* 自旋锁 */
};
static struct gpioled_dev gpioled; /* led设备 */
/*
* @description : 打开设备
* @param - inode : 传递给驱动的inode
* @param - filp : 设备文件,file结构体有个叫做private_data的成员变量
* 一般在open的时候将private_data指向设备结构体。
* @return : 0 成功;其他 失败
*/
static int led_open(struct inode *inode, struct file *filp)
{
unsigned long flags;
filp->private_data = &gpioled; /* 设置私有数据 */
spin_lock_irqsave(&gpioled.lock, flags); /* 上锁 */
if (gpioled.dev_stats) { /* 如果设备被使用了 */
spin_unlock_irqrestore(&gpioled.lock, flags);/* 解锁 */
return -EBUSY;
}
gpioled.dev_stats++; /* 如果设备没有打开,那么就标记已经打开了 */
spin_unlock_irqrestore(&gpioled.lock, flags);/* 解锁 */
return 0;
}
/*
* @description : 从设备读取数据
* @param - filp : 要打开的设备文件(文件描述符)
* @param - buf : 返回给用户空间的数据缓冲区
* @param - cnt : 要读取的数据长度
* @param - offt : 相对于文件首地址的偏移
* @return : 读取的字节数,如果为负值,表示读取失败
*/
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
return 0;
}
/*
* @description : 向设备写数据
* @param - filp : 设备文件,表示打开的文件描述符
* @param - buf : 要写给设备写入的数据
* @param - cnt : 要写入的数据长度
* @param - offt : 相对于文件首地址的偏移
* @return : 写入的字节数,如果为负值,表示写入失败
*/
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
int retvalue;
unsigned char databuf[1];
unsigned char ledstat;
struct gpioled_dev *dev = filp->private_data;
retvalue = copy_from_user(databuf, buf, cnt); /* 接收APP发送过来的数据 */
if(retvalue < 0) {
printk("kernel write failed!\r\n");
return -EFAULT;
}
ledstat = databuf[0]; /* 获取状态值 */
if(ledstat == LEDON) {
gpio_set_value(dev->led_gpio, 0); /* 打开LED灯 */
} else if(ledstat == LEDOFF) {
gpio_set_value(dev->led_gpio, 1); /* 关闭LED灯 */
}
return 0;
}
/*
* @description : 关闭/释放设备
* @param - filp : 要关闭的设备文件(文件描述符)
* @return : 0 成功;其他 失败
*/
static int led_release(struct inode *inode, struct file *filp)
{
unsigned long flags;
struct gpioled_dev *dev = filp->private_data;
/* 关闭驱动文件的时候将dev_stats减1 */
spin_lock_irqsave(&dev->lock, flags); /* 上锁 */
if (dev->dev_stats) {
dev->dev_stats--;
}
spin_unlock_irqrestore(&dev->lock, flags);/* 解锁 */
return 0;
}
/* 设备操作函数 */
static struct file_operations gpioled_fops = {
.owner = THIS_MODULE,
.open = led_open,
.read = led_read,
.write = led_write,
.release = led_release,
};
/*
* @description : 驱动出口函数
* @param : 无
* @return : 无
*/
static int __init led_init(void)
{
int ret = 0;
const char *str;
/* 初始化自旋锁 */
spin_lock_init(&gpioled.lock);
/* 设置LED所使用的GPIO */
/* 1、获取设备节点:gpioled */
gpioled.nd = of_find_node_by_path("/gpioled");
if(gpioled.nd == NULL) {
printk("gpioled node not find!\r\n");
return -EINVAL;
}
/* 2.读取status属性 */
ret = of_property_read_string(gpioled.nd, "status", &str);
if(ret < 0)
return -EINVAL;
if (strcmp(str, "okay"))
return -EINVAL;
/* 3、获取compatible属性值并进行匹配 */
ret = of_property_read_string(gpioled.nd, "compatible", &str);
if(ret < 0) {
printk("gpioled: Failed to get compatible property\n");
return -EINVAL;
}
if (strcmp(str, "alientek,led")) {
printk("gpioled: Compatible match failed\n");
return -EINVAL;
}
/* 4、 获取设备树中的gpio属性,得到LED所使用的LED编号 */
gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);
if(gpioled.led_gpio < 0) {
printk("can't get led-gpio");
return -EINVAL;
}
printk("led-gpio num = %d\r\n", gpioled.led_gpio);
/* 5.向gpio子系统申请使用GPIO */
ret = gpio_request(gpioled.led_gpio, "LED-GPIO");
if (ret) {
printk(KERN_ERR "gpioled: Failed to request led-gpio\n");
return ret;
}
/* 6、设置PI0为输出,并且输出高电平,默认关闭LED灯 */
ret = gpio_direction_output(gpioled.led_gpio, 1);
if(ret < 0) {
printk("can't set gpio!\r\n");
}
/* 注册字符设备驱动 */
/* 1、创建设备号 */
if (gpioled.major) { /* 定义了设备号 */
gpioled.devid = MKDEV(gpioled.major, 0);
ret = register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME);
if(ret < 0) {
pr_err("cannot register %s char driver [ret=%d]\n", GPIOLED_NAME, GPIOLED_CNT);
goto free_gpio;
}
} else { /* 没有定义设备号 */
ret = alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME); /* 申请设备号 */
if(ret < 0) {
pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n", GPIOLED_NAME, ret);
goto free_gpio;
}
gpioled.major = MAJOR(gpioled.devid); /* 获取分配号的主设备号 */
gpioled.minor = MINOR(gpioled.devid); /* 获取分配号的次设备号 */
}
printk("gpioled major=%d,minor=%d\r\n",gpioled.major, gpioled.minor);
/* 2、初始化cdev */
gpioled.cdev.owner = THIS_MODULE;
cdev_init(&gpioled.cdev, &gpioled_fops);
/* 3、添加一个cdev */
cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);
if(ret < 0)
goto del_unregister;
/* 4、创建类 */
gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);
if (IS_ERR(gpioled.class)) {
goto del_cdev;
}
/* 5、创建设备 */
gpioled.device = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);
if (IS_ERR(gpioled.device)) {
goto destroy_class;
}
return 0;
destroy_class:
device_destroy(gpioled.class, gpioled.devid);
del_cdev:
cdev_del(&gpioled.cdev);
del_unregister:
unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);
free_gpio:
gpio_free(gpioled.led_gpio);
return -EIO;
}
/*
* @description : 驱动出口函数
* @param : 无
* @return : 无
*/
static void __exit led_exit(void)
{
/* 注销字符设备驱动 */
cdev_del(&gpioled.cdev);/* 删除cdev */
unregister_chrdev_region(gpioled.devid, GPIOLED_CNT); /* 注销设备号 */
device_destroy(gpioled.class, gpioled.devid);/* 注销设备 */
class_destroy(gpioled.class);/* 注销类 */
gpio_free(gpioled.led_gpio); /* 释放GPIO */
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ALIENTEK");
MODULE_INFO(intree, "Y");
编译驱动程序 编写 Makefile 文件,本章实验的 Makefile 文件和第四十章实验基本一样,只是将 obj-m 变 量的值改为 spinlock.o,Makefile 内容如下所示: Makefile 文件
KERNELDIR := /home/user/linux/atk-mp1/linux/my_linux/linux-5.4.31
CURRENT_PATH := $(shell pwd)
obj-m := spinlock.o
build: kernel_modules
kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
第 4 行,设置 obj-m 变量的值为 spinlock.o。 输入如下命令编译出驱动模块文件: make -j32 编译成功以后就会生成一个名为"spinlock.ko"的驱动模块文件。
spinlockApp.c
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
/***************************************************************
Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
文件名 : atomicApp.c
作者 : 正点原子Linux团队
版本 : V1.0
描述 : 自选锁测试APP,测试自选锁变量能不能实现一次
只允许一个应用程序使用LED。
其他 : 无
使用方法 :./atomicApp /dev/gpioled 0 关闭LED灯
./atomicApp /dev/gpioled 1 打开LED灯
论坛 : www.openedv.com
日志 : 初版V1.0 2021/1/04 正点原子Linux团队创建
***************************************************************/
#define LEDOFF 0
#define LEDON 1
/*
* @description : main主程序
* @param - argc : argv数组元素个数
* @param - argv : 具体参数
* @return : 0 成功;其他 失败
*/
int main(int argc, char *argv[])
{
int fd, retvalue;
char *filename;
unsigned char cnt = 0;
unsigned char databuf[1];
if(argc != 3){
printf("Error Usage!\r\n");
return -1;
}
filename = argv[1];
/* 打开beep驱动 */
fd = open(filename, O_RDWR);
if(fd < 0){
printf("file %s open failed!\r\n", argv[1]);
return -1;
}
databuf[0] = atoi(argv[2]); /* 要执行的操作:打开或关闭 */
/* 向/dev/gpioled文件写入数据 */
retvalue = write(fd, databuf, sizeof(databuf));
if(retvalue < 0){
printf("LED Control Failed!\r\n");
close(fd);
return -1;
}
/* 模拟占用25S LED */
while(1) {
sleep(5);
cnt++;
printf("App running times:%d\r\n", cnt);
if(cnt >= 5) break;
}
printf("App running finished!");
retvalue = close(fd); /* 关闭文件 */
if(retvalue < 0){
printf("file %s close failed!\r\n", argv[1]);
return -1;
}
return 0;
}
编译测试 APP 输入如下命令编译测试 atomicApp.c 这个测试程序: arm-none-linux-gnueabihf-gcc spinlockApp.c -o spinlockApp
信号量实验
本节我们使用信号量来实现一次只能有一个应用程序访问 LED 灯,信号量可以导致休眠, 因此信号量保护的临界区没有运行时间限制,可以在驱动的 open 函数申请信号量,然后在 release 函数中释放信号量。但是信号量不能用在中断中.
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ ./semaApp /dev/gpioled 0
App running times:-1097040768
App running times:-1097040768
App running times:-1097040768
App running finished!App running times:-1095050112
App running times:-1095050112
App running times:-1095050112
App running times:-1095050112
App running times:-1095050112
App running finished![3]+ Done ./semaApp /dev/gpioled 0
[root@ATK-stm32mp1]:/lib/modules/5.4.31$
[root@ATK-stm32mp1]:/lib/modules/5.4.31$
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ ./semaApp /dev/gpioled 0 &
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ ./semaApp /dev/gpioled 0App running times:-1095533440 1 &
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ App running times:-1095533440
App running times:-1095533440
App running times:-1095533440
App running times:-1095533440
3、运行测试 将 上 一 小 节 编 译 出 来 的 semaphore.ko 和 semaApp 这 两 个 文 件 拷 贝 到 rootfs/lib/modules/5.4.31 目录中,重启开发板,进入到目录 lib/modules/5.4.31 中,输入如下命令 加载 semaphore.ko 驱动模块:
depmod //第一次加载驱动的时候需要运行此命令
modprobe semaphore.ko //加载驱动
驱动加载成功以后就可以使用 semaApp 软件测试驱动是否工作正常,测试方法和 28.1.2 小 节中一样,先输入如下命令让 semaApp 软件模拟占用 25S 的 LED 灯:
./ semaApp /dev/gpioled 1& //打开 LED 灯
紧接着再输入如下命令关闭 LED 灯:
./ semaApp /dev/gpioled 0& //关闭 LED 灯
注意两个命令都是运行在后台,第一条命令先获取到信号量,因此可以操作 LED 灯,将 LED 灯打开,并且占有 25S。第二条命令因为获取信号量失败而进入休眠状态,等待第一条命 令运行完毕并释放信号量以后才拥有 LED 灯使用权,将 LED 灯关闭
如果要卸载驱动的话输入如下命令即可:
rmmod semaphore.ko
App running finished!rmmod semaphore.ko
[2]+ Done ./semaApp /dev/gpioled 1
[1]+ Done ./semaApp /dev/gpioled 0
[root@ATK-stm32mp1]:/lib/modules/5.4.31$
互斥体实验
最适合互斥的 就是互斥体 mutex 了。 mutex 实现对 LED 灯的互斥访问.
实验程序编写 1、修改设备树文件 本章实验是在上一节实验的基础上完成的,同样不需要对设备树做任何的修改。 2、LED 驱动修改 本节实验在第上一节实验驱动文件 semaphore.c 的基础上修改而来。新建名为"10_mutex"
要先卸载 rmmode atmic.ko 前面的加载
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ depmod
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ modprobe semaphore.ko
[24315.448914] led-gpio num = 0
[24315.450560] led-gpio num =83
[24315.453480] gpioled: Failed to request led-gpio
[24315.568634] led-gpio num = 0
[24315.570253] led-gpio num =83
[24315.573201] gpioled: Failed to request led-gpio
modprobe: can't load module semaphore.ko (semaphore.ko): Device or resource busy
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ lsmod
Module Size Used by Tainted: G
atomic 16384 0
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ rmmode atmic.ko
-sh: rmmode: not found
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ rmmod atomic.ko
[root@ATK-stm32mp1]:/lib/modules/5.4.31$
[root@ATK-stm32mp1]:/lib/modules/5.4.31$
[root@ATK-stm32mp1]:/lib/modules/5.4.31$
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ lsmod
Module Size Used by Tainted: G
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ modprobe semaphore.ko
[24391.689843] led-gpio num = 0
[24391.691387] led-gpio num =83
[24391.694455] gpioled major=241,minor=0
[root@ATK-stm32mp1]:~$ cd /lib/modules/5.4.31/
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ ls
aaaa beepApp led.ko mutex.ko
atomic.ko chrdevbase.ko ledApp mutexApp
atomicApp chrdevbaseApp modules.alias newchrled.ko
bbb dtsled.ko modules.dep semaApp
beep.ko led modules.symbols semaphore.ko
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ lsmod
Module Size Used by Tainted: G
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ depmod
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ modprobe mutex.ko
[ 99.402322] led-gpio num = 0
[ 99.403863] led-gpio num =83
[ 99.406928] gpioled major=241,minor=0
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ ./mutexApp /dev/gpioled 1&
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ ./mutexApp /dev/gpioled App running times:-1098351456
App running times:-1098351456
App running times:-1098351456
App running times:-1098351456
App running times:-1098351456
App running finished!
Error Unsage!
[1]+ Done ./mutexApp /dev/gpioled 1
[root@ATK-stm32mp1]:/lib/modules/5.4.31$
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ ./mutexApp /dev/gpioled 1&
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ ./mutexApp /dev/gpioled 0&
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ App running times:-1098769248
App running times:-1098769248
App running times:-1098769248
App running times:-1098769248
App running times:-1098769248
App running finished!App running times:-1098826592
App running times:-1098826592
App running times:-1098826592
App running times:-1098826592
App running times:-1098826592
App running finished!
[2]+ Done ./mutexApp /dev/gpioled 0
[1]+ Done ./mutexApp /dev/gpioled 1
[root@ATK-stm32mp1]:/lib/modules/5.4.31$
[root@ATK-stm32mp1]:/lib/modules/5.4.31$