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

Linux字符设备驱动开发的三种方式(分析+对比+示例)

文章目录

  • 一. 字符设备的驱动方法
  • 二. 三种方法的对比
  • 三. 开发环境
  • 四. 代码示例
    • 1. 传统设备驱动模型
    • 2. 总线设备驱动模型
    • 3. 设备树驱动模型
  • 五. 相关链接

一. 字符设备的驱动方法

字符设备驱动 是指在I/O传输过程中以字节流进行读写操作的设备。典型的如LCD、蜂鸣器、SPI、触摸屏等驱动,都属于字符设备驱动的范畴,Linux系统下的大部分的驱动程序都是属于字符设备驱动。

Linux 驱动 = Linux软件框架 + 硬件操作。 Linux驱动在系统中实际起到承上启下的作用,上承应用程序,对下则实现了具体的硬件操作。字符设备有非常多种,但无论何种字符设备,Linux软件框架的核心是不变的,差别就在于如何指定硬件资源。本文主要讲述三种字符设备的驱动框架:传统设备驱动模型、总线设备驱动模型、设备树驱动模型。

1. 传统设备驱动模型: 传统的Linux设备驱动模型将设备的一切操作都放在了一个文件当中,文件既包含了硬件相关的设备信息(例如引脚信息),也包含了对设备的操作函数,这种模型简单直观。但是由于设备信息和驱动代码杂糅在一起,一旦硬件信息发生改变(例如硬件更改引脚,或者需要移植到另外一个平台上),便需要修改驱动源码,然后重新编译,非常不利于扩展。

2. 平台总线设备驱动模型: 为了解决传统设备驱动不易扩展的问题,总线设备驱动使用虚拟总线将设备信息和驱动程序进行分离,其中设备信息指定对应硬件资源(例如引脚信息),驱动程序完成相应的逻辑操作。平台总线会维护两条链表,分别管理设备和驱动,当一个设备被注册到总线上时,总线会根据其名字搜索对应的驱动,如果找到就将设备信息导入驱动程序并执行驱动。当一个驱动被注册到平台总线的时候,总线也会搜索设备。总之,平台总线负责将设备信息和驱动代码进行匹配,这样就可以做到驱动和设备信息的分离。平台总线驱动模型将设备与驱动解耦开来,便于扩展与移植,但是冗余代码太多,且还是需要重新编译。

3. 设备树驱动模型: 设备树(dts文件)是描述计算机的特定硬件设备信息的数据结构,以便于操作系统的内核可以管理和使用这些硬件,包括CPU或CPU,内存,总线和其他一些外设。dtb文件(dtb为dts编译后的二进制文件)会被保存到ROM中,最终通过bootbolader被加载到内核,这样内核就可以通过解析设备树来让驱动去控制实际的硬件。通过设备树对硬件信息的抽象,驱动代码只要负责处理逻辑,而关于设备的具体信息存放到设备树文件中。因此,如果只是硬件接口信息的变化而没有驱动逻辑的变化,开发者只需要修改设备树文件信息,不需要改写驱动代码。设备树驱动模型相比于平台总线设备驱动模型,将所有的设备信息集成在了设备树文件当中,大大减少了冗余代码,且只在硬件信息更改的情况下无需重新编译,只需提供不同的设备树文件。

二. 三种方法的对比

假设某公司需要使用同一款芯片开发两款产品,分别为设备一与设备二。两个设备均需要使用IO引脚控制LED灯,但是引脚位置不同,下面分别使用传统设备驱动模型、平台总线设备驱动模型、 设备树驱动模型来开发,其中区别如下:

设备传统设备驱动模型总线设备驱动模型设备树驱动模型
设备一  driver_gpio.c
1. 分配一个file_operations结构体。
2. 设置 file_operations。
  - open 根据平台设备初始化GPIO引脚。
  - write 根据应用程序控制引脚。
  - close 释放硬件资源。
