内容依然来自于正点原子。
Linux内核时间管理
内容包括:
- 系统频率设置
- 节拍率:高节拍率的优缺点
- 全局变量jiffies
- 绕回的概念(溢出)
- API函数(处理绕回)
HZ为每秒的节拍数
Linux内核定时器
内容包括:
- 内核定时器的使用:设置超时时间
- 内核定时器特点:超时后会自动关闭
- timer_list结构体表示内核定时器,expires成员变量为超时时间(单位为节拍数),function为定时处理函数
- 初始化定时器的API函数
- 内核定时器的使用流程代码
Linux内核短延时函数
代码
timer.c
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/cdev.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/of_address.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/kern_levels.h>
// #include <linux/semaphore.h>#define LED_COUNT 1 // 设备个数
#define LED_NAME "led" // 设备名称/* ioctl函数命令定义 */
#define CMD_LED_CLOSE (_IO(0xEF,0x1)) /* turn off led */
#define CMD_LED_OPEN (_IO(0xEF,0x2)) /* turn on led */
#define CMD_SET_PERIOD (_IO(0xEF,0x3)) /* set LED闪烁频率 */struct led_dev {dev_t devid; // 设备号struct cdev cdev; // cdevstruct class *class; // classstruct device *device; // devieint major; // majorint minor; // minorstruct device_node *nd; // device_nodeint led_gpio; // led_gpioint period; /* period (ms) */struct timer_list timer; /* timer */spinlock_t spinlock; /* spinlock */
};static struct led_dev led;static int led_open(struct inode * inode, struct file *filp){return 0;
}static ssize_t led_read(struct file *filp, char __user *buf,size_t cnt, loff_t *offt){return 0;
}static ssize_t led_write(struct file *filp, char __user *buf,size_t cnt, loff_t *offt){return 0;
}static int led_release(struct inode * inode, struct file *filp){return 0;
}/* ioctl函数 */
static long led_unlocked_ioctl(struct file *filp, unsigned int cmd,unsigned long arg){unsigned long flags;spin_lock_irqsave(&led.spinlock, flags); // 自旋锁上锁switch(cmd){ // 应用程序发来的命令case CMD_LED_CLOSE:del_timer_sync(&led.timer);gpio_set_value(led.led_gpio, 0);break;case CMD_LED_OPEN:del_timer_sync(&led.timer);gpio_set_value(led.led_gpio, 1);break;case CMD_SET_PERIOD:led.period = arg;mod_timer(&led.timer, jiffies + msecs_to_jiffies(arg));break;default:break;}spin_unlock_irqrestore(&led.spinlock, flags); // 自旋锁解锁return 0;
}static struct file_operations led_fops = { // 设备操作函数.owner = THIS_MODULE,.open = led_open,.release = led_release,.read = led_read,// .write = led_write,.unlocked_ioctl = led_unlocked_ioctl, /* ioctl函数 */
};/* 定时器回调函数 */
static void led_timer_function(struct timer_list *unused){static bool on = 1;unsigned long flags;on = !on; /* 取反,实现LED反转 */spin_lock_irqsave(&led.spinlock, flags); // 上锁gpio_set_value(led.led_gpio, on); // 设置led-gpio电平状态/* 重启timer */mod_timer(&led.timer, jiffies + msecs_to_jiffies(led.period));spin_unlock_irqrestore(&led.spinlock, flags); // 解锁
}static int __init led_init(void){int ret = 0;int val;const char *str;spin_lock_init(&led.spinlock); // 初始化自旋锁led.nd = of_find_node_by_path("/led3"); // 获取设备节点if(led.nd == NULL){printk(KERN_ERR "key: Failed to get led node\r\n");return -EINVAL;} ret = of_property_read_string(led.nd, "status", &str); // 获取status属性if(!ret){if(strcmp(str, "okay"))return -EINVAL;}ret = of_property_read_string(led.nd, "compatible", &str); // 获取compatible属性if(ret < 0){printk(KERN_ERR "led: Failed to get compatible property\r\n");return -EINVAL;}if(strcmp(str, "fmql,led")){ // 匹配compatible属性printk(KERN_ERR "led: compatible math failed\r\n");return -EINVAL;}printk(KERN_INFO "led: device matches succeed\r\n");led.led_gpio = of_get_named_gpio(led.nd, "led-gpio", 0); // 获取led使用的gpio编号if(!gpio_is_valid(led.led_gpio)){printk(KERN_ERR "led: Failed to get led-gpio\r\n");return -EINVAL;}printk(KERN_INFO "led: led-gpio num = %d\r\n", led.led_gpio);ret = gpio_request(led.led_gpio, "LED GPIO"); // 向GPIO子系统申请使用GPIOif(ret){printk(KERN_ERR "led: Failed to request led-gpio\r\n");return ret;}ret = of_property_read_string(led.nd, "default-state", &str); // 设置led初始状态if(!ret){if(!strcmp(str, "on"))val = 1;elseval = 0;} else val = 0;gpio_direction_output(led.led_gpio, val);/* 注册字符设备驱动 */if(led.major){ // 创建设备号led.devid = MKDEV(led.major, 0);ret = register_chrdev_region(led.devid, LED_COUNT, LED_NAME);if(ret)goto out1;} else {ret = alloc_chrdev_region(&led.devid, 0, LED_COUNT, LED_NAME);if(ret)goto out1;led.major = MAJOR(led.devid);led.minor = MINOR(led.devid);}printk(KERN_INFO "led: major = %d, minor = %d\r\n", led.major, led.minor);led.cdev.owner = THIS_MODULE;cdev_init(&led.cdev, &led_fops); // 初始化cdevret = cdev_add(&led.cdev, led.devid, LED_COUNT); // 添加cdevif(ret)goto out2;led.class = class_create(THIS_MODULE, LED_NAME); // 创建类if(IS_ERR(led.class)){ret = PTR_ERR(led.class);goto out3;}led.device = device_create(led.class, NULL, led.devid, NULL, LED_NAME); //创建设备if(IS_ERR(led.device)){ret = PTR_ERR(led.device);goto out4;}/* 初始化timer* 绑定function函数* 未设置周期:不会激活timer*/timer_setup(&led.timer, led_timer_function, 0);return 0;out4:class_destroy(led.class);
out3:cdev_del(&led.cdev);
out2:unregister_chrdev_region(led.devid, LED_COUNT);
out1:gpio_free(led.led_gpio);return ret;
}static void __exit led_exit(void){/* delete timer */del_timer_sync(&led.timer);// 注销: 设备,类,cdev,设备号// 释放GPIOdevice_destroy(led.class, led.devid);class_destroy(led.class);cdev_del(&led.cdev);unregister_chrdev_region(led.devid, LED_COUNT);gpio_free(led.led_gpio);
}module_init(led_init);
module_exit(led_exit);MODULE_AUTHOR("Skylar <Skylar@33.com>");
MODULE_DESCRIPTION("FMQL Timer");
MODULE_LICENSE("GPL");
timerAPP.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>/* ioctl命令 */
#define CMD_LED_CLOSE (_IO(0xEF,0x1))
#define CMD_LED_OPEN (_IO(0xEF,0x2))
#define CMD_SET_PERIOD (_IO(0xEF,0x3))/** @description : main主程序* @param - argc : argv数组元素个数* @param - argv : 具体参数* @return : 0 成功;其他 失败*/
int main(int argc, char *argv[])
{int fd, ret;unsigned int cmd;unsigned int period;/* 传递两个参数 */if(argc != 2){printf("Usage:\n""\t.timerAPP /dev/key @ open LED device\n");return -1;}fd = open(argv[1], O_RDWR);if(fd < 0){printf("ERROR: %s file open failed\r\n", argv[1]);return -1;}/* 通过命令控制LED设备 */for(;;){printf("Input CMD:");scanf("%d", &cmd);switch(cmd){case 0:cmd = CMD_LED_CLOSE; break;case 1:cmd = CMD_LED_OPEN;break;case 2:cmd = CMD_SET_PERIOD;printf("Input Timer Period:");scanf("%d",&period);break;case 3:close(fd);return 0;default://cmd = CMD_LED_CLOSE;break;}ioctl(fd, cmd, period);}//close(fd);//return 0;
}