Uwaga! Ta strona wysyła Ci "ciasteczko".
Użytkowników online:
2
Kursy>Kurs AVR-GCC>Kurs AVR-GCC, cz.5
printer_icon

Kurs AVR-GCC cz.5

29.03.2010 ABXYZ

Ostatnio omawiane były tablice i funkcje, a jeszcze wcześniej: zmienne, pętle i instrukcje warunkowe. W tej części kursu tematem przewodnim będzie tekst i działania na tekście. Napiszę też kilka zdań na temat preprocesora języka C. Kolejnym omawianym zagadnieniem będzie podział kodu źródłowego programu na oddzielnie kompilowane pliki. W części praktycznej będziemy bawić się alfanumerycznym wyświetlaczem LCD, przyłączymy do AVRa termometr cyfrowy ds18b20, a dalej połączymy AVRa z komputerem PC poprzez port szeregowy RS232.

Programy z tekstem

Dotąd w przykładach z kursu używane były jedynie zmienne liczbowe. A co z tekstem ? Oczywiście tekst przechowuję się w pamięci komputera również w postaci liczb. Po prostu małym i wielkim literom alfabetu, cyfrom oraz wszystkim innym znakom przyporządkowuje się kolejne liczby z pewnego zakresu. Zwykle jeden znak zajmuje w pamięci komputera jeden bajt (osiem bitów), najczęściej używanym bywa kodowanie ASCII lub jego rozszerzenia.

kody ascii
Tablica kodów ASCII. Literom alfabetu, cyfrom oraz wszystkim innym znakom przyporządkowuje się kolejne liczby. Kliknij w obrazek, żeby obejrzeć całość.

Inaczej inż jest w wielu innych językach programowania w C nie przewidziano specjalnego typu zmiennej przeznaczonego do przechowywania tekstu. W języku C do przechowywania tekstu wykorzystuje się tablice typu char. Aby zapamiętać tekst kolejne pola tablicy wypełniane są kodami ASCII znaków tworzących tekst.

obrazek
Tablica wypełniona kodami ASCII kolejnych liter tworzących napis "Siemka!". W języku C tekst przechowuje się po prostu w tablicach typu char.

W kodzie źródłowym programu można posługiwać się stałymi znakowymi i stałymi napisowymi. Stała znakowa ma postać znaku objętego pojedynczymi cudzysłowami i posiada wartość liczbową kodu ASCII tego znaku.

int jeden_znak ; char jakis_napis[7]; /* W zmiennej jeden_znak znajdzie się wartość 65(kod ASCII znaku A) */ jeden_znak = 'A'; /* Zapisujemy tekst do tablicy znak po znaku */ jakis_napis[0] = 'S'; jakis_napis[1] = 'i'; jakis_napis[2] = 'e'; jakis_napis[3] = 'm'; jakis_napis[4] = 'k'; jakis_napis[5] = 'a'; jakis_napis[6] = '!';

Stałe napisowe tworzy się obejmując fragment tekstu parą podwójnych cudzysłowów. Definiując tablicę znakową można ją jednocześnie zainicjować stałą napisową. Tym sposobem tablica, w momencie jej tworzenie, zostanie wypełniona kodami ASCII kolejnych znaków tworzących napis.

/*Tworzona tablica zostanie wypełniona ciągiem znaków */ char jakis_napis[] = "Siemka!"; /* Zawartość tablicy: jakis_napis[0] = 'S' jakis_napis[1] = 'i' jakis_napis[2] = 'e' jakis_napis[3] = 'm' jakis_napis[4] = 'k' jakis_napis[5] = 'a' jakis_napis[6] = '!' jakis_napis[7] = 0x0 */

W przykładzie wyżej, w tablicy za ostatnim znakiem napisu, kompilator dodatkowo wstawi bajt o wartości zero. Znak o kodzie zero pełni tu rolę znacznika końca ciągu znaków. Jest zasadą w języku C że ciągi znaków kończą się znakiem o kodzie równym zero. Tekst może mięć dowolną długość, aby się tylko zmieścił w  tablicy wzraz z ograniczającym go bajtem zero.

