史志网站建设,电商网站设计规范,wordpress付费主题博客,女生适合学计算机的哪个专业一、 字符设备驱动简介 字符设备是Linux驱动中最基本的一类设备驱动#xff0c;字符设备就是一个一个字节#xff0c;按照字节流进行读写操作的设备#xff0c;读写数据是分先后顺序的。比如常见的点灯、按键、IIC、SPI、LCD 等等都是字符设备#xff0c;这些设备的驱动就叫…一、 字符设备驱动简介 字符设备是Linux驱动中最基本的一类设备驱动字符设备就是一个一个字节按照字节流进行读写操作的设备读写数据是分先后顺序的。比如常见的点灯、按键、IIC、SPI、LCD 等等都是字符设备这些设备的驱动就叫做字符设备驱动。
Linux驱动基本原理Linux中一切皆为文件驱动加载成功后会在/dev目录下生成一个相应的文件应用程序通过对这个名为/dev/xxx的文件进行相应的操作即可实现对硬件的操作。
比如LED驱动会有/dev/led驱动文件应用程序使用open函数来打开该文件若要点亮或关闭led就使用write函数写入开关值若要获取led灯的状态就用read函数从驱动文件中读取相应的状态使用完成后使用close函数关闭该驱动文件。
Linux驱动运行方式有以下两种
将驱动编译进内核中 当Linux内核启动时就会自动运行驱动程序将驱动编译成模块 在内核启动后使用insmod命令加载驱动模块
在驱动开发阶段一般都将其编译为模块不需要编译整个Linux代码方便调试驱动程序。当驱动开发完成后根据实际需要可以选择是否将驱动编译进Linux内核中。
二、字符设备驱动模板
包括读写函数、poll机制、异步通知、定时器、中断、自动创建设备节点和环形缓冲区。
#include linux/module.h
#include linux/poll.h
#include linux/fs.h
#include linux/errno.h
#include linux/miscdevice.h
#include linux/kernel.h
#include linux/major.h
#include linux/mutex.h
#include linux/proc_fs.h
#include linux/seq_file.h
#include linux/stat.h
#include linux/init.h
#include linux/device.h
#include linux/tty.h
#include linux/kmod.h
#include linux/gfp.h
#include linux/gpio/consumer.h
#include linux/platform_device.h
#include linux/of_gpio.h
#include linux/of_irq.h
#include linux/interrupt.h
#include linux/irq.h
#include linux/slab.h
#include linux/fcntl.h
#include linux/timer.hstruct gpio_desc{int gpio;int irq;char *name;int key;struct timer_list key_timer;//定时器结构体
} ;static struct gpio_desc gpios[2] {{131, 0, gpio_100ask_1, },{132, 0, gpio_100ask_2, },
};/* 主设备号 */
static int major 0;
static struct class *gpio_class;/* 环形缓冲区 */
#define BUF_LEN 128
static int g_keys[BUF_LEN];
static int r, w;struct fasync_struct *button_fasync;#define NEXT_POS(x) ((x1) % BUF_LEN) //取模运算算出下一个位置static int is_key_buf_empty(void)
{return (r w);
}static int is_key_buf_full(void)
{return (r NEXT_POS(w));
}static void put_key(int key)
{if (!is_key_buf_full()){g_keys[w] key;w NEXT_POS(w);}
}static int get_key(void)
{int key 0;if (!is_key_buf_empty()){key g_keys[r];r NEXT_POS(r);}return key;
}static DECLARE_WAIT_QUEUE_HEAD(gpio_wait);// static void key_timer_expire(struct timer_list *t)
static void key_timer_expire(unsigned long data)
{/* data gpio */// struct gpio_desc *gpio_desc from_timer(gpio_desc, t, key_timer);struct gpio_desc *gpio_desc (struct gpio_desc *)data;int val;int key;val gpio_get_value(gpio_desc-gpio);//printk(key_timer_expire key %d %d\n, gpio_desc-gpio, val);key (gpio_desc-key) | (val8);put_key(key);//按键值放入环形缓冲区wake_up_interruptible(gpio_wait);kill_fasync(button_fasync, SIGIO, POLL_IN);
}/* 实现对应的open/read/write等函数填入file_operations结构体 */
static ssize_t gpio_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{//printk(%s %s line %d\n, __FILE__, __FUNCTION__, __LINE__);int err;int key;if (is_key_buf_empty() (file-f_flags O_NONBLOCK))return -EAGAIN;wait_event_interruptible(gpio_wait, !is_key_buf_empty());key get_key();err copy_to_user(buf, key, 4);return 4;
}static ssize_t gpio_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{unsigned char ker_buf[2];int err;if (size ! 2)return -EINVAL;err copy_from_user(ker_buf, buf, size);if (ker_buf[0] sizeof(gpios)/sizeof(gpios[0]))return -EINVAL;gpio_set_value(gpios[ker_buf[0]].gpio, ker_buf[1]);return 2;
}static unsigned int gpio_drv_poll(struct file *fp, poll_table * wait)
{//printk(%s %s line %d\n, __FILE__, __FUNCTION__, __LINE__);poll_wait(fp, gpio_wait, wait);return is_key_buf_empty() ? 0 : POLLIN | POLLRDNORM;
}static int gpio_drv_fasync(int fd, struct file *file, int on)
{if (fasync_helper(fd, file, on, button_fasync) 0)return 0;elsereturn -EIO;
}/* 定义自己的file_operations结构体*/
static struct file_operations gpio_key_drv {.owner THIS_MODULE,.read gpio_drv_read,.write gpio_drv_write,.poll gpio_drv_poll,.fasync gpio_drv_fasync,
};static irqreturn_t gpio_key_isr(int irq, void *dev_id)
{struct gpio_desc *gpio_desc dev_id;printk(gpio_key_isr key %d irq happened\n, gpio_desc-gpio);//定时器 用来消除抖动mod_timer(gpio_desc-key_timer, jiffies HZ/5);//修改定时器的超时时间 jiffies(当前时间) 赫兹/5return IRQ_HANDLED;//成功处理
}/* 在入口函数 */
static int __init gpio_drv_init(void)
{int err;int i;int count sizeof(gpios)/sizeof(gpios[0]);printk(%s %s line %d\n, __FILE__, __FUNCTION__, __LINE__);for (i 0; i count; i){ gpios[i].irq gpio_to_irq(gpios[i].gpio);//引脚编号转换成中断号//设置定时器定时器结构体定时器超时函数传给超时函数的参数setup_timer(gpios[i].key_timer, key_timer_expire, (unsigned long)gpios[i]);//timer_setup(gpios[i].key_timer, key_timer_expire, 0);更高版本的内核用该函数gpios[i].key_timer.expires ~0;//超时时间无穷大//启动定时器add_timer(gpios[i].key_timer);//注册中断 //中断号 中断处理函数 中断触发类型 名字不重要 最后一个参数是用户自行决定是否要传给中断函数的参数err request_irq(gpios[i].irq, gpio_key_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 100ask_gpio_key, gpios[i]);}/* 注册file_operations 注册字符设备驱动程序*/ major register_chrdev(0, 100ask_gpio_key, gpio_key_drv); /* /dev/gpio_desc */gpio_class class_create(THIS_MODULE, 100ask_gpio_key_class);if (IS_ERR(gpio_class)) {printk(%s %s line %d\n, __FILE__, __FUNCTION__, __LINE__);unregister_chrdev(major, 100ask_gpio_key);return PTR_ERR(gpio_class);}device_create(gpio_class, NULL, MKDEV(major, 0), NULL, 100ask_gpio); /*/dev/100ask_gpio */return err;
}/* 有入口函数就应该有出口函数卸载驱动程序时就会去调用这个出口函数 */
static void __exit gpio_drv_exit(void)
{int i;int count sizeof(gpios)/sizeof(gpios[0]);printk(%s %s line %d\n, __FILE__, __FUNCTION__, __LINE__);device_destroy(gpio_class, MKDEV(major, 0));class_destroy(gpio_class);unregister_chrdev(major, 100ask_gpio_key);for (i 0; i count; i){free_irq(gpios[i].irq, gpios[i]);del_timer(gpios[i].key_timer);}
}module_init(gpio_drv_init);
module_exit(gpio_drv_exit);MODULE_LICENSE(GPL);