Linux第79步_使用自旋锁保护某个全局变量来实现“互斥访问”共享资源

news2024/11/24 16:18:02

自旋锁使用注意事项:自旋锁保护的“临界区”要尽可能的短

因此,在open()函数中申请“spinlock_t自旋锁结构变量”,然后在release()函数中释放“spinlock_t自旋锁结构变量”,这种方法就行不通了。如果使用一个变量“dev_stats”来表示“共享资源的使用标志”,则“dev_stats > 0”,表示共享资源已经被使用,而“dev_stats = 0”表示允许使用共享资源。因此,真正实现设备互斥访问的是变量“dev_stats”,我们用自旋锁对“dev_stats”来做保护,就可以实现“互斥访问”共享资源。

1、创建MySpinlockLED目录

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

切换到“/home/zgq/linux/Linux_Drivers/”目录

输入“mkdir MySpinlockLED回车”,创建“MySpinlockLED”目录

输入“ls回车”查看“/home/zgq/linux/Linux_Drivers/”目录下的文件和文件夹

2、添加gpio_led节点

若在stm32mp157d-atk.dts文件中的根节点下没有gpio_led节点,则添加gpio_led节点

3、编译设备树

1)、在VSCode终端,输入“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/”目录下的所有文件和文件夹

4、创建LED.c

#include "LED.h"

#include <linux/gpio.h>

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

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

#include <linux/of_gpio.h>

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

struct MySpinlockLED_dev  strMySpinlockLED;

int Get_gpio_num(void);

int led_GPIO_request(void);

void led_switch(u8 sta,struct MySpinlockLED_dev *dev);

int Get_gpio_num(void)

{

  int ret = 0;

  const char *str;

  /* 设置LED所使用的GPIO */

  /* 1、获取设备节点:strMySpinlockLED */

  strMySpinlockLED.nd = of_find_node_by_path("/gpio_led");

  //path="/gpio_led,使用“全路径的节点名“在“stm32mp157d-atk.dts“中查找节点“gpio_led”

  //返回值:返回找到的节点,如果为NULL,表示查找失败。

  if(strMySpinlockLED.nd == NULL) {

    printk("gpio_led node not find!\r\n");

    return -EINVAL;

  }

  /* 2.读取status属性 */

  ret = of_property_read_string(strMySpinlockLED.nd, "status", &str);

  //在gpio_led节点中,status = "okay";

  //指定的设备节点strMySpinlockLED.nd

  //proname="status",给定要读取的属性名字

  //out_string=str:返回读取到的属性值

  //返回值:0,读取成功,负值,读取失败。

  if(ret < 0) return -EINVAL;

  if (strcmp(str, "okay")) return -EINVAL;

  //strcmp(s1,s2),当s1<s2时,返回值为负数

  //strcmp(s1,s2),当s1>2时,返回值为正数

  //strcmp(s1,s2),当s1=s2时,返回值为0

  /* 3、获取compatible属性值并进行匹配 */

  ret = of_property_read_string(strMySpinlockLED.nd, "compatible", &str);

  //在gpio_led节点中,compatible = "zgq,led";

  //指定的设备节点strMySpinlockLED.nd

  //proname="compatible",给定要读取的属性名字

  //out_string=str:返回读取到的属性值

  //返回值:0,读取成功,负值,读取失败。

  if(ret < 0) {

    printk("gpio_led node: Failed to get compatible property\n");

    return -EINVAL;

  }

  if (strcmp(str, "zgq,led")) {

    printk("gpio_led node: Compatible match failed\n");

    return -EINVAL;

  }

  /* 4、 根据设备树中的"led-gpio"属性,得到LED所使用的LED编号 */

  strMySpinlockLED.led_gpio = of_get_named_gpio(strMySpinlockLED.nd, "led-gpio", 0);

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

  //np=strMySpinlockLED.nd,指定的“设备节点”

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

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

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

  if(strMySpinlockLED.led_gpio < 0) {

    printk("can't get led-gpio");

    return -EINVAL;

  }

  printk("led-gpio num = %d\r\n", strMySpinlockLED.led_gpio);

  //打印结果为:“led-gpio num = 128“

  //因为GPIO编号是从0开始的,GPIOI端口的序号是8,每个端口有16个IO口,因此GPIOI0的编号为8*16=128

  return 0;

}

