Linux第92步_如何编写“设备树”下的platform设备驱动

Linux字符设备驱动,新字符设备驱动和设备树下的GPIO驱动,都是配置IO引脚所使用的GPIO寄存器,驱动开发方式和裸机没啥区别。Limux内核提供了pinctrl和gpio子系统用于GPIO驱动,借助它可简化GPIO驱动开发。

对GPIO进行读写操作,使用pinctrl和gpio子系统来实现,但像I2C、SPI、LCD这些复杂外设的驱动就要使用"设备树下的platform驱动"来进行开发。学完设备树下的platform驱动,说明你已经掌握linux下最常见的驱动编写方式。

Linux驱动开发不需要严格按照框架开发,但遵循框架可以大大简化开发的过程,提高代码的可维护性。‌Linux驱动开发遵循一定的框架和规范,主要是为了确保驱动的稳定性和兼容性。Linux驱动框架为开发者提供了一套标准的接口和函数,使得驱动的开发更加规范化和模块化。例如,字符设备驱动、块设备驱动和网络设备驱动都有各自的规范和标准,开发者需要实现特定的函数,如:open、close、write和read等‌。

platform设备驱动,也叫平台设备驱动。

对于STM32MP1来说,若要编写"设备树"下的platform设备驱动,需要先修改"pinctrl-stm32.c"这个文件,否则在使用pinctrl的时候,会提示GPIO引脚无法申请到。

1、使用Vscode打开文件夹,见下图:

修改"pinctrl-stm32.c"文件

1)、打开虚拟机上"VSCode",点击"文件",点击"打开文件夹",点击"zgq",点击"linux",点击"atk-mp1",点击"linux",点击"my_linux",点击"linux-5.4.31",点击"确定",点击"转到",点击"转到文件"。见下图:

2)、输入"pinctrl-stm32.c",得到下图:

3)、点击文本框下面的"pinctrl-stm32.c",就可以打开这个文件了。

4)、点击"编辑",点击"查找",在弹出的文本框中输入"pinmux_ops stm32_pmx_ops",就会显示"pinmux_ops stm32_pmx_ops结构",见下图:

5)、将".strict= true"修改为".strict= false",见下图的红线框:

6)、点击"文件",点击"保存"

7)、在VSCode终端,输入"make uImage dtbs LOADADDR=0XC2000040 -j8

回车",执行编译"Image"和"dtbs",并指定装载的起始地址为0XC2000040,j8表示指定采用8线程执行。"make dtbs",用来指定编译设备树。

2、创建"引脚的pinctl节点"为"led_pins_a"

1)、点击"转到",点击"转到文件",在文本框中输入"stm32mp15-pinctrl.dtsi",见下图:

2)、点击"文件结果"前面的"stm32mp15-pinctrl.dtsi",就可以打开这个文件了。见下图:

3)、点击"编辑",点击"查找",再弹出的文本框中输入"&pinctrl",然后按一下"回车",就会跳至"pinctrl"节点。

见下图:

4)、输入内容如下:

led_pins_a: gpioled-0 {

pins {

pinmux = <STM32_PINMUX('I', 0, GPIO)>;

/*设置 PI0复用为GPIO功能*/

drive-push-pull;/*设置PI0为推挽输出*/

bias-pull-up; /*设置PI0为内部上拉*/

utput-high; /*设置PI0默认输出为高电平*/

slew-rate = <0>;/*设置PI0的速度为0档,也就是最慢*/

};

};

这是led的"pinctrl节点",见下图:

pinmux = <STM32_PINMUX('I', 0, GPIO)>;表示将PI0复用GPIO

pinmux = <STM32_PINMUX('F', 14, ANALOG)>;表示将PF14复用为ADC2_in6

pinmux = <STM32_PINMUX('F', 13, AF6)>;

表示将PF13重新映射到DFSDM1_DATIN3引脚;

pinmux = <STM32_PINMUX('H', 2, AF14)>;表示将PH2重映射到LCD_R0引脚;

3、检查引脚是否被其他设备复用

