środa, 6 czerwca 2012

Podstawowe dyrektywy języka BASCOM.



      
     W tym artykule zajmę się opisywaniem podstawowych dyrektyw języka BASCOM bez których program nie mógł by działać. Wszystkie kody źródłowe zamieszczone w tej serii artykułów są kompatybilne z mikrokontrolerem Attiny2313 lub Atmega88 (ewentualny zamiennik to Atmega8A). Pierwsze ćwiczenia zostaną zrealizowane na Attiny 2313, natomiast kolejne (już bardziej zaawansowane) na Atmedze88. Najprawdopodobniej ukażę się także prezentacja płytki testowej stworzonej specjalnie na potrzeby tego cyklu artykułów. Będzie to istotne kiedy przejdziemy do ćwiczeń praktycznych.
 Pierwszą z wcześniej wspomnianych dyrektyw jest polecenie config służące do ustalenia które bity danych portów mają zostać wejściami a które wyjściami. Polecenie Input oznacza wejście ,a Output wyjście. Wejście jak sama nazwa wskazuje stanie się nogą procesora odbierającą informacje (będą tam więc podłączone np. przyciski lub czujniki) , natomiast wyjścia będą wysyłać dane (podłączymy tam np. diody LED lub też tranzystory sterujące - np. silnikami itp.) Przykład takiej konfiguracji to:

Config Pind.0 = Input  
Config Pind.1 = Input
Config Pind.2 = Input
Config Pind.3 = Input
Config Pind.4 = Input
Config Pind.5 = Input
Config Pind.6 = Output
Config Pinb.0 = Input
Config Pinb.1 = Input
Config Pinb.2 = Input
Config Pinb.3 = Input
Config Pinb.4 = Output
Config Pinb.5 = Output
Config Pinb.6 = Output
Config Pinb.7 = Output

Z wyżej przedstawionego fragmentu programu  wynika ,że nóżki o numerach od 0 do 5 portu d staną się wejściami. Jedynie nóżka 6 portu d zostanie wejściem. W porcie b bity od 0 do 3 są wejściami, natomiast od 4 do 7 wyjściami. Zwróć uwagę, że  mikrokontroler Attiny2313 posiada 7 bitów portu d oraz 8 bitów portu b. Taka konfiguracja zajmuje bardzo dużo miejsca w programie (w pamięci FLASH zajmie tyle samo ile niżej przedstawione rozwiązania ,ale chodziło mi o to ,że zmniejsza czytelność zapisu - lepiej zrobić to krócej) Istnieje więc możliwość skonfigurowania całego portu za jednym razem.

Config Portd = Input
Config Portb = Output


 Takie rozwiązanie wygląda już znacznie bardziej elegancko ,jednak nie daje nam możliwości niezależnego kontrolowania poszczególnych bitów. Możemy jedynie zdecydować ,aby wszystkie nóżki mikrokontrolera należące do danego portu zostały  wyjściami bądź wejściami. W wyżej przedstawionej sytuacji cały port d przeznaczyłem jako wejście a port b jako wyjście.               


Istnieje jednak sposób na niezależne konfigurowanie poszczególnych bitów a przy tym zachowanie krótkiego zapisu, który wykorzystuje system zero-jedynkowy. Cyfra 0 oznacza tu wejście natomiast wyjście to cyfra 1. Polecenie to wygląda w następujący sposób:


Config Portd = &B1000000
Config Portb = &B11110000


  Powyższy zapis oznacza to samo co pierwszy a przy tym jest o wiele krótszy. Bit nr 0 (najmłodszy) to ten ,który został umieszczony jako pierwszy z prawej strony. Kolejne cyfry  oznaczają kolejne piny danego portu. Znaki &B uprzedzają kompilator ,że liczba która za nimi stoi  zapisana jest w systemie binarnym. Istnieje bowiem możliwość jeszcze krótszego zapisu z wykorzystaniem systemu dziesiętnego. Po prostu zamieniamy system zapisu z zero-jedynkowego na dziesiętny i usuwamy znaki &B.

Config Portd = 63
Config Portd = 15


