Chinaunix首页 | 论坛 | 博客
  • 博客访问: 296476
  • 博文数量: 65
  • 博客积分: 185
  • 博客等级: 入伍新兵
  • 技术积分: 609
  • 用 户 组: 普通用户
  • 注册时间: 2012-11-06 21:41
个人简介

好好学习,天天向上

文章分类

全部博文(65)

文章存档

2022年(3)

2021年(25)

2020年(1)

2019年(3)

2016年(2)

2015年(3)

2014年(14)

2013年(7)

2012年(7)

我的朋友

分类: LINUX

2012-11-07 19:18:41

项目有痕:模拟脉冲宽度测量程序  
2011-12-02 20:06:31|  分类: 项目有痕 |  标签:嵌入式   |字号 订阅
  固定频率的脉冲信号上升沿和下降沿之间的时差,即半个周期,测量这个时差,便可得出频率值。本程序通过操作按键来模拟脉冲信号,按键抬起,则触发上升沿中断,按下则触发下降沿中断,检测二次中断期间定时器的计数差值,并据此算出时间差。

  本程序中定时器中断频率为100赫兹,通过读取定时器中断计数值及定时器监视寄存器(TCNTO)的值,计时精度可达0.2微秒。

  程序由计时器驱动模块,按键中断模块及测试程序组成。代码如下:

/* ******************************************************
 * Name: pwm.c
 * Proj: 测量脉冲宽度(Mini2440)
 * Desc: 用按键模拟脉冲源, 计算从按键抬起至下次按键按下所经过的时间
         本模块通过设置定时器, 实现计时功能
 * Author & Date: Joshua Chan, 2011/12/02
 * ******************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

/* 定义定时器发生中断的频率为100 */
#define FREQ 100

/* 定义时器中断计数 */
static volatile unsigned long my_jif;

/* 定时器中断处理函数 */
static irqreturn_t pwm_isr(int irq, void *pin)
{
    my_jif++;
    return IRQ_HANDLED;
}

/* 定时器缓冲寄存器的值, 当中断频率为100时, 其值为50625 */
static unsigned long tcnt;

/* 设置定时器中断频率函数 */
static void pwm_freq_set(unsigned long freq)
{
    unsigned long tcfg0;
    unsigned long tcfg1;
    unsigned long tcon;
    unsigned long tcmp;
    struct clk *pclk;
    unsigned long pclk_rate;

    /* 先通过clk_get()函数取得pclk结构内容, 再用clk_get_rate()
     * 获得pclk的频率 */
    pclk = clk_get(NULL, "pclk");
    pclk_rate = clk_get_rate(pclk);
    printk("Pclk_rate = %lu\n", pclk_rate);

    /* 配置TCFG0寄存器, 设置prescaler值为4 */
    tcfg0 = __raw_readl(S3C2410_TCFG0);
    tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK;
    tcfg0 |= (5 - 1);
    __raw_writel(tcfg0, S3C2410_TCFG0);
    printk("Prescaler value = %d\n", 5-1);

    /* 配置TCFG1寄存器, 设置divider值为2 */
    tcfg1 = __raw_readl(S3C2410_TCFG1);
    tcfg1 &= ~S3C2410_TCFG1_MUX0_MASK;
    tcfg1 |= S3C2410_TCFG1_MUX0_DIV2;
    __raw_writel(tcfg1, S3C2410_TCFG1);
    printk("Divider value = %d\n", 2);

    /* 根据freq值求出计数值, 并存入TCNTB0和TCMPB0中 */
    tcnt = (pclk_rate / 5 / 2) / freq;
    tcmp = tcnt / 2;
    __raw_writel(tcnt, S3C2410_TCNTB(0));
    __raw_writel(tcmp, S3C2410_TCMPB(0));
    printk("Timer input clock freq (pclk/5/2) = %lu\n", pclk_rate/5/2);
    printk("tcnt = %lu\n", tcnt);

    /* 配置TCON寄存器, 设置循环加载, 更新计数值并启动计数器 */
    tcon = __raw_readl(S3C2410_TCON);
    tcon &= ~0x1F;
    tcon |= 0xB;
    __raw_writel(tcon, S3C2410_TCON);

    /* 根据设备手册, 将计数值更新位清零, 以便下次写入 */
    tcon &= ~0x2;
    __raw_writel(tcon, S3C2410_TCON);
}

/* 停止定时器 */
static void pwm_stop(void)
{
    unsigned long tcon;

    tcon = __raw_readl(S3C2410_TCON);
    tcon &= ~1;
    __raw_writel(tcon, S3C2410_TCON);
}

/* 定义file_operations结构, 准备混杂设备的注册 */
static struct file_operations pwm_fops = {
    .owner = THIS_MODULE,
};

/* 定义混杂设备结构, 填充必要信息 */
static struct miscdevice pwm_misc = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "my_pwm",
    .fops = &pwm_fops,
};

/* 驱动入口函数, 申请中断, 注册混杂设备, 设置频率 */
static int __init pwm_init(void)
{
    request_irq(IRQ_TIMER0, pwm_isr, IRQ_TYPE_EDGE_BOTH, "my_timer", &tcnt);
    misc_register(&pwm_misc);
    pwm_freq_set(FREQ);

    return 0;
}

/* 驱动出口函数, 完成清理工作 */
static void __exit pwm_exit(void)
{
    free_irq(IRQ_TIMER0, &tcnt);
    pwm_stop();
    misc_deregister(&pwm_misc);
}

module_init(pwm_init);
module_exit(pwm_exit);
MODULE_LICENSE("GPL");

/* 导出变量, 以便在按键中断处理函数中使用 */
EXPORT_SYMBOL(my_jif);
EXPORT_SYMBOL(tcnt);