3. 注册设备()。
4. module_init ( )入口函数。
5. module_exit ( )出口函数。
















device_gpio.c
1. 分配一个 platform_device结构体。
2. 设置 platform_device。
 - resource 指定GPIO引脚
3. 注册 platform_device。
4. module_init ( )入口函数。
5. module_exit ( )出口函数。


driver_gpio.c
1. 分配一个 platform_driver结构体。
2. 设置 platform_driver。
  - probe
   - 链接硬件资源 resource
   - 分配 file_operation
  - remove
3. 设置 file_operation
  - open 初始化引脚
  - write 根据应用程序控制引脚。
  - close 释放硬件资源          
4. 注册 platform_driver。
5. module_init ( )入口函数。
6. module_exit ( )出口函数。
dts 设备树文件
1. 根据需求在dts设备树文件中指定硬件资源(GPIO)。
2. 内核根据dts生成的dtb文件分配/设置/注册platform_device 。





driver_gpio.c
1. 分配一个 platform_driver结构体。
2. 设置 platform_driver。
  - probe
   - 链接硬件资源 resource
   - 分配 file_operation
  - remove
3. 设置 file_operation
  - open 初始化引脚
  - write 根据应用程序控制引脚。
  - close 释放硬件资源
4. 注册 platform_driver。
5. module_init ( )入口函数。
6. module_exit ( )出口函数。


设备二driver_gpio.c
1. 分配一个file_operations结构体。
2. 设置 file_operations。
  - open 根据平台设备初始化GPIO引脚。
  - write 根据应用程序控制引脚。
  - close 释放硬件资源。
3. 注册设备。
4. module_init ( )入口函数。
4. module_exit ( )出口函数。
device_gpio.c
1. 分配一个 platform_device结构体。
2. 设置 platform_device。
 - resource 指定GPIO引脚
3. module_init ( )入口函数。
4. module_exit ( )出口函数。

device_gpio.c
不变。
dts 设备树文件
1. 根据需求修改dts设备树文件中指定硬件资源(GPIO)。
2. 内核会根据dts生成的dtb文件分配/设置/注册platform_device 。



device_gpio.c
不变。
优缺点 优点:简单直观

缺点:不易扩展
优点:易扩展

缺点:代码冗余
优点:易扩展、无冗余代码

缺点:上手难度高

三. 开发环境

Soc芯片   :全志V3s
开发环境  :Ubuntu 20.04
开发板系统 :Licheepi Linux
驱动程序交叉编译器 :gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf

硬件电路 用的是用的是自己画的开发板,基于全志的V3s芯片,采用金手指的方式设计。芯片的PB4引脚连接了一颗黄色的LED灯,下面我们就以传统设备驱动模型、平台总线设备驱动模型、设备树驱动模型为例,写点简单的示例来驱动一个GPIO,从而点亮一颗灯。

软件系统 使用的荔枝派的主线uboot+主线Linux的方式,源码和编译器都可以再荔枝派的官网上找到(荔枝派官网)。不同的Linux开发板只在硬件的寄存器操作上有所不同,驱动框架是通用的,如果使用的也是全志的Soc,甚至源码可以直接复用。

四. 代码示例


以下示例均是驱动开发板的PB4引脚进行高低电平交替切换,交替频率为1s。

1. 传统设备驱动模型

driver_gpio.c

#include <linux/init.h>             /* module_init()等宏定义头文件   */
#include <linux/module.h>           /* MODULE_LICENSE等宏定义头文件  */          
#include <linux/gpio.h>             /* gpio 相关头文件              */
#include <linux/fs.h>               /* file_operations 的相关头文件 */
#include <linux/uaccess.h>          /* copy_from_user()的头文件    */
#include <linux/device.h>           /* class_destroy()等函数头文件  */

#define SUNXI_PA_BASE	0
#define SUNXI_PB_BASE	32
#define SUNXI_PC_BASE	64
#define SUNXI_PD_BASE	96
#define SUNXI_PE_BASE	128
#define SUNXI_PF_BASE	160
#define SUNXI_PG_BASE	192
#define SUNXI_PH_BASE	224

