树莓派驱动开发(四)杂项设备驱动之点亮LED

杂项设备驱动和字符设备驱动代码上的区别在于,杂项设备驱动无需复杂的注册设备了,省了一大段代码,看起来更好理解。

第一步

首先还是要找到相应的寄存器和GPIO的引脚,我们还是选择GPIO4,具体过程请看上篇 树莓派驱动开发(三)字符设备驱动之点亮LED 这里不再赘述

第二步

我将驱动代码命名为led.c,以下为头文件以及我们要操作的寄存器地址

#include <linux/init.h> //初始化头文件
#include <linux/module.h> //最基本的文件,支持动态添加和卸载模块。
#include <linux/miscdevice.h> //注册杂项设备头文件
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/io.h>

#define GPIO4_DR 0xfe200000
#define GPIO4_H  0xfe20001c
#define GPIO4_L  0xfe200028

unsigned int *vir_gpio4_dr=NULL;
unsigned int *vir_gpio4_h=NULL;
unsigned int *vir_gpio4_l=NULL;

接着是我们的初始化函数和退出函数,利用API函数注册了杂项设备以及地址映射

//文件操作集
struct file_operations misc_fops = {
                .owner = THIS_MODULE,
                .open = misc_open,
                .release = misc_release,
                .read = misc_read,
                .write = misc_write,
                };
//miscdevice 结构体
struct miscdevice misc_dev = {
        .minor = MISC_DYNAMIC_MINOR,
        .name = "hello_misc",
        .fops = &misc_fops,
};
static int misc_init(void)
{
        int ret;
        ret = misc_register(&misc_dev); //注册杂项设备
        if (ret < 0)
        {
                printk("misc registe is error \n");
        }
        printk("misc registe is succeed \n");

        vir_gpio4_dr=ioremap(GPIO4_DR,4);
        if(vir_gpio4_dr==NULL )
        {
                printk("gpio4dr ioremap error\n");
                return EBUSY;
        }

        vir_gpio4_h=ioremap(GPIO4_H,4);
        if( vir_gpio4_h==NULL)
        {
                printk("gpio4h ioremap error\n");
                return EBUSY;
        }

        vir_gpio4_l=ioremap(GPIO4_L,4);
        if(vir_gpio4_l==NULL)
        {
                printk("gpio4l ioremap error\n");
                return EBUSY;
        }
        printk("gpio ioremap success\n");
        return 0;
}
static void misc_exit(void)
{
        misc_deregister(&misc_dev); //卸载杂项设备
        printk(" misc gooodbye! \n");
        iounmap(vir_gpio4_dr);
        iounmap(vir_gpio4_h);
        iounmap(vir_gpio4_l);
}
module_init(misc_init);
module_exit(misc_exit);
MODULE_LICENSE("GPL");

最后是杂项设备的操作函数,读和写都是利用api函数进行内核空间和用户空间的数据交换

ssize_t misc_read(struct file *file, char __user *ubuf, size_t size, loff_t *loff_t)
{
        char kbuf[] = "hehe";
        if (copy_to_user(ubuf, kbuf, strlen(kbuf)) != 0)
        {
                printk("copy_to_user error\n ");
                return -1;
        }
        printk("misc_read\n ");
        return 0;
}

ssize_t misc_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t)
{
        char kbuf[64] = {0};
        if (copy_from_user(kbuf, ubuf, size) != 0)
        {
                printk("copy_from_user error\n ");
                return -1;
        }
        printk("kbuf is %s\n ", kbuf);
        *vir_gpio4_dr |=(1<<(3*4));
        if(kbuf[0]==1)
        {

                *vir_gpio4_h |=(1<<4);
        }
        else if(kbuf[0]==0)
        {
                *vir_gpio4_l |=(1<<4);
        }
        return 0;
}

int misc_release(struct inode *inode, struct file *file)
{
        printk("hello misc_relaease bye bye \n ");
        return 0;
}

int misc_open(struct inode *inode, struct file *file)
{
        printk("hello misc_open\n ");
        return 0;
}

全部代码

led.c

#include <linux/init.h> //初始化头文件
#include <linux/module.h> //最基本的文件,支持动态添加和卸载模块。
#include <linux/miscdevice.h> //注册杂项设备头文件
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/io.h>

#define GPIO4_DR 0xfe200000
#define GPIO4_H  0xfe20001c
#define GPIO4_L  0xfe200028

