项目有痕:模拟脉冲宽度测量程序
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
/* 定义定时器发生中断的频率为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
/* 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;
}