一、Git仓库用法
1、linu终端输入下面命令安装
git clone https://e.coding.net/weidongshan/linux_course/linux_basic_develop.git
2、
进入到GIT仓库目录
cd /D/abc/doc_and_source_for_mcu_mpu
在doc_and_source_for_mcu_mpu目录下,执行以下命令获得资料的最新版本。
git pull origin
二、字符设备驱动开发
1、hello驱动程序步骤
- 创建
file_operations
结构体(字符设备驱动的核心) - 注册字符设备设备
- 写入口函数(相当于main)
- 写退出函数
三、字符设备驱动程序源码
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/mman.h>
#include <linux/random.h>
#include <linux/init.h>
#include <linux/raw.h>
#include <linux/tty.h>
#include <linux/capability.h>
#include <linux/ptrace.h>
#include <linux/device.h>
#include <linux/highmem.h>
#include <linux/backing-dev.h>
#include <linux/shmem_fs.h>
#include <linux/splice.h>
#include <linux/pfn.h>
#include <linux/export.h>
#include <linux/io.h>
#include <linux/uio.h>
#include <linux/uaccess.h>
static int major;
static int hello_open (struct inode *node, struct file *filp)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
static ssize_t hello_read (struct file *filp, char __user *buf, size_t size, loff_t *offset)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return size;
}
static ssize_t hello_write(struct file *filp, const char __user *buf, size_t size, loff_t *offset)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return size;
}
static int hello_release (struct inode *node, struct file *filp)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
/* 1. create file_operations */
static const struct file_operations hello_drv = {
.owner = THIS_MODULE,
.read = hello_read,
.write = hello_write,
.open = hello_open,
.release = hello_release,
};
/* 2. register_chrdev */
/* 3. entry function */
static int hello_init(void)
{
major = register_chrdev(0, "100ask_hello", &hello_drv);
return 0;
}
/* 4. exit function */
static void hello_exit(void)
{
unregister_chrdev(major, "100ask_hello");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
用户与内核传数据和创建设备文件驱动程序
#include "asm/cacheflush.h"
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/mman.h>
#include <linux/random.h>
#include <linux/init.h>
#include <linux/raw.h>
#include <linux/tty.h>
#include <linux/capability.h>
#include <linux/ptrace.h>
#include <linux/device.h>
#include <linux/highmem.h>
#include <linux/backing-dev.h>
#include <linux/shmem_fs.h>
#include <linux/splice.h>
#include <linux/pfn.h>
#include <linux/export.h>
#include <linux/io.h>
#include <linux/uio.h>
#include <linux/uaccess.h>
static struct class *hello_class; //定义一个类型,用于创建设备
static int major; //主设备号
static unsigned char hello_buf[100]; //内核数据缓存
static int hello_open (struct inode *node, struct file *filp)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
static ssize_t hello_read (struct file *filp, char __user *buf, size_t size, loff_t *offset)
{
unsigned long len = size > 100 ? 100 : size;
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
copy_to_user(buf, hello_buf, len); //将内核数据发送给用户
return len;
}
static ssize_t hello_write(struct file *filp, const char __user *buf, size_t size, loff_t *offset)
{
unsigned long len = size > 100 ? 100 : size;
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
copy_from_user(hello_buf, buf, len);
return len;
}
static int hello_release(struct inode *node, struct file *filp)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
/* 1. create file_operations */
static const struct file_operations hello_drv = {
.owner = THIS_MODULE,
.read = hello_read,
.write = hello_write,
.open = hello_open,
.release = hello_release,
};
/* 2. register_chrdev */
/* 3. entry function */
static int hello_init(void)
{ //申请设备号,只能进行注测主设备号0-255个设备,次设备号全部被占用
major = register_chrdev(0, "100ask_hello", &hello_drv); //0代表自动寻找设备号,
hello_class = class_create(THIS_MODULE, "hello_class"); //创建一个类型
if (IS_ERR(hello_class)) {
printk("failed to allocate class\n");
return PTR_ERR(hello_class);
}
//创建一个名字为hello的设备
device_create(hello_class, NULL, MKDEV(major, 0), NULL, "hello"); /* /dev/hello */
return 0;
}
/* 4. exit function */
static void hello_exit(void)
{
device_destroy(hello_class, MKDEV(major, 0));
class_destroy(hello_class);
unregister_chrdev(major, "100ask_hello");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
应用
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
/* 写: ./hello_test /dev/xxx 100ask
* 读: ./hello_test /dev/xxx
*/
int main(int argc, char **argv)
{
int fd;
int len;
char buf[100];
if (argc < 2)
{
printf("Usage: \n");
printf("%s <dev> [string]\n", argv[0]);
return -1;
}
// open
fd = open(argv[1], O_RDWR);
if (fd < 0)
{
printf("can not open file %s\n", argv[1]);
return -1;
}
if (argc == 3)
{
// write
len = write(fd, argv[2], strlen(argv[2])+1);
printf("write ret = %d\n", len);
}
else
{
// read
len = read(fd, buf, 100);
buf[99] = '\0';
printf("read str : %s\n", buf);
}
// close
close(fd);
return 0;
}
四、 APP使用驱动的4种方式
1、驱动程序:提供能力,不提供策略
-
非阻塞(查询)
-
阻塞(休眠-唤醒)
-
poll(定个闹钟)
-
异步通知
妈妈怎么知道卧室里小孩醒了? -
时不时进房间看一下: 查询方式
- 简单,但是累
-
进去房间陪小孩一起睡觉,小孩醒了会吵醒她: 休眠-唤醒
-
不累,但是妈妈干不了活了
-
妈妈要干很多活,但是可以陪小孩睡一会,定个闹钟: poll 方式
- 要浪费点时间, 但是可以继续干活。
- 妈妈要么是被小孩吵醒,要么是被闹钟吵醒。
-
妈妈在客厅干活,小孩醒了他会自己走出房门告诉妈妈: 异步通知
- 妈妈、小孩互不耽误
2、中断的引入
- 非阻塞:没有解除睡眠立即返回错误
- 阻塞如下图:
- poll机制如下图
- 异步通知
五、 字符设备的另一种注册方法cdev
驱动程序
#include "asm-generic/errno-base.h"
#include "asm/cacheflush.h"
#include "linux/cdev.h"
#include "linux/fs.h"
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/mman.h>
#include <linux/random.h>
#include <linux/init.h>
#include <linux/raw.h>
#include <linux/tty.h>
#include <linux/capability.h>
#include <linux/ptrace.h>
#include <linux/device.h>
#include <linux/highmem.h>
#include <linux/backing-dev.h>
#include <linux/shmem_fs.h>
#include <linux/splice.h>
#include <linux/pfn.h>
#include <linux/export.h>
#include <linux/io.h>
#include <linux/uio.h>
#include <linux/uaccess.h>
static struct class *hello_class;
static struct cdev hello_cdev;
static dev_t dev;
static unsigned char hello_buf[100];
static int hello_open (struct inode *node, struct file *filp)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
static ssize_t hello_read (struct file *filp, char __user *buf, size_t size, loff_t *offset)
{
unsigned long len = size > 100 ? 100 : size;
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
copy_to_user(buf, hello_buf, len);
return len;
}
static ssize_t hello_write(struct file *filp, const char __user *buf, size_t size, loff_t *offset)
{
unsigned long len = size > 100 ? 100 : size;
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
copy_from_user(hello_buf, buf, len);
return len;
}
static int hello_release (struct inode *node, struct file *filp)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
/* 1. create file_operations */
static const struct file_operations hello_drv = {
.owner = THIS_MODULE,
.read = hello_read,
.write = hello_write,
.open = hello_open,
.release = hello_release,
};
/* 2. register_chrdev */
/* 3. entry function */
static int hello_init(void)
{
int ret;
// register_chrdev
//参数一:分配设备号成功后用来存放分配到的设备号,分配结束后要把主设备号提取出来(major = MAJOR(devno);)因为主设备号会变所以要将变得重新赋值
//参数二:起始的次设备号,一般为0
//参数三:count:申请的设备数量,从起始设备号累加。如果在创建一个设备,主设备号跟前一个一样,次设备号为1的设备,依然可以访问,就是同一个设备。
//参数四:/proc/devices文件中与该设备对应的名字,方便用户层查询主次设备号
ret = alloc_chrdev_region(&dev, 0, 2, "hello"); //自动注册设备
if (ret < 0)
{
printk(KERN_ERR "alloc_chrdev_region() failed for hello\n");
return -EINVAL;
}
cdev_init(&hello_cdev, &hello_drv); //将hello_drv与hello_cdev链接起来
ret = cdev_add(&hello_cdev, dev, 2);//将设备号添加到设备结构体
if (ret)
{
printk(KERN_ERR "cdev_add() failed for hello\n");
return -EINVAL;
}
hello_class = class_create(THIS_MODULE, "hello_class");
if (IS_ERR(hello_class)) {
printk("failed to allocate class\n");
return PTR_ERR(hello_class);
}
//创建一名字为hello的设备
device_create(hello_class, NULL, dev, NULL, "hello"); /* /dev/hello */
return 0;
}
/* 4. exit function */
static void hello_exit(void)
{
device_destroy(hello_class, dev);
class_destroy(hello_class);
//unregister_chrdev(major, "100ask_hello");
cdev_del(&hello_cdev);
unregister_chrdev_region(dev, 2);
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
app
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
/* 写: ./hello_test /dev/xxx 100ask
* 读: ./hello_test /dev/xxx
*/
int main(int argc, char **argv)
{
int fd;
int len;
char buf[100];
if (argc < 2)
{
printf("Usage: \n");
printf("%s <dev> [string]\n", argv[0]);
return -1;
}
// open
fd = open(argv[1], O_RDWR);
if (fd < 0)
{
printf("can not open file %s\n", argv[1]);
return -1;
}
if (argc == 3)
{
// write
len = write(fd, argv[2], strlen(argv[2])+1);
printf("write ret = %d\n", len);
}
else
{
// read
len = read(fd, buf, 100);
buf[99] = '\0';
printf("read str : %s\n", buf);
}
// close
close(fd);
return 0;
}
六、 通用框架1
1、GPIO子系统
在开发板上执行如下命令查看已经在使用的GPIO状态:
# cat /sys/kernel/debug/gpio
gpiochip0: GPIOs 0-15, parent: platform/soc:pin-controller@50002000, GPIOA:
gpio-10 ( |heartbeat ) out lo
gpio-14 ( |shutdown ) out hi
gpiochip1: GPIOs 16-31, parent: platform/soc:pin-controller@50002000, GPIOB:
gpio-26 ( |reset ) out hi ACTIVE LOW
gpiochip2: GPIOs 32-47, parent: platform/soc:pin-controller@50002000, GPIOC:
gpiochip3: GPIOs 48-63, parent: platform/soc:pin-controller@50002000, GPIOD:
怎么确定GPIO引脚的编号?方法如下:
① 先在开发板的/sys/class/gpio目录下,找到各个gpiochipXXX目录:(这个后面的数是起始地址,不跟上面那个查到的组数一样)
② 然后进入某个gpiochipXXX目录,查看文件label的内容,就可以知道起始号码XXX对于哪组GPIO
那么GPIO4_14的号码是96+14=110,可以如下操作读取按键值:
[root@100ask:~]# echo 110 > /sys/class/gpio/export // gpio_request
[root@100ask:~]# echo in > /sys/class/gpio/gpio110/direction // gpio_direction_input
[root@100ask:~]# cat /sys/class/gpio/gpio110/value // gpio_get_value
[root@100ask:~]# echo 110 > /sys/class/gpio/unexport // gpio_free
GPIO子系统函数有新、老两套:
descriptor-based | legacy |
---|---|
获得GPIO | |
gpiod_get | gpio_request |
gpiod_get_index | |
gpiod_get_array | gpio_request_array |
devm_gpiod_get | |
devm_gpiod_get_index | |
devm_gpiod_get_array | |
设置方向 | |
gpiod_direction_input | gpio_direction_input |
gpiod_direction_output | gpio_direction_output |
读值、写值 | |
gpiod_get_value | gpio_get_value |
gpiod_set_value | gpio_set_value |
释放GPIO | |
gpio_free | gpio_free |
gpiod_put | gpio_free_array |
gpiod_put_array | |
devm_gpiod_put | |
devm_gpiod_put_array |
2、中断函数
2.1 使用中断的流程
在驱动程序里使用中断的流程如下:
-
确定中断号
-
注册中断处理函数,函数原型如下:
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev);
-
在中断处理函数里
- 分辨中断
- 处理中断
- 清除中断
2.2 函数细节
request_irq函数的第1个参数是中断号,可以根据GPIO函数获得中断号:
int gpio_to_irq(unsigned int gpio);
int gpiod_to_irq(const struct gpio_desc *desc);
request_irq函数的第2个参数是函数指针:
enum irqreturn {
IRQ_NONE = (0 << 0),
IRQ_HANDLED = (1 << 0),
IRQ_WAKE_THREAD = (1 << 1),
};
typedef enum irqreturn irqreturn_t;
typedef irqreturn_t (*irq_handler_t)(int irq, void *dev);
request_irq函数的第3个参数有如下取值:
(上升沿触发、下降沿触发…)
#define IRQF_TRIGGER_NONE 0x00000000
#define IRQF_TRIGGER_RISING 0x00000001
#define IRQF_TRIGGER_FALLING 0x00000002
#define IRQF_TRIGGER_HIGH 0x00000004
#define IRQF_TRIGGER_LOW 0x00000008
#define IRQF_SHARED 0x00000080
request_irq函数的第4个参数是中断的名字,可以在执行cat /proc/interrupts
的结果里查看。
request_irq函数的第5个参数是给中断处理函数使用的。
2.3 代码
driver
#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.h>
struct 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", 1,},
{132, 0, "gpio_100ask_2", 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) ((x+1) % 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); //读取引脚 0或1
//printk("key_timer_expire key %d %d\n", gpio_desc->gpio, val);
key = (gpio_desc->key) | (val<<8); //key值用来放在环形数组中,用key第八位看按键是否按下
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;
else
return -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);
//jiffies是全局变量
mod_timer(&gpio_desc->key_timer, jiffies + HZ/5); //消抖,每次抖动时长小于(HZ/5)*Tms,都会推迟进入中断服务程序
return 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);
}
}
/* 7. 其他完善:提供设备信息,自动创建设备节点 */
module_init(gpio_drv_init);
module_exit(gpio_drv_exit);
MODULE_LICENSE("GPL");
app
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <poll.h>
#include <signal.h>
static int fd;
/*
* ./button_test /dev/100ask_button0
*
*/
int main(int argc, char **argv)
{
int val;
struct pollfd fds[1];
int timeout_ms = 5000;
int ret;
int flags;
int i;
/* 1. 判断参数 */
if (argc != 2)
{
printf("Usage: %s <dev>\n", argv[0]);
return -1;
}
/* 2. 打开文件 */
fd = open(argv[1], O_RDWR | O_NONBLOCK);
if (fd == -1)
{
printf("can not open file %s\n", argv[1]);
return -1;
}
for (i = 0; i < 10; i++)
{
if (read(fd, &val, 4) == 4)
printf("get button: 0x%x\n", val);
else
printf("get button: -1\n");
}
flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
while(1)
{
if (read(fd, &val, 4) == 4)
printf("get button: 0x%x\n", val);
else
printf("while get button: -1\n");
}
close(fd);
return 0;
}
非阻塞及环形读写
阻塞访问
POLL机制
异步通知
LED驱动编写(imx6ull)
driver
#include "asm-generic/errno-base.h"
#include "asm-generic/gpio.h"
#include "asm/uaccess.h"
#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.h>
struct gpio_desc{
int gpio;
int irq;
char *name;
int key;
struct timer_list key_timer;
} ;
static struct gpio_desc gpios[2] = {
{131, 0, "led0", },
//{132, 0, "led1", },
};
/* 主设备号 */
static int major = 0;
static struct class *gpio_class;
/* 实现对应的open/read/write等函数,填入file_operations结构体 */
static ssize_t gpio_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
char tmp_buf[2];
int err;
int count = sizeof(gpios)/sizeof(gpios[0]); //表示有多少可操作引脚
if (size != 2)
return -EINVAL;
err = copy_from_user(tmp_buf, buf, 1);
if (tmp_buf[0] >= count) //大于可操作数量报错
return -EINVAL;
tmp_buf[1] = gpio_get_value(gpios[tmp_buf[0]].gpio);
err = copy_to_user(buf, tmp_buf, 2);
return 2;
}
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) //传入的参数不是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;
}
/* 定义自己的file_operations结构体 */
static struct file_operations gpio_key_drv = {
.owner = THIS_MODULE,
.read = gpio_drv_read,
.write = gpio_drv_write,
};
/* 在入口函数 */
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++)
{
/* set pin as output */
err = gpio_request(gpios[i].gpio, gpios[i].name);//其实就是让内核检查一下该GPIO引脚是否被其它设备占用,如果没有占用则返回0并用label做一下标记,表示被本设备占用,否则返回负数
if (err < 0) {
printk("can not request gpio %s %d\n", gpios[i].name, gpios[i].gpio);
return -ENODEV;
}
(gpios[i].gpio, 1); //设置gpio输出为1
}
/* 注册file_operations */
major = register_chrdev(0, "100ask_led", &gpio_key_drv); /* /dev/gpio_desc */
gpio_class = class_create(THIS_MODULE, "100ask_led_class");
if (IS_ERR(gpio_class)) {
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
unregister_chrdev(major, "100ask_led_class");
return PTR_ERR(gpio_class);
}
device_create(gpio_class, NULL, MKDEV(major, 0), NULL, "100ask_led"); /* /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_led");
for (i = 0; i < count; i++)
{
gpio_free(gpios[i].gpio);
}
}
/* 7. 其他完善:提供设备信息,自动创建设备节点 */
module_init(gpio_drv_init);
module_exit(gpio_drv_exit);
MODULE_LICENSE("GPL");
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <poll.h>
#include <signal.h>
static int fd;
//int led_on(int which);
//int led_off(int which);
//int led_status(int which);
/*
* ./led_test <0|1|2|..> on
* ./led_test <0|1|2|..> off
* ./led_test <0|1|2|..>
*/
int main(int argc, char **argv)
{
int ret;
char buf[2];
int i;
/* 1. 判断参数 */
if (argc < 2)
{
printf("Usage: %s <0|1|2|...> [on | off]\n", argv[0]);
return -1;
}
/* 2. 打开文件 */
fd = open("/dev/100ask_led", O_RDWR);
if (fd == -1)
{
printf("can not open file /dev/100ask_led\n");
return -1;
}
if (argc == 3)
{
/* write */
buf[0] = strtol(argv[1], NULL, 0); //将字符串内容转化成整数
if (strcmp(argv[2], "on") == 0) //strcmp字符串比较,如果相等返回0
buf[1] = 0;
else
buf[1] = 1;
ret = write(fd, buf, 2);
}
else
{
buf[0] = strtol(argv[1], NULL, 0);
ret = read(fd, buf, 2);
if (ret == 2)
{
printf("led %d status is %s\n", buf[0], buf[1] == 0 ? "on" : "off");
}
}
close(fd);
return 0;
}
SR501红外模块驱动编写(imx6ull)
驱动
#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.h>
struct gpio_desc{ //定义一个gpio结构体
int gpio;
int irq;
char *name;
int key;
struct timer_list key_timer;
} ;
static struct gpio_desc gpios[2] = { //赋初值
{115, 0, "sr501", }, //io口的编号,0号中断,名称
};
/* 主设备号 */
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; //异步通信用的信号结构体,他可以提供一个pid用于信号通信
#define NEXT_POS(x) ((x+1) % BUF_LEN) //环形接收对数组的下标操作
static int is_key_buf_empty(void) //接收数组为空
{
return (r == w);
}
static int is_key_buf_full(void) //如果接收数组已满将
{
return (r == NEXT_POS(w)); //读下标等于写下标(x)+1就代表满了
}
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); //初始化gpio休眠队列
/* 实现对应的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 unsigned int gpio_drv_poll(struct file *fp, poll_table * wait) //poll机制,这次没用着
{
//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) //构造button_fasync结构体,方便后面程序寻找pid
{
if (fasync_helper(fd, file, on, &button_fasync) >= 0)
return 0;
else
return -EIO;
}
/* 定义自己的file_operations结构体 */
static struct file_operations gpio_key_drv = {
.owner = THIS_MODULE,
.read = gpio_drv_read,
.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;
int val;
int key;
printk("gpio_key_isr key %d irq happened\n", gpio_desc->gpio);
val = gpio_get_value(gpio_desc->gpio);
//printk("key_timer_expire key %d %d\n", gpio_desc->gpio, val);
key = (gpio_desc->key) | (val<<8);
put_key(key);
wake_up_interruptible(&gpio_wait);
kill_fasync(&button_fasync, SIGIO, POLL_IN);
return 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);
err = request_irq(gpios[i].irq, gpio_key_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, gpios[i].name, &gpios[i]); //使能按键中断,中断号为gpios[i].irq,中断服务函数gpio_key_isr,触发方式为上升沿下降沿触发IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
}
/* 注册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, "sr501"); /* /dev/sr501 */
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]);
}
}
/* 7. 其他完善:提供设备信息,自动创建设备节点 */
module_init(gpio_drv_init);
module_exit(gpio_drv_exit);
MODULE_LICENSE("GPL");
app
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <poll.h>
#include <signal.h>
static int fd;
/*
* ./button_test /dev/sr501
*
*/
int main(int argc, char **argv)
{
int val;
struct pollfd fds[1];
int timeout_ms = 5000;
int ret;
int flags;
int i;
/* 1. 判断参数 */
if (argc != 2)
{
printf("Usage: %s <dev>\n", argv[0]);
return -1;
}
/* 2. 打开文件 */
fd = open(argv[1], O_RDWR);
if (fd == -1)
{
printf("can not open file %s\n", argv[1]);
return -1;
}
while (1)
{
if (read(fd, &val, 4) == 4)
printf("get button: 0x%x\n", val);
else
printf("get button: -1\n");
}
close(fd);
return 0;
}
SR04超声波测距模块驱动
驱动
#include "asm-generic/gpio.h"
#include "asm/delay.h"
#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.h>
#define CMD_TRIG 100 //ioctrl任务选择常量
struct gpio_desc{
int gpio;
int irq;
char *name;
int key;
struct timer_list key_timer;
} ;
static struct gpio_desc gpios[2] = { //定义两个io口
{115, 0, "trig", },
{116, 0, "echo", },
};
/* 主设备号 */
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) ((x+1) % 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);
/* 实现对应的open/read/write等函数,填入file_operations结构体 */
static ssize_t sr04_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;
// printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); 加上这条语句就读不出来数据了,可能是函数内部关了中断
wait_event_interruptible(gpio_wait, !is_key_buf_empty());
key = get_key();
err = copy_to_user(buf, &key, 4);
return 4;
}
static unsigned int sr04_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 sr04_fasync(int fd, struct file *file, int on)
{
if (fasync_helper(fd, file, on, &button_fasync) >= 0)
return 0;
else
return -EIO;
}
// ioctl(fd, CMD, ARG)
static long sr04_ioctl(struct file *filp, unsigned int command, unsigned long arg)
{
// send trig
switch (command)
{
case CMD_TRIG:
{
//printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
gpio_set_value(gpios[0].gpio, 1); //根据超声波传感器手册启动前要发送一个20us高电平
udelay(20);
gpio_set_value(gpios[0].gpio, 0);
}
}
return 0;
}
/* 定义自己的file_operations结构体 */
static struct file_operations sr04_drv = {
.owner = THIS_MODULE,
.read = sr04_read,
.poll = sr04_poll,
.fasync = sr04_fasync,
.unlocked_ioctl = sr04_ioctl,
};
static irqreturn_t sr04_isr(int irq, void *dev_id)
{
struct gpio_desc *gpio_desc = dev_id;
int val;
static u64 rising_time = 0;
u64 time;
val = gpio_get_value(gpio_desc->gpio);
//printk("sr04_isr echo pin %d is %d\n", gpio_desc->gpio, val);如果加上打印会超时
if (val)
{
/* 上升沿记录起始时间 */
rising_time = ktime_get_ns();
}
else
{
if (rising_time == 0)
{
//printk("missing rising interrupt\n");
return IRQ_HANDLED;
}
/* 下降沿记录结束时间, 并计算时间差, 计算距离 */
time = ktime_get_ns() - rising_time;
rising_time = 0;
put_key(time);
wake_up_interruptible(&gpio_wait);
kill_fasync(&button_fasync, SIGIO, POLL_IN);
}
return IRQ_HANDLED;
}
/* 在入口函数 */
static int __init sr04_init(void)
{
int err;
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
// trig pin
err = gpio_request(gpios[0].gpio, gpios[0].name);
gpio_direction_output(gpios[0].gpio, 0);
// echo pin
{
gpios[1].irq = gpio_to_irq(gpios[1].gpio); //获取中断号
err = request_irq(gpios[1].irq, sr04_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, gpios[1].name, &gpios[1]); //初始化按键中断
}
/* 注册file_operations */
major = register_chrdev(0, "100ask_sr04", &sr04_drv); /* /dev/gpio_desc */
gpio_class = class_create(THIS_MODULE, "100ask_sr04_class");
if (IS_ERR(gpio_class)) {
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
unregister_chrdev(major, "100ask_sr04");
return PTR_ERR(gpio_class);
}
device_create(gpio_class, NULL, MKDEV(major, 0), NULL, "sr04"); /* /dev/sr04 */
return err;
}
/* 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数
*/
static void __exit sr04_exit(void)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
device_destroy(gpio_class, MKDEV(major, 0));
class_destroy(gpio_class);
unregister_chrdev(major, "100ask_sr04");
// trig pin
gpio_free(gpios[0].gpio);
// echo pin
{
free_irq(gpios[1].irq, &gpios[1]);
}
}
/* 7. 其他完善:提供设备信息,自动创建设备节点 */
module_init(sr04_init);
module_exit(sr04_exit);
MODULE_LICENSE("GPL");
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <poll.h>
#include <signal.h>
#include <sys/ioctl.h>
#define CMD_TRIG 100
static int fd;
/*
* ./button_test /dev/sr04
*
*/
int main(int argc, char **argv)
{
int val;
struct pollfd fds[1];
int timeout_ms = 5000;
int ret;
int flags;
int i;
/* 1. 判断参数 */
if (argc != 2)
{
printf("Usage: %s <dev>\n", argv[0]);
return -1;
}
/* 2. 打开文件 */
fd = open(argv[1], O_RDWR);
if (fd == -1)
{
printf("can not open file %s\n", argv[1]);
return -1;
}
while (1)
{
ioctl(fd, CMD_TRIG);
printf("I am goning to read distance: \n");
if (read(fd, &val, 4) == 4)
printf("get distance: %d cm\n", val*17/1000000);
else
printf("get distance err\n");
sleep(1); //根据硬件时序图不能太快发送高电平
}
close(fd);
return 0;
}
超声波测距程序改进(在app加入poll机制,或在驱动iocontrl里面加一个定时器)
#include "asm-generic/errno.h"
#include "asm-generic/gpio.h"
#include "asm/delay.h"
#include "linux/jiffies.h"
#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.h>
#define CMD_TRIG 100
struct gpio_desc{
int gpio;
int irq;
char *name;
int key;
struct timer_list key_timer;
} ;
static struct gpio_desc gpios[2] = {
{115, 0, "trig", },
{116, 0, "echo", },
};
/* 主设备号 */
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) ((x+1) % 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);
/* 实现对应的open/read/write等函数,填入file_operations结构体 */
static ssize_t sr04_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;
// printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
wait_event_interruptible(gpio_wait, !is_key_buf_empty());
key = get_key();
if (key == -1)
return -ENODATA;
err = copy_to_user(buf, &key, 4);
return 4;
}
static unsigned int sr04_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 sr04_fasync(int fd, struct file *file, int on)
{
if (fasync_helper(fd, file, on, &button_fasync) >= 0)
return 0;
else
return -EIO;
}
// ioctl(fd, CMD, ARG)
static long sr04_ioctl(struct file *filp, unsigned int command, unsigned long arg)
{
// send trig
switch (command)
{
case CMD_TRIG:
{
//printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
gpio_set_value(gpios[0].gpio, 1);
udelay(20);
gpio_set_value(gpios[0].gpio, 0);
// start timer
mod_timer(&gpios[1].key_timer, jiffies + msecs_to_jiffies(50)); //50ms后进入定时器服务程序
}
}
return 0;
}
/* 定义自己的file_operations结构体 */
static struct file_operations sr04_drv = {
.owner = THIS_MODULE,
.read = sr04_read,
.poll = sr04_poll,
.fasync = sr04_fasync,
.unlocked_ioctl = sr04_ioctl,
};
static irqreturn_t sr04_isr(int irq, void *dev_id)
{
struct gpio_desc *gpio_desc = dev_id;
int val;
static u64 rising_time = 0;
u64 time;
val = gpio_get_value(gpio_desc->gpio);
//printk("sr04_isr echo pin %d is %d\n", gpio_desc->gpio, val);
if (val)
{
/* 上升沿记录起始时间 */
rising_time = ktime_get_ns();
}
else
{
if (rising_time == 0)
{
//printk("missing rising interrupt\n");
return IRQ_HANDLED;
}
/* 下降沿记录结束时间, 并计算时间差, 计算距离 */
// stop timer
del_timer(&gpios[1].key_timer);
time = ktime_get_ns() - rising_time;
rising_time = 0;
put_key(time);
wake_up_interruptible(&gpio_wait);
kill_fasync(&button_fasync, SIGIO, POLL_IN);
}
return IRQ_HANDLED;
}
static void sr04_timer_func(unsigned long data) //如果超时还没有接收到数据进入此函数
{
put_key(-1); //在数组里写入-1
wake_up_interruptible(&gpio_wait); //唤醒睡眠,将-1发出去
kill_fasync(&button_fasync, SIGIO, POLL_IN);
}
/* 在入口函数 */
static int __init sr04_init(void)
{
int err;
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
// trig pin
err = gpio_request(gpios[0].gpio, gpios[0].name);
gpio_direction_output(gpios[0].gpio, 0);
// echo pin
{
gpios[1].irq = gpio_to_irq(gpios[1].gpio);
err = request_irq(gpios[1].irq, sr04_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, gpios[1].name, &gpios[1]);
setup_timer(&gpios[1].key_timer, sr04_timer_func, (unsigned long)&gpios[1]);
}
/* 注册file_operations */
major = register_chrdev(0, "100ask_sr04", &sr04_drv); /* /dev/gpio_desc */
gpio_class = class_create(THIS_MODULE, "100ask_sr04_class");
if (IS_ERR(gpio_class)) {
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
unregister_chrdev(major, "100ask_sr04");
return PTR_ERR(gpio_class);
}
device_create(gpio_class, NULL, MKDEV(major, 0), NULL, "sr04"); /* /dev/sr04 */
return err;
}
/* 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数
*/
static void __exit sr04_exit(void)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
device_destroy(gpio_class, MKDEV(major, 0));
class_destroy(gpio_class);
unregister_chrdev(major, "100ask_sr04");
// trig pin
gpio_free(gpios[0].gpio);
// echo pin
{
free_irq(gpios[1].irq, &gpios[1]);
del_timer(&gpios[1].key_timer);
}
}
/* 7. 其他完善:提供设备信息,自动创建设备节点 */
module_init(sr04_init);
module_exit(sr04_exit);
MODULE_LICENSE("GPL");
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <poll.h>
#include <signal.h>
#include <sys/ioctl.h>
#define CMD_TRIG 100
static int fd;
/*
* ./button_test /dev/sr04
*
*/
int main(int argc, char **argv)
{
int val;
struct pollfd fds[1];
int timeout_ms = 5000;
int ret;
int flags;
int i;
/* 1. 判断参数 */
if (argc != 2)
{
printf("Usage: %s <dev>\n", argv[0]);
return -1;
}
/* 2. 打开文件 */
fd = open(argv[1], O_RDWR);
if (fd == -1)
{
printf("can not open file %s\n", argv[1]);
return -1;
}
while (1)
{
ioctl(fd, CMD_TRIG);
printf("I am goning to read distance: \n");
fds[0].fd = fd;
fds[0].events = POLLIN;
if (1 == poll(fds, 1, 5000)) //poll函数有数据返回的是个正数,数值为设备个数,超过5s会跳出阻塞返回值为0
{
if (read(fd, &val, 4) == 4)
printf("get distance: %d cm\n", val*17/1000000);
else
printf("get distance err\n");
}
else
{
printf("get distance poll timeout/err\n");
}
sleep(1);
}
close(fd);
return 0;
}
内核不同调用的函数不一样(内核哪里在入门视频第五章有讲)
步进电机控制原理与接线
编程思路
编程
#include "asm-generic/gpio.h"
#include "asm/gpio.h"
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/delay.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.h>
struct gpio_desc{
int gpio;
int irq;
char *name;
int key;
struct timer_list key_timer;
} ;
static struct gpio_desc gpios[] = {
{115, 0, "motor_gpio0", },
{116, 0, "motor_gpio1", },
{117, 0, "motor_gpio2", },
{118, 0, "motor_gpio3", },
};
/* 主设备号 */
static int major = 0;
static struct class *gpio_class;
/* 马达引脚设置数字 */
static int g_motor_pin_ctrl[8]= {0x2,0x3,0x1,0x9,0x8,0xc,0x4,0x6};
static int g_motor_index = 0;
void set_pins_for_motor(int index)
{
int i;
for (i = 0; i < 4; i++)
{
gpio_set_value(gpios[i].gpio, g_motor_pin_ctrl[index] & (1<<i) ? 1 : 0);
}
}
void disable_motor(void)
{
int i;
for (i = 0; i < 4; i++)
{
gpio_set_value(gpios[i].gpio, 0);
}
}
/* int buf[2];
* buf[0] = 步进的次数, > 0 : 逆时针步进; < 0 : 顺时针步进
* buf[1] = mdelay的时间
*/
static ssize_t motor_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
int ker_buf[2];
int err;
int step;
if (size != 8)
return -EINVAL;
err = copy_from_user(ker_buf, buf, size);
if (ker_buf[0] > 0)
{
/* 逆时针旋转 */
for (step = 0; step < ker_buf[0]; step++)
{
set_pins_for_motor(g_motor_index);
mdelay(ker_buf[1]);
g_motor_index--;
if (g_motor_index == -1)
g_motor_index = 7;
}
}
else
{
/* 顺时针旋转 */
ker_buf[0] = 0 - ker_buf[0];
for (step = 0; step < ker_buf[0]; step++)
{
set_pins_for_motor(g_motor_index);
mdelay(ker_buf[1]);
g_motor_index++;
if (g_motor_index == 8)
g_motor_index = 0;
}
}
/* 改进:旋转到位后让马达不再消耗电源 */
disable_motor();
return 8;
}
/* 定义自己的file_operations结构体 */
static struct file_operations gpio_key_drv = {
.owner = THIS_MODULE,
.write = motor_write,
};
/* 在入口函数 */
static int __init motor_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++)
{
err = gpio_request(gpios[i].gpio, gpios[i].name);
gpio_direction_output(gpios[i].gpio, 0);
}
/* 注册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, "motor"); /* /dev/motor */
return err;
}
/* 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数
*/
static void __exit motor_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++)
{
gpio_free(gpios[i].gpio);
}
}
/* 7. 其他完善:提供设备信息,自动创建设备节点 */
module_init(motor_init);
module_exit(motor_exit);
MODULE_LICENSE("GPL");
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <poll.h>
#include <signal.h>
#include <stdlib.h>
static int fd;
/*
* ./button_test /dev/motor -100 1
*
*/
int main(int argc, char **argv)
{
int buf[2];
int ret;
/* 1. 判断参数 */
if (argc != 4)
{
printf("Usage: %s <dev> <step_number> <mdelay_number>\n", argv[0]);
return -1;
}
/* 2. 打开文件 */
fd = open(argv[1], O_RDWR | O_NONBLOCK);
if (fd == -1)
{
printf("can not open file %s\n", argv[1]);
return -1;
}
buf[0] = strtol(argv[2], NULL, 0);
buf[1] = strtol(argv[3], NULL, 0);
ret = write(fd, buf, 8);
close(fd);
return 0;
}
DHT11驱动程序编写
驱动
#include "asm-generic/errno-base.h"
#include "asm-generic/gpio.h"
#include "linux/jiffies.h"
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/delay.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.h>
struct gpio_desc{
int gpio;
int irq;
char *name;
int key;
struct timer_list key_timer;
} ;
static struct gpio_desc gpios[] = {
{115, 0, "dht11", },
};
/* 主设备号 */
static int major = 0;
static struct class *gpio_class;
static u64 g_dht11_irq_time[84];
static int g_dht11_irq_cnt = 0;
/* 环形缓冲区 */
#define BUF_LEN 128
static char g_keys[BUF_LEN];
static int r, w;
struct fasync_struct *button_fasync;
static irqreturn_t dht11_isr(int irq, void *dev_id);
static void parse_dht11_datas(void);
#define NEXT_POS(x) ((x+1) % 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(char key)
{
if (!is_key_buf_full())
{
g_keys[w] = key;
w = NEXT_POS(w);
}
}
static char get_key(void)
{
char 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)
{
// 解析数据, 放入环形buffer, 唤醒APP
parse_dht11_datas();
}
/* 实现对应的open/read/write等函数,填入file_operations结构体 */
static ssize_t dht11_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
int err;
char kern_buf[2];
if (size != 2)
return -EINVAL;
g_dht11_irq_cnt = 0;
/* 1. 发送18ms的低脉冲 */
err = gpio_request(gpios[0].gpio, gpios[0].name);
gpio_direction_output(gpios[0].gpio, 0);
gpio_free(gpios[0].gpio);
mdelay(18);
gpio_direction_input(gpios[0].gpio); /* 引脚变为输入方向, 由上拉电阻拉为1 */
/* 2. 注册中断 */
err = request_irq(gpios[0].irq, dht11_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, gpios[0].name, &gpios[0]);
mod_timer(&gpios[0].key_timer, jiffies + 10); //超时后进入定时器中断服务程序,如果没超时(也就是接收到了84个脉冲)会在外部中断里把这个定时器删除
/* 3. 休眠等待数据 */
wait_event_interruptible(gpio_wait, !is_key_buf_empty());
free_irq(gpios[0].irq, &gpios[0]);
//printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
/* 设置DHT11 GPIO引脚的初始状态: output 1 */
err = gpio_request(gpios[0].gpio, gpios[0].name);
if (err)
{
printk("%s %s %d, gpio_request err\n", __FILE__, __FUNCTION__, __LINE__);
}
gpio_direction_output(gpios[0].gpio, 1);
gpio_free(gpios[0].gpio);
/* 4. copy_to_user */
kern_buf[0] = get_key(); //只得到湿度跟温度的整数部分一共两个字节
kern_buf[1] = get_key();
printk("get val : 0x%x, 0x%x\n", kern_buf[0], kern_buf[1]);
if ((kern_buf[0] == (char)-1) && (kern_buf[1] == (char)-1)) //必须强转为char因为arm-gcc编译器把char转成unsigned char
{
printk("get err val\n"); //在终端输入dmesg可以查看
return -EIO;
}
err = copy_to_user(buf, kern_buf, 2);
return 2;
}
static int dht11_release (struct inode *inode, struct file *filp)
{
return 0;
}
/* 定义自己的file_operations结构体 */
static struct file_operations dht11_drv = {
.owner = THIS_MODULE,
.read = dht11_read,
.release = dht11_release,
};
static void parse_dht11_datas(void)
{
int i;
u64 high_time;
unsigned char data = 0;
int bits = 0;
unsigned char datas[5];
int byte = 0;
unsigned char crc;
/* 数据个数: 可能是81、82、83、84 */
if (g_dht11_irq_cnt < 81)
{
/* 出错 */
put_key(-1);
put_key(-1);
// 唤醒APP
wake_up_interruptible(&gpio_wait);
g_dht11_irq_cnt = 0;
return;
}
// 解析数据
for (i = g_dht11_irq_cnt - 80; i < g_dht11_irq_cnt; i+=2)
{
high_time = g_dht11_irq_time[i] - g_dht11_irq_time[i-1];
data <<= 1;
if (high_time > 50000) /* data 1 */
{
data |= 1;
}
bits++;
if (bits == 8)
{
datas[byte] = data;
data = 0;
bits = 0;
byte++;
}
}
// 放入环形buffer
crc = datas[0] + datas[1] + datas[2] + datas[3];
if (crc == datas[4])
{
put_key(datas[0]);
put_key(datas[2]);
}
else
{
put_key(-1);
put_key(-1);
}
g_dht11_irq_cnt = 0;
// 唤醒APP
wake_up_interruptible(&gpio_wait);
}
static irqreturn_t dht11_isr(int irq, void *dev_id)
{
struct gpio_desc *gpio_desc = dev_id;
u64 time;
/* 1. 记录中断发生的时间 */
time = ktime_get_ns();
g_dht11_irq_time[g_dht11_irq_cnt] = time;
/* 2. 累计次数 */
g_dht11_irq_cnt++;
/* 3. 次数足够: 解析数据, 放入环形buffer, 唤醒APP */
if (g_dht11_irq_cnt == 84)
{
del_timer(&gpio_desc->key_timer); //干掉超时限制的定时器
parse_dht11_datas();
}
return IRQ_HANDLED;
}
/* 在入口函数 */
static int __init dht11_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);
/* 设置DHT11 GPIO引脚的初始状态: output 1 */
err = gpio_request(gpios[i].gpio, gpios[i].name);
gpio_direction_output(gpios[i].gpio, 1);
gpio_free(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, dht11_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "100ask_gpio_key", &gpios[i]);
}
/* 注册file_operations */
major = register_chrdev(0, "100ask_dht11", &dht11_drv); /* /dev/gpio_desc */
gpio_class = class_create(THIS_MODULE, "100ask_dht11_class");
if (IS_ERR(gpio_class)) {
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
unregister_chrdev(major, "100ask_dht11");
return PTR_ERR(gpio_class);
}
device_create(gpio_class, NULL, MKDEV(major, 0), NULL, "mydht11"); /* /dev/mydht11 */
return err;
}
/* 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数
*/
static void __exit dht11_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_dht11");
for (i = 0; i < count; i++)
{
//free_irq(gpios[i].irq, &gpios[i]);
//del_timer(&gpios[i].key_timer);
}
}
/* 7. 其他完善:提供设备信息,自动创建设备节点 */
module_init(dht11_init);
module_exit(dht11_exit);
MODULE_LICENSE("GPL");
应用
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <poll.h>
#include <signal.h>
static int fd;
/*
* ./button_test /dev/mydht11
*
*/
int main(int argc, char **argv)
{
char buf[2];
int ret;
int i;
/* 1. 判断参数 */
if (argc != 2)
{
printf("Usage: %s <dev>\n", argv[0]);
return -1;
}
/* 2. 打开文件 */
fd = open(argv[1], O_RDWR | O_NONBLOCK);
if (fd == -1)
{
printf("can not open file %s\n", argv[1]);
return -1;
}
while (1)
{
if (read(fd, buf, 2) == 2)
printf("get Humidity: %d, Temperature : %d\n", buf[0], buf[1]);
else
printf("get dht11: -1\n");
sleep(5);
}
close(fd);
return 0;
}