设备树下的 LED 驱动实验

本文介绍了如何修改设备树来添加一个名为alphaled的节点,并详细展示了对应的设备树代码。接着,提供了LED驱动程序的实现,包括打开/关闭LED灯的函数,以及驱动的注册和设备节点的创建。最后提到了测试APP的编写,用于控制LED灯的开关状态。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

修改设备树文件

在根节点“/”下创建一个名为“alphaled”的子节点,打开 imx6ull-alientek-emmc.dts 文件, 在根节点“/”最后面输入如下所示内容:

alphaled {
     #address-cells = <1>;
     #size-cells = <1>;
     compatible = "atkalpha-led";
     status = "okay";
     reg = < 0X020C406C 0X04 /* CCM_CCGR1_BASE */
     0X020E0068 0X04 /* SW_MUX_GPIO1_IO03_BASE */
     0X020E02F4 0X04 /* SW_PAD_GPIO1_IO03_BASE */
     0X0209C000 0X04 /* GPIO1_DR_BASE */
     0X0209C004 0X04 >; /* GPIO1_GDIR_BASE */
 };

/*
第 2、3 行,属性#address-cells 和#size-cells 都为 1,表示 reg 属性中起始地址占用一个字长
(cell),地址长度也占用一个字长(cell)。
第 4 行,属性 compatbile 设置 alphaled 节点兼容性为“atkalpha-led”。
第 5 行,属性 status 设置状态为“okay”。
第 6~10 行,reg 属性,非常重要!reg 属性设置了驱动里面所要使用的寄存器物理地址,比
如第 6 行的“0X020C406C 0X04”表示 I.MX6ULL 的 CCM_CCGR1 寄存器,其中寄存器首地
址为 0X020C406C,长度为 4 个字节。

*/

设备树修改完成以后输入如下命令重新编译一下 imx6ull-alientek-emmc.dts:  make dtbs

编译完成以后得到 imx6ull-alientek-emmc.dtb,使用新的 imx6ull-alientek-emmc.dtb 启动 Linux 内核。Linux 启动成功以后进入到/proc/device-tree/目录中查看是否有“alphaled”这个节 点,如图

 如果没有“alphaled”节点的话请重点查看下面两点:

①、检查设备树修改是否成功,也就是 alphaled 节点是否为根节点“/”的子节点。

②、检查是否使用新的设备树启动的 Linux 内核。

可以进入到alphaled 目录中,查看一下都有哪些属性文件,

LED 灯驱动程序编写

#include <linux/kernel.h>
#include <linux/ide.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>


#define LEDOFF 0  //关闭
#define LEDON  1   //打开

#define  DTSLED_NAME  "dts_led"   //设备名字
#define  DTSLED_CONUT  1     //设备号个数

/*地址映射后的虚拟地址指针*/
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;


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

    struct device_node *bl_nd;  //设备节点
    
};

struct dtsled_dev  dtsled;   /*led设备*/

/*LED灯打开/关闭*/
static void led_switch(u8 sta)
{
      u32 val = 0;
      if(sta == LEDON)
      {
          val = readl(GPIO1_DR);
          val &= ~(1 << 3);         //bit3清零,打开led灯
          writel(val,GPIO1_DR);
      }
      else if (sta == LEDOFF)
      {
          val = readl(GPIO1_DR);
          val |= (1 << 3);
          writel(val,GPIO1_DR);
      }
}



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

    return 0;
}

static int dtsled_close(struct inode *inode, struct file *file)
{   
    struct  dtsled_dev *dev = (struct dtsled_dev *)file -> 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)
    {
        return -EFAULT;
    }
     /*判断是开灯还是关灯*/
    led_switch(databuf[0]);
    return 0;
}



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

/*入口*/
static int __init   dtsled_init(void)
{
    int ret = 0;
    const char *str;
    //u32 brival[10];
    //u8 i = 0;
    unsigned int val = 0;
 
    /*1.申请设备号*/
    dtsled.major = 0; //设备号由内核分配
    if(dtsled.major)   //定义了设备号
    {
        dtsled.devid = MKDEV(dtsled.major,0);
        ret = register_chrdev_region(dtsled.devid,DTSLED_CONUT,DTSLED_NAME);

    }else      //没有定义设备号
    {
        ret = alloc_chrdev_region(&dtsled.devid,0,DTSLED_CONUT,DTSLED_NAME);
        dtsled.major = MAJOR(dtsled.devid);  
        dtsled.minor = MINOR(dtsled.devid); 
        if (ret < 0)
        {
            printk("dtsled chardev_region  err\r\n");
            return -1;
        }
         
    }

    /*2.添加字符设备*/
    dtsled.cde.owner = THIS_MODULE;
    cdev_init(&dtsled.cde,&dtsled_fops);
    ret = cdev_add(&dtsled.cde,dtsled.devid,DTSLED_CONUT);
    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;
    }
    /*4.创建设备*/
    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.bl_nd = of_find_node_by_path("/alphaled");
    if (dtsled.bl_nd == NULL)
    {
        ret = -EINVAL;
        goto fail_findnd;
    }

    /*获取属性*/
    ret = of_property_read_string(dtsled.bl_nd,"status",&str);
    if (ret < 0)
    {
        goto fail_rs;
    }else 
    {
        printk("status = %s\r\n",str);
    }

    ret = of_property_read_string(dtsled.bl_nd,"compatible",&str);
    if (ret < 0)
    {
        goto fail_rs;
    }else 
    {
        printk("compatible = %s\r\n",str);
    }

