极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 18891|回复: 12

外部中断方式测瞬时风速的程序(同时证明了外部中断发生后程序会回到中断处继续执行)

[复制链接]
发表于 2012-10-9 21:50:01 | 显示全部楼层 |阅读模式
本帖最后由 shenhaiyu 于 2012-10-10 12:13 编辑

最近需要做一套带有测风速功能的仪器,苦于网上和坛子里都没有例子,于是自己动手改造了一个。
使用了TB买的一个手持风向风速仪,

将下面的把手拆掉,将原先4.5V的电源线接入Arduino的5V电源,GND接GND。风速仪里面是一个4叶的光电编码器,在板子上找到编码器信号放大的输出点,焊上一条引线接入Arduino的 Pin 2 做外部中断。



当时以为外部中断会打断正在运行的程序重新从loop的头部开始运行,
于是发了求助帖:http://www.geek-workshop.com/thread-2106-1-1.html
后来还是决定自己写程序试一下,代码如下:
  1. float sCode = 0;              // 码盘计数(即状态跳变次数)
  2. float ws = 0;                 // 1s 平均(瞬时)风速
  3. unsigned long t1 = 0, t2 = 0; // 时间标记

  4. void setup()
  5. {
  6.   Serial.begin(9600);              // 初始化串口通信
  7.   attachInterrupt(0, Code, CHANGE); // 设置风杯码盘外部中断
  8. }

  9. void loop()
  10. {
  11.   t1=0;
  12.   while(1)
  13.   {
  14.    Serial.print(t1);
  15.    Serial.print(" ");
  16.    Serial.println(sCode);
  17.    t1++;
  18.   }
  19. }

  20. // 风速码盘计数
  21. void Code()
  22. {
  23.   sCode += 1; // 码盘计数加一
  24. }
复制代码
运行后转动风杯,可以看到串口监视器里显示类似这样的值:

384 37.00
385 37.00
386 37.00
387 37.00
388 38.00
389 38.00
390 38.00
391 39.00
392 39.00
393 39.00
394 40.00
395 40.00
396 40.00
397 40.00
398 41.00
399 41.00
400 41.00
401 42.00
402 42.00
403 42.00
404 43.00
405 43.00
406 43.00
407 43.00
408 44.00
409 44.00
410 44.00
411 45.00
412 45.00
413 45.00
414 45.00
415 46.00
416 46.00
417 46.00
418 47.00
419 47.00
420 47.00
421 47.00
422 48.00
423 48.00
424 48.00
425 49.00
426 49.00
427 49.00
428 49.00
429 50.00
430 50.00
431 50.00
432 51.00
433 51.00
434 51.00
435 51.00
436 52.00
437 52.00
438 52.00
439 53.00
440 53.00
441 53.00
442 53.00
443 54.00
444 54.00
445 54.00
446 55.00
447 55.00
448 55.00
449 55.00
450 56.00
451 56.00
452 56.00
453 56.00
454 57.00
455 57.00

证明程序没有因外部中断而重新在loop开始处运行,但是会对程序运行时间有一定影响。
既然实验证明了中断发生后,程序会回到中断发生的地方继续执行,于是决定将这部分功能直接写入我的系统中。

下面是完整的瞬时风速(准确说是1s平均风速)程序,也给大家留个参考吧(补充了计算时关闭中断的函数)
  1. float sCode = 0;              // 码盘计数(即状态跳变次数)
  2. float ws = 0;                 // 1s 平均(瞬时)风速
  3. unsigned long t1 = 0, t2 = 0; // 时间标记

  4. void setup()
  5. {
  6.   Serial.begin(9600);              // 初始化串口通信
  7.   attachInterrupt(0, Code, CHANGE); // 设置风杯码盘外部中断
  8. }

  9. void loop()
  10. {
  11.   t1 = millis();
  12.   if(abs(t1 - t2) >= 1000) // 1000ms 时间间隔
  13.   {
  14.     detachInterrupt(0) // 关闭中断
  15.     t2 = t1;                                         // 更新时间标记
  16.     ws = 2 * 3.14159265 * 0.025 * 2.9 * (sCode / 8); // 计算风速 u=2πRbn
  17.     Serial.println(ws);                             // 输出 1s 平均(瞬时)风速
  18.     sCode = 0;                                       // 码盘计数归零
  19.     delay(1);
  20.     attachInterrupt(0, Code, CHANGE); // 重新设置外部中断
  21.   }
  22. }

  23. // 风速码盘计数
  24. void Code()
  25. {
  26.   sCode += 1; // 码盘计数加一
  27. }