STM32MP1的一个引脚可以复用为多种功能,由于"stm32mp15-pimctrl.dtsi"是ST公司根据自己的开发板编写的,因此PI0这个引脚就可能被ST公司用作其他功能。因此,我们需要修改这个PI0引脚。

1)、打开虚拟机上"VSCode",点击"文件",点击"打开文件夹",点击"zgq",点击"linux",点击"atk-mp1",点击"linux",点击"my_linux",点击"linux-5.4.31",点击"确定",点击"转到",点击"转到文件",在文本框中输入"stm32mp15-pinctrl.dtsi"。

2)、点击"文件结果"前面的"stm32mp15-pinctrl.dtsi",就可以打开这个文件了。

3)、点击"编辑",点击"查找",再弹出的文本框中输入"STM32_PINMUX('I', 0,",然后按一下"回车",就会跳至"STM32_PINMUX('I', 0,",见下图:

4)、将上图中,画红框中的语句屏蔽掉,确保所使用的设备树中,一个引脚只复用为一个功能!

4、检查 GPIO是否被其他设备占用

检查引脚是否被其他设备复用后,然后检查 GPIO是否被其他设备占用。因为我们是在ST官方提供的设备树上修改的,因此还要检査一下当PI0作为GPIO的时候,ST官方是否将这个GPIO分配给其他设备。其实对于PI0这个引脚来说不会的,因为ST官方将其复用为了LCD_G5,所以也就不存在说将其在作为GPIO分配给其他设备。但是我们在实际开发中要考虑到这一点,说不定其他的引脚就会被分配给某个设备做GPIO,而我们没有检查,导致两个设备共用一个GPIO,那么肯定有一个因为申请不到GPIO而导致驱动无法工作。所以当我们将一个引脚用作GPIO的时候,一定要检査一下"当前设备树"里面是否有其他设备也使用到了这个GPIO,保证设备树中只有一个设备树在使用这个GPIO。

5、在设备树文件中创建"设备节点"为"gpioled"

1)、打开虚拟机上"VSCode",点击"文件",点击"打开文件夹",点击"zgq",点击"linux",点击"atk-mp1",点击"linux",点击"my_linux",点击"linux-5.4.31",点击"确定",点击"转到",点击"转到文件",在文本框中输入"stm32mp157d-atk.dts"。打开这个设备树文件,见下图:

2)、输入内容如下:

gpioled {

compatible = "zhang,led";

pinctrl-names = "default";

status = "okay";

pinctrl-0 = <&led_pins_a>;

/*表示gpioled节点的父节点为led_pins_a*/

led-gpio = <&gpioi 0 GPIO_ACTIVE_LOW>;

/*PI0默认输出低电平,这个0表示端口引脚的下标*/

};

注意:在编写platfonm驱动的时候of_match_table属性表中要有"zhang,led"。

见下图的红方框:

注意:在编写platfonm驱动的时候of_match_table属性表中要有"zhang,led"。

6、编译

1)、在VSCode终端,输入"cd linux-5.4.31/回车"

输入"make uImage dtbs LOADADDR=0XC2000040 -j8回车",执行编译"Image"和"dtbs",并指定装载的起始地址为0XC2000040,j8表示指定采用8线程执行。"make dtbs",用来指定编译设备树。

2)、输入"ls arch/arm/boot/uImage -l"

查看是否生成了新的"uImage"文件

3)、输入"ls arch/arm/boot/dts/stm32mp157d-atk.dtb -l"

查看是否生成了新的"stm32mp157d-atk.dtb"文件

拷贝输出的文件:

4)、输入"cp arch/arm/boot/uImage /home/zgq/linux/atk-mp1/linux/bootfs/ -f回车",执行文件拷贝,准备烧录到EMMC;

5)、输入"cp arch/arm/boot/dts/stm32mp157d-atk.dtb /home/zgq/linux/atk-mp1/linux/bootfs/ -f回车",执行文件拷贝,准备烧录到EMMC

6)、输入"cp arch/arm/boot/uImage /home/zgq/linux/tftpboot/ -f回车",执行文件拷贝,准备从tftp下载;

