Ta strona używa ciasteczek (plików cookies), dzięki którym może działać lepiej. Dowiedz się więcejRozumiem i akceptuję

Komunikacja SPI - USI. Przerwanie SPI po wysłaniu danych

Autor Wiadomość
NightTrain 



Posty: 5
Wysłany: 13-02-2018, 19:14   Komunikacja SPI - USI. Przerwanie SPI po wysłaniu danych

Dzień dobry,

Mam zamiar zrealizować komunikację radiową pomiędzy dwoma mikrokontrolerami z wykorzystaniem modułu nRF24L01+. Do tego celu muszę opanować komunikację SPI.
Zrobiłem prosty układ z wykorzystaniem ATmega644 oraz ATtiny2313A. ATmega jest układem MASTER, który wysyła cyklicznie jeden znak po magistrali SPI do urządzenia SLAVE (ATtiny2313), który go odbiera u siebie interfejsem USI, a następnie wyświetla na LCD. W momencie kiedy MASTER wysyła dane do SLAVE, SLAVE wysyła do MASTERA również jeden znak. Po odebraniu znaku MASTER zmienia stan diody LED.
Cały problem w tym, że nie wykonuje się przerwanie występujące po pomyślnym wysłaniu danych przez MASTER. Komunikacja działa poprawnie, bo diody LED obrazujące nadanie i odebranie danych przez poszczególne układy działają zgodnie z oczekiwaniami.

Podczas ustawiania bitów odpowiedzialnych za SPI w układzie MASTER, ustawiłem bit odpowiedzialny za przerwanie. W ATmega644 jest to bit SPIE w rejestrze SPCR.
Nie stosowałem natomiast polecenia SEI() dlatego, że po umieszczeniu go w kodzie, program w ogóle nie rusza. Zawiesza się.
W SLAVE również nie używam polecenia SEI(), a mimo to przerwanie od przepełnienia licznika interfejsu USI wykonuje się poprawnie.

Tworząc kod opierałem się na przykładach z książki T. Francuz (Język C dla mikrokontrolerów AVR) oraz korzystałem z kursu C publikowanego na łamach czasopisma EdW.

Podsumowując: Jeśli przerwanie od SPI w układzie MASTER, nie wykonuje się dlatego, że brakuje komendy SEI() to dlaczego dodanie komendy SEI() powoduje zawieszenie się AVRa?
Z góry dziękuję za pomoc.

