Skocz do zawartości

[Programowanie] Budowa własnej biblioteki USART od podstaw


Demooon

Pomocna odpowiedź

Moduł USART możemy znaleźć w zdecydowanej większości mikrokontrolerów. Jest to najbardziej rozpowszechniony interfejs komunikacyjny, jednak sprawia on często wielu problemów początkującym. W niniejszym artykule postaram się omówić od podstaw tworzenie własnej biblioteki w środowisku Eclipse wykorzystującej ten interfejs w trybie asynchronicznym w oparciu o notę katalogową popularnej Atmegi8. Transmisja wykorzystująca zbudowane funkcje będzie polegać na oczekiwaniu ustawienia odpowiednich flag, czyli odpytywanie cykliczne. (ang. Polling)

1. Tworzenie plików nagłówkowych

Aby napisać własną bibliotekę musimy mieć przygotowaną bazę. Tworzymy nowy projekt, piszemy funkcję główną, a następnie wewnątrz funkcji głównej dowolnie wybraną pętle nieskończoną. Warto od razu dołączyć do naszego projektu dedykowane biblioteki m.in. do obsługi portów, przerwań i opóźnień systemowych. Oszczędzi to nam niepotrzebnego szukania błędów w programie, gdy źródłem problemów okaże się brak dołączonej biblioteki.

Krok 1: Tworzymy nowy folder w naszym projekcie, nadajemy mu nazwę, a następnie w jego wnętrzu tworzymy plik nagłówkowy z rozszerzeniem .h (Header File). W pliku tym zamieścimy wkrótce potrzebne definicje preprocesora jak i deklaracje funkcji.

Krok 2: Do naszego pliku źródłowego należy dołączyć nasz nowo powstały plik nagłówkowy poleceniem #include w następujący sposób: #include „nazwafolderu/nazwapliku.h”. Polecenie to umieszcza się pod dołączeniem pozostałych bibliotek.

Krok 3: We wnętrzu naszego folderu tworzymy kolejny plik nagłówkowy(Source File). Po wpisaniu nazwy należy samodzielnie dopisać rozszerzenie .c. Wówczas plik ten będzie plikiem funkcyjnym przechowywującym definicje funkcji (ich ciała) których będziemy mogli używać w naszym programie.

Krok 4: Do powstałego pliku z rozszerzeniem .c należy dołączyć poleceniem #include nasz plik nagłówkowy z rozszerzeniem .h.

2. Tworzenie funkcji obsługującej interfejs szeregowy USART

#ifndef USART_H_
#define USART_H_

#define USART_BAUDRATE 9600
#define UBRR (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)

void usart_init(void);
void usart_wyslijznak(char znak);
void usart_wyslijstring(char *s);
void usart_wyslijliczbe(uint32_t liczba, uint16_t typ);
char usart_pobierzznak();

#endif

Powyżej zamieszczony jest finalny kod w pliku usart.h który chcemy stworzyć.

Na początek definiujemy prędkość z jaką chcemy dokonywać przesyłania danych. Następnie już na poziomie kompilacji obliczamy wartość UBRR którą wkrótce wpiszemy do dwubajtowego rejestru mikrokontrolera przechowywującego prędkość transmisji. Należy wykorzystać do tego wzór podany przez producenta zamieszczony w nocie katalogowej. Nie jest konieczne wcześniejsze definiowanie częstotliwości taktowania procesora F_CPU gdyż Eclipse zrobi to za nas.

Następnie musimy umieścić deklaracje funkcji które zamierzamy zbudować. Będą to kolejno funkcje do inicjalizacji naszego USART’a , wysłania pojedynczego znaku, wysłania łańcucha znaków, liczby w dowolnym systemie i odebrania pojedynczego znaku.

#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdlib.h>
#include "usart.h"
void usart_init(void){
UBRRH = (UBRR>>8);
UBRRL =  UBRR;
UCSRB |= (1<<RXEN)|(1<<TXEN);
UCSRC |= (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);
}
void usart_wyslijznak(char dana){
while( !(UCSRA & (1<<UDRE)));
UDR = dana;
}
void usart_wyslijstring(char *s){
while(*s) usart_wyslijznak(*s++);
}
void usart_wyslijliczbe(uint32_t liczba, uint16_t typ){
char bufor[17];
ltoa(liczba,bufor,typ);
usart_wyslijstring(bufor);
}
char usart_pobierzliczbe(){
while ( !(UCSRA & (1<<RXC)));
return UDR;
}

Powyżej zamieszczony jest finalny kod w pliku usart.c który chcemy stworzyć.

Na początek najważniejsza funkcja w naszej bibliotece czyli usart_init(). Funkcja ta ma za zadanie wpisanie od rejestru UBRR mikrokontrolera interesującej nas prędkości przesyłania danych, włączenie pinów Tx i Rx za pomocą ustawienia bitów RXEN i TXEN w rejestrze UCSRB ( wówczas piny te tracą możliwość pracy jako normalne piny I/O ) i ustawienie właściwości ramki danych. Wykorzystamy najpopularniejszą postać ramki czyli 8N1 ( 8 bitów danych, brak kontroli parzystości non-parity, 1 bit topu)

