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

Kurs AVR-GCC cz.4

17.08.2009 ABXYZ

Kiedy będzie dalsza część kursu? W poprzedniej części kursu omawiałem zmienne, operatory, pętle oraz instrukcje sterujące jak if i switch. W tej części kursu wprowadzimy do programu funkcje i tablice, omówię temat zakresu widoczności zmiennych oraz kod BCD. W uruchamianych przykładach będziemy wykorzystywać diody LED, siedmiosegmentowe wyświetlacze LED, klawiaturę wykonaną z dwunastu przycisków oraz buzzer.

Tablice

Jeśli w programie jest większa liczba danych jednego typu ,to zamiast deklarować wiele pojedynczych zmiennych, lepiej jest utworzyć tablice. W tablicy można przechowywać jednocześnie wiele zmiennych jednakowego typu. Wszystkie elementy tablicy są ponumerowane, dostęp do dowolnej zmiennej w tablicy uzyskuje się podając nazwę tablicy i numer elementu.

Tablicę deklaruje się pisząc kolejno: typ danych, nazwę tablicy i objętą parą nawiasów kwadratowych [] liczbę elementów tablicy.

int main(void) { /* Deklaracja tablicy 'pomiar' o 64 elementach typu char */ char pomiar[64]; /* Deklaracja tablicy 'wysokosc' o 32 elementach typu int */ int wysokosc[32]; /* Deklaracja tablicy 'temperatura' o 16 elementach typu double */ double temperatura[16];

Elementami tablic mogą być wartości typów: char, int, float, double oraz: struktury, unie, pola bitowe i  wskaźniki o których będzie mowa w następnej części kursu. Elementami tablicy mogą być też same tablice; w taki sposób tworzy się w języku C tablice wielowymiarowe.

/* 2 elementowa tablica 40 elementach tablic typu char */ char bufor[2][40]; /* Tablica trójwymiarowa */ char jakas_tablica[4][8][3];

Definiując tablicę można jednocześnie tablicę inicjalizować wartościami początkowymi. W tym celu dopisuje się na końcu definicji: operator = , a po nim, objęte parą nawiasów klamrowych {}, wartości początkowe kolejnych elementów tablicy rozdzielone przecinkami.

/* Definicja tablicy z jednoczesną inicjalizacją wartościami początkowymi dwóch pierwszych elementów tablicy */ char tablica[4] = {0x01,0x02}; /* Definicja tablicy z jednoczesną inicjalizacją wartościami początkowymi wszystkich czterech elementów. W takim przypadku można nie wpisywać w definicji ilości elementów tablicy, kompilator sam to wyliczy. */ char tablica[] = {0x01,0x02,0x04,0x08}; /* Definicja z inicjalizacją tablicy dwuwymiarowej */ char inna_tablica[][2] = { 0x31,0x02, 0xf1,0x02, 0x41,0xf2, 0xf1,0xc2, 0x11,0xa2 };

W programie elementami tablicy posługujemy się w identyczny sposób jak pojedynczymi zmiennymi. Każdy element tablicy ma nazwę składającą się z nazwy tablicy oraz objętego parą nawiasów kwadratowych indeksu(numeru elementu). Elementy tablicy numerowane są zaczynając od zera, a nie od jedynki, należy to koniecznie zapamiętać.

/* Przypisanie pierwszemu elementowi tablicy wartości 23.5 */ temperatura[0] = 23.5; /* Przypisanie drugiemu elementowi tablicy wartości 22.0 */ temperatura[1] = 22.0; /* Przypisanie ósmemu elementowi tablicy wartości 17.5 */ temperatura[7] = 17.5; /* Przypisanie jakiejs_zmiennej wartości czwartego elementu tablicy */ jakas_zmienna = temperatura[3];

Trzeba też pamiętać, że jeżeli zdeklarujemy tablicę n elementową i spróbujemy zapisać coś pod indeksem równym lub większym n to kompilator może nie zgłosić błędu, ale skutkować to będzie nieprawidłowym działaniem programu.

Przy operacjach na tablicach szczególnie użyteczna może być instrukcja pętli for, gdzie licznik pętli będzie indeksem tablicy.

unsigned char i,j; int tablica_1[16]; int tablica_2[16]; int tablica_3[10][10]; /* Kolejnym elementom tablicy przypisane zostaną wartości: 0,10,20,30..150 */ for(i = 0; i < 16; i++) tablica_1[i] = i * 10; /* Kopiowanie zawartości tablica_1 do tablica_2 */ for(i = 0; i < 16; i++) tablica_2[i] = tablica_1[i]; /* Wypełnienie tablicy dwuwymiarowej */ for(i = 0; i < 9; i++) for(j = 0; j < 9; j++) tablica_3[i][j] = i * j;

Na tym etapie nauki, te informacje na temat tablic mam wystarczą, dalej w kursie jeszcze powrócimy do tablic..

Funkcje

Program składa się z ciągu instrukcji zapisanych w pamięci i wykonywanych jedna po drugiej. Obok instrukcji, które realizują konkretne zadania, na przykład włączają i wyłączają diodę LED, istnieją też specjalne instrukcje sterujące przebiegiem programu jak np.: if-else, switch, for, while; które były tematem poprzedniej części kursu.

Obok wspomnianych instrukcji sterujących istnieje możliwość tworzenia podprogramów. Zwykle dłuższe programy dzielone są na odrębne fragmenty, nazywane podprogramami lub procedurami. Spośród podprogramów wyróżnić można podprogram pierwszy (główny), od którego pierwszej instrukcji rozpoczyna się działanie całego programu. Umieszczając w programie specjalną instrukcję - instrukcję wywołania podprogramu - można wykonać wybrany podprogram.

podprogramy
Przebieg przykładowego programu składającego się z podprogramu głównego i dwóch podprogramów. Cały program zaczyna się od pierwszej instrukcji podprogramu głównego.

A jaki może być pożytek z podziału programu na podprogramy ? Na przykład, jeśli jakiś dłuższy fragment kodu powtarza się w wielu miejscach programu, oczywistym rozwiązaniem będzie umieścić ten fragment w odrębnym podprogramie. W wyniku otrzymamy znacznie krótszy program o przejrzystej strukturze. W tak napisanym programie łatwiej jest wprowadzać zmiany. Oczywiście sposób podziału programu nie bywa przypadkowy, podprogramy zwykle realizują konkretne zadania np. odczyt czujnika temperatury, włączenie sygnalizacji albo uruchomienie serwomechanizmu.

W języku C podprogramy nazywa się funkcjami. Każdy program w języku C musi zawierać funkcje o nazwie "main" - każda funkcja ma nadaną nazwę (identyfikator). Funkcja main jest wspomnianym wcześniej podprogramem pierwszym(głównym), od którego pierwszej instrukcji zaczyna się wykonanie całego programu.

/* Najprostszy program-całość umieszczona w funkcji main */ #define F_CPU 1000000L #include <avr/io.h> /* Definicja funkcji main */ int main(void) { /* Instrukcje naszego programu */ }

Obok funkcji main programista może tworzyć(definiować) własne funkcje, można też wykorzystywać gotowe funkcje z biblioteki standardowej języka C, dostarczonej razem z kompilatorem. Razem z kompilatorem avr-gcc wykorzystuje się bibliotekę AVR Libc, która pełni rolę biblioteki standardowej, dopasowanej do możliwości 8-bitowych mikrokontrolerów AVR. W  przykładach z kursu często używana jest funkcja z biblioteki AVR Libc o nazwie _delay_ms. Funkcja ta wprowadza do programu opóźnienie o zadanym w milisekundach okresie czasu; w avr-libc dostępna jest także podobna funkcja _delay_us, dla której długość opóźnienia podaje się w mikrosekundach.

Instrukcja wywołania funkcji składa się z nazwy funkcji i objętej parą nawiasów okrągłych listy argumentów, i kończy się średnikiem. Argumenty funkcji to dane przekazywane do funkcji, jak na przykład wartość opóźnienia w funkcji _delay_ms. Jeśli funkcja oczekuje kilku argumentów, kolejne argumenty oddziela się przecinkami. Gdy funkcja nie oczekuje żadnego argumentu, to uruchamiając ją, po nazwie funkcji wstawiamy "pustą" parę nawiasów okrągłych ().

/* Funkcje _delay_ms, _delay_us oczekują przy wywołaniu jednego argumentu typu double -wartości opóźnienia */ /* Argumentem funkcji jest wartość stała 80 */ _delay_ms(80); /* Argumentem funkcji będzie wartość wyrażenia jakas_zmienna+20 */ _delay_us(jakas_zmienna + 20); /* Funkcje mogą oczekiwać jednego lub większej liczby argumentów albo żadnego */ /* Jeśli jakaś funkcja oczekuje kilku argumentów, kolejne argumenty oddziela się przecinkiem. */ jakas_funkcja(10, 17.3, jakas_zmienna); /* Inna funkcja nie oczekuje żadnego argumentu */ inna_funkcja();

Po zakończeniu działania funkcje mogą zwracać dane. Na przykład funkcja rand z biblioteki standardowej zwraca wartość typu int, wartością tą jest liczba pseudolosowa z przedziału od 0 do RAND_MAX(0x7FFF). Jeżeli funkcja zwraca wartość, wtedy całe wyrażenie: nazwa_funkcji(argumenty_funkcji) posiada wartość i typ, podobnie zmienne. Wtedy, przykładowo, można instrukcję wywołania funkcji "nazwa_funkcji(argumenty_funkcji)" postawić po prawej stronie operatora przypisania, żeby zwróconą przez funkcję wartość przypisać od zmiennej; albo umieścić w warunku instrukcji if-else.

/* Funkcja rand, obliczająca liczbę losową, zwraca wartość typu int */ jakas_ zmienna = rand() + 100; /* Jeśli wartość zwrócona przez funkcje rand będzie większa od 10, wtedy warunek w instrukcji if będzie spełniony */ if( rand() > 10) {

Funkcja może zwracać tylko wartość jednej zmiennej lub nie zwracać niczego.

Własną funkcję tworzy(definiuje) się wpisując kolejno: typ zwracanej wartości, nazwę nowej funkcji, listę parametrów funkcji objętą parą nawiasów okrągłych (); deklaracje kolejnych parametrów oddziela się przecinkami. W  definicji funkcji argumenty nazywa się parametrami. Następnie, między parą nawiasów klamrowych {}, umieszcza się instrukcja po instrukcji kod funkcji.

/* Definicja funkcji */ typ nazwa_funkcji(parametry) { /* Instrukcje wewnątrz funkcji */ }

Jeśli w definicji funkcji jako zwracany typ wstawi się słówko void, oznaczać to, że funkcja nie będzie niczego zwracać; a jeśli wstawimy void w miejsce listy parametrów, to funkcja nie będzie oczekiwać żadnych argumentów. W naszych krótkich programach definicje funkcji będą umieszczane w  pliku przed funkcją main.

/* Definicja funkcji które niczego nie zwraca i nie oczekuje żadnych argumentów */ void io_init(void) { DDRD = 0x0f; PORTD = 0xf0; DDRB = 0x0f; } /* Definicja funkcji main */ int main(void) { /* Wywołanie funkcji io_init */ io_init();

Poniżej znajduje się przykład definicji funkcji, która przyjmuje dwa argumenty typu unsigned int i nie zwraca niczego. Wartości argumentów dostępne są wewnątrz funkcji w specjalnie tworzonych zmiennych. W chwili wywołania funkcji, tworzone są zmienne o typach i nazwach jakie znajdują się na liście parametrów. Zmienne te są inicjalizowane wartościami argumentów podanych przy wywołaniu funkcji. W przykładzie poniżej, w funkcji beep dostępne są dwie zmienne o nazwach: frequency i  duration zawierające wartości wysłanych do funkcji argumentów.

/* Definicja funkcji beep */ void beep(unsigned int frequency, unsigned int duration) { unsigned int i,t,n; t = 125000/frequency; n = (250UL*duration)/t; PORTB |= 0x01; PORTB &= ~0x02; for(i=0; i < n; i++) { PORTB ^= 0x01; PORTB ^= 0X02; _delay_loop_2(t); } } /* Definicja funkcji main */ int main(void) { int f = 440, d = 250; /* Przykłady wywołanie funkcji beep */ beep(1000,1000); /* Do funkcji zostaną przekazane wartości zmiennych f i d */ beep(f,n);

Kolejny przykład to definicja funkcji, która zwraca wartość typu unsigned char i oczekuje argumentu typu unsigned char. Tu nową rzeczą jest instrukcja return, wymusza ona natychmiastowy powrót z funkcji. Jeżeli funkcja coś zwraca, wtedy po prawej stronie instrukcji return wstawia się wyrażenie, którego wartość zostanie zwrócona po wyjściu z funkcji. Jak już pisałem, gdy funkcja coś zwraca, instrukcja wywołania funkcji, całe wyrażenie: "nazwa_funkcji(argumenty_funkcji)" ma przypisaną wartość zwracaną przez funkcję.

/* Funkcja zmienia wartość argumentu na bajt w kodzie BCD */ unsigned char bin2bcd(unsigned char bin) { /* Jeśli bin > 99, to funkcja zwraca wartość argumentu bin */ if(bin>99) return bin; /* Funkcja zwraca wartość wyrażenia po prawej stronie instrukcji return */ return bin/10<<4 | bin%10; } /* Wywołanie funkcji bin2bcd, zwrócona przez funkcję wartość 0x99 zostanie przypisane zmiennej */ jakas_zmienna = bin2bcd(99);

Tablice przekazywane są do funkcji w odmienny sposób, niż zmienne. Jeśli przekazuje się do funkcji argumenty nie będące tablicami, to w momencie wywołania funkcji tworzone są zmienne o nazwach parametrów funkcji, zmienne te inicjalizowane są wartościami argumentów podanymi w instrukcji wywołania funkcji. Natomiast, jeśli argumentem funkcji jest tablica, to w momencie wywołania funkcji, nie jest tworzona kopia tej tablicy; wewnątrz funkcji wszystkie działania przeprowadzane są na oryginalnej tablicy podanej jako argument w instrukcji wywołania funkcji. Przykład:

/* Definicja funkcji */ void funkcja(int n, int tablica[]) { /* Wypełnia tablicę */ for(; n; n--) tablica[n-1] = n; } int main(void) { /* Definicja tablicy */ int tablica[16]; int n = 16; /* Zmienne przekazywane są do funkcji przez wartość, a tablice przez wskaźnik. Po wykonaniu funkcji wartość zmiennej n nie zmieni się, natomiast tablica zostanie wypełniona */ funkcja(n, tablica);

Podsumowując, argumenty do/z funkcji przekazywane są przez wartość, z wyjątkiem tablic które przekazywane są poprzez wskaźnik. Wskaźniki omówię szczegółowo w kolejnej części kursu.

Zakres widoczności zmiennych

Zmienne mogą być deklarowane wewnątrz funkcji(zmienne lokalne) albo poza wszystkimi funkcjami(zmienne globalne). Zmienne deklarowane poza wszystkimi funkcjami, na początku pliku, istnieją przez cały czas działania programu i są dostępne we wszystkich funkcjach w pliku. Zmienne deklarowane wewnątrz funkcji tworzone są w momencie wywołania funkcji i istnieją jedynie do momentu zakończenia działania funkcji. Zmienne deklarowane wewnątrz funkcji dostępne są tylko w obrębie funkcji, w której zostały zdeklarowane. W różnych funkcjach mogą istnieć zmienne o tej samej nazwie.

/* Zmienna dostępna we wszystkich funkcja w całym pliku */ int zmienna_globalna ; /* Definicja funkcji */ void funkcja(int n, int tablica[]) { /* Zmienna dostępna tylko w tej funkcji */ double zmienna_lokalna; zmienna_globalna = 17; } /* Definicja innej funkcji */ void inna_funkcja(char s ) { /* Zmienna dostępna tylko w tej funkcji */ double zmienna_lokalna; zmienna_lokalna = zmienna_globalna; }

Jeśli w funkcji zostanie utworzona zmienna o nazwie identycznej z nazwą istniejącej zmiennej utworzonej poza funkcjami, wtedy, w funkcji, pod tą nazwą dostępna będzie zmienna deklarowane w funkcji.

/* Zmienna dostępna we wszystkich funkcja w całym pliku */ int jakas_zmienna ; /* Definicja funkcji */ int jakas_funkcja(void) { /* Zmienna dostępna tylko w tej funkcji */ int jakas_zmienna; /* Zapis nastąpi do zmiennej zdefiniowanej w tej funkcji. */ jakas_zmienna = 17; }

Zmienne deklarowane wewnątrz funkcji tworzone są w chwili wejścia do funkcji i istnieją tylko do momentu wyjścia z funkcji. Zawartość tych zmiennych po wyjściu z funkcji jest bezpowrotnie tracona. Jeżeli zależy nam, aby zmienna deklarowana wewnątrz funkcji istniała przez cały okres działania programu, i aby pomiędzy kolejnymi wywołaniami funkcji zawartość tej zmiennej nie była tracona, to należy deklaracje tej zmiennej poprzedzić słówkiem static, takie zmienne nazywa się zmiennymi statycznymi. Zmienne deklarowane poza wszystkimi funkcjami otrzymują wartość początkową równą zero, natomiast zmienne deklarowane wewnątrz funkcji mają nieokreśloną, przypadkową wartość początkową, ale nie zmienne statyczne, te również otrzymują wartość początkową równą zero.

/* Definicja funkcji */ int licz(int n) { /* Definicja statycznej zmiennej licznik, która będzie istnieć przez cały okres działania programu */ static int licznik; licznik += n; return licznik; } int main(void) { /* Funkcja zwróci wartość 2 */ jakas_zmienna = licz(2); /* Funkcja zwróci wartość 5 */ jakas_zmienna = licz(3);

Kod BCD

W elektronice cyfrowej nierzadko wykorzystywany jest kod BCD(ang. Binary-Coded Decimal), czyli dziesiętny zakodowany dwójkowo. My będziemy przyłączać do mikrokontrolera 7-segmentowe wyświetlacze LED poprzez układy 7447(dekoder kodu BCD na kod wyświetlacza 7-segmentowego). Zaletą kodu BCD jest prostota: posługujemy się systemem dziesiętnym i każda cyfra (0,1..9) liczby zapisywana jest na czterech bitach.

bityliczba
0000 000000
0000 000101
0000 001002
0000 001103
0000 010004
0000 010105
0000 011006
0000 011107
0000 100008
0000 100109
0001 000010
0001 000111
0001 001012
0001 001113
0001 010014
..
..
..
1001 011197
1001 100098
1001 100199
Kodowanie BCD

Elementy i schematy

Zależnie od przykładu, będziemy do mikrokontrolera atmega16 przyłączać: osiem diod LED, dwa 7-segmentowe wyświetlacze LED, klawiaturę wykonaną z dwunastu przycisków, dwa przekaźniki oraz brzęczyk(bez generatora, sam przetwornik piezo).

Cały schemat podzieliłem na kilka części:

schemat z atmega16
Schemat 4.1 - sposób przyłączenia do układu atmega16 zasilania, resetu i złącza programatora. Kliknij w obrazek, żeby powiększyć.
schemat z atmega16
Schemat 4.2 Dwa 7-segmentowe wyświetlacze LED przyłączone do mikrokontrolera poprzez układy 7447 (dekoder kodu BCD na kod wyświetlacza 7-segmentowego). Kliknij w obrazek, aby powiększyć.
obrazek
Fot. 4.1 Dwa 7 segmentowe wyświetlacze LED wraz z dekoderami 7447 umieszczone na osobnej płytce.
schemat z atmega16
Schemat 4.3 Schemat klawiatury (układ klawiszy jak w telefonie) i sposób jej przyłączenia do portów we/wy mC atmega16. Kliknij w obrazek, żeby powiększyć.
klawiatura
Fot. 4.2 Klawiatura wykonana z przycisków micro swich na płytce uniwersalnej.
piezo
Schemat. 4.4 Sposób przyłączenia przetwornika piezo.
membrana piezo
Fot. 4.3 Buzzer bez generatora ( przetwornik piezo - membrana z puszką ) wylutowany ze starego, popsutego kalkulatora.
membrana piezo
Fot. 4.4 Przetwornik piezo - membrana przylutowana do płytki obwodu drukowanego.
obrazek
Schemat 4.5 Sposób przyłączenia przekaźników.

Przykładowe programy

Przygotowałem cztery przykładowe programy, do uruchomienia jako ćwiczenia. Do przykładów dołączone są animacje pokazującą efekt działania programów oraz krótki opis. Wszystkie przykłady są raczej proste, nikt nie powinien mieć problemów ze zrozumieniem jak działają.

Poniżej znajduje się szkielet programu, wszystkie przykłady napisane są według tego wzoru.


/**** PLIKI NAGŁÓWKOWE ****/

#include <avr/io.h>
#include <util/delay.h>
#define F_CPU 1000000L

/**** ZMIENNE GLOBALNE ****/


/**** DEFINICJE FUNKCJI ****/

/*  Inicjalizacja, konfiguracja sprzętu */
void init(void)
{

}

/* Inne funkcje */


/**** POCZĄTEK PROGRAMU ****/

/* Definicja funkcji main */
int main(void)
{
   /* Deklaracje zmiennych */

/* Inicjalizacja */
   init();

   /* Główna pętla programu */ 
   for(;;)
    {
    }
}
Szkielet prostego programu.

Pozytywka elektroniczna.

Ten przykładowy programik odgrywa krótką melodyjkę z pomocą buzzera, nagranie dźwięku:

piezo_buzzer

W programie najwięcej pracuje funkcja beep, która generuje sygnał prostokątny na wyprowadzeniach PB0 i PB1, gdzie przyłączony jest buzzer (przetwornik piezo). Funkcja ta przyjmuje dwa argumenty: częstotliwość sygnału w hercach i  długość czasu trwania sygnału w milisekundach. Generowanie dźwięku odbywa się programowo, bez użycia układów czasowych AVRra. Do uzyskania krótkich opóźnień w programie używana się funkcji: void _delay_loop_2(unsigned int __count) z biblioteki avr-libc, która wprowadza opóźnienie czterech cykli procesora na jeden __count. Uwaga! Funkcja beep będzie działać właściwie tylko dla uC AVR pracującego z częstotliwością 1MHz.

Inna funkcja, o nazwie play, odgrywa melodyjkę dźwięk po dźwięku wywołując funkcję beep. Funkcja play oczekuje argumentów: tablicy dźwięków oraz indeks pierwszego i ostatniego dźwięku. Elementami tablicy dźwięków mają być tablice o dwóch elementach typu int: częstotliwość podana w hercach i długość trwania dźwięku w milisekundach. W programie zdefiniowano, poza funkcjami,tablicę dźwięków o nazwie koziolek. Tablice definiowane poza funkcjami dostępne są bezpośrednio we wszystkich funkcjach w całym pliku. Jednak w przykładzie tablica jest przekazywana do funkcji jako jeden z argumentów, dzięki temu struktura programu jest bardziej przejrzysta, a funkcja jest bardziej uniwersalna.

/*
KURS AVR-GCC cz.4
Program, z pomocą  buzzera (przetwornika piezo),
odgrywa krótką melodyjkę.
  
układ atmega 1MHz
PB0 -> R(330Ohm) -> BUZZER -> PB1 
*/

#define F_CPU 1000000L
#include <avr/io.h>
#include <util/delay.h>

/**** ZMIENNE GLOBALNE ****/
/* 
Tablica dzwięków:
częstotliwść(Hz), czas_trwania(ms), częstotliwość, ... 
*/
int koziolek[][2]={
                    523,125,
                    587,125,
                    659,250,
                    698,125,
                    659,125,
                    587,250,
                    523,250,
                    1047,250,
                    784,250,
                    523,250,
                    1047,250,
                    784,250,
                    523,250,
                    1047,250,
                    784,1000 };

/**** DEFINICJE WŁASNYCH FUNKCJI ****/

/* Konfiguruje porty we/wy uC */
void init(void)
{
  /* PB0,PB1 - wyścia */
  DDRB  = 0x03;
  PORTB = 0x00;
}

/*
Funkcja generuje sygnał prostokątny na wyprowadzeniach PB0 i PB1,
gdzie przyłączony jest  buzzer. Funkcja przyjmuje argumenty: 
częstotliwość(Hz) sygnału i długość czasu trwania sygnału (ms).
*/
void beep(unsigned int frequency, unsigned int duration)
{  
  unsigned int i,t,n;  
  t = 125000/frequency;  
  n = (250UL*duration)/t;

  PORTB |= 0x01;
  PORTB &= ~0x02;
  for(i=0; i < n; i++) 
  {
    PORTB ^= 0x01;  
    PORTB ^= 0X02;  
    _delay_loop_2(t);
  } 
}
/*
Odgrywa melodyjkę dzwięk po dzwięku. Jako argumentów funkcja 
oczekuje tablicy dzwięków oraz numerów pierwszego i ostatniego
dzwięku. Elementami tablicy dźwięków  są tablice o dwóch elementach
typu int (częstotliwość w Hz i długość trwania dzwięku w ms).
*/
void play(int nots[][2], unsigned int start, unsigned int stop)
{
   int n;
   
   for(n=start; n <= stop; n++)
        beep(nots[n][0], nots[n][1]);
}            

/**** POCZĄTEK PROGRAMU ****/            

/* Definicja głównej funkcji */
int main(void)
{
   /* Konfiguracja sprzętu */
   init();

   /* Nieskończona pętla */
   while (1) 
   {    
      /* Gra dwukrotnie ten sam "kawałek" */
      play(koziolek,0,14);
      play(koziolek,0,14);
  
      /*  Chwila spokoju :) */   
      _delay_ms(6000); 
   }
   return 0;
}
Listing 4.1 Program "Pozytywka elektroniczna".

Klawiaturka "telefoniczna".

W tym przykładzie program odczytuje w pętli stan klawiatury i wyświetla numer ostatniego wciśniętego przycisku.

animacja
Animacja prezentuje sposób działania programu "Klawiatura telefoniczna".

W programie kluczową rolę odgrywa funkcja read_keypad, która sprawdza kolejno wszystkie przyciski klawiatury i zwraca numer pierwszego wciśniętego przycisku, albo zero, jeśli żaden przycisk nie został wciśnięty. Czytanie klawiatury odbywa się w następujący sposób: Wybierane są kolejno wiersz po wierszu kalawiatury i dla każdego wiersza testowane są przyciski kolumna po kolumnie.

Każdy przycisk klawiatury jednym wyprowadzeniem przyłączony jest do jednej z czterech linii wierszy i drugim wyprowadzeniem do jednej z trzech linii kolumn. Linie wierszy klawiatury przyłączone są do wyjść P0..PD3 uC AVR, a linie kolumn do wejść PD4..PD6. Początkowo wszystkie cztery wyjścia ustawiana są na stan wysoki napięcia. Wiersz, którego przyciski mają być testowane, wybiera się wymuszając na tej linii stan niski. Wejścia uC, do których przyłączone są linie kolumn klawiatury, są wewnętrzne podciągnięte przez rezystor do napięcia zasilania, więc normalnie występuje na wejściach stan wysoki. Gdy w wybranym wierszu zostanie wciśnięty przycisk, wtedy linia wiersza zewrze się z linią kolumny, i na linii kolumny też pojawi się stan niski.

animacja
Sposób testowania przycisków klawiatury.
/*
KURS AVR-GCC cz.4
Klawiatura telefonu

układ atmega16 (1MHz)

*/
#define F_CPU 1000000L
#include <avr/io.h>
#include <util/delay.h>

/**** DEFINICJE WŁASNYCH FUNKCJI ****/

/* Konfiguracja sprzętu */
void init(void)
{
   /* Konfiguruj portu A jako wyjścia */
   /* Wyświetlacz */
   DDRA  = 0xFF;
   PORTA = 0xFF;

   /* Klawiaturka  PD0..PD7 */
   DDRD  = 0x0f;
   PORTD = 0x7f;
}

/* 
Funkcja zmienia bajt w kodzie binarnym na bajt 
zakodowany w BCD 
*/
unsigned char bin2bcd(unsigned char bin)
{
   if(bin>99) return bin;

   return  bin/10<<4 | bin%10;
}

/*
Funkcja sprawdza kolejno wszystkie przyciski klawiatury
i zwraca numer pierwszego wciśniętego przycisku, albo zero,
gdy żaden przycisk nie został wciśnięty.
 */
unsigned char read_keypad(void)
{
   unsigned char row,col,key;
   
   for(row=0x7e,key=1; row>=0x77; row=(row<<1|0x1)&0x7f)
   {
        PORTD = row; 
      for(col=0x10; col< 0x80; col<<=1, key++)
                   if(!(PIND & col)) return key;
   }
   return 0;
}

/**** POCZĄTEK PROGRAMU ****/

/* Definicja funkcji main */
int main(void)
{
   unsigned char key;

/* Konfiguracja portów we/wy */
   init();

/* Nieskończona pętla */
   for(;;)
      if(key = read_keypad())
             PORTA = bin2bcd(key);
}
Listing 4.2 Program "Klawiatura telefoniczna".

Zamek na szyfr

W tym przykładzie wykorzystywane są: klawiatura (PD0..PD7), bzzer(PB0,PB1), i sześć diod led (PA0..PA5); linia PC0 steruje blokadą rygla zamka. Aby otworzyć zamek należy wybrać na klawiaturze sześć właściwych cyfr. Wciskając przycisk z gwiazdką można skasować wszystkie wprowadzone cyfry i rozpocząć wpisywanie kodu od początku. Przy wciśnięciu każdej cyfry zapala się kolejna dioda led i słychać krótki sygnał dźwiękowy. Po wprowadzeniu właściwego kodu natychmiast następuje odblokowanie rygla zamka. Otwarcie zamka sygnalizowane jest migotaniem wszystkich sześciu diod i sygnałem dźwiękowym o zmieniającej się częstotliwości. Wpisanie niewłaściwego kodu sygnalizowane jest wygaszeniem wszystkich sześciu diod LED i długim, "niskim", dźwiękiem.

W przykładzie wykorzystywane są omówione wcześnie funkcje beep i read_keypad.

zamek na szyfr
Animacja prezentuje działanie programu "Zamek na szyfr".
/*
KURS AVR-GCC cz.4
Zamek na szyfr (schemat i opis działania w artykule)

układ atmega16 (1MHz)
*/

/**** PLIKI NAGŁÓWKOWE ****/

#define F_CPU 1000000L
#include <avr/io.h>
#include <util/delay.h>


/**** DEFINICJE FUNKCJI ****/

/* Inicjalizacja i konfiguracja sprzętu */
void init(void)
{
   /* Konfiguruje portów we/wy */
   /* PA0..PA6 - diody led */
   DDRA  = 0x3F;
   PORTA = 0x3F;

   /* PB0,PB1 - piezo buzzer */
   DDRB  = 0x03;
   PORTB = 0x00;

   /* Klawiaturka */
   DDRD  = 0x0f;
   PORTD = 0x7f;

   /* PC0 - rygiel zamka */
   DDRC  = 0x01;
   PORTC = 0x00;
}

/*
Funkcja sprawdza kolejno wszystkie przyciski klawiatury
i zwraca numer pierwszego wciśniętego przycisku, albo zero,
jeśłi żaden przycisk nie został wciśnięty.
 */
unsigned char read_keypad(void)
{
   unsigned char row,col,key;
/* 
Wybiera wiersz po wierszu i testuje przyciski kolumna po
kolumnie, zmienna key przechowuje numer przycisku 1-12.
*/
   for(row=0x7e,key=1; row>=0x77; row=(row<<1|0x1)&0x7f)
   {
      PORTD = row;
      for(col=0x10; col< 0x80; col<<=1, key++)
                  if(!(PIND & col)) return key;
   }
   /* Jeśli żaden z przycisków nie został wciśnięty,
      wyjście z kodem zero */
   return 0;
}

/*
Funkcja generuje sygnał prostokątny na wyprowadzeniach PB0 i PB1,
gdzie przyłączony jest  buzzer. Funkcja przyjmuje argumenty: 
częstotliwość(Hz) sygnału i długość czasu trwania sygnału (ms).
*/
void beep(unsigned int frequency, unsigned int duration)
{
   unsigned int i,t,n;  
   t = 125000/frequency;  
   n = (250UL*duration)/t;

   PORTB |= 0x01;
   PORTB &= ~0x02;
   for(i=0; i < n; i++) 
   {
      PORTB ^= 0x01;  
      PORTB ^= 0X02;  
      _delay_loop_2(t);
   }
}


/****   POCZĄTEK PROGRAMU   ****/

/* Definicja funkcji main */
int main(void)
{
   /* Deklaracje zmiennych */
   unsigned char i,j,key,err=0;

   /* 6 cyfrowy klucz */
   unsigned char pass[] = {7,8,7,8,7,8};

   /* Konfiguracja i inicjalizacja sprzętu */
   init();

   /* Główna pętla programu */
   for(;;)
   {
         /* Blokada rygla zamka */
      PORTC &= ~0x01;
      /* Wygaszenie diod led */
      PORTA |= 0x3f;
      /* Zeruje licznik błędów */
      err = 0;

      /* Odczyt i porównanie z wzorem 6 cyfr klucza */
      for(i=0; i<6; i++)
      {
         /* Oczekiwanie na wciśnięcie przycisku klawiatury */
         while(!(key = read_keypad()));

         /* Jeśli wciśnięto przycisk gwiazdka *, to kasowanie 
         wprowadzonych cyfr */
         if(key == 10) break;

         /* Sygnalizacja postępu wprowadzania kolejnych cyfr klucza */
         PORTA &= ~(1<<i);
         beep(880,100);    

         /* Oczekiwanie na zwolnienie przycisku */
         while(read_keypad());
         /* Zatrzymanie aż wygasną drgania styków przycisku */
         _delay_ms(80);

        /* Każda wprowadzona cyfra klucza jest porównywana z 
           wzorem w tablicy "pass", jeśli nie pasuje,
           licznik błędów w zmiennej "err" jest zwiększany o jeden */
         if(key != pass[i]) err++;

         /* Jeśli wprowadzona cyfra klucza jest ostatnią */
         if(i == 5)
         {
            /* Jeśli wprowadzono właściwy klucz */
            if(!err)
            {
               /* Zwolnienie blokady rygla zamka */
               PORTC |= 0x01;

               /* Sygnalizacja otwarcia zamka */
               for(j=0; j<6; j++)
               {
                  PORTA ^= 0x3f; 
                  beep(880,200);
                  PORTA ^= 0x3f;
                  beep(440,200);
               }
            }
            else
            {
               /* Sygnalizacja błędu */
               beep(200, 600);
               PORTA |= 0x3f;
            }
         }
      }
   }
}
Listing 4.3 Program "Zamek na szyfr"

Wyłącznik czasowy

Jak w tytule punktu, wyłącznik czasowy. Wybiera się na klawiaturze liczbę sekund 1-99 i następnie, wciskając przycisk gwiazdka uruchamia się odliczanie od wybranej liczby do zera. Dwucyfrowe liczby wprowadza się wciskając daw przyciski w krótkim odstępie czasu, pierwszą - dziesiątki, drugą - jedności.

klawiatura
Animacja prezentuje działanie programu "Wyłącznik czasowy"
/*
   KURS AVR-GCC cz.4
   Wyłącznik czasowy (schemat i opis działania w artykule)
   
   układ atmega16 (1MHz)
*/

#define F_CPU 1000000L
#include <avr/io.h>
#include <util/delay.h>

/**** DEFINICJA WŁASNYCH FUNKCJI ****/

/*  Inicjalizacja, konfiguracja sprzętu */
void init(void)
{
   /* Konfiguruje portów we/wy */
   /*  Wyświetlacz */
   DDRA  = 0xFF;
   PORTA = 0xFF;
   
   /* Klawiaturka */
   DDRD  = 0x0f;
   PORTD = 0x7f;
   
   /* Przekaźnik */
   DDRC  = 0x01;
   PORTC = 0x00;
}

/* 
Funkcja zmienia 8-bit wartość w kodzie dwójkowym 
na wartość w kodzie BCD
*/
unsigned char bin2bcd(unsigned char bin)
{ 
   if(bin > 99) return bin;

   return  bin/10<<4 | bin%10 ;
}

/*
Funkcja sprawdza kolejno wszystkie przyciski klawiatury
i zwraca numer pierwszego wciśniętego przycisku, albo zero,
gdy żaden przycisk nie został wciśnięty.
 */
unsigned char read_keypad(void)
{
   unsigned char row,col,key;

/* 
Wybiera wiersz po wierszu i testuje przyciski kolumna po
kolumnie, zmienna key przechowuje numer przycisku 1-12.
*/
   for(row=0x7e,key=1; row>=0x77; row=(row<<1|0x1)&0x7f)
   {
      PORTD = row;
      for(col=0x10; col< 0x80; col<<=1, key++)
                              if(!(PIND & col)) return key;
   }
   /* Jeśli żaden z przycisków nie został wciśnięty, 
   wyjście z kodem zero */
   return 0;
}

/**** POCZĄTEK PROGRAMU ****/

/* Definicja funkcji main */
int main(void)
{
   /* Deklaracje zmiennych */
   unsigned char i,key;
   signed char  v = 0;

   /* Tablica kodowania  przycisków klawiatury */
   unsigned char codetab[]= {0xff,1,2,3,4,5,6,7,8,9,0,0,0};

   /* Konfiguracja, inicjalizacja sprzętu */
   init();

   /* Główna pętla programu */ 
   for(;;)
   {
      /* Funkcja read_keypad zwraca wartość (kod przycisku) różną 
     od zera, gdy wykryje wciśnięcie przycisku na klawiaturze. */
      key = read_keypad();

     /* Wprowadzenie liczby sekund (0-99) czasu zliczania */
      if(key && key < 10)
      {
         v = bin2bcd(codetab[key]);
         /* Wyświetla pierwszą cyfrę */
         PORTA = v;
         /* Opóźnienie dla wygaśnięcia drgań styków przycisku */
         _delay_ms(20);
            
         /* Oczekiwanie na zwolnienie przycisku */   
         while(read_keypad()){};
         _delay_ms(20);

         /* W pętli, 20 razy odczytuje klawiaturę oczekując 
            wprowadzenia drugiej cyfry */
         for(i=20; i>0; i--)
         {
            if((key = read_keypad()))
            {
               _delay_ms(20);
            /* Składa obie wprowadzone cyfry w jedną liczbę */
               v = v*10 + codetab[key];
      /* Przesuwa pierwszą cyfrę  wyświetlacza pozycję na lewo */
               PORTA <<= 4;
            /* Wyświetla drugą cyfrę */
               PORTA |= codetab[key];
            /* Czeka na zwolnienie przycisku */
               while(read_keypad()){};
               _delay_ms(20);
               break;
            }
            /* Dodatkowe opóźnienie między kolejnymi odczytami 
            klawiatury*/
            _delay_ms(50);
            /* Wygasza pierwszą cyfrę na wyświetlaczu, jeśli
               wprowadzono liczbę jednocyfrową */
            if(i == 1) PORTA |= 0XF0;
         }
      }
      /* Wciśnięcie przycisku z gwiazdką, gdy wcześnie wprowadzona
         liczba sekund była większa od zera, rozpoczyna odliczanie */
      else if (key == 10 && v)
      {
         PORTC |= 0x01; // włącza przekaźnik

         /* Odliczanie od v do zera, zmienna v zawiera wprowadzoną 
           wcześniej liczbę sekund */
         do{
            _delay_ms(960); // Prawie sekunda opóźnienia 
            v--;
         /* Wyświetla aktualny stan licznika */
            PORTA = bin2bcd(v);
         }while(v>0);

      PORTC &= ~0x01; // wyłącza przekaźnik
      } 
   } 
}
Listing 4.4 Program "Wyłącznik czasowy".

W następnej części kursu..

W następnej części kursu tematem przewodnim będą tekst i działania na tekście. Kolejnym tematem będzie podział kodu źródłowego programu na oddzielnie kompilowane pliki. Napiszę też kilka zdań na temat preprocesora języka C. W części praktycznej będziemy bawić się alfanumerycznym wyświetlaczem LCD HD44780, przyłączymy też AVRa do komputera PC poprzez port szeregowy rs232.



Kurs AVR-GCC cz.3 | Kurs AVR-GCC cz.5

legenda

Komentarze (66)

Krzychu
11.11.2016 (12:03)
gość
W kodzie "Zamek na szyfr" w komentarzu jest mały błąd:
/* Konfiguruje portów we/wy */
/* PA0..PA6 - diody led */
DDRA = 0x3F;
PORTA = 0x3F;

Powinno być PA0..PA5
Jarko
16.08.2016 (17:57)
gość
Czy w programie pozytywki zamiast tablicy:
int koziolek[][2]={
523,125,
587,125,
659,250};
nie powinno być:
int koziolek[][2]={
{523,125},
{587,125},
{659,250}};
Eugeniusz
17.06.2016 (01:42)
gość
@Jacek 54
"Jako argumentów funkcja oczekuje tablicy dzwięków oraz numerów pierwszego i ostatniego dzwięku."

int koziolek[][2]={...}; //tablica dźwięków
|
V
play(koziolek,0,14);
|
V
void play(int nots[][2], unsigned int start, unsigned int stop) {...}

//nots[][2] < -- koziolek, start <-- 0, stop <-- 14

Narysowałem ci mapę z legendą ;) poczytaj lepiej o zmiennych lokalnych... i nie tylko. Miłej lektury.
Jacek 54
23.03.2016 (18:55)
gość
Witam,

W programie pozytywka nierozumiem funkcji:

void play(int nots[][2], unsigned int start, unsigned int stop)

Co to jest: nots

Skąd program wie, że Start i stop to jest początek i koniec tablicy ?

for(n=start; n <= stop; n++)
beep(nots[n][0], nots[n][1]);

Nie czuję tego :(
Michał
27.02.2016 (14:21)
gość
Chciałem zrobić ten "tester klawiatury", ale nie miałem wyświetlaczy oraz pracowałem na Atmedze8. Pomyślałem także, że jakbym chciał pokazać to na diodach LED to musiałbym mieć ich 12, a nie miałem tyle wolnych wyjść w 1 porcie i wszytko musiało być w różnych portach. I pomyślałem, aby otrzymaną liczbę pokazać w kodzie binarnym. Podłączyłem klawiaturę (PD0...PD6) i 4 diody LED do portu C (PC0...PC3). I przerobiłem jedną funkcję na własną. I wszytko działało dobrze ;p. Oto mój przerobiony kod:
/*
Układ Atmega8 (1MHz)
Klawiatura binarna
*/
#define F_CPU 1000000L
#include <avr/io.h>
#include <util/delay.h>

/* Konfiguracja sprzętu */
void init(void)
{
/* Konfiguruj portu C jako wyjścia */
/* Wyświetlacz binarny */
DDRC = 0xFF;
PORTC = 0xFF;

/* Klawiaturka PD0..PD7 */
DDRD = 0x0f;
PORTD = 0x7f;
}

/*
Funkcja zmienia bajt w kodzie binarnym na bajt
zakodowany w BCD
*/
unsigned char bin2bin(unsigned char bin)
{
unsigned char k;
if(bin>99) return bin;
else if(bin==1) k=0x01;
else if(bin==2) k=0x02;
else if(bin==3) k=0x03;
else if(bin==4) k=0x04;
else if(bin==5) k=0x05;
else if(bin==6) k=0x06;
else if(bin==7) k=0x07;
else if(bin==8) k=0x08;
else if(bin==9) k=0x09;
else if(bin==10) k=0x0a;
else if(bin==11) k=0x0b;
else if(bin==12) k=0x0c;

return k;
}

/*
Funkcja sprawdza kolejno wszystkie przyciski klawiatury
i zwraca numer pierwszego wciśniętego przycisku, albo zero,
gdy żaden przycisk nie został wciśnięty.
*/
unsigned char read_keypad(void)
{
unsigned char row,col,key;

for(row=0x7e,key=1; row>=0x77; row=(row<<1|0x1)&0x7f)
{
PORTD = row;
for(col=0x10; col< 0x80; col<<=1, key++)
if(!(PIND & col)) return key;
}
return 0;
}

/**** POCZĄTEK PROGRAMU ****/

/* Definicja funkcji main */
int main(void)
{
unsigned char key;

/* Konfiguracja portów we/wy */
init();

/* Nieskończona pętla */
for(;;)
if(key = read_keypad())
PORTC = bin2bin(key);
}
Rafał
18.12.2015 (16:38)
gość
W jaki sposób można zrobić własną tablicę dźwięków z pliku *.mp3?
admunt
18.12.2015 (08:21)
gość
Mam pytanie do - Pozytywki elektronicznej: Gdzie można pobrać więcej Tablic dźwięków ?
admunt
18.12.2015 (08:18)
gość
Więcej Tu można nauczyć się programowania niż na Uczelni ; )
xyz
30.03.2014 (13:27)
gość
Witam,
Świetny kurs zawiera w streszczeniu wszystko co potrzeba.

Kiedy można spodziewać się następnej części?
Raf
16.01.2014 (16:38)
gość
Mam pytanie co do projektu z buzzerem. Jak napisać program, w którym każdy przycisk odpowiada za inny dźwięk, a sekwencję dźwięków można zapisać i odtworzyć?
Bartek
28.11.2013 (00:07)
gość
Wydaje mi się że znalazłem błąd, w tabeli jak przedstawiasz liczby dziesiętne w postaci binarnej od 97-99.
Pozdrawiam ;)
D_K
16.04.2013 (21:36)
gość
Dziękuję bardzo za odpowiedź, więc użyję prostowniczą, bo tylko taką mam. Bardzo fajny kurs!!!
abxyz
16.04.2013 (14:39)
autor strony
Te diody przy klawiaturce są konieczne ( rodzaj diody nie jest ważny), jest to zabezpieczenie portów, żeby przypadkiem nieograniczony prąd z wyjścia w stanie wysokim nie popłyną do wyjścia w stanie niskim - gdy są wciśnięte jednocześnie dwa przyciski na jednej linii.
D_K
15.04.2013 (23:09)
gość
Witam:)
Mam pytanie, czy konieczne jest zastosowanie diody w klawiaturze ? ewentualnie czy można użyć diody prostowniczej ? pytam gdyż nie mam takiej diody jak na schemacie :)
Marek Golonka
15.03.2013 (10:09)
gość
nie rozumiem tych obliczeń na częstotliwość i czas trwania w funkcji BEEP:
t = 125000/frequency;
n = (250UL*duration)/t;

dlaczego akurat liczby 125000 i 250 i skąd to się wzięło? Czy to jest zależne od procesora i gdzie konkretnie jest opisane dobieranie tych liczb?
Gofer
22.02.2013 (21:39)
gość
Hej, świetny kurs ale mam mały problem:
skleciłem sobie klawiaturę wg schematu i napisałem kod w wersji lekko mniej skompresowanej, żeby zobaczyć czy wszystko łapię jeśli chodzi o zasadę jej działania, samą funkcję zacząłem tak:
int klaw(void){
DDRC |= 0x0F;

DDRB &= ~0x07;
PORTB |= 0x07;

PORTC = 0x0E;
_delay_ms(1);

if(!(PINB & 0x01))
return 1;
else if(!(PINB & 0x02))
return 2;
else if(!(PINB & 0x04))
return 3;

dalej leci tak samo dla kolejnych wierszy. No i z najmniejszymi delay'ami działa, a bez jakby przeskakuje wiersz, tzn zamiast 1 daje 4, zamiast 4 daje 7 itd. Czym to jest spowodowane?
abxyz
23.11.2011 (23:19)
autor strony
Dawid
23.11.2011 (17:35)
gość
Witam, kurs jest bajeczny i tutaj wielki + dla autora :)
Mam jedno pytanko odnośnie melodyjki, w jaki sposób pozyskać częstotliwości i długości dźwięków jakieś melodyjki ?
W jaki sposób sporządzić taką tablicę jaką sporządził autor dla koziołka matołka ? np. jakbym chciał odegrać wlazł kotek na płotek
baks
29.06.2011 (19:31)
gość
Witam, na początku chciałbym pogratulować autorowi - na prawdę świetny kurs.
Jeżeli ktoś ma zamiar uruchomić program z koziołkiem na uC taktowanym z inną częstotliwością niż 1 Mhz musi zmodyfikować
jedną linijkę kodu a dokładnie wywołanie funkcje _delay_loop_2( takt_Mhz*t).

Najłatwiej dodać dodatkową definicję na początku, dzięki czemu program będzie działał zawsze poprawnie bez względu na zegar

// na początku np za includami
#define takt_Mhz F_CPU/1000000

//w pętli for funkcji beep
_delay_loop_2( takt_Mhz*t);
abxyz
11.03.2011 (23:18)
autor strony
Poprawiłem, dzięki
GAndaLF
11.03.2011 (17:03)
gość
Witam, znalazłem błąd w kodzie wymienionego wyżej programu.

w tym miejscu:
play(koziolek,0,15);
play(koziolek,0,15);

powinno być:
play(koziolek,0,14);
play(koziolek,0,14);

ponieważ element nr 15 to znak końca tabeli i zagrają się jakieś śmieci.
qsiek
20.12.2010 (05:54)
gość
Hej.
Mam problem ze stosem w swojej atmedze128. Mianowicie wywołuje funkcje po czym uC samoistnie się resetuje przy powrocie z funkcji. Problem znika gdy dodam przedrostek inline przed funkcją, ale też nie zawsze.

Czy jest możliwe uszkodzenie stosu w jakiś sposób?

Czy może jakieś ustawienie w makefile'u ogranicza stos?

Czy zaprogramowanie (prog_uchar) w pamięci tablice rozmiaru 12kB może coś uszkodzić?

Proszę o jakąś wskazówkę.
Paw
15.11.2010 (17:18)
gość
Hej czy mógłby ktoś mi napisać w jaki sposób użyć funkcji play przy taktowaniu 8MHz? Z góry dzięki
abxyz
03.09.2010 (14:13)
autor strony
Tutaj akurat oscyloskop się nie przyda, wystarczy przejrzeć poprzednią cześć kursu.

250UL - oznacza, że stała 250 ma mieć typ unsigned long int (32-bity bez znaku), domyślnie stałe liczbowe w AVR-GCC posiadają typ int (16-bitów ze znakiem).

Przykładowo wartość wyrażenia w nawiasie
(250UL*duration)
ma typ unsigned long int

natomiast wartość wyrażenia
(250 * duration)
byłaby typu unsigned int, bo zmienna duration jest zdeklarowana jako unsigned int

W tym drugim przypadku, wynik mnożenia mógłby się nie zmieścić w na 16 bitach.

Jeśli w obliczanym wyrażeniu występują zmienne|stałe różnych typów, to wynik przyjmuje typ "najszerszej" zmiennej|stałej stojącej w tym wyrażeniu.
O przekształcaniu typów będę jeszcze pisał w kursie.

Spectrum
03.09.2010 (12:57)
gość
Witam, rozgryzam krok po kroku artykuł, nawet z pomocą oscyloskopu. na razie zatrzymałem się na programie z melodyjką i w związku z tym mam pytanie - co oznaczają litery UL za wartością 250UL?
carkar
12.07.2010 (09:34)
użytkownik
Świetny artykuł, oby tak dalej!! :)
Grayfox
31.05.2010 (16:59)
gość
Ok, nevermind, doszedłem sam do tego :)
Długość stanu wysokiego i niskiego musi być taka sama ;)
-------------------------
PORTB=0x01;
_delay_us(5000);
PORTB=0x00;
_delay_us(5000);
--------------------------
działa.