Kod programu: Zaznacz cały
/*
 * Master.c

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

void SPI_master_init()
{
    DDRB|= _BV(PB4) |_BV(PB5) | _BV(PB7);    //Piny MOSI, SCK jako wyjście
    SPCR|=_BV(SPE) | _BV(MSTR) |_BV(SPR0) |_BV(SPIE); //SPI Enable, Tryb master, CLK/16, Interrupt Enable
    SPSR|=_BV(SPIF);//Interrupt Flag - Transmission Complete
    SPDR; //Skasuj flagę SPIF
        sei();//Bez tego komunikacja działa.
}

uint8_t SPI_send_rec_byte(uint8_t byte)
{
    SPDR=byte;
    while(!(SPSR & _BV(SPIF)));
    return SPDR;
}

volatile uint8_t liczba = 0;
   
int main()
{
    SPI_master_init();
    DDRB|=_BV(PB2) |_BV(PB3); // PB2 i PB3 jako wyjście - LED
    PORTB|=_BV(PB2); //Wyłączony LED
    PORTB|=_BV(PB3);
   
    char znak;

    while(1)
    {
        znak = (char)SPI_send_rec_byte('D'); //Wyślij do SLAVE znak 'D', to co odbierzesz wpisz do zmiennej 'znak'.
   
        if (znak == 'B')//Jesli odebrano 'B' mrugnij LEDem.
        {
            PORTB&=~_BV(PB2);
            _delay_ms(30);
            PORTB|=_BV(PB2);
            _delay_ms(30);
        }
       
       
        _delay_ms(1000);
    }
}

ISR(SPI_STC_vect)//Każde wystąpienie przerwania SPI (kazde zakonczenie nadawania) zmienia stan diody LED. TO SIE NIE WYWOŁUJE.
{
    if(liczba%2)
    {
        PORTB&=~_BV(PB2);
    }
    else
    {
        PORTB|=_BV(PB2);
    }
    liczba++;
    if(liczba == 9) liczba = 0;
}

Warto przeczytać » Kurs Intel Edison - #4 - podstawy Linuxa, praca w konsoli


Ostatnio zmieniony przez NightTrain 13-02-2018, 19:18, w całości zmieniany 2 razy  
Postaw piwo autorowi tego posta
 
 
marek1707 



Posty: 4928
Pomógł: 513 razy
Otrzymał 672 piw(a)
Skąd: WAW
Programuję w:
C, asm
Wysłany: 13-02-2018, 19:31   

Nie możesz jednocześnie używać funkcji ISR i robić aktywnego oczekiwania na bit SPIF. Zakończenie transmisji ustawia ten bit, ale to powoduje wywołanie (gdy zrobiłeś sei()) obsługi, co z automatu kasuje SPIF i program główny już go nie zobaczy. Następuje klincz. Musisz się zdecydować: albo obsługuj całą transmisję w ISR i tam też odczytuj dane, machaj diodkami itp, albo rób wszystko na poolingu i po odebraniu bajtu analizuj go i mrugaj do woli.
Podsumowując: zarówno ISR jak i odczyt rejestru SPCR a potem odczyt SPDR powodują skasowanie SPIF. Koniecznie przeczytaj uważnie opis tych bitów w datasheet procesora, rozdział o SPI.

Postaw piwo autorowi tego posta
 
 
Więcej szczegółów
Wystawiono 1 piw(a):
NightTrain
NightTrain 



Posty: 5
Wysłany: 14-02-2018, 14:06   

Dziękuję za odpowiedź.
Faktycznie, wystarczyło ponowne przeczytanie dokumentacji, a właściwie jednego zdania: " writing a byte to the SPI Data Register starts the SPI clock generator, and the hardware shifts the eight bits into the Slave". Czyli wystarczy wpisać dane do SPDR, a potem oczekiwać albo na ISR od SPI, które jest wywoływane bitem SPIF albo czekać aż warunek pustej pętli WHILE przestanie być spełniany. Teraz rozumiem.

Dodałem polecenie SEI(), a funkcję wysyłającą bajt po SPI zmieniłem do postaci:
Kod programu: Zaznacz cały
uint8_t SPI_send_rec_byte(uint8_t byte)
{
    SPDR=byte;
}


W przerwaniu sprawdzam to co przesłał SLAVE.
Dziękuję za pomoc.

Ostatnio zmieniony przez NightTrain 14-02-2018, 14:07, w całości zmieniany 1 raz  
Postaw piwo autorowi tego posta
 
 
marek1707 



Posty: 4928
Pomógł: 513 razy
Otrzymał 672 piw(a)
Skąd: WAW
Programuję w:
C, asm
Wysłany: 14-02-2018, 14:22   

Ale jeśli teraz tak wygląda funkcja startu SPI, to program który ją wywołuje może bezkarnie robić to o wiele za często. Przy jednym bajcie (i sztucznym opóźnieniu) Twój pomysł zadziała, ale w przypadku dłuższych ramek takich jak do modułu radiowego - już nie. Gdzie będzie synchronizacja procesora i SPI aby "podawać" kolejne bajty w takt ich wysyłania? SPI w AVR nie ma buforowania.

Ostatnio popularny » Kurs elektroniki - #10 - Podsumowanie, Quiz


Postaw piwo autorowi tego posta
 
 
NightTrain 



Posty: 5
Wysłany: 15-02-2018, 19:06   

No tak. Teraz działa bo wysyłam 1 bajt i czekam 100ms co w zupełności wystarcza do wysłania tego bajtu. Przy większych pakietach to się nie uda.
Jeśli chodzi o moduł nRF24L01 to w dużej mierze opieram się o projekt ze strony: http://gizmosnack.blogspo...01-and-avr.html .
Wykorzystuję kod tam zaprezentowany, jednak nie do końca on działa poprawnie. Funkcja, której zadaniem jest wysyłanie danych przez SPI wygląda tak:
Kod programu: Zaznacz cały
char Write_Byte_SPI(unsigned char cData)
{
    SPDR = cData;
    while(!(SPSR & (1<<SPIF)));
    return SPDR;
}


Pusta pętla WHILE kręci się do momentu ustawienia bitu SPIF, następnie zwraca to co odebrano.

Funkcja ta jest wywoływana do przesyłania komend do modułu nRF24L01. I faktycznie, jak się dzisiaj okazało, przy próbie wywołania funkcji RESET (odpowiedzialnej za skasowanie w module radiowym flag zakończenia nadawania/odebrania pakietu) program zawiesza się.
Funkcja RESET wygląda następująco:
Kod programu: Zaznacz cały

void reset(void)//Po każdym odebraniu/wysłaniu danych IRQ musi zostać skasowane w nRF. Robi to poniższa funkcja.
{
    _delay_us(10);
    CLEARBIT(PORTC, 6);//CSN w stan niski
    _delay_us(10);
    Write_Byte_SPI(W_REGISTER + STATUS);//Zapisz do rejestru STATUS.
    _delay_us(10);
    Write_Byte_SPI(0x70);//Skasuj wszystkie IRQ w rejestrze STATUS.
    _delay_us(10);
    SETBIT(PORTC, 6);//CSN w stan wysoki.
}


W_REGISTER i STATUS to nazwy dla rejestrów modułu radiowego. W_REGISTER to 0x20, a STATUS to 0x07.

Ustawienia konfiguracyjne rejestrów modułu nRFl24L01 odbywają się z wykorzystaniem funkcji Write_To_nRF. Przyjmuje ona 4 argumenty typu UINT8_T.
Kod programu: Zaznacz cały

uint8_t *Write_To_nRF(uint8_t readWrite, uint8_t reg, uint8_t *val, uint8_t antVal)

Przesłanie z wykorzystaniem funkcji Write_To_nRF działa poprawnie. W ciele funkcji mamy kilka wywołań funkcji Write_Byte_SPI do której przekazywana jest np. wartość 0xE1 - wtedy nic się nie zacina i działa poprawnie. Jak się domyślam dlatego, że argumenty funkcji Write_To_nRF są rzutowane na UINT8_T i dopiero potem przekazane do Write_Byte_SPI.
W przypadku wywołania funkcji Write_Byte_SPI(W_REGISTER + STATUS) mamy tak naprawdę Write_Byte_SPI(0x20 + 0x07). Nie ma tutaj rzutowania na UINT8_T i może dlatego program nie działa.

Przy próbie wywołania funkcji RESET() ze zmienionymi liniami:
Kod programu: Zaznacz cały
 
Write_Byte_SPI(W_REGISTER + STATUS);
Write_Byte_SPI(0x70);

na
Kod programu: Zaznacz cały

Write_Byte_SPI((uint8_t)(W_REGISTER + STATUS));
Write_Byte_SPI((uint8_t)(0x70));

program nie zawiesza się.

Czy dobrze to wszystko rozumuję ?

Ostatnio zmieniony przez NightTrain 16-02-2018, 09:49, w całości zmieniany 1 raz  
Postaw piwo autorowi tego posta
 
 
marek1707 



Posty: 4928
Pomógł: 513 razy
Otrzymał 672 piw(a)
Skąd: WAW
Programuję w:
C, asm
Wysłany: 15-02-2018, 19:59   

Nie rzutowanie nie ma tu nic do rzeczy. Kompilator dba od takie szczegóły podczas przekazywania parametrów do funkcji. I tak na każdy argument typu uint8_t lub uint16_t jest rezerwowana para rejestrów czyli 16 bitów.
Czy ten program/biblioteka zawiesza się w swojej pierwotnej wersji, czy coś podmieniałeś i wtedy przestaje działać. Nie zrozumiałem na jakiej wersji kodu pracujesz. Funkcja Write_Byte_SPI() wygląda poprawnie. Jej kilkukrotne wywołania, nawet z argumentami uint16_t nie powinny powodować problemów. Może program "zawiesza się" gdzieś indziej? Próbowałeś wstawiać jakieś kontrolne machanie liniami portów lub wypisywania komunikatów przez UART itp? Takie proste mechanizmy pomagają sprawdzić gdzie program jest i co robi nawet bez sprzętowego debugera. Nie rozumiem po co tam są te opóźnienia. Na jaką prędkość masz skonfigurowany SPI?

Postaw piwo autorowi tego posta
 
 
NightTrain 



Posty: 5
Wysłany: 16-02-2018, 10:15   

Wykorzystuję program ze zdjęć z linku, który podałem w poprzednim poście. Po prostu przepisałem program ze zdjęć. Autor co prawda udostępnia kod do skopiowania pod linkiem: https://gist.github.com/klalle/83ec2a1a691523f2829f jednak nie wykorzystałem go, gdyż różni się do tego, który opisuje na blogu. Jest dla mnie mniej zrozumiały.
Wspomniana przeze mnie funkcja RESET ma dokładnie taką postać jak wskazuje Autor bloga, czyli:
Kod programu: Zaznacz cały
void reset(void)//Po kazdym odebraniu/wyslaniu danych IRQ musi zostać skasowane w nRF. Robi to poniższa funkcja.
{
    _delay_us(10);
    CLEARBIT(PORTC, 6);//CSN w stan niski
    _delay_us(10);
    Write_Byte_SPI(W_REGISTER + STATUS);//Zapisz do rejestru STATUS.
    _delay_us(10);
    Write_Byte_SPI(0x70);//Skasuj wszystkie IRQ w rejestrze STATUS.
    _delay_us(10);
    SETBIT(PORTC, 6);//CSN w stan wysoki.
}
Wywołanie tej funkcji w głównej pętli WHILE powoduje zawieszenie się programu. Wystarczy usunąć dwie linie wywołujące funkcję Write_Byte_SPI żeby program się nie zawieszał. W pętli WHILE wywołuję funkcję RESET a po powrocie z niej zmieniam stan diody. Bez wywołań funkcji Write_Byte_SPI w funkcji RESET, dioda zmienia stan (wszystko działa), po dodaniu dwóch linii wywołujących Write_Byte_SPI program się zawiesza.
Prędkość SPI mam ustawioną na 1MHz.
Opóźnienia Autor bloga komentuje następująco:
Kod programu: Zaznacz cały

_delay_us(10); //Make sure last command was while ago


Może nie będzie to zbyt estetyczne, ale tak wygląda kod, który teraz generuje w/w problem.
Kod programu: Zaznacz cały

/*
 * Transmiter.c
 *
 * Created: 2018-02-09 13:03:58
 * Author : Piotr
 */

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