/* sunxi gpio name space */
#define GPIOA(n)	(SUNXI_PA_BASE + (n))
#define GPIOB(n)	(SUNXI_PB_BASE + (n))
#define GPIOC(n)	(SUNXI_PC_BASE + (n))
#define GPIOD(n)	(SUNXI_PD_BASE + (n))
#define GPIOE(n)	(SUNXI_PE_BASE + (n))
#define GPIOF(n)	(SUNXI_PF_BASE + (n))
#define GPIOG(n)	(SUNXI_PG_BASE + (n))
#define GPIOH(n)	(SUNXI_PH_BASE + (n))

static int major = 0;
static struct class *gpio_class;

static int gpio_open(struct inode *node,struct file *filp)
{
	/*申请GPIO*/
	if(gpio_request(GPIOB(4),"PB4")!=0)
	{
		printk(KERN_ERR "GPIOB(4) init err!\n");//打印错误信息
		return -1;
	}

	/*设置为输出功能,输出0*/
	gpio_direction_output(GPIOB(4), 0);
	
	return 0;
}

static int gpio_release(struct inode *node,struct file *filp)
{
	/*灭灯*/
	gpio_set_value(GPIOB(4), 0);

	/*释放GPIO硬件资源*/
	gpio_free(GPIOB(4));

	return 0;
}

static ssize_t gpio_read(struct file *filp,char __user *buf,size_t size,loff_t *off)
{
	return 0;
}

static ssize_t gpio_write(struct file *filp,const char __user *buf,size_t size,loff_t *off)
{
	unsigned char val_led;

	copy_from_user(&val_led,buf,1);

	if(val_led)
	{
		/*GPIO输出高电平,开灯*/
		gpio_set_value(GPIOB(4), 1);
	}
	else
	{
		/*GPIO输出低电平,关灯*/
		gpio_set_value(GPIOB(4), 0);
	}

	return 1;
}

static struct file_operations gpio_oprs = {
	.owner = THIS_MODULE,
	.open  = gpio_open,
	.read  = gpio_read,
	.write = gpio_write,
	.release = gpio_release,
};

//入口:insmod
static int __init gpio_init(void)
{
	major = register_chrdev(0,"mygpio",&gpio_oprs);

	gpio_class = class_create(THIS_MODULE,"mygpio");

	/*  /dev/gpio  */
	device_create(gpio_class,NULL,MKDEV(major,0),NULL,"mygpio");

    printk("LED init ...\n");//打印提示信息

    return 0;
}
 
//出口:rmmod
static void __exit gpio_exit(void)
{
	unregister_chrdev(major,"mygpio");

	device_destroy(gpio_class,MKDEV(major,0));

	class_destroy(gpio_class);

    printk(KERN_INFO "LED exit...\n");//打印提示信息
}

/*三要素*/
module_init(gpio_init);
module_exit(gpio_exit);
MODULE_LICENSE("GPL");

MODULE_AUTHOR("LDL-1027221389.qq.com");//模块作者的声明
MODULE_DESCRIPTION("LED Driver");//当前模块的功能描述

驱动文件的Makefile文件如下:

ERNELDIR := /home/ldl13927222972/Linux_V3s/linux
CURRENT_PATH := $(shell pwd)

obj-m := driver_gpio.o

export ARCH=arm
export CROSS_COMPILE=/opt/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabi-

build: kernel_modules

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

编译生成driver_gpio.ko文件,在开发板的终端使用insmod xx.ko加载驱动模块、使用rmmod xx卸载驱动模块、使用lsmod查看驱动模块。

