piątek, 25 marca 2016

Porozmawiajmy - USART - Konfiguracja

Nasz mikrokontroler wie już jak poinformować świat, że coś się zmieniło w jego rejestrach. Wie też, jak odczytać sygnały, które mu chcemy przekazać. Czas najwyższy nauczyć go jak rozmawiać z urządzeniami jego pokroju.

Mowa jest złotem

Żeby się porozumiewać, człowiek przez lata ukształtował coś, z czego wszyscy korzystamy - język. W dużym uproszczeniu medium transmisyjnym w większości przypadków jest powietrze - to w nim rozchodzi się dźwięk - głoski wyrażane przez nasze aparaty mowy, które po pewnym czasie trafiają do uszu naszego rozmówcy. Sposób sprawdzony, znany od tysiącleci, posiadający wiele implementacji. Więc na co czekamy? Przełóżmy to na "elektroniczne".

Gotowe rozwiązania

Potrzebujemy medium transmisyjnego - będzie nim przewodnik - np. przewód albo miedziana ścieżka na płytce. Głoski zastąpimy poziomami napięcia - wysokim i niskim. Dwa stany w zupełności wystarczą. Aparat gębowy zastąpimy linią nadającą, a ucho linią odbierającą. Analogią dla ośrodka mowy i słuchu naszego mózgu będzie kodzik wykonywany przez procesor - to do niego będzie należała decyzja, co mówić i czego słuchać.

Do you speak high-low?

Możemy sobie już wyobrazić 2 urządzenia, które "krzyżując" swoje linie mogłyby ze sobą rozmawiać. Mogłyby, ale się zapewne nie dogadają. Potrzebują do tego jeszcze punkt odniesienia, względem którego będą weryfikować, czy to co do nich dotarło to jeszcze stan niski, czy już stan wysoki. Dajmy im w tym celu jeszcze jedną linię. Czy nasze urządzenia będą w stanie się dogadać? Tak, jeżeli tylko nauczymy je tego samego "języka", a przynajmniej kilku zasad, których będziemy przestrzegać w czasie komunikacji. I tu pojawia się określenie protokół.

Źródło: atmega162 datasheet
Załóżmy, że nasze urządzenia będą rozmawiać "ramkami", czyli zestawem stanów wysokich i niskich w określonym porządku. Ramkę rozpoczną bitem startu (i będą wiedziały jak go "wystawić"), następnie prześlą dane, po nich bit parzystości, a na koniec bit stopu. Następnie przejdą w stan nasłuchiwania. Ważne jest, by obie strony wiedziały, jakiego formatu ramki powinny się spodziewać. Dodajmy do tego deklarację prędkości, z jaką będzie przeprowadzana transmisja.

USART

W ten sposób zdefiniowaliśmy elementy potrzebne do wykorzystania układu transmisji danych obecnego na pokładzie prostej Atmegi (znajdziemy go też w Arduino, a nawet w Malince). Moduł ten zwalnia nas z konieczności implementacji całego protokołu transmisji. Mało tego, programiści Atmela oddali do naszej dyspozycji m.in. makra ustawiające parametry komunikacji i przerwania.
Jak się nietrudno domyślić - po jednej stronie komunikacyjnego układu stać będzie nasz mikrokontroler, po drugiej... cokolwiek mogące obsłużyć protokół UART/USART. Co to może być? Moduł Bluetooth, który stanie się dla nas przepustką do świata bez przewodów, inna Atmega albo nasz domowy PC wyposażony w port COM (w tym przypadku przyda się konwerter napięć - zanim podepniesz 2 rzeczy ze sobą, sprawdź, jakie poziomy logiczne obsługują, i czy aby na pewno rozmawiają na tym samym poziomie - 12V nie zadziała kojąco na mikrokontroler zasilany napięciem 3.3V - i tłumaczenie, że chciałem tylko porozmawiać nic nie da...).

Kodzik!

Wiemy już jak działa komunikacja. Czas poinstruować naszego procka.

#include <avr/io.h>
#include <avr/interrupt.h>
#define F_CPU 8000000UL
#define USART_BAUDRATE 19200// 115200
#define UBRR_VALUE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1) // 25

void USART0Init(void)
{
    // Set baud rate
    UBRR0L = (uint8_t)UBRR_VALUE;
    UBRR0H = (uint8_t)(UBRR_VALUE>>8);

    // Set frame format to 8 data bits, no parity, 1 stop bit
    UCSR0C=(1<<URSEL0)|(1<<2)|(1<<1);

    //enable reception and RC complete interrupt
    UCSR0B |= (1<<RXEN0)|(1<<RXCIE0)|(1<<TXEN0);
}

void UsartFlush(void)
{
    UCSR0B &= ~(1<<RXEN0);
    UCSR0B |= (1<<RXEN0);
}

void UsartWrite(char* text)
{
    while(*text)
    {
        while(!(UCSR0A & (1<<UDRE0)))
        {
            //Do nothing
        }

        //Now write the data to USART buffer
        UDR0=*text++;
    }
}


int main(void)
{
    USART0Init();

    // set pb0 and pb1 pins as output
    DDRB |= (1 << PB0) | (1 << PB1);

    // turn off leds connected to pb
    PORTB |= (1 << PB0) | (1 << PB1);

    // initialize interrupts
    sei();
   
    while (1)
    {
    }
}

// onUsart
ISR(USART0_RXC_vect)
{
    char temp;
 
    temp = UDR0;
   
    if (temp == '0')
    {
        // switch led state connected to pb0
        PORTB ^= (1 << PB0);
    }
    else if (temp == '1')
    {
        // switch led state connected to pb1
        PORTB ^= (1 << PB1);
    }
}




Brak komentarzy:

Prześlij komentarz