我一直在寻找这个问题的答案,但却找不到任何东西。当我的ISR被触发时,它会通过并完成它应该完全正常的所有事情,然后在退出并返回主循环之前,ISR再次执行。一旦它第二次消失,它就会返回主循环。这只发生在我使用115V继电器来操作中断时。
我试图检测何时停电或电源重新启动。我正在使用引脚更改中断来检测继电器是闭合还是打开。当电源熄灭时,继电器将打开并触发ISR。如果我将此设置连接到正常的按钮或开关,一切都按照需要工作,没有问题,只有当它连接到继电器时才会出现问题。
以下是我的代码:(我知道我不需要cli我只是在尝试一切)
ISR(PCINT2_vect){
cli();
sbi(PORTC,5);
_delay_ms(6000);
cbi(PORTC,5);
for(delay_counter=0;delay_counter<2;delay_counter++)
{
_delay_ms(6000);
}
sbi(PORTC,5);
_delay_ms(6000);
if(bit_is_set(PIND,2))
{
lcd_clrscr();
lcd_puts("Sending SMS");
usart_print("at");
USART_Transmit('\r');
_delay_ms(6000);
for(i=0;i<=1;i++)
{
usart_print("at*smsm2m=");
USART_Transmit('"');
for(j=0;j<11;j++)
{
USART_Transmit(Alert_Numbers[i][j]);
}
usart_print(" Power has been lost");
USART_Transmit('"');
USART_Transmit('\r');
_delay_ms(6000);
}
lcd_clrscr();
lcd_puts("SMS Sent");
_delay_ms(6000);
lcd_clrscr();
lcd_puts("Status:NO POWER");
cbi(PORTC,5);
}
else if(bit_is_clear(PIND,2))
{
lcd_clrscr();
lcd_puts("System Reset");
_delay_ms(6000);
_delay_ms(6000);
usart_print("at");
USART_Transmit('\r');
_delay_ms(6000);
for(i=0;i<=1;i++)
{
usart_print("at*smsm2m=");
USART_Transmit('"');
for(j=0;j<11;j++)
{
USART_Transmit(Alert_Numbers[i][j]);
}
usart_print(" Pump regained power");
USART_Transmit('"');
USART_Transmit('\r');
_delay_ms(6000);
}
lcd_clrscr();
lcd_puts("POWER ON");
_delay_ms(6000);
lcd_clrscr();
lcd_puts("Status: Good");
}
else
{
}
}
int main(void)
{ /*Initializations*/
DDRC = 0x20; // PORTC,5 is now output
sbi(PORTC,5);
USART_Init(51);
lcd_init(LCD_DISP_ON);
lcd_clrscr();
/*Set interrupts*/
DDRD = 0b11111011; // set PD2 to input
PORTD = 0b00000100; // set PD2 to high
PCICR |= (1 << PCIE0);
PCMSK0 |= (1 << PCINT0);
PCICR |= (1<<PCIE2);
PCMSK2 |= (1<<PCINT18);
sei();
lcd_clrscr();
lcd_puts("Status: Good");
/*Main Program Loop: NOP*/
while(1)
{
lcd_clrscr();
lcd_puts("MAIN LOOP");
for(delay_counter=0;delay_counter<3;delay_counter++)
{
_delay_ms(6000);
}
}
}
答案 0 :(得分:1)
我自己遇到了问题,所以我决定回答它,即使它是一个旧条目:
中断触发两次的原因是中断标志未复位。这是一些atmega类型的问题。您可以通过一个简单的方法解决问题:
在ISR中断函数的最后发布:
PCIFR |= (1<<PCIF2);
这将在PCINT2中断的中断标志位中写入“1”。如果使用其他中断,则必须将其他标志设置为1.请注意,中断标志是反转的。因此1禁用中断,而0触发中断。
查看数据表: http://www.atmel.com/images/doc2545.pdf 见第13.2.5点:
Bit 2 - PCIF2:引脚更改中断标志2 - 当任何PCINT23..16引脚上的逻辑更改触发中断请求时,PCIF2置1 (一)。如果是I位 SREG和PCICR中的PCIE2位是 设置(一),MCU将跳转到 相应的中断向量。标志被清除 当执行中断例程时。 或者,可以通过向其写入逻辑标志来清除该标志。
我希望这对你和其他有同样问题的人有所帮助。只需在数据表中搜索寄存器名称和中断标志对应的位,并将其设置为1。
再见
答案 1 :(得分:0)
我正在使用引脚更改中断来检测继电器是否关闭或 开。
不要那样做。认真。不要试图将机械开关连接到中断引脚以触发ISR。
如果您坚持这样做,请至少确保交换机的信号在硬件中正好去抖,然后才会到达μC的输入引脚
此外,任何类型的等待(_delay_ms(6000);
)都不是人们想要在ISR中拥有的。
答案 2 :(得分:0)
您的代码失败的原因:当信号弹跳时,即使您在中断中等待6秒,也会再次设置该标志(当您仍在ISR中时)。快速而肮脏的解决方案是在退出ISR之前重置标志。
两个(在我看来)更好的解决方案:
如果您想在软件中执行此操作,只需在ISR例程中设置一个指示事件的变量。然后,在你的主循环(或函数)中,对debis进行去抖(10到100ms就足够了)。
如果您想在硬件中执行此操作,只需将一个RC组合作为滤波器添加到引脚(c接地和r到继电器。只需尝试一些组合,如100k和10μF。
我的个人推荐: 做两件事;)
注解: - 在中断程序中使用6秒延迟实际上是一种不好的做法。更好:使用一个称为10 ms的定时器,如果标志置位且继电器指示掉电,则增加一个计数器。如果继电器有电,则将标志和计数器复位为0.如果计数器达到600,则表示停电时间超过6秒。
玩得开心!