int led_GPIO_request(void)

{

  int ret = 0;

  /* 5.向gpio子系统申请使用“gpio编号” */

  ret = gpio_request(strMySpinlockLED.led_gpio, "LED-GPIO");

  //gpio=strMySpinlockLED.led_gpio,指定要申请的“gpio编号”

  //Iabel="LED-GPIO",给这个gpio引脚设置个名字为"LED-GPIO"

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

  if (ret) {

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

    return ret;

  }

  /* 6、设置PI0为输出,并且输出高电平,默认关闭LED灯 */

  ret = gpio_direction_output(strMySpinlockLED.led_gpio, 1);

  //gpio=strMySpinlockLED.led_gpio,指定的“gpio编号”,这里是128,对应的是GI0引脚

  //value=1,设置引脚输出高电平

  //返回值:0,设置“引脚输出为vakued的值”成功;负值,设置“引脚输出为vakued的值”失败。

  if(ret < 0) {

    printk("can't set gpio!\r\n");

  }

  return 0;

}

void led_switch(u8 sta,struct MySpinlockLED_dev *dev)

{

if(sta == LEDON) {

    gpio_set_value(dev->led_gpio, 0); /* 打开LED灯 */

}

else if(sta == LEDOFF) {

    gpio_set_value(dev->led_gpio, 1); /* 关闭LED灯 */

}

}

5、创建LED.h

#ifndef __LED_H

#define __LED_H

#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/cdev.h> //使能cdev结构

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

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

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

#define LEDOFF 0 /* 关灯 */

#define LEDON 1 /* 开灯 */

struct MySpinlockLED_dev{

  dev_t devid; /*声明32位变量devid用来给保存设备号*/

  int major;   /*主设备号*/

  int minor;   /*次设备号*/

  struct cdev  cdev; /*字符设备结构变量cdev */

  struct class *class;     /*类*/

  struct device *device;  /*设备*/

  struct device_node *nd; /*设备节点*/

  int led_gpio;   /*led所使用的GPIO编号*/

  int dev_stats; /*使用状态,0,设备未使用;>0,设备已经被使用 */

  spinlock_t lock; /* 自旋锁 */

};

extern struct MySpinlockLED_dev strMySpinlockLED;

extern int Get_gpio_num(void);

extern int led_GPIO_request(void);

extern void led_switch(u8 sta,struct MySpinlockLED_dev *dev);

#endif

6、创建LEDInterface.c

#include "LED.h"

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

//使能MySpinlockLED_init(),MySpinlockLED_exit()

#include <linux/gpio.h>

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

//gpio_direction_output(),gpio_get_value(),gpio_set_value()

#include <linux/spinlock.h>

//使能DEFINE_SPINLOCK(),spin_lock_init(),spin_lock(),spin_trylock(),spin_is_locked()

//spin_lock_irq(),spin_unlock_irq(),spin_lock_irqsave(),spin_unlock_irqrestore()

#define MySpinlockLED_CNT    1   //定义设备数量为1

#define MySpinlockLED_NAME  "MySpinlockLEDName"  //定义设备的名字

/* 打开设备 */

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

{

  unsigned long flags;

  filp->private_data = &strMySpinlockLED; /*设置私有数据*/

  spin_lock_irqsave(&strMySpinlockLED.lock, flags);

/* 上锁,准备修改strMySpinlockLED.dev_stats */

  if (strMySpinlockLED.dev_stats)

  { /*strMySpinlockLED.dev_stats>0表示“共享资源“被使用了*/

spin_unlock_irqrestore(&strMySpinlockLED.lock, flags);

/* 共享资源被使用了,记住要解锁 */

    return -EBUSY;

  }

  strMySpinlockLED.dev_stats++;

/*strMySpinlockLED.dev_stats加1,记录“共享资源“设备被使用了*/

  spin_unlock_irqrestore(&strMySpinlockLED.lock, flags);

/* 修改strMySpinlockLED.dev_stats后,要记住解锁 */

  printk("MySpinlockLED_open!\r\n");

  return 0;

}

