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
Logo

昇腾计算产业是基于昇腾系列(HUAWEI Ascend)处理器和基础软件构建的全栈 AI计算基础设施、行业应用及服务,https://devpress.csdn.net/organization/setting/general/146749包括昇腾系列处理器、系列硬件、CANN、AI计算框架、应用使能、开发工具链、管理运维工具、行业应用及服务等全产业链

更多推荐