Jeżeli w stałej napisowej potrzeba wstawić znak podwójnego cudzysłowu, to należy go poprzedzić znakiem backslash (\"). A jeśli chcemy wstawić sam znak backslash, to należy wpisać dwa znaki backslash (\\). Są to tzw. sekwencje specjalne zaczynające się od znaku backslash, dalej jeszcze będę o nich pisał.

/* Do tablicy zapisany zostanie ciąg znaków: abcdef"gh\i\jklmnop"qrs'tuv'wxyz */ char jakis_napis[] = "abcdef\"gh\\i\\jklmnop\"qrs'tuv'wxyz";

Jeśli jakaś funkcja oczekuje jako argumentu tablicy typu char, to jako argument, zamiast nazwy tablicy, można wstawić stałą napisową.

/* Definicja tablicy */ char tablica[]="KURS AVR-GCC"; /* Definicja przykładowej funkcji, która jak argumentu oczekuje tablicy typu char */ void funkcja(char tablica[]) { } int main(void) { /* Wywołanie funkcji */ funkcja(tablica); /* Jako argument można wstawić stałą napisową */ funkcja("KURS AVR-GCC");

W języku C brakuje również operatorów przeznaczonych do działań na tekście, takie operacje jak porównywanie czy łączenie napisów pozostaje zaprogramować samemu. Nie jest to nic specjalnie trudnego, oto kilka przykładów prostych operacji na tekstach:

Wykorzystując instrukcję pętli można porównywać dwa ciągi znaków.

/* Przyrównanie ciągu znaków */ unsigned char i; char str1[]= "KURS AVR-GCC"; char str2[]= "KURS AVR-GCC"; for(i=0; str1[i]==str2[i] && str1[i]; i++); /* Jeśli warunek spełniony, to porównywane ciągi znaków róźnią się */ if(str1[i] || str2[i])

Podobne używając instrukcji pętli można połączyć dwa lub więcej napisów w jeden tekst.

/* Łączenie ciągów znaków */ unsigned char i,j; char str1[]= "KURS"; char str2[]= " AVR-GCC"; char str3[]= " cz.5"; char buffer[18]; /* Łączy trzy ciągi znaków . Całość zostanie zapisana w tablicy 'buffer[]' */ for(i=0,j=0; buffer[j]=str1[i]; i++,j++); for(i=0 ; buffer[j]=str2[i]; i++,j++); for(i=0 ; buffer[j]=str3[i]; i++,j++);

A tak z pomocą instrukcji pętli for można wyznaczyć długość ciągu znaków zakończonego zerem.

/* Obliczanie długości ciągu znaków */ char s[] = "KURS AVR-GCC"; unsigned char i; /* Zmienna 'i' zawierać będzie długość ciągu znaków w tablicy 's[]' . Bajt o wartości zero na końcu ciągu nie jest liczony. */ for(i=0; s[i]; i++);

Jeżeli zamierzamy na wyświetlaczu alfanumerycznym pokazać wartość zmiennej liczbowej, to koniecznym będzie zamienić wartość liczbową na ciąg znaków. Kawałek kodu poniżej zmienia 16-bitową liczbę całkowitą bez znaku na odpowiadający jej ciąg cyfr (kodów ASCII cyfr). Wartość liczbowa w zmiennej 'a' jest cyklicznie w pętli dzielona przez 10, dopóki nie stanie się zerem - dzielenie całkowite. Obliczana w każdej iteracji pętli reszta z dzielenia stanowi cyfrę stojącą na kolejnej pozycji w liczbie, idąc w kierunku od cyfry najmniej znaczącej do najbardziej znaczącej. Reszta z dzielenia (liczba z zakresu 0..9) zmieniana jest na kod ASCII cyfry przez dodanie do niej wartości kodu ASCII cyfry zero (48).

/* Zmiana liczby na ciąg znaków ASCII */ signed char i; unsigned int a ; char buffer[6]; a = 65535; /* Wypełnia tablicę 'buffer[]' kodami ASCII cyfr skaładających się na liczbę w zmiennej 'a' */ for(i=4,buffer[5]=0; a; a/=10,i--) buffer[i]= a%10 + '0'; for(; i>=0; i--) buffer[i] = ' ';

Opisane działania na tekstach można zrealizować również wykorzystując funkcje z biblioteki standardowej języka C.

Do przekształcenia wartości liczbowych na ciągi znaków i w ogóle do formowania komunikatów tekstowy użyteczne mogą być standardowe funkcje printf() i sprintf(); aby móc z nich skorzystać należy gdzieś na początku pliku wstawić polecenie:

#include <stdio.h>

Funkcje printf i sprintf różnią się od siebie tym, że sprintf zapisuje dane do tablicy, zaś printf do standardowego wyścia. Na kilku prostych przykładach wyjaśnię działanie funkcji sprintf.

char buf[32]; int t = 21; int p = 1013; int v = 10; /* W tablicy 'buf' zostanie zapisany ciąg znaków: 'Temperatura powietrza: 21°C' */ sprintf(buf,"Temp. powietrza: %d°C",t); /* W tablicy 'buf' zostanie zapisany ciąg znaków: 'T:21°C, P:1013hPa, V:10m/s' */ sprintf(buf,"T:%d°C, P:%dhPa, V:%dm/s",t,p,v);

Pierwszym argumentem funkcji sprintf() jest tablica znakowa, miejsce w pamięci, gdzie zostanie zapisany ciąg znaków zakończony zerem - tworzony komunikat. Drugim argumentem sprintf() jest ciąg znaków zawierający format komunikatu. Format zawiera stałą treść komunikatu wraz z tzw. "specyfikacjami przekształceń". Specyfikacje przekształceń są to takie "znaczki-krzaczki":) w rodzaju: %d, %4d, %u, %x, %6.1f i podobnie wyglądające, które zostaną w wyjściowym komunikacie zastąpione jakąś treścią. A czym ? W naszym przykładzie, w miejscu pierwszego wystąpienia specyfikacji %d zostanie wstawiona wartość trzeciego argumentu funkcji sprintf, czyli wartość zmiennej 't' wypisana w postaci liczby dziesiętnej. Tak samo następne występujące w formacie specyfikacje zostaną zmienione wartościami kolejnych argumentów funkcji sprintf. Funkcją sprintf nie ma ustalonej liczby argumentów, więc w formacie komunikatu można umieszczać dowolną ilość specyfikacji przekształceń. Na temat funkcji o zmiennej liczbie argumentów napiszę jak się nadarzy okazja.

Specyfikacje przekształcenia zaczynają się od znaku procenta % i kończą znakiem przekształcenia. Na przykład: %d -zastąpione zostanie liczbą całkowitą; %u-liczbą całkowitą bez znaku; o-liczbą ósemkową; %x-liczbą szesnastkową; c-jednym znakiem; $s-ciągiem znaków; %f-liczbą niecałkowitą(zmiennoprzecinkową) w postaci: część całkowita, kropka, część ułamkowa (np.: 12.345). Aby wypisać sam znak procent % wstawia się dwa znaki procent %%. Następne przykłady przekształceń: %6d -liczba całkowita zajmująca co najmniej 6 znaków (na przykład: [][][]123); %6.2f -liczba zmiennopozycyjna o długości 6 znaków i z dwiema cyframi po przecinku (na przykład: []12.34)

char buf[32]; double l = 12.3456; int valve = 80; /* W tablicy 'buf' zostanie zapisany ciąg znaków: 'Valve: 80%' */ sprintf(buf,"Valve%6d%%",valve); /* W tablicy 'buf' zostanie zapisany ciąg znaków: 'Length= 12.34' */ sprintf(buf,"Length=%6.2f",l);

Opisałem tu jedynie część możliwości funkcji sprintf(), po resztę odsyłam do podręcznika języka C i do dokumentacji biblioteki AVRLibc.

Ale uwaga, użycie w programie dla 8-bitowego mikrokontrolera dość rozbudowanych funkcji sprintf, printf skutkuje znaczącym wzrostem wielkości kodu wynikowego. W AVRLibC domyślnie, celem oszczędzania pamięci, funkcje printf, sprintf nie obsługują liczb zmiennoprzecinkowych. Aby włączyć obsługę liczb zmiennoprzecinkowych uruchamiamy program MFile, wczytujemy plik makefile naszego projektu i w menu "Makefile->printf()options" zaznaczamy opcję "floating point".

obrazek
Okno programu MFile. Opcja "floating point" włącza dla funkcji printf obsługę zmiennych zmiennopozycyjnych.

Kto wcześniej uczył się języka C pisząc programy na komputer PC, ten na pewno pamięta funkcję printf, jej używa się najczęściej aby coś napisać na ekranie tekstowym. Funkcja printf różni się od omawianej wcześniej sprintf tym, że wysyła formatowany komunikat do tzw. standardowego sterumienia wyjściowego (stdout); funkcja sprintf zapisuje wynik do tablicy znakowej.

obrazek
Funkcja printf wysyła dane do standardowego wyjścia. Domyślnie na komputerze PC standardowe wyście to ekran tekstowy.

W chwili uruchomienia programu tworzone są strumienie danych: standardowe wejście(stdin), standardowe wyjście(stdout) i standardowe wyjście dla komunikatów o błędach(stderr) W przypadku programów uruchamianych na komputerze PC standardowy strumień wyjściowy kierowany jest domyślnie na ekran monitora, ale może być też przekierowany do innego urządzenia jak drukarka lub plikiem na dysku; standardowe wejście domyślnie połączone jest z klawiaturą komputera PC. W przypadku programów uruchamianych na mikrokontrolerze, gdy brak monitora i klawiatury, można powiązać standardowe strumienie danych (stdin i stdout) z portem szeregowym uC. Praktycznie wszystkie mikrokontrolery posiadają wbudowane układy transmisji szeregowej. Można podłączyć przewodem uC z komputerem PC poprzez port szeregowy RS232C i komunikować się z  mikrokontrolerem używając monitora i klawiatury.

W stałych znakowych i napisowych można umieszczać tzw. sekwencje specjalne. Sekwencje specjalne zaczynają się od znaku backslash "\", przykładowo sekwencja "\n" to znak nowego wiersza (LF Line Feed, kod ASCII 0x0A). Jeśli tekst składa się z wielu wierszy, to każdy wiersz zakończony jest znakiem nowego wiersza.

W przykładzie poniżej uruchomiłem programik, który instrukcją printf wypisuje trzy linijki tekstu. Dalej wypisany tekst pokazany jest w postać kodów ASCII. Czerwonym kolorem podkreśliłem sekwencje specjalne \n w tekście i odpowiadające im kody ASCII 0x0A.

Newline
Znak nowego wiersza '\n' kodowany jest bajtem o wartości 0x0A. Kliknij w obrazek, żeby obejrzeć całość

Programik ten został skompilowany i uruchomiony w systemie Linux, ale jeśli ten sam programik skompilujemy w produkcie o nazwie "Windows", to znak nowej linii \n kodowany będzie z użyciem dwóch bajtów: 0x0D,0x0A.

Newlinewine
W Windowsie znak nowego wiersza '\n' kodowany jest z użyciem dwóch bajtów: 0x0D, 0x0A. Kliknij w obrazek, żeby obejrzeć całość

Preprocesor języka C

Nie należ mylić poleceń preprocesora z instrukcjami programu, polecenia preprocesora zaczynają się znakiem hash "#". Preprocesor przystępuje do działania jeszcze przed właściwą kompilacją i automatycznie edytuje tekst źródłowy programu. Preprocesor potrafi wykonywać kilka rodzajów prostych ale użytecznych operacji na tekście. W programach najczęściej można spotkać polecenia #include i #define. Polecenie #include, w miejscu jego wystąpienia, wkleja zawartość innego wskazanego pliku tekstowego. Jeżeli nazwa dołączanego pliku jest objęta parą cudzysłowów, wtedy plik poszukiwany jest w katalogu projektu.

#include "plik.h"

Jeżeli nazwa wklejanego pliku objęta jest parą znaków <>, wtedy plik poszukiwany jest w katalogu, gdzie znajdują się standardowe pliki nagłówkowe.

#include <stdio.h>

Zwykle nazwy plików dołączanym poleceniem #include posiadają rozszerzenie .h i nazywane są plikami nagłówkowymi. A co zawierają dołączane pliki? Mogą zawierać dowolny kod w języku C, zwykle zawierają deklaracje funkcji i różne makrodefinicje.

W najprostszym sposobie użycia polecenie #define (makrodefinicja) zastępuje w tekście źródłowym programu każde wystąpienie wskazanej nazwy na inny podany ciąg znaków - podobnie jak działa opcja "Zmień" typowego edytora tekstu.

#define NAZWA zastępujący ciąg znaków

Zastępujący ciąg znaków rozciąga się do końca linii, aby go kontynuować w kolejnych liniach tekstu, należy każdą przedłużaną linię zakończyć znakiem backslash \ .

#define NAZWA zastępujący\ ciąg znaków

Polecenie #define działa od miejsca wystąpienia do końca pliku albo do wystąpienia polecenia #undef NAZWA, a zawartość stałych napisowych jest przy zastępowaniu pomijana. Przyjęło się, że nazwy w makrodefinicji pisane są wielkimi literami aby się odróżniały od zmiennych i stałych programu.

Dla pokazania jak działa polecenie #define skompilowałem niewielki programik z opcją kompilatora -E. Przy wywołaniu kompilatora GCC z opcją -E, proces tłumaczenia kodu źródłowego programu zatrzymuje się po przejściu preprocesora i w wyniku otrzymujemy plik z tekstem programu przetworzonym tylko przez preprocesor. Warto zapamiętać tę opcję, może się przydać przy szukaniu błędów.

obrazek
Efekt działania polecenia #define. Programik w pliku zabawa.c skompilowałem z opcją kompilatora -E. W wyniku kompilator GCC zwrócił plik zabawa.txt zawierający tekst źródłowy programu przetworzony jedynie przez preprocesor.

A teraz praktyczny przykład wykorzystania makrodefinicji. Przypuśćmy, że jest dioda LED, która ma coś sygnalizować, przyłączona do jednego z portów we/wy AVRa. A po całym programie rozsiane są instrukcje włączające lub wyłączające diodę LED w rodzaju:

PORTD |= (1<<PD0); PORTD &= ~(1<<PD0);

W instrukcjach tych bezpośrednio wskazano nazwę portu i numer bitu. Taki sposób pisania programu nie jest dobry. Bo jeżeli zdecydujemy się na zmiany w projekcie i na nowym schemacie dioda LED będzie przyłączona do innego wyprowadzenia niż poprzednio, to wtedy trzeba będzie w całym programie wszystkie instrukcje sterujące diodą LED odszukać i zmodyfikować. Lepiej odrazu pisać programy w taki sposób, aby w przyszłości, przy zmianie schematu, modyfikacja programu nie nastręczała wielkich problemów. I właśnie do tego celu może się przydać prepreprocesor. W przykładowym programie poniżej można wskazać port, do którego przyłączono diodę LED, edytując makrodefinicje na początku programu. Poleceniem #define zdefiniowany trzy nazwy: SET_OUT_LED, SET_LED, CLR_LED, które preprocesor zastąpi odpowiednim kodem. Nazwy te mogą być używane jak instrukcje programu, SET_LED włącza diodę LED, CLR_LED - wyłącza, SET_OUT_LED ustawia port, do którego przyłączono diodę LED, jako wyjście.

#include <avr/io.h> /* Początkowo diodę LED przyłączono do wyprowadzenia PD0 */ /* #define SET_OUT_LED DDRD |= (1<<PD0) #define SET_LED PORTD |= (1<<PD0) #define CLR_LED PORTD &= ~(1<<PD0) */ /* Schemat się zmienił, aktualnie dioda LED przyłączona jest do wyprowadzenia PB3 */ #define SET_OUT_LED DDRB |= (1<<PB3) #define SET_LED PORTB |= (1<<PB3) #define CLR_LED PORTB &= ~(1<<PB3) int main(void) { /* Ustawia PB3 jako wyjście */ /* Preprocesor zastąpi SET_OUT_LED instrukcją DDRB |= (1<<PB3) */ SET_OUT_LED; /* Jakiś kawałek kodu */ /* Zapala diodę LED */ /* Preprocesor zastąpi SET_LED instrukcją PORTB |= (1<<PB3) */ SET_LED; /* Jakiś kawałek kodu */ /* Gasi diodę LED */ /* Preprocesor zastąpi CLR_LED instrukcją PORTB &= ~(1<<PB3) */ CLR_LED; /* Dalsze instrukcje programu */

Istnieje możliwość tworzenia makrodefinicji z argumentami. Listę argumentów makra umieszcza się między parą nawiasów okrągłych (), podobnie jak w definicji funkcji. W przykładzie poniżej utworzone zostało użyteczne makro _BV(numer_bitu). W tekście programu każde wystąpienie _BV(numer_bitu) preprocesor zastąpi wartością (1<<numer_bitu), czyli jedynką przesuniętą w lewo o ilość pozycji podaną jako argument makra.

#include <avr/io.h> /* Makrodefinicja z argumentem */ #define _BV(bit) (1 << (bit)) int main(void) { /* Preprocesor zastąpi _BV(3) wyrażeniem (1 << (3))*/ PORTB |= _BV(3);

Nie ma potrzeby samemu definiować _BV(bit), jest już takie makro zdefiniowane w jednym z plików dołączanych poleceniem:

#include <avr/io.h>

Z pomocą instrukcji preprocesora można wskazać kompilatorowi, które fragmenty kodu programu mają być wzięte pod uwagę w procesie kompilacje, a które fragmenty kodu mają być pominięte. Nazywa się to kompilacją warunkową i używane są do tego celu polecenia: #if, #elif, #else, #endif oraz #ifdef, #ifndef. Z poleceń tych tworzy się konstrukcje w rodzaju:

#if WARUNEK_1 /* Fragment kodu włączany do porgramu jeśli spełniony jest WARUNEK_1*/ #elif WARUNEK_2 /* Fragment kodu włączany do porgramu jeśli spełniony jest WARUNEK_2*/ #else /* Fragment kodu włączany do programu jeśli żaden w warunków nie został spełniony*/ #endif

Gdzie #elif i #else nie muszą wystąpić. I tak, jeśli spełniony jest warunek stojący zaraz po #if lub #elif, wtedy następne linie kodu, aż do wystąpienia #endif, #elif lub #else, są włączane do programu. Jeśli żaden z warunków nie jest spełniony, wtedy częścią programu staje się fragment kodu między #else i #endif. W przykładzie poniżej zdefiniowano nazwę F_CPU z przypisaną częstotliwość pracy mikrokontrolera; zależnie od częstotliwości preprocesor włącza do programu jeden z trzech fragmentów kodu.

//#define F_CPU 1000000UL #define F_CPU 4000000UL int main() { #if F_CPU <= 1000000 /* Fragment kodu włączany do programu jeżeli F_CPU <= 1MHz */ #elif 1000000 < F_CPU && F_CPU <= 8000000 /* Fragment kodu włączany do programu jeżeli 1MHz < F_CPU <= 8MHz */ #else /* Fragment kodu włączany do programu jeżeli F_CPU > 8MHz*/ #endif

W warunku po #if i #elif można wstawiać wyrażenie defined(NAZWA), wyrażenie to przyjmuje wartość logiczną PRAWDA, jeżeli dana nazwa została wcześnie zdefiniowana poleceniem #define; w przeciwnym przypadku wyrażenie to posiada wartość logiczną FAŁSZ.

#define MICRO intmain() { #if defined(MICRO) /* Ten fragment kodu zostanie włączony do programu, bo wcześniej zdefiniowano nazwę MICRO */ #else /* Ten fragment kodu byłby włączony do programu, gdyby nie zdefiniowano nazwy MICRO */ #endif

Istnieją także polecenia #ifdef i #ifndef. Kod występujący po #ifdef NAZWA jest włączany do programu, jeżeli wcześniej zdefiniowano nazwę poleceniem #define NAZWA. Natomiast kod #ifndef NAZWA jest włączany do programu, jeżeli nie zdefiniowano wcześniej nazwy poleceniem #define NAZWA. Polecenie #if z warunkiem defined(NAZWA) można zastąpić #ifdef NAZWA.

Aby zapobiec sytuacji, że jakiś plik mógłby być włączony poleceniem #include wielokrotnie, treść dołączanego pliku umieszcza się między parą poleceń #ifndef i #endif.

/* Zawartość przykładowego pliku dołączanego poleceniem #include "plik.h"*/ /* Jeżeli wcześniej nazwa PLIK została zdefiniowana poleceniem #define PLIK, to dalsza część pliku nie zostanie włączona do programu */ #ifndef PLIK /* Definicja nazwy PLIK, aby zapobiec wielokrotnemu dołączaniu plik.h */ #define PLIK /* Deklaracje funkcji, makrodefinicje itp.*/ #endif /* KONIEC PLIKU */

Podział kodu źródłowego programu na osobno kompilowane pliki

Dotychczas, we wszytkich programikach naszego kursu, całość kodu źródłowego umieszczana była w jednym pliku, jak w poniższym przykładzie.

//----------------------------------------------------------- // plik "main.c" //----------------------------------------------------------- #include <avr/io.h> /* Definicje zmiennych globalnych */ char tytul[]="KURS AVR-GCC, cz.5"; int czesc = 5; /* Definicje kilku funkcji */ void funkcja_1(int a, int b) { } int funkcja_2(void) { funkcja_4(tytul); funkcja_1(czesc, 1); } double funkcja_3(double x) { funkcja_1(2, 6); funkcja_2(); } char funkcja_4(char s[]) { } /* Główna funkcja programu */ int main(void) { funkcja_1(czesc, 6); funkcja_2(); funkcja_3(3.14); funkcja_4(tytul); }

Ale istnieje możliwość podziału kodu źródłowego programu na mniejsze fragmenty umieszczane w osobno kompilowanych plikach. Przykładowo możemy podzielić nasz programik w następujący sposób: funkcja_1 i funkcja_2 do pliku "file1.c"; funkcja_3 i funkcja_4 do pliku "file2.c"; zmienne globalne i funkcja main do pliku "main.c"

//----------------------------------------------------------- // plik "file1.c" //----------------------------------------------------------- #include <avr/io.h> /* Deklaracje zmiennych i funkcji zdefiniowanych poza plikiem "file1.c" */ extern char tytul[]; extern int czesc; char funkcja_4(char s[]); void funkcja_1(int a, int b) { } int funkcja_2(void) { funkcja_4(tytul); funkcja_1(czesc,1); } //----------------------------------------------------------- // plik "file2.c" //----------------------------------------------------------- /* Deklaracje funkcji zdefiniowanych poza plikiem "file2.c" */ void funkcja_1(int , int ); int funkcja_2(void); double funkcja_3(double x) { funkcja_1(2,6); funkcja_2(); } char funkcja_4(char s[]) { return 0; } //----------------------------------------------------------- // plik "main.c" //----------------------------------------------------------- #include <avr/io.h> /* Definicje zmiennych globalnych */ char tytul[]="KURS AVR-GCC, cz.5"; int czesc = 5; /* Deklaracje funkcji zdefiniowanych poza plikem "main.c" */ void funkcja_1(int , int ); int funkcja_2(void); double funkcja_3(double); char funkcja_4(char []); /* Główna funkcja programu */ int main(void) { funkcja_1(czesc,6); funkcja_2(); funkcja_3(3.14); funkcja_4(tytul); }

Aby mieć możliwość użycia funkcji zdefiniowanej w innym pliku, należy gdzieś wcześniej w programie umieścić deklarację tej funkcji. Deklaracja funkcji wygląda prawie tak samo jak pierwsza linia definicji funkcji zakończona średnikiem.

typ nazwa_funkcja( typ_arg_1, typ_arg_2, ... );

Przypominam, że definicja oznacza tworzenie funkcji(zmiennej), natomiast deklaracja jedynie informuje kompilator jakiego typu wartość funkcja zwraca i jakich oczekuje argumentów. Zatem definicja funkcji jest jednocześnie jej deklaracją, ale deklaracja nie definiuje(tworzy) funkcji. I jak wcześniej pisałem, żeby mieć możliwość użycia funkcji zdefiniowanej(utworzonej) w oddzielnie kompilowanym pliku, należy wcześniej przed użyciem tę funkcje zdeklarować - właśnie, żeby poinformować kompilator jakiego typu wartość funkcja zwraca i jakich oczekuje argumentów.

Zmienne mogą być definiowane wewnątrz funkcji(zmienne lokalne) albo poza wszystkimi funkcjami(zmienne globalne), o tym pisałem w poprzedniej części kursu. Zmienne definiowane wewnątrz funkcji tworzone są w chwili wywołania funkcji i przestają istnieć w  momencie powrotu z funkcji, przechowywane dane są tracone. Przy każdym wywołaniu funkcji zmienne tworzone są od nowa. Jeśli zależy nam żeby zmienna deklarowane w funkcji istniała przez cały okres działania programu i dane w zmiennej nie były tracone po wyjściu z funkcji, to należy deklaracje zmiennej poprzedzić słówkiem static; takie zmienne nazywa się statycznymi. Zmienne deklarowane na początku pliku, poza wszystkimi funkcjami pliku istnieją przez cały czas działania programu i są dostępne we wszystkich funkcjach w pliku. Aby mieć dostęp do zmiennej globalnej zdefiniowanej w osobno kompilowanym pliku, należy przed użyciem wcześniej tę zmienną zdeklarować; dodatkowo przed taką deklaracją należy wstawić słówko extern.

extern typ_zmiennej nazwa_zmiennej;

Z kolei żeby ograniczyć zasięg widoczności takiej zmiennej jedynie do pliku, w którym została zdefiniowana, poprzeda się deklaracje zmienej słówkiem static.

static typ_zmiennej nazwa_zmiennej;

Podobnie jeśli definicję funkcji poprzedzimy słówkiem static, funkcja ta będzie dostępna tylko w tym jednym pliku, w którym została zdefiniowana

Zwykle deklaracje funkcji umieszcza się w osobnych plikach, tzw. plikach nagłówkowych z rozszerzeniem .h I wtedy, aby móc wykorzystać funkcję zdefiniowaną w innym pliku, dołącza się plik nagłówkowy zawierający definicję tej funkcji, wstawiając gdzieś na początku instrukcję preprocesora #include "nazwa.h"

//---------------------------------------------------------- // plik "file1.c" //---------------------------------------------------------- #include <avr/io.h> /* Dołącza plik zawierający deklaracje funkcji i zmiennych zdefiniowanych poza plikiem "file1.c" */ #include "rozne.h" void funkcja_1(int a, int b) { } int funkcja_2(void) { funkcja_4(tytul); funkcja_1(czesc,1); } //---------------------------------------------------------- // plik "file2.c" //---------------------------------------------------------- /* Dołącza plik zawierający deklaracje funkcji i zmiennych zdefiniowanych poza plikiem "file2.c" */ #include "rozne.h" double funkcja_3(double x) { funkcja_1(2,6); funkcja_2(); return 0; } char funkcja_4(char s[]) { } //---------------------------------------------------------- // plik "rozne.h" //---------------------------------------------------------- // Deklaracje kilku funkcji i zmniennych /* Poniższe polecenia preprocesora zapobiegną przypadkowi, w którym "rozne.h" mógłby być kilkakrotnie włączony do jednedo pliku */ #ifndef ROZNE_FUNKCJE #define ROZNE_FUNKCJE extern char tytul[]; extern int czesc; void funkcja_1(int, int); int funkcja_2(void); double funkcja_3(double); char funkcja_4(char []); #endif //--------------------------------------------------------- // plik "main.c" //--------------------------------------------------------- #include <avr/io.h> /* Dołącza plik zawierający deklaracje funkcji i zmiennych zdefiniowanych poza plikiem "main.c" */ #include "rozne.h" /* Definicje zmiennych globalnych */ char tytul[]="KURS AVR-GCC, cz.5"; int czesc = 5; /* Definicje funkcji */ static void funkcja(void) { } /* Główna funkcja programu */ int main(void) { funkcja_1(czesc,6); funkcja_2(); funkcja_3(3.14); funkcja_4(tytul); }

Aby skompilować nasz pokrojony programik, potrzebujemy utworzyć w katalogu projektu odpowiedni plik makefile. Robimy to w podobny sposób, jak poprzednio, z pomocą programu MFile. Wpierw klikamy w menu opcję Makfile->Main file name.. i w  okienku które się pojawi wpisujemy nazwę pliku "main" (wpisujemy nazwę pliku bez rozszerzenia .c) Dodatkowo należy wybrać z menu opcję Makefile->C/C++source files(s) i w okienku wpisać nazwy plików "file1.c" i "file2.c" (tym razem należy wpisać nazwy plików wraz z rozszerzeniami .c )

mfile
Okno programu MFile. Wpisujemy nazyw wszytkich plków *.c projektu. Kliknij w obrazek, żeby zobaczyć całość.

Przyjrzyjmy się teraz przebiegowi kompilacji naszego pokrojonego programiku. Program make wywołuje kompilator avr-gcc cztery razy. Wpierw wszystkie pliki projektu z rozszerzeniem .c (main.c, file1.c, file2.c) są pojedynczo kompilowane, w  wyniku powstają trzy plik pośrednie: main.o, file1.o, file2.o Za czwartym razem avr-gcc uruchamiany jest żeby połączyć wymienione pliki pośrednie w całość; tworzony jest plik wynikowy main.elf Na koniec uruchamiany jest program narzędziowy avr-objdump, który tworzy z pliku wynikowego main.elf pliki dla programatora main.hex i main.epp. Warto zauważyć, że przy kolejnej próbie kompilacji projektu program make kompiluje jedynie te plik .c, które zostały w międzyczasie edytowanie.

avrgcc
Etapy kompilacja projekty złożnego z kilku plików *.c Kliknij w obrazek, żeby obejrzeć całość.

Wyświetlacz alfanumeryczny LCD.

Dla uruchamiania przykładowych programików będzie potrzebny wyświetlacz alfanumeryczny LCD, moduł z układem HD44780. Ale bez obaw, takie wyświetlacze są łatwo dostępne i  nie kosztują drogo. Ja przyłączyłem do AVRa moduł widoczny na fotografii poniżej, na którym można wyświetlić dwie linie tekstu po szesnaście znaków, moduły 2X16 są chyba najczęściej spotykane. Do tego celu można również wykorzystać wyświetlacze o dwudziestu lub czterdziestu znakach w linii, lecz w takim przypadku pewnie potrzebne będą drobne modyfikacje w kodzie przykładów.

obrazek
Moduł wyświetlacza LCD 2X16 wykorzystany przy uruchomieniu przykładów.

W kursie nie będę szczegółowo tłumaczy jak programować tego typu wyświetlacz, jest to temat na osobny artykuł, który w przyszłości napiszę i umieszczę na stronie w dziale artykuły. W zamian przygotowałem zestaw gotowych funkcji do obsługi wyświetlaczy LCD ze sterownikiem HD44780. W przykładach, w których będzie wykorzystywany wyświetlacz, trzeba będzie skopiować do katalogu projektu plik: hd44780.c, hd44780.h Plik hd44780.c zawiera definicje funkcji do obsługi wyświetlaczy, zaś w pliku hd44780.h znajdują się deklaracje tych funkcji, makrodefinicje przypisujące sygnały wyświetlacza do wybranych wyprowadzeń AVRa oraz kilka przydatnych makroinstrukcji. Dalej w tekści, przy opisie uruchamianych przykładów, objśnię jak tych funkcji używać.

Poniżej na schemacie pokazane jest w jaki sposób przyłączyłem wyświetlacz do AVRa atmega16.

obrazek
Schemat 5.1 Schemat przyłączenia wyświetlacza LCD(hd44780) do AVRa ATMEGA16.

Dla sterowania wyświetlaczem potrzebne jest siedem linii we/wy mikrokontrolera: trzy linie sterujące: RS, RW, E i cztery linie danych D4,D5,D5,D7. Będziemy programować wyświetlacz w trybie 4-bitowym (tylko cztery linie danych), wyprowadzenia wyświetlacza D0, D1, D2 ,D3 nie będą wykorzystywane. Ja przyłączyłem wyświetlacz do portów PA0..PA6 uC atmega16, ale nic nie stoi na przeszkodzie aby wykorzystać dowolne siedem linii we/wy AVRa. W tym celu należy zmodyfikować makrodefinicje w poniższym fragmencie pliku hd44780.h

/* RS */ #define SET_OUT_LCD_RS DDRA |= _BV(PA0) #define SET_LCD_RS PORTA |= _BV(PA0) #define CLR_LCD_RS PORTA &= ~_BV(PA0) /* RW */ #define SET_OUT_LCD_RW DDRA |= _BV(PA1) #define SET_LCD_RW PORTA |= _BV(PA1) #define CLR_LCD_RW PORTA &= ~_BV(PA1) /* E */ #define SET_OUT_LCD_E DDRA |= _BV(PA2) #define SET_LCD_E PORTA |= _BV(PA2) #define CLR_LCD_E PORTA &= ~_BV(PA2) /* D4 */ #define SET_OUT_LCD_D4 DDRA |= _BV(PA3) #define SET_IN_LCD_D4 DDRA &= ~_BV(PA3) #define SET_LCD_D4 PORTA |= _BV(PA3) #define CLR_LCD_D4 PORTA &= ~_BV(PA3) #define IS_SET_LCD_D4 PINA & _BV(PA3) /* D5 */ #define SET_OUT_LCD_D5 DDRA |= _BV(PA4) #define SET_IN_LCD_D5 DDRA &= ~_BV(PA4) #define SET_LCD_D5 PORTA |= _BV(PA4) #define CLR_LCD_D5 PORTA &= ~_BV(PA4) #define IS_SET_LCD_D5 PINA & _BV(PA4) /* D6 */ #define SET_OUT_LCD_D6 DDRA |= _BV(PA5) #define SET_IN_LCD_D6 DDRA &= ~_BV(PA5) #define SET_LCD_D6 PORTA |= _BV(PA5) #define CLR_LCD_D6 PORTA &= ~_BV(PA5) #define IS_SET_LCD_D6 PINA & _BV(PA5) /* D7 */ #define SET_OUT_LCD_D7 DDRA |= _BV(PA6) #define SET_IN_LCD_D7 DDRA &= ~_BV(PA6) #define SET_LCD_D7 PORTA |= _BV(PA6) #define CLR_LCD_D7 PORTA &= ~_BV(PA6) #define IS_SET_LCD_D7 PINA & _BV(PA6)

Przykładowo, jeżeli linia sygnału D7 wyświetlacza ma być przyłącza do portu PD0 AVRa, wtedy należy zmodyfikować w poniższym fragmęcie pliku hd44780.h to, co zaznaczone jest na czerwono, czyli nazwę portu (A,B,C,D) i numer bitu.

/* D7 */ #define SET_OUT_LCD_D7 DDRD |= _BV(PD0) #define SET_IN_LCD_D7 DDRD &= ~_BV(PD0) #define SET_LCD_D7 PORTD |= _BV(PD0) #define CLR_LCD_D7 PORTD &= ~_BV(PD0) #define IS_SET_LCD_D7 PIND & _BV(PD0)

Termometr cyfrowy DS18B20

Obok wyświetlacza i przycisków przyłączymy do AVRa także termometr cyfrowy DS18B20. Wszystkie potrzebne informacje na temat układu DS18B20 można znaleźć w jego karcie katalogowej ds18b20.pdf

obrazek
Termometr cyfrowy ds18b20

Układ scalony DS18B20 jest czujnikiem cyfrowym z interfejsem 1-wire, mikrokontroler komunikuje się z DS18B20 wykorzystując tylko jedną linię we/wy.

obrazek
Schemat 5.2 Schemat przyłączenia termometru cyfrowego ds18b20 do AVRa ATMEGA16.

W tej części kursu jeszcze nie będę tłumaczył jak działa magistrala 1-wire i jak programować układ ds18b20, w zamian przygotowałem gotowy zestaw funkcji - minimum kodu do odczytu wartości temperatury z ds18b20. W przykładach, w których będzie wykorzystywany termometr ds18b20 należy do katalogu projektu skopiować pliki: ds18b20.h i ds18b20.c. Ja przyłączyłem układ ds18b20 do wyprowadzenia PD7 AVRa atmega16. Ale można wykorzystać dowolny port AVRa, w tym celu należy zmodyfikować makrodefinicje w poniższym fragmencie pliku ds18b20.h Wystarczy odpowiednio zmienić, zaznaczone kolorem czerwony, nazwę portu(A,B,C,D) i numer bitu (0..7).

/* DS18B20 przyłączony do portu PD7 AVRa */ #define SET_ONEWIRE_PORT PORTD |= _BV(7) #define CLR_ONEWIRE_PORT PORTD &= ~_BV(7) #define IS_SET_ONEWIRE_PIN PIND & _BV(7) #define SET_OUT_ONEWIRE_DDR DDRD |= _BV(7) #define SET_IN_ONEWIRE_DDR DDRD &= ~_BV(7)

Sprzężenie AVRa z komputerem PC poprzez port szeregowy

Kolejny schemat przedstawia sposób połączenia portu szeregowego AVRa atmega16 z interfejsem RS232C komputera PC.

schemat
Schemat 5.3 Schemat połączenia portu szeregowego AVRa atmega16 z interfejsem rs232c komputera PC. Kliknij w obrazek, żeby powiększyć.

Potrzebne są: złącze DB-9 żeńskie, przewód trójżyłowy (około 60cm), układ scalony MAX232 i kilka kondensatorów jak na schemacie. Do połączenia portu szeregowego mikrokontrolera z portem szeregowym rs232 komputera PC konieczny jest konwerter napięć RS232C<=>TTL - na przykład układ scalony MAX232. Ja umieściłem MAX232 i współpracujące z nim kondensatory na osobnej płytce.

obrazek
Przewód łączący AVRa z komputerem PC i na osobnej płytce układ MAX232 wraz ze współpracującymi z nim kondensatorami.

Niektóre komputery, te nowsze, mogą nie posiadać portu rs232c. Brak ten można obejść stosując adapter, przejściówkę z USB na RS232. Taka przejściówka kosztuje niewiele, a  może być bardzo użyteczna, szczególnie do laptoka :)