/* 从设备读取数据,保存到首地址为buf的数据块中,长度为cnt个字节 */

//file结构指针变量flip表示要打开的设备文件

//buf表示用户数据块的首地址

//cnt表示用户数据的长度,单位为字节

//loff_t结构指针变量offt表示“相对于文件首地址的偏移”

static ssize_t MySpinlockLED_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)

{

  return 0;

}

/* 向设备写数据,将数据块首地址为buf的数据,长度为cnt个字节,发送给用户 */

//file结构指针变量flip表示要打开的设备文件

//buf表示用户数据块的首地址

//cnt表示用户数据的长度,单位为字节

//loff_t结构指针变量offt表示“相对于文件首地址的偏移”

static ssize_t MySpinlockLED_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)

{

  int ret = 0;

  unsigned char databuf[1];

  unsigned char ledstat;

  ret = copy_from_user(databuf, buf, cnt);

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

  if(ret <0){

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

    ret = -EFAULT;

  }

  ledstat = databuf[0];/*获取到应用传递进来的开关灯状态*/

  led_switch(ledstat,filp->private_data);/*执行开灯或执行关灯*/

  return ret;

}

/* 关闭/释放设备 */

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

{

  unsigned long flags;

  struct MySpinlockLED_dev *dev = filp->private_data;

  /* 关闭驱动文件的时候将dev_stats减1 */

  spin_lock_irqsave(&dev->lock, flags);

/* 上锁,准备修改dev->dev_stats */

  if (dev->dev_stats) {

dev->dev_stats--;

/*strMySpinlockLED.dev_stats减1,记录“共享资源“设备没有被使用*/

  }

  spin_unlock_irqrestore(&dev->lock, flags);

/* 修改完dev->dev_stats后,记住解锁 */

  return 0;

}

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

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

const struct file_operations MySpinlockLED_fops = {

  .owner = THIS_MODULE,

  .open = MySpinlockLED_open,

  .read = MySpinlockLED_read,

  .write = MySpinlockLED_write,

  .release = MySpinlockLED_release,

};

/*驱动入口函数 */

static int  __init MySpinlockLED_init(void)

