最简单的GPIO点灯驱动

1:本文只是简单的通过gpio来控制亮灯的程序,我不是大神,但我在努力,请各位多多指教

首先我们要写的是一个驱动,而驱动又必须编译进内核才可以,那怎么样内核才知道你的程序是驱动而不是一般程序呢?没错,我们有现成的宏来干这个事。

点击(此处)折叠或打开

  1. module_init();

  1. module_exit();

如果你问我这些宏是怎么来的,我只能说它们来自内核,而它们的目的就是告诉内核这是个驱动程序,装载是调用module_init();所指定的函数,而卸载是调用module_exit();所指定的函数,同时它们还可以验证函数的加载方式等其它功能。

既然我们使用的是gpio引脚,自然需要它们的地址咯!

点击(此处)折叠或打开

  1. volatile unsigned long *gpfcon = NULL;

  1. volatile unsigned long *gpfdat = NULL;

Volatile:这个关键字的意思就是禁止编译器的优化,我们知道计算机在存储的时候为了存储的速度快,会把某些地址的值拷贝到寄存器中,这样以后直接读取寄存器的值速度会加快,但是有时候我们需要的值变化特别快,或者会在我们不知道的情况下改变它的值,而寄存器的值和内存地址的值可能不同步,那时我们就希望编译器不要优化,直接去读取内存地址的值,这时我们就用到了这个关键字。

上面说到module_init();会指定一个函数作为驱动的入口函数,可是内核没有提供入口函数,所以要我们自己去写

加入入口函数是:static int first_drv_init(void);

此时宏module_init();改为module_init(first_drv_init);这样驱动就知道从哪里开始执行了,

那么入口函数里面我们都做什么呢?

看看内核就会知道,内核给我们提供了一个结构体file_operations,是一个字符设备把驱动的操作和设备号联系在一起的纽带,是一系列指针的集合,每个被打开的文件都对应于一系列的操作,这就是file_operations,用来执行一系列的系统调用。我们用的是简单的gpio点灯,所以我们用的成员不多,如下只有open和write

点击(此处)折叠或打开

  1. static struct file_operations first_drv_fops = {

  1. .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */

  1. .open = first_drv_open,

  1. .write = first_drv_write,

  1. };

既然是函数指针,那么函数 first_drv_open就得我们自己去写啦!

点击(此处)折叠或打开

  1. static int first_drv_open(struct inode *inode, struct file *file)

  1. {

  1. //printk("first_drv_open\n");

  1. /* 配置GPF4,5,6为输出 */

  1. *gpfcon &= ~((0x3<<(4*2)) | (0x3<<(5*2)) | (0x3<<(6*2)));

  1. *gpfcon |= ((0x1<<(4*2)) | (0x1<<(5*2)) | (0x1<<(6*2)));

  1. return 0;

  1. }

在open函数中我们就是配置了gpio的配置引脚让他们具有对应的功能,具体应该参照原理图来写,这里我们用的是三星的s3c2440的芯片,

接下来就是write操作

点击(此处)折叠或打开

  1. static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)

  1. {

  1. int val;

  1. //printk("first_drv_write\n");

  1. copy_from_user(&val, buf, count); // copy_to_user();

  1. if (val == 1)

  1. {

  1. // 点灯

  1. *gpfdat &= ~((1<<4) | (1<<5) | (1<<6));

  1. }

  1. else

  1. {

  1. // 灭灯

  1. *gpfdat |= (1<<4) | (1<<5) | (1<<6);

  1. }

  1. return 0;

  1. }

这里主要是对gpio引脚的数据寄存器放入1,0,来控制对应引脚是否为高点平,低电平,

上面说了内核给了我们module_init();module_exit();和file_operations,前面的宏自然不用多说,内核自然回去识别,可是我们的file_operations结构体有了我们自己取的名字,内核还认识吗?这里我们就要使用另一个函数了

点击(此处)折叠或打开

  1. register_chrdev(0, "first_drv", &first_drv_fops); // 注册, 告诉内核

这个函数的功能就是告诉内核,我这个驱动的file_operations结构体是first_drv_fops,驱动名字是first_drv,0表示自动分配一个设备号,

当然在卸载驱动是我们也要卸载这个结构体

