Атмель: не может переключиться на основную после прерывания - PullRequest
0 голосов
/ 19 июня 2020

Я использую atmega328P, как показано на прикрепленном изображении, когда выполняется прерывание, программа не возвращается к основному, чтобы выполнить остальную часть программы? я сделал 2 функции; один мигающий светодиод в порту C, а другой в ПОРТУ D Светодиод в ПОРТУ D (прерывание) работает нормально, но светодиод для ПОРТА C в основном не работает, есть проблема?

#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();

}

Ответы [ 3 ]

0 голосов
/ 21 июня 2020

прежде всего ваш код будет иметь ошибку компиляции ! потому что вы не предоставляете ссылку на spi_send_data функцию

, но давайте представим, что вы включили ее над этой частью кода

проанализируйте свой код

вы говорите, что

выполняется прерывание, программа не возвращается к основному

путь выполнения программы обязательно обратится к основной подпрограмме ... где она go?! :) код выполнит функцию portc_led один раз ( и только один раз ), может быть, до прерывания или, может быть, после прерывания, или, может быть, прерывание произойдет между функцией ... так что, возможно, portc_led alredy выполнено происходит до прерывания, но вы не видели его, потому что он был выполнен только за 400 ms !! и после фин sh прерывания нечего делать просто ждать другого прерывания! ..

простое решение: попробуйте изменить 100ms задержку в portc_led на большую задержку, и вы увидите ...

маленькие советы для улучшения практики методы

  1. посмотрите на этот код PORTB=(0<<5) эквивалент portB=0b00000000 когда вы пытаетесь очистить один бит в регистре, вы очищаете все биты регистра! что нехорошо ... используйте этот код PORTB&=~(1<<5) для очистки одного бита, который заставляет bitwaise & между портом c и 0b11101111 изменять только один бит и сохранять другие биты, как
  2. всегда в подпрограмме прерывания make он настолько мал, насколько вы можете ... просто поднимите флаги и обработайте его в основном l oop ... прочитайте больше, как вы должны сделать это небольшой рутиной
  3. ваша программа доза не имеет миан л oop !! (иногда его называют super l oop) .. это l oop - это просто бесконечность l oop, которая появляется после инициализации вашей системы и запускается снова и снова ... какой-то компилятор добавляет пустую бесконечность l oop в конце основной подпрограммы и другой порции компилятора не добавляйте ... это хорошая практика иметь main l oop в основной подпрограмме, даже если вы ее не используете! Чтобы ваша программа оставалась в живых

измените код

не то, что следующий код не будет запускать гашение параллельно (одновременно), он будет запускать их последовательно ( не одновременно) .. если вы хотите иметь параллельное гашение, используйте прерывание таймера в portc_led вместо задержки / или используйте RTOS (немного продвинутый topi c)


#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;
}


0 голосов
/ 22 июня 2020

Спасибо @ibram Reda за ваши комментарии: 1 / для функции spi_send_data () я забыл включить свой файл spi.h :) Я изменил свой код и сделал 2 файла: один для ведущего, а другой для ведомого. Сделал пу sh -кнопку, при ее нажатии будет отправлена ​​команда на включение светодиода ведомого. Я поделюсь с вами:)

Мастер. c

/*
 * SPI_master.c
 *
 * Created: 6/17/2020 9:11:39 PM
 * Author : ajbeli
 */ 
#ifndef F_CPU
#define F_CPU 16000000UL
#endif

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

#define ACK 0x01 

uint8_t data = 0x01 ;

int main (void)
{
    DDRD &= ~(1<<6) ; // 6 input 
    
    PCICR = (1<<PCIE2); // set interruption for PORT D 
    PCMSK2 = (1<<PCINT22); // set Pin 6 
    
    //SPI settings
    
    spi_init_master() ;
    
    spi_select_clock_rate(spi_clock_rate_f128) ;
    
    spi_enable_int() ;
    
    spi_enable();
     
    spi_set_clock_polarity(spi_data_order_lsb_first);

    sei();

    while(1)
    {
    
    }
 }
 
 ISR(PCINT2_vect)
 {
     if(bit_is_set(PIND, PIND6))
     {
         SPDR = 0x01 ;
         while(!(SPSR & (1<< SPIF))) ;

     }
    
 }

Salve. c

/*
 * SPI_Slave.c
 *
 * Created: 6/17/2020 9:44:53 PM
 * Author : ajbeli
 */ 

#ifndef F_CPU
#define F_CPU 16000000UL
#endif

#include <avr/io.h>
#include <spi.h>
#include <util/delay.h>
#include <avr/interrupt.h>
volatile uint8_t data ;


int main(void)
{

    spi_init_slave() ; 

    spi_select_clock_rate(spi_clock_rate_f128);

    spi_enable_int();

    spi_enable();
    
    spi_set_clock_polarity(spi_data_order_lsb_first);
    
    DDRD = (1<<DDD7) ;
    
    sei();
    while(1)
    {

    }

}

ISR (SPI_STC_vect)
{
    data = SPDR;
    if(data == 0x01)
    {
        PORTD^=(1<<PORTD7);
    }
}

Для раба. c даже если я поставил if (data == 0x01), он отлично работает :)

0 голосов
/ 19 июня 2020

В вашем коде есть две концептуальные ошибки:

  1. Пока выполняется подпрограмма обслуживания прерывания, основная функция не может работать.

  2. После portc_led() возвращается функция main(). В зависимости от среды выполнения вашей системы компиляции (предположительно, некоторого G CC) он, наконец, работает в бесконечном l oop, ничего не делая. Только прерывания продолжают работать.

...