Mój błąd, nie pomyślałem ;)
Stanley
15.05.2010 (06:00)
gość
Witam, jak należy zmodyfikować kod aby obsługiwał klawiaturę 4x4?
Z tego co zauważyłem to trzeba zmodyfikować linijkę:

"for(col=0x10; col<=0x80; col>>=1, key++)"

ABXYZ
13.05.2010 (01:15)
autor strony
W przykładach przetwornik piezo przyłączony jest pomiędzy dwie linie we/wy (PB0 i PB1) - dla zwiększenia siły dźwięku. Oczywiście, możesz przyłączyć przetwornik piezo do dowolnych portów we/wy AVRa
kaeltaz
08.05.2010 (15:42)
gość
Witam a jest możliwość podłączenia przetwornika piezo bez generatora do innego pinu niż PB0 i PB1. I czy można podłączyć takie 2 pieza do dwóch wyjść mikrokontrolera podłączając + po vcc a minusy do pinow np. PB6 i PB7.
20rafalo
04.04.2010 (19:44)
gość
Teraz już sprawa wyjaśniona :-)

W następnych kursach jeśli można - proszę o trochę bardziej szczegółowe komentarze - tam gdzie trzeba.

A jeszcze lepiej , jak by były w każdej linijce, choć dwa słowa :-)
Było by super.
Kurs najlepszy w necie pod uC Atmela jaki widziałem.
Pozdrawiam
ABXYZ
03.04.2010 (16:37)
autor strony
Nie ma tu najmniejszego błędu. Być może fragment artykułu mówiący o zmiennych statycznych jest nie czytelny. Wyjaśniam: W tej funkcji zmienna &quot;licznik&quot; jest zmienną statyczną - słówko static w deklaracji. Zmienne statyczne mają początkową wartość równą zero. Zmienne statyczne zachowują swoją wartość pomiędzy kolejnymi wywołaniami funkcji.
Przy pierwszym wywołaniu funkcji, początkowa wartość zmiennej licznik (zero) została zwiększona o 2. Przy kolejnym wywołaniu funkcji, wartość zmiennej licznik (2) została zwiększona o 3.