7)、输入"cp arch/arm/boot/dts/stm32mp157d-atk.dtb /home/zgq/linux/tftpboot/ -f回车",执行文件拷贝,准备从tftp下载;

8)、输入"ls -l /home/zgq/linux/atk-mp1/linux/bootfs/回车",查看"/home/zgq/linux/atk-mp1/linux/bootfs/"目录下的所有文件和文件夹

9)、输入"ls -l /home/zgq/linux/tftpboot/回车",查看"/home/zgq/linux/tftpboot/"目录下的所有文件和文件夹

输入"chmod 777 /home/zgq/linux/tftpboot/stm32mp157d-atk.dtb回车"

给"stm32mp157d-atk.dtb"文件赋予可执行权限

输入"chmod 777 /home/zgq/linux/tftpboot/uImage回车" ,给"uImage"文件赋予可执行权限

输入"ls /home/zgq/linux/tftpboot/回车",查看"/home/zgq/linux/tftpboot/"

8、编写"platform驱动"

"引脚的pinctl节点led_pins_a" 和"设备节点gpioled"创建完成后,就表示"设备已经准备好了",接下来就要编写"platform驱动"。

1)、创建Platform_GpioLED目录

输入"cd /home/zgq/linux/Linux_Drivers/回车"

切换到"/home/zgq/linux/Linux_Drivers/"

输入"ls回车",查看"/home/zgq/linux/Linux_Drivers/"

输入"mkdir Platform_GpioLED回车",创建"Platform_GpioLED"目录

输入"ls回车",查看"/home/zgq/linux/Linux_Drivers/"

2)、新建Platform_GpioLED_Driver.c

打开虚拟机上"VSCode",点击"文件",点击"打开文件夹",点击"zgq",点击"linux",点击"Linux_Drivers",点击"Platform_GpioLED"。

3)、点击"确定"。点击"文件",点击"新建文件",点击"文件",点击"另存为"。然后在"名称"右边的文本框中输入"Platform_GpioLED_Driver.c",点击"保存"

Platform_GpioLED_Driver.c文件内容如下:

#include <linux/types.h>

/* 数据类型重命名 使能bool,u8,u16,u32,u64, uint8_t, uint16_t, uint32_t, uint64_t 使能s8,s16,s32,s64,int8_t,int16_t,int32_t,int64_t */

#include <linux/ide.h> //使能copy_from_user(),copy_to_user()

#include <linux/module.h> //使能LEDDriver_init(),LEDDriver_exit()

#include <linux/gpio.h>

//使能gpio_request(),gpio_free(),gpio_direction_input(),

//使能gpio_direction_output(),gpio_get_value(),gpio_set_value()

#include <linux/cdev.h> //使能cdev结构

#include <linux/device.h>//使能class结构和device结构

#include <linux/of_gpio.h>

//使能of_gpio_named_count(),of_gpio_count(),of_get_named_gpio()

#include <linux/fs.h> //使能fasync_struct结构

#include <linux/platform_device.h> //使能platform_driver结构

#define LEDDEV_CNT 1 //设备数量

#define LEDDEV_NAME "platform_led" //定义设备的名字

#define LEDOFF 0

#define LEDON 1

/* leddev设备结构体 */

struct leddev_dev{

dev_t devid; /*设备号*/

struct cdev cdev; /*cdev*/

struct class *class; /*类*/

struct device *device; /*设备*/

struct device_node *node;/*LED设备节点*/

int gpio_led; /*LED灯GPIO标号*/

};

struct leddev_dev leddev; /* led设备 */

//函数功能:sta=LEDON表示开灯, sta=LEDOFF表示关灯

void led_switch(u8 sta)

{

if (sta == LEDON ) gpio_set_value(leddev.gpio_led,0);

//设置输出低电平

else if (sta == LEDOFF) gpio_set_value(leddev.gpio_led,1);

//设置输出高电平

}

static int led_gpio_init(struct device_node *nd)