/* ******************************************************
 * Name: button.c
 * Proj: 测量脉冲宽度(Mini2440)
 * Desc: 用按键模拟脉冲源, 计算从按键抬起至下次按键按下所经过的时间
         本模块通过按键动作, 使引脚电平发生变化, 从而触发中断, 并
         在中断处理函数中读取定时器的值, 实现脉冲时间的精确测量
 * Author & Date: Joshua Chan, 2011/12/02
 * ******************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

/* Mini2440的按键key0, 对应端口为GPG0, 对应中断为EINT8 */

/* 为了使驱动中的read()函数在没有中断发生时休眠, 需先定义一个等待队列 */
static DECLARE_WAIT_QUEUE_HEAD(btn_wq);

/* 定义等待队列的唤醒条件 */
static volatile int btn_press;

/* 读取相应键值, 即GPGDAT相应位的值, 通过键值可以判断按下/抬起状态 */
static unsigned int key_val;

/* 由于这里使用内核函数s3c2410_gpio_getpin()读取GPGDAT的值,
   所以需要提供按键的pin值, 并作为request_irq()的参数传入 */
static unsigned int pin = S3C2410_GPG0;

/* 定义容纳2个元素的数组, 第1个位置存放定时器中断计数值, 第2个位置
   存放寄存器TCNTO已计数的值, 其中, t1为开始读数, t2为结束读数,
   t为计算结果 */
static unsigned long t1[2];
static unsigned long t2[2];
static unsigned long t[2];

/* 外部变量, 在定时器模块中有定义 */
extern unsigned long my_jif;
extern unsigned long tcnt;

/* 读取TCNTO的当前值 */
static inline unsigned short get_tcnto0(void)
{
    return __raw_readw(S3C2410_TCNTO(0));
}

/* 处理计数结果 */
static inline void calc(void)
{
    if (t2[1] >= t1[1]) {
        t[0] = t2[0] - t1[0];
        t[1] = t2[1] - t1[1];
    } else {
        t[0] = t2[0] - 1 - t1[0];
        t[1] = t2[1] + tcnt - t1[1];
    }
}

static int count;    // 测量次数计数
static int flag = 0; // 计数状态, 1表示计数已开始

/* 定义中断处理函数, 读取中断按键的状态值(按下为低, 抬起为高),
   得出计时结果, 并唤醒等待队列 */
static irqreturn_t btn_isr(int irq, void *pin)
{
    key_val = !s3c2410_gpio_getpin(*(unsigned int *)pin);
    if (key_val) {       // 按键按下
        if (flag == 1) { // 计数已开始
            flag = 0;    // 停止计数
            t2[0] = my_jif;
            t2[1] = tcnt - (unsigned long)get_tcnto0();
            calc();
            printk("Timer %d stopped.\n", count);
            printk("t[0] = %lu, t[1] = %lu\n", t[0], t[1]);

            /* 唤醒read()函数 */
            btn_press = 1;
            wake_up_interruptible(&btn_wq);
        }
    } else {      // 按键抬起
        flag = 1; // 计数开始
        t1[0] = my_jif;
        t1[1] = tcnt - (unsigned long)get_tcnto0();
        printk("Timer %d start...\n", ++count);
    }

    return IRQ_HANDLED;
}

/* 注册中断, 完成申请中断号, 向内核加入中断处理函数, 设定中断触发条件等任务,
   注册成功后可在/proc/interrupts中查看 */
static int btn_open(struct inode *inode, struct file *file)
{
    request_irq(IRQ_EINT8,  btn_isr, IRQ_TYPE_EDGE_BOTH, "my_button1", &pin);

    return 0;
}

/* 定义驱动中的read()函数, 将测量结果传回用户空间, 无中断发生则休眠 */
static ssize_t btn_read(struct file *file, char __user *buf,
                        size_t count, loff_t *loff)
{
    wait_event_interruptible(btn_wq, btn_press);
    btn_press = 0;
    copy_to_user(buf, &t, sizeof(t));

    return sizeof(t);
}

/* 驱动中的release()函数, 注销中断 */
static int btn_close(struct inode *inode, struct file *file)
{
    free_irq(IRQ_EINT8,  &pin);
    return 0;
}

/* 驱动中的设备操作方法结构体 */
static struct file_operations btn_fops = {
    .owner = THIS_MODULE,
    .open = btn_open,
    .read = btn_read,
    .release = btn_close,
};

/* 定义混杂设备结构 */
static struct miscdevice btn_misc = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "my_button",
    .fops = &btn_fops,
};

/* 驱动入口函数, 完成驱动注册, 设备自动生成工作 */
static __init int btn_init(void)
{
    misc_register(&btn_misc);
    return 0;
}

/* 驱动出口函数, 完成驱动清理工作 */
static __exit void btn_exit(void)
{
    misc_deregister(&btn_misc);
}

module_init(btn_init);
module_exit(btn_exit);
MODULE_LICENSE("GPL");


/* ******************************************************
 * Name: test.c
 * Proj: 测量脉冲宽度(Mini2440)
 * Desc: 驱动测试程序
 * Author & Date: Joshua Chan, 2011/12/02
 * ******************************************************/
#include
#include
#include
#include

int main(void)
{
    int fd;
    unsigned long t[2];
    double time;

    fd = open("/dev/my_button", O_RDONLY);
    if (fd < 0) {
        printf("Open /dev/my_button failed.\n");
        return -1;
    }

    /* 循环读取测量结果, 转换成时间单位打印输出 */
    while (true) {
        read(fd, &t, sizeof(t));
        time = (t[0] / 100.0) + (t[1] / 100.0 / 50625.0);
        printf("================================\n");
        printf("time = %.2lf s\n\n", time);
    }

    close(fd);

    return 0;
}

阅读(1103) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~