Kolejna rzecz. Domyślnie wszystkie porty we/wy AVRa są skonfigurowane bezpiecznie jako wejścia, więc rejestry PORTx, DDRx mają początkową wartość 0 - wystarczy to zapamiętać
20rafalo
03.04.2010 (14:23)
gość
"int licz(int n) {
static int licznik; licznik += n;
return licznik; }
int main(void)
{ /* Funkcja zwróci wartość 2 */ jakas_zmienna = licz(2);
/* Funkcja zwróci wartość 5 */ jakas_zmienna = licz(3);"
zauważyłem tu drobny błąd - ważny dla początkujących:
Mianowicie: funkcja zwróci wartość 5, a nie 3.
Ale i tak to nie do końca prawda, gdyż autor nie wpisał wartości początkowej zmiennej "licznik"
mogły być tam różne śmieci - 4 czy 77. Wtedy wynik byłby zupełnie inny.
Jest to ważne, gdyż pamiętam jak zaczynałem się uczyć C++ to każde pominięcie kwestii odbijało się kompletnym brakiem zrozumienia kodu.

Prosił bym autora aby łopatologicznie wpisywał wartości początkowe dla portów uC podczas ich definicji zaraz za main, gdyż jeśli np PORTD ma wartość domyślną 0xff, to często autor to pomija.
Jest potem problem dalej w analizie programu podczas ustawiania bitów -
np if(PORTD & 0x80)... wtedy zadaje sobie pytanie jaką wartość ma PORTD?? przecież nie był ustawiany wartością początkową

Oczywiście te małe uwagi są po to by polepszać kurs :-)

