内容依然来自于正点原子。
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; // cdev
struct class *class; // class
struct device *device; // devie
int major; // major
int minor; // minor
struct device_node *nd; // device_node
int led_gpio; // led_gpio
int 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子系统申请使用GPIO
if(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;
else
val = 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); // 初始化cdev
ret = cdev_add(&led.cdev, led.devid, LED_COUNT); // 添加cdev
if(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,设备号
// 释放GPIO
device_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;
}