#define BIT(x) (1<<(x))
#define SETBITS(x,y) ((x)|=(y))
#define CLEARBITS(x,y) ((x)&=(~(y)))
#define SETBIT(x,y) SETBITS((x), (BIT((y))))
#define CLEARBIT(x,y) CLEARBITS((x), (BIT((y))))

#define W 1
#define R 0

uint8_t *data;


/* nRF24L01+ pinout:
CE - PC7
CSN - PC6
SCK - SCK (PB7)
MOSI - MOSI (PB5)
MISO - MISO (PB6)
IRQ - INT0 (PD2)
*/

void SPI_init(void)
{
    DDRC|= _BV(PC6)|_BV(PC7); //Pin CSN i CE jako wyjscie
    DDRB|=_BV(PB4) |_BV(PB5) |_BV(PB7); // Pin SS, MOSI pin SCK jako wyjście
    SPCR|=_BV(SPE) |_BV(MSTR) |_BV(SPR0) |_BV(SPIE); // SPI Enable, Tryb Master, CLK/16, Przerwanie
    //SPSR|=_BV(SPIF);// Flaga przerwania - transmisja zakonczona
    SPDR;//Skasuj flagę SPIF
   
    SETBIT(PORTC, 6); //CSN High
    CLEARBIT(PORTC, 7); //CE Low
   
}

