linux 驱动开发之平台设备驱动设备树 led字符驱动的开发(详细注释)

我们来开始我们Linux下第一个platform设备的开发,通过linux 应用程序来控制开发板上的一个gpio led灯的亮灭.
首先介绍一下平台总线设备的开发过程,linux中采用了设备和驱动分离的思想,通过总线将设备和驱动匹配,所以这里涉及三个概念:总线(bus),设备(device)和驱动(driver),总线就像是红娘月老,做设备和驱动匹配的工作,比起其他内核中的总线比如i2c,spi,usb,在这里platform是个虚拟的总线.

因为分为驱动代码和应用代码,
我们先写驱动层代码吧:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/ide.h>
#include <linux/fcntl.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/mach/map.h>

struct led_data {//led设备属性
    int major;//主设备号
    int minor;//次设备号
    dev_t devid;//设备id
    struct cdev cdev;//字符设备
    struct class *class;//设备类
    struct device *device;//设备
    struct device_node *node;//设备节点
    int pin;
};

static struct led_data led;//声明一个驱动实例

static int led_open(struct inode *inode, struct file *file){//驱动层打开文件
    file->private_data = &led;//将led实例保存为私有数据
    return 0;
}
static ssize_t  led_write(struct file *file, const char __user *buf, size_t size, loff_t *lof){
    char data[2];//两个字节的buffer
    int res = 0;
   res = copy_from_user(data , buf ,size);//从用户空间拷贝过来buffer
   if(res<0){
       printk("kernel write err\n");
       return -EFAULT;
   }else{
       printk("copy_from_user is ok, buf = %d\n",data[0]);
   }
    if(data[0]==1){//如果用户程序写1则灯亮起来,否则灭
        gpio_set_value(led.pin,1);
    }else if(data[0]==0){
        gpio_set_value(led.pin,0);
    }
    return 0;
}

static int  led_release(struct inode *inode, struct file *file){
    return 0;//啥也不做,可以不用声明这个函数
}

static const struct file_operations  led_ops = {
    .owner = THIS_MODULE,
    .open = led_open,//注册文件控制函数
    .write = led_write,
    .release = led_release,
};

    
static const struct of_device_id	led_of_match[] = {
    {.compatible = "myz-led"},//通过compatible属性来匹配设备树的gpio-led
    { },
};

static int led_probe(struct platform_device *pdev){//当设备和驱动匹配成功就会执行probe函数
        alloc_chrdev_region(&led.devid,0,1,"led-myz");//申请字符设备
        led.major = MAJOR(led.devid);//生成主设备号
        cdev_init(&led.cdev,&led_ops);//初始化字符设备并注册文件操作结构体
        cdev_add(&led.cdev,led.devid,1);//添加一个字符设备
        led.class=class_create(THIS_MODULE,"led-myz");//产生类
        if(IS_ERR(led.class)){
            return PTR_ERR(led.class);
        }
        led.device = device_create(led.class,NULL,led.devid,NULL,"led-myz");//生成设备
       if(IS_ERR(led.device)){
            return PTR_ERR(led.device);
        }

        led.node = of_find_node_by_path("/gpioled");//通过设备树路径找到gpio的节点
        if(led.node == NULL){
            printk("gpioled node is not find\n");
            return -EINVAL;
        }else{
            printk("gpioled node is  find\n");
        }
        led.pin = of_get_named_gpio(led.node,"led-gpio",0);//通过节点下的gpio名字获取gpio引脚
        if(led.pin<0){
            printk("led-pin is not find \n");
            return -EINVAL;
        }else{
            printk("led-pin is find \n");
        }
        gpio_request(led.pin,"led0");//申请gpio
        gpio_direction_output(led.pin,1);//设置gpio 为输出
        printk("led- probe over \n");
        return 0;
}

static int led_remove(struct platform_device *pdev){//当驱动注销时就会执行remove函数
        gpio_set_value(led.pin,0);//设置引脚为低
        cdev_del(&led.cdev);//字符设备删除
        unregister_chrdev_region(led.devid,1);//注销led驱动
        device_destroy(led.class,led.devid);//摧毁类
        class_destroy(led.class);//摧毁驱动
        return 0;
}

static struct platform_driver led_driver ={
    .probe = led_probe,//注册probe和remove函数
    .remove = led_remove,
    .driver = {
        .name =  "led",
        .of_match_table = led_of_match,//注册of_match结构体
    },
};

static int __init led_init(void){
    return platform_driver_register(&led_driver);//注册platform驱动
}

static void __exit led_exit(void){
    platform_driver_unregister(&led_driver);//注销platform驱动
}

module_init(led_init);//insmod的时候会调用这个函数
module_exit(led_exit);//rmmod的时候会调用这个函数

MODULE_LICENSE("GPL");//驱动协议必不可少


在设备树文件xxx.dts中的设备树根目录中添加

	gpioled {
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "myz-led";
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_led>;
		led-gpio = <&gpio5 3 GPIO_ACTIVE_LOW>;
		status = "okay";
	 };

在linux内核根目录下执行

make dtbs

生成xxx.dtb文件,重启开发板系统加载xxx.dtb文件到内核中

然后编写应用层代码:

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

int main(int argc,char *argv[]){
    char *filename;
    int fd ,res;
    char data[2]={0};
    if(argc != 3){
        printf("err usage\n");
        return -1 ;
    }
    filename = argv[1];
    fd = open(filename,O_RDWR);
    if(fd<0){
        printf("%s open failed\n",filename);
        return -1;
    }

    data[0] = atoi(argv[2]);
    res = write(fd,data,sizeof(data));
    if(res<0){
        printf("file %s write err\n",filename);
        close(fd);
        return -1;
    }
    res = close(fd);
    if(res<0){
        printf("file %s close err\n",filename);
        return -1;
    }
}