To wszystkie z możliwości konfiguracji wejść i wyjść w języku BASCOM. Z których będziesz korzystać musisz zdecydować sam. Ja używam przedostatniego sposobu ze względu na dość wysoką czytelność przy krótkim zapisie. Sposób ten jest preferowany nie tylko przeze mnie ale i zapewne przez większość ludzi zajmujących się pisaniem takich programów.
Poza konfiguracją portów na początku pisania programu należy zdecydować także jaki stan spoczynkowy będą posiadać nóżki naszego mikroprocesora. Są to stany wysokie(1) lub niskie(0) które przybiorą bity wyjść i wejść  zaraz po uruchomieniu programu. Jeśli dana noga procesora przyjmie stan niski (0) oznacza to ,że posiądzie potencjał masy - a więc jeśli będzie do niej podłączony przewód do plusa to popłynie prąd (w przeciwnym razie nie). I analogicznie w drugim wypadku. Jeśli dana noga procesora przyjmie stan wysoki (1) oznacza to ,że stanie się jakby "plusem" - a więc jeśli będzie do niej podłączony przewód do masy to popłynie prąd (w przeciwnym razie nie).Podczas jego działania mogą one zmieniać swoje wartości (wszystko zależy od programu).Można to zrobić w dwojaki sposób.

Portd.0 = 0
Portd.1 = 0
Portd.2 = 0
Portd.3 = 0
Portd.4 = 0
Portd.5 = 0
Portd.6 = 0
Portb.0 = 1
Portb.1 = 1
Portb.2 = 1
Portb.3 = 1
Portb.4 = 1
Portb.5 = 0
Portb.6 = 0
Portb.7 = 0      
                                                                                                                                                                  Albo krótszy zapis, który ja preferuję:                                                                                                                                         
Portd = &B0000000
Portb = &B0001111


Podobnie jak przy konfiguracji możemy to zapisać w jeszcze krótszy sposób z udziałem systemu dziesiętnego. Obowiązują tu takie same zasady tzn. usuwamy znaki  &B.

Portd = 0
Portb = 15


Kolejną bardzo ważna dyrektywą jest polecenie Do…Loop . Jest to nieskończona pętla. Polecenia zawarte pomiędzy członem Do a Loop są wykonywane w kółko. Przyjrzyjmy się poniższej części kodu:

Do
Polecenie 1
Polecenie 2
Polecenie 3
Loop


Taki zapis oznacza że procesor najpierw wykona polecenie pierwsze ,potem drugie , następnie trzecie i wróci na początek pętli by znowu wykonywać polecenie pierwsze itd. Gdybyśmy nie zastosowali pętli Do…Loop wówczas polecenia zostały by wykonane a następnie procesor przestałby pracować. Należało by go zresetować ,aby ponownie wykonał program. W późniejszych artykułach ,kiedy zajmę się praktycznym wykorzystaniem poznanej wiedzy dobrze zrozumiesz sens tej dyrektywy.


Ostatnie z ważnych poleceń jest dyrektywa End. Kończy ona po prostu program. Należy umieścić ją na końcu kodu źródłowego. Chodź nie zawsze. Bowiem jeśli wykorzystujemy przerwania lub podprogramy to one znajdują się na końcu. Lepiej jest więc stwierdzić ,że umieszczamy polecenie End zawsze zaraz za poleceniem Loop.


Przy pisaniu programu niezwykle pomocne, choć nie niezbędne są komentarze. Pozwalają one w „ludzki” sposób opatrzyć dane polecenia programu wyjaśnieniami co miałeś na myśli. Aby napisać komentarz należy użyć znaku „” lub polecenia Rem. W komentarzach możemy napisać co chcemy i nie wpływa to na działanie programu. Po prostu ułatwiają one późniejszą analizę programu.
Na zakończenie chciałbym przedstawić najprostszy (w zasadzie nie jest najprostszy ,ale możemy tak przyjąć ponieważ będzie stanowił dla nas niejako bazę do tworzenia kolejnych programów)  możliwy program. Mikrokontroler zaprogramowany w ten sposób nie będzie jednak  wykonywać żadnych zadań.


Config Portd = &B1000000                              ‘konfiguuruję portd
Portd = &B0000000                                          ‘ustawiam stany spoczynkowe
Config Portb = &B11110000                            ‘konfiguruję portb
Portb = &B0001111                                          ‘ustawiam stany spoczynkowe
Do                                                                      Rem początek pętli
Loop                                                                  ‘koniec pętli
End                                                                   ‘koniec programu


W zasadzie polecenie Do…Loop jest tu niepotrzebne, ale  chciałem wykorzystać wszystkie dyrektywy ,które poznaliśmy w tym artykule. Myślę ,że nie ma sensu wgrywać go do procesora ,ponieważ i tak nie zauważymy różnicy w jego działaniu.

