Atmel:中断后无法转向主

Atmel : can't turn to the main after interruption

我用的是atmega328P,如图所示 当执行中断时,程序不会回到main执行程序的其余部分? 我做了 2 个功能;一个在端口 C 中闪烁,另一个在端口 D 中闪烁 端口 D(中断)中的 Led 工作正常,但主端口 C 的 Led 未执行 有问题吗??

#ifndef F_CPU
#define F_CPU 16000000UL
#endif

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

#define ACK 0x01 

void portc_led(void)
{
    PORTC|=(1<<5);
    _delay_ms(100);
    PORTC=(0<<5) ;
    _delay_ms(100);
    PORTC|=(1<<5);
    _delay_ms(100);
    PORTC=(0<<5) ;
    _delay_ms(100);
}

void portd_led(void)
{
    PORTD|=(1<<7);
    _delay_ms(1000);
    PORTD=(0<<7) ;
    _delay_ms(100);
    PORTD|=(1<<7);
    _delay_ms(1000);
    PORTD=(0<<7) ;
    _delay_ms(100);
    PORTD|=(1<<7);
    _delay_ms(1000);
    PORTD=(0<<7) ;
    _delay_ms(100);
    PORTD|=(1<<7);
    _delay_ms(1000);
    PORTD=(0<<7) ;
    _delay_ms(100);
}


int main(void)
{

    DDRB |= (1<<2)|(1<<3)|(1<<5);    // SCK, MOSI and SS as outputs
    DDRB &= ~(1<<4);                 // MISO as input

    SPCR |= (1<<MSTR);               // Set as Master
    SPCR |= (1<<SPR0)|(1<<SPR1);     // divided clock by 128
    SPCR |= (1<<SPIE);               // Enable SPI Interrupt
    SPCR |= (1<<SPE);                // Enable SPI


    DDRC= 0xFF ; // set PORT C as output 
    DDRD = 0xFF ;
    sei();

    spi_send_data(ACK);

    portc_led();

}



ISR(SPI_STC_vect)
{
    portd_led();

}

您的代码中有两个概念性错误:

  1. 只要中断服务程序是运行ning,main函数就可以运行.

  2. portc_led() 之后,main() 函数 returns。根据编译器系统(可能是某些 GCC)的 运行time 环境,它最终 运行 陷入无限循环,什么都不做。只有中断不断触发。

首先,您的代码将出现 编译错误! 因为您没有提供对 spi_send_data 函数的引用

但让我们假设您将它包含在这段代码之上

分析你的代码

你这么说

interruption is executed , the program doesn't turn back to the main

程序执行路径肯定会回到主程序...它会去哪里?! :) 代码将执行 portc_led 函数一次( 且仅执行一次 )可能在中断之前或可能在中断之后或可能在函数之间发生中断...... 所以也许 portc_led 已经在中断之前执行了,但你没有看到它,因为它只在 400 ms 中执行!在完成中断之后无事可做,只需等待其他中断! ..

简单的解决方案: 尝试将 portc_led 中的 100ms 延迟更改为更大的延迟,您会看到 ...

提高练习技巧的小建议

  1. 看看这段代码 PORTB=(0<<5) 相当于 portB=0b00000000 当您尝试清除寄存器中的单个位时,您会清除所有寄存器的位!这不好...使用此代码 PORTB&=~(1<<5) 清除单个位,使 portc 和 0b11101111 之间的比特位和仅更改单个位并保持其他位不变
  2. 始终在中断例程中使其尽可能小...只需提高标志并在主循环中处理...read more way you should make it small routine
  3. 你的程序没有主循环!! (有时称为超级循环).. 这个循环只是一个无限循环,在你的系统初始化和 运行 一遍又一遍之后出现......一些编译器在主例程的末尾添加空的无限循环而其他编译器不添加......这是一个很好的做法即使您不使用它,也要在主例程中有一个主循环!让您的程序保持活力

修改代码

不是下面的代码不会并行(同时)执行消隐,它会串联执行(不是同时)..如果你喜欢并行消隐使用portc_led 中的定时器中断代替延迟/或使用 RTOS(有点高级的主题)


#ifndef F_CPU
#define F_CPU 16000000UL
#endif

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

#define ACK 0x01

volatile char spi_interupt_flag = 0;


void portd_led(void)
{
    // this will blank led in pd7 for 3 sec
    for(char i=0;i<6;i++){  
        PORTD^=(1<<PD7) ; // toggle pd7
        _delay_ms(500); // you will see 500 ms blank
    }
}


int main(void)
{

    DDRB |= (1<<2)|(1<<3)|(1<<5);    // SCK, MOSI and SS as outputs
    DDRB &= ~(1<<4);                 // MISO as input

    SPCR |= (1<<MSTR);               // Set as Master
    SPCR |= (1<<SPR0)|(1<<SPR1);     // divided clock by 128
    SPCR |= (1<<SPIE);               // Enable SPI Interrupt
    SPCR |= (1<<SPE);                // Enable SPI


    DDRC= 0xFF ; // set PORT C as output
    DDRD = 0xFF ;
    sei();

    spi_send_data(ACK); // this code will make compile error if you not provide a source for implementation

    
    
    while(1){
        if (spi_interupt_flag)
        {
            //this code only execute when data received from SPI
            portd_led(); //do whatever you want to do for handle it
            spi_interupt_flag = 0; //reset the flag again
        }
        PORTC^=(1<<PC5); //toggle pc5 for ever
        _delay_ms(1000); // pc5 will toggle every 1 sec unless ther is interupt 
    }

}



ISR(SPI_STC_vect)
{
    // just set a flag for handle interrupt in main     
    spi_interupt_flag = 1;
}