PS Dobry kurs
Pozdrawiam
kubeqz
05.03.2010 (23:02)
gość
Kolejna częśc bardzo udanego kursu :) Jakby ktos mial problemy z dzialaniem funkcji &quot;play&quot; na uC taktowanym inaczej niz 1Mhz, to wystarczy zmienic w niej tylko linijke
beep(nots[n][0], nots[n][1]);
na
beep(nots[n][0], &quot;taktowanie_w_Mhz&quot; * nots[n][1]);
cezar89
03.03.2010 (16:13)
gość
saly nie wiem czy tam trafiłeś... na wkretak.pl jest przyzwoicie opisane multipleksowanie wyśw. LED
saly
28.02.2010 (16:55)
gość
Kurs super, dużo z niego zaczerpnąłem, z niecierplwością czekam na dlasze części.
Mam pytanko czy móglby ktoś polecić mi materiał który objaśnia multipleksowanie wyświetlaczy LED, w C na ATMEGA8, w necie jest tego dużo ale wszędzie gotowce, a ja chciałbym znaleść materiał, który tłumaczy program w taki sposób jak ten kurs. Ewentulanie polecić dobrą książkę dla ATMEGI w C
Bob
26.02.2010 (11:50)
gość
Z niecierpliwością czekam na dalszy ciąg kursu.
ABXYZ
10.02.2010 (10:37)
autor strony
"Jak to się stało, ale się stało" i się skasowało :)
Faktycznie brakowało zmiennej. Wszystkie przykłady z kursu są uruchamiane, tyko zwykle komentarze dopisuje dopiero przy edycji artykułu.

