IMX6ULL的定时器驱动(搭配pinctrl和gpio子系统,自旋锁)

自旋锁

自旋锁顾名思义就是一个一直在旋转循环忙等待的锁,哪个线程有锁就是哪个线程在访问共享资源。而其它线程只能在那等待,当锁被释放了之后,才轮到其它线程占有这个锁。自旋锁只适用于线程持有锁的时间不算太长的情况下,否则会浪费处理器时间,降低系统性能。

自旋锁API函数

在这里插入图片描述
适用于线程和线程之间。

需要注意的是:被自旋锁保护的临界区绝对不能调用任何能够引起睡眠或阻塞的API函数,不然可能导致死锁现象。如果在线程之间的并发访问时,中断也要参与的话,在中断里可以使用自旋锁,但是在获取锁之前要先禁止本地中断(本CPU中断),否则就可能导致死锁现象。

线程与中断并发访问处理API函数

在这里插入图片描述

自旋锁使用注意事项

  1. 锁的持有时间不能太长;
  2. 自旋锁保护的临界区内不能调用任何可能导致线程休眠的 API 函数;
  3. 不能递归申请自旋锁;
  4. 为了驱动的可移植性,不管用的是单核的还是多核的 SOC,都将其当做多核 SOC 来编写驱动程序。

定时器相关的API函数

  1. init_timer

    /*
    	@function: 负责初始化 timer_list 类型变量
    	@param:
    			timer:要初始化的定时器
    */
    
    void init_timer(struct timer_list *timer)
    
  2. add_timer

    /*
    	@function: 负责向 Linux 内核注册定时器
    	@param:
    			timer:要注册的定时器
    */
    void add_timer(struct timer_list *timer)
    
  3. del_timer

    /*
    	@function: 用于删除一个定时器
    	@param:
    			timer:要删除的定时器
    	@return:
    			0,定时器还没被激活;1,定时器已经激活
    */
    int del_timer(struct timer_list * timer)
    
  4. del_timer_sync

    /*
    	@function: 用于删除一个定时器,但是不能使用在中断上下文中,会等待其他处理器使用完定时器再删除
    	@param:
    			timer:要删除的定时器
    	@return:
    			0,定时器还没被激活;1,定时器已经激活
    */
    int del_timer_sync(struct timer_list *timer)
    
  5. mod_timer

    /*
    	@function: 用于修改定时值
    	@param:
    			timer:要修改的定时器
    			expires:修改后的超时时间
    	@return:	
    			0,调用 mod_timer 函数前定时器未被激活
    			1,调用 mod_timer 函数前定时器已被激活
    */
    int mod_timer(struct timer_list *timer, unsigned long expires)
    

Linux内核短延时函数

在这里插入图片描述

ioctl命令

//不带参数的ioctl命令
#define _IO(type,nr)		_IOC(_IOC_NONE,(type),(nr),0)
//带读参数的ioctl命令
#define _IOR(type,nr,size)	_IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
//带写参数的ioctl命令
#define _IOW(type,nr,size)	_IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
//带读写参数的ioctl命令
#define _IOWR(type,nr,size)	_IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))

type:用来区分不同驱动的,类似设备号,详细描述位于内核源码的/Documentation/ioctl/ioctl_number.txt文档中。

nr:命令编号。

定时器驱动代码

驱动功能:

​ 实现开启、关闭定时器;配置定时器超时时间;用LED翻转来表示定时器工作。

#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/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/device.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

/* IO控制命令 */
#define CLOSE_CMD       (_IO(0XEF, 0x1))
#define OPEN_CMD        (_IO(0XEF, 0x2))
#define SETPERIOD_CMD   (_IO(0XEF, 0x3))

#define DEVICE_CNT       1           /* 设备号个数 */
#define DEVICR_NAME      "xxx"       /* 设备名字 */

#define DEFAULT_PERIOD  1000		/* 默认定时器的超时时间 */

/* timer 设备结构体 */
struct device_dev
{
    /* 字符驱动设备 */
    dev_t devid;                    /* 设备号 */
    struct  cdev    cdev;           /* cdev */
    struct  class   *class;         /* 类 */
    struct  device  *device;        /* 设备 */
    struct  device_node *nd;        /* 设备节点 */
    int major;                      /* 主设备号 */
    int minor;                      /* 次设备号 */

