当前位置: 首页 > news >正文

【学习日记】【第五十一章 Linux中断实验】【流程图】——正点原子I.MX6U嵌入式Linux驱动开发

1 概述

本文主要是笔者根据《正点原子I.MX6U嵌入式Linux驱动开发》中 “第五十一章 Linux中断实验” 的程序绘制的流程图,方便理解中断和定时器的使用。

2 流程图

驱动程序
按键按下
按键松开
中断触发
按键释放
无按键释放
构建设备号
驱动入口函数
注册字符设备
创建类
创建设备
初始化按键
申请中断
创建定时器
等待中断
中断服务函数
启动定时器
定时器服务函数
读取GPIO值
设置按键值
设置释放标记
设备打开
设置私有数据
设备打开完成
设备读取
读取按键值
返回按键值
返回错误
应用程序
参数错误
参数正确
打开失败
打开成功
读取错误
读取成功
退出
检查参数
主程序开始
打印错误信息
打开设备文件
打印错误信息
进入读取循环
读取设备数据
处理错误
打印按键值
关闭设备文件
主程序结束

3 正点原子的代码

3.1 驱动程序

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>#define IMX6UIRQ_CNT 1 /* 设备号个数 */
#define IMX6UIRQ_NAME "imx6uirq" /* 名字 */
#define KEY0VALUE 0X01 /* KEY0 按键值 */
#define INVAKEY 0XFF /* 无效的按键值 */
#define KEY_NUM 1 /* 按键数量 *//* 中断 IO 描述结构体 */
struct irq_keydesc {int gpio; /* gpio */int irqnum; /* 中断号 */unsigned char value; /* 按键对应的键值 */char name[10]; /* 名字 */irqreturn_t (*handler)(int, void *); /* 中断服务函数 */
};/* imx6uirq 设备结构体 */
struct imx6uirq_dev {dev_t devid; /* 设备号 */struct cdev cdev; /* cdev */struct class *class; /* 类 */struct device *device; /* 设备 */int major; /* 主设备号 */int minor; /* 次设备号 */struct device_node *nd; /* 设备节点 */atomic_t keyvalue; /* 有效的按键键值 */atomic_t releasekey; /* 标记是否完成一次完成的按键 */struct timer_list timer; /* 定义一个定时器 */struct irq_keydesc irqkeydesc[KEY_NUM]; /* 按键描述数组 */unsigned char curkeynum; /* 当前的按键号 */
};struct imx6uirq_dev imx6uirq; /* irq 设备 *//* 中断服务函数,开启定时器,延时 10ms,定时器用于按键消抖。 */
static irqreturn_t key0_handler(int irq, void *dev_id) {struct imx6uirq_dev *dev = (struct imx6uirq_dev *)dev_id;dev->curkeynum = 0;dev->timer.data = (volatile long)dev_id;mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10));return IRQ_RETVAL(IRQ_HANDLED);
}/* 定时器服务函数,用于按键消抖 */
void timer_function(unsigned long arg) {unsigned char value;unsigned char num;struct irq_keydesc *keydesc;struct imx6uirq_dev *dev = (struct imx6uirq_dev *)arg;num = dev->curkeynum;keydesc = &dev->irqkeydesc[num];value = gpio_get_value(keydesc->gpio); /* 读取 IO 值 */if (value == 0) { /* 按下按键 */atomic_set(&dev->keyvalue, keydesc->value);} else { /* 按键松开 */atomic_set(&dev->keyvalue, 0x80 | keydesc->value);atomic_set(&dev->releasekey, 1); /* 标记松开按键 */}
}/* 按键 IO 初始化 */
static int keyio_init(void) {unsigned char i = 0;int ret = 0;imx6uirq.nd = of_find_node_by_path("/key");if (imx6uirq.nd == NULL) {printk("key node not find!\r\n");return -EINVAL;}/* 提取 GPIO */for (i = 0; i < KEY_NUM; i++) {imx6uirq.irqkeydesc[i].gpio = of_get_named_gpio(imx6uirq.nd, "key-gpio", i);if (imx6uirq.irqkeydesc[i].gpio < 0) {printk("can't get key%d\r\n", i);}}/* 初始化 key 所使用的 IO,并且设置成中断模式 */for (i = 0; i < KEY_NUM; i++) {memset(imx6uirq.irqkeydesc[i].name, 0, sizeof(imx6uirq.irqkeydesc[i].name));sprintf(imx6uirq.irqkeydesc[i].name, "KEY%d", i);gpio_request(imx6uirq.irqkeydesc[i].gpio, imx6uirq.irqkeydesc[i].name);gpio_direction_input(imx6uirq.irqkeydesc[i].gpio);imx6uirq.irqkeydesc[i].irqnum = irq_of_parse_and_map(imx6uirq.nd, i);printk("key%d:gpio=%d, irqnum=%d\r\n", i, imx6uirq.irqkeydesc[i].gpio, imx6uirq.irqkeydesc[i].irqnum);}/* 申请中断 */imx6uirq.irqkeydesc[0].handler = key0_handler;imx6uirq.irqkeydesc[0].value = KEY0VALUE;for (i = 0; i < KEY_NUM; i++) {ret = request_irq(imx6uirq.irqkeydesc[i].irqnum, imx6uirq.irqkeydesc[i].handler,IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,imx6uirq.irqkeydesc[i].name, &imx6uirq);if (ret < 0) {printk("irq %d request failed!\r\n", imx6uirq.irqkeydesc[i].irqnum);return -EFAULT;}}/* 创建定时器 */init_timer(&imx6uirq.timer);imx6uirq.timer.function = timer_function;return 0;
}/* 打开设备 */
static int imx6uirq_open(struct inode *inode, struct file *filp) {filp->private_data = &imx6uirq; /* 设置私有数据 */return 0;
}/* 从设备读取数据 */
static ssize_t imx6uirq_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) {int ret = 0;unsigned char keyvalue = 0;unsigned char releasekey = 0;struct imx6uirq_dev *dev = (struct imx6uirq_dev *)filp->private_data;keyvalue = atomic_read(&dev->keyvalue);releasekey = atomic_read(&dev->releasekey);if (releasekey) { /* 有按键按下 */if (keyvalue & 0x80) {keyvalue &= ~0x80;ret = copy_to_user(buf, &keyvalue, sizeof(keyvalue));} else {goto data_error;}atomic_set(&dev->releasekey, 0); /* 按下标志清零 */} else {goto data_error;}return 0;data_error:return -EINVAL;
}/* 设备操作函数 */
static struct file_operations imx6uirq_fops = {.owner = THIS_MODULE,.open = imx6uirq_open,.read = imx6uirq_read,
};/* 驱动入口函数 */
static int __init imx6uirq_init(void) {/* 1、构建设备号 */if (imx6uirq.major) {imx6uirq.devid = MKDEV(imx6uirq.major, 0);register_chrdev_region(imx6uirq.devid, IMX6UIRQ_CNT, IMX6UIRQ_NAME);} else {alloc_chrdev_region(&imx6uirq.devid, 0, IMX6UIRQ_CNT, IMX6UIRQ_NAME);imx6uirq.major = MAJOR(imx6uirq.devid);imx6uirq.minor = MINOR(imx6uirq.devid);}/* 2、注册字符设备 */cdev_init(&imx6uirq.cdev, &imx6uirq_fops);cdev_add(&imx6uirq.cdev, imx6uirq.devid, IMX6UIRQ_CNT);/* 3、创建类 */imx6uirq.class = class_create(THIS_MODULE, IMX6UIRQ_NAME);if (IS_ERR(imx6uirq.class)) {return PTR_ERR(imx6uirq.class);}/* 4、创建设备 */imx6uirq.device = device_create(imx6uirq.class, NULL, imx6uirq.devid, NULL, IMX6UIRQ_NAME);if (IS_ERR(imx6uirq.device)) {return PTR_ERR(imx6uirq.device);}/* 5、 初始化按键 */atomic_set(&imx6uirq.keyvalue, INVAKEY);atomic_set(&imx6uirq.releasekey, 0);keyio_init();return 0;
}/* 驱动出口函数 */
static void __exit imx6uirq_exit(void) {unsigned int i = 0;/* 删除定时器 */del_timer_sync(&imx6uirq.timer);/* 释放中断 */for (i = 0; i < KEY_NUM; i++) {free_irq(imx6uirq.irqkeydesc[i].irqnum, &imx6uirq);gpio_free(imx6uirq.irqkeydesc[i].gpio);}cdev_del(&imx6uirq.cdev);unregister_chrdev_region(imx6uirq.devid, IMX6UIRQ_CNT);device_destroy(imx6uirq.class, imx6uirq.devid);class_destroy(imx6uirq.class);
}module_init(imx6uirq_init);
module_exit(imx6uirq_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zuozhongkai");

3.2 应用程序

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include "linux/ioctl.h"/** @description : main 主程序* @param - argc : argv 数组元素个数* @param - argv : 具体参数* @return : 0 成功;其他 失败*/
int main(int argc, char *argv[])
{int fd;int ret = 0;char *filename;unsigned char data;if (argc != 2) {printf("Error Usage!\r\n");return -1;}filename = argv[1];fd = open(filename, O_RDWR);if (fd < 0) {printf("Can't open file %s\r\n", filename);return -1;}while (1) {ret = read(fd, &data, sizeof(data));if (ret < 0) { /* 数据读取错误或者无效 */// 处理错误} else { /* 数据读取正确 */if (data) /* 读取到数据 */printf("key value = %#X\r\n", data);}}close(fd);return ret;
}

http://www.mrgr.cn/news/1229.html

相关文章:

  • 智慧排水远程监测系统物联网解决方案
  • 网络通信要素
  • 27 lvs-nat模式与lvs-dr模式设置
  • 无线领夹麦克风哪个品牌音质最好?一文看懂如何挑选好的麦克风
  • 浅谈C#之Winform
  • 深入学习SQL优化的第二天
  • selenium学习记录
  • 【js引擎】如何使用 quickjs 把一个 js 值转换成 c 字符串
  • Java实现Excel导入(从oss中导入到数据库)
  • Spark-SparkSubmit详细过程
  • HarmonyOS 端云一体化 -- 项目初始化
  • 网络编程(TCP/UDP)
  • JVM对象在堆、栈、TLAP上的分配
  • 使用Python实现深度学习模型:智能娱乐与虚拟现实技术
  • SQL— DML语句学习【后端 10】
  • 10结构型设计模式——桥接模式
  • 嵌入式系统:全面解读与关键要点
  • HTML 基础要素解析
  • 零基础STM32单片机编程入门(三十九) 多传感器模块之NFC刷卡开门实战源码
  • C语言刷题日记(附详解)(1)