Może w następnej wersji strony wprowadzę punkty za aktywność, Realista otrzymałeś 3 punkty :)
Realista
09.02.2010 (22:02)
gość
Wydaje mi sie ze jest blad w listingu 4.2 . Mi sie nie chce skompilowac.Chyba w brakuje deklaracji zmiennej key w funkcji głownej, tak na moje.
Nikt
03.02.2010 (00:01)
gość
Dziękuję za informację, będziemy czekać :) Powodzenia.
Tom
02.02.2010 (20:01)
gość
Ja pamiętam. Pamiętać będę :)

Z niecierpliwością czekam na kolejne części kursu.

Pozdrowienia dla autora kursu.
freeteh
31.01.2010 (22:14)
gość
super, już nie mogę się doczekać:)
ABXYZ
31.01.2010 (20:35)
autor strony
Będą kolejne części kursu. Nawet jeśli Nikt już nie pędzie pamiętać :) I będą coraz ciekawsze.
Nikt
31.01.2010 (18:20)
gość
Widzę, że chyba o dalszej części kursu możemy powoli zapomnieć...
ABXYZ
04.01.2010 (20:14)
autor strony
Faktycznie strona wygląda jak rozgrzebana i dawno porzucona budowa stadionu, ale spokojnie, wcale nie porzuciłem tej stronki, jedynie przełożyłem na potem :)
Będą kolejne części kursu AVR-GCC, a także inne kursy i teksty.
Nikoś
03.01.2010 (13:30)
gość
Błagamy o dalsze części... kurs jest rewelacyjny :)))
Kowal
28.12.2009 (23:23)
gość
Kiedy kolejna czesc ??
lechunlx
21.12.2009 (18:28)
gość
No musze przyznać, że niepotrzebnie w ogóle zabierałem się wcześniej za bascoma ;) bardzo dobry kurs i proste do zrozummienia przykłady ;)
Pozdrawiam autora.
Kuba
13.12.2009 (21:06)
gość
Pytanie do autora: kiedy możemy się spodziewać kolejnej części kursu?
Majkel
16.11.2009 (23:43)
gość
Ja uważam że autor bardzo dobrze prowadzi naukę C dla AVR. Bo żeby zająć się sprawami bardziej skomplikowanymi jak PWM czy 1Wire to trzeba znać podstawy tego języka. Swoją drogą nie ma tak wiele artykułów w sieci które są po polsku i potrafią wytłumaczyć z czym jeść "C".
Gdy człowiek pozna podstawy "C" to potem może sobie spojrzeć w datasheet i samemu napisać bibliotekę dla jakiejś funkcji.
Michał
15.11.2009 (18:03)
gość
Super strona. Czekam na więcej artykułów.
Beginer
10.11.2009 (20:13)
gość
We&#039;re waiting for more... Good luck :)
olelek
05.11.2009 (16:37)
gość
Fajny kurs, mam tylko gorącą prośbę - opisz bardziej praktyczne strony programowania avr: obsługę pwm, RC5, komunikację 1WIRE i I2C, komparator, LCD, jakieś ciekawe funkcje biblioteczne i pliki nagłówkowe. Podstawy czystego języka C można znaleźć wszędzie, a z zastosowaniem go w avr-ach jest już gorzej. Pozdrawiam!
Łukasz
04.11.2009 (18:22)
gość
Kurs pokazał mi że "C" nie jest takie straszne jak myślałem :D Ponieważ wcześniej pisałem w Bascomie, bardzo przyzwyczaiłem się do symulatora jaki on posiada. I tutaj mała prośba dla autora: Czy mógłbyś pokazać jak zintegrować symulator Bascomowy lub ten z Avr Studio z WinAvr? Szukałem oczywiście materiałów, jednak opisy są dość niejasne... Pozdrawiam i życzę sukcesów na przyszłość