char Write_Byte_SPI(unsigned char cData)
{
    SPDR = cData; // Wpisz dane do rejestru
    while(!(SPSR & (1<<SPIF)));//Poczekaj na koniec transmisji
    return SPDR; //Zwróć to co odebrano. (Kiedy wysyła, to jednocześnie odbiera).
}

uint8_t *Write_To_nRF(uint8_t readWrite, uint8_t reg, uint8_t *val, uint8_t antVal)
{
    //readWrtie - W lub R, reg - rejestr, *val - tablica danych, antVal - liczba INTów w pakiecie.
    cli();
   
    if(readWrite == W) //W = 1, R = 0 (define na początku).
    {
        reg = W_REGISTER + reg; //Dodaj nowy bit do rejestru.
    }
   
    static uint8_t ret[32]; //Tablica, która zostanie zwrócona na końcu. "*" na początku funkcji jest potrzebna, żeby było możliwe zwrócenie tablicy.
   
    _delay_us(10);
    CLEARBIT(PORTC, 6);//CSN w stan niski
    _delay_us(10);
    Write_Byte_SPI(reg); //Ustaw nRF w stan odczytu albo zapisu. Zależnie przy było W czy R.
    _delay_us(10);
   
    int i;
    for(i = 0; i < antVal; i++)
    {
        if(readWrite == R && reg != W_TX_PAYLOAD)//Czy chcesz odczytać rejestr
        {//Zapisując do W_TX_PAYLOAD nie można dodać W dopóki on jest na tym samym poziomie w rejestrze (?)
            ret[i] = Write_Byte_SPI(NOP);//Wyślij bajt, żeby odczytać dane.
            _delay_us(10);
        }
        else
        {
            Write_Byte_SPI(val[i]);
            _delay_us(10);
        }
    }
    SETBIT(PORTC, 6); //CSN w stan wysoki
    sei();
   
    return ret; //Zwróc tablice.
}

