STM32MP157 | 使用 Linux 内核定时器消抖

一、定时器简介

(1)什么是定时器(timer)

定时器实际上就是Soc当中的一个内部外设。

  1. 定时器有什么用

(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;        //0
    };
	
   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>;   //gpiod_to_irq
};  

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;
    // 0.初始化定时器
    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;
    // 0.初始化定时器
    mytimer.expires = jiffies + 1;
    timer_setup(&mytimer, timer_handle, 0);
    add_timer(&mytimer);

    // 1.获取节点
    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++)
    {
        // 2.获取gpio号
        gpiono[i] = of_get_named_gpio(node, "keys", i);
        if (gpiono[i] < 0)
        {
            printk("get gpio number error\n");
            return gpiono[i];
        }
        // 3.映射软中断号
        irqno[i] = irq_of_parse_and_map(node, i);
        if (!irqno[i])
        {
            printk("get irq number error\n");
            return -EAGAIN;
        }

        // 4.注册中断
        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.加载驱动,查看驱动,并测试

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值