{

  int ret;

  strMySpinlockLED.dev_stats=0;

/*初始化“共享资源使用标志”*/

  spin_lock_init(&strMySpinlockLED.lock);

  /* 初始化自旋锁 */

  ret=Get_gpio_num();//读引脚编号

  if(ret < 0) return ret;

/* 1、申请“gpio编号”*/

  ret=led_GPIO_request();//申请“gpio编号” 

  if(ret < 0) return ret;//向gpio子系统申请使用“gpio编号” 失败

  /*2、申请设备号*/

  strMySpinlockLED.major=0;

  if(strMySpinlockLED.major)/*如果指定了主设备号*/

  {

    strMySpinlockLED.devid = MKDEV(strMySpinlockLED.major, 0);

    //输入参数strMySpinlockLED.major为“主设备号”

    //输入参数0为“次设备号”,大部分驱动次设备号都选择0

    //将strMySpinlockLED.major左移20位,再与0相或,就得到“Linux设备号”

ret=register_chrdev_region( strMySpinlockLED.devid,\

                       MySpinlockLED_CNT, \

                       MySpinlockLED_NAME );

    //strMySpinlockLED.devid表示起始设备号

    //MySpinlockLED_CNT表示次设备号的数量

    //MySpinlockLED_NAME表示设备名

    if(ret < 0)

      goto free_gpio;

  }

  else

  { /* 没有定义设备号 */

ret=alloc_chrdev_region( &strMySpinlockLED.devid,\

                     0, \

                     MySpinlockLED_CNT,\

                     MySpinlockLED_NAME);

    /* 申请设备号 */

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

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

    //MySpinlockLED_CNT:要申请的次设备号数量;

    //MySpinlockLED_NAME:表示“设备名字”

    if(ret < 0)

      goto free_gpio;

    strMySpinlockLED.major = MAJOR(strMySpinlockLED.devid);

    /* 获取分配号的主设备号 */

    //输入参数strMySpinlockLED.devid为“Linux设备号”

    //将strMySpinlockLED.devid右移20位得到“主设备号”

    strMySpinlockLED.minor = MINOR(strMySpinlockLED.devid);

    /* 获取分配号的次设备号 */

    //输入参数strMySpinlockLED.devid为“Linux设备号”

    //将strMySpinlockLED.devid与0xFFFFF相与后得到“次设备号”

  }

  /*3、注册字符设备*/

  strMySpinlockLED.cdev.owner = THIS_MODULE;

  //使用THIS_MODULE将owner指针指向当前这个模块

  cdev_init(&strMySpinlockLED.cdev,&MySpinlockLED_fops);

  //注册字符设备,初始化“字符设备结构变量strMySpinlockLED.cdev”

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

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

  /*4、添加字符设备*/

  ret=cdev_add(&strMySpinlockLED.cdev,strMySpinlockLED.devid,MySpinlockLED_CNT);

  //添加字符设备

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

  //strMySpinlockLED.devid表示设备号

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

  if(ret < 0 ) //添加字符设备失败

    goto del_register;

  printk("dev id major = %d,minor = %d\r\n", strMySpinlockLED.major, strMySpinlockLED.minor);

  printk("MySpinlockLED_init is ok!!!\r\n");

  /*5、自动创建设备节点 */

  strMySpinlockLED.class =class_create(THIS_MODULE, MySpinlockLED_NAME);

  if (IS_ERR(strMySpinlockLED.class)){

    goto del_cdev;

  }

  /*6、创建设备 */

  strMySpinlockLED.device = device_create(strMySpinlockLED.class, NULL, strMySpinlockLED.devid, NULL, MySpinlockLED_NAME);

  //创建设备

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

  //NULL表示没有父设备

  //strMySpinlockLED.devid是设备号;

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

  //MySpinlockLED_NAME是设备名字

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

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

  if (IS_ERR(strMySpinlockLED.device)){

    goto destroy_class;

  }

  return 0;

destroy_class:

  class_destroy(strMySpinlockLED.class);

  //删除类

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

del_cdev:

   cdev_del(&strMySpinlockLED.cdev);

   //删除字符设备

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

del_register:

  unregister_chrdev_region(strMySpinlockLED.devid, MySpinlockLED_CNT);

  /* 释放设备号 */

  //strMySpinlockLED.devid:需要释放的起始设备号

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

free_gpio://申请设备号失败

  /*释放gpio编号*/

  gpio_free(strMySpinlockLED.led_gpio);

  return -EIO;

}

/*驱动出口函数 */

static void __exit MySpinlockLED_exit(void)

{

  /*1、删除字符设备*/

  cdev_del(&strMySpinlockLED.cdev);

  /*删除字符设备*/

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

  /*2、 释放设备号 */

  unregister_chrdev_region(strMySpinlockLED.devid, MySpinlockLED_CNT);

  /*释放设备号 */

  //strMySpinlockLED.devid:需要释放的起始设备号

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

  /*3、 删除设备 */

  device_destroy(strMySpinlockLED.class, strMySpinlockLED.devid);

  //删除创建的设备

  //strMySpinlockLED.class是要删除的设备所处的类

  //strMySpinlockLED.devid是要删除的设备号

  

  /*4、删除类*/

  class_destroy(strMySpinlockLED.class);

  //删除类

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

  /*5、释放gpio编号*/

  gpio_free(strMySpinlockLED.led_gpio);

}

