Skocz do zawartości

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


NightTrain

Pomocna odpowiedź

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.

/*
* 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;
}

Link do komentarza
Share on other sites

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.

  • Lubię! 1
Link do komentarza
Share on other sites

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:

uint8_t SPI_send_rec_byte(uint8_t byte) 
{ 
   SPDR=byte; 
} 

W przerwaniu sprawdzam to co przesłał SLAVE.

Dziękuję za pomoc.

Link do komentarza
Share on other sites

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.

Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

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.blogspot.com/2013/04/tutorial-nrf24l01-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:

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:

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.

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:

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

na

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

program nie zawiesza się.

Czy dobrze to wszystko rozumuję ?

Link do komentarza
Share on other sites

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?

  • Pomogłeś! 1
Link do komentarza
Share on other sites

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:

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:

_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.

/*
* 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 .

Link do komentarza
Share on other sites

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"..

  • Pomogłeś! 1
Link do komentarza
Share on other sites

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:

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.

Link do komentarza
Share on other sites

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ść.

Link do komentarza
Share on other sites

Gość es2
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ę.

Link do komentarza
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.