{

int ret;

/* 从设备树中获取GPIO */

leddev.gpio_led = of_get_named_gpio(nd, "led-gpio", 0);

//在gpio_led节点中,led-gpio = <&gpioi 0 GPIO_ACTIVE_LOW>

//nd是指定的"设备节点"

//propname="led-gpio",给定要读取的属性名字

//Index=0,给定的GPIO索引为0

//返回值:正值,获取到的GPIO编号;负值,失败。

if(!gpio_is_valid(leddev.gpio_led)) {

printk(KERN_ERR "leddev: Failed to get led-gpio\n");

return -EINVAL;

}

/* 申请使用GPIO */

ret = gpio_request(leddev.gpio_led, "LED0");

//gpio=leddev.gpio_led,指定要申请的"gpio编号"

//Label="LED0",给这个gpio引脚设置个名字为"LED0"

//返回值:0,申请"gpio编号"成功;其他值,申请"gpio编号"失败;

if (ret)

{

printk(KERN_ERR "led: Failed to request led-gpio\n");

return ret;

}

/* 将GPIO设置为输出模式并设置GPIO初始电平状态 */

gpio_direction_output(leddev.gpio_led,1);

//设置LED引脚输出高电平

return 0;

}

/* 打开设备 */

static int led_open(struct inode *inode, struct file *filp)

{

return 0;

}

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;

retvalue = copy_from_user(databuf, buf, cnt);

//将buf[]中的前cnt个字节拷贝到databuf[]中

if(retvalue < 0) {

printk("kernel write failed!\r\n");

return -EFAULT;

}

ledstat = databuf[0];

if (ledstat == LEDON) { led_switch(LEDON); }

else if (ledstat == LEDOFF) { led_switch(LEDOFF); }

return 0;

}

/* 设备操作函数 */

/*声明file_operations结构变量led_fops*/

/*它是指向设备操作函数的集合的变量*/

static struct file_operations led_fops = {

.owner = THIS_MODULE,

.open = led_open,

.write = led_write,

};

//函数功能:

//flatform驱动的probe函数,当驱动与设备匹配以后此函数就会被执行

//参数dev指向platform设备

//返回值为0,表示成功;其他负值,失败

static int led_probe(struct platform_device *pdev)

{

int ret;

printk("led driver and device was matched!\r\n");

/* 初始化 LED */

ret = led_gpio_init(pdev->dev.of_node);

if(ret < 0) return ret;

/* 1、设置设备号 */

ret = alloc_chrdev_region(&leddev.devid, 0, LEDDEV_CNT, LEDDEV_NAME);

//注册字符设备驱动

//leddev.devid:保存申请到的设备号

//0:次设备号的起始地址

//LEDDEV_CNT:要申请的设备数量;

//LEDDEV_NAME:表示"设备名字"

if(ret < 0)

{

pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n", LEDDEV_NAME, ret);

goto free_gpio;

}

/* 2、初始化cdev */

leddev.cdev.owner = THIS_MODULE;

cdev_init(&leddev.cdev, &led_fops);

//初始化字符设备

//leddev.cdev是等待初始化的结构体变量

//led_fops就是字符设备文件操作函数集合

/* 3、添加一个cdev */

ret = cdev_add(&leddev.cdev, leddev.devid, LEDDEV_CNT);

//添加字符设备

// &leddev.cdev表示指向要添加的字符设备,即字符设备结构testcdev变量

//leddev.devid表示设备号

//LEDDEV_CNT表示需要添加的设备数量

if(ret < 0) goto del_unregister;

/* 4、创建类*/

leddev.class = class_create(THIS_MODULE, LEDDEV_NAME);

//创建类 //使用THIS_MODULE将owner指针指向当前这个模块 //LEDDEV_NAME是类名字 //返回值是指向结构体class的指针,也就是创建的类

if (IS_ERR(leddev.class)) goto del_cdev;

/* 5、创建设备*/

leddev.device = device_create(leddev.class, NULL, leddev.devid, NULL, LEDDEV_NAME);

//创建设备

//设备要创建在leddev.class类下面

//NULL表示没有父设备

//leddev.devid是设备号;

//参数drvdata=NULL,设备没有使用数据

//LEDDEV_NAME是设备名字

//如果设置fmt=LEDDEV_NAME的话,就会生成/dev/LEDDEV_NAME设备文件。

//返回值就是创建好的设备。

if (IS_ERR(leddev.device)) goto destroy_class;

return 0;

destroy_class:

class_destroy(leddev.class);

//注销类,删除类

//leddev.class就是要删除的类

del_cdev:

cdev_del(&leddev.cdev);

//删除字符设备

//&leddev.cdev表示指向需要删除的字符设备,即字符设备结构leddev.cdev变量

del_unregister:

unregister_chrdev_region(leddev.devid, LEDDEV_CNT);

/* 释放设备号 */

//leddev.devid:需要释放的设备号

//LEDDEV_CNT:需要释放的次设备号数量;

free_gpio:

gpio_free(leddev.gpio_led);

/* 注销GPIO,leddev.gpio_led:要释放的"gpio编号"*/

return -EIO;

}

