在 AVR 中处理多个 PCINT 的最佳方法
Posted
技术标签:
【中文标题】在 AVR 中处理多个 PCINT 的最佳方法【英文标题】:Best way to handle multiple PCINT in AVR 【发布时间】:2021-12-31 02:18:48 【问题描述】:我正在 Attiny85 上测试一些东西,并考虑了处理中断规则的最佳方法。我知道在中断处理程序中有很多代码是不好的,但我不确定是否有任何其他方法可以做到这一点。我希望我的主程序在 PCINT 上休眠和唤醒,PCINT 来自多个引脚(旋转编码器 A、b 和开关和接收 UART),所以我想在处理程序中有很多代码。
确定哪个引脚导致中断的代码如下所示
#include <avr/io.h>
#include <stdint.h> // has to be added to use uint8_t
#include <avr/interrupt.h> // Needed to use interrupts
volatile uint8_t portbhistory = 0xFF; // default is high because the pull-up
int main(void)
DDRB &= ~((1 << DDB0) | (1 << DDB1) | (1 << DDB2)); // Clear the PB0, PB1, PB2 pin
// PB0,PB1,PB2 (PCINT0, PCINT1, PCINT2 pin) are now inputs
PORTB |= ((1 << PORTB0) | (1 << PORTB1) | (1 << PORTB2)); // turn On the Pull-up
// PB0, PB1 and PB2 are now inputs with pull-up enabled
PCICR |= (1 << PCIE0); // set PCIE0 to enable PCMSK0 scan
PCMSK0 |= (1 << PCINT0); // set PCINT0 to trigger an interrupt on state change
sei(); // turn on interrupts
while(1)
/*main program loop here */
ISR (PCINT0_vect)
uint8_t changedbits;
changedbits = PINB ^ portbhistory;
portbhistory = PINB;
if(changedbits & (1 << PB0))
/* PCINT0 changed */
if(changedbits & (1 << PB1))
/* PCINT1 changed */
if(changedbits & (1 << PB2))
/* PCINT2 changed */
然后在中断处理程序中的每个 if 语句中,都会有代码处理一些事情,比如这段代码,打开 Timer0
TCNT0 = 0; // Set counter to 0
OCR0A = SERIAL_BIT_TIME; // Call timer interrupt in middle of first bit
position = 0; // Reset position and data
TIMSK |= 1 << OCIE0A; // Enable interrupt for compare register A (timer interrupt)
TIFR |= 1 << OCF0A; // Clear timer interrupt flag to prevent it jumping directly there
PCMSK &= ~(1 << SERIAL_RECEIVE); // Disable pin change interrupt
或者使用 switch 输入,if 语句中的代码将是
if (lightState)
dali.transmit(ADDRESS, OFF);
lightState = 0;
else
dali.transmit(ADDRESS, ON);
lightState = 1;
这会是一个愚蠢的解决方案吗?
【问题讨论】:
不要在 ISR 中传输任何内容。简单地在“正常”程序部分中查看标志并传输。我个人更喜欢使用 RTOS 来处理。 但是如果我设置了标志并让主程序检查标志,我还能让微控制器休眠并只在中断时唤醒吗? 你有主程序。它进入睡眠状态。中断到达,标志被设置,程序传输数据并返回睡眠。又一次,又一次,又一次....... 好的。也许我很笨,但我不确定主程序将如何以这种方式实现。你会在睡眠电话之后只使用“检查标志”代码吗? 【参考方案1】:volatile uint8_t flag;
int main(void)
DDRB &= ~((1 << DDB0) | (1 << DDB1) | (1 << DDB2)); // Clear the PB0, PB1, PB2 pin
// PB0,PB1,PB2 (PCINT0, PCINT1, PCINT2 pin) are now inputs
PORTB |= ((1 << PORTB0) | (1 << PORTB1) | (1 << PORTB2)); // turn On the Pull-up
// PB0, PB1 and PB2 are now inputs with pull-up enabled
PCICR |= (1 << PCIE0); // set PCIE0 to enable PCMSK0 scan
PCMSK0 |= (1 << PCINT0); // set PCINT0 to trigger an interrupt on state change
sei(); // turn on interrupts
while(1)
gotosleep();
do
switch(flag)
case 1:
dosomething1();
break;
case 2:
dosomething2();
break;
case 3:
dosomething3();
break;
cli();
flag = 0;
sei();
while(flag);
ISR (PCINT0_vect)
uint8_t changedbits;
changedbits = PINB ^ portbhistory;
portbhistory = PINB;
if(changedbits & (1 << PB0))
flag = 1;
if(changedbits & (1 << PB1))
flag = 2;
if(changedbits & (1 << PB2))
flag = 3;
【讨论】:
那么在其中一个中断发生后,它会重新进入休眠状态并等待新的中断? 在中断之后它将处理它必须处理的事情,如果没有任何事情要处理(想象下一个 iunterupt 在 sei 之后和 while 循环检查之前到达)它将进入睡眠状态。 哦,哇,好的,非常感谢 请记住,在 AVR 上,引脚更改可能会触发中断,但引脚会在 ISR 读取之前更改回来。此代码将忽略这些短引脚更改,这并不总是您想要的。从睡眠中唤醒会延长引脚可以变回的时间段,从而导致错过更改。以上是关于在 AVR 中处理多个 PCINT 的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章
在一个 UIViewController 中处理多个 UITableView 的最佳方法?