Na koniec warto wspomnieć jeszcze o dwóch poleceniach kompilatora odpowiedzialnych za ustawienie zewnętrznego taktowania i typu procesora. Są to $regfile (określa model mikrokontrolera) i $crystal (określa częstotliwość kwarcu wyrażoną w hz). Polecenia te wpisujemy na początku programu np. :

$regfile = "2313def.dat"   
$crystal = 4000000 
 
Powyższy zapis oznacza ,że pracujemy na uC - attiny2313 z taktowaniem 4Mhz = 4000000hz. Należy również  pamiętać, że do nazwy procesora dodajemy rozszerzenie .dat . Ja jednak nie umieszczam tych informacji w programie. Zamiast wpisywać te polecenia w tekst programu wygodniej dokonać koniecznych zmian w zakładce "Options" (ikona zębatki na górnym pasku menu). Mamy tam możliwość wskazania procesora ,który używamy oraz taktowania współpracującego z nim kwarcu (bądź też oscylatora wewnętrznego czy np. generatora częstotliwości)
                               Options - Compiler - Chip - w oknie Chip wybieramy 2313.dat 
                               Options - Compiler - Communication - w oknie Frequency wybieramy 4000000

Jeśli w programie wpiszemy sprzeczne dane względem tych zawartych w ustawieniach to dane w ustawieniach zostaną zignorowane.Należy jednak zaznaczyć, że polecenie  nie ingeruje w fusebity mikrokontrolera i nie spowoduje przestawienia kwarcu (należy to zrobić za pomocą AVR studio tak jak pokazałem w ostatnim artykule). To polecenie jedynie informuje BASCOMA z jakim kwarcem będzie współpracował. Ma to istotne znacznie zwłaszcza przy obsłudze poleceń takich jak wait, waitms , waitus, lcd itp.Dokładnie przyczynę tego zjawiska omówię w kolejnych częściach artykułu.

piątek, 1 czerwca 2012

Mikrokontrolery od strony technicznej, systemy zapisu liczb i operatory logiczne

    
      Język BASCOM nie wymaga od programisty szczególnej znajomości budowy mikroprocesora. Jednak aby móc skutecznie pisać programy należy zapoznać się przynajmniej z podstawami tego zagadnienia. Mikrokontrolery AVR są to procesory 8 bitowe. Oznacza to ,że w jednym cyklu potrafią przetworzyć 8 bitów danych czyli jeden bajt.  Bit jest to najmniejsza jednostka informacji dla mikrokontrolera. Może ona przyjąć stan wysoki (1) lub niski (0).  Cykle w pracy mikrokontrolera generuje rezonator kwarcowy. Każdy z takich generatorów ma określoną częstotliwość np. 8Mhz.  Liczba Mhz oznacza ilość milionów cykli wykonywanej w jednostce czasu ( w tym wypadku sekundzie). Wcześniej wspomniana częstotliwość 8Mhz oznacza ,że kwarc wytwarza 8000000 cykli na sekundę. Czym szybszym  kwarcem dysponuje mikrokontroler tym dokładniej potrafi działać.  Do bezproblemowego działania kwarcu niezbędne są również kondensatory ceramiczne o pojemności rzędu kilkudziesięciu bądź kilkunastu pF (zazwyczaj uC działa także bez tych kondensatorów ja jednak dla bezpieczeństwa stosuje 2x kondensator 15pF) . Jednak istnieje możliwość rezygnacji  z zewnętrznego rezonatora i korzystania z wewnętrznego taktowania (w wypadku attiny2313 domyślnie jest to 1Mhz , można wykorzystać także większe częstotliwości jednak w porównaniu do zewnętrznego kwarcu dokładność wewnętrznego oscylatora pozostawia wiele do życzenia). Wówczas poza kwarcem nie podłączamy także kondensatorów.

Kolejnym zagadnieniem dotyczącym obsługi mikrokontrolera jest funkcja reset, będąca jedną z nóżek naszego procesora. Reset pozwala na natychmiastowe przerwanie działania programu i uruchomienie go od początku. Aby uruchomić funkcje reset należy połączyć nóżkę reset z potencjałem masy. Aby mikrokontroler poprawnie działał jednocześnie nóżka reset powinna być połączona z plusem zasilania za pomocą rezystora o oporze 10kohm. Dodatkowo dla pewności nóżkę reset można połączyć z masą za pomocą kondensatora ceramicznego 10nF. Poprawne połączenie kwarcu wraz z kondensatorami oraz resetu do mikroprocesora przedstawia poniższy rysunek:




Na powyższym schemacie uwzględniłem kondensator filtrujący napięcie - powinien być ceramiczny o pojemności przynajmniej 10nF (najlepiej więcej - nawet do 100nF). Ulokować należy go jak najbliżej zasilania. Jest to bardzo istotne jednak często pomijane przez początkujących. Bez użycia tego kondensatora mikroprocesor może przejawiać dziwne zachowania - np. czasem układ uruchamia się , innym razem nie. Niekiedy występuje efekt "palucha" :) polegający na samoistnym restarcie uC pod wpływem dotknięcia jego obudowy. Wiele osób przygląda się wówczas ze zdziwieniem jak za sprawą dotknięcia czarodziejskiej różdżki układ się wyłącza. Kierunek podłączenia tego rodzaju kondensatora (ceramicznego) jest dowolny.
Ponad to wyżej zamieściłem także rezonator kwarcowy. Podobnie jak kondensator ceramiczny możemy podłączyć go w dowolnym kierunku. Dodatkowo obie nogi kwarcu odciągamy do masy 2 kondensatorami - czasem działa bez tych kondensatorów ale nie warto ryzykować. Dokładna wartość tych kondensatorów zamieszczona jest w nocie katalogowej konkretnego układu (zależy ona od częstotliwości kwarcu i modelu uC). Ja zazwyczaj stosuje ok. 15 pF - nie można użyć zbyt dużych pojemności bo wówczas sygnał z kwarcu będzie "ścianny" i nie dotrze do procesora. Warto wziąść także pod uwagę ,iż procesor taktowany wyższą częstotliwością wymaga większego napięcia zasilania (np. Atmega8A może być zasilana napięciem 2,7v - 5v jednak jeśli użyjemy kwarcu 16Mhz minimalne napięcie zasilania wynosi 4,5v).
Aby zmusić procesor do współpracy z zewnętrznym kwarcem (domyślnie jest wewnętrzny) należy zmienić ustawienie fusebitów - bitów konfiguracyjnych. Można dokonać tego np. za pomocą wcześniej opisanego programu - AVR studio.  Po połączeniu się z programatorem (co opisałem w poprzednim artykule) należy wybrać zakładkę "Fuses" i z rozwijanej listy "SUT_CKSEL" wybrać opcje odpowiednią dla danego kwarcu.


Poniżej przedstawiam wariant z ustawieniem dla zewnętrznego rezonatora 16 Mhz 
Ext.Crystal/Resonator High Freq- oznacza że mówimy o zewnętrznym kwarcu
Start-up time: 16K CK  - oznacza ,że uruchamiamy kwarc o częstotliwości 16Mhz
+ 64ms - oznacza ,że procesor uruchomi się po czasie 64ms od włączenia zasilania. Można ustawić większą szybkość, ale wówczas procesor będzie działać mniej stabilniej. Następnie klikamy "Program".

Przy ustawianiu fusebitów należy zachować ostrożność ponieważ przypadkowe ustawienie jakiegoś innego fusebita może zablokować procesor. Możemy np. wyłączyć wejście RESET co spowoduje uniemożliwienie programowania uC normalnym programatorem. Co więcej jeśli zaprogramujemy dany kwarc ,ale nie podłączymy go ,albo podłączymy inny kwarc (lub np. generator częstotliwości) procesor także nie będzie działać.


Ostatnia kwestia dotyczy odciągnięcia RESETU. Zawsze, nawet gdy go nie używamy należy odciągnąć go rezystorem 10kohm do VCC. Jeśli chcemy w naszym układzie uwzględnić przycisk reset to dodatkowo odciągamy wejście RESET przez ten przycisk do plusa.


Teraz przejdę do omówienia pamięci. Mikroprocesor  posiada jej 3 rodzaje:

Pamięć FLASH W niej zapisywany jest program ,a więc instrukcja działania mikrokontrolera. Pamięć ta ulega zmianie jedynie wówczas ,gdy zdecydujesz się zaprogramować procesor. Podczas zaniku zasilania pamięć FLASH nadal pozostaje niezmieniona.  Jest to pamięć najmniej wytrzymała na przeprogramowanie (zazwyczaj możemy wykonać ok. 1000 przeprogramowań). Informacje w niej zawarte można porównać do BIOSU komputera.

