输入更改时可以自动调用功能吗?


21

目前,我的草图是每次绕主循环检查一次输入引脚。如果检测到更改,它将调用自定义函数以对此做出响应。这是代码(精简了要点):

int pinValue = LOW;

void pinChanged()
{
    //...
}

void setup()
{
    pinMode(2, INPUT);
}

void loop()
{
    // Read current input
    int newValue = digitalRead(2);

    // Has the input changed?
    if (newValue != pinValue) {
        pinValue = newValue;
        pinChanged();
    }
}

不幸的是,对于输入的非常短的更改(例如短暂的脉冲),这并不总是能正常工作,尤其loop()是在运行缓慢的情况下。

有没有办法让Arduino检测到输入变化并自动调用我的函数?


1
您正在寻找其外部中断的内容
Butzke 2014年

Answers:


26

您可以使用外部中断来执行此操作。不过,大多数Arduino仅在有限数量的引脚上支持此功能。有关完整的详细信息,请参见中的文档attachInterrupt()

假设您使用的是Uno,可以这样进行:

void pinChanged()
{
    //...
}

void setup()
{
    pinMode(2, INPUT);
    attachInterrupt(0, pinChanged, CHANGE);
}

void loop()
{
}

pinChanged()每当在外部中断0上检测到更改时,就会调用此方法。在Uno上,它对应于GPIO引脚2。其他板上的外部中断编号是不同的,因此检查相关文档很重要。

但是这种方法有局限性。定制pinChanged()功能被用作中断服务程序(ISR)。这意味着在loop()调用执行期间,其余代码()中的所有内容都会暂时停止。为了防止干扰任何重要的时间,您应该致力于使ISR尽可能快。

还需要注意的是,在ISR期间不会再有其他中断运行。这意味着任何依赖于中断的内容(例如内核delay()millis()函数)可能无法在其中正常运行。

最后,如果您的ISR需要更改草图中的所有全局变量,则通常应将其声明为volatile,例如:

volatile int someNumber;

这很重要,因为它告诉编译器该值可能会意外更改,因此应注意不要使用任何过时的副本/缓存。


关于问题中提到的“简短脉冲”,引脚是否必须在最短时间内处于触发中断的状态?(显然,它比轮询要少得多,这取决于循环中正在发生的事情)
sachleen 2014年

1
@sachleen只要在执行ISR功能期间不会发生(如答案中所述),它将起作用。这就是为什么pinChanged()应该尽可能短。因此,通常,最短时间应该是执行pinChanged()功能本身的时间。
jfpoilpret 2014年

2
+1非常详细的答案,其中包括使用中断时必须关心的所有重要内容!
jfpoilpret 2014年

3
除了声明共享全局变量外volatile,如果全局变量的宽度大于1个字节(如someNumber一样),还必须防止程序访问字节之间发生引脚更改中断。诸如此类的语句someNumber +=5;涉及添加低字节并添加带有进位的高字节。这两个(对于更广泛的变量,更多)不能被中断除。分别关闭中断并在操作之前和之后进行恢复就足够了。
JRobert

@sachleen-关于最小脉冲大小。很难在数据手册中找到确切的答案,但是从引脚更改中断的时序来看,它们在半个时钟周期内被锁存。一旦“记住”了中断,它就会被记住,直到ISR介入并处理它为止。
尼克·加蒙

5

配置为数字输入的任何引脚上的任何更改状态都可能产生中断。与INT1或INT2引起的唯一中断向量不同,PinChangeInt功能使用一个公共向量,然后该向量的中断服务程序(又称为ISR)需要确定哪个引脚发生了变化。

幸运的是,PinChangeInt库使此操作变得容易。

PCintPort::attachInterrupt(PIN, burpcount,RISING); // attach a PinChange Interrupt to our pin on the rising edge
// (RISING, FALLING and CHANGE all work with this library)
// and execute the function burpcount when that pin changes

0

如果您想检测超过阈值的电压,而不仅仅是高电平或低电平,则可以使用模拟比较器。草图示例:

volatile boolean triggered;

ISR (ANALOG_COMP_vect)
  {
  triggered = true;
  }

void setup ()
  {
  Serial.begin (115200);
  Serial.println ("Started.");
  ADCSRB = 0;           // (Disable) ACME: Analog Comparator Multiplexer Enable
  ACSR =  bit (ACI)     // (Clear) Analog Comparator Interrupt Flag
        | bit (ACIE)    // Analog Comparator Interrupt Enable
        | bit (ACIS1);  // ACIS1, ACIS0: Analog Comparator Interrupt Mode Select (trigger on falling edge)
   }  // end of setup

void loop ()
  {
  if (triggered)
    {
    Serial.println ("Triggered!"); 
    triggered = false;
    }

  }  // end of loop

这对于诸如光检测器之类的东西很有用,在这种情况下,您可能需要检测输入上从1V到2V的变化。

电路示例:

在此处输入图片说明

您还可以使用处理器上的输入捕捉单元,通过保存定时器1的当前计数来记住某些输入的确切时间。这可以让您存储发生以下事件的确切时刻(几乎准确)。引起了人们的兴趣,而不是引入可以使用ISR捕获当前时间之前的延迟(可能是几微秒)。

对于对时间要求严格的应用,这可以提高精度。

示例应用程序:将Arduino变成电容器测试仪

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.