unsigned int *vir_gpio4_dr=NULL;
unsigned int *vir_gpio4_h=NULL;
unsigned int *vir_gpio4_l=NULL;
ssize_t misc_read(struct file *file, char __user *ubuf, size_t size, loff_t *loff_t)
{
        char kbuf[] = "hehe";
        if (copy_to_user(ubuf, kbuf, strlen(kbuf)) != 0)
        {
                printk("copy_to_user error\n ");
                return -1;
        }
        printk("misc_read\n ");
        return 0;
}

ssize_t misc_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff_t)
{
        char kbuf[64] = {0};
        if (copy_from_user(kbuf, ubuf, size) != 0)
        {
                printk("copy_from_user error\n ");
                return -1;
        }
        printk("kbuf is %s\n ", kbuf);
        *vir_gpio4_dr |=(1<<(3*4));
        if(kbuf[0]==1)
        {

                *vir_gpio4_h |=(1<<4);
        }
        else if(kbuf[0]==0)
        {
                *vir_gpio4_l |=(1<<4);
        }
        return 0;
}

int misc_release(struct inode *inode, struct file *file)
{
        printk("hello misc_relaease bye bye \n ");
        return 0;
}

int misc_open(struct inode *inode, struct file *file)
{
        printk("hello misc_open\n ");
        return 0;
}
//文件操作集
struct file_operations misc_fops = {
                .owner = THIS_MODULE,
                .open = misc_open,
                .release = misc_release,
                .read = misc_read,
                .write = misc_write,
                };
//miscdevice 结构体
struct miscdevice misc_dev = {
        .minor = MISC_DYNAMIC_MINOR,
        .name = "hello_misc",
        .fops = &misc_fops,
};
static int misc_init(void)
{
        int ret;
        ret = misc_register(&misc_dev); //注册杂项设备
        if (ret < 0)
        {
                printk("misc registe is error \n");
        }
        printk("misc registe is succeed \n");

        vir_gpio4_dr=ioremap(GPIO4_DR,4);
        if(vir_gpio4_dr==NULL )
        {
                printk("gpio4dr ioremap error\n");
                return EBUSY;
        }

        vir_gpio4_h=ioremap(GPIO4_H,4);
        if( vir_gpio4_h==NULL)
        {
                printk("gpio4h ioremap error\n");
                return EBUSY;
        }

        vir_gpio4_l=ioremap(GPIO4_L,4);
        if(vir_gpio4_l==NULL)
        {
                printk("gpio4l ioremap error\n");
                return EBUSY;
        }
        printk("gpio ioremap success\n");
        return 0;
}
static void misc_exit(void)
{
        misc_deregister(&misc_dev); //卸载杂项设备
        printk(" misc gooodbye! \n");
        iounmap(vir_gpio4_dr);
        iounmap(vir_gpio4_h);
        iounmap(vir_gpio4_l);
}
module_init(misc_init);
module_exit(misc_exit);
MODULE_LICENSE("GPL");

还要写个应用层的测试文件来控制LED的亮灭
app.c

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{
        int fd;

        char buf[64] = "0";

        fd = open("/dev/hello_misc",O_RDWR);//打开设备节点

        if(fd < 0)
        {
                perror("open error \n");
                return fd;
        }

        buf[0]=atoi(argv[1]);
        //read(fd,buf,sizeof(buf));

        write(fd,buf,sizeof(buf)); //向内核层写数据

        //printf("buf is %s\n",buf);

        close(fd);

        return 0;
}

Makefile每次只需要改变要编译的文件名就行
Makefile

# 判断是否在内核构建系统内。如果没有定义 KERNELRELEASE,则表示这是从命令行调用。
ifneq ($(KERNELRELEASE),)
        # 如果不是内核构建系统,则定义需要编译的模块对象文件。
        # obj-m 是内核模块的编译变量,+= 表示添加模块文件(.o)
        obj-m += led.o
else
        # 定义内核头文件的位置,使用当前正在运行的内核版本。
        KDIR := /home/interest/linux/lib/modules/$(shell uname -r)/build

        # 定义当前的工作目录。
        PWD := $(shell pwd)

        # 默认目标。如果调用了 make 而没有指定目标,会执行这个部分。
        # -C $(KDIR) 表示切换到内核源码目录进行编译
        # M=$(PWD) 表示在当前模块的目录下执行内核模块编译
all:
        $(MAKE) -C $(KDIR) M=$(PWD) modules

        # 清理目标:用于清理编译产生的中间文件。
clean:
        rm -f *.mod.c *.order *.ko *.o *.mod *.symvers
endif

测试

将led一遍接gpio4的引脚,一遍接GND

Make //编译驱动代码
gcc app.c -o app //编译测试代码
sudo insmod led.ko //加载驱动模块
sudo ./app 1 //led亮
sudo ./app 0 //led灭
sudo rmmod led //卸载模块

完成!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值