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

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

Autor Wiadomość
NightTrain 



Posty: 4
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;
}

Polecany artykuł » Kurs Arduino II - #5 - klawiatura, własny system alarmowy


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



Posty: 4592
Pomógł: 484 razy
Otrzymał 630 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: 4
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: 4592
Pomógł: 484 razy
Otrzymał 630 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.

Popularny artykuł » Tworzenie aplikacji Android - #1 - Wstęp


Postaw piwo autorowi tego posta
 
 
NightTrain 



Posty: 4
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.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:
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: 4592
Pomógł: 484 razy
Otrzymał 630 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: 4
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: 4592
Pomógł: 484 razy
Otrzymał 630 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
 
 
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