app_gpio.c

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc,char *argv[])
{
    int fd;
    char *path = "/dev/mygpio";
    fd = open(path,O_RDWR);

    if(fd == -1)
    {
        printf("can not open file %s\n",path);
        return -1;
    }
    while(1)
    {   
        unsigned char gpio_status;

        gpio_status = 0;
        write(fd,&gpio_status,sizeof(gpio_status));
        sleep(1);

        gpio_status = 1;
        write(fd,&gpio_status,sizeof(gpio_status));
        sleep(1);
    }
}

应用程序的Makefile文件如下:

app_gpio:app_gpio.c
        /opt/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc -o app_gpio app_gpio.c
clean:
        rm -f app_gpio

在开发板终端加载好driver_gpio.ko模块后,直接执行编译好的app_gpio文件,就可以实现LED的闪烁效果。

2. 总线设备驱动模型

device_gpio.c

#include <linux/kernel.h>           /* ARRAY_SIZE()相关头文件 */
#include <linux/platform_device.h>  /* platform_device_register()相关头文件 */
#include <linux/module.h>           /* MODULE_LICENSE等宏定义头文件  */      

/* 全志平台端口映射 */
#define SUNXI_PA_BASE	0
#define SUNXI_PB_BASE	32
#define SUNXI_PC_BASE	64
#define SUNXI_PD_BASE	96
#define SUNXI_PE_BASE	128
#define SUNXI_PF_BASE	160
#define SUNXI_PG_BASE	192
#define SUNXI_PH_BASE	224

/* 全志平台端口映射 */
#define GPIOA(n)	(SUNXI_PA_BASE + (n))
#define GPIOB(n)	(SUNXI_PB_BASE + (n))
#define GPIOC(n)	(SUNXI_PC_BASE + (n))
#define GPIOD(n)	(SUNXI_PD_BASE + (n))
#define GPIOE(n)	(SUNXI_PE_BASE + (n))
#define GPIOF(n)	(SUNXI_PF_BASE + (n))
#define GPIOG(n)	(SUNXI_PG_BASE + (n))
#define GPIOH(n)	(SUNXI_PH_BASE + (n))

/* 设置硬件资源 */
static struct resource gpio_resource[] = {
    [0] = {
        /* 引脚PB4的硬件描述*/
        .start = GPIOB(4),
        .end   = GPIOB(4),
        .flags = IORESOURCE_MEM,
    },
};

static void gpio_release(struct device *dev)
{
	printk(KERN_INFO "Releasing GPIO device resources\n");
}

static struct platform_device gpio_dev = {
    .name    = "mygpio",
    .id      = -1,
    .num_resources = ARRAY_SIZE(gpio_resource),
    .resource      = gpio_resource,
    .dev           = {
        .release = gpio_release,},
};

/* 入口函数 */
static int __init gpio_dev_init(void)
{
    /* 注册平台设备 */
    platform_device_register(&gpio_dev);

    return 0;
}

/* 出口函数 */
static void __exit gpio_dev_exit(void)
{
    /* 删除平台设备 */
    platform_device_unregister(&gpio_dev);
}