Przykłady do uruchomienia

Przygotowałem cztery przykładowe programiki do uruchamiania jako ćwiczenia. Starałem się maksymalnie uprościć kod przykładów, zgodnie z zasadą: dobry przykład to krótki przykład :) Ala zalecam przed przystąpieniem do uruchamiania przykładów chociaż przejrzeć artykuł, wtedy nikt nie powinien mieć trudności ze zrozumieniem jak działają.

Przykład pierwszy - Powitanie

W pierwszym przykładzie program wypisuje na ekranie wyświetlacza kilka słów powitania.

obrazek
Animacja pokazuje efekt działania programu "Powitanie"

Żeby skompilować program, należy do katalogu projektu skopiować trzy zamieszczone poniżej pliki: main.c, hd44780.h, hd44780.c W pliku "main.c" znajduje się kod naszego przykładu.

/*
   Plik main.c

   KURS AVR-GCC cz.5
   Wyświetlacz alfanumeryczny LCD HD44780
   (schemat i opis działania w artykule)   
   
   układ atmega16 (1MHz)
*/

#include <avr/io.h>
#include <util/delay.h>
/* Wstawia w tym miejscu zawartość 
pliku hd44780.h*/
#include "hd44780.h"


int main(void)
{
    /* Napisy przechowujemy w tablicach */
    char str1[] = "KURS";
    char str2[] = " AVR-GCC";
    char str3[] = "cz.5";

    /* Funkcja inicjalizuje wyświetlacz*/
    lcd_init();
    /* Włącza wyświetlanie */
    LCD_DISPLAY(LCDDISPLAY);

    while(1)
    {    
        /* Czyści cały ekran */
        LCD_CLEAR;            

        /* Ustawia kursor w pozycji: 
        pierwszy wiersz, szósta kolumna */
        LCD_LOCATE(5,0);

        /* Wysyła do wyświetlacza jeden znak*/
        LCD_WRITE_DATA('S');
        _delay_ms(200);

        LCD_WRITE_DATA('i');
        _delay_ms(200);

        LCD_WRITE_DATA('e');
        _delay_ms(200);

        LCD_WRITE_DATA('m');
        _delay_ms(200);    

        LCD_WRITE_DATA('k');
        _delay_ms(200);

        LCD_WRITE_DATA('a');
        _delay_ms(200);

        LCD_WRITE_DATA('!');
        _delay_ms(2000);    

        LCD_CLEAR;

        LCD_LOCATE(2,0);

        /* Funkcja lcd_puts wysyła do 
        wyświetlacza ciąg  znaków */
        lcd_puts(str1);
        _delay_ms(800);    

        lcd_puts(str2);
        _delay_ms(800);    

        LCD_LOCATE(6,1);
        lcd_puts(str3);    

        _delay_ms(2000);
        LCD_CLEAR; 

        LCD_LOCATE(1,0);
        /* Jako argumentu funkcji można 
           wstawić stałą napisową */
        lcd_puts("Programy");

        _delay_ms(800);

        LCD_LOCATE(4,1);
        lcd_puts("z tekstem:)");
        
        /* Czeka 2.5 sek. */
        _delay_ms(2500);    
        
    }
    return 0;
}
Listing 5.1 Powitanie

Plik hd44780.c zawiera zestaw funkcji do obsługi wyświetlacza.

/*
  Plik hd44780.c

  Definicje kilku funkcji do obsługi alfanumerycznego
  wyświetlacza LCD HD44780
*/


#include<avr/io.h>
#include<util/delay.h>
#include "hd44780.h"

/*--------------------------------------------------------*/
/* Zapis danej lub instrukcji */

void WriteToLCD (unsigned char v,unsigned char  rs)
{
    unsigned char bf;

    SET_OUT_LCD_D4;
    SET_OUT_LCD_D5;
    SET_OUT_LCD_D6;
    SET_OUT_LCD_D7;

    if(v&0x10) SET_LCD_D4; else CLR_LCD_D4;
    if(v&0x20) SET_LCD_D5; else CLR_LCD_D5;
    if(v&0x40) SET_LCD_D6; else CLR_LCD_D6;
    if(v&0x80) SET_LCD_D7; else CLR_LCD_D7;
 
    CLR_LCD_E;
    if(rs) SET_LCD_RS;else CLR_LCD_RS;
    CLR_LCD_RW;

    LCD_NOP;
    SET_LCD_E;
    LCD_NOP; 
    CLR_LCD_E;
    LCD_NOP;
 
    if(v&0x01) SET_LCD_D4; else CLR_LCD_D4;
    if(v&0x02) SET_LCD_D5; else CLR_LCD_D5;
    if(v&0x04) SET_LCD_D6; else CLR_LCD_D6;
    if(v&0x08) SET_LCD_D7; else CLR_LCD_D7;
 
    LCD_NOP;
    SET_LCD_E;
    LCD_NOP; 
    CLR_LCD_E;
    LCD_NOP;
 
    SET_IN_LCD_D4;
    SET_IN_LCD_D5;
    SET_IN_LCD_D6;
    SET_IN_LCD_D7;

    CLR_LCD_RS;
    SET_LCD_RW;
    SET_LCD_D7;


/* Przydałby się pełny odczyt */
    do
    {
        LCD_NOP;
        SET_LCD_E;
        LCD_NOP;
        bf = IS_SET_LCD_D7;
        CLR_LCD_E;
        LCD_NOP;
        SET_LCD_E;
        LCD_NOP;
        LCD_NOP;
        CLR_LCD_E;
        
    }while( bf );
}


/*--------------------------------------------------------*/
/* Funkcja odczytuje adres i flage zajetosci */

unsigned char ReadAddressLCD ( void)
{
    unsigned char g = 0 ;

    CLR_LCD_RS;
    SET_LCD_RW; 

    SET_IN_LCD_D4;
    SET_IN_LCD_D5;
    SET_IN_LCD_D6;
    SET_IN_LCD_D7;

    LCD_NOP;
    SET_LCD_E;
    LCD_NOP;

    if(IS_SET_LCD_D4) g+=16;
    if(IS_SET_LCD_D4) g+=32;
    if(IS_SET_LCD_D4) g+=64;
    if(IS_SET_LCD_D4) g+=128;
 
    CLR_LCD_E;
    LCD_NOP;
    SET_LCD_E;  
    LCD_NOP;
  
    if(IS_SET_LCD_D4) g+=8;
    if(IS_SET_LCD_D4) g+=4;
    if(IS_SET_LCD_D4) g+=2;
    if(IS_SET_LCD_D4) g+=1;
  
    CLR_LCD_E; 

    return  g ;
}


/*---------------------------------------------------------*/
/* Inicjalizacja wyświetlacza */

void lcd_init(void)
{
    _delay_ms(31);    
   
    SET_OUT_LCD_RS;
    SET_OUT_LCD_RW;
    SET_OUT_LCD_E;
    SET_OUT_LCD_D4;
    SET_OUT_LCD_D5;
    SET_OUT_LCD_D6;
    SET_OUT_LCD_D7;

    CLR_LCD_E;
    CLR_LCD_RS;
    CLR_LCD_RW;
    SET_LCD_D4;
    SET_LCD_D5;
    CLR_LCD_D6;
    CLR_LCD_D7;        
  
    LCD_NOP;
    SET_LCD_E;
    LCD_NOP; 
    CLR_LCD_E;
    LCD_NOP;
    _delay_ms(10);

    LCD_NOP;
    SET_LCD_E;
    LCD_NOP; 
    CLR_LCD_E;
    LCD_NOP;
    _delay_ms(2);

    LCD_NOP;
    SET_LCD_E;
    LCD_NOP; 
    CLR_LCD_E;
    LCD_NOP;
    _delay_ms(2);

    CLR_LCD_D4;
    LCD_NOP;
    SET_LCD_E;
    LCD_NOP; 
    CLR_LCD_E;
    LCD_NOP;
    _delay_us(80);

    WriteToLCD (0x28 , LCDCOMMAND) ;
    LCD_DISPLAY(0) ;
    LCD_CLEAR ;
    LCD_ENTRY_MODE(LCDINCREMENT) ;
}


/*--------------------------------------------------------*/
/* Wyswietla tekst na aktualnej pozycji kursora */

void lcd_puts(char *str)
{
    unsigned char i =0;

    while( str[i])
        LCD_WRITE_DATA(str[i++]) ;
}
Listing 5.2 Zestaw funkcje do obsług wyświetlacza LCD (hd44780)

W pliku hd44780.h znajdują się deklaracje funkcji do obsługi wyświetlacza, makrodefinicje przypisujące sygnały wyświetlacza do wybranych wyprowadzeń AVRa oraz kilka przydatnych makroinstrukcji.

/*
Plik hd44780.h
*/

#ifndef LCD_HD44780
#define LCD_HD44780

/* RS */
#define SET_OUT_LCD_RS  DDRA  |=  _BV(PA0)
#define SET_LCD_RS      PORTA |=  _BV(PA0)
#define CLR_LCD_RS      PORTA &= ~_BV(PA0)

/* RW */
#define SET_OUT_LCD_RW  DDRA  |=  _BV(PA1)
#define SET_LCD_RW      PORTA |=  _BV(PA1)
#define CLR_LCD_RW      PORTA &= ~_BV(PA1)

/* E */
#define SET_OUT_LCD_E   DDRA  |=  _BV(PA2)
#define SET_LCD_E       PORTA |=  _BV(PA2)
#define CLR_LCD_E       PORTA &= ~_BV(PA2)

/* D4 */
#define SET_OUT_LCD_D4  DDRA  |=  _BV(PA3)
#define SET_IN_LCD_D4   DDRA  &= ~_BV(PA3)
#define SET_LCD_D4      PORTA |=  _BV(PA3)
#define CLR_LCD_D4      PORTA &= ~_BV(PA3)
#define IS_SET_LCD_D4   PINA  &   _BV(PA3)

/* D5 */
#define SET_OUT_LCD_D5  DDRA  |=  _BV(PA4)
#define SET_IN_LCD_D5   DDRA  &= ~_BV(PA4)
#define SET_LCD_D5      PORTA |=  _BV(PA4)
#define CLR_LCD_D5      PORTA &= ~_BV(PA4)
#define IS_SET_LCD_D5   PINA  &   _BV(PA4)

/* D6 */
#define SET_OUT_LCD_D6  DDRA  |=  _BV(PA5)
#define SET_IN_LCD_D6   DDRA  &= ~_BV(PA5)
#define SET_LCD_D6      PORTA |=  _BV(PA5)
#define CLR_LCD_D6      PORTA &= ~_BV(PA5)
#define IS_SET_LCD_D6   PINA  &   _BV(PA5)

/* D7 */
#define SET_OUT_LCD_D7  DDRA  |=  _BV(PA6)
#define SET_IN_LCD_D7   DDRA  &= ~_BV(PA6)
#define SET_LCD_D7      PORTA |=  _BV(PA6)
#define CLR_LCD_D7      PORTA &= ~_BV(PA6)
#define IS_SET_LCD_D7   PINA  &   _BV(PA6)


#define LCD_NOP asm volatile("nop\n\t""nop\n\t" "nop\n\t" "nop\n\t" ::);



#define LCDCOMMAND 0
#define LCDDATA    1

#define LCD_LOCATE(x,y)  WriteToLCD(0x80|((x)+((y)*0x40)), LCDCOMMAND)

#define LCD_CLEAR              WriteToLCD(0x01, LCDCOMMAND)
#define LCD_HOME               WriteToLCD(0x02, LCDCOMMAND)

/* IDS */

#define LCDINCREMENT           0x02
#define LCDDECREMENT           0x00
#define LCDDISPLAYSHIFT        0x01

#define LCD_ENTRY_MODE(IDS)    WriteToLCD(0x04|(IDS), LCDCOMMAND)

/* BCD */
#define LCDDISPLAY             0x04
#define LCDCURSOR              0x02
#define LCDBLINK               0x01

#define LCD_DISPLAY(DCB)       WriteToLCD(0x08|(DCB), LCDCOMMAND)

/* RL */
#define LCDLEFT                0x00
#define LCDRIGHT               0x04

#define LCD_SHIFT_DISPLAY(RL)  WriteToLCD(0x18|(RL), LCDCOMMAND)
#define LCD_SHIFT_CURSOR(RL)   WriteToLCD(0x10|(RL), LCDCOMMAND)

#define LCD_CGRAM_ADDRESS(A)   WriteToLCD(0x40|((A)&0x3f), LCDCOMMAND)
#define LCD_DDRAM_ADDRESS(A)   WriteToLCD(0x80|((A)&0x7f), LCDCOMMAND)

#define LCD_WRITE_DATA(D)      WriteToLCD((D),LCDDATA)


void lcd_init(void);
void WriteToLCD(unsigned char v,unsigned char rs);
unsigned char ReadAddressLCD(void);
void lcd_puts(char *str);

#endif
Listing 5.3

Następnie należy utworzyć w katalogu projektu odpowiedni plik Makefile, z pomocą programu Mfile, tak jak robiliśmy to przy poprzednich przykładach. Dodatkowo należy wybrać z menu programu Mfile opcję Makefile->C/C++source files(s) i w okienku wpisać nazwę pliku: hd44780.c (nazwę pliku trzeba wpisać wraz z rozszerzeniem .c)

mfile
Okno programu MFile. Wpisujemy nazwy wszytkich plików *.c projektu. Kliknij w obrazek, żeby zobaczyć całość.

A jeszcze musimy wpisać w pliku Makefile częstotliwość sygnału taktującego procesor; częstotliwość wpisujemy ręcznie, gdyż brak takiej opcji w menu. Aby móc edytować treść tworzonego pliku Makefile trzeba zaznaczyć w menu "Makefile" opcję "Enable Editing of Makefile"

Odnajdujemy w nowo tworzonym pliku Makefile fragment zaczynającym się od:

#Processor frequency

I wpisujemy częstotliwość sygnału taktującego procesor

F_CPU = 1000000
obrazek
Okno programu MFile. Wpisujemy ręcznie częstotliwość pracy mikrokontrolera.

Przypominam. Jeżeli w programie wykorzystywane są funkcje _delay_ms lub _delay_us, to należy dodać informację o częstotliwość sygnału taktującego procesor, inaczej funkcje te nie będą działać prawidłowo. Informację tę można przekazać umieszczając na początku każdego pliku z kodem makrodefinicję

#define F_CPU 1000000UL

Jednak znacznie wygodniej jest wspiać wczęstotliwość pracy procesora w pliku Makefile, raz dla wszytkich plików programu, tak, jak w naszym przykładzie. Ale jest to tylko informacja dla kompilatora, żeby faktycznie zmienić częstotliwość zegara z jakim uC atmega pracuje, trzeba zaprogramować tzw fusebity.

Celem tego przykładu jest pokazanie w jaki sposób napisać cokolwiek na ekranie wyświetlacza z użyciem funkcji zapisanych w pliku hd44780.c Pierwsza rzecz to należy gdzieś na początku pliku programu wstawić polecenie preprocesora #include dołączające plik hd44780.h zawierający deklaracje funkcji zdefiniowanych w pliku hd44780.c

#include "hd44780.h"

W kolejnym kroku, zanim zaczniemy pisać na wyświetlaczu, trzeba wywołać funkcję lcd_init, funkcja ta realizuje procedurę programowej inicjalizacji wyświetlacza i następnie przełącza interfejs wyświetlacza do trybu 4-bitowego. Po wykonaniu funkcji lcd_init wyświetlacz zostaje wygaszony. Włączyć wyświetlanie można z pomocą makroinstrukcji LCD_DISPLAY.

/* Funkcja inicjalizuje wyświetlacz*/ lcd_init(); /* Włącza wyświetlanie */ LCD_DISPLAY(LCDDISPLAY);

Z pomocą makra LCD_LOCATE wskazujemy pozycje na ekranie wyświetlacza (kolumnę i wiersz), gdzie zamierzamy coś napisać. Kolumny i wiersze liczone są zaczynając od zera.

/* szósta kolumna, pierwszy wiersz */ LCD_LOCATE(5,0);

Pojedyncze znaki można wysyłać do wyświetlacza wykorzystując makro LCD_WRITE_DATA.

/* Wysyła do wyświetlacza jeden znak*/ LCD_WRITE_DATA('S');

Po wykonaniu LCD_WRITE_DATA numer kolumny zwiększa się o jeden, więc jeśli wyślemy kilka znaków jeden za drugim, to zobaczymy na ekranie wyświetlacza napis. Napisy (ciągi znaków zakończone zerem) można wysyłać do wyświetlacza funkcją lcd_puts. Funkcja lcd_puts po prostu wysyła do wyświetlacza znak po znaku cały napis wywołując w pętli makro LCD_WRITE_DATA. Jako argument funkcji lcd_puts wstawia się nazwę tabeli z tekstem lub stałą napisową.

/* Funkcja lcd_puts wysyła do wyświetlacza ciąg znaków */ lcd_puts(str1); /* Jako argumentu funkcji można wstawić stałą napisową */ lcd_puts("Programy");

Makro LCD_CLEAR czyści cały ekran wyświetlacza i ustawia aktualny numer kolumny i wiersza na 0.

/* Czyści cały ekran */ LCD_CLEAR;

I jeszcze kursor, tzw. znak zachęty, zachęcający do prowadzania danych z klawiaturki. Kursor ma postać poziomej kreski wyświetlanej pod kratką, gdzie ma zostać wpisany kolejny znak. Kursor można pokazać uruchamiając makro LCD_DISPLAY.

