嵌入式开发版采用正点原子linux开发版
linux内核定时器是基于jiffies节拍进行调度执行的一种机制,类似于“软中断”,之运行一次之后便会被注销。
由于中断资源的访问,所以对于一些被访问的数据结构都应该使用并发访问保护,防止竞争条件,而且因为他是处于非进程的上下文中,所以回调函数需要注意,不能执行休眠和调度。
在
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() 。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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,
};
关于并发保护使用的自旋锁,自旋锁的使用相对简单,主要是为了确保该部分代码的执行一致性,完整性,不存在其他任何进程同时进行访问。
#include
#include
#include
#include
#include
#include
#include "stdlib.h"
#include
#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;
}
使用的是正点原子提供的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,还是很好玩的。