(七)linux下的定时器驱动led闪烁

一、linux下的定时器驱动led闪烁

1.1、嵌入式开发版

嵌入式开发版采用正点原子linux开发版

1.2、linux下的timer

linux内核定时器是基于jiffies节拍进行调度执行的一种机制,类似于“软中断”,之运行一次之后便会被注销。
由于中断资源的访问,所以对于一些被访问的数据结构都应该使用并发访问保护,防止竞争条件,而且因为他是处于非进程的上下文中,所以回调函数需要注意,不能执行休眠和调度。

在<Linux/timer.h>有关timer定义的一个结构体。

struct timer_list {
	/*
	 * All fields that change during normal runtime grouped to the
	 * same cacheline
	 */
	struct list_head entry;
	unsigned long expires;
	struct tvec_base *base;

	void (*function)(unsigned long);
	unsigned long data;

	int slack;

#ifdef CONFIG_TIMER_STATS
	int start_pid;
	void *start_site;
	char start_comm[16];
#endif
#ifdef CONFIG_LOCKDEP
	struct lockdep_map lockdep_map;
#endif
};

其中我们关注两个点void (*function)(unsigned long);和unsigned long data;
这两个,第一个是timer的回调函数,第二个则是回调函数的传递参数。

1、使用timer的流程是,先定义一个timer结构体,然后调取 init_timer() 来初始化该结构体,然后将回调函数和传递的参数相对的应的赋值。
2、但是此刻还是没有开始进行timer的开始,需要调取函数 mod_timer() ,来进行timer的开始也可以通过其他函数来实现开启timer。
3、如果想要删除定时器,可以调用 del_timer_sync()

1.3、驱动代码的编写

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/timer.h>

#define TIMER_NAME  "timer_led"
#define TIMER_CT    1

#define CLOSE_CMD   (_IO(0xEF,0x01))
#define OPEN_CMD    (_IO(0xEF,0x02))
#define SET_CMD     (_IO(0xEF,0x03))

struct timer_struct{
    spinlock_t	lock;
    int major;
    dev_t devid;
    struct cdev cdev;
    struct class *class;
    struct device *device;
    struct timer_list timer;

    struct device_node *nd;
    int gpio_num;

    int time_period;
};

struct timer_struct timer_dev;


static int timer_open(struct inode *inode, struct file *file)
{

	file->private_data = &timer_dev;
    

    return 0;

}

static long timer_unlocked_ioctl(struct file *file, unsigned int cmd,unsigned long arg)
{
    struct timer_struct *dev = (struct timer_struct *)file->private_data;
    int time_period = 0;
    unsigned long flags;

    printk("input\r\n");

    switch (cmd)
    {
    case CLOSE_CMD:
        del_timer_sync(&dev->timer);
        gpio_set_value(dev->gpio_num,1);
        break;
    
    case OPEN_CMD:
        spin_lock_irqsave(&dev->lock,flags);
        time_period = dev->time_period;
        mod_timer(&dev->timer,jiffies + msecs_to_jiffies(dev->time_period));
        spin_unlock_irqrestore(&dev->lock,flags);
        break;

    case SET_CMD:

        spin_lock_irqsave(&dev->lock,flags);
        dev->time_period = arg;
        mod_timer(&dev->timer,jiffies + msecs_to_jiffies(dev->time_period));
        spin_unlock_irqrestore(&dev->lock,flags);

        break;
    
    default:
        break;
    }

    return 0;
}

static const struct file_operations timer_fops = {
    .owner          = THIS_MODULE,
    .open           = timer_open,
    .unlocked_ioctl = timer_unlocked_ioctl,
};

void timer_function(unsigned long arg)
{
    struct timer_struct *dev = (struct timer_struct *)arg;
    static int status = 0;
    unsigned long  flags;
    int time_period;

    status  = !status;
    
    gpio_set_value(dev->gpio_num,status&0x01);

    spin_lock_irqsave(&dev->lock,flags);
    time_period = dev->time_period;
    mod_timer(&dev->timer,jiffies + msecs_to_jiffies(dev->time_period));
    spin_unlock_irqrestore(&dev->lock,flags);
}


int led_init(void)
{
    int ret;
    timer_dev.nd = of_find_node_by_path("/gpioled");

    if(timer_dev.nd == NULL){
        printk("not find node\r\n");
        return -1;
    }

    printk("find node\r\n");

    timer_dev.gpio_num = of_get_named_gpio(timer_dev.nd,"led-gpio",0);
    if(timer_dev.gpio_num < 0){
        printk("no get gpio\r\n");
        return -2;
    }

    printk("gpio num:%d\r\n",timer_dev.gpio_num);

    ret = gpio_request(timer_dev.gpio_num,"led0");
    if(ret < 0){
        printk("led already use\r\n");
        return -3;
    }

    ret = gpio_direction_output(timer_dev.gpio_num,1);
    if(ret < 0){
        printk("gpio set err\r\n");
        return -4;
    }

    return 0;

}