void nRF24L01_init()
{
    _delay_ms(100);
   
    uint8_t val[5]; //Dane do przekazania funkcji Write_To_Nrf
    val[0] = 0x01;
    Write_To_nRF(W, EN_AA, val, 1);//Tryb zapisu, zapis do rejestru EN_AA, val - dane do zapisania, 1 - liczba bajtow
   
    val[0] = 0x01;
    Write_To_nRF(W, EN_RXADDR, val, 1);//Aktywny kanał 0 transmisji
   
    //Ustawienie rozmiaru adresu modul nRF (jak duzo zajmuje bajtow)
    val[0] = 0x03;
    Write_To_nRF(W, SETUP_AW, val, 1);
   
    //Ustawienie czestotliwości
    val[0] = 0x01;//2,401GHz
    Write_To_nRF(W, RF_CH, val, 1);
   
    //Ustawienie trybu poboru mocy i szybkości
    val[0] = 0x07;//0b00000111 bit 3 = 0 co oznacza 1Mbps, (wiekszy zasieg), bit 2 i 1 trby mocy (11 - 0dB, 11 - -18dB)
    Write_To_nRF(W, RF_SETUP, val, 1);
   
   
    //Adres odbiornika (5 bajtów)
    int i;
    for(i = 0; i <5; i++)
    {
        val[i] = 0x12;
    }
    Write_To_nRF(W, RX_ADDR_P0, val, 5); //Wcześniej był wybrany kanał 0, wiec ten adres został nadany dla kanalu 0. Mozna dodać inne adresy, żeby komunikować się z większą liczką modułów. nRF.
   
    //Adres nadajnika (5 bajtów)
    for(i = 0; i<5; i++)
    {
        val[i] = 0x12;
    }
    Write_To_nRF(W, TX_ADDR, val, 5);
   
    //Ustawienie rozmiaru danych
    val[0] = 5; //5 bajtów na pakiet
    Write_To_nRF(W, RX_PW_P0, val, 1);
   
    //Ustawienie rejestru CONFIG.
    val[0] = 0x1E; //bit O = 0 -> Transmiter, bit 0 = 1 -> Receiver, bit 1 = 1 -> Power Up, bit 4 = 1 ->mask_MAX_RT. IRQ nie jest wyzwalane, jeśli transmisja się nie powwiedzie.
    Write_To_nRF(W, CONFIG, val, 1);
   
    //Liczba prób i opóźnienia pomiędzy próbami
    val[0] = 0x2F; // bit 2 -> Opoznienie 750us, F -> liczba prób (15). Opoznienie musi wynosi minimum 500us przy 250kbps oraz jesli dane maja wiecej niż 5 bajtów przy predkosci 1Mbps i jeśli mają powyżej 15 bajtów przy 2Mbps.
    Write_To_nRF(W, SETUP_RETR, val, 1);
   
    //Opoznienie konieczne zeby przejsc w tryb StandBy (niski stan na CE)
    _delay_ms(100);
   
}