/* Włącza wyświetlanie i kursor */ LCD_DISPLAY(LCDDISPLAY|LCDCURSOR);

W następnej części kursu pokażę jeszcze jak uzyskać na ekranie wyświetlacza polskie znaki z "ogonkami" ąćęńłóśźż oraz przedstawię kilka innych, ciekawych możliwości jakie oferują tego typu wyświetlacze.

Przykład drugi - Licznik owiec

Po prostu licznik. Licząc owce lub gwiazdy na niebie lub cokolwiek innego nie trudno o pomyłkę, zatem jak widać taki licznik może być szalenie użyteczny:) Obok wyświetlacza dołączyłem do AVRa trzy przyciski, pierwszy przycisk zwiększa licznik o jeden, drugi przycisk zmniejsza licznik o jeden, a trzeci przycisk zeruje licznik. Przyciski przyłączone są do portów PB0..PB2 AVRa.

obrazek
Animacja pokazuje sposób działa program "Licznik owiec". Pierwszy przycisk zwiększa licznik o jeden, drugi zmniejsza o jeden, a trzeci przycisk zeruje licznik.

Działanie programu jest bardzo proste, jest w pamięci zmienna całkowita (32 bity) - licznik. Każdorazowo przy zmianie stanu licznika wartość liczbowa w zmiennej zmieniana jest na ciąg znaków (kodów ASCII), który jest wysyłany do wyświetlacza.

Aby skompilować program, należy do katalogu projektu, obok pliku main.c, skopiować pliki: hd44780.h, hd44780.c i oczywiście stworzyć odpowiedni plik Makefile - tak jak w poprzednim przykładzie.

/*
   Plik "main.c"

   KURS AVR-GCC cz.5 (przykład nr. 2)
   Licznik owiec :)
   (schemat i opis działania w artykule)   
   
   uC atmega16 (1MHz)
*/

#include <avr/io.h>
#include <util/delay.h>
/* Dołącza deklaracje funkcji obsługujących 
   wyświetlacz */
#include "hd44780.h"


/* ZMIENNE GLOBALNE */

/* W tablicy będą formowane komunikaty 
   wysyłane do wyświetlacza */
unsigned char str1[17]="----------------";


/* DEFINICJE FUNKCJI */

/* Funkcja aktualizuje zawartość ekranu */
static void lcd(unsigned long int a)
{
    signed char i;
   
/* Zamiana 32 bitowej liczby bez znaku 
   na ciąg znaków ASCII */    
    for(i=12; i>=3; a/=10 ,i--) 
                str1[i] = a % 10 +'0';

/* Ustawia kursor w pierwszej kolumnie
   pierwszego wersza */    
    LCD_LOCATE(0,0);

/* Wysyła do wyświetlacza ciąg znaków z 
   tablicy str1 */
    lcd_puts(str1);
}


/* GŁÓWNA FUNKCJA */
int main(void)
{

  /* Zmienna przechowuje stan licznika */    
  unsigned long int n=0;

  /* PB0,PB2  wejściami z podciągnięciem do VCC */
  DDRB  = 0x00;
  PORTB = 0x07;
  
  /* Programowa inicjalizacja wyświetlacza */
  lcd_init();
  /* Włącza wyświetlanie */
  LCD_DISPLAY(LCDDISPLAY);  
  /* Czyści  ekran */
  LCD_CLEAR;            
  /* Wyświetla początkowy stan licznika */
  lcd(n);
  

  /* Główna pętla  */
  while(1)
  {
     /* Jeśli pierwszy przycisk wciśnięty */
     if(!(PINB & 0x01))
     {  
/* Czas na wygaśnięcie drgań styków przycisku*/     
        _delay_ms(80);
    /* Oczekuje na zwolnienie przycisku*/
        while(!(PINB & 0x01)) {}
/* Czas na wygaśnięcie drgań styków przycisku*/  
    _delay_ms(80);
        
    /* Zwiększa licznik o 1 */
        n++;
    /* Aktualizuje zawartość ekranu */
     lcd(n);
    }

    /* Jeśli drugi przycisk wciśnięty */
    else if(!(PINB & 0x02))
    {
        _delay_ms(80);
        while(!(PINB & 0x02)) {}
        _delay_ms(80);

    /* Zmniejsza licznik o 1 */
    n--;
    /* Aktualizuje zawartość ekranu */
     lcd(n);
    }
    /* Jeśli trzeci przycisk wciśnięty */
    else if(!(PINB & 0x04))
    {
        _delay_ms(80);
        while(!(PINB & 0x04)) {}
        _delay_ms(80);
        
    /* Zeruje licznik */
    n=0;
    /* Aktualizuje zawartość ekranu */
     lcd(n);
    }
  }

}
Listing 5.4 Licznik owiec

Przykład trzeci - termometr cyfrowy

W tym przykładzie, obok wyświetlacza LCD, dołączyłem do AVRa scalony termometr cyfrowy DS18B20. AVR komunikuje się z układem DS18B20 poprzez szeregową magistralę 1-wire. Termometr DS18B20 jest inteligentnym czujnikiem cyfrowym, w wyniku pomiaru otrzymujemy gotową wartość liczbową - temperaturę wyskalowaną w stopniach Celsjusza. Jak pisałem wcześniej, w tej części kursu nie będę jeszcze szczegółowo tłumaczył jak działa magistrala 1-wire i jak programować termometr DS18B20, jest to temat na oddzielny artykuł, w zamian przygotowałem gotowy zestaw funkcji z pomocą których odczytamy temperaturę z DS18B20.

obrazek
Animacja prezentuje działanie programu "Termometr cyfrowy"

Celem tego przykładu jest pokazanie sposobu użycia funkcji sprintf. W skrócie program działa w następujący sposób: Najpierw następuje odczyt wartości temperatury z czujnika DS18B20. Aktualna wartość temperatury przechowywana jest w zmiennej rzeczywistej (typ double) o nazwie 'temp'. Następnie funkcja standardowa sprintf zmienia wartość liczbową w zmiennej 'temp', na ciąg znaków i formuje w tablicy 'str' komunikat tekstowy. Dalej komunikat w tablicy 'str' wysyłany jest do wyświetlacza LCD. Odczyt temperatury i wyświetlenie wyniku wykonuje się w głównej pętli programu.

Żeby skompilować program, należy do katalogu projektu skopiować następujące pliki: main.c, hd44780.h, hd44780.c, ds18b20.h, ds18b20.c.

/*
   Plik "main.c"

   KURS AVR-GCC cz.5
   (xyz.isgreat.org)

   Termometr cyfrowy, przykład nr. 3
   (schemat i opis działania w artykule)
   atmega16 (1MHz)
*/

#include <stdio.h>
#include <avr/io.h>
#include <util/delay.h>
#include "hd44780.h"
#include "ds18b20.h"

/* W tablicy będą formowane komunikaty tekstowe
   wysyłane do wyświetlacza */
char str[17]="   Termometr    ";

int main(void)
{
  /* Zmienna przechowuje aktualną wartość temperatury */        
  double temp;
  /* W tablicy zapisywane będą dane odczytane z układu ds18b20 */
  unsigned char ds18b20_pad[9];
  
  /* Funkcja inicjalizuje wyświetlacz */
  lcd_init();
  /* Włącza wyświetlanie */
  LCD_DISPLAY(LCDDISPLAY);  
  /* Czyści  ekran */
  LCD_CLEAR;            
  
  /* Wyświetla tytuł */  
  LCD_LOCATE(0,0);
  lcd_puts(str);

  while(1)
  {
    /* Funkcja 'ds18b20_ConvertT' wysyła do układu ds18b20 
       polecenie pomiaru */      
     if(ds18b20_ConvertT())
    {

       /* 750ms - czas konwersji */
       _delay_ms(750);

      /* Odczyt z układu ds18b20, dane zapisywane są w tablicy ds18b20_pad. 
         Dwie pierwsze pozycje w tablicy to kolejno mniej znaczący bajt i bardziej 
     znaczący bajt wartość zmierzonej temperatury */            
       ds18b20_Read(ds18b20_pad);
          
      /* Składa dwa bajty wyniku pomiaru w całość. Cztery pierwsze bity mniej
         znaczącego bajtu to część ułamkowa wartości temperatury, więc całość
         dzielona jest przez 16 */       
       temp = ((ds18b20_pad[1] << 8) + ds18b20_pad[0]) / 16.0 ;
      
      /* Formułuje komunikat w tablicy 'str' */
       sprintf(str,"%4.1f\xdf""C", temp);

       LCD_LOCATE(5,1);
      /* Wysyła komunikat do wyświetlacza */
       lcd_puts(str);
    }
  }
}
Listing 5.5 Termometr cyfrowy
/*
   Plik ds18b20.h

   (xyz.isgreat.org)  
*/

#ifndef DS18B20_H
#define DS18B20_H

/* DS18B20 przyłączony do portu  PD7 AVRa  */
#define SET_ONEWIRE_PORT     PORTD  |=  _BV(7)
#define CLR_ONEWIRE_PORT     PORTD  &= ~_BV(7)
#define IS_SET_ONEWIRE_PIN   PIND   &   _BV(7)
#define SET_OUT_ONEWIRE_DDR  DDRD   |=  _BV(7)
#define SET_IN_ONEWIRE_DDR   DDRD   &= ~_BV(7)

unsigned char ds18b20_ConvertT(void);
int ds18b20_Read(unsigned char []);
void OneWireStrong(char);
unsigned char OneWireReset(void);
void OneWireWriteByte(unsigned char);
unsigned char OneWireReadByte(void);

#endif
Listing 5.6
/*
   Plik ds18b20.c
   (minimum kodu do odczytu temperatury z ds18b20)

   xyz.isgreat.org
*/

#include <avr/io.h>
#include <util/delay.h>
#include "ds18b20.h"


/**********************************************************/

unsigned char ds18b20_ConvertT(void)
{
  if (!OneWireReset()) return 0;

  OneWireWriteByte(0xcc); // SKIP ROM
  OneWireWriteByte(0x44); // CONVERT T

  return -1;
}

/***********************************************************/

int ds18b20_Read(unsigned char scratchpad[])
{
  unsigned char i;    

  if (!OneWireReset()) return 0;

  OneWireWriteByte(0xcc); // SKIP ROM
  OneWireWriteByte(0xbe); // READ SCRATCHPAD

  for(i=0; i<9; i++) scratchpad[i] = OneWireReadByte();
 
  return 1;
}

/**********************************************************/

void OneWireStrong(char s)
{
  if (s)
  {
     SET_ONEWIRE_PORT; 
     SET_OUT_ONEWIRE_DDR; 
  }
  else
  {
     SET_IN_ONEWIRE_DDR; 
  }
}

/**********************************************************/

unsigned char OneWireReset()
{
  CLR_ONEWIRE_PORT; 

  if (!(IS_SET_ONEWIRE_PIN)) return 0;  

  SET_OUT_ONEWIRE_DDR; 
  _delay_us(500);
  SET_IN_ONEWIRE_DDR; 
  _delay_us(70);

  if(!(IS_SET_ONEWIRE_PIN))
  {
    _delay_us(500);
    return(1);
  }

  _delay_us(500);

return(0);
}

/**********************************************************/

void OneWireWriteByte(unsigned char byte)
{
   unsigned char i;

   CLR_ONEWIRE_PORT; 

   for (i=0; i<8; i++)
   {
     SET_OUT_ONEWIRE_DDR; 

     if (byte & 0x01)
     {
       _delay_us(7);
       SET_IN_ONEWIRE_DDR; 
       _delay_us(70);
     }
     else
     {
        _delay_us(70);
        SET_IN_ONEWIRE_DDR; 
        _delay_us(7);
     }

     byte >>= 1;
   }
}

/***********************************************************/

unsigned char OneWireReadByte(void)
{
  unsigned char i, byte = 0;

  SET_IN_ONEWIRE_DDR; 
  
  for (i=0; i<8; i++)
  {
     SET_OUT_ONEWIRE_DDR; 
     _delay_us(7);
     SET_IN_ONEWIRE_DDR; 
     _delay_us(7);
     byte >>= 1;
     
     if(IS_SET_ONEWIRE_PIN) byte |= 0x80;

     _delay_us(70);
  }

  return byte;
}

Listing 5.7 Minimum kodu do odczytu temperatury z ds18b20

Następnie należy utworzyć w katalogu projektu odpowiedni plik Makefile, tak jak robiliśmy to przy poprzednich przykładach. Dodatkowo należy wybrać z menu programu Mfile opcję Makefile->C/C++source files(s) i w okienku, które się pojawi wpisać nazwy plików: hd44780.c ds18b20.c (wpisujemy same nazwy plików źródłowych, bez ścieżki)

obrazek
MFile-edytor plików Makefile. Wpisujemy nazwy wszytkich plików *.c projektu.

Tak jak pisałem wcześniej, domyślnie funkcje printf i sprintf nie obsługują liczb zmiennopozycyjnych. Zatem trzeba też w menu "Makefile->printf() options" zaznaczyć opcję "floating point".

obrazek
MFile-edytor plików Makefile. Opcja "floating point" włącza dla funkcji: printf, sprintf obsługę zmiennych zmiennopozycyjnych.

Tu warto zauważyć, że użycie w programie funkcji 'sprintf' lub 'printf', szczególnie z opcją 'floating point', skutkuje znacznym wzrostem długości kodu wynikowego. A i jeszcze koniecznie musimy wpisać w pliku Makefile częstotliwość sygnału taktującego procesor, tak jak opisałem to w pierszym przykładzie.

Przykład czwarty - Transmisja szeregowa do komputera PC

W tym przykładzie wykorzystywany jest też termometr ds18b20 i dodatkowo połączymy przewodem port szeregowy AVRa z interfejsem rs233c komputera PC. Wyniki pomiaru temperatury będą trafiały do komputera PC.

Program kompilujemy podobnie jak poprzednie przykłady, w katalogu projektu powinny znajdować się pliki: main.c, ds18b20.h, ds18b20.c. W pliku Makefile należy wpisać częstotliwość sygnału taktującego procesor oraz włączyć obsługę zmiennych zmiennopozycyjnych dla funkcji printf.

Programik działa w następujący sposób: W pętli, z układu ds18b20, odczytywana jest wartość temparatury i następnie informacja ta wysyłana jest poprzez port szeregowy AVRa do komputera PC. Komunikat tekstowy zawierający informację o wartości zmierzonej temperatury formowany jest z użyciem standardowej funkcji printf. Wynik działania funkcji printf, znak po znaku, wysyłany jest do standardowego wyjścia 'stdout'. Z kolei strumień danych 'stdout' został w programie skierowany do nadajnika portu szeregowego AVRa.

/*
   Plik "main.c"

   KURS AVR-GCC cz.5
   (xyz.isgreat.org)

   Transmisja szeregowa do komputera PC, przykład nr. 4
   (schemat i opis działania w artykule)

   atmega16 (1MHz)
*/

/* Prędkość transmisji 2400 */
#define BAUD 2400
#define MYUBRR  F_CPU/BAUD/16-1

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

/* Inicjuje port szeregowy AVRa */
void USART_init(unsigned int myubrr)
{
    /* Ustala prędkość transmisji */
    UBRRH = (unsigned char)(myubrr>>8);
    UBRRL = (unsigned char)myubrr;

    /* Włącza nadajnika */
    UCSRB = (1<<TXEN);
  
    /* Format ramki: 8 bitów danych, 1 bit stopu, brak bitu parzystości */
    UCSRC = (1<<URSEL)|(3<<UCSZ0); 
}


/* Wysyła znak do portu szeregowego */
static int USART_Transmit(char c, FILE *stream)
{
    while(!(UCSRA & (1<<UDRE)));
    UDR = c;

    return 0;
}


/* Tworzy strumienia danych o nazwie 'mystdout' połączony
    z funkcją 'USART_Transmit' */
static FILE mystdout = FDEV_SETUP_STREAM(USART_Transmit, NULL, _FDEV_SETUP_WRITE);


/*  GŁÓWNA FUNKCJA  */
int main(void)
{
  /* Zmienna 'temp' przechowuje wartość temperatury */    
  double temp;

  /* Do tej tablicy zapisywane będą dane odczytane z układu ds18b20 */
  unsigned char ds18b20_pad[9];

  /* Inicjalizuje  port szeregowy AVRa */
  USART_init(MYUBRR);

  /* Przekierowuje standardowe wyjście do  'mystdout' */
  stdout = &mystdout;

  /* Główna pętla */
  while(1)
  {
    /* Funkcja 'ds18b20_ConvertT' wysyła do układu ds18b20 
       polecenie pomiaru */
    if(ds18b20_ConvertT())
    {

       /* 750ms - czas konwersji */
       _delay_ms(750);

      /* Odczyt z układu ds18b20, dane zapisywane są w tablicy ds18b20_pad. 
         Dwie pierwsze pozycje w tablicy to kolejno mniej znaczący bajt, bardziej 
         znaczący bajt wartość zmierzonej temperatury */               
       ds18b20_Read(ds18b20_pad);
 
      /* Składa dwa bajty wyniku pomiaru w całość. Cztery pierwsze bity mniej
         znaczącego bajtu to część ułamkowa wartości temperatury, więc całość
         dzielona jest przez 16 */
       temp = ((ds18b20_pad[1] << 8) + ds18b20_pad[0]) / 16.0 ;
      
      /* Wysyła komunikat do portu szeregowego */
      printf("Temperatura powietrza:%5.2f°C\n", temp);
    }
  }
}
Listing 5.8 Transmisja szeregowa do PC

W pliku main.c znajdują się dwie funkcje do obsługi portu transmisji szeregowej USART AVRa. Pierwsza funkcja 'USART_init' włącza nadajnik portu szeregowego i ustawia parametry transmisji, druga funkcja 'USART_Transmit' wysyła znak do portu szeregowego. A znajdująca się na początku pliku main.c makrodefinicja

#define BAUD 2400

ustala prędkość transmisji. W tej części kursu nie będę jeszcze szczegółowo wyjaśniał jak programować układ transmisji szeregowej USART AVRa, wykorzystamy gotowe funkcje bez zagłębiania się w rejestry i bity.

Jednym z argumentów funkcji 'USART_Transmit' jest znak, który funkcja wstawia do nadajnika portu szeregowego. W programie standardowe wyjście 'stdout' skojarzone zostało z funkcją 'USART_Transmit', oznacza to. że, dla każdego wysłanego do 'stdout' znaku zostaje uruchomiona funkcja 'USART_Transmit', która wpisuje otrzymany znak do nadajnika portu szeregowego.

Celem komunikacji poprzez interfejs rs232c potrzebujemy uruchomić na komputerze PC odpowiedni program, terminal rs232. Z pomocą terminala rs232 możemy najprościej odczytywać komunikaty, które przyszły do komputera PC poprzez port rs232c. Jednym z popularniejszych tego rodzaju programów działających w Windows jest darmowy "Bray's Terminal" bray_termimal.zip

Po uruchomieniu programu terminala należy ustawić następujące parametry transmisji 2400,8,N,1, na ilustracji poniżej zaznaczyłem te opcje na czerwono. Następnie należy wybrać numer wykorzystywanego portu (COM1, COM2) i kliknąć przycisk "Connect".

terminal
Okno programu "Bray's Terminal"- odczyt danych z portu rs232c.

W następnej części kursu.

W następnej części, jeśli taka powstanie:) , zakończę omawiać podstawy języka C: wskaźniki, konwersje typów . Będzie o przerwaniach, o danych w pamięci Flash, a  także o programowaniu fusebitów i zmianie częstotliwości pracy mikrokontrolera.



Kurs AVR-GCC cz.4

Inne artykuły na podobny temat:

  1. Kurs AVR-GCC Wyświetlacz od Nokii 3310
legenda

Komentarze (202)