/*

//用来移除platform驱动

//dev指向platform设备

//返回值为0,成功;其他负值,失败

*/

static int led_remove(struct platform_device *dev)

{

gpio_set_value(leddev.gpio_led, 1);/*先关闭LED,再卸载驱动*/

gpio_free(leddev.gpio_led);

/* 注销GPIO,leddev.gpio_led:要释放的"gpio编号"*/

cdev_del(&leddev.cdev);

//删除字符设备

//&leddev.cdev表示指向需要删除的字符设备,即字符设备结构leddev.cdev变量

unregister_chrdev_region(leddev.devid, LEDDEV_CNT);

/*注销设备号,释放设备号 */

//leddev.devid:需要释放的设备号

//LEDDEV_CNT:需要释放的次设备号数量;

device_destroy(leddev.class, leddev.devid);

/* 注销设备 */

//删除创建的设备

//参数leddev.class是设备所处的类,leddev.devid是设备号

class_destroy(leddev.class);

//注销类,删除类

//leddev.class就是要删除的类

return 0;

}

/* 匹配列表 */

//驱动中的compatible属性要和和设备树中的compatible属性相匹配。

static const struct of_device_id led_of_match[] = {

{ .compatible = "zhang,led" /*这是驱动中的compatible属性*/},

{/*这是一个空元素,在编写of_device_id时最后一个元素一定要为空*/ }

};

MODULE_DEVICE_TABLE(of, led_of_match);

//声明led_of_match为设备匹配表

/* platform驱动结构体 */

static struct platform_driver led_driver = {

.driver = {

.name = "stm32mp1-led",

/* 驱动名字stm32mp1-led.ko,用于和设备匹配 */

.of_match_table = led_of_match,

/*of_match_table匹配列表为led_of_match*/

},

.probe = led_probe, /*调用led_probe()函数*/

.remove = led_remove,/*调用led_remove()函数*/

};

/*驱动的入口函数*/

static int __init leddriver_init(void)

{

return platform_driver_register(&led_driver);

//向Linux内核注册一个platform驱动

//led_driver:要注册的 platform 驱动

//返回值:负数,失败;0,成功;

}

/*驱动的出口函数 */

static void __exit leddriver_exit(void)

{

platform_driver_unregister(&led_driver);

//卸载一个platform驱动

//led_driver:要卸载的platform驱动

}

module_init(leddriver_init);

//声明leddriver_init()为驱动入口函数,注意module_init为小写字母;

module_exit(leddriver_exit);

//声明leddriver_exit()为驱动出口函数,注意module_init为小写字母;

MODULE_LICENSE("GPL"); //LICENSE采用"GPL协议"

MODULE_AUTHOR("zgq"); //添加作者名字

MODULE_DESCRIPTION("This is Platform_Driver_Test_Module!");//模块介绍

MODULE_INFO(intree, "Y");

//去除显示"loading out-of-tree module taints kernel."

9、新建PLATFORM_GPIOLED_APP.c

PLATFORM_GPIOLED_APP.c文件内容如下:

#include "stdio.h"

#include "unistd.h"

#include "sys/types.h"

#include "sys/stat.h"

#include "fcntl.h"

#include "stdlib.h"

#include "string.h"

