Linux设备驱动总结(二)-----Linux让LED灯闪起来


众所周知,LED灯闪烁实验,就相当于编程中的helloworld,是最简单入门,也是可以帮助广大爱好学习者建立自信的,看了设备驱动一个多月,终于我也让自己的led灯闪亮了。

要让led灯闪烁,就是要控制引脚输入高低电平,不断交替变化,然而Linux中,上面的文章提到,驱动和应用是分开的,驱动层对硬件直接操作,应用层通过调用驱动层接口,来实现逻辑功能。

我们先来看驱动程序:(驱动程序涉及太多知识,建议大家去看国嵌Linux的设备驱动的视频,讲的很详细,基础知识不赘述,只是简单总结一下)

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <mach/gpio.h>

//定义命令
#define LED_ON		_IOW('h',0x01,unsigned long)		//LED开的命令
#define LED_OFF		_IOW('h',0x02,unsigned long)		//LED关的命令


int major_no = 255;			//定义主设备号
struct cdev cdev;			//字符设备的数据结构
dev_t devno;					//设备号
int open_state = 0;			//1为打开,0为关闭

/*-----------------------------------------------------------------------------
  函数名:      led_open
  参数:        struct inode *inode,struct file *filp
  返回值:      int
  描述:        open对应的驱动函数
 *-----------------------------------------------------------------------------*/
int led_open(struct inode *inode,struct file *filp )
{
	if(open_state == 0)
	{
		open_state =  1;
		printk("Open file suc!\n");
		return 0;
	}
	else
	{
		printk("The file has opened!\n");
		return -1;
	}
}

/*-----------------------------------------------------------------------------
  函数名:      led_ioctl
  参数:        struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg
  返回值:      int
  描述:        ioctl对应的驱动函数
 *-----------------------------------------------------------------------------*/
int led_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg)
{
	switch(cmd)
	{
		case LED_ON: printk("ON!\n");
						at91_set_gpio_value(AT91_PIN_PC0, 1); 			//灯亮起来
						break;

		case LED_OFF:printk("OFF\n");
						at91_set_gpio_value(AT91_PIN_PC0, 0); 			//灯灭掉
						break;

		default :printk("Error command!\n");
	}
	return 0;
}

const struct file_operations led_fop =
{
	.owner = THIS_MODULE,
	.open  = led_open,
	.ioctl = led_ioctl,
};
/*-----------------------------------------------------------------------------
  函数名:      gpio_init
  参数:        void
  返回值:      int
  描述:        模块初始化函数,在安装模块时候执行
 *-----------------------------------------------------------------------------*/
static int __init gpio_init(void)  
{
	int ret;

	printk("------GPIO test init-----\n");
	printk("Register the char dev!\n");

	/**
	 * 静态创建前,可以查看/proc/devices 查看已经被使用的设备号
	 */
	devno = MKDEV(major_no,0);
	ret = register_chrdev_region(devno,1,"led");		//向内核注册设备号
	if(ret < 0)
	{
		printk("Register Error!\n");
		return ret;
	}

	/**
	 * 初始化字符设备
	 */
	cdev_init(&cdev,&led_fop);
	cdev.owner = THIS_MODULE;
	cdev.ops   = &led_fop;
	cdev_add(&cdev,devno,1);						//向内核注册添加字符设备

	at91_set_gpio_output(AT91_PIN_PC0,1);		//设置引脚为输出功能,且为高电平
	return 0;	
}

/*-----------------------------------------------------------------------------
  函数名:      gpio_exit
  参数:        void
  返回值:      void
  描述:        模块卸载函数,在卸载模块时候执行
 *-----------------------------------------------------------------------------*/
static void __exit gpio_exit(void)	
{
	unregister_chrdev_region(devno,1);
	open_state = 0;
	printk("GPIO test End\n");
}

module_init(gpio_init);
module_exit(gpio_exit);

MODULE_LICENSE("GPL");

这是笔者自己写的驱动程序,注释非常详细,这里也只是做几个简单说明:

1.笔者的LED灯连接的是AT91sam9260芯片的PC0引脚,高电平点亮,而at91_set_gpio_value类似的函数,就是Linux封装好的,可以清楚的看到,第一个参数是引脚编号,第二个参数是要输出的电平,其它的我们都不用管。不用再对寄存器初始化什么的了。前面提过,这些函数就分装在linux内核源代码下的arch/arm/mach_at91下,在arch/arm/下有很多的目录,里面都封装着对应芯片实现相同功能的函数,读者可以自己去看看。

2.一般设备初始化步骤,首先先申请一个设备号,然后想内核注册字符设备,主要就是关联file_operations这个结构,每次应用程序调用open,其实是通过VFS系统调用成我上面驱动的led_open(这个国嵌的视频讲的也很清楚,看完视频再来看这个驱动程序,一切都很明了)

3.应用程序通过ioctl发送命令给驱动,实际是调用了led_ioctl,这个函数就是解析各种命令,执行相应的动作,就是典型的命令-反应模型!

可能你会觉得很烦,但是不急,看看应用程序,你就会知道,这种分而治之的形式其实很好:

#include <stdio.h>
#include <sys/ioctl.h>
#include <signal.h>
#include <stdlib.h>
#include <time.h>

#define LED_ON		_IOW('h',0x01,unsigned long)		//LED开的命令
#define LED_OFF		_IOW('h',0x02,unsigned long)		//LED关的命令

int fd;

void close_file(int sig_no)
{
	if(SIGINT == sig_no)
	{
		close(fd);
		printf("Close the file suc!\n");
		exit(0);
	}
}
int main(void)
{

	fd = open("/dev/led",0);
	if(fd < 0)
	{
		perror("Open file Error:");
		return -1;
	}

	signal(SIGINT,close_file);
    while(1)
    {
	ioctl(fd,LED_ON);		//led on
	usleep(500000);		//delay 500 ms
	ioctl(fd,LED_OFF);		//led off
	usleep(500000);
    }
	return 0;
}
应用程序,真的就像操作文件一样操作设备了,先打开了设备文件,然后只要往驱动程序发送命令就行了,应用层开发者完全不用理会设备是怎么实现的,这样大大加快开发速度,驱动和应用可以并行。

 

运行结果如下:

首先要创建设备文件:mknod /dev/led c 255 0

c 代表是字符设备,255 是主设备号,0是次设备号

root@at91sam9260ek:/mnt/char_dev# insmod char_dev.ko
------GPIO test init-----
Register the char dev!
root@at91sam9260ek:/mnt/char_dev# mknod /dev/led c 255 0
root@at91sam9260ek:/mnt/char_dev# ./test
Open file suc!
ON!
OFF
ON!
OFF
ON!
OFF
ON!
OFF
ON!
OFF
ON!
OFF
ON!
OFF
ON!
OFF
ON!
OFF
ON!
OFF
ON!
OFF
ON!
OFF
ON!
OFF
ON!
OFF
ON!
OFF
Close the file suc!


 

PS:linux延迟真是简单!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值