kokodin
07.04.2023 (22:29)
gość
Dawno temu korzystałem z tego artykułu by coś dziwnego zbudować. i oczywiście to coś dziwnego działa do tej pory .
Ale jedna rzecz rzuca mi sie teraz w oczy w kwestii poradnika.
Przydał by sie kolejny segment odnośnie przechwytywania sygnałów i ich interpretacji. buforowanie sygnałów szeregowych, różnicowych i szeregowych, przechwytywanie z nich informacji i wykonywanie działań na podstawie takich rozkazów
teoretycznie atmega 8 pracujaca z zegarem 16mhz mogła by zastąpić dekoder dcc kolejki, gdyby tylko zrozumiała rozkazy z szyn.
abxyz
20.09.2021 (12:59)
autor strony
Cześć !

Tak zwykle jest, ściągnięty z internetu kod rzadko spełnia oczekiwania, dobrze, gdy w ogóle coś działa - ciężko zaczynać całkiem od zera.
Te wyświetlacze (ks0108), z interfejsem równoległym, niepraktyczne są, do łączenia mnóstwo przewodów :( A obecnie można kupić takie cuda... , za grosze
Piotr
18.09.2021 (21:16)
gość
Witam po b. długim czasie.Chciałbym zobaczyć jak podchodzisz do tematu graficznych LCD na przykładzie popularnego KS0108, i choć popularnego i sporej do niego ilości materiałów w sieci, to nie wszystko a w zasadzie wszystko co znalazłem, nie działa tak jak się tego spodziewałem. Np jeśli działa tryb tekstowy to nie działa wczytywanie plików bmp.
Pozdrawiam, Piotr od referatu ;-)
saly
03.02.2021 (16:44)
gość
Witam
Czy podpowie ktoś jak zaprogramować Atmega328PA, w wykazie MFile nie ma tego układu i wykazie scalaków programu AVRdude 1.05 też nie ma tego scalaka.
Używam programatora zgodnego z AVR-Doper (STK500), ISP AVR - USB.
Pakiet AVR WinAVR-20100110-
Damian
11.04.2020 (08:18)
gość
Witam
Czy można do wyświetlacza HD44780 wysłać ciąg liczb (liczby odpowiadają znakom ASCII) np. 656667 tak żeby ten wyświetlił "ABC". Jak dobrze rozumień napis ""ABC" kompilator C++ przekształca na liczby o wartości 656667 odpowiadające znakom ASCII, a funkcja sprintf za pomocą specyfikatorów przekształceń % przekształca te wartości na znaki ASCII, a wyświetlacz wyświetla ABC.
Otrzymuje po USART z modułu RFID ciągi liczb, np. o wartości 666567 które odpowiadają znakom ASCII i chciałbym je wyświetlić na wyświetlaczu w postaci znaków ABC, a używając funkcji sprint ten wyświetla mi 666567.
Wiadomo że można napisać program który rozpozna to i przekształci to na ASCII ale czy można prościej.
Może za pomocą innych funkcji np. lcd_puts(), jak to zrobić żeby LCD wyświetlał znaki ASCII po wysłaniu mu ich wartości liczbowych 656667.
saly
24.02.2020 (06:03)
gość
Cieszę się że będą kolejne części, moja przygoda z avr zaczęła się od Twojej strony. Kiedy przerobiłem przykłady to szukałem dalszych omówień, ale znalazłem kursów z tak prostym a zarazem szczegółowym omówieniem.
Będę wdzięczny za kolejne przykłady i gotowy się odwdzięczyć.
abxyz
09.02.2020 (19:46)
autor strony
Minęło już prawie 10 LAT! braku aktywności,
ale nigdy nie zamierzałem porzucić mojej stronki.
Zamierzam, między innymi, napisać kolejne części
kursu AVR-GCC, a te które już są uaktualnić
Już właściwie zacząłem.
Zapomnijcie o ściepie.
Wszystko będzie całkiem za darmo i bez reklam :)
saly
08.02.2020 (22:02)
gość
Tylko jak urządzić taką zbiórkę, może autor ma jakiś pomysł.
np.
-dostęp do postu dla użytkowników premium
-patronite, lub polak potrafi
-zbiórka na konto sms.
Piotr
02.02.2020 (14:25)
gość
saly napisał: Czy byli by chętni na kolejną część i ewentualną ściepę.

Jasne,żę tak.
Piotr
02.02.2020 (14:25)
gość
saly napisał: Czy byli by chętni na kolejną część i ewentualną ściepę.

Jasne,żę tak.
abxyz
27.01.2020 (15:17)
autor strony
:)
saly
25.01.2020 (21:38)
gość
Witam
Czy autor był by skłony na kolejną część kursu gdybyśmy zrobili zrzutę, lub w jakoś inaczej odwdzięczyli się za trud pracy.

Czy byli by chętni na kolejną część i ewentualną ściepę.
\max
20.12.2019 (22:04)
gość
Witam, a u mnie na wyswietlaczu pojawia sie nieprawidlowy wynik, zamiast nieco ponad 20 stopni, pokazuje np. 2,92. Nie wiem co moze byc tego przyczyna. Kod jest przekopiowany ze strony. ATMega128, AS ver 7.02
AlexxGreen
09.09.2016 (11:36)
gość
Jestem przyzwyczajony do tworzenia kompatybilnych porty szeregowe RS232 podłączone poprzez wirtualny bezmodemowym. Porty tranmisji w parach, tworząc wirtualny "most": dane wysyłane przez jedną aplikację zostanie natychmiast odebrany na drugim końcu parę na skutek innej aplikacji. Virtual Null Modem - http://www.eltima.com/products/virtual-null-modem/
Piotr
17.08.2016 (02:01)
gość
abxyz napisał:...Jak mawiają: Mądrej głowie dość dwie słowie. A głupiemu i referatu mało.Pozdrawiam.

No toś mi pojechał.Dzięki.

=====================================================
Sorry :) Tak mi się bezsensu chlapło. A referatu zawsze warto posłuchać, Nawet z najgłupszej książki można się czegoś nauczyć.
ABXYZ

kriselektro
24.04.2016 (23:08)
gość
Mam problem ze skompilowniem projektu i instrukcją endif pomoze ktos rozwiazać problem ? Dzieki

Compiling C: main.c
avr-gcc -c -mmcu=atmega8 -I. -gdwarf-2 -DF_CPU=1000000UL -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=./main.lst -std=gnu99 -MMD -MP -MF .dep/main.o.d main.c -o main.o
In file included from main.c:15:
hd44780.h:92:2: error: #endif without #if
make.exe: *** [main.o] Error 1
Pawciu
31.01.2016 (20:16)
gość
Witam. Mój problem polega na tym, że nie pokazuję mi temperatury. Po połączeniu i wgraniu programu na mikroprocesor wyświetla się tylko napis termometr. Kilka razy sprawdzałem kod i wszystkie połączenia na płytce. Czy ktoś ma podobnie?? Proszę o pomoc.
Aanonim
11.11.2015 (18:21)
gość
Co oznacza fragment argumentu \xdf funkcji sprintf?
koksinus
04.10.2015 (17:33)
użytkownik
kurs genialny i zrozumiały a tak to kiedy kolejna część kursu bo ja głupieję i czekam a inne kursy to kretynizm
Robert
18.01.2015 (21:18)
gość
Miałem ten sam problem. Chodzi tu o to, co Autor opisał odnośnie zmiany w ustawieniach pliku makefile dla printf'a. W Atmel Studio 6 ustawiamy to we właściwościach projektu, w zakładce "Toolchain" musimy w części dotyczącej Linkera w sekcji General zaznaczyć "Use vprintf library" oraz w sekcji "Libraries" dodać bibliotekę "printf_flt". Po zapisaniu zmian i skompilowaniu projektu, na moim termometrze w końcu pojawiła się temperatura zamiast znaku zapytania.
Adam
15.11.2014 (17:55)
gość
Witam, mam pewien problem z termometrem ponieważ po podłączeniu wszystkiego wyświetla się napis termometr a pod nim tylko ?°C co z tym zrobić ?
abxyz
29.09.2014 (22:38)
autor strony
Witam!
Myślę, że nie ma takiej potrzeby. Najtrudniej jest zacząć. A to co już napisałem na tej stronie powinno wystarczyć początkującym, żeby gładko rozpocząć zabawę z programowanie mikrkontrolerów. Dalej należy uczyć się samodzielnie, w sieci jest mnóstwo tekstów w tym temacie - nie ma potrzeby, abym tłumaczył z polskiego na "nasze", częściej z angielskiego:)
Jak mawiają: Mądrej głowie dość dwie słowie. A głupiemu i referatu mało.
Pozdrawiam
Piotr
26.09.2014 (00:57)
gość
Witam.
Czy pojawi sie 6 częśc ?
Pozdrawiam,Piotr
abxyz
25.06.2014 (13:17)
autor strony
Przykładowe programy zamieszczone tutaj są ćwiczeniami do kursu,
ćwiczymy tu użycie zmiennych, pętli, funkcji, czyli tego o czym pisałem w kursie,
natomiast nie uczę tutaj jak należy robić "fachowo" licznik czy termometr.

Licznik można zrealizować oczywiście korzystając ze sprzętowego licznika/tajmera AVRa - poszukaj w internecie:)
mairos
23.06.2014 (00:43)
gość
Witam!
Czyżby nikt nie uruchamiał "liczenia owiec", bo ja mam mały problem licznik wszedł w pole tekstowe, a druga linia pusta kompilator ma jeden błąd w pliku głównym w 43 linii
"pointer targets in passing argument 1 of `lcd_puts` differ in signedness" i nie mam pojęcia co z tym zrobić.
Robiłem to na Atmega8, więc pomyślałem, że to może coś z dopasowaniem procka i przeszedłem na Atmega162 i dokładnie to samo. Wyświetlacze też wymieniałem. Poza tym zauważyłem, że za dużo to nie może policzyć uzyskałem 60 imp. na sek. po usunięciu wszystkich "delay" w pliku głównym. Szkoda bo można by zrobić z tego pożyteczne urządzenia, a może da się jeszcze to przyśpieszyć?
dominico2000
01.06.2014 (11:43)
gość
No i gdzie ta 6 część ,powstanie taka w ogóle
AlexWest
19.05.2014 (12:52)
gość
Bardzo prosze o pomoc w przykładzie POWITANIE nie chcą mi się skompilować pliki robię wszytko tak jak opisane kopiuję do folderu projektu trzy pliki main.c hd44780.c i hd44780.h i zaznaczam w makefile main bez rozszeżenia c potem w C/C++ source file(s) wpisuje hd44780.c bez main (z main też probowałem) nie zaznaczam floaing point i bez wpisania recznie czestotliwosci(tez zaznaczałem i wpisywałem 1000000) i za chiny sie nie chce skompilować ? CO ROBIE ZLE ???
warsztat
04.05.2014 (17:29)
gość
do wszystkich z kwadratami - regulacja kontrastu wyświetlacza czyli napięcie z potencjometru na pin3 wyświetlacza (lub inny opisany w PDF CONTRAST ADJUST) a kurs bajka, coraz lepiej mi idzie :) a to pierwszy dzień tak na prawdę.
cyprian
02.02.2014 (15:04)
gość
Mam problem z pierwszym programem który obsługuje LCD. Program się ładnie kompiluje, bez problemu wgrywa się na atmege ale po wgraniu na LCD widać tylko kwadraty w górnym wierszu. Jaki może być przyczyna?
Luki
22.12.2013 (19:53)
gość
Przydała by się następna część o fusebitach :)
Admunt
22.12.2013 (16:28)
gość
Witam
Może zrobimy sterownik łańcucha choinkowego LED na AVR z możliwością losowego wyświetlania ???
Szukam rozwiązania na google i chyba nikt tego tematu nie ruszył w języku C
Daniel
18.12.2013 (12:03)
gość
Czy powstanie dalsza część kursu ?
Admunt
29.11.2013 (21:42)
gość
To rzeczywiści problem jeśli ktoś lubi korzystać z uniwersalnej płytki wtykowej.
A czy do układów SMD, które podałeś są dostępne wkładki przez które można zamontować AVRa na płytce wtykowej ???
abxyz
29.11.2013 (14:36)
autor strony
Tak są AVRy z USB, przykładowo AT90USB162, ATmega32U2 . Niestety, dla początkujących elektroników , wszystkie wyłącznie w obudowach SMD.W sieci dostępne są również darmowe biblioteki do obsługi USB,
http://www.fourwalledcubicle.com/files/LUFA/Doc/120219/html/index.html
Admunt
28.11.2013 (22:13)
gość
Czy znacie AVRa który ma wbudowaną obsługę USB ??? Czyli bez -pośredniej komunikacji przez przez RS232

A tak w ogóle jestem jestem fanem strony i podziwiam wkład w portal :))
randomowy_gosciu
10.09.2013 (08:47)
gość
Kurs jest genialny. Stary, powinieneś usunąć tą stronę z internetu i wydać ten kurs w formie książki. Myślę, że zarobiłbyś trochę kasy.
pozdrawiam
(p.s. jeśli jednak nie masz zamiaru tego robić, to czekam oczywiście na 6 część kursu ;])
mariuszasd
26.08.2013 (13:37)
gość
Super kurs! Kiedy następna część?
ojtush
28.05.2013 (16:10)
gość
Jeżeli komuś nie działał ds18b20 a szczególnie funkcja sprintf to macie tu kod działający

zmieniłem na inną funkcję dtostrf(temp,5,1,str);
dziala na 100%


plik główny main.c
#include "LCD.h"
#include "Ds18b20.h"

#define F_CU 1000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/io.h>
#include <stdlib.h>

char str[17]=" Termometr ";
int main()
{
double temp;
/* W tablicy zapisywane będą dane odczytane z układu ds18b20 */
unsigned char ds18b20_pad[9];


LCD_Initalize();
LCD_Home();
LCD_Clear();



while(1){
if(ds18b20_ConvertT())
{

/* 750ms - czas konwersji */
_delay_ms(1750);
LCD_Clear();
/* Odczyt z układu ds18b20, dane zapisywane są w tablicy ds18b20_pad.
Dwie pierwsze pozycje w tablicy to kolejno mniej znaczący bajt i bardziej
znaczący bajt wartość zmierzonej temperatury */
ds18b20_Read(ds18b20_pad);

/* Składa dwa bajty wyniku pomiaru w całość. Cztery pierwsze bity mniej
znaczącego bajtu to część ułamkowa wartości temperatury, więc całość
dzielona jest przez 16 */
temp = ((ds18b20_pad[1] << 8) + ds18b20_pad[0]) / 16.0 ;

/* Formułuje komunikat w tablicy 'str' */
//sprintf(str,"%4d""C", temp);
dtostrf(temp,5,1,str);
LCD_WriteText(str);

}

}
}

abxyz
09.05.2013 (01:16)
autor strony
W tym kursie używamy tylko oprogramowania "open source":)
mario246
06.05.2013 (19:01)
gość
Kolego możesz dokładniej wyjaśnić jak w atmel studio uruchomić ten pomiar temperatury?
abxyz
21.03.2013 (21:42)
autor strony
Aby zmniejszyć czas trwania pomiaru temperatury, z 750ms do 100ms, można zmienić rozdzielczość do 9 bitów, patrz ds18b20 datasheet.


piter_kula
20.03.2013 (19:44)
gość
Znalazłem odpowiedź..trzeba dodać bibliotekę libm.a oraz libprintf_flt oraz w opcjach linkera wpisać -Wl,-u,vfprintf. Uruchomiłem program na ATmega8 zajmuje 55%. Mam takie pytanie czy częstotliwość wysyłania pomiaru można jakoś zwiększyć?
piter_kula
18.03.2013 (20:25)
gość
Witam, mam takie pytanie ponieważ korzystam z programu AVRStudio, a gdy podłączyłem czujnnik oraz zaprogramowałem uc na wyświetlaczu pokazuje mi sie tylko napis temperatura a poniżej nic. Ma to chyba związek z funkcją printf. Moje pytanie gdzie w AVRStudio ustawia się "floating point"?
Rafał
16.03.2013 (12:33)
gość
Dzięki teraz już wszystko jasne. Mam jeszcze jedna pytanie jak można zapisać jakąś liczbę do zmiennej tak aby ona nie znikła podczas braku zasilania avr-a. Chodzi o to że próbuje zrobić układ który poniżej pewnej temperatury załączał by przekaźnik, tylko że jeżeli będę wprowadzał za pomocą przycisków podłączonych do do portów avra jakaś dane to zniknie mi ona jeżeli braknie zasilania. Jak można zapisywać dane do pamięci eprom?
abxyz
15.03.2013 (21:46)
autor strony
Poniżej są przykładowe wartości odczytane z czujnika ds18b20, pierwszy odczytywany jest mniej znaczący bajt. Rozdzielczość 12 bitów, cztery mniej znaczące bity to część ułamkowa.

0000 0111 1101 0000 +125*
0000 0101 0101 0000 +85
0000 0001 1001 0001 +25.0625
0000 0000 1010 0010 +10.125
0000 0000 0000 1000 +0.5
0000 0000 0000 0000 0
1111 1111 1111 1000 -0.5
1111 1111 0101 1110 -10.125
1111 1110 0110 1111 -25.0625
1111 1100 1001 0000 -55

Rafał
15.03.2013 (00:05)
gość
Witam
Mam problem chciałbym wyświetlić temperature zamiast lcd na wyświetlaczu 7 segmentowym tylko mam problem z zamiana tego co jest w tablicy ds18b20_pad na kod bcd. Moze mi ktoś wytłumaczyć ten fragment:
temp = ((ds18b20_pad[1] << 8) + ds18b20_pad[0]) / 16.0 ;
Ja to rozumiem tak mamy takie wartosci w tablicy
ds18b20_pad[1] np 21 czyli 10110
ds18b20_pad[0] np 120 czyli 1111000
Przesuwamy w lewo i dodajemy czyli mamy
1011001111000 dziesietnie jest to 5752 dzielimy przez 16
wychodzi: 359 czyli 101100111 i brak w tym sensu

z góry dzięki
Krzysiek
02.03.2013 (01:59)
gość
Witam,
bardzo podoba mi się kurs. Dla osób początkujących (tak jak ja) jest tu dużo ciekawych wskazówek.

Uruchomiłem sobie termometr z wyświetlaczem, ale niestety zamiast temperatury (wartości) wyświetla mi się znak "?". Czego to może być wina?

Sam termometr działa dobrze bo zrobiłem sobie dodatkowo wskazania na diodach dla odpowiednich przedziałów temperatur i ładnie mi się zaświecają jak ogrzewam termometr, a jak ochładzam to gasną.
bogdanc
26.02.2013 (13:05)
gość
Tak zrobiłem wszystko jak trzeba.

Mój błąd bo jak okazało się wysłano mi DS1820 zamiast DS18b20.
więc muszę zrobić małą przeróbkę programu.

pozdrawiam
abxyz
25.02.2013 (21:25)
autor strony
A czy plik Makefile utworzyłeś z pomocą programu MFile, postępując dokładnie według wskazówek w tekście ???
bogdanc
25.02.2013 (13:27)
gość
kurs genialny:)

Mam problem, temperatura pokojowa 22 stopnie a pokazuje mi 2 stopnie wszystko podlaczone jak w kursie.

pozdrwaiam
abxyz
13.02.2013 (14:52)
autor strony
Niczego nie potrzeba zmieniać, podłączasz dodatkowo zasilanie podświetlenia wyświetlacza: pin15 do +5V, pin16 do GND
dafaq
13.02.2013 (12:12)
gość
Witam!
Posiadam wyświetlacz ze sterownikiem S6A0069 Czy (i jak?) należy zmieniać pliki hd44780.c i hd44780.h, by to zadziałało? Dodam, że ten wyświetlacz ma nie 14, lecz 16 pinów ze względu na podświetlenie.
bogdanc
13.02.2013 (01:11)
gość
Czekamy na dalsze części Kursu....!!!!!!!!!!!!!!
Tomek
03.02.2013 (17:00)
gość
Witam!