Pamięć RAM (Inna nazwa SRAM)Jest to pamięć w której zapisywane są informacje wybierane przez mikroprocesor na podstawie programu zapisanego w pamięci FLASH (np. wartości zmiennych). Informacje zawarte w pamięci RAM bardzo często ulegają zmianie podczas działania programu a w wyniku zaniku zasilania wszystkie informacje w niej zawarte ulegają utraceniu. Pamięć RAM jest w stanie dużo więcej razy zmieniać zapisane w sobie informacje niż inne rodzaje pamięci.

Pamięć EEPROMPamięć ta zapisuje informacje wybrane przez mikrokontroler na podstawie danych zebranych przez procesor podczas działania programu. Nie ulega ona utracie podczas zaniku napięcia zasilania. Jest bardziej wytrzymała na przeprogramowywanie niż pamięć FLASH ,jednak mniej odporna pod tym względem od pamięci RAM. 

Istotnymi elementami budowy mikroprocesora są także rejestry danych ,czyli tak jakby komórki pamięci procesora. Najważniejszymi rejestrami dla początkującego programisty BASCOMA są porty (w przypadku attiny2313 to portd i portb). Porty składają się z pojedynczych bitów ,których fizycznymi odpowiednikami są określone nóżki mikrokontrolera. Mogą one być wejściami procesora i służyć do zbierania przez niego informacji lub wyjściami w celu generowania sygnału zwrotnego.W bardziej zaawansowanych językach np. assemblerze programiści pisząc program uruchamiają wszystkie rejestry bezpośrednio co wymaga ogromnej wiedzy na ich temat. Pisząc w BASCOM’ie nie musisz się jednak oto martwić ,bo podczas kompilacji program ten zamienia dyrektywy języka na polecenia odpowiedzialne za uruchamianie bądź wyłączanie poszczególnych rejestrów.

Na co dzień używamy dziesiętnego systemu zapisu liczb. Jednak w informatyce znacznie częściej wykorzystuje się dwójkowy system zapisu liczb (tzw. system binarny). Dwójkowy system zapisu liczb musisz dobrze poznać i zrozumieć dlatego też postaram  Ci się go w prosty sposób wytłumaczyć.Przykładem liczby dwójkowej jest np. „1010”.W systemie tym cyfra będąca pierwszą od lewej symbolizuje wartość  1, cyfra stojąca na drugim miejscu od lewej oznacza wartość dwukrotnie większą czyli 2 , trzecia cyfra od lewej wartość  4 , czwarta cyfra od lewej wartość 8 itd. Aby obliczyć wartość liczby zapisanej w systemie zero-jedynkowym należy zsumować wartości miejsc na których zapisane są cyfry 1. A więc pierwsze miejsce od lewej zajmuje cyfra zero czyli wartość przypisaną do tego miejsca należy pominąć, kolejna liczba to 1 dlatego też należy uwzględnić wartość zajmowanego przez nią miejsca ,a więc jest to 2, kolejna cyfra to 0 czyli możemy ją pominąć , a ostatnia cyfra to 1 ,symbolizująca wartość 8. Wartość szukanej liczby to 10 ,bo 2 + 8 =10.

Poniżej przedstawiam dodatkowe przykłady liczb zapisanych w systemie dwójkowym i ich dziesiętne odpowiedniki:
1011 = 11
1111 = 15
11 = 3
1 = 1
0 = 0
10101 = 21
101 =  5
10000000 = 128
10000001 = 129
10000011 = 131

W informatyce często używa się także systemu szesnastkowego w którym do zapisu liczb wykorzystuje się właśnie 16 znaków. Są to cyfry od 0 do 9 i litery od A do F. Na razie znajomość tego systemu nie jest Ci potrzebna. Przykłady liczb zapisanych w systemie szesnastkowym to : AA , 1A , 3 , BF ,9F ,G, 2C , CD , CCC , 659 .

Przy pisaniu programów często będziesz miał do czynienia z operatorami logicznymi. Określają one, w jaki sposób dane warunki muszą zostać spełnione aby zdanie zostało uznane jako prawdziwe. Istnieją cztery podstawowe operatory logiczne. 

AND – Aby zdanie zostało uznane jako prawda oba warunki muszą zostać spełnione.

OR – Aby zdanie zostało uznane jako prawda przynajmniej jeden warunek musi zostać spełniony

XOR – Aby zdanie zostało uznane jako prawda dokładnie jeden warunek musi zostać spełniony

NOT – Zmienia wartość warunku na przeciwną. Prawdę na fałsz natomiast fałsz na prawdę.

W tej części to już wszystko.