static int __init timer_init(void)
{
    int ret = 0;
    timer_dev.major = 0;

    spin_lock_init(&timer_dev.lock);

    if(timer_dev.major){
        timer_dev.devid = MKDEV(timer_dev.major,0);
        ret = register_chrdev_region(timer_dev.devid,TIMER_CT,TIMER_NAME);
    }
    else{
        ret = alloc_chrdev_region(&timer_dev.devid,0,TIMER_CT,TIMER_NAME);
        timer_dev.major = MAJOR(timer_dev.devid);
    }

    if(ret < 0){
        printk("chrdev err\r\n");
        goto chrdev_err;
    }


    printk("major num:%d\r\n",timer_dev.major);

    timer_dev.cdev.owner = THIS_MODULE; 

    cdev_init(&timer_dev.cdev,&timer_fops);

    ret = cdev_add(&timer_dev.cdev,timer_dev.devid,TIMER_CT);

    if(ret < 0){
        printk("cdev err\r\n");
        goto cdev_err;
    }
    timer_dev.class = class_create(THIS_MODULE,TIMER_NAME);

    if(IS_ERR(timer_dev.class)){
        printk("class err\r\n");
        ret = PTR_ERR(timer_dev.class);
        goto class_err;
    }

    timer_dev.device = device_create(timer_dev.class,NULL,timer_dev.devid,NULL,TIMER_NAME);
    if(IS_ERR(timer_dev.device)){
        printk("device err\r\n");
        ret = PTR_ERR(timer_dev.device);
        goto device_err;
    }

    ret = led_init();
    if(ret < 0){
        printk("led err\r\n");
        goto led_err;
    }

    timer_dev.time_period = 1000;

    init_timer(&timer_dev.timer);
    timer_dev.timer.function    = timer_function;
    timer_dev.timer.data        = (unsigned long)&timer_dev;

   // mod_timer(&timer_dev.timer, jiffies+ msecs_to_jiffies(timer_dev.time_period));

    return 0;

led_err:
    device_destroy(timer_dev.class,timer_dev.devid);

device_err:
    class_destroy(timer_dev.class);

class_err:
    cdev_del(&timer_dev.cdev);

cdev_err:
    unregister_chrdev_region(timer_dev.devid,TIMER_CT);

chrdev_err:
    return ret;


}

static void __exit timer_exit(void)
{
    gpio_set_value(timer_dev.gpio_num,1);
    gpio_free(timer_dev.gpio_num);
    del_timer_sync(&timer_dev.timer);
    device_destroy(timer_dev.class,timer_dev.devid);
    class_destroy(timer_dev.class);
    cdev_del(&timer_dev.cdev);
    unregister_chrdev_region(timer_dev.devid,TIMER_CT);
}

module_init(timer_init);
module_exit(timer_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("gale");

这一次,在fops结构体上增加了一个函数unlocked_ioctl,在对于驱动而言,该函数主要是实现一些命令的功能,其实一开始的时候我也想不明白,如果是一些命令也可以write来实现,为何还会出现一个unlocked_ioctl(其实就是ioctl函数),其实如果采用write来实现也不是不可,但是给人一种紊乱的感觉,因为驱动有可能涉及到关闭,打开等操作,采用专门的函数来处理可能会给人很明确的感觉,这只是个人理解,具体的可以参考这篇博客linux 内核 - ioctl 函数详解

static const struct file_operations timer_fops = {
    .owner          = THIS_MODULE,
    .open           = timer_open,
    .unlocked_ioctl = timer_unlocked_ioctl,
};

关于并发保护使用的自旋锁,自旋锁的使用相对简单,主要是为了确保该部分代码的执行一致性,完整性,不存在其他任何进程同时进行访问。

1.4、应用程序

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include "stdlib.h"
#include <linux/ioctl.h>

#define CLOSE_CMD   (_IO(0xEF,0x01))
#define OPEN_CMD    (_IO(0xEF,0x02))
#define SET_CMD     (_IO(0xEF,0x03))


int main(int argc,char *argv[])
{
    int ret = 0;
    int cmd;
    int arg;
    int fd;
    char *filename;
    char writebuff[1];
    unsigned char str[100];

    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){
        printf("Input CMD:");
        ret = scanf("%d",&cmd);

        printf("cmd:%d\r\n",cmd);

        if(cmd == 1){
            cmd = CLOSE_CMD;
        }
        else if(cmd == 2){
            cmd = OPEN_CMD;
        }

        else if(cmd == 3){
            cmd = SET_CMD;
            printf("Input arg:");
            ret = scanf("%d",&arg);
            printf("arg:%d\r\n",arg);
        }
        
        ioctl(fd,cmd,arg);
    }
    
    ret = close(fd);
    if(ret < 0){
        printf("close file err:%s\r\n",filename);
    }
    else{
        
    }
    

    return 0;
}

1.5、Makefile

使用的是正点原子提供的Makefile

KERNELDIR := /home/gale/linux/linux/nxp_alientek_linux

CURRENT_PATH := $(shell pwd)

obj-m := timer.o

build: kernel_modules

kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

二、个人理解

timer还是相对容易理解的,加上之前驱动的led还有自旋锁,该例程使用了三方面的知识,timer,自旋锁,led,还是很好玩的。

发布了29 篇原创文章 · 获赞 0 · 访问量 428

猜你喜欢

转载自blog.csdn.net/weixin_42547950/article/details/104241940