void Transmit_payload(uint8_t *W_buff)
{
    Write_To_nRF(R, FLUSH_TX, W_buff, 0);//Wyczyszczenie rejestru ze starych danych
    Write_To_nRF(R, W_TX_PAYLOAD, W_buff, 5);
    sei();
   
    _delay_ms(10);//Konieczne opoznienie zeby pracowac po zaladowaniu do nRF danych
    SETBIT(PORTC, 7);//CE w stan wysoki - rozpocznij transmisje
    _delay_us(20);
    CLEARBIT(PORTC, 7); //CE w stan niski - zakoncz transmisje.
    _delay_ms(10);
   
}


void reset(void)//Po kazdym odebraniu/wyslaniu danych IRQ musi zostać skasowane w nRF. Robi to poniższa funkcja.
{
    _delay_us(10);
    CLEARBIT(PORTC, 6);//CSN w stan niski
    _delay_us(10);
    Write_Byte_SPI(W_REGISTER + STATUS);//Zapisz do rejestru STATUS.
    _delay_us(10);
    Write_Byte_SPI(0x70);//Skasuj wszystkie IRQ w rejestrze STATUS.
    _delay_us(10);
    SETBIT(PORTC, 6);//CSN w stan wysoki.
}
   
int main(void)
{
    DDRA|=_BV(PA3) |_BV(PA2) |_BV(PA1) |_BV(PA0);// LED
    PORTB|=_BV(PB4);
    uint8_t W_buffer[6];
    uint8_t a=0;

    SPI_init();
    nRF24L01_init();
   
    sei();
   
    while (1)
    {
       
        for(uint8_t i = 0; i<5; i++)
        {
            W_buffer[i] = 0x93;
        }
       
        Transmit_payload(W_buffer);
       
        PORTA&=~_BV(PA1); //Zaświec LED blad
        _delay_ms(50);
        PORTA|=_BV(PA1);//Wylacz LED blad
        _delay_ms(50);
       
        reset();       
    }
}


Dla większej czytelności usunąłem niektóre funkcje jak np. funkcję odbioru danych od drugiego nRF24L01, czy funkcję konfigurującą przerwanie generowane przez moduł radiowy.
Plik nagłówkowy nRF24L01.h ma następującą postać: https://github.com/maniacbug/RF24/blob/master/nRF24L01.h .

Ostatnio zmieniony przez NightTrain 16-02-2018, 10:20, w całości zmieniany 5 razy  
Postaw piwo autorowi tego posta
 
 
marek1707 



Posty: 4928
Pomógł: 513 razy
Otrzymał 672 piw(a)
Skąd: WAW
Programuję w:
C, asm
Wysłany: 16-02-2018, 11:42   

Jeśli odblokujesz globalne przerwania (sei()) i jakiekolwiek przerwanie które nie ma swojego jawnego ISR, procesor to przerwanie obsłuży. Ty tak robisz z SPI (bit SPIE) a kompilator w miejsce każdego niezdefiniowanego wektora przerwań z definicji podstawia skok do początku programu właśnie po to, by takie historie miały swój spektakularny, smutny koniec.
Pierwsze użycie Write_Byte_SPI() ustawia SPIF i pozamiatane..
Nie widzę żebyś wstawił gdziekolwiek jakąś kontrolną operację typu mrugnięcie diodą czy wypisanie "Jestem w miejscu X". Wypracuj sobie jakieś metody śledzenia kodu, bo przecież nie zawsze przypadek będzie tak oczywisty. Gdybyś miał np. mrugnięcie 100ms diodką zawsze tuż na początku main'a szybko być zauważył, że procesor się resetuje a nie "zawiesza"..

Postaw piwo autorowi tego posta
 
 
NightTrain 



Posty: 5
Wysłany: 20-02-2018, 14:19   

Dziękuję za odpowiedź.
Dodałem kilka LEDów sygnalizujących aktualny stan programu:
LED1 - wejście do funkcji MAIN,
LED2 - funkcja Write_To_nRF,
LED3 - zakończenie funkcji NRF24L01INIT - ustawienie rejestrów modułu nRF24L01,
LED4 - poprawne ustawienie rejestru STATUS modułu - sprawdzane w pętli WHILE,
LED5 - funkcja RESET odpowiedzialna za skasowanie flag przerwań w module nRF po każdym nadaniu/odebraniu danych,
LED6 - sygnalizacja niepowodzenia transmisji,
LED7 - sygnalizacja nowych danych do odczytu,
LED8 - obieg pętli WHILE.