Mam pytanie czy jest możliwy odbiór przez 8-bitowe SPI danych z przetwornika A/C 12-bitowego? Jak nie to czy w ogóle jest możliwy odczyt danych przez atmegi z przetworników 12-bitowych.

Pozdrawiam
migral
25.01.2013 (02:24)
użytkownik
Witam serdecznie autora i kursantów kurs jest po prostu super napisany może dla nowicjusza jak ja troszkę od 3 części zrobiło się trudnawo jednak porównując multum różnych kursów i książek z internetu jakie zassałem to z ręką na sercu powiem krótko wymiata ! wszystkie jakie czytałem oczywiście pod kontem
praktycznego podejścia i objaśnień .
Mam nadzieje że będzie kontynuacja :)
Pozdrawiam wszystkich
seroo
08.12.2012 (14:45)
gość
Bardzo fajny kurs, nie traci zwolenników w miarę upływu czasu.
Czekam aż będzie jeszcze coś nowego.
Pozdrawiam Autora.
ShadowZ
24.11.2012 (18:47)
gość
Czy już przesądzone jest że nie powstanie kolejna część kursu?

Chętnie poczytałbym o fusebitach :C
abxyz
25.06.2012 (22:38)
autor strony
Taka uwaga - w fabrycznie nowych atmega16(32) linie PC2..PC5 są niedostępne jako porty we/wy - przydzielone są do interfejsu JTAG. JTAG można odłączyć programując fuse bity.
karol
25.06.2012 (21:42)
gość
Witam!
W moim wyświetlaczu RW podłączone jest do masy, więc skorzystałem z kodu w linku autora,kilka postów poniżej. Zamieniłem port D na C, podlączyłem poprawnie a mimo to mam podświetlony tylko pierwszy wiersz (cały, bez jakichkolwiek znaków) w czym może być problem???
To moje pierwsze podejście do LCD. do czego służy pin CTR z wyświeltacza?
abxyz
24.05.2012 (17:03)
autor strony
Xara (community), GIMP
hexen2k
24.05.2012 (07:09)
gość
W jakim programie są zrobione te animacje wyświetlacza HD44780 ?
abxyz
14.05.2012 (19:54)
autor strony
Przed chwilą specjalnie:) skompilowałem przykład z termometrem, i oto masz wynik :

AVR Memory Usage
----------------
Device: atmega16
Program: 4738 bytes (28.9% Full)
(.text + .data + .bootloader)
Data: 26 bytes (2.5% Full)
(.data + .bss + .noinit)
-------- end --------
horacy
14.05.2012 (18:44)
gość
Eno nie. Zrobiłem tak jak w opisie, z tym że po włączeniu obsługi zmiennoprzecinkowych liczb w sprintf objętość z 6kb wzrosłą do 14kb. :P
abxyz
14.05.2012 (14:31)
autor strony
Jak byś skompilował projekt, tak jak opisałem to w kursie, wtedy wielkość kodu wynikowego przykładu nie przekroczyłaby 5kb.
horacy
14.05.2012 (00:23)
gość
Przy użyciu funkcji sprintf program termometr zajmuje z 14kb!!! Programy w Bascom zajmują dużo, dużo mniej. Do ilu udało Wam się zejść w duł? mi po napisaniu własnej procedury do 5kb.
zxc12
01.04.2012 (14:28)
gość
Gratuluję życzliwego podejścia do ludzi. Niestety wielu którzy mają wiedzę na określony temat nie zamierzają dzielic się z innymi. Można odniść wrażenie że to polska wada narodowa. Na forach obcojęzycznych można znaleźć wiele informacji natomiast na polskich jest dużo jadu i chęci górowania nad innymi.
Tylko współpraca obroni naród prze zalewem chńszczyzny i podniesie polską kulturę techniczną.
DZIĘKUJĘ i życzę wszelkiej pomyślności
abxyz
15.12.2011 (19:46)
autor strony
Tu są pliki drugiej wersji pierwszego przykładu - dla przypadku, gdy wyprowadzenie RW wyświetlacza przyłączono na stałe do masy.

http://hobby.abxyz.bplaced.net/kurs_avrgcc/01_RW.zip

Tym razem LCD przyłączony jest do portu D AVRa ( RW do masy)

LCD_RS - AVR_PD0
LCD_RW - GND
LCD_E - AVR_PD1

LDC_D4 - AVR_PD2
LCD_D5 - AVR_PD3
LCD_D6 - AVR_PD4
LCD_D7 - AVR_PD5
trTon
15.12.2011 (13:01)
gość
mam takie zapytanie chodzi o programowanie wyświetlacza 16x2. Moja płytka testowa nie posiada wyjścia RW z wyświetlacza, wiec muszę jakoś przerobić kod ?
C_nie
15.12.2011 (11:23)
gość
A ja włożę łyżkę dziegciu do beczki miodu. Doszedłem do tego miejsca, zrobiłem bilans za i przeciw i nie zamierzam kontynuować. O ile C++ dla PC wydaje mi się językiem logicznym i dosyć przyjaznym o tyle AVR GCC to jakiś koszmarny potworek. Jeżeli aby zmienić ustawienia sprzętowe muszę ustawiać w karkołomny sposób określone bity w konkretnych rejestrach, których mnogość nazw muszę ciągle pamiętać, to mam chyba do czynienia z jakimś strukturalnym asemblerem a nie językiem wysokiego poziomu. Jeśli ktoś chce się mordować, życzę powodzenia. Dla mnie jest to nużące, mało czytelne i zabija radość tworzenia. Najczęściej jestem w stanie 2-3 razy szybciej zrobić to samo w BASCOM-ie a w zaoszczędzonym czasie wolę iść na spacer. Ogólna moda i dowartościowanie z powodu znajomości C do mnie nie przemawia. Uczestnikom kursu, którzy pozostają, życzę wytrwałości i sukcesów w opanowaniu języka AVR GCC. Może kiedyś (gdy będę miał więcej czasu) zmienię zdanie? Kto wie?
ponulaczek
08.12.2011 (22:45)
gość
Dlaczego jak odepnę programator od usb to się wyświetlają znaczki=krzaczki??
Niecierpliwy
21.11.2011 (19:01)
gość
Witam

Gratuluję kursu,jest świetny.Kiedy można się spodziewać kolejnej części?
Tomek
15.11.2011 (00:45)
gość
Dziekuje za kurs :) Lepiej napisanego jeszcze nie spotkalem. Czekam na dalsze czesci lub jakies ciekawe artykuly zwiazane ze srodowiskiem AVR.
Rain
31.10.2011 (21:25)
gość
Świetna strona. Opisy i przykłady są opisane na tyle łatwo, aby zagadnienia zrozumiał nawet ktoś, kto już posiada tylko podstawy elektroniki. Sam myślałem, aby uczyć się czystego C, ale w zasadzie tu wszystko jest opisane. Pozdrawiam.
Bert
05.10.2011 (01:40)
gość
Dzięki wielkie za kurs!
abxyz
31.08.2011 (17:44)
autor strony
Racja, już to poprawiłem. Tak się zdarza, że mimo poważnego błędu wydaje się że program działa właściwie, ale do czasu :)
MrT
31.08.2011 (14:39)
gość
Jako że to mój pierwszy post, to: witam i wielkie dzięki dla autora! Kurs jest świetny - od niego zacząłem przygodę z avr.

Miałem jednak pewien problem z termometrem.
Mianowicie w funkcji int ds18b20_Read(unsigned char scratchpad[]) znajduje się linijka
for(i=0; i<10; i++) scratchpad[i] = OneWireReadByte();
która sugeruje, że tablica scratchpad jest co najmniej 10-cio elementowa. Jednak w pliku main.c do funkcji ds18b20_Read jest przekazywana tablica 9-cio elementowa unsigned char ds18b20_pad[9];. Więc w funkcji ds18b20_Read mamy wyjście poza zasięg tablicy, a co za tym idzie modyfikację przypadkowego obszaru pamięci.

Podejrzewam, że w funkcji ds18b20_Read powinno być
for(i=0; i<9; i++) scratchpad[i] = OneWireReadByte();
ponieważ datasheet ds18b20 mówi, że scratchpad ma 9 bajtów.

Pozdrawiam i z niecierpliwością czekam na kolejna część kursu ;)
dodo
09.08.2011 (23:34)
gość
Po pierwsze chce podziekowac za super kurs i nie trace nadziei na kolejne czesci :)

Mam problem z wyskakujacymi krzakami na LCD (pierwszy przyklad). Dokladnie to problem pojawia sie po wlaczeniu uC, a nastepnie co drugi reset (prawie zawsze co drugi reset). Myslalem ze to za krotkie czasy przy inicjalizacji LCD, ale wydluzanie nic nie dalo. Zaobserwowalem za to, ze:
1. Zwiekszenie czestotliwosci pracy oscylatora wewnetrznego powyzej 1MHz eliminuje problem.
2. Skasowanie jednego czy dwoch "nop" troche poprawia sytuacje bo problem jest np co 3 reset.
3. Jesli poczekam odpowiednio dlugo (czasami jedna petla while calego programu, czasami dwie) to komunikaty na LCD zaczynaja dzialac poprawnie.

Dla scislosci przestudiowalem pdfa od mojego LCD i pierwsza wersje napisalem prawie od podstaw z zupelnie inna inicjalizacja taka (chyba) jaka byla podana przez producenta, a dopiero potem skopiowalem dokladnie to co jest w przykladach zmieniajac tylko porty. Zmiany nie zauwazylem.

O co chodzi?
20rafalo
06.08.2011 (14:39)
użytkownik
dzięki
Max jest sprawny.
Jednak nadal nie mogę przesłać informacji do kompa i odwrotnie
Jak sprawdzić czy max komunikuje się z kompem(programem).
Jest wyjaśniona gdzieś dokładna procedura?
Z góry dzięki
abxyz
03.08.2011 (13:53)
autor strony
Prosto można sprawdzić, czy max232c działa, multimetrem : Jeśli na wejście TxIN poda się napięci 5V to na wyjściu TxOUT powinno być napięcie ok -10V, a jeśli na wejściu TxIN poda się napięci 0V, to na wyjściu TxOUT powinno być napięcie ok. 10V.
20rafalo
01.08.2011 (13:01)
użytkownik
Nie mogę się skomunikować z PC za pomocą RS232. Jak wykryć gdzie jest błąd? jak dokładnie skonfigurować program aby sprawdzić sam układ max232 czy jest wszystko ok.
pzdr
krzys_h
04.07.2011 (16:06)
użytkownik
Jest możliwość wpisywania własnych znaków do wyświetlacza i późniejszego wyświetlania ich, ale nie wiem dokładnie jak to zrobić
mateusz
20.06.2011 (12:44)
gość
Ma ktoś jakiś pomysł aby w tej blibilotece do obsługi wyświetlacza HD44780 można było wygenerować dowolny znak? (np. strzałke? )
Paweł
07.03.2011 (00:21)
gość
Na prawdę duże słowa uznania dla autora. Gratuluję i życzę dalszej wytrwałości w tworzeniu tego dzieła. Na pewno wiele osób skorzysta, w tym ja. Dziękuję
Maziu
05.03.2011 (13:47)
gość
To znowu ja :) i kolejny sukces
http://www.youtube.com/watch?v=KZYhBpfK8Vg
Termometr na DS18B20 z pamięcią najmniejszej i największej wartości :)
Maziu
03.03.2011 (14:08)
gość
Bardzo przydatna strona :) programowanie przy jej pomocy przychodzi łatwo i przyjemnie :) Znając tylko podstawy C dałem radę stworzyć prosty watomierz na LCD :)
http://www.youtube.com/watch?v=0mZkTq7X4Qw

Osobiście chciałbym żeby następna część kursu była poświęcona :
-PCF8563 albo podobnym czasoodmierzaczą
-odczytywaniu i zapisywaniu danych na karcie SD :)
stoper19
10.02.2011 (15:43)
gość
Witam. Chcę wykonać taki termomter ale złożony z 3 czujników 2 z nich w odległości ok 2 - 3 m od procesora a jeden 10 m. dodatkowo jeden przycisk do zmiany wysietlania temperatury z poszczegolnych czujnikow. Czy czujnik w odleglosci 10 m bedzie dzialal poprawnie. czy treść programu uda mi się zmieścic na attiny2313? Kurs bardzo przydatny
Karol
11.01.2011 (19:38)
gość
Witam, kurs pozytywnie wykonany jednak mam problem z kodem licznika owiec ogolenie chodzi o to iz po wcisnieciu kilka razy zwiekszenia licznika atmega sie zawiesza i nie moge dojsc dla czego...
pracuje na atmedze 128
Bob
29.12.2010 (12:31)
gość
Z niecierpliwością czekam na kolejną część i mam nadzieję, że autorowi nie zabraknie czasu i zaangażowania do ukończenia kolejnej części, z góry wielkie dzięki za włożony wkład i poświęcony czas (na pewno było go sporo ) w dotychczasowe kursy, tym bardziej, iż niema z ego żadnych korzyści.
xxx
29.12.2010 (00:02)
gość
po problemie, mfile pyta sie o plik, w momencie wybierania go przez "browse" sam wpisuje do mfile'a ścieżkę do pliku, a jeśli jest w tym samym katalogu co mfile to powinno być bez ścieżki, po prostu hd44780.c
xxx
28.12.2010 (22:07)
gość
mam ten sam problem co Pan kilka postów ponizej:

"make.exe: *** No rule to make target `main.elf', needed by `elf'. Stop.

> Process Exit Code: 2
> Time Taken: 00:02"

czy moglibyscie tutaj napisac gdzie kolega mial blad? ;]
Ed
26.12.2010 (12:25)
gość
"zadanie przerosło ambicje i umiejętności autora"

Skoro uważasz, że masz większe umiejętności, proszę bardzo - napisz swoją część, założę się, że autor ją tutaj udostępni!
Realista
25.12.2010 (19:51)
gość
Ciąg dalszy prawdopodobnie nie powstanie - zadanie przerosło ambicje i umiejętności autora - a szkoda, bo kurs był świetny...
Michał
15.12.2010 (22:28)
gość
Gratuluje świetnej roboty! Ten kurs jest na prawdę fajnie napisany, zachęca do pogłębiana wiedzy i sprawia, że programowanie mikrokontrolerów jest czymś przyjemnym, ciekawym. Nie odrzuca, nie odstrasza, ale w prosty i zarazem profesjonalny sposób objaśnia poszczególne zagadnienia! Jeszcze raz gratuluje i czekam na kolejną część. Pozdrawiam
waad
23.11.2010 (22:09)
gość
I tego się trzymajmy ! Z przyjemności i dla przyjemności tu jesteśmy. Będzie kolejny odcinek - OK, nie będzie - dziękujmy za to co jest. Węcej luzu KURSANCI !
abxyz
07.11.2010 (14:34)
autor strony
Niewłaściwe podejście do sprawy, nie ma sensu specjalnie czekać na kolejne części tego kursu, bo nie wiadomo kiedy będą, możne nigdy, przecież ja tę stronkę robię dla zabawy. Te pięć istniejących części kursu należy traktować jako przystępne wprowadzenie do tematu i dalej uczyć się samodzielnie.
W internecie można znaleźć wszystko co jest potrzebne do nauki programowania mikrokontrolerów. Wydano wiele książek na temat programowania mikrokontrolerów AVR, były kursy w czasopismach EdW, Elektronika Praktyczna, pisane przez zawodowców ,w sieci można zamówić CDROMy z tymi kursami.
Oczywiście zamierzam kontynuować kurs i myślę że, bez względu na to, kiedy się pojawią kolejne części, na pewno się komuś to przyda.
Goblin
07.11.2010 (13:34)
gość
Kurs świetny... był, bo chyba umarł. Jak dla mnie i zapewne dla wielu innych, oczekiwanie osiem miesięcy na nową część mija się z celem :( W tym tempie prędzej emerytury się doczekam niż jego końca. Szkoda... Wiązałem z tym kursem wielkie nadzieje.
leo1
31.10.2010 (19:04)
gość
Gratuluje kursu. Wszystko w przystępny i przejrzysty sposób.

Jeśli można coś dodać to proszę o obsługę programową przez USB. Odbieranie wysyłanie danych tak aby PC widział nasz projeky jako COM. Myśle, że to warzne zagadnienie i można zastosować w wielu projektach.
mix
20.09.2010 (01:11)
gość
swietny kurs, mam nadzieje ze nie będzie trzeba długo czekać na następna cześc?:)
bobofrut
11.09.2010 (19:40)
gość
Dziękuje za kurs, bardzo mi pomógł w "opanowywaniu" procesorów z rodziny AVR.
adamus
07.09.2010 (18:14)
użytkownik
Witam raz jeszcze!
Raczkuje jeszcze w programowaniu i mam pytanie do wszystkich:
Czy można w programowaniu wyświetlaczy LCD bezproblemowo wykorzystać bibliotekę string.h ??? Czy ona jest tylko dostępna tylko dla C++ a nie w C ???

pozdro
Adam
adamus
07.09.2010 (18:01)
użytkownik
Witam wszystkich!
Czym sugerowałeś się używając do testów Atmgi 16 a nie np. Atmegi 8 ???
Czy tylko tym, że 16 ma więcej portów czy miałeś coś innego na uwadze ???
Czy zdarzyło Ci się kiedyś przekroczyć pamięć Atmegi podczas wpisywania do środka programu ???
A jak to sprawdzić czy rozmiar pliku nie przekracza możliwości uC ???

Gubię się w parametrach bo jeśli przykładowo Atmega8 ma parametry FLASH=2kB, SRAM=1kB, EEPRM=512B to tylko FLASH mówi nam czy uC da rade pomieścić program ??? (czasami mi to myli z EEPRM albo SRAM )

pozdro
Adam
abxyz
31.08.2010 (00:26)
autor strony
A czy plik Makefile utworzyłeś z pomocą programu MFile, postępując dokładnie według wskazówek w tekście ???
Bartek
30.08.2010 (23:37)
gość
Mam następujący problem. Złożyłem układ zgodnie z instrukcją, skompilowałem i wgrałem kod i na wyświetlaczu pojawia mi się znak zapytania i symbol Celcjuszy. Jeżeli odłączę termometr, to pojawia mi się sam napis Termometr. Jakieś pomysły co może być źle?
abxyz
23.08.2010 (17:36)
autor strony
Jeżeli linia RW wyświetlacza przyłączona jest na stałe do masy(tak często jest zrobione na różnych płytkach startowych), to przykłady z wyświetlaczem nie będą działać. Pisałem już o tym w komentarzach poniżej i dodałem wersję kodu z RW podpiętym do masy.

Tu są pliki do przykładu pierwszego

http://hobby.abxyz.bplaced.net/kurs_avrgcc/01_RW.zip
Tym razem LCD przyłączony jest do portu D AVRa (sygnał RW do masy)

LCD_RS - AVR_PD0
LCD_RW - GND
LCD_E - AVR_PD1

