Arduino uno中进行数字读取和脉冲计数的CPU周期

3
我将尝试计算 HB100 微波传感器200ms 时间量子中的脉冲数量。
以下是代码:
#include <SoftwareSerial.h>
#include <elapsedMillis.h>
elapsedMillis ElapsedTime;

#define Sensor A0
#define TimeQuanta 200

int Counter = 0;
boolean LastState;

void setup()
{
  Serial.begin(250000);
  pinMode(Sensor, INPUT);  
}

void loop()
{
  Counter = 0;
  ElapsedTime = 0;
  while (ElapsedTime < TimeQuanta ){
    LastState = digitalRead(Sensor);
    if (LastState == LOW && digitalRead(Sensor) == HIGH ){
      Counter += 1;  //Compare Last state with current state 
    }
  }
  Serial.print(digitalRead(Sensor));
  Serial.print("\t");
  Serial.println(Counter);
}

我需要知道数字读取周期。

我正在将传感器的上一个状态与当前状态进行比较,如果有变化(从低到高),则计数器会增加。但是,我的计数器始终是0

  • 这段代码是否正确(if条件)?
  • 我需要添加一些延迟吗?
  • 可以计算这些脉冲吗?

这是逻辑分析仪输出的微波传感器图像:

enter image description here


编辑:如果在if前添加delay(1);,那么计数器就不再是0了。

3个回答

3

你可以使用Timer1来计算经过的时间。

// Set Timer1 without prescaler at CPU frequency
TCCR1A = 0; // TCCRx - Timer/Counter Control Register. The pre-scaler can be configured here. 
TCCR1B = 1;

noInterrupts ();  // Disable interrupts.

uint16_t  StartTime = TCNT1;  // TCNTx - Timer/Counter Register. The actual timer value is stored here.
digitalRead(pin); // your code.
uint16_t EndTime = TCNT1
uint16_t ElapsedTime = EndTime - StartTime;

interrupts (); //Enable interrupts.

作为第二种解决方案,您可以设置和取消一个引脚,并使用逻辑分析仪计算时间。
DDRD = DDRD | B10000000; // Set digital pin 7 as output.
PORTD = PORTD | B10000000; // Set digital pin 7.
digitalRead(pin); // your code.
PORTD = PORTD & B01111111; // Unset digital pin 7.

3
由于这些脉冲似乎甚至不到微秒长,因此您不能使用需要大约80个机器周期(约5us @16MHz)的digitalRead函数。更别说在每个循环迭代中打印如此多的数据了!所以你有超过1MHz的频率,你的代码可能只能计数大约10kHz(最好的情况下,也许更少)。无论如何,你应该使用带有T1输入时钟源的HW Timer/Counter 1。这个计数器能够以主时钟速率的一半(50%脉宽)计数脉冲。而且使用Timer/Counter 1相当简单:
TCCR1A = 0; // default mode, no output compare modes
TCCR1B = _BV(CS10) | _BV(CS11) | _BV(CS12); // clock select mode 7 - External clock source on T1 pin. Clock on rising edge.

每200毫秒读取一次TCNT1,最后将其重置为0或仅记住上一个值并进行差异计算(不要忘记它只是16位数字)。


谢谢你的回答,我在哪里可以找到有关任何操作的CPU周期文档? 现在打印已经超出了while循环。 - Mehran
我只是在使用自己的“基准测试”。但是可以切换一些输出并测量频率。也许有一些关于这个主题的文章。 - KIIV

2
KIIV非常清楚地解释了你的代码为什么不能工作,这里我想提出一个实现相同目标的替代方法。

代码:

const byte interruptPin = 2;
volatile unsigned long counter = 0;                // overflow after 2^32-1 pulses

void setup() {
  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin), count, RISING);
  Serial.begin(19200);
}

void loop() {
  Serial.print("No. pulses: ");
  Serial.println(counter);
  delay(1000);
}

void count() {
  counter++;                                       // never use print() in an ISR!
}

请注意,我更改了输入引脚,因为在使用此技术时可以使用的引脚受到所使用的板子的限制,具体信息请参考这里
Board                                Digital Pins Usable For Interrupts
Uno, Nano, Mini, other               2, 3
Mega, Mega2560, MegaADK              2, 3, 18, 19, 20, 21
Micro, Leonardo, other 32u4-based    0, 1, 2, 3, 7
Zero                                 all digital pins, except 4
MKR1000 Rev.1                        0, 1, 4, 5, 6, 7, 8, 9, A1, A2
Due, 101                             all digital pins

谢谢,我会阅读提到的文档并测试代码。我需要在200毫秒内持续计数脉冲,所以我认为delay(1000)不合适。 - Mehran
1
延迟出现在 print 函数上,脉冲通过 中断服务例程 count() 进行连续计数。你可以减少 延迟,但只能到一定程度。现代的 Arduino 实现使用 中断 来处理 print(),这意味着如果你滥用这个指令,那么你可能会错过 一些脉冲 或者出现其他奇怪的行为。考虑到你的脉冲高频率,你肯定不想在每个脉冲后都使用 print()。也许 每 100/1000 个脉冲 可能是一个更好的主意。 - Patrick Trentin
你的代码运行正常。我以前没有使用过ISR,我应该多读一些相关资料,感谢你的建议。 - Mehran

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接