module_init(gpio_dev_init);
module_exit(gpio_dev_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("LDL");
MODULE_DESCRIPTION("Platform device model");

driver_gpio.c

#include <linux/init.h>             /* module_init()等宏定义头文件   */
#include <linux/module.h>           /* MODULE_LICENSE等宏定义头文件  */          
#include <linux/gpio.h>             /* gpio 相关头文件              */
#include <linux/fs.h>               /* file_operations 的相关头文件 */
#include <linux/uaccess.h>          /* copy_from_user()的头文件    */
#include <linux/device.h>           /* class_destroy()等函数头文件  */
#include <linux/platform_device.h>  /* platform_device_register()等函数头文件 */

static int major = 0;
static int gpio_1;

static struct class *gpio_class;

static int gpio_open(struct inode *node,struct file *filp)
{
	/*申请GPIO*/
	if(gpio_request(gpio_1,"PB4")!=0)
	{
		printk(KERN_ERR "GPIOB(4) init err!\n");//打印错误信息
		return -1;
	}

	/*设置为输出功能,输出0*/
	gpio_direction_output(gpio_1, 0);
	
	return 0;
}

static int gpio_release(struct inode *node,struct file *filp)
{
	/*灭灯*/
	gpio_set_value(gpio_1, 0);

	/*释放GPIO硬件资源*/
	gpio_free(gpio_1);

	return 0;
}

static ssize_t gpio_read(struct file *filp,char __user *buf,size_t size,loff_t *off)
{
	return 0;
}

static ssize_t gpio_write(struct file *filp,const char __user *buf,size_t size,loff_t *off)
{
	unsigned char val_led;

	copy_from_user(&val_led,buf,1);

	if(val_led)
	{
		/*GPIO输出高电平,开灯*/
		gpio_set_value(gpio_1, 1);
	}
	else
	{
		/*GPIO输出低电平,关灯*/
		gpio_set_value(gpio_1, 0);
	}

	return 1;
}

static struct file_operations gpio_oprs = {
	.owner = THIS_MODULE,
	.open  = gpio_open,
	.read  = gpio_read,
	.write = gpio_write,
	.release = gpio_release,
};

static int gpio_probe(struct platform_device *pdev)
{
	struct resource *pin_1;

	/* 根据platform_device链接硬件资源 */
	pin_1 = platform_get_resource(pdev,IORESOURCE_MEM,0);

	gpio_1 = pin_1->start;

	major = register_chrdev(0,"mygpio",&gpio_oprs);

	gpio_class = class_create(THIS_MODULE,"mygpio");

	/*  /dev/mygpio  */
	device_create(gpio_class,NULL,MKDEV(major,0),NULL,"mygpio");

	return 0;
}

int static gpio_remove(struct platform_device *pdev)
{
	unregister_chrdev(major,"mygpio");

	device_destroy(gpio_class,MKDEV(major,0));

	class_destroy(gpio_class);

	return 0;

}

struct platform_driver gpio_drv = {
	.probe   = gpio_probe,
	.remove  = gpio_remove,
	.driver  = {
		.name = "mygpio",
	}
};

//入口:insmod
static int __init gpio_init(void)
{	
	/* 注册平台驱动 */
	platform_driver_register(&gpio_drv);

    printk("LED init ...\n");//打印提示信息

    return 0;
}
 
//出口:rmmod
static void __exit gpio_exit(void)
{
	/* 删除平台驱动 */
    platform_driver_unregister(&gpio_drv);

    printk(KERN_INFO "LED exit...\n");//打印提示信息
}

/*三要素*/
module_init(gpio_init);
module_exit(gpio_exit);
MODULE_LICENSE("GPL");

MODULE_AUTHOR("LDL-1027221389.qq.com");//模块作者的声明
MODULE_DESCRIPTION("LED Driver");//当前模块的功能描述

两个文件的Makefile文件如下:

KERNELDIR := /home/ldl13927222972/Linux_V3s/linux
CURRENT_PATH := $(shell pwd)

obj-m += driver_gpio.o
obj-m += device_gpio.o

export ARCH=arm
export CROSS_COMPILE=/opt/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabi-

build: kernel_modules

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

编译生成driver_gpio.ko、device_gpio.ko文件,在开发板的终端使用insmod xx.ko分别加载两个模块。再执行应用程序,便可以使得LED灯闪烁。

app_gpio.c

与传统设备驱动模型中的应用程序相同,都是通过open与write函数操作dev/mygpio文件。

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc,char *argv[])
{
    int fd;
    char *path = "/dev/mygpio";
    fd = open(path,O_RDWR);

    if(fd == -1)
    {
        printf("can not open file %s\n",path);
        return -1;
    }
    while(1)
    {   
        unsigned char gpio_status;

        gpio_status = 0;
        write(fd,&gpio_status,sizeof(gpio_status));
        sleep(1);

        gpio_status = 1;
        write(fd,&gpio_status,sizeof(gpio_status));
        sleep(1);
    }
}

应用程序的Makefile文件如下:

app_gpio:app_gpio.c
        /opt/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc -o app_gpio app_gpio.c
clean:
        rm -f app_gpio

3. 设备树驱动模型

dts设备树修改

设备树加入以下内容:

mygpio {
		compatible = "my-gpio";
		my-gpios = <&pio 1 4 GPIO_ACTIVE_LOW>; /* PB4 */
	};

需检查设备树的其他内容,避免出现PB4引脚被其他所占用的情况。

driver_gpio.c

#include <linux/init.h>             /* module_init()等宏定义头文件   */
#include <linux/module.h>           /* MODULE_LICENSE等宏定义头文件  */          
#include <linux/gpio.h>             /* gpio 相关头文件              */
#include <linux/fs.h>               /* file_operations 的相关头文件 */
#include <linux/uaccess.h>          /* copy_from_user()的头文件    */
#include <linux/device.h>           /* class_destroy()等函数头文件  */
#include <linux/platform_device.h>  /* platform_device_register()等函数头文件 */

#include <linux/gpio/consumer.h>
#include <linux/gpio.h>         // 用于 GPIO 操作
#include <linux/of_gpio.h>      // 用于设备树 GPIO 操作
#include <linux/of.h>           // 用于设备树操作

static int major = 0;
static int gpio_num;  // 使用 gpio_num 来存储 GPIO 引脚编号

static struct class *gpio_class;

static int gpio_probe(struct platform_device *pdev)
{
    gpio_num = of_get_named_gpio(pdev->dev.of_node, "my-gpios", 0);
    if (gpio_num < 0) {
        pr_err("Failed to get GPIO from device tree\n");
        return gpio_num;
    }

    /* 初始化 GPIO */
    if (gpio_request(gpio_num, "PB4") != 0) {
        pr_err("Failed to request GPIO %d\n", gpio_num);
        return -1;
    }

    gpio_direction_output(gpio_num, 0);  // 设置为输出,初始为 0

    // 注册字符设备
    major = register_chrdev(0, "mygpio", &gpio_oprs);
    if (major < 0) {
        printk(KERN_ERR "Failed to register char device\n");
        gpio_free(gpio_num);  // 释放 GPIO 资源
        return major;
    }

    // 创建 class 和 device
    gpio_class = class_create(THIS_MODULE, "mygpio");
    if (IS_ERR(gpio_class)) {
        unregister_chrdev(major, "mygpio");
        gpio_free(gpio_num);
        return PTR_ERR(gpio_class);
    }

    device_create(gpio_class, NULL, MKDEV(major, 0), NULL, "mygpio");

    return 0;
}

static int gpio_remove(struct platform_device *pdev)
{
    unregister_chrdev(major, "mygpio");

    device_destroy(gpio_class, MKDEV(major, 0));

    class_destroy(gpio_class);

    return 0;
}

struct platform_driver gpio_drv = {
    .probe   = gpio_probe,
    .remove  = gpio_remove,
    .driver  = {
        .name = "mygpio",
    }
};

static int gpio_open(struct inode *node, struct file *filp)
{
    /* 设置为输出功能,输出0 */
    gpio_direction_output(gpio_num, 0);
    return 0;
}

static int gpio_release(struct inode *node, struct file *filp)
{
    /* 灭灯 */
    gpio_set_value(gpio_num, 0);

    /* 释放 GPIO 硬件资源 */
    gpio_free(gpio_num);
    
    return 0;
}

static ssize_t gpio_read(struct file *filp, char __user *buf, size_t size, loff_t *off)
{
    return 0;
}

static ssize_t gpio_write(struct file *filp, const char __user *buf, size_t size, loff_t *off)
{
    unsigned char val_led;

    copy_from_user(&val_led, buf, 1);

    if (val_led) {
        /* GPIO 输出高电平,开灯 */
        gpio_set_value(gpio_num, 1);
    } else {
        /* GPIO 输出低电平,关灯 */
        gpio_set_value(gpio_num, 0);
    }

    return 1;
}