#if 0
    ret = of_property_read_u32_array(dtsled.bl_nd,"reg",brival,10);
    if (ret <0)
    {
        goto fail_rs;
    }else
    {   
        printk("reg data:\r\r");
        for( i = 0; i < 10; i++)
        {
         printk("%#X ",brival[i]);
        } 
        printk("\r\n");     
    }


    /*led灯初始化*/
    IMX6U_CCM_CCGR1 = ioremap(brival[0],brival[1]);
    SW_MUX_GPIO1_IO03 = ioremap(brival[2],brival[3]);
    SW_PAD_GPIO1_IO03 = ioremap(brival[4],brival[5]);
    GPIO1_DR = ioremap(brival[6],brival[7]);
    GPIO1_GDIR = ioremap(brival[8],brival[9]);

#endif

	IMX6U_CCM_CCGR1 = of_iomap(dtsled.bl_nd, 0);
	SW_MUX_GPIO1_IO03 = of_iomap(dtsled.bl_nd, 1);
  	SW_PAD_GPIO1_IO03 = of_iomap(dtsled.bl_nd, 2);
	GPIO1_DR = of_iomap(dtsled.bl_nd, 3);
	GPIO1_GDIR = of_iomap(dtsled.bl_nd, 4);

    /*初始化*/
     val = readl(IMX6U_CCM_CCGR1);
     val &= ~(3 <<26);  //先清除以前的配置bit26,27
     val |= 3 << 26;
     writel(val,IMX6U_CCM_CCGR1);

     writel(0x5,SW_MUX_GPIO1_IO03);  //设置复用
     writel(0x10b0,SW_PAD_GPIO1_IO03);  //设置电气属性
 
     val = readl(GPIO1_GDIR);
     val |= 1 << 3;               //bit3置1,设置为输出
     writel(val,GPIO1_GDIR);
 
 
    val = readl(GPIO1_DR);
    val |= ~(1 << 3);         //bit3置1,打开led灯
    writel(val,GPIO1_DR);
    

    return 0;

fail_rs:
fail_findnd:
  device_destroy(dtsled.class,dtsled.devid);
fail_device:
    class_destroy(dtsled.class);
fail_class:
    cdev_del(&dtsled.cde);
fail_cdev:
    unregister_chrdev_region(dtsled.devid,DTSLED_CONUT);
    return  ret;

}


/*出口*/
static void __exit  dtsled_exit(void)
{   
    /*取消地址映射*/
     iounmap(IMX6U_CCM_CCGR1);
     iounmap(SW_MUX_GPIO1_IO03);
     iounmap(SW_PAD_GPIO1_IO03);
     iounmap(GPIO1_DR);
     iounmap(GPIO1_GDIR);

    /*删除设备*/
    cdev_del(&dtsled.cde);
    /*释放设备号*/
    unregister_chrdev_region(dtsled.devid,DTSLED_CONUT);

    /*摧毁设备*/
    device_destroy(dtsled.class,dtsled.devid);

    /*卸载设备节点类*/
    class_destroy(dtsled.class);
}


//注册驱动和卸载驱动
module_init(dtsled_init);
module_exit(dtsled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("hsj");

编写测试 APP

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

#define LEDOFF 0 
#define LEDON  1

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;
   }

   filename = argv[1];
   fd = open(filename,O_RDWR);
   if(fd < 0)
   {
      printf("open failed\r\n");
      return -1;
   }
   
   databuf[0] = atoi(argv[2]);   //将字符转换为数字
   retvalue = write(fd,databuf,1);
   if (retvalue < 0)
    {
      printf("LED Control failed!\r\n");
      close(fd);
      return -1;   
    }


    close(fd);
    return 0;
}

运行测试

depmod //第一次加载驱动的时候需要运行此命令 modprobe

dtsled.ko //加载驱动

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值