module_init(MySpinlockLED_init);

//指定MySpinlockLED_init()为驱动入口函数

module_exit(MySpinlockLED_exit);

//指定MySpinlockLED_exit()为驱动出口函数

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

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

MODULE_INFO(intree,"Y");

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

7、LED_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运行命令:./LED_APP filename <1>|<0>如果是1表示打开LED,如果是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];

  unsigned char cnt = 0;

  if(argc != 3)

  {

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

    return -1;

  }

  //argv[]是指向输入参数“./LED_App” “/dev/LED” “1”

  filename = argv[1];

  //argv[1]指向字符串“/dev/LED”

  fd = open(filename, O_RDWR);

  //如果打开“/dev/LED”文件成功,则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表示关闭失败

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

    return -1;

  }

  /* 模拟占用10S LED */

//添加延时程序,导致关闭文件推迟,为了是演示互斥访问共享资源的效果

  while(1) {

    sleep(5);

    cnt++;

    printf("App running times:%d\r\n", cnt);

    if(cnt >= 2) break;

  }

  /* 关闭设备 */

  retvalue = close(fd);

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

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

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

  if(retvalue < 0)

  {

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

    return -1;

  }

else printf("close file %s\r\n", filename);

  return 0;

}

8、创建Makefile

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

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

CURRENT_PATH := $(shell pwd)

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

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

MyAPP := LED_APP

MySpinlockLED_Module-objs = LEDInterface.o LED.o

obj-m := MySpinlockLED_Module.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

9、添加“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/MySplinlockLED",

                "/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

}

10、编译

输入“make clean回车

输入“make drv回车

输入“make app回车

输入“make install回车

输入“ls /home/zgq/linux/nfs/rootfs/lib/modules/5.4.31/ -l回车”产看是存在“LED_APP和MySpinlockLED_Module.ko

11、测试

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

输入“root

输入“cd /lib/modules/5.4.31/回车

切换到“/lib/modules/5.4.31/”目录

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

输入“ls -l”查看“MySpinlockLED_Module.ko和LED_APP”是否存在

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

输入“modprobe MySpinlockLED_Module.ko”,加载“MySpinlockLED_Module.ko”模块

输入“lsmod”查看有哪些驱动在工作

输入“ls /dev/MySpinlockLEDName -l回车”,发现节点文件“/dev/MySpinlockLEDName

输入“./LED_APP /dev/MySpinlockLEDName 1&回车”执行开灯

注意:“ &”表示在后台运行LED_APP这个软件

等待出现“close file /dev/MySpinlockLEDName”按下“回车键”。

注意:如果没有出现“close file /dev/MySpinlockLEDName”,就急着输入“./LED_APP /dev/MySpinlockLEDName 0回车”,就会提示“Can't open file /dev/MySpinlockLEDName”,这就避免线程竞争访问贡献资源

输入“./LED_APP /dev/MySpinlockLEDName 0回车”执行关灯

输入“rmmod MySpinlockLED_Module.ko”,卸载“MySpinlockLED_Module.ko”模块

注意:输入“rmmod MySpinlockLED_Module”也可以卸载“MySpinlockLED_Module.ko”模块

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

输入“ls /dev/MySpinlockLEDName -l回车”,查询节点文件“/dev/MySpinlockLEDName”是否存在

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1531641.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Nanya(南亚科技)DRAM芯片选型详解

一、DRAM产品选型 普通SDRAM只在时钟的上升期进行数据传输&#xff0c;DDR内存能够在时钟的上升期和下降期各传输一次数据&#xff0c;因此性能翻倍&#xff0c;被称为双倍速率同步动态随机存储器。因此DDR内存可以在与SDRAM相同的总线频率下达到更高的数据传输率。DDR是一种掉…

Pygame AttributeError no attribute ‘display‘问题及其解决方法