static struct file_operations gpio_oprs = {
    .owner = THIS_MODULE,
    .open  = gpio_open,
    .read  = gpio_read,
    .write = gpio_write,
    .release = gpio_release,
};

// 入口:insmod
static int __init gpio_init(void)
{
    /* 注册平台驱动 */
    platform_driver_register(&gpio_drv);

    printk("LED init ...\n");  // 打印提示信息

    return 0;
}
 
// 出口:rmmod
static void __exit gpio_exit(void)
{
    /* 删除平台驱动 */
    platform_driver_unregister(&gpio_drv);

    printk(KERN_INFO "LED exit...\n");  // 打印提示信息
}

/* 三要素 */
module_init(gpio_init);
module_exit(gpio_exit);
MODULE_LICENSE("GPL");

MODULE_AUTHOR("LDL-1027221389.qq.com");  // 模块作者的声明
MODULE_DESCRIPTION("LED Driver");  // 当前模块的功能描述

app_gpio.c

与传统设备驱动模型以及总线设备驱动模型程序相同,都是通过open与write函数操作dev/mygpio文件。

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc,char *argv[])
{
    int fd;
    char *path = "/dev/mygpio";
    fd = open(path,O_RDWR);

    if(fd == -1)
    {
        printf("can not open file %s\n",path);
        return -1;
    }
    while(1)
    {   
        unsigned char gpio_status;

        gpio_status = 0;
        write(fd,&gpio_status,sizeof(gpio_status));
        sleep(1);

        gpio_status = 1;
        write(fd,&gpio_status,sizeof(gpio_status));
        sleep(1);
    }
}

应用程序的Makefile文件如下:

app_gpio:app_gpio.c
        /opt/gcc-linaro-6.3.1-2017.05-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc -o app_gpio app_gpio.c
clean:
        rm -f app_gpio

五. 相关链接

  1. 全志 linux-sunxi 官网 :https://linux-sunxi.org/。
  2. 荔枝派Zero官网:https://wiki.sipeed.com/soft/Lichee/zh/Zero-Doc/Start/board_intro.html
  3. 本文所写的所有代码示例:https://download.csdn.net/download/weixin_44793491/90178263

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

相关文章:

  • [Unity Shader] 【图形渲染】Shader数学基础12-坐标空间变换
  • 各种电机原理介绍
  • UE5仿漫威争锋灵蝶冲刺技能
  • 基于深度学习多图像融合的屏幕缺陷检测方案
  • 以太网帧、IP数据报图解
  • Vue3之状态管理Vuex
  • Ubuntu 24.04.1 解决部分中文字符(门、径)显示错误的问题
  • 学python还是学java?哪个相对来说比较容易上手?
  • Python 面向对象编程 五(结束)组合
  • 【C++】模板与泛型编程(一):定义模板,模板参数
  • oracle: create new database
  • JavaScriptEs6 - String类和Array类扩展内容
  • ThinkPHP接入PayPal支付
  • 一个比RTK或redux更轻量级更易使用的 React 第三方状态管理工具库的配置与使用
  • 云手机方案总结
  • 代码随想录day25 回溯4
  • C++ 23版的最新特性
  • WebService简介
  • 建筑工地AI安全检测系统:YOLO11数据标注训练与PyQt5界面设计应用开发
  • 契约锁数智化合同大会全国巡展启动,助力合同管理全程数字化转型
  • 【FAQ】HarmonyOS SDK 闭源开放能力 — Vision Kit(2)
  • 如何打造用户友好的维护页面:6个创意提升WordPress网站体验
  • 一键打断线(根据相交点打断)——CAD c# 二次开发
  • 查询Elasticsearch索引刷新间隔
  • [Unity Shader] 【游戏开发】【图形渲染】Shader数学基础3:矢量与标量的乘法与除法详解
  • IntelliJ IDEA 基本使用教程及Spring Boot项目搭建实战