//APP运行命令:./PLATFORM_GPIOLED_APP MyPLATFORM_GPIOLED 1表示打开LED

//APP运行命令:./PLATFORM_GPIOLED_APP MyPLATFORM_GPIOLED 0表示关闭LED

#define LEDOFF 0 /* 关灯 */

#define LEDON 1 /* 开灯 */

/*

参数argc: argv[]数组元素个数

参数argv[]:是一个指针数组

返回值: 0 成功;其他 失败

*/

int main(int argc, char *argv[])

{

int fd, retvalue;

char *filename;

unsigned char databuf[1];

if(argc != 3)

{

printf("Error Usage!\r\n");

return -1;

}

//argv[]是指向输入参数"./PLATFORM_GPIOLED_App" "/dev/MyPLATFORM_GPIOLED" "1"

filename = argv[1];

//argv[1]指向字符串"/dev/MyPLATFORM_GPIOLED"

fd = open(filename, O_RDWR);

//如果打开"/dev/PLATFORM_GPIOLED"文件成功,则fd为"文件描述符"

//fd=0表示标准输入流; fd=1表示标准输出流;fd=2表示错误输出流;

if(fd < 0)

{

printf("Can't open file %s\r\n", filename);

return -1;

}

databuf[0]= atoi(argv[2]); /* 写入的数据,是数字的,表示打开或关闭 */

retvalue = write(fd, databuf, 1);

//将databuf[]中前1个字节发送给用户

//返回值大于0表示写入的字节数;

//返回值等于0表示没有写入任何数据;

//返回值小于0表示写入失败

if(retvalue < 0)

{

printf("write file %s failed!\r\n", filename);

close(fd);

//fd表示要关闭的"文件描述符"

//返回值等于0表示关闭成功

//返回值小于0表示关闭失败

return -1;

}

/* 关闭设备 */

retvalue = close(fd);

//fd表示要关闭的"文件描述符"

//返回值等于0表示关闭成功

//返回值小于0表示关闭失败

if(retvalue < 0)

{

printf("Can't close file %s\r\n", filename);

return -1;

}

return 0;

}

10、新建Makefile

Makefile文件如下:

KERNELDIR := /home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31

#使用":="将其后面的字符串赋值给KERNELDIR

CURRENT_PATH := $(shell pwd)

#采用"shell pwd"获取当前打开的路径

#使用"$(变量名)"引用"变量的值"

MyAPP := PLATFORM_GPIOLED_APP

MyPLATFORM_GPIOLED-objs = Platform_GpioLED_Driver.o

obj-m := MyPLATFORM_GPIOLED.o

CC := arm-none-linux-gnueabihf-gcc

drv:

(MAKE) -C (KERNELDIR) M=$(CURRENT_PATH) modules

app:

(CC) (MyAPP).c -o $(MyAPP)

clean:

(MAKE) -C (KERNELDIR) M=$(CURRENT_PATH) clean

rm $(MyAPP)

install:

sudo cp *.ko $(MyAPP) /home/zgq/linux/nfs/rootfs/lib/modules/5.4.31/ -f

11、添加"c_cpp_properties.json"

按下"Ctrl+Shift+P",打开VSCode控制台,然后输入"C/C++:Edit Configurations(JSON)",打开以后会自动在".vscode "目录下生成一个名为"c_cpp_properties.json" 的文件。

修改c_cpp_properties.json内容如下所示:

{

"configurations": [

{

"name": "Linux",

"includePath": [

"${workspaceFolder}/**",

"/home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31",

"/home/zgq/linux/Linux_Drivers",

"/home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31/arch/arm/include",

"/home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31/include",

"/home/zgq/linux/atk-mp1/linux/my_linux/linux-5.4.31/arch/arm/include/generated"

],

"defines": [],

"compilerPath": "/usr/bin/gcc",

"cStandard": "gnu11",

"cppStandard": "gnu++14",

"intelliSenseMode": "gcc-x64"

}

],

"version": 4

}

12、编译

输入"make clean回车"

输入"make drv回车"

输入"make app回车"

输入"make install回车"