接着写一个简单的makefile用来编译这两个代码

KERNELDIR 		:=/home/mayunzhi/linux/Linux-4.9.88
CURRENT_PATH 	:=$(shell pwd)
ARM-CC			:=arm-linux-gnueabihf-gcc

APP				:=app
DRV				:=led
obj-m 			:=$(DRV).o


build:kernel_modules

kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
	rm -f ./app
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
	
app:
	$(ARM-CC) $(APP).c -o $(APP)

mv:
	mv ./app *.ko 	/home/mayunzhi/linux/nfs/rootfs/lib/modules/4.1.15-2.1.0+g30278ab/

首先执行make编译驱动生成led.ko

然后执行make app 生成app可执行文件

再通过make mv 将ko文件和app文件移动到开发板nfs文件系统中,从开发版的终端进入饱含着两个文件的文件夹,执行

insmod led.ko

查看led驱动

lsmod

接着运行app程序,用法如下:

#开灯
./app /dev/led-myz 1 
#开发板上的灯亮了起来
#关灯
./app /dev/led-myz 0
#开发板上的灯灭了

最后卸载驱动命令如下;

rmmod led.ko

在这里插入图片描述

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Linux字符设备驱动开发是指在Linux系统中编写驱动程序,使得用户可以通过字符设备接口来访问硬件设备。这种驱动程序通常用于控制串口、并口、USB设备等。开发Linux字符设备驱动需要掌握Linux内核的基本知识,包括进程管理、内存管理、中断处理、设备驱动等方面。此外,还需要了解字符设备驱动的编写流程、驱动程序的结构和接口等。开发Linux字符设备驱动需要使用C语言和Linux内核编程接口。 ### 回答2: Linux字符设备驱动开发Linux系统中的一部分,它允许开发人员在Linux系统上使用字符设备,这些字符设备可以包括串口、USB口、网卡等。Linux字符设备驱动开发可帮助开发人员实现各种各样的设备驱动,从而增强Linux系统的功能。 在Linux字符设备驱动开发过程中,需要注意以下几点: 1. 实现设备驱动的一个基本框架,包括注册设备设备的初始化,以及对设备进行读写操作等。 2. 开发人员不仅需要熟悉驱动程序开发技术,还需要了解Linux内核系统的相关知识,例如进程、中断、内存管理等。 3. 应该在代码注释中提供详细的文档,以方便其他开发人员进行维护和修改。 4. 在实现字符设备驱动过程中,必须保证安全性和可靠性,防止设备出现故障或者损坏用户的数据。 5. 在测试和维护设备驱动时,需要使用一些常见的工具和技术,例如devfs、udev等。 总之,Linux字符设备驱动开发是一个需要熟练技能和丰富经验的过程。开发人员需要有足够的专业知识和经验来确保设备驱动的高效和稳定性。通过精心设计和开发Linux字符设备驱动可以提供高性能、高可靠性、易于使用的设备驱动,从而大大增强了Linux系统的功能和灵活性。 ### 回答3: Linux字符设备驱动开发Linux系统中的一个重要领域。其主要任务是开发一些支持字符设备驱动程序,从而使用户能够在Linux系统中使用各种不同类型的字符设备,例如串口、打印机、读卡器和磁盘等。同时,这些驱动程序还要保证设备完全可靠和高效地工作,确保系统的安全性和性能。 Linux字符设备驱动开发需要掌握以下基本知识: 1.了解Linux系统体系结构和内核架构 Linux系统由内核和用户空间组成,内核作为系统的核心组件,是实现系统功能的主要部分,因此了解内核体系结构和架构是开发Linux字符设备驱动所必须掌握的知识。 2.熟悉字符设备的相关知识 字符设备Linux系统中的一种重要的设备类型,它与其他类型设备不同之处在于它只能逐个字符地进行读写操作。因此需要深入了解字符设备的相关知识,例如驱动的主要功能、驱动程序与设备的交互方式、设备控制结构等。 3.熟练掌握C语言及Linux内核编程技术 编写Linux字符设备驱动程序需要掌握良好的C语言编程知识以及熟练的Linux内核编程技术,包括内存管理、进程管理、文件系统、中断处理等。同时,还需要了解Linux内核代码的结构和代码的编写规范,以便于编写出符合内核标准的驱动程序。 4.掌握Linux驱动框架的使用方法 为了简化Linux驱动开发流程,Linux提供了一些驱动框架,这些框架定义了一些驱动程序中常用的接口和函数,能够方便驱动程序的开发和调试。因此,Linux字符设备驱动开发者需要掌握其中的一些驱动框架,如字符驱动框架。 5.熟悉Linux字符设备驱动开发过程 Linux字符设备驱动开发过程主要包括驱动程序的初始化、驱动程序的主要功能实现、驱动程序的卸载等环节。在开发过程中,需要合理使用系统提供的工具和调试手段,如gdb、strace、make等,以便于分析和排查驱动程序出现的问题,确保驱动程序的稳定和可靠性。 总之,在Linux字符设备驱动开发过程中,开发者需要掌握相关的知识和技能,以实现对字符设备的编程和调试,开发出满足用户需求的高质量驱动程序。同时,Linux字符设备驱动开发也是一项长期持续的工作,开发者需要时刻关注最新的技术发展和硬件设备变化,才能更好地适应市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值