点击(此处)折叠或打开

  1. unregister_chrdev(major, "first_drv"); // 卸载

光这样的话还不行,我们{BANNED}最佳好自己去主动创建节点,不然每次加载驱动都要创建,太麻烦了

点击(此处)折叠或打开

  1. firstdrv_class = class_create(THIS_MODULE, "firstdrv");

  1. firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */

在卸载的时候自然需要销毁节点

点击(此处)折叠或打开

  1. class_device_unregister(firstdrv_class_dev);

  1. class_destroy(firstdrv_class);

细心的人肯定发现了,我们的gpio引脚目前还没有地址呢?来映射一下吧!

点击(此处)折叠或打开

  1. gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);

  1. gpfdat = gpfcon + 1;

这样我们{BANNED}最佳简单的驱动就搞定啦,以下是完整驱动和它的测试程序

A:驱动

点击(此处)折叠或打开

  1. #include <linux/module.h>

  1. #include <linux/kernel.h>

  1. #include <linux/fs.h>

  1. #include <linux/init.h>

  1. #include <linux/delay.h>

  1. #include <asm/uaccess.h>

  1. #include <asm/irq.h>

  1. #include <asm/io.h>

  1. #include <asm/arch/regs-gpio.h>

  1. #include <asm/hardware.h>

  1. static struct class *firstdrv_class;

  1. static struct class_device *firstdrv_class_dev;

  1. volatile unsigned long *gpfcon = NULL;

  1. volatile unsigned long *gpfdat = NULL;

  1. static int first_drv_open(struct inode *inode, struct file *file)

  1. {

  1. //printk("first_drv_open\n");

  1. /* 配置GPF4,5,6为输出 */

  1. *gpfcon &= ~((0x3<<(4*2)) | (0x3<<(5*2)) | (0x3<<(6*2)));

  1. *gpfcon |= ((0x1<<(4*2)) | (0x1<<(5*2)) | (0x1<<(6*2)));

  1. return 0;

  1. }

  1. static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)

  1. {

  1. int val;

  1. //printk("first_drv_write\n");

  1. copy_from_user(&val, buf, count); // copy_to_user();

  1. if (val == 1)

  1. {

  1. // 点灯

  1. *gpfdat &= ~((1<<4) | (1<<5) | (1<<6));

  1. }

  1. else

  1. {

  1. // 灭灯

  1. *gpfdat |= (1<<4) | (1<<5) | (1<<6);

  1. }

  1. return 0;

  1. }

  1. static struct file_operations first_drv_fops = {

  1. .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */

  1. .open = first_drv_open,

  1. .write = first_drv_write,

  1. };

  1. int major;

  1. static int first_drv_init(void)

  1. {

  1. major = register_chrdev(0, "first_drv", &first_drv_fops); // 注册, 告诉内核

  1. firstdrv_class = class_create(THIS_MODULE, "firstdrv");

  1. firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */

  1. gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);

  1. gpfdat = gpfcon + 1;

  1. return 0;

  1. }

  1. static void first_drv_exit(void)

  1. {

  1. unregister_chrdev(major, "first_drv"); // 卸载

  1. class_device_unregister(firstdrv_class_dev);

  1. class_destroy(firstdrv_class);

  1. iounmap(gpfcon);

  1. }

  1. module_init(first_drv_init);

  1. module_exit(first_drv_exit);

  1. MODULE_LICENSE("GPL");

B:测试

点击(此处)折叠或打开

  1. #include <sys/types.h>

  1. #include <sys/stat.h>

  1. #include <fcntl.h>

  1. #include <stdio.h>

  1. /* firstdrvtest on

  1. * firstdrvtest off

  1. */

  1. int main(int argc, char **argv)

  1. {

  1. int fd;

  1. int val = 1;

  1. fd = open("/dev/xyz", O_RDWR);

  1. if (fd < 0)

  1. {

  1. printf("can't open!\n");

  1. }

  1. if (argc != 2)

  1. {

  1. printf("Usage :\n");

  1. printf("%s \n", argv[0]);

  1. return 0;

  1. }

  1. if (strcmp(argv[1], "on") == 0)

  1. {

  1. val = 1;

  1. }

  1. else

  1. {

  1. val = 0;

  1. }

  1. write(fd, &val, 4);

  1. return 0;

  1. }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值