Funkcja MAIN wygląda tak:
1) - określenie kierunków działania pinów, do których podpięte są diody, wyłączenie diod.
2) - mignięcie diodą LED1 - funkcja MAIN.
3) - wywołanie funkcji SPIInit().
4) - wywołanie funkcji nRF24L01Init(). Podczas obsługi tej funkcji kilkakrotnie miga dioda sygnalizująca funkcję Write_To_nRF - logiczne, funkcja nRF24L01 kilkakrotnie wywołuje funkcję Write_To_nRF,
5) - wywołanie funkcji konfigurującej przerwanie INT0. Pod pin PD2 (INT0) podłączony jest pin IRQ modułu radiowego. Docelowo, zmienia on stan na niski po odebraniu/wysłaniu danych przed moduł nRF. Ma to spowodować wywołanie przerwania w programie.
6) - sprawdzenie stanu rejestru STATUS modułu nRF - jeśli jest OK zaświecenie diody. Tutaj wszystko działa.
7) - wysłanie 5 bajtów danych funkcją Transmit_Payload. Wywołuje ona 2 razy funkcję Write_To_nRF więc dioda sygnalizująca tę funkcją miga 2 razy.
8) - sprawdzenie czy transmisja się powiodła - jeśli nie, ma zapalić się LED. Nic się nie świeci, więc przyjmuję, że jest OK.
9) - sprawdzenie czy są jakieś nowe dane do odczytu. Jeśli tak, zaświeć LED. LED się świeci - AVR odebrał jakieś dane od modułu nRF. Jak wynika z działania SPI skoro wysłał to jednocześnie odebrał. Działa.
10) - wywołanie funkcji RESET - skasowanie flag przerwań w module nRF. Dioda miga - funkcja wykonała się prawidłowo.
11) - mignięcie diody na końcu pętli WHILE - OK.

Problem jest z przerwaniem generowanym przez moduł nRF24L01. Konfiguracja przerwania w programie wygląda następująco:
Kod programu: Zaznacz cały
void INT0_interrupt_init(void)//Przerwanie generowane po pomyslnej transmisji lub odbiorze pakietu. Pin IRQ zmienia swój stan na niski, co powoduje przerwanie na pinie INT0 procesora.
{
    DDRD|=_BV(PD2);//INT0 jak wejscie
    PORTD|=_BV(PD2);//Podciaganie wejscia INT0
    EICRA|=_BV(ISC01) |_BV(ISC00);//Zbocze narastajace
    EIMSK|=_BV(INT0);//Zezwolenie na przerwanie INT0
   
}

Podłączenie przycisku podającego masę na pin PD2 powoduje wywołanie przerwania i jego obsługę (mignięcie diodą). Zakładam więc, że przerwanie od strony programu działa poprawnie. Mamy podciąganie, więc w stanie normalnym napięcie na pinie PD2 wynosi 5V, wciśnięcie przycisku (podanie masy) oraz puszczenie przycisku (zmiana ze stanu niskiego na wysoki) powoduje wywołanie przerwania.
Według dokumentacji modułu nRF, bity 4,5 i 6 rejestru CONFIG odpowiedzialne są za wybór źródła przerwania pojawiającego się na pinie IRQ. Może to być:
- bit 4 - osiągnięto maksymalną liczbę retransmisji danych,
- bit 5 - wysłano dane,
- bit 6 - nowe dane.

Ustawienie rejestru CONFIG na wartość 0x1E (źródłem przerwania jest wysłanie oraz odebranie danych) powoduje, że stan pinu IRQ modułu nie zmienia się - cały czas jest na nim obecne napięcie 3,3V. Wnioskuję, że moduł nie odebrał ani nie wysłał żadnych danych. Pojawiają się rozbieżności. Skoro tak, to dlaczego dwa warunki w pętli WHILE sprawdzające czy transmisja się powiodła oraz czy są nowe dane do odczytu wskazują, że wszystko przebiegło pomyślnie ?
Ustawiłem również rejestr CONFIG na wartośc 0x6E (źródłem przerwania jest tylko bit MAX_RT rejestru STATUS wskazujący, że osiągnięto maksymalną liczbę retransmisji danych; 15 razy). Zakładam, że transmisja nie przebiega pomyślnie, dane są wysyłane kilkakrotnie aż do osiągnięcia maksymalnej liczby retransmisji (15 razy) następnie jest generowane przerwanie. Przy takich ustawieniach pin IRQ modułu nRF zmienia swój stan, napięcie zmienia się odpowiednio 0V do 3,3V.

