一、定时器简介
(1)什么是定时器(timer)
定时器实际上就是Soc当中的一个内部外设。
- 定时器有什么用
(1)定时器可以让SoC在执行主程序的同时,可以(通过定时器)具有计时功能,到了一定时间(计时结束)后,定时器会产生中断提醒CPU,CPU会去处理中断并执行定时器的ISR。从而去执行预先设定好的事件。
(2)定时器就好像是CPU的一个秘书一样,这个秘书专门管帮CPU来计时,并到时间后提醒CPU要做某件事情。所以CPU有了定时器之后,只需要预先把自己XX时间之后必须要做的事情绑定到定时器中断ISR即可,到了时间之后定时器就会以中断的方式提醒CPU来处理这个事情。
二、定时器的相关api
1.定时器的值加1代表走多长时间?
定时器的频率可以通过make menuconfig选配,选配后的值保存在内核顶层目录下的.config
CONFIG_HZ=100 开发板 =====> 10ms
CONFIG_HZ=250 ubuntu =====> 4ms
2.当前的时间如何获取?
jiffies: 内核时钟节拍数,从内核启动这一刻起,这个值一直在增加。
#include <linux/timer.h>
1.分配对象
struct timer_list {
struct hlist_node entry;
unsigned long expires;
void (*function)(struct timer_list *);
unsigned long data;
u32 flags;
};
struct timer_list mytimer;
2.对象初始化
mytimer.expires = jiffies+HZ;
timer_setup(&mytimer, 定时器处理函数, 0);
3.注册
void add_timer(struct timer_list *timer);
int mod_timer(struct timer_list *timer, unsigned long expires);
4.注销
int del_timer(struct timer_list * timer);
三、 添加设备树的节点
1. 添加设备树
//在stm32mp175a-fsmp1a.dts中添加如下节点:
myirqs{
interrupt-parent = <&gpiof>;
interrupts = <9 0>,<7 0>,<8 0>;
keys = <&gpiof 9 0>,<&gpiof 7 0>,<&gpiof 8 0>;
};
2. 重新编译设备树
make dtbs
重启开发板
安装驱动
四. 编写按键消抖驱动
1.先写个模块
#include <linux/init.h>
#include <linux/module.h>
static int __init timer_key_init(void)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
static void __exit timer_key_exit(void)
{
printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
return 0;
}
module_init(timer_key_init);
module_exit(timer_key_exit);
MODULE_LICENSE("GPL");
2. 写个Makefile编译一下:
ifeq ($(arch),arm)
KERNELDIR :=/home/linux/linux-5.10.61
CROSS_COMPILE ?=arm-linux-gnueabihf-
else
KERNELDIR :=/lib/modules/$(shell uname -r)/build
CROSS_COMPILE ?=
endif
modname ?=
PWD :=$(shell pwd)
CC :=$(CROSS_COMPILE)gcc
all:
make -C $(KERNELDIR) M=$(PWD) modules
# $(CC) test.c -o test
clean:
make -C $(KERNELDIR) M=$(PWD) clean
# rm test
install:
cp *.ko ~/nfs/rootfs/
# cp test ~/nfs/rootfs/
help:
echo "make arch = arm or x86 modname= dirvers file name"
obj-m:=$(modname).o
linux@ubuntu:~/linu/driver/csdn/timer$ make arch=arm modname=timer
3. 再写定时器驱动框架
#include <linux/init.h>
#include <linux/module.h>
struct timer_list mytimer;
void timer_handle(struct timer_list *timer)
{
}
static int __init timer_key_init(void)
{
int ret, i;
mytimer.expires = jiffies + 1;
timer_setup(&mytimer, timer_handle, 0);
add_timer(&mytimer);
return 0;
}
static void __exit timer_key_exit(void)
{
}
module_init(timer_key_init);
module_exit(timer_key_exit);
MODULE_LICENSE("GPL");
4.加入中断子系统
#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/of_irq.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
struct timer_list mytimer;
struct device_node *node;
unsigned int irqno[3];
const char *irqname[] = {"key1", "key2", "key3"};
int gpiono[3];
void timer_handle(struct timer_list *timer)
{
if (gpio_get_value(gpiono[0]) == 0)
{
printk("key1 ..............\n");
}
if (gpio_get_value(gpiono[1]) == 0)
{
printk("key2 ..............\n");
}
if (gpio_get_value(gpiono[2]) == 0)
{
printk("key3 ..............\n");
}
}
irqreturn_t key_irq_handle(int irq, void *dev)
{
mod_timer(&mytimer, jiffies + 1);
return IRQ_HANDLED;
}
static int __init timer_key_init(void)
{
int ret, i;
mytimer.expires = jiffies + 1;
timer_setup(&mytimer, timer_handle, 0);
add_timer(&mytimer);
node = of_find_node_by_path("/myirqs");
if (node == NULL)
{
printk("find node error\n");
return -EAGAIN;
}
for (i = 0; i < ARRAY_SIZE(irqno); i++)
{
gpiono[i] = of_get_named_gpio(node, "keys", i);
if (gpiono[i] < 0)
{
printk("get gpio number error\n");
return gpiono[i];
}
irqno[i] = irq_of_parse_and_map(node, i);
if (!irqno[i])
{
printk("get irq number error\n");
return -EAGAIN;
}
ret = request_irq(irqno[i], key_irq_handle,IRQF_TRIGGER_FALLING, irqname[i], (void *)i);
if (ret)
{
printk("request irq error\n");
return ret;
}
}
return 0;
}
static void __exit timer_key_exit(void)
{
int i = 0;
for (i = 0; i < ARRAY_SIZE(irqno); i++)
{
free_irq(irqno[i], (void *)i);
}
del_timer(&mytimer);
}
module_init(timer_key_init);
module_exit(timer_key_exit);
MODULE_LICENSE("GPL");
四、测试驱动模块
1.加载驱动,查看驱动,并测试
|