目录 前言 错误原因 解决方法 1. 确保正确安装 Pygame 2. 检查 Python 路径和 Pygame 模块位置 3. 修复模块命名冲突 4. 检查代码错误 5. 检查 Pygame 版本兼容性 代码示例 总结 前言 Pygame 是一个非常受欢迎的 Python 游戏开发库&#xff0c;它提供了丰富的功能和…

Linux 学习必备:CentOS 技能提升一站式平台!

介绍&#xff1a;CentOS是一种广泛使用的Linux发行版&#xff0c;特别受到企业用户的青睐。 首先&#xff0c;CentOS的全称是Community Enterprise Operating System&#xff0c;中文意思是“社区企业操作系统”。它起源于Red Hat Enterprise Linux (RHEL)的源代码&#xff0c;…

极智压缩 ——帮你高清无损压缩 JPG/PNG/GIF 图片!

引言 图片太大怎么办&#xff1f;图片该如何压缩&#xff1f;压缩后图片为什么变模糊了&#xff1f;屏幕前的你是不是经常被这些问题所困扰&#xff0c;腾讯云数据万象推出的图片极智压缩服务&#xff0c;可以帮你高清无损压缩 JPG/PNG/GIF 图片&#xff0c;解决你95%以上的图片…

解决虚拟机Linux ens33 没有 IP 地址

解决方法&#xff1a; 先进入 root 模式 sudo su 查看目录 ls /etc/sysconfig 找到上述文件夹 ls /etc/sysconfig/network-scripts/ 用 vim 打开 ifcfg-ens33 这个文件&#xff08;不都是这个名字&#xff0c;按这个方法找到这个文件就行&#xff09; vim /etc/sysconfig/netw…

学习笔记-华为IPD转型2020:3,IPD的实施

3. IPD的实施 1999 年开始的 IPD 转型是计划中的多个转型项目中的第一个&#xff08;Liu&#xff0c;2015&#xff09;。华为为此次转型成立了一个专门的团队&#xff0c;从大约20人开始&#xff0c;他们是华为第一产业的高层领导。董事会主席孙雅芳是这个团队的负责人。该团…

【算法与数据结构】深入二叉树实现超详解

文章目录 &#x1f4dd;前言&#x1f320; 接口函数✏️ 实现函数&#x1f309;创建树的新节点&#x1f320;通过前序遍历的数组构建二叉树&#x1f309;包装通过前序遍历的数组构建二叉树&#x1f320;二叉树的销毁&#x1f320;层次遍历&#x1f320;第一种实现&#xff1a;不…

如何在尽量不损害画质的前提下降低视频占内存大小?视频格式科普及无损压缩软件推荐

大家好呀&#xff0c;相比大家都有对视频画质和体积的追求和取舍&#xff0c;那么&#xff0c;如何才能在不牺牲画质的前提下&#xff0c;尽可能的将视频大小降低到极致呢&#xff1f; 首先我们要了解视频的构成&#xff0c;要想降低视频的体积大小&#xff0c;我们可以从以下几…

Learn OpenGL 22 高级光照与Gamma校正

高级光照 Blinn-Phong 冯氏光照不仅对真实光照有很好的近似&#xff0c;而且性能也很高。但是它的镜面反射会在一些情况下出现问题&#xff0c;特别是物体反光度很低时&#xff0c;会导致大片&#xff08;粗糙的&#xff09;高光区域。下面这张图展示了当反光度为1.0时地板会…

品牌方年度抖音店铺打造流量运营孵化方案

【干货资料持续更新&#xff0c;以防走丢】 品牌方年度抖音店铺打造流量运营孵化方案 部分资料预览 资料部分是网络整理&#xff0c;仅供学习参考。 PDF共120页&#xff08;完整资料包含以下内容&#xff09; 目录 抖音年度短视频直播运营规划方案 1. 帐号视频发布规划 问…

55、服务攻防——数据库安全RedisHadoopMysql未授权访问RCE

