实现s3c2440的硬件定时器驱动(顺带分析不同定时要求需要用何种方式)

最近在项目中遇到需要定时1ms,用msleep定时不准,误差有时候很大;用mdelay又会忙等待,在延迟过程中无法运行其他任务;所以只能自己写一个定时器驱动了。

平台用的是s3c2440cpu ,内核linux2.6.32.2 ,编译器arm-linux-gcc-4.3.2

根据cpu手册,设置

Timer input clock Frequency = PCLK / {prescaler value+1} / {divider value}
{prescaler value} = 0~255
{divider value} = 2, 4, 8, 16

所以定时范围为0.04us---5.3686 sec     计数寄存器只有16位,感觉太小了

为了和大家分享贴出来,主要是根据网上的资料(http://www.arm9home.net/read.php?tid-3113-page-1.html)和数据手册写了一个自己项目使用的timer0驱动:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <mach/hardware.h>
#include <linux/cdev.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/poll.h>
#include <asm/uaccess.h>
#include <asm/ioctl.h>
#include <mach/regs-gpio.h>
#include <mach/regs-irq.h>
#include <linux/miscdevice.h>
#include <linux/sched.h>
#include <linux/gpio.h>
#include <plat/regs-timer.h>
#include <asm/io.h>

#include <linux/irq.h>

#include <linux/platform_device.h>
//#include <linux/timer0_ioc.h>
#include "timer0_ioc.h"
#define DEVICE_NAME "timer0"

static wait_queue_head_t ioctl_timer_ready;
unsigned long Ftclk,Fpclk=50000000; //s3c2440a的默认Fpclk为50MHz
unsigned int tcfg0,tcfg1,tcon;
int sleep_flag=0;
struct cdev *p_cdev; //声明一个指向字符设备结构体的指针
#define timer_irq IRQ_TIMER0
static irqreturn_t timer_interrupt(void)
{
    printk("Timer0 interrupt occured!\n");
    sleep_flag = 1;
    wake_up_all(&ioctl_timer_ready);
    return IRQ_HANDLED;
}

static int timer_open(struct inode *inode,struct file *filp)
{
    int ret;

    tcfg0 = inl(S3C2410_TCFG0);
    tcfg1 = inl(S3C2410_TCFG1);
    tcon = inl(S3C2410_TCON);

    outl((tcfg0 &= ~0xff) | 255,S3C2410_TCFG0);  //设置预分频
    outl((tcfg1 &= ~0xf) | 3,S3C2410_TCFG1);     //设置分频和模式
    Ftclk=Fpclk/(255+1)/16;  //参考datasheet公式
    outl(Ftclk,S3C2410_TCNTB(0));  //写入定时初值
    outl(0,S3C2410_TCMPB(0));  //写入终点比较值
    outl(tcon | S3C2410_TCON_T0MANUALUPD,S3C2410_TCON);  //手动刷新一次,将数据装入TCNT和TCMP
    tcon = inl(S3C2410_TCON) & ~S3C2410_TCON_T0MANUALUPD;
    outl(tcon | (S3C2410_TCON_T0START|S3C2410_TCON_T0RELOAD),S3C2410_TCON);  //设置自动装载初值,开始计数
     printk(KERN_EMERG "Register IRQ_TIMER0 successfully!\n");
    ret=request_irq(timer_irq,&timer_interrupt,IRQF_DISABLED,DEVICE_NAME,NULL);
    if(ret<0){
        printk(KERN_EMERG "Register IRQ_TIMER0 failed!\n");
        return ret;
    }
   
}

int timer_ioctl (struct inode *inode, struct file *filp,
                 unsigned int cmd, unsigned long arg)
{
 short int b_data;
 int ret=0;
 switch(cmd) {
  case READ_TIMER_INT_FLAG:       //读取中断
   wait_event_interruptible(ioctl_timer_ready, (sleep_flag == 1));
          sleep_flag = 0;
   ret = copy_to_user((u8 *)(arg),(u8 *)(&ret), 4);
   break;
  case SET_TIMER_TIME:
   copy_from_user((u8 *)(&b_data),(u8 *)arg,  2);
   Ftclk = (Ftclk*(unsigned long)b_data)/1000;
   outl(Ftclk,S3C2410_TCNTB(0));  //写入定时初值
   outl(0,S3C2410_TCMPB(0));  //写入终点比较值
   outl(tcon | S3C2410_TCON_T0MANUALUPD,S3C2410_TCON);
   tcon = inl(S3C2410_TCON) & ~S3C2410_TCON_T0MANUALUPD;
   outl(tcon | (S3C2410_TCON_T0START|S3C2410_TCON_T0RELOAD),S3C2410_TCON);  //设置自动装载初值,开始计数
   printk(KERN_EMERG "timer's time is %ld\r\n",Ftclk);
   break;
  default:
   printk("no ioctl code now\n");
          return 1;
  }
 return ret;
}

static int timer_close(struct inode *inode,struct file *filp)
{
    free_irq(timer_irq,NULL);
    return 0;
}

static struct file_operations timer_fops={
    .owner=THIS_MODULE,
    .open=timer_open,
    .ioctl = timer_ioctl,
    .release=timer_close,
};

 

static struct miscdevice misc = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = DEVICE_NAME,
    .fops = &timer_fops,
};

static int __init dev_init(void)
{
    int ret;
 
    ret = misc_register(&misc);
 init_waitqueue_head(&ioctl_timer_ready);
    printk (DEVICE_NAME"\tinitialized\n");

    return ret;
}

static void __exit dev_exit(void)
{
    misc_deregister(&misc);
}

MODULE_LICENSE("GPL");
MODULE_AUTHOR("HJW");
module_init(dev_init);
module_exit(dev_exit);

 

 


测试程序:

 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <errno.h>
#include "timer0_ioc.h"
int main(void)
{
    int fd;
    short int time = 3000;
    int arg;
    int ioctl_ret;
    fd=open("/dev/timer0",O_RDWR);
    if(fd<0){
        printf("Open /dev/timer failed!\n");
        exit(1);
    }
    else
    {
     printf("Open device successfully!\n");
     ioctl(fd,SET_TIMER_TIME,&time);
    }
    while(1)
    {
 ioctl_ret=ioctl(fd,READ_TIMER_INT_FLAG,&arg);
 if(ioctl_ret == -108)
 {
  printf("ioctl failed!\r\n");
 }
 else
 {
  printf("This is interrupt!\r\n");
 }
    }
   
    close(fd);
    return 0;
}

这次经验告诉我:

us,ms级定时必须用硬件定时,当然如果你在定时期间没有其他任务需要执行可以使用udelay,mdelay,也可以。

s级定时直接可以用sleep了。

 

本人初出茅庐,欢迎提出意见,共同交流进步。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值