Bity UCSZ1 i UCSZ0 służą do ustawienia ośmiobitowej ramki danych, natomiast bit URSEL za możliwość modyfikacji rejestru UCSRC. Jak widzimy producent podaje kompletny przykładowy kod do inicjalizacji modułu, jednak kod ten zawiera ustawienie dwóch bitów stopu (bit USBS). Wystarczy nie ustawiać tego bitu aby zastosować jeden bit stopu.

Kolejną funkcją jest wysyłanie pojedynczego znaku. Jest to pierwsza funkcja która pozwala nam na komunikację naszego mikrokontrolera ze światem zewnętrznym. Jest to prosta funkcja w całości podana przez producenta mająca za zadanie wpisanie naszej zmiennej którą chcemy wysłać do buforu nadawczego UDR mikrokontrolera. Wpisanie to następuje po sprawdzeniu flagi UDRE w rejestrze UCSRA która informuje nas czy bufor nadawczy jest gotowy na przyjęcie kolejnych danych.

Funkcja wysyłająca string wykorzystuje poprzednio napisaną funkcję wysyłającą znak. Przyjmuje ona jako argument ciąg znaków, który wysyłany jest element po elemencie w pętli while. Dane będą wysyłane dopóki pętla nie trafi na znak ‘/0’ o kodzie ASCII =0 który znajduje się na końcu każdego ciągu znaków.

Funkcja wysyłająca liczbę to najdłuższa funkcja zaraz po inicjalizacyjnej w naszej bibliotece. Przyjmuje ona dwa argumenty: liczbę którą chcemy wysłać i system liczbowy na którą chcemy naszą liczbę przekonwertować do łańcucha znakowego. Zmienna typ może przyjąć wartość m.in. 2,8,16 w zależności od tego jaki system liczbowy chcemy uzyskać. Wewnątrz tworzona jest siedemnastoelementowa tablica znaków która będzie naszym buforem. Całą pracę związaną z konwersją wykonuje za nas funkcja ltoa ( long to ascii), a wynik tej konwersji zapisywany jest w buforze, który zostaje wysłany.

Ostatnią funkcją jest odebranie pojedynczego znaku. Podobnie jak usart_wylijznak() jest w całości dostępna w nocie katalogowej producenta. Sprawdza ona w pętli while czy w buforze odbiorczym czekają na nas jakieś dane które możemy odczytać. Jeśli tak funkcja zwraca zawartość bufora. Warto zwrócić uwagę że zapis UDR dotyczy jednocześnie bufora nadawczego jak i odbiorczego naszego mikrokontrolera w zależności od wykonywanej przez nas akcji.

Czas na praktyczne wykorzystanie naszej nowo powstałej biblioteki. W tym celu musimy posiadać przejściówkę USB-UART , USB-RS232 lub wykonać taką samemu. Dostępnych schematów w sieci jest mnóstwo. Ja posłużę się gotową z popularnym i niezawodnym układem FT232 ( widoczny na zdjęciu )

Bardzo istotne jest aby połączyć przejściówkę zgodnie z zasadą krzyżowania przewodów ( RX mikrokontrolera do TX przejściówki, a TX do RX). Kolejną rzeczą jest uruchomienie programu terminala na naszym PC np. Realterm i ustawienie takich samych właściwości ramki danych jak w uC, w przeciwnym przypadku terminal wyświetli „krzaki”.

Dla przykładu wyślemy przez USART dane z kanału pierwszego przetwornika analogowo-cyfrowego dostępnego w Atmedze. Napięcie mierzone będzie na potencjometrze ,które płynnie będziemy zmieniać w zakresie od 0 do 5V poprzez obrót gałki. Gotowy kod do obsługi przetwornika i wysłania pomiarów dzięki naszej bibliotece przedstawiam poniżej:

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "USART/usart.h"

uint16_t zmierz(){
ADMUX |= (1<<MUX0);
ADCSRA |= (1<<ADSC);
while ( ADCSRA & (1<<ADSC) );
		return ADCW;
}
int main(void)
{
usart_init();
ADCSRA |= (1<<ADEN);
ADCSRA |= (1<<ADPS1)|(1<<ADPS2);
ADMUX |= (1<<REFS0);
while(1)
{

_delay_ms(800);
usart_wyslijliczbe(zmierz(),10);
usart_wyslijznak(' ');
}

}

Po prawidłowym podłączeniu i zaprogramowaniu naszym oczom ukaże się „rozmowa” mikrokontrolera z komputerem.

Tak zbudowana transmisja wykorzystująca tzw. polling może posłużyć do odczytu stanu portów, wartości zwracanych przez nowo testowane przez nas czujniki, czy do debugowania. Możemy także wykorzystać jak w powyższym przykładzie przetwornik , a na podstawie danych z jakiegoś czujnika który zwraca zmienne napięcie rozrysować jego charakterystykę. Do szerszych zastosowań m.in. zdalnego sterowania, wysyłania danych w tle działania programu efektywniejszy jest jednak sposób polegający na przerwaniach.

Artykuł ten potwierdza, że sprawne posługiwanie się notą katalogową nie jest trudne konieczne jednak są podstawy znajomości języka angielskiego. Postępując analogicznie możemy stworzyć bibliotekę do obsługi interfejsu TWI czy SPI.

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

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • 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.