LDC_D4 - AVR_PD2
LCD_D5 - AVR_PD3
LCD_D6 - AVR_PD4
LCD_D7 - AVR_PD5
L_M
23.08.2010 (09:41)
użytkownik
Rozumiem że zrobiłeś odpowiednie zmiany w pliku "HD44780.h" A jeszcze jedno: podłączyłeś końcówkę RW? W Bascomie ona nie jest wykorzystywana.
paczkaexpres
22.08.2010 (22:38)
gość
Sprawdziłem kontrast i wszystko działa . Poza tym gdy go spróbowałem zaprogramować w BASCOmie to nie było żadnych problemów.
L_M
22.08.2010 (15:44)
użytkownik
Sprawdź czy kontrast zmienia się po pokręceniu potencjometrem, ewentualnie zmierz, czy napięcie na jego środkowej nodze zmienia się podczas kręcenia.
paczkaexpres
20.08.2010 (19:34)
gość
Witam . Mam problem z uruchomieniem wyświetlacza ( konfiguracja portów to RS - PD2 ,E - PD3 , D4 -PD4 , D5 - PD5 , D6 - PD6 , D7 - PD7 ) otóż po kompilacji programu i wgraniu go do procka widzę tylko jeden rząd czarnych kwadratów .
20rafalo
15.08.2010 (09:37)
użytkownik
20rafalo
28.07.2010 (23:21)
gość
Ekspertem jeszcze nie jestem :-P
Program cały czas poprawiam, jak skończę to być może umieszczę kod.
Dodam jeszcze jeden czujnik do pomiaru temp wody w bojlerze, jak już przeciągnąłem przewody :-P
Czekamy na kolejną odsłonę kursu - myślę, że będzie napisany bardziej o samym uC i będzie zawierał duuuuuuuuużo komentarzy w samym programie :-)
L_M
28.07.2010 (15:34)
użytkownik
Szkoda że tylko HEXa udostępniłeś, mimo że nie widzę kodu źródłowego to muszę Ci zwrócić uwagę na pewną rzecz: chodzi o to że "siłą" magistrali 1Wire jest to, że układy występujące na niej mogą pracować na wspólnej linii. Co więcej mogą to być elementy o różnym przeznaczeniu czyli czujniki temperatury, pamięci eeprom, tranzystory itd. Niestety sterowanie tych układów za pomocą "C" wymaga sporych umiejętności programistycznych. Może autor pokusiłby się o napisanie odpowiedniej aplikacji?.
Pozdrawiam.
20rafalo
28.07.2010 (09:59)
gość
L_M masz rację - to co wymieniłeś jest ważne. A o wskaźnikach jest dużo materiałów.

Załączam link do mojej stronki z programem termometr z 3 czujnikami
http://sites.google.com/site/itswiat/gallery
L_M
27.07.2010 (19:36)
gość
Kurcze błąd, Oczywiście chodziło mi o przetwornik C/A
L_M
27.07.2010 (19:33)
gość
Sebi na assembler to chyba jeszcze za wcześnie, moim zdaniem wiele innych rzeczy wymaga jeszcze omówienia: przerwania, tablice stałych umieszczonych w pamięci programu itd. W sumie o wskaźnikach też chętnie bym poczytał, ponieważ temat wydaje mi się dość skomplikowany. No i brakuje mi w kursie tego co czyni AVRy tak lubianymi (wg mnie) czyli przedstawienia zasobów sprzętowych: pamięci eeprom, timerów(szczególnie pracujących w trybach PWM), sprzętowej obsługi I2C(TWI), SPI, przetwornika C/A itd. Pozdrawiam.
sebi
27.07.2010 (15:07)
użytkownik
Wcześniej było wspomniane że następna część kursu będzie zawierać łączenie asm z C i moje propozycje to wzmianka o wskaźnikach które można by powiązać z assembler ponieważ tam często odwołujemy się do adresów w pamięci. Moją drugą prośbą jest krótki fragment o trybach oszczędzania energii w AVR.
pasta20
27.07.2010 (14:33)
gość
Fajny kurs czekam na następną część :) Ciekawe kiedy ona wyjdzie.
Pozdrawiam.
20rafalo
26.07.2010 (16:25)
gość
L_M każdy z innego portu
L_M
26.07.2010 (15:43)
gość
Rafalo mam do Ciebie pytanie: twoje czujniki pracują na jednej linii czy każdy obsługujesz z innego pinu?
20rafalo
25.07.2010 (17:30)
gość
Zrobiłem również z 3 czujnikami, w tym 1 odczytuje na naszą prośbę - u mnie temperaturę z pieca.
Mogę udostępnić program, oczywiście za zgodą abxyz, gdyż korzystałem z funkcji zamieszczonych w kursie.
20rafalo
24.07.2010 (18:23)
gość
Witam
Udało mi się samemu (na podstawie programu z kursu) zrobić termometr z KILKOMA CZUJNIKAMI. Aktualnie zrobiłem na 2 czujniki :-)

Być może program nie jest zoptymalizowany, ale na potrzeby termometru chodzi aż za dobrze !!
20rafalo
23.07.2010 (20:54)
gość
Jak by autor podpowiedział co zmienić aby móc obsłużyć kilka Ds18b20 - było by dobrze. Mam koncepcję ale zasobożerną :-/

PS też się podpisuje za tym, aby więcej pisać na o "AVR" niż o "C++"
Oraz więcej komentarzy w samym programie :-) - programy trudniejsze to i komentarzy więcej.
Już apelowałem w tej sprawie :-P
rav
23.07.2010 (19:44)
gość
Powtórzę to co już nie raz zostało napisane, że kurs na prawdę udany.
Mam pytanie kiedy możemy spodziewać się kolejnej części? Czy to kwestia tygodni czy raczej miesięcy?
Jeśli mogę mieć sugestię, to tak jak przedmówca wspomniał więcej tematyki związanej z AVRami (przerwania, działanie z urządzeniami zewnętrznymi, przetwornik A/C) a mniej samej tematyki C, bo o tym jest stosunkowo więcej informacji w internecie niż o AVRach.
abxyz
23.07.2010 (10:08)
autor strony
Domyślam się że chodzi o czujniki ds18b20.

Więcej niż jeden ds18b20 można przyłączyć do AVRa na dwa sposoby: szeregowo (wszystkie czujniki do jednej lini we/wy AVRa - magistrala 1-wire) lub równolegle ( jeden czujnik do jednego portu we/wy).

Fragment kodu dołączony do przykładu "Termometr cyfrowy" umożliwia odczyt temperatury tylko z jednego ds18b20, ale łatwo można przerobić te funkcje, by odczytywały temperaturę z czujnika podłączonego do wskazanego portu we/wy.

Natomiast przystosowanie tego kodu do odczytu wielu czujników przyłączonych do magistrali 1-Wire jest odrobinę bardziej skomplikowane - na pewno wrócimy do tego tematu w którejś części kursu.

Oczywiście klasa rezystora podciągającego nie wpływa na dokładność temperatury odczytanej z ds18b20, bo przecież AVR komunikuje się z ds18b20 "cyfrowo" (interfejs 1-Wire)
20rafalo
21.07.2010 (09:50)
gość
Witam
Ile maksymalnie można podłączyć czujników do atmega 16, tyle ile jest wolnych portów?
Czy klasa rezystora podciągającego wpływa na dokładność pomiarów temperatury?
20rafalo
20.07.2010 (13:04)
gość
Podziękowania dla autora za pomoc w poprawieniu błędu w pliku make.
Życzliwość
Pozdrawiam
20rafalo
20.07.2010 (08:44)
gość
Właśnie dlatego nie lubię tego programu- ciągle coś mu nie pasuje. Chyba dlatego, że darmowy.
Wpisałem main bez rozszerzenia .c
Oto fragmenty pliku makefile:

" F_CPU = 1000000


# Output format. (can be srec, ihex, binary)
FORMAT = ihex


# Target file name (without extension).
TARGET = main

# MCU name
MCU = atmega16


# List C source files here. (C dependencies are automatically generated.)
SRC = $(TARGET).c D:/AVR programy/PROGRAMY2010/notapadd/ds18b20.c D:/AVR programy/PROGRAMY2010/notapadd/hd44780.c
# If this is left blank, then it will use the Standard printf version.
PRINTF_LIB = $(PRINTF_LIB_FLOAT)
#PRINTF_LIB = $(PRINTF_LIB_MIN)
#PRINTF_LIB = $(PRINTF_LIB_FLOAT)
"
Wg mnie plik makefile jest ok. Ale jakiś powód tego musi być.



abxyz
19.07.2010 (23:43)
autor strony
W menu programu MFile wybieramy opcję:

Makefile->Main file name

i w okienku, które się pojawi wpisujemy nazwę głównego pliku programu np: "main", nazwę pliku wpisujemy BEZ ROZSZERZENIA ".c";

Przypuszczam, że kolega tu wpisał nazwę pliku z rozszerzeniem, w takim przypadku wyskakuje podobny komunikat

20rafalo
19.07.2010 (22:58)
gość
To wiem..
Tylko rezultat jest taki:
> "make.exe" all

-------- begin --------
avr-gcc (WinAVR 20100110) 4.3.3
Copyright (C) 2008 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

make.exe: *** No rule to make target `main.elf', needed by `elf'. Stop.

> Process Exit Code: 2
> Time Taken: 00:02

PS za niego nie sworze pliku .elf
abxyz
19.07.2010 (22:25)
autor strony
Ale my nie wykorzystujemy w naszym kursie "krowiastego" programu AVRStudio :). Potrzebny plik Makefile tworzymy z pomocą programu MFile z pakietu WinAVR, tak jak opisane jest to w pierwszej części kursu. Następnie z poziomu konsoli(wiersz poleceń) lub edytora Notepad(też z pakietu WinAVR) wydajemy polecenie make, żeby skompilować i make program - żeby zaprogramować AVRa.


20rafalo
19.07.2010 (21:37)
gość
Programmers notepad nie potrafi stworzyc pliku .hex
make.exe: *** No rule to make target `main.elf', needed by `elf'. Stop.
I dlatego używam AVRStudio, gdzie wszystko jest w jednym miejscu
Poza tym czesto pojawiają się dziwne problemy, dowodem tego jest wiele postów od użytkowników na ten temat :-P
20rafalo
19.07.2010 (21:24)
gość
Zaznaczyłem, ale AVRStudio podmienia plik makefile i tworzy swój domyślny, a nie wiem jak w programie zaznaczyć te opcje
abxyz
19.07.2010 (21:15)
autor strony
Domyślnie funkcje printf i sprintf nie obsługują liczb zmiennopozycyjnych. Więc tworząc plik Makefile z pomocą programu MFile trzeba w menu "Makefile->printf() options" zaznaczyć opcję "floating point". Czy zaznaczyłeś tę opcję?
20rafalo
19.07.2010 (20:30)
gość
Już warninga nie ma - uruchomiłem ponownie kompa- prawo marfiego :-P
Dodam, że wszystkie programy kompiluje za pomocą AVRStudio 4.16 + niezbędny winAVR
Do tej pory wszystkie programy ze wszystkich kursów działały bezproblemowo.
Połączenia mam dobrze- raptem kilka przewodów :-)
czujników mam kilka - odpada uszkodzenie. Atmega się programuje dobrze, także nie wiem co jest nie tak.
Schematy mam z kursu.
20rafalo
19.07.2010 (19:46)
gość
A warning jest taki:
d:/winavr/lib/gcc/../../avr/include/util/delay.h:85:3: warning: #warning "F_CPU not defined for <util/delay.h>"
20rafalo
19.07.2010 (19:32)
gość
teraz jest przejrzyściej :-)
PS skompilowałem program "termometr" - wszystko ładnie poszło (1 ostrzeżenie, ale z zasady przy każdym programie mam- zależnie od kompilatora na ile jest dokładny :-P)
I mam problem - zamiast wyświetlać temperaturę wyświetla" ? 'C"
Co jest nie tak?
Z góry dzięki
abxyz
19.07.2010 (18:24)
autor strony
To

for(i=12; i>=3; a/=10 ,i--) str1[i] = a % 10 +'0';

jest równoważne temu

for(i=12; i>=3; i--)
{
str1[i] = a % 10 +'0';
a/=10;
}
20rafalo
19.07.2010 (18:10)
gość
Nie do końca rozumiem ten fragment kodu:
"static void lcd(unsigned long int a)
{
signed char i;

/* Zamiana 32 bitowej liczby bez znaku
na ciąg znaków ASCII */
for(i=12; i>=3; a/=10 ,i--)
str1[i] = a % 10 +'0';
"

jeśli wcisnę raz przycisk to n=1, czyli argument fun - a=1;
a=a/10 równe 0;
0%10+'0' to zero, a ma być 1. Po co jest to a=a/10
Jeśli ktoś może wskazać gdzie źle rozumuję byłbym wdzięczy.
Pozdrawiam
elektronik
19.07.2010 (01:32)
gość
świetny kurs, dzięki! ;)
abxyz
12.07.2010 (22:17)
autor strony
Przede wszystkim dlatego, że ATmega16(32) ma aż 32 linie we/wy - wygodne
carkar
12.07.2010 (09:37)
użytkownik
Dziwi mnie tylko przejście an ATMega16, przecież wszystkie tu programy zmieściłyby się na ATMega8 a niektóre nawet na ATTiny2313.

Uczeń
12.07.2010 (01:58)
gość
Zapewne na kolejną część kursu przyjdzie nam jeszcze sporo poczekać... Czekamy z niecierpliwością :)
elojason
28.06.2010 (14:53)
gość
Świetny kurs, czekam na kolejne odsłony, naprawdę znalazłem tutaj wszystko co potrzebne do szczęścia początkującemu. Takich rzeczy ludzie potrzebują:)

A tak a propos przykładu 4 czy Autor mógłby spróbować zawrzeć w następnym rozdziale albo dopisać do tego jakiś krótki przykład jak wygląda TRANSMISJA SZEREGOWA Z PC DO ATMEGA, taki jakiś najprostszy przykład, że z terminala wysyła 1 to na LCD jest jakiś napis a jak wyśle 2 to jest jakiś drugi napis.


Pozdrawiam
Michał
21.06.2010 (12:42)
gość
W przykładach z termometrem po nadaniu 0x44 i starcie konwersji powinno być chyba jakieś opóźnienie. Czas konwersji dla 12-bitowej rozdzielczości wynosi max 750ms. Dla 9-bitowej trochę poniżej 100ms.
Bez opóźnienia wypluwa bez przerwy 85 stopni. Przynajmniej tak jest w moim przypadku:)
A może się mylę ??
Polprzewodnikowy
09.06.2010 (16:19)
gość
Dobrze by było żebyś wyjaśnił co to są wskaźniki.
deep
07.06.2010 (16:38)
gość
Takie pytanie z innej beczki:
Jak zmienić częstotliwość wew. kwarcu na 12.8 MHz ?
Wiem, że idzie i bardzo mi to jest potrzebne.
Dzięki :)
Czeladzian
24.05.2010 (23:15)
gość
funkcja typu void nie zwraca wartości. W funkcjach void nie piszesz return(cos)
Dziechu
24.05.2010 (23:06)
gość
Doskonały kurs. Zaczynam już co nieco łapać. Do tej pory programowałem w assemblerze. Mam jedno pytanie - główną funkcję można nazwać main(), ale częściej widzę coś takiego - int main(void) - co daje to int na początku i void w nawiasie? Czasami jest także np. void costam()
miszczu
24.05.2010 (22:11)
gość
Witam.Wiem że może piszę trochę nie na temat,ale dopiero co zacząłem programować atmege8 i mam następujący problem:
-na komputerze stacjonarnym, programy się kompilują i atmega się programuje
-na lapku programy także się kompilują ale atmega nie chce się zaprogramować.Giveio zainstalowane,odinstalowałem drukarkę z portu lpt i dalej nic.Programuje programatorem pod gniazdo LPT.Czy port może być uszkodzony?W jaki sposób go sprawdzić?
pasta20
20.05.2010 (20:00)
gość
Fajny kursik :) he ja chyba zacznę się uczyć asembler fajnie jak byś go zaczął pisać.
Pozdrawiam.
ABXYZ
20.05.2010 (18:34)
autor strony
OK, dorobię i wstawię wersję kodu z RW podpiętym do masy.



Tu są pliki do przykładu pierwszego
http://hobby.abxyz.bplaced.net/kurs_avrgcc/01_RW.zip
Tym razem LCD przyłączony jest do portu D AVRa (sygnał RW do masy)

LCD_RS - AVR_PD0
LCD_RW - GND
LCD_E - AVR_PD1

LDC_D4 - AVR_PD2
LCD_D5 - AVR_PD3
LCD_D6 - AVR_PD4
LCD_D7 - AVR_PD5
jocker
20.05.2010 (11:16)
gość
Witam, świetna robota. Własnie znalazłem kurs i męcze sie z lekcją 5. Korzystam z płytki EVBavr 05 i wyswietlacz pracuje bez sprawdzania flagi zajetości. Czyli RW podpiety bezpośrednio do masy. Kombinuje jak przerobić podane w przykładzie 1 pliki żeby coś wyswietlić, ale nie wiele mi z tych kombinacji wychodzi. Czy mógłby ktoś pomóc ? jak przerobić podane pliki do mojego przykładu. Jeden z kolegów wcześniej juz sygnalizował taki problem , nie podał jednak co zmodyfikowal.
ABXYZ
12.05.2010 (14:12)
autor strony
U mnie czwarty przykład kompiluje się bez problemów, żadnych błędów czy ostrzeżeń.

link
http://www.nongnu.org/avr-libc/user-manual/group__avr__stdio.html
browar
12.05.2010 (00:05)
gość
witam,
mam mały problem, i nawet nie wiem z czego wynika przy pisaniu komunikacji przez RS-a w tym miejscu

static FILE mystdout = FDEV_SETUP_STREAM(USART_Transmit, NULL, _FDEV_SETUP_WRITE);

wypluwa błąd
warning: initialization from incompatible pointer type
nie wiem o co chodzi, byłbym wdzieczny jakbyś mogł po krótce wytłumaczyć tą funkcje albo jakiś link do stronki żeby ją skumać,
pozdrawiam
ABXYZ
07.05.2010 (23:41)
autor strony
1-Wire nie jest (na razie) tematem naszego kursu. Wykorzystałem termometr ds18b20 jedynie po to, aby nieco uatrakcyjnić przykłady. Odsyłam do Google, w sieci jest zatrzęsienie materiałów na ten temat.
Paweł
04.05.2010 (09:41)
gość
A w jaki sposób podłączyć dwa ds18b20? chodzi mi o część kodu,który to obsługuje. Pozdrawiam
lemis
25.04.2010 (12:39)
gość
próbowałem zaprogramować swój układ innym programatorem i wszystko działało poprawnie. Ten programator też był podobno sprawdzany i powinien działać ale gdy próbuję wgrać program wyskakuje mi taki komunikat.
> "make.exe" program
avrdude -p atmega16 -P usb -c usbasp -U flash:w:led.hex

avrdude: warning: cannot set sck period. please check for usbasp firmware update.
avrdude: error: programm enable: target doesn't answer. 1
avrdude: initialization failed, rc=-1
Double check connections and try again, or use -F to override
this check.


avrdude done. Thank you.

make.exe: *** [program] Error 1

> Process Exit Code: 2
> Time Taken: 00:01
Hass-pol
22.04.2010 (20:24)
gość
Witam wszystkich.
Jeszcze raz dziękuje autorowi za ten kurs.

Tez miałem problem z LCD.
Do testów używam już gotowej płytki testowej w której oryginalnie producent nie wyprowadził wejścia RW... tylko podlaczył RW na stałe do masy, co okazało się kluczowe.

W Bascomie wszystko pięknie działa bez RW i myślałem ze w C też będzie, jednak się myliłem.



Realista
22.04.2010 (10:13)
gość
Tak, teraz mi ruszył. Możliwe, że to opóźnienia wszystko psuły. Dzięki;)
ABXYZ
21.04.2010 (20:37)
autor strony
Wyświetlacze od poszczególnych producentów mogą się między sobą nieco różnić, i pewnie stąd te problemy.

Wprowadziłem zmiany w pliku hd44780.c Tak jak napisał Kowalski, zwiększyłem opóźnienia w funkcji inicjalizacji.