输入"ls /home/zgq/linux/nfs/rootfs/lib/modules/5.4.31/ -l回车"查看是存在"PLATFORM_GPIOLED_APP和MySpinlockLED_Module.ko"

13)、拷贝驱动

输入"cd /home/zgq/linux/Linux_Drivers/Platform_GpioLED/"

输入"ls"

输入"sudo cp MyPLATFORM_GPIOLED.ko /home/zgq/linux/nfs/rootfs/lib/modules/5.4.31"

输入密码"123456回车"

输入"sudo cp PLATFORM_GPIOLED_APP /home/zgq/linux/nfs/rootfs/lib/modules/5.4.31"

输入"cd /home/zgq/linux/nfs/rootfs/lib/modules/5.4.31"

输入"ls -l"

14)、测试

启动开发板,从网络下载程序

输入"root"

输入"cd /lib/modules/5.4.31/"

在nfs挂载中,切换到"/lib/modules/5.4.31/"目录,

注意:"lib/modules/5.4.31/"在虚拟机中是位于"/home/zgq/linux/nfs/rootfs/"目录下,但在开发板中,却是位于根目录中。

输入"ls"

输入"depmod",驱动在第一次执行时,需要运行"depmod"

输入"lsmod"查看有哪些驱动在工作;

若MyPLATFORM_GPIOLED.ko在工作,则输入"rmmod MyPLATFORM_GPIOLED.ko回车"卸载"MyPLATFORM_GPIOLED.ko"

输入"modprobe MyPLATFORM_GPIOLED.ko",加载"MyPLATFORM_GPIOLED.ko"模块

输入"cd /sys/bus/platform/drivers/"

切换到"/sys/bus/platform/drivers/"目录。

我们在Platform_GpioLED_Driver.c中设置led_driver 中的".name"字段为"stm32mp1-led",因此在"/sys/bus/platform/drivers/"目录下存在名为"stmm32mpl-led"这个文件。同理,"/sys/bus/platform/devices/"目录下也存在gpioled的设备文件,也就是设备树中gpioled这个节点。

输入"ls stm32mp1-led -l回车"

输入"cd /sys/bus/platform/devices/"

切换到"/sys/bus/platform/devices/"目录。

输入"ls gpioled -l回车"

输入"cd /lib/modules/5.4.31/"

输入"ls /dev/platform_led -l回车",发现节点文件"/dev/platform_led"

输入"./PLATFORM_GPIOLED_APP /dev/platform_led 1回车"执行写1开灯

输入"./PLATFORM_GPIOLED_APP /dev/platform_led 0回车"执行写0关灯

操作完成,则执行卸载模块:

输入"rmmod MyPLATFORM_GPIOLED.ko",卸载"MyPLATFORM_GPIOLED.ko"模块

注意:输入"rmmod platform_led"也可以卸载"MyPLATFORM_GPIOLED.ko"模块

输入"lsmod"查看有哪些驱动在工作。

输入"modprobe MyPLATFORM_GPIOLED.ko",加载"MyPLATFORM_GPIOLED.ko"模块

输入"cat /proc/devices回车"查询设备号

platform_led的设备号为241

相关推荐
Ronin30527 分钟前
【Linux系统】进程间通信:System V IPC——共享内存
linux·服务器·system v 共享内存
网硕互联的小客服1 小时前
Apache 如何支持SHTML(SSI)的配置方法
运维·服务器·网络·windows·php
etcix1 小时前
implement copy file content to clipboard on Windows
windows·stm32·单片机
谱写秋天1 小时前
在STM32F103上进行FreeRTOS移植和配置(STM32CubeIDE)
c语言·stm32·单片机·freertos
基于python的毕设1 小时前
C语言栈的实现
linux·c语言·ubuntu
落日漫游2 小时前
K8s核心组件全解析
运维·docker·运维开发
luoqice2 小时前
linux下找到指定目录下最新日期log文件
linux·算法
林开落L2 小时前
库的制作与原理
linux·开发语言·动静态库·库的制作
2401_888423093 小时前
Linux软件编程-线程(2)
linux
共享家95274 小时前
linux-数据链路层
linux·网络·macos