复制代码
其中风速计算这句:
ws = 2 * 3.14159265 * 0.025 * 2.9 * (sCode / 8);
还可以改进一下,不使用这种误差较大的线性公式,换用这里提到的完整公式:
http://wenku.baidu.com/view/06705521af45b307e871971d.html

欢迎大家讨论改进的办法~~~~~

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复

使用道具 举报

 楼主| 发表于 2012-10-9 21:54:43 | 显示全部楼层
同样我也遇到了灵异事件,不知道大家是否注意到了delay(1);这句,如果不添加这个延时,程序会隔一段时间输出一个0值,准确说是连续执行了两次if里的程序,明明abs(t1 - t2) >= 1000不成立却还要执行if里的东西,真是奇怪,但是加上delay(1);就没有这个问题了,求解释
回复 支持 反对

使用道具 举报

发表于 2012-10-10 09:55:38 | 显示全部楼层
shenhaiyu 发表于 2012-10-9 21:54
同样我也遇到了灵异事件,不知道大家是否注意到了delay(1);这句,如果不添加这个延时,程序会隔一段时间输出 ...

你应该把那段数据贴上来,还有计算过程中,应该先关闭中断
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-10-10 11:16:18 | 显示全部楼层
飞翔的红猪 发表于 2012-10-10 09:55
你应该把那段数据贴上来,还有计算过程中,应该先关闭中断

有关数据,就是看串口监视器中会突然多显示出两行数字,第一行是正常风速值,第二行就是一个0值
回复 支持 反对

使用道具 举报

发表于 2012-10-11 16:18:37 | 显示全部楼层
为什么楼主要选择attachInterrupt(0, Code, CHANGE); 引脚改变时中断呢?为什么不用上升沿或者下降沿中断一次?
回复 支持 反对

使用道具 举报

发表于 2012-10-11 16:19:12 | 显示全部楼层
shenhaiyu 发表于 2012-10-10 11:16
有关数据,就是看串口监视器中会突然多显示出两行数字,第一行是正常风速值,第二行就是一个0值

多的两行数据是同时出现的吗?还是1S后跑出个0?
回复 支持 反对

使用道具 举报

发表于 2012-10-11 16:22:23 | 显示全部楼层
如果想解决那个0的输出
楼主就试试interrupts(),noInterrupts()这两个指令
关闭全部中断和开启全部中断,应该就没有问题了
回复 支持 反对

使用道具 举报

发表于 2012-10-11 17:36:15 | 显示全部楼层

我的分析是系统会自动调用定时中断指令进行某些后台程序所导致的,你可以把目前的问题当做编译器的一个BUG,以后写的时候注意下吧!
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-10-11 17:57:05 | 显示全部楼层
zhangdeyue1 发表于 2012-10-11 17:36
我的分析是系统会自动调用定时中断指令进行某些后台程序所导致的,你可以把目前的问题当做编译器的一个BU ...

嗯,以后会注意的,谢谢指教
回复 支持 反对

使用道具 举报

发表于 2012-10-11 18:11:42 | 显示全部楼层
shenhaiyu 发表于 2012-10-11 17:57
嗯,以后会注意的,谢谢指教

其实分析millis();就应该大概估计到,这个时间肯定不会通过延时来记录,所以我怀疑是通过定时器来记录时间!
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-10-12 23:22:21 | 显示全部楼层
zhangdeyue1 发表于 2012-10-11 18:11
其实分析millis();就应该大概估计到,这个时间肯定不会通过延时来记录,所以我怀疑是通过定时器来记录时间 ...

我晕。。。这个有点。。。。。
回复 支持 反对

使用道具 举报

发表于 2014-2-11 21:40:34 | 显示全部楼层
shenhaiyu 发表于 2012-10-9 21:54
同样我也遇到了灵异事件,不知道大家是否注意到了delay(1);这句,如果不添加这个延时,程序会隔一段时间输出 ...

detachInterrupt(0) // 关闭中断
attachInterrupt(0, Code, CHANGE); // 重新设置外部中断

如果他们是非同步的, 那就很正常啊,
比如 关闭中断只是下达指令, 结果真正关闭时,重新设置外部中断刚刚搞定。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2014-2-16 21:42:29 | 显示全部楼层
knite 发表于 2014-2-11 21:40
detachInterrupt(0) // 关闭中断
attachInterrupt(0, Code, CHANGE); // 重新设置外部中断

就是说命令并不是立刻执行的,而是有一定的延迟呗?
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 需要先绑定手机号

Archiver|联系我们|极客工坊

GMT+8, 2024-5-5 00:53 , Processed in 0.043040 second(s), 22 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表