当前位置: 首页 > article >正文

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

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

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

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

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_APPMySpinlockLED_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


http://www.kler.cn/a/396383.html

相关文章:

  • vue项目PC端和移动端实现在线预览pptx文件
  • 什么是项目完整性管理?
  • AI大模型(二):AI编程实践
  • NPOI 实现Excel模板导出
  • 【鸿蒙开发】第十一章 Stage模型应用组件-任务Mission
  • 在 Oracle Linux 8.9 上安装Oracle Database 23ai 23.5
  • STM32 串口输出调试信息
  • watch监听事件未生效
  • 网络动力学
  • 飞创直线电机模组 VS 传统丝杆模组:谁是自动化传动领域的王者?
  • HarmonyOS ArkTs 解决流式传输编码问题
  • 一文1800字使用Jmeter进行http接口性能测试!
  • Vue Shop Vite官网、Vue Admin Plus官网、前端框架、演示地址、源码、文档
  • Swift从0开始学习 函数和闭包 day2
  • PostgreSQL 数据加密和数据解密
  • Vue 学习随笔系列十五 -- 数组遍历方法
  • 基于VUE实现语音通话:边录边转发送语言消息、 播放pcm 音频
  • Vue.js 前端框架入门
  • Python学习从0到1 day27 Python 高阶技巧 ③ 设计模式 — 单例模式
  • Wi-Fi背后的工作原理与技术发展历程介绍【无线通信小百科】
  • 柯桥生活英语口语学习“面坨了”英语怎么表达?
  • Ubuntu联网问题处理
  • springboot的依赖实现原理:spring-boot-starter-parent解析
  • P3-3.【结构化程序设计】第三节——知识要点:while语句、do-while语句和for语句
  • 移植LVGL8.2以及移植过程的理解
  • Element表格show-overflow-tooltip属性