    /* GPIO */
    int xxx_gpio;                   /* 设备所使用的 GPIO 编号 */

    /* 自旋锁 */
#if 1
    spinlock_t lock;                /* 定义自旋锁,用来保护 timeperiod */
#endif

#if 1
    /* 定时器 */
    int timeperiod;                 /* 定时周期,单位为 ms */
    struct  timer_list  timer;      /* 定义一个定时器 */
#endif
};

/* 创建一个 device 设备 */
struct device_dev device;

/* 定义一个 sta 作为 led 的状态值 */
static int sta = 1;

/* 利用 pinctrl 和 gpio 子系统初始化 IO */
static int gpio_init(void)
{
    int ret = 0;
    /* 通过设备树路径找到节点, 这里的node指的是在设备树中的节点的名字*/
    device.nd = of_find_node_by_path("/node");

    /* 获取设备树中的GPIO的属性,节点,xxx-gpio属性,gpio编号 */
    device.xxx_gpio = of_get_named_gpio(device.nd, "xxx-gpio", 0);
    if(timer.xxx_gpio < 0)
    {
        printk("can't get device\r\n");
        return -EINVAL;
    }

    /* 申请一个GPIO,并命名为xxx */
    gpio_request(device.xxx_gpio, "xxx");

    /* 配置 GPIO 为输出模式,默认输出高电平 */
    ret = gpio_direction_output(device.xxx_gpio, 1);
    if(ret < 0) 
    {
        printk("can't set gpio!\r\n");
    }
    return 0;
}

/* led 翻转 */
void led_toggle(void)
{
    sta =! sta;
    gpio_set_value(dev->led_gpio, sta);
}

/* io控制函数 */
static long timer_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    struct timer_dev *dev = (struct timer_dev *)filp->private_data;
    int timerperiod;
    unsigned long flags;
    
    /* 定时器控制命令 */
    switch(cmd)
    {
        /* 关闭定时器 */
        case CLOSE_CMD:
            del_timer_sync(&dev->timer);
            break;
        
        /* 打开定时器 */
        case OPEN_CMD:
            spin_lock_irqsave(&dev->lock, flags);
            timerperiod = dev->timeperiod;
            spin_unlock_irqrestore(&dev->lock, flags);
            mod_timer(&dev->timer, jiffies + msecs_to_jiffies(timerperiod));
            break;
            
        /* 配置定时器定时周期 */
        case SETPERIOD_CMD:
            spin_lock_irqsave(&dev->lock, flags);
            dev->timeperiod = arg;
            spin_unlock_irqrestore(&dev->lock, flags);
            mod_timer(&dev->timer, jiffies + msecs_to_jiffies(arg));
            break;
        
        default:
            break;
    }
    return 0;
}



#if 1
/* 定时器回调函数,当定时器定时完毕会自动执行 */
void timer_function(unsigned long arg)
{
    struct device_dev *dev = (struct device_dev *)arg;
    int timerperiod;
    unsigned long flags;

    led_toggle();

    /* 重启定时器 */
    /* 1. 保存中断状态,关闭本地中断,获取自旋锁 */
    spin_lock_irqsave(&dev->lock, flags);
    /* 2. 更新一下定时器周期 */
    timerperiod = dev->timeperiod;
    /* 3. 恢复中断状态,打开本地中断,释放自旋锁 */
    spin_unlock_irqrestore(&dev->lock, flags);
    /* 4. 修改定时器的超时时间 */
    mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->timeperiod));
}
#endif


/* 设备开启函数 */
static int device_open(struct inode *inode, struct file *filp)
{
    int ret = 0;
    filp->private_data = &device;           /* 设置私有数据 */

#if 1
    device.timeperiod = DEFAULT_PERIOD;     /* 配置定时器默认周期 */
#endif

    ret = gpio_init(); 		 	            /* 初始化IO */
    if (ret < 0)
    {
        return ret;
    }
    return 0;
}