Łukasz
ABXYZ
11.10.2009 (00:22)
autor strony
Może w niewłaściwej kolejności podłączyłeś linie wejść A B C D
Daro
06.10.2009 (21:11)
gość
Jaka może być przyczyna pojawiania się na wyświetlaczu krzaków zamiast zwykłych liczb?
Dodam że zastosowałem dekoder CD4543BE
ABXYZ
18.09.2009 (12:22)
autor strony
"Jak podłączyć przekaźnik do zamka na szyfr?"

Ja przy uruchamianiu przykładów użyłem układ scalony ULN2803A, za chwilę narysuje i wstawię jakiś schemacik.
Pae
17.09.2009 (14:16)
gość
Świetny kurs
Jak podłączyć przekaźnik do zamka na szyfr??
ailpein
15.09.2009 (14:49)
gość
Swietna sprawa, wlasnie zaczynam przygode z uC, z pewnoscia zaczne do przedstawionych na stronie kursow.

A.
malpek
27.08.2009 (18:58)
gość
cieszę się że widzę kolejną część kursu

http://wkretak.pl/articles.php?cat_id=1
to jest link do innego kursu programowania mikrokontrolerów w C opisane są w nim timery, wyświetlacze LED i LCD oraz Watchdog,
mam nadzieje że mój komentarz nie zostanie odebrany jako reklama, nie jestem autorem, ale jako uzupełnienie i inne spojrzenie na podstawy programowania, pozdrawiam wszystkie zaczynających zabawę z C
Piotr
27.08.2009 (17:20)
gość
&quot;Dzięki ci &#039;O Panie&#039; za to i błagamy cię &#039;O Panie&#039; o resztę .... &quot;
Z poważaniem
Piotr
KubX
25.08.2009 (19:27)
gość
Twój kurs jest bardzo pomocny, łatwy do zrozumienia. Dobrze by było, gdybyś zwrócił uwagę stronie sprzętowej (timery, adc, itd.).
Zauważyłem mały błąd - w części o kodzie BCD:
0000 0111 - 06 (powinno być 0000 0110)
0000 0111 - 07
Greg
24.08.2009 (23:36)
gość
Gratuluję, ma Pan dar do tłumaczenia złożonych zagadnień w przystępny sposób -- życzę Panu wytrwałości i niecierpliwie czekam na kolejne części kursu!
Szwagier90
22.08.2009 (21:30)
gość
Bardzo fajnie wytłumaczony każdy szczegół. Pozdrawiam.
Rafał
18.08.2009 (22:49)
gość
Czekałem z niecierpliwością i oto jest :) Dzięki za kolejną część kursu. Pozdrawiam i życzę wytrwałości.
czeladzian
18.08.2009 (08:19)
gość
Nareszcie!!! :)
Kursy>Kurs AVR-GCC>Kurs AVR-GCC, cz.4
Ostatnie artykuły