Czy teraz działa ???
Realista
21.04.2010 (17:14)
gość
Hm mi też coś nie chce działać z tym wyświetlaczem. Piny zmieniłem na piny portu c, układ uruchamiam na atmega168 i kwarcu 8Mhz i lipa.W bascomie chodzi mi wszystko idealnie a tutaj narazie nie mam pojecia o co kaman. Ale kurs ekstra jak zawsze;)
Kowalski
20.04.2010 (15:45)
gość
Witam, problem z wyświetlaniem krzaków tkwił w czasach opóźnień w funkcji inicjalizacji, wystarczyło trochę zwiększyć opóźnienia , aby wszystko działało jak Bóg przykazał :P
Kendzior
19.04.2010 (18:00)
gość
U mnie też jest podobny problem, zamiast tekstu wyświetla krzaki, a dokładnie kółka i strzałki, widać, że kursor mruga w różnych miejscach, a w dodatku wyświetla się tylko górna linia. Mój układ to: Attiny2113 i kwarc 16MHz.
ABXYZ
18.04.2010 (22:54)
autor strony
No nie wiem co jeszcze poradzić...
Sprawdziłem wszystkie cztery przykłady na atmega16 z zegarem 1MHz , 4MHz, 8MHz i 16MHz i wszystko ładnie działa.

Może spróbuj uruchomić jakiś gotowy, prosty przykład z wyświetlaczem w BASCOMie.
Żeby sprawdzić czy błąd nie tkwi w sprzęcie.
Programik w rodzaju "Hello World" w bascomie zajmuje tylko kilka linijek.
Lichu
18.04.2010 (20:02)
gość
Fuse bity zrobiłem, nawet coś w okienku mi się pojawiło i success :P

Ale mimo tego nadal nie działa :( Próbowałem pododawać opóźnienia czasowe (jeszcze większe) i i tak nic to nie pomogło...

A najgorsze jest to, że nie znam przyczyny dlaczego się tak dzieje :(

Jakieś pomysły?
ABXYZ
18.04.2010 (18:34)
autor strony
Zapisujemy wartość 0xff do Extended Fuse Byte

Trzeba otworzyć okienko wiersza polecenia

przycisk Start>>Programy>> .. coś_tam... >>Wiersz polecenia
(nie pamiętam dokładnie gdzie to jest, nie mam już windowsa)

i wpisać polecenie

avrdude.exe -c stk500v2 -p m128 -U efuse:w:0xff:m
Lichu
18.04.2010 (17:07)
gość
Zakładając, że mam masakrę jeżeli chodzi o kable i nie chcę przelutowywać, jakbym chciał programować fuse bit to w <właśnie, w którym miejscu dokładnie, w okienku output? WinAVR czy w mfilu?> musiałbym wpisać:

avrdude -c stk500v2 -p m16 -U hfuse:w: ...


<stk500v2 = AVRPROG USBv2 ?>
i teraz w jaki sposób zapisać, że chodzi mi o bit M103C?

Po namyśle, dzięki, że mi to uświadomiłeś, kiedyś czytałem o tej kompatybilności, nie zwróciłem na to specjalnie uwagi i w sumie bardzo będzie mi przeszkadzać w przyszłościowych projektach :P
ABXYZ
18.04.2010 (13:42)
autor strony
Drobna komplikacja. Fragment z atmega128 datasheet

The ATmega128 is by default shipped in ATmega103 compatibility mode. Thus, if the parts are not programmed before they are put on the PCB, PORTC will be output during first power up, and until the ATmega103 compatibility mode is disabled.

Można przyłączyć wyświetlacz do innego portu niż C, albo wyłączyć "ATmega103 compatibility mode" programując fusebity.
Lichu
18.04.2010 (12:07)
gość
Ble... sorki, jeszcze raz:

Wyprowadzenia poszczególnych końcówek:
RW - PC0
RS - PC7 (tak się wlutowało :P)
E - PA7
D7 - PC4
D6 - PC3
D5 - PC6
D4 - PC5
Lichu
18.04.2010 (12:06)
gość
Siema,

Nadal nic mi to nie pomaga :(

Wyprowadzenia poszczególnych końcówek:
RW - PC0
RS - PC7 (tak się wlutowało :P)
E - PA7
D7 - PC4
D6 - PC3
D5 - PC5
D4 - PC5

Ustawiwszy wszystkie piny jako wyjścia i na podciąg do Vcc, miernikiem mierząc w stosunku do gnd sygnały w odpowiednich miejscach występowały (+4.5 V).

Dzielnik napięcia zrobiłem z 1 kohm... (2 rezystorki 510 ohm) Na rysunku jest prawie 5 razy większy, tak więc czy to może być problemem?

No i tak - po włączeniu zasilania, nawet bez inicjalizacji w uP, wyświetlacz się zapala.

Z innej stronki ściągnąłem sterowniki i również ten sam problem.

Wszelkie modyfikacje w pliku .h jak i makefile zrobiłem.

F1 ? :P
ABXYZ
17.04.2010 (22:32)
autor strony
Dla częstotliwości pracy AVR większej niż 8MHz faktycznie przykłady z wyświetlaczem mogły nie działać, Już jest poprawione.
Skopiuj ze stronki plik hd44780.h i skompiluj przykład ponownie, nie zapominając o wpisaniu do pliku Makefile częstotliwości pracy uC.
Lichu
17.04.2010 (19:16)
gość
Faktycznie...
Po głębszym zbadaniu problemu program nie przebija się do pętli while(1) - wywala się na lcd_init();

LCD: wc0802c v1.0
uP: zl7avr

Nawet jak zakomentuję tego {...}while(bf), to nie mogę nic na wyświetlaczu zrobić :(

Polutowane piny sprawdziłem z 10 razy...
ABXYZ
17.04.2010 (18:07)
autor strony
Jeśli próbujesz uruchamiać przykłady na jakimś "kupnym" zestawie startowym (tak przypuszczam), to może się zdarzyć, że linia R/W wyświetlacza LCD jest na stałe przylutowana do masy, i w tedy przykłady z kursu nie będą działać.
Gdy sygnał R/W wyświetlacza jest przylutowany do masy, wtedy mikroprocesor może wysyłać dane do wyświetlacza, ale nie może niczego z wyświetlacza odczytać.
A w naszych przykładach funkcje do obsługi LCD odczytują z wyświetlacza flagę zajętości, żeby sprawdzić, czy wyświetlacz jest gotowy na przyjęcie nowych rozkazów i danych.
Zamiast sprawdzać flagę zajętości, można by w programie wstawiać opóźnienia, czas na zakończenie wewnętrznych operacji wyświetlacza, a linię R/W przyłączyć na stałe do masy. Ale to nie jest zbyt eleganckie rozwiązanie.

Lichu
17.04.2010 (16:57)
gość
Siema,

Kurs jak dla mnie bomba :D Na każdą kolejną część coraz większe oczekiwanie :P

W każdym razie mam taki problem.
Podłączyłem wyświetlacz, polutowałem troszkę. Odpalam program - nie działa. Kombinuję, kombinuję. Nic nie wychodzi. Nagle spostrzegam, że wszystkie diody (podłączone pod port B - x 8) się palą. Od tamtego czasu nie mogę za pomocą czegokolwiek zrealizować jakiegokolwiek programu, tylko diody się świecą.

Czy to oznacza, że żem przez przypadek zepsuł moją atmegę128? :(

Fuse bitów nie ruszałem.
Programuję w WinAVR.
ABXYZ
16.04.2010 (12:29)
autor strony
> Kowalski

Można by na próbę uruchomić jakiś prosty przykład z wyświetlaczem w BASCOMie, aby dowiedzieć się czy problem stoi po stroni programu, czy po stronie sprzętu.
ABXYZ
16.04.2010 (11:30)
autor strony
Wygląda na to że, programator działa, ale AVR nie reaguje.

Zwykle taki komunikat wyskakuje, gdy układ jest błędnie zmontowany. Trzeba sprawdzić połączenia: zasilanie AVRa, reset, połączenia z programatorem.
lemis
16.04.2010 (10:25)
gość
po zainstalowaniu tego drugiego sterownika przy próbie wgrania programu wyskakuje takie coś:

> "make.exe" program
avrdude -p atmega16 -P usb -c usbasp -U flash:w:led.hex

avrdude: warning: cannot set sck period. please check for usbasp firmware update.
avrdude: error: programm enable: target doesn't answer. 1
avrdude: initialization failed, rc=-1
Double check connections and try again, or use -F to override
this check.


avrdude done. Thank you.

make.exe: *** [program] Error 1

> Process Exit Code: 2
> Time Taken: 00:01

Czy avrdude jakoś się ustawia?
Kowalski
16.04.2010 (10:01)
gość
Witam, muszę powiedzieć na wstępie, że kurs jest bardzo dobry. Jednak pojawił się problem podobny jak u kolegi poniżej. Układ połączeń jest podpięty zgodnie z tym podanym na stronie i odpowiednio zmienione PINy na PORTB w programie, gdyż używam Attiny2313. Skompilowany program wrzuciłem na procka, jednak o dziwo nic nie działa, wyświetla się tylko strzałka. Udało mi się także dostać inny gotowy program w którym przewija się tekst, lecz efekt jest ten sam i wyświetla mi przewijające się krzaki, zamiast tekstu. Podłączenie sprawdzałem juz chyba ze 100 razy, ale wydaje sie być ok, fuse bity też sa ustawione odpowiednio do podłączonego kwarca. nie wiem w czym moze tkwić problem
ABXYZ
15.04.2010 (11:09)
autor strony
Konfiguracja programatora USBasp

1. W programatorze trzeba połączyć zworkę Slow SCK, pozostałe dwie zworki mają być rozwarte, schemat poniżej

http://www.fischl.de/usbasp/bilder/usbasp_circuit.png

2. Instalujemy w systemie windows sterownik ze strony
http://www.fischl.de/usbasp/

3. Składamy układ jak na tym schemacie
http://hobby.abxyz.bplaced.net/img/art002_schemat1.png

I powinno działać

Jeśli dalej nie działa, to można spróbować z tym sterownikiem
http://www.ulrichradig.de/home/uploads/File/USBasp/091122_USBASP_Driver_Win32-Win64_v0_1_12_1.zip
lemis
14.04.2010 (17:50)
gość
dzięki 20rafalo ale właśnie w tym tkwi problem. Nie wiem jak go skonfigurować. Jeśli ktoś jest w stanie mi pomóc bardzo o to proszę.
aa
14.04.2010 (16:28)
gość
Artykuł bardzo dobry, ale wydaje mi się że powinno być mniej kursu czystego C, bo to można wyczytać z książek, a więcej opisów właśnie specjalizowanych funkcji do obsługi np. wyświetlaczy czy pamięci EEPROM procesora.

Ale ogólnie fajny kurs :)
Paweł
13.04.2010 (23:16)
gość
Najgorsze w tym wszystkim jest to, że znowu trzeba będzie czekać :(
Pozdrowienia dla autora!
20rafalo
12.04.2010 (18:23)
gość
lemis masz problem z programatorem - tzn nie skonfigurowałeś urządzenia w windows.

Ja miałem kiedyś podobnie. Wystarczyła konfiguracja.
lemis
10.04.2010 (14:17)
gość
Po napisaniu programu kompilacja przebiega poprawnie, a gdy chcę wgrać program do mikrokontrolera to pojawia się taki komunikat:

>make.exe program
avrdude -p atmega16 -P usb -c usbasp -U flash:w:led.hex
avrdude: error: could not find USB device "USBasp"; with vid=0x16c0 pid=0x5dc
make.exe: *** [program] Error 1

> Process Exit Code: 2
> Time Taken: 00:01

Co jest nie tak?
ABXYZ
09.04.2010 (11:48)
autor strony
Ja takie bardziej wymagające fragmenty kodu piszę po prostu w asemblerze. Bo programowanie w C 8-bitowych mikrokontrolerów przypomina czasem grę w bierki w rękawicach bokserskich :) Niewykluczone, że napiszę również krótki kurs asemblera, Ale nie byłby to kurs pisania programów w czystym asemblerze, a raczej zajmowalibyśmy się łączeniem zalet C i asemblera w jednym programie.
ABXYZ
09.04.2010 (02:21)
autor strony
Spróbuj do tego celu użyć funkcję _delay_loop_1 lub _delay_loop_2

http://www.nongnu.org/avr-libc/user-manual/group__util__delay__basic.html
Raffsen
08.04.2010 (21:06)
gość
Tak, nowe układy mają ustawiony wewnętrzny oscylator na 1 MHz. Nadmienię tu, że zmieniłem za pomocą "fuse bits" opcję na zewnętrzny rezonator kwarcowy i dodałem kwarc 8 MHz. Używam VMLAB'a wraz z zintegrowanym AVR GCC i programu PonyProg2000. Zauważam dużą zmianę prędkości działania funkcji pomiędzy aktywacją oscylatora wewnętrznego 1 MHz a kwarcem zewnętrznym 8 MHz na podstawie migającej diodki, czyli wnioskuję, że 'fuse bits' są ustawione poprawnie. Mimo to nie potrafię zejść do opóźnienia np 20 us za pomocą "_delay_us(20);" - otrzymuję dużo większą wartość - sprawdzone na oscyloskopie znajomego. I tu kolejne moje pytanie czy da się to jakoś poprawić, czy należy informować kompilator o taktowaniu zewnętrznego kwarcu - wspomniane "#define F_CPU 8000000UL", czy może da się zadeklarować opóźnienia jakoś bardziej precyzyjnie - mam ściśle określone parametry podłączanego układu do których muszę się dostosować, a za pomocą funkcji "_delay_us(20);" jest to chyba niemożliwe... Bardzo proszę o pomoc.
Paweł
08.04.2010 (02:31)
gość
Wielki szacun dla autorów! Właśnie czegoś takiego brakowało.
ABXYZ
07.04.2010 (16:43)
autor strony
Nowe (ze sklepu) układy AVR atmega16 skonfigurowane są do pracy z wewnętrznym oscylatorem RC 1MHz; jeśli chcemy zwiększyć częstotliwość pracy mikrokontrolera, trzeba odpowiednio konfigurować tzw. fusebity.

Makrodefinicja w rodzaj:

#define F_CPU 1000000UL

jest tylko informacją dla funkcji: _delay_ms, _delay_us

Jeżeli chciałbyś podłączyć do atmega16 rezonator kwarcowy 3-8 MHz, to w wierszu poleceń piszesz:

avrdude -c usbasp -p m16 -U lfuse:w:0xef:m

oczywiście w miejsce usbasp wstawiasz swój programator

A żeby powrócić do poprzedniej konfiguracji (wewnętrzny oscylator RC 1MHZ)

avrdude -c usbasp -p m16 -U lfuse:w:0xe1:m

W następnej części kursu zamierzam omówić ten temat dokładniej.

Raffsen
07.04.2010 (00:53)
gość
Witam, z góry uprzedzam że dopiero odkrywam tajniki AVR-ków, oraz GCC, a już niepokoi minie pewna rzecz, mianowicie mam kwarc zewnętrzny 8 MHz i mimo zapisania informacji dla kompilatora w takiej postaci: "#define F_CPU 4000000UL", to np "_delay_ms(20);" nie odpowiada w rzeczywistości 20 ms, tak samo np "_delay_us(20);" nie odpowiada 20 us. Czasy te są nawet kilkakrotnie większe od zadanych - stąd pytanie jak to zorganizować w jakiś inny, bardziej precyzyjny sposób, no i dlaczego tak się dzieje. Bardzo proszę o pomoc. Pozdrawiam
sq2ajo
03.04.2010 (21:55)
gość
Super kurs.
Interesuje mnie jak na podstawie licznika owiec zrobic taki licznik aby od/do zadanej liczby dodawał lub odejmował jeden po każdym nacisnieciu przycisku UP lub DN.
I zeby ta wartosc byla wysylana do portu.
Ale to nie wszystko , musze uzyc dwuch portow , bo potrzebuje wartosc 16 bitową.
ABXYZ
02.04.2010 (16:50)
autor strony
W wierszu poleceń

aby odczytać hfuse:

avrdude -c usbasp -p m16 -U hfuse:r:con:h

aby zapisać hfuse:

avrdude -c usbasp -p m16 -U hfuse:w:0xD9:m

W miejsce usbasp należy oczywiście wpisać własny programator

Uwaga!!! Trzeba zachować ostrożność zapisując hfuse, bo można przypadkiem zablokować możliwości programowania AVRa programatorem AVR-ISP.
KR
02.04.2010 (15:01)
gość
OK, nie wiedziałem, dzięki. Doszedłem do tego, że należy ustawić low fuse 0xE1 i high fuse 0xD9, używam avrdude, ale jakoś ten JTAG nie chce się wyłączyć mimo wszystko...może ktoś wie jak ustawić fusy w avrdude tak aby wyłączyć JTAG?
ABXYZ
02.04.2010 (01:13)
autor strony
Nie, takiego ograniczenia nie ma, to by było niepraktyczne. Dla każdego sygnału: RS, RW, E, DB4, DB5, DB6, DB7 możesz wybrać dowolną linię we/wy AVRa
Twój problem wynika chyba z tego, że w atmega16 domyślnie jest włączony interfejs JTAG i w tej konfiguracji nie można wykorzystywać pinów PC2-PC5 jako uniwersalnych cyfrowych wejść/wyjść. JTAG można wyłączyć, jeżeli jest niepotrzebny, programując fusebity.
KR
02.04.2010 (00:39)
gość
Pozdrawiam autora, uważam, że powinien on wydać książkę na ten temat. Ogromny talent dydaktyczny.

Mam jeszcze pytanie:
Próbowałem podłączyć wyświetlacz LCD w następujący sposób:
RS -> PD0
RW -> PD1
E -> PD2
DB4 -> PC0
DB5 -> PC1
DB6 -> PC2
DB7 -> PC3

Niestety wyświetlacz podłączony w ten sposób nie działa...
Gdy podłączam wszystkie piny do jednego portu (np. D) to działa poprawnie. Czy jest to zasadą, że nie można podłączać wyświetlaczy do różnych portów???
Realista
31.03.2010 (23:31)
gość
Hm myślałem, że już pozamiatane i nie ma co czekać na następną część kursu ... a tu niespodzianka na Święta :)Była mi bardzo potrzebna komunikacja RS232 w C- kurs się nie pojawiał to musiałem rozpykać RSa sam, ale co tam teraz się dokształce. Pisze akurat prace inżynierską w tym języku (wcześniej pisałem w bascomie) i ten kurs bardzo mi się przydaje:) Wielkie dzięki dla szefa tej stronki ;D
Bogdan
31.03.2010 (09:43)
gość
Dzięki z kolejną część kursu, w żadnej książce nie jest w taki zrozumiały sposób i z tyloma przykładami wytłumaczone programowanie uC, gdyby nie ten kurs moje umiejętności w tej dziedzinie były by mizerne.
Rafał
31.03.2010 (01:01)
gość
Długo czekaliśmy, ale się doczekaliśmy :) Gratuluję wytrwałości :) Kurs w sam raz na święta ;) Jeszcze raz wielkie gratulacje.
Judas
30.03.2010 (14:29)
gość
Dzięki wielkie za kurs!
W końcu formuła na jaką czekałem - teoria i praktyka w każdym odcinku. Wszystko jasno i klarownie. Jestem fanem tego kursu i z niecierpliwością oczekuję na każdą jego część. Mógłby się jednak ukazywać częściej :) Ale widać ogromny wkłada pracy autora za należy mu się ogromny szacunek.

Powodzenia i wytrwałości
Pozdrawiam
Łukasz
30.03.2010 (09:43)
gość
Fajny prezent na święta, dzięki :)
Vansel
29.03.2010 (22:23)
gość
NARESZCIE! Tyle czasu czekałem na kolejną część kursu.
Z 4 części nie byłem zbytnio zadowolony, bo opierała się głównie na samej składni języka C. 5 część mnie zaskoczyła, coś bardziej złożonego w tematyce elektroniki połączone z programowaniem.
Czekam na kolejną część.
Pozdrawiam, Vansel.
Czeladzian
29.03.2010 (21:50)
gość
wreszcie jakieś sensowne zajęcie na święta!!! :)
ogrudek
29.03.2010 (21:31)
gość
jestem pełen uznania! GRATULUJE wytrwałosci i pomyslu!
Kursy>Kurs AVR-GCC>Kurs AVR-GCC, cz.5
Ostatnie artykuły