/* 从设备读取数据 */
static ssize_t device_read(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
    int ret = 0;
    unsigned char stat;
    struct device_dev *dev = filp->private_data;
    stat = gpio_get_value(dev->xxx_gpio);
    
    /* some operations of stat */

    return ret;
}

/* 向设备写入数据 */
static ssize_t device_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
    int retvalue = 0;
    unsigned char databuf[1];
#if 1
    unsigned char user_1 = 0;
#if 0
    unsigned char i = 0;
    unsigned char index = 1;
    unsigned char user_buf[index];
#endif
    
    struct device_dev *dev = filp->private_data;
    retvalue = copy_from_user(databuf,buf,cnt);

#if 1
    user_1 = databuf[0];
#if 0
    for ( i = 0; i < index; i++)
    {
        user_buf[index] = databuf[index];
    }
#endif

    /* some operations of data */

    return 0;
}

/* 设备释放函数 */
static int device_release(struct inode *inode, struct file *filp)
{
    return 0;
}

static struct file_operations key_fops = 
{
    .owner      =   THIS_MODULE,
    .open       =   device_open,
    .write      =   device_write,
    .read       =   device_read,
    .release    =   device_release,
}

/* 驱动入口函数 */
static int __init device_init(void)
{
    /* 注册字符设备驱动 */
    /* 1、创建设备号 */
    if (device.major) { /* 定义了设备号 */
        device.devid = MKDEV(device.major, 0);
        register_chrdev_region(device.devid, DEVICE_CNT, DEVICE_NAME);
    } else { /* 没有定义设备号 */
        alloc_chrdev_region(&device.devid, 0, DEVICE_CNT, DEVICE_NAME);
        device.major = MAJOR(device.devid); /* 获取主设备号 */
        device.minor = MINOR(device.devid); /* 获取次设备号 */
    }

    /* 2、注册设备 */
    device.cdev.owner = THIS_MODULE;
    cdev_init(&device.cdev, &device_fops);
    cdev_add(&device.cdev, device.devid, DEVICE_CNT);

    /* 3、创建类 */
    device.class = class_create(THIS_MODULE, DEVICE_NAME);
    if (IS_ERR(device.class)) {
        return PTR_ERR(device.class);
    }

    /* 4、创建设备 */
    device.device = device_create(device.class, NULL, device.devid, NULL, DEVICE_NAME);
    if (IS_ERR(device.device)) {
        return PTR_ERR(device.device);
    }

#if 1
        /* 初始化自旋锁 */
        spin_lock_init(&device.lock);
#endif

#if 1
    /* 初始化 timer,设置定时器处理函数,还未设置周期,还不会激活定时器 */
    init_timer(&device.timer);
    /* 配置定时器回调函数 */
    device.timer.function = timer_function;
    device.timer.data = (unsigned long)&timer;
#endif

    return 0;
}

/* 驱动出口函数 */
static void __exit timer_exit(void)
{
    gpio_set_value(device.xxx_gpio,0);
    del_timer_sync(&device.timer);

    cdev_del(&device.cdev);
    unregister_chrdev_region(device.devid, DEVICE_CNT);
    device_destroy(device.class, device.devid);
    class_destroy(device.class);
    gpio_free(device.xxx_gpio);
}

定时器应用代码

#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"

/* 命令值 */
#define CLOSE_CMD       (_IO(0XEF, 0x1)) /* 关闭定时器 */
#define OPEN_CMD        (_IO(0XEF, 0x2)) /* 打开定时器 */
#define SETPERIOD_CMD   (_IO(0XEF, 0x3)) /* 设置定时器周期命令 */

int main(int argc, char *argv[])
{
    int fd, ret;
    char *filename;
    unsigned int cmd;
    unsigned int arg;
    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);
        if(ret != 1)
        {
            gets(str);
        }

        if(cmd == 1)
            cmd = CLOSE_CMD;
        else if (cmd == 2)
        {
            cmd = OPEN_CMD;
        }
        else if (cmd == 3)
        {
            cmd = SETPERIOD_CMD;
            printf("Input Timer Period:");
            ret = scanf("%d", &arg);
            if(ret != 1)
            {
                gets(str);
            }
        }
        ioctl(fd, cmd, arg);
    }
    close(fd);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值