Podsumowując: układ nie odbiera danych (również nie wysyła), więc nie ma zmiany stanu pinu IRQ.
W pętli WHILE sprawdzam stan bitu MAX_RT (maksymalna liczba retransmisji) i jeśli wynosi 1 oznacza, że transmisja się nie powiodła. Cały czas ma on wartość 0 bo dioda się nie świeci. Na tej podstawie założyłem, że transmisja przebiegła pomyślnie. Z drugiej strony, pin IRQ modułu nRF wskazuje, że bit ten został ustawiony co oznacza niepowodzenie transmisji.

Ostatnio zmieniony przez NightTrain 20-02-2018, 14:24, w całości zmieniany 2 razy  
Postaw piwo autorowi tego posta
 
 
marek1707 



Posty: 4928
Pomógł: 513 razy
Otrzymał 672 piw(a)
Skąd: WAW
Programuję w:
C, asm
Wysłany: 20-02-2018, 14:59   

Ustawienie bitu w DDRD oznacza wyjście, nie wejście.

Przycisk produkuje mnóstwo zboczy w obu kierunkach zarówno przy wciskaniu jak i puszczaniu, nie używaj żadnej mechaniki do testowania wejść przerwań. Poza tym ma tyle "siły" że zdusi do zera także wyjście procesora ustawione w stan wysoki. Natomiast scalak może tego nie umieć.

Skoro przerwanie jest sygnalizowane przez stan niski wyjścia IRQ, to dlaczego ustawiłeś czułość INT0 na zbocze narastające?

Nie widzimy kodu a Twoje tłumaczenia mogą być tylko życzeniami a nie jego prawdziwym działaniem. Gdyby wszystko było tak jak mówisz, to nie miałbyś problemów.

EDIT: Spróbuj zadawać bardziej konkretne pytania. Do celów testowych twórz prostszy kod. Jedna operacja ze scalakiem, ma być zgłoszone przerwanie, procesor go nie widzi, mierzysz woltomierzem, napięcie jest dziwne (np. z 3.3V zrobiło się 1.5V), szukasz przyczyny, znajdujesz: konflikt dwóch wyjść.

Ostatnio zmieniony przez marek1707 20-02-2018, 15:01, w całości zmieniany 1 raz  
Postaw piwo autorowi tego posta
 
 
es2 



Posty: 175
Pomógł: 2 razy
Otrzymał 4 piw(a)
Programuję w:
C ASM AHDL CUPL
Należę do:
AVT
Wysłany: 27-02-2018, 21:22   

marek1707 napisał/a:
a kompilator w miejsce każdego niezdefiniowanego wektora przerwań z definicji podstawia skok do początku programu właśnie po to, by takie historie miały swój spektakularny, smutny koniec.

Można to przechwycić deklarując ISR( BADISR_vect ). Zawsze tak robię i wstawiam tam pułapkę. Tak samo zawsze (o ile procek pozwala) uruchamiam przerwania od WDG. W przerwaniu odczytują do zmiennej adres powrotu PC. Bardzo ułatwia to pracę.


http://er-mik.prv.pl/projekty_avt.php
Postaw piwo autorowi tego posta
 
 
Wyświetl posty z ostatnich:   
Odpowiedz do tematu
Nie możesz pisać nowych tematów
Nie możesz odpowiadać w tematach
Nie możesz zmieniać swoich postów
Nie możesz usuwać swoich postów
Nie możesz głosować w ankietach
Nie możesz załączać plików na tym forum
Możesz ściągać załączniki na tym forum
Wersja do druku

Skocz do:  

Nie rozwiązałeś swojego problemu? Zobacz podobne tematy: [C] Sterowanie serwo... Funkcja "CONFIG... Programy w c - mobil... PWM na atmega32 w C...
lub przeszukaj forum po wybranych tagach: danych, komunikacja, po, przerwanie, spi, spi, usi., wyslaniu


Powered by phpBB modified by Przemo © 2003 phpBB Group
Popularne kursy: Arduinopodstawy elektroniki