orangepi3lts Timer0和位原子操作
·
orangepi3lts 的CPU有两个timer定时器,这里使用的是timer0。
这里主要包含3个文件,atomic_bit.c是内核驱动,makefile是编译脚本,main.c是测试程序。
除了应用timer0几个寄存器知识外,还涉及misc混杂设备知识,IO宏知识,原子位操作知识。
废话不多说上文件,第一个atomic_bit.c,加载模块后会自动在/dev/目录下创建atomic_bit_dev混杂设备文件,main.c会打开它用于测试竞态是否有效,timer0计时是否准确,可靠。
/*
* 使用原子操作解决资源竞态问题
* 适用于所有竞态 * #define pos 0
* unsigned long bit_num = 0
* set_bit(pos, &bit_num); * change_bit(pos, &bit_num); * clear_bit(pos, &bit_num);
* test_bit(pos, &bit_num);
* 类似中断一样,可以使用一个位标注资源是否被使用。
* 位原子操作是使用带中断自旋锁实现的,所以才能解决所有竞态问题
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/ioctl.h>
#include <linux/miscdevice.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <asm/atomic.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("XXXX");
MODULE_VERSION("0.0.1");
// Timer0 基地址
#define TIMER_BASE 0x03009020
#define TIMER_START _IO('t', 0)
#define TIMER_GET _IOR('t', 1, long)
void __iomem *atomic_bit_base = NULL;
// 下面两个变量用于位原子操作
#define BIT_POS 0
volatile unsigned long atomic_num = 0;
int atomic_bit_open(struct inode *node, struct file *file){
printk(KERN_INFO"My misc device opened\n");
if(!test_bit(BIT_POS, &atomic_num)){
return -EBUSY;
}
clear_bit(BIT_POS, &atomic_num);
atomic_bit_base = ioremap(TIMER_BASE, SZ_32);
if(IS_ERR(atomic_bit_base)){
printk(KERN_INFO"ioremap error\n");
return -1;
}
// 此处设置timer0最长计时1S
// set init value as 12M, 1S
iowrite32(12000000, atomic_bit_base + 0x04);
// set clock source is 24M, pre-scale is 2
iowrite32(0x94, atomic_bit_base);
return 0;
}
int atomic_bit_release(struct inode *node, struct file *file){
printk(KERN_INFO"My misc device close\n");
iounmap(atomic_bit_base);
set_bit(BIT_POS, &atomic_num);
return 0;
}
long atomic_bit_ioctl(struct file *file, unsigned int cmd, unsigned long arg){
long value = 0;
int ret = 0;
switch(cmd){
case TIMER_START:
// reload atomic_bit0
iowrite32(ioread32(atomic_bit_base)|(1<<1), atomic_bit_base);
// wait load all right
while((ioread32(atomic_bit_base) >> 1) & 1) ;
// enable atomic_bit0
iowrite32(ioread32(atomic_bit_base)|(1 << 0), atomic_bit_base);
break;
case TIMER_GET:
// disable atomic_bit0
iowrite32(ioread32(atomic_bit_base) & ~(1 << 0), atomic_bit_base);
value = ioread32(atomic_bit_base + 0x8);
value = 12000000 - value;
value /= 12;
ret = copy_to_user((void *)arg, &value, sizeof(long));
if(ret < 0){
printk(KERN_INFO"copy to user error\n");
iounmap(atomic_bit_base);
set_bit(BIT_POS, &atomic_num);
return -1;
}
break;
default:
printk(KERN_INFO"Unknown command\n");
iounmap(atomic_bit_base);
set_bit(BIT_POS, &atomic_num);
return -EINTR;
break;
}
return 0;
}
struct file_operations fops = {
.owner = THIS_MODULE,
.open = atomic_bit_open,
.release = atomic_bit_release,
.unlocked_ioctl = atomic_bit_ioctl,
};
struct miscdevice misc_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "atomic_bit_dev",
.fops = &fops,
};
static int __init atomic_bit_init(void){
printk(KERN_INFO"Misc device init\n");
misc_register(&misc_dev);
set_bit(BIT_POS, &atomic_num);
return 0;
}
static void __exit atomic_bit_exit(void){
printk(KERN_INFO"Misc device exit\n");
misc_deregister(&misc_dev);
}
module_init(atomic_bit_init);
module_exit(atomic_bit_exit);
main.c测试文件,运行程序会自动测试一次timer0。将timer0硬件获取的计时值,和gettimeofday系统计算时间比较。会有一点差距但大体相当。
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/time.h>
#include <errno.h>
#define TIMER_START _IO('t', 0)
#define TIMER_GET _IOR('t', 1, long)
int main(int argc, char **argv){
int fd = 0;
int ret = 0;
char c = 0;
long hard_time = 0;
long hard_back = 0;
long time_cost = 0;
struct timeval start;
struct timeval end;
if(0 > (fd = open("/dev/atomic_bit_dev", O_RDWR))){
fprintf(stderr,"Open /dev/wdt_dev error\n"); return -EINVAL;
}
do{ gettimeofday(&start,NULL);
ioctl(fd, TIMER_START);
// 延时0.5mS usleep(500000);
ioctl(fd, TIMER_GET, &hard_time); hard_back = hard_time;
hard_time = 0;
ioctl(fd, TIMER_START);
// 延时0.05mS
usleep(50000);
ioctl(fd, TIMER_GET, &hard_time);
hard_back += hard_time;
gettimeofday(&end, NULL);
time_cost = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_usec - start.tv_usec);
printf("hardware get time is %ld, and software get time is %ld\n",hard_back, time_cost);
c = getchar();
}while('q' != c);
close(fd);
return 0;
}
最后是编译脚本makefile
CONFIG_MODULE_SIG=n KERNDIR:=/lib/modules/`uname -r`/build
PWD:= $(shell pwd)
obj-m:= atomic_bit.o
all:
make -C $(KERNDIR) M=$(PWD) modules ; \
gcc -o main main.c
clean:
make -C $(KERNDIR) M=$(PWD) clean ; \
rm main
使用方法:
make
sudo insmod atomic_use.ko
sudo ./main
# 正常会显示: hardware get time is 550206, and software get time is 550218
# 前面一个数字是硬件timer0计算的时间,单位是us。
# 后面是用系统gettimeofday计算的时间,也是us。可以看到差距不大。可以更改main.c中usleep参数测试不同时间
# 按Ctrl + z后台运行main程序
# 再次运行main程序
sudo ./main
# 得到错误:Open /dev/wdt_dev error
# 前台第一个main程序,输入q字符,结束程序
fg
q
# 再次运行main程序就可以打开了
sudo ./main
昇腾计算产业是基于昇腾系列(HUAWEI Ascend)处理器和基础软件构建的全栈 AI计算基础设施、行业应用及服务,https://devpress.csdn.net/organization/setting/general/146749包括昇腾系列处理器、系列硬件、CANN、AI计算框架、应用使能、开发工具链、管理运维工具、行业应用及服务等全产业链
更多推荐

所有评论(0)