您的位置:首页 > 其它

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

2012-09-20 16:57 555 查看
最近在项目中遇到需要定时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了。

 

本人初出茅庐,欢迎提出意见,共同交流进步。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c timer struct module file 任务