文章目录 常见服务应用的安全测试&#xff1a; 配置不当——未授权访问安全机制——特定安全漏洞安全机制——弱口令爆破攻击 应用服务安全测试流程&#xff1a; 判断服务开放情况——端口扫描&组合猜解等 端口扫描&#xff1a;服务开放&#xff0c;绑定端口没开放&#…

浅谈前端路由原理hash和history

1、认识前端路由 本质 前端路由的本质&#xff0c;是监听 url 地址或 hash 值的改变&#xff0c;来切换渲染对应的页面组件 前端路由分为两种模式 hash 模式 history 模式 两种模式的对比 2、hash 模式 &#xff08;1&#xff09;hash 定义 hash 模式是一种把前端路由的路…

管理类联考–复试–英文面试–问题--规划介绍原因做法--纯英文版

借鉴 https://www.bilibili.com/video/BV1Dk4y187zN/?p4&spm_id_from333.880.my_history.page.clickhttps://www.bilibili.com/video/BV1Dk4y187zN/?p4&spm_id_from333.880.my_history.page.click https://ttsreader.com/zh/https://ttsreader.com/zh/ 规划 视频版…

MyBatis3源码深度解析(十七)MyBatis缓存(一)一级缓存和二级缓存的实现原理

文章目录 前言第六章 MyBatis缓存6.1 MyBatis缓存实现类6.2 MyBatis一级缓存实现原理6.2.1 一级缓存在查询时的使用6.2.2 一级缓存在更新时的清空 6.3 MyBatis二级缓存的实现原理6.3.1 实现的二级缓存的Executor类型6.3.2 二级缓存在查询时使用6.3.3 二级缓存在更新时清空 前言…

Java基础 学习笔记八

控制语句 分支语句 switch switch语句完整格式 expression 中执行完必须是个值并且必须是 int 或者 枚举 或者 字符串类型break语句只要执行&#xff0c;switch就要结束default语句不是必须的&#xff0c;但是建议写上&#xff0c;这样程序更加健壮 switch(expression){//exp…

浅谈 电脑和车的对比

https://www.zhihu.com/question/547115488 电脑CPU与汽车发动机有哪些相同点与不同点&#xff1f; - 知乎 就想机械硬盘一样 我们的技术可能达不到 但是我们可以弯道超车 比如长江存储的SSD可以取代 以前的机械硬盘

如何基于香橙派AIpro开发AI推理应用

简介 香橙派AIpro开发板采用昇腾AI技术路线&#xff0c;接口丰富且具有强大的可扩展性&#xff0c;提供8/20TOPS澎湃算力&#xff0c;可广泛使用于AI边缘计算、深度视觉学习及视频流AI分析、视频图像分析、自然语言处理等AI领域。通过昇腾CANN软件栈的AI编程接口&#xff0c;可…

深度学习pytorch——统计属性(持续更新)

矩阵范数 vs 向量范数 向量范数 1-范数&#xff1a;所有元素的绝对值之和。 2-范数&#xff1a;所有元素的平方之和&#xff0c;在开根号。 p-范数&#xff1a;所有元素的绝对值的p次方之和&#xff0c;再求其1/p次方。 例&#xff1a;向量X[2, 3, -5, -7] &#xff0c;求向…

深入理解Linux内核页表映射分页机制原理

深入理解Linux内核页表映射分页机制原理 前言 操作系统用于处理内存访问异常的入口操作系统的核心任务是对系统资源的管理&#xff0c;而重中之重的是对CPU和内存的管理。 为了使进程摆脱系统内存的制约&#xff0c;用户进程运行在虚拟内存之上&#xff0c;每个用户进程都拥…

静态网络配置

一、查看网络命令 1.命令行查看网络配置 1、查看ip\硬件设备-网卡 ifconfig -a ifconfig ens160 网卡名称 ip addr show ip addr show ens160 nmcli device show ens160 nmcli con up ens160 2、主机名称 hostname hostname hfj.huaxia.com 3、查看路由和网关 rou…