文章目录
全系列传送门
Linux嵌入式驱动开发01——第一个驱动Hello World(附源码)
Linux嵌入式驱动开发06——第一个相对完整的驱动实践编写
Linux嵌入式驱动开发07——GPIO驱动过程记录(飞凌开发板)
Linux嵌入式驱动开发11——平台总线模型修改为设备树实例
Linux嵌入式驱动开发12——pinctl和gpio子系统实践操作
Linux嵌入式驱动开发13——ioctl接口(gpio控制使用)
Linux嵌入式驱动开发14——中断的原理以及按键中断的实现(tasklet中断下文)
Linux内核定时器概念
Linux内核定时器基础知识
Linux内核定时器相关函数
时间转换函数
ms转换时钟节拍函数
us转换时钟节拍函数
宏DEFINE_TIMER
add_timer函数
del_timer函数
mod_timer函数
示例代码
我们现在每隔一秒钟打印一句话
#include <linux/init.h>
#include <linux/module.h>
#include <linux/timer.h>
static void timer_function(unsigned long data);
/* 使用DEFINE_TIMER宏
* 第一个参数:变量名
* 第二个参数:超时处理函数
* 第三个参数:传递给超时处理函数的参数
* 第四个参数:到点时间,一般在启动定时器前需要重新初始化
* */
DEFINE_TIMER(test_timer, timer_function, 0, 0);
/*超时处理函数*/
static void timer_function(unsigned long data)
{
printk("This is timer_function\n");
mod_timer(&test_timer, jiffies + 1*HZ); // 设置下一个定时器点
}
static int hello_init(void)
{
printk("hello world\n"); // 在内核中无法使用c语言库,所以不用printf
test_timer.expires = jiffies + 1*HZ; // 设置定时时间定时一秒钟
add_timer(&test_timer); // 启动定时器
return 0;
}
static void hello_exit(void)
{
printk("bye\n");
del_timer(&test_timer);
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL"); //声明模块拥有开源许可
按键消抖
对于之前stm32按键消抖的方案,主要就是通过一个delay函数延时,然后再查看电平
对于我们的Linux,中断中,首先是有一个上下文的概念,上文要越快越好,同时在中断上文里是不可以进行睡眠的,所以不能用传统的延时方法。
这时候就可以使用linux 定时器,Linux定时器是基于未来时间点的方式执行,
使用了定时器后,会调用超时处理函数,这时候我们的判断函数只需要在超时处理函数中执行就可以了
代码目前有问题,还没有调好
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/interrupt.h>
#include <linux/of_irq.h>
#include <linux/irqreturn.h>
#include <linux/timer.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
struct device_node *test_device_node;
int gpio_name; // gpio编号
int irq; // 中断号
static int button_val;
static void timer_function(unsigned long data);
/* 使用DEFINE_TIMER宏
* 第一个参数:变量名
* 第二个参数:超时处理函数
* 第三个参数:传递给超时处理函数的参数
* 第四个参数:到点时间,一般在启动定时器前需要重新初始化
* */
DEFINE_TIMER(test_timer, timer_function, 0, 0);
/*超时处理函数*/
static void timer_function(unsigned long data)
{
printk("This is timer_function\n");
/* 读取按键值 */
// button_val = gpio_get_value(gpio_name);
// if(button_val == 0) //按键未被按下
// printk("key is pressed\n");
}
irq_handler_t test_key_handle(int irq, void *args) // 中断处理函数
{
printk("test_key_handle ok!!!\n");
test_timer.expires = jiffies + msecs_to_jiffies(20); // 设置定时时间定时20ms
add_timer(&test_timer); // 启动定时器
return IRQ_HANDLED; // 中断程序的返回值只有两个IRQ_NONE和IRQ_HANDLED。
}
/*probe函数*/
int beep_probe(struct platform_device *pdev){
int ret = 0;
printk("beep_probe ok!!!\n");
/*间接获取设备节点信息*/
/********查找指定路径的节点***********/
test_device_node = of_find_node_by_path("/test_key"); // 节点名字叫做test_key
if(test_device_node == NULL) {
printk("of_find_node_by_path error!!!\n");
return -1;
}else {
printk("of_find_node_by_path ok!!!\n");
printk("test_device_node name is %s\n", test_device_node->name);
}
gpio_name = of_get_named_gpio(test_device_node, "gpios", 0); //gpios是设备树节点里的索引值gpios = <&gpio3 29 GPIO_ACTIVE_LOW>;
if(gpio_name < 0) {
printk("of_get_named_gpio error!!!\n");
return -1;
}else{
printk("of_get_named_gpio ok!!!\n");
}
gpio_direction_input(gpio_name); // 因为是模拟按键,方向设置成输入模式
// irq = gpio_to_irq(gpio_name); // 通过gpio函数获取中断号,参数是gpio编号
/* irq_of_parse_and_map,通过设备树中interrupts获取中断号
* 第一个参数:设备树节点
* 第二个参数:索引值,这里只有一个,所以是0
* */
irq = irq_of_parse_and_map(test_device_node, 0);
printk("irq is %d\n", irq);
/* request_irq
* 第一个参数:中断号,
* 第二个参数:中断处理函数,
* 第三个参数:中断标志(边沿触发方式),
* 第四个函数:中断名字,
* 第五个参数:设备结构体,传给中断处理函数irq_hander_t的第二个参数
*/
ret = request_irq(irq, test_key_handle, IRQF_TRIGGER_RISING, "test_key", NULL);
if(ret < 0) {
printk("request_irq failed!!!\n");
return -1;
}else{
printk("request_irq successful!!!\n");
}
return 0;
}
const struct platform_device_id beep_id_table = {
.name = "keys",
};
int beep_remove(struct platform_device *pdev){
printk("beep_remove ok!!!\n");
return 0;
}
const struct of_device_id of_match_table_test[] = {
{.compatible = "keys"},
{} // 不写会提示警告
};
struct platform_driver beep_device = {
.probe = beep_probe, // 这个probe函数其实和 device_driver中的是一样的功能,但是一般是使用device_driver中的那个
.remove = beep_remove, // 卸载平台设备驱动的时候会调用这个函数,但是device_driver下面也有,具体调用的是谁这个就得分析了
.driver = {
.owner = THIS_MODULE,
.name = "keys",
.of_match_table = of_match_table_test, // 最优先匹配of_match_table其次是id_table最后是name
}, // 内置的device_driver 结构体
.id_table = &beep_id_table,
};
static int beep_driver_init(void)
{
int ret = 0;
printk("beep_driver_init ok!!!\n"); // 在内核中无法使用c语言库,所以不用printf
ret = platform_driver_register(&beep_device);
if(ret < 0){
printk("platform_driver_register error!!!\n");
return ret;
}else{
printk("platform_driver_register ok!!!\n");
}
return 0;
}
static void beep_driver_exit(void)
{
printk("beep_driver_exit bye!!!\n");
free_irq(irq, NULL);
del_timer(&test_timer);
platform_driver_unregister(&beep_device);
}
module_init(beep_driver_init);
module_exit(beep_driver_exit);
MODULE_LICENSE("GPL"); //声明模块拥有开源许可