fmql之Linux内核定时器

内容依然来自于正点原子。

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;
}


运行结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值