为什么main()函数只有在中断ISR(ADC_vect)执行时才被反复执行?

问题描述 投票:0回答:2

我正在制作一个基于数字时钟的项目,在面包板上使用

atmega328p
。我将 4 个按钮连接到
ADC3
通道,并使用
INT0
作为触发器来启动 ADC 转换。我还启用了
TimerCounter1
来计算时间。

我在使用增加按钮增加时间时遇到问题,因为

setTime(3, 30)
函数调用位于
int main()
函数中,按下按钮后,该函数
setTime(3, 30)
会一次又一次地执行。仅当执行
ISR(ADC_vect)
时才会发生这种情况。

这是我认为相关的代码部分:

xxx.ino

#include <avr/io.h>

int main()
{
  Serial.begin(9600);
  
  // PB5
  DDRB |= (1<<DDB5);

  // ### Want to see how many times these functions - __init__timer(), setTime(), __init__button() gets executed after the Interrupt ###
  PORTB ^= (1<<PORTB5);

  __init__timer();
  
  //setTime(3, 30);

  __init__button();

  while(1)
  { 

  }

  return 0;
}

void __init__timer()
{ 
  // Enable the global Interrupt
  SREG |= (1<<7);
  // Enable the output compare match on comapare A 
  TIMSK1 |= (1<<OCIE1A);

  // Set TimerCounter1 in CTC mode
  TCCR1A &= ~(1<<WGM10);
  TCCR1A &= ~(1<<WGM11);
  TCCR1B |= (1<<WGM12);
  TCCR1B &= ~(1<<WGM13);

  // Set the prescalr to 1024 so we get 15625 Hz pulses(counts) per second
  TCCR1B |= (1<<CS10);
  TCCR1B &= ~(1<<CS11);
  TCCR1B |= (1<<CS12);

  // Assign the OCR1A to 46875 (3 seconds)
  OCR1A = 46875;
}

ISR(TIMER1_COMPA_vect)
{ 
  Serial.println("ISR(TIMER1_COMPA_vect) executed. -> PB5 Not toggled");
}

void __init__button()
{
  // Select the voltage referance - AREF pin
  ADMUX &= ~(1<<REFS1);
  ADMUX &= ~(1<<REFS0);

  // Select the input channel - ADC3
  ADMUX |= (1<<MUX0);
  ADMUX |= (1<<MUX1);
  ADMUX &= ~(1<<MUX2);
  ADMUX &= ~(1<<MUX3);

  // Configure the ADCSRA registor

  SREG |= (1<<7);
  ADCSRA |= (1<<ADIE);

  // Set the prescalar to 128 - 125KHz
  ADCSRA |= (1<<ADPS0);
  ADCSRA |= (1<<ADPS1);
  ADCSRA |= (1<<ADPS2);

  // Enable the INT0 
  EIMSK |= (1<<INT0);
  EICRA |= (1<<ISC00);
  EICRA |= (1<<ISC01);

  ADCSRB &= ~(1<<ADTS0);
  ADCSRB |= (1<<ADTS1);
  ADCSRB &= ~(1<<ADTS2);
  ADCSRA |= (1<<ADATE);

  // This will start the conversion if trigger INT0 is pressed
  ADCSRA |= (1<<ADEN);
}

ISR(ADC_vect)
{
  if((ADC < 750) && (ADC > 600))
  {
    Serial.println("Button 1 -> PB5 toggled");
  }
  if((ADC < 500) && (ADC > 350))
  {
    Serial.println("Increase time button pressed... -> PB5 toggled");
    // increaseTime();
  }
  if((ADC < 300) && (ADC > 200))
  {
    Serial.println("Decrease button pressed... -> PB5 toggled");
    // decreaseTime();
  }
  if((ADC < 200) && (ADC > 100))
  {
    Serial.println("button 4 -> PB5 toggled");
  }
}

这是串行输出:

ISR(TIMER1_COMPA_vect) executed. -> PB5 Not toggled
ISR(TIMER1_COMPA_vect) executed. -> PB5 Not toggled
ISR(TIMER1_COMPA_vect) executed. -> PB5 Not toggled
ISR(TIMER1_COMPA_vect) executed. -> PB5 Not toggled
ISR(TIMER1_COMPA_vect) executed. -> PB5 Not toggled
ISR(TIMER1_COMPA_vect) executed. -> PB5 Not toggled
ISR(TIMER1_COMPA_vect) executed. -> PB5 Not toggled
Increase time button pressed... -> PB5 toggled
ISR(TIMER1_COMPA_vect) executed. -> PB5 Not toggled
ISR(TIMER1_COMPA_vect) executed. -> PB5 Not toggled
Increase time button pressed... -> PB5 toggled
ISR(TIMER1_COMPA_vect) executed. -> PB5 Not toggled
Increase time button pressed... -> PB5 toggled
I⸮Increase time button pressed... -> PB5 toggled
ISR(TIMER1_COMPA_vect) executed. -> PB5 Not toggled
Increase time button pressed... -> PB5 toggled
ISR(TIMER1_COMPA_vect) executed. -> PB5 Not toggled
ISR(TIMER1_COMPA_vect) executed. -> PB5 Not toggled
ISR(TIMER1_COMPA_vect) executed. -> PB5 Not toggled
ISR(TIMER1_COMPA_vect) executed. -> PB5 Not toggled

我的问题:

为什么当

int main()
执行时,
ISR(ADC_vect)
函数中的指令会被执行一遍又一遍,而当
ISR(TIMER1_COMPA_vect)
执行时却不执行?仅当执行
PB5
时,
ISR(ADC_vect)
引脚才会切换。

arduino interrupt avr arduino-uno avr-gcc
2个回答
1
投票

任何函数中的代码都会从函数开始到结束按顺序运行一次。就你的

main()
函数而言,它会达到无限循环,并永远停留在那里。

中断(以及附加到其调用向量的函数)是不按顺序调用的“特殊”代码段。也就是说,它们会在需要运行时(例如发生定时器比较时)中断正常的程序流程,然后将 CPU 返回到原来的位置。

在您的情况下,无限循环点的指令流最终会产生以下效果:

loop:
  nop       ; do absolutely nothing
  rjmp loop   ;go back to the "loop" label

发生中断时,执行路径基本上如下所示:

loop:
  rjmp interrupt ;to the interrupt
  (... interrupt here ...)
  return here    ; back from interrupt
  nop       ; do absolutely nothing
  rjmp loop   ;go back to the "loop" label

0
投票

最后我发现了问题所在 - 为什么

main()
函数只有在
ISR(ADC_vect)
中断发生时才再次执行。

就像上面

djph
的回答所说,
main()
函数只执行一次,然后在
while(1);
中永远循环,当发生中断时,程序暂停当前任务并从循环
while(1);
跳转到相应的中断例程 -
ISR()
然后程序跳回循环 -
while(1)
,意味着在
main()
中断执行后不会再次执行
ISR()
函数。

因此,就我而言,仅当 ISR(ADC_vect) 中断发生时,

main()
函数才会再次执行。为什么?

答案:

如果我们看看我如何初始化 ADC ->

void __init__button()
功能:

// Select the voltage referance - AREF pin
  ADMUX &= ~(1<<REFS1);
  ADMUX &= ~(1<<REFS0);

  // Select the input channel - ADC3
  ADMUX |= (1<<MUX0);
  ADMUX |= (1<<MUX1);
  ADMUX &= ~(1<<MUX2);
  ADMUX &= ~(1<<MUX3);

  // Configure the ADCSRA registor

  SREG |= (1<<7);
  ADCSRA |= (1<<ADIE);

  // Set the prescalar to 128 - 125KHz
  ADCSRA |= (1<<ADPS0);
  ADCSRA |= (1<<ADPS1);
  ADCSRA |= (1<<ADPS2);

  // Enable the INT0 
  EIMSK |= (1<<INT0);
  EICRA |= (1<<ISC00);
  EICRA |= (1<<ISC01);

  ADCSRB &= ~(1<<ADTS0);
  ADCSRB |= (1<<ADTS1);
  ADCSRB &= ~(1<<ADTS2);

  // 
  ADCSRA |= (1<<ADATE);

  // This will start the conversion if trigger INT0 is pressed
  ADCSRA |= (1<<ADEN);

ADC 自动触发启用位,ADC 将在所选触发信号的上升沿开始转换 - 即

INT0

ADCSRA |= (1<<ADATE); 
  // Enable the INT0 
  EIMSK |= (1<<INT0);
  EICRA |= (1<<ISC00);
  EICRA |= (1<<ISC01);

所以这意味着如果我按下按钮,不仅会发生

ADC
中断,还会发生
INT0
中断,但是在我的代码中没有
ISR(INT0_vect)
例程。
这就是问题...... 将
ISR(INT0_vect)
例程添加到我的代码中后,当
main()
中断发生时,
ISR(ADC_vect)
函数不会再次执行,

ISR(INT0_vect)
{ 
  // Do nothing
}

最后在我的数字时钟项目中,我现在可以在按下增加按钮时增加时间......谢谢。

可能有更好的方法来做到这一点,但这对我有用。

我不知道为什么如果发生特定中断而我们忘记将相应的例程 -

main()
添加到代码中,
ISR()
函数会再次执行。

编辑:

我想我现在知道为什么

main()
再次被处决 - 如果发生意外中断(启用中断且未安装处理程序,这通常表明存在错误),则默认操作是通过跳转到重置向量来重置设备。

ISR()
中断详细信息:www.nongnu.org/avr-libc/user-manual/group__avr__interrupts

这是处理空中断的更好方法 -

EMPTY_INTERRUPT(INT0_vect);
© www.soinside.com 2019 - 2024. All rights reserved.