5.设备树下的 LED 驱动实验

Pasted image 20250210163629.png
Pasted image 20250210164139.png
输入图片中的命令进行编译操作!
Pasted image 20250210164518.png

如果想学编写流程的话就是看我之前的博客!!!
我会把这部分比上一个博客多的·部分·标出来!

#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 <asm/mach/map.h>
#include <asm/uaccess.h>

#define DTSLED_COUNT  1 /*设备号个数*/
#define DTSLED_NAME  "dtsled" /*名字*/
#define LEDOFF      0     /*关闭*/ 
#define LEDON       1     /*打开*/

/*地址映射后的虚拟地址指针*/
static void __iomem *MP157_MPU_AHB4_PERIPH;
static void __iomem *MP157_GPIOI_MODER;
static void __iomem *MP157_GPIOI_OTYPER;
static void __iomem *MP157_GPIOI_OSPEEDR;
static void __iomem *MP157_GPIOI_PUPDR;
static void __iomem *MP157_GPIOI_BSRR;

/*地址映射*/
static void led_ioremap(u32 *regdata)
{
   MP157_MPU_AHB4_PERIPH = ioremap(regdata[0],regdata[1]);
   MP157_GPIOI_MODER = ioremap(regdata[2],regdata[3]);
   MP157_GPIOI_OTYPER = ioremap(regdata[4],regdata[5]);
   MP157_GPIOI_OSPEEDR = ioremap(regdata[6],regdata[7]);
   MP157_GPIOI_PUPDR = ioremap(regdata[8],regdata[9]);
   MP157_GPIOI_BSRR = ioremap(regdata[10],regdata[11]);
}

/*取消地址映射*/
static void led_iounmap(void)
{
  iounmap(MP157_MPU_AHB4_PERIPH);
  iounmap(MP157_GPIOI_MODER);
  iounmap(MP157_GPIOI_OTYPER);
  iounmap(MP157_GPIOI_OSPEEDR);
  iounmap(MP157_GPIOI_PUPDR);
  iounmap(MP157_GPIOI_BSRR);
}

/*dtsled设备结构体*/
struct  dtsled_dev {
    dev_t devid;   /*设备号*/
    struct cdev cdev;/*字符设备*/
    struct class *class;/*类*/
    struct device *device;/*设备*/
    int   major;   /*主设备号*/
    int   minor;   /*次设备号*/
    struct device_node *nd;/*设备节点*/

};

struct dtsled_dev dtsled;   /*led设备*/

/*LED打开或关闭*/
void led_switch(u8 sta)
{
   u32 val=0;
   if (sta == LEDON)
   {
      val = readl(MP157_GPIOI_BSRR);
      val &= ~(0x1 << 16);
      val |= (0x1 << 16);/*Bits 31:16 BR[15:0]*/
      writel(val,MP157_GPIOI_BSRR);
   }
   else if(sta == LEDOFF)
   {
      val = readl(MP157_GPIOI_BSRR);
      val &= ~(0X1 << 0);
      val |= (0x1 << 0);/*Bits 15:0 BS[15:0],BS位比BR位有更高的优先级*/
      writel(val,MP157_GPIOI_BSRR);
   }
}

static int dtsled_open(struct inode *inode, struct file *filp)
{
   filp->private_data = &dtsled;
   return 0;
}

static int dtsled_release(struct inode *inode, struct file *filp)
{
   struct dtsled_dev *dev = (struct dtsled_dev*)filp->private_data;
   return 0;
}

static ssize_t dtsled_write(struct file *filp, const char __user *buf,
			 size_t count, loff_t *ppos)
{
   struct dtsled_dev *dev = (struct dtsled_dev*)filp->private_data;
   int retvalue;
   unsigned char databuf[1]; 
   retvalue = copy_from_user(databuf,buf,count);
   if (retvalue < 0)
   {
      printk("kernel write failed!\r\n");
      return -EFAULT;
   }

   /*判断开灯还是关灯*/
   led_switch(databuf[0]);
     return 0;
}

/*dts字符设备操作集*/
static const struct file_operations dtsled_fops ={
    .owner   = THIS_MODULE,
	.write	= dtsled_write,
	.open   = dtsled_open,
	.release = dtsled_release,
};


/*入口*/
static int __init dtsled_init(void)
{
    int ret = 0;
    u8 i = 0;
    const char *str;
    u32 regdata[20];
    u32 val;
    //struct property *proper;
    /*注册字符设备*/
    /*1.申请设备号*/
    dtsled.major = 0;/*设备号由内核分配*/
    if(dtsled.major){/*定义了设备号*/
       dtsled.devid = MKDEV(dtsled.major,0);
       ret = register_chrdev_region(dtsled.devid,DTSLED_COUNT,DTSLED_NAME);
    }else{/*没有给定设备号*/
       ret = alloc_chrdev_region(&dtsled.devid,0,DTSLED_COUNT,DTSLED_NAME);
       dtsled.major = MAJOR(dtsled.devid);
       dtsled.minor = MAJOR(dtsled.devid);
    }
    if(ret < 0){
      goto fail_devid;
    }
    /*2.添加字符设备*/
    dtsled.cdev.owner = THIS_MODULE;
    cdev_init(&dtsled.cdev,&dtsled_fops);
    ret = cdev_add(&dtsled.cdev,dtsled.devid,DTSLED_COUNT);
    if(ret < 0){
        goto fail_cdev;
    }
    /*3.自动创建设备节点*/
    dtsled.class = class_create(THIS_MODULE,"DTSLED_NAME");
    if(IS_ERR(dtsled.class))
   {
   ret = PTR_ERR(dtsled.class);
   goto fail_class;
   }
	dtsled.device = device_create(dtsled.class,NULL,
			     dtsled.devid,NULL,DTSLED_NAME);
   if(IS_ERR(dtsled.device))
   {
      ret = PTR_ERR(dtsled.device);
      goto fail_device;
   }

    /*获取设备树属性内容*/
    dtsled.nd = of_find_node_by_path("/stm32mp1_led");
    if(dtsled.nd == NULL) {/*失败*/
     return -EINVAL;
     goto fail_findnd;
    }
    /*获取compatible属性内容*/
    ret = of_property_read_string(dtsled.nd, "compatible", &str);
    if(ret < 0){
        goto fail_rs;
    }else{
        printk(KERN_CONT"compatible = %s\r\n",str);
    }
    /*获取 status 属性内容*/
    ret = of_property_read_string(dtsled.nd, "status", &str);
    if(ret < 0){
        goto fail_rs;
    }else{
        printk(KERN_CONT"status = %s\r\n",str);
    }
    /*获取reg属性内容*/
    ret = of_property_read_u32_array(dtsled.nd,"reg",regdata,12);
    if(ret < 0){
        goto fail_rs;
    } else {
        printk("reg data:\r\n");
        for(i = 0; i < 12; i++){
        printk(KERN_CONT"%#X ",regdata[i]);
        }
        printk("\r\n");
     }
     /*LED灯初始化*/
     led_ioremap(regdata);
    /*时钟初始化*/
    val = readl(MP157_MPU_AHB4_PERIPH);/*获取虚拟地址下的32位数据*/
    val &= ~(0X1 << 8);/*清除以前的地址,Bit8置0,清除之前的配置*/
    val |= (0x1 << 8);/*Bit8置1,开启RCC-GPIOI时钟使能*/
    writel(val,MP157_MPU_AHB4_PERIPH);/*重新写入数据到虚拟地址*/
    
        /*设置PIO口通用输出*/
    val = readl(MP157_GPIOI_MODER);
    val &= ~(0X3 << 0); /* bit0:1 清零 */
    val |= (0X1 << 0); /* bit0:1 设置 01 */
    writel(val, MP157_GPIOI_MODER);/*GPIOI_MODER设置为01,通用输出模式*/
    
    /*设置PIO口为推挽模式*/
    val = readl(MP157_GPIOI_OTYPER);
    val &= ~(0X1 << 0); /* bit0 清零,设置为推挽*/
    writel(val,MP157_GPIOI_OTYPER);
    
    /*设置PIO的速度*/
    val = readl(MP157_GPIOI_OSPEEDR);
    val &= ~(0X3 << 0);/*Bit0:1清零*/
    val |= (0X2 << 0); /*Bit0:1设置为10*/
    writel(val,MP157_GPIOI_OSPEEDR);

    /*设置PIO口为上拉模式*/
    val = readl(MP157_GPIOI_PUPDR);
    val &= ~(0X3 << 0);/*Bit0:1清零*/
    val |= (0X1 << 0);/*Bit0:1设置为01*/
    writel(val,MP157_GPIOI_PUPDR);

    /*默认关闭 LED*/
    val = readl(MP157_GPIOI_BSRR);
    val &= ~(0X1 << 0);
    val |= (0x1 << 0);/*相当于在PIO高电平*/
    writel(val,MP157_GPIOI_BSRR);
    return ret;
fail_rs:
fail_findnd:
     device_destroy(dtsled.class,dtsled.devid);
  /*摧毁类*/
fail_device:
     class_destroy(dtsled.class);
fail_class:
     cdev_del(&dtsled.cdev);
fail_cdev:
     unregister_chrdev_region(dtsled.devid,DTSLED_COUNT);
fail_devid:
    return ret;

}

/*出口*/
static void __exit dtsled_exit(void)
{
  /*取消地址映射*/
  led_iounmap();
  /*删除字符设备*/
  cdev_del(&dtsled.cdev);
  /*注销设备号*/
  unregister_chrdev_region(dtsled.devid,DTSLED_COUNT);
  /*摧毁设备*/
  device_destroy(dtsled.class,dtsled.devid);
  /*摧毁类*/
  class_destroy(dtsled.class);

}

/*注册驱动和卸载驱动*/
module_init(dtsled_init);
module_exit(dtsled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("chensir");
MODULE_INFO(intree,"Y");

ledAPP.c

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"  /*open执行所需头文件,根文件目录终端输入man 2 open*/
#include "sys/stat.h"   /*open执行所需头文件,根文件目录终端输入man 2 open*/
#include "fcntl.h"      /*open执行所需头文件,根文件目录终端输入man 2 open*/
#include "stdlib.h"     
#include "string.h"     /*memcpy执行所需头文件,根文件目录终端输入man 3 memcpy*/

/*
*argc:应用程序参数个数
*argv[]:具体的参数内容,字符串形式
*./ledAPP  <fliename>  <0:1>  0表示关灯,1表示开灯
*./ledAPP /dev/dtsled 0   关灯
*./ledAPP /dev/dtsled 1   开灯
*/

#define LEDOFF   0
#define LEDON    1

int main(int argc,char *argv[])
{
  int fd,retvalue;/*fd:类似文件的ID号;retvalue:返回值。*/
  unsigned char databuf[1];/*这种数组可以存储数字或字符*/
  char *filename;
  if(argc !=3)
  {
    printf("Error usage!\r\n");
    return -1;
  }
  filename = argv[1];
  /*打开文件*/
  /* int open (const char *pathname,int flags)*/
  fd = open(filename,O_RDWR);/*以读写模式打开文件,fd用于打开文件获取ID号*/
  if(fd<0)
  {
    printf("file %s open failed!\r\n",filename);
    return -1;
  }
  databuf[0] = atoi(argv[2]);/*将字符转换为数字*/
  retvalue=write(fd,databuf,sizeof(databuf));
  if(retvalue<0)
  {
    printf("LED Control Failed!\r\n");
    close(fd);
    
  }
  close(fd);
  return 0;
  }

makefile

KERNELDIR := /home/chensir/linux/atk-mp1/linux/my_linux/linux-5.4.31
CURRENT_PATH := $(shell pwd) 
obj-m := dtsled.o

build: kernel_modules

kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

Pasted image 20250211174732.png
开灯!
0306da85b1b491fe1f7c1f175c118e4.jpg
关灯!
8715eede5829c0574981cc168480e91.jpg
比上一个博客多的部分·:
1
/dtsled设备结构体/
struct dtsled_dev {
dev_t devid; /设备号/
struct cdev cdev;/字符设备/
struct class *class;//
struct device *device;/设备/
int major; /主设备号/
int minor; /次设备号/
struct device_node *nd;/设备节点/

};
2
/获取设备树属性内容/
dtsled.nd = of_find_node_by_path(“/stm32mp1_led”);
if(dtsled.nd == NULL) {/失败/
return -EINVAL;
goto fail_findnd;
}
/获取compatible属性内容/
ret = of_property_read_string(dtsled.nd, “compatible”, &str);
if(ret < 0){
goto fail_rs;
}else{
printk(KERN_CONT"compatible = %s\r\n",str);
}
/获取 status 属性内容/
ret = of_property_read_string(dtsled.nd, “status”, &str);
if(ret < 0){
goto fail_rs;
}else{
printk(KERN_CONT"status = %s\r\n",str);
}
/获取reg属性内容/
ret = of_property_read_u32_array(dtsled.nd,“reg”,regdata,12);
if(ret < 0){
goto fail_rs;
} else {
printk(“reg data:\r\n”);
for(i = 0; i < 12; i++){
printk(KERN_CONT"%#X ",regdata[i]);
}
printk(“\r\n”);
}
3
fail_rs:
fail_findnd:
device_destroy(dtsled.class,dtsled.devid);
/摧毁类/
fail_device:
class_destroy(dtsled.class);
fail_class:
cdev_del(&dtsled.cdev);
fail_cdev:
unregister_chrdev_region(dtsled.devid,DTSLED_COUNT);
fail_devid:
return ret;
dtsled.c
4
/出口/
static void __exit dtsled_exit(void)
{
/取消地址映射/
led_iounmap();
/删除字符设备/
cdev_del(&dtsled.cdev);
/注销设备号/
unregister_chrdev_region(dtsled.devid,DTSLED_COUNT);
/摧毁设备/
device_destroy(dtsled.class,dtsled.devid);
/摧毁类/
class_destroy(dtsled.class);

}
没了!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值