Skocz do zawartości
Zaloguj się, aby obserwować  
kermit

[Kurs] Programowanie mikrokontrolerów AVR w języku assembler - część 4

Pomocna odpowiedź

Część 4 - ADC

W dzisiejszesj części naszego kursu zajmemy się konfiguracją przetwornika analogowo-cyfrowego, czyli ADC(analog to digital coverter).

Czym on jest i po co nam on?

Otóż przetwornik analogowy/cyfrowy jest to jeden z modułów naszego mikrokontrolera.Po co nam on?Jak zapewne wiecie nasze mikrokontrolery są układami cyfrowymi-znaczy to, że rozumieją one jedynie wartośći 0 i 1, wymyślono więc coś takiego jak przetwornik analogowo-cyfrowy służy on nam do zamiany sygnału analogowego, czyli sygnału który może przyjmować dowolną wartość( w przypadku gdy taki sygnał będziemy bezpośrednio podawać na wejście naszego przetwornika, może on wynosić max. 5V) na sygnał cyfrowy, czyli sygnał zrozumiały dla procesora, dzięki temu w programie możemy potem operować na wartościach, które zostały przetworzone/zmierzone przez nasz przetwornik.

Jak go uruchomimy?

Podobnie jak inne peryferia, czyli musimy skonfigurować rejestr/y odpowiadające za jego działanie. W przypadku przetwornika rejestrami odpowiedzialnymi za jego działanie są rejestry ADMUX oraz ADCSRA. Informacje o tych rejestrach możemy znaleźć od strony 214 noty aplikacyjnej, ale polecam zacząć lekture od strony 201, ponieważ znajdziemy tam wszystkie informacje o przetworniku A/C w naszej atmedze. Poza rejestrami odowiedzialnymi za konfigurację ADC, powinniśmy zwrócić uwagę na jeszcze jeden rejestr a mianowicie- rejestr ADC w którym, po dokonaniu pomiaru, możemy znaleźć jego wynik, z racji tego że nasz przetwornik jest 10-bitowy, a rejestry mamy jedynie 8-bitowe to rejestr ADC podzielony jest na 2 rejestry- ADCH(starsza część słowa) i ADCL(młodza część) .

Co to znaczy, że przetwornik jest 10-bitowy?

Otóż znaczy to, że wynik pomiaru może być przedstawiony jako wartość od 0 do 1024,a jak to się ma do rzeczywistego poziomu napięcia, który mierzony jest na wejściu przetwronika?

możemy to wyliczyć ,ze wzoru:

Wynika z tego, że Vin = Aref/1024 x ADC(wartośc jaką możemy znaleźć w rejestrze ADC). Przy okazji widzimy do czego służy nam pin AREF, podajemy na niego napięcie odniesienia dla naszego przetwornika.

Teraz przechodzimy do konfiguracji :

rejestr ADMUX wygląda tak:

MUX0,1,2,3,4 – Odpowiadają za wybór kanału ADC na którym będziemy dokonywali pomiaru, nasza atmega32 ma tych kanałów 8, wszystkie są podłączone do portu A, ale pomiaru dokonywać możemy naraz tylko na jednym z tych kanałów. Tabelkę z tym które bity należy ustawić, aby wybrać żądany kanał znajdziemy na stronie 215 noty aplikacyjnej.

ADLAR – Jak już pisałem dysponujemy przetwornikiem 10-bitowym, a bit ten odpowiedzialny jest za prezentację wyniku w rejestrach ADCH i ADCL, jeśli ustawimy ten bit na "1" to 2 najmniej znaczące bity znajdą się w rejestrze ADCL, a 8 bitów bardziej znaczących w rejstrze ADCH, jeśli bit ten pozostawimy ustawiony na "0" to 2 najbardziej znaczące bity znajdą się w rejestrze ADCH, a 8 mniej znaczących znajdzie się w rejestrze ADCL.Najlepiej tłumaczy to ilustracja, która mozna znaleźć w nocie aplikacyjne, a wygląda ona tak:

Ale co to znaczy z punkt widzenia programisty?

Bit ADLAR ustawiamy, gdy wystarczy nam 8-bitowa rozdzielczość pomiaru, ponieważ wtedy możemy znaleźć wszystkie bardziej znaczące bity w rejestrze ADCH i nie musimy zawracać sobie głowy pozostałymi 2bitami z rejestru ADCL.W naszym wypadku 8-bitowa rozdzielczośc(w wypadku gdy Aref = 5v, to rozdielczośc wynosi 0,019V na działkę) pomiaru jest jak najbardziej wystarczająca, więc ustawiamy ten bit na "1".

REFS0,1- bity odpowiedzialne za wybór napięcia odniesienia, ustawiamy je według tabelki

W naszym wypadku mamy na pinie AREF mamy,a przynajmniej powinniśmy mieć kondensator 100nF podłączony do masy, więc ustawiamy bit REFS0

Kolejnym rejestrem, który należy skonfigurować jest rejestr ADCSRA:

ADEN – co tu dużo pisać, ustawienie tego bitu włącza ADC

ADSC – ustawienie tego bitu powoduje rozpoczęcie pomiaru

ADATE – ustawienie tego bitu powoduje wybranie innego sposobu (niż ustawienie bitu ADSC) wyzwalania pomiaru, proponuję otworzyć stronę 218 noty aplikacyjnej atmegi32 i samemu sprawdzić co mamy do wyboru 🙂 w przykładowych programach nie korzystałem z tego, ale jeśli ktoś jest ambitny to polecam poeksperymentować 🙂

ADIF – jest to tzw. "flaga" ustawienie tego bitu następuje automatycznie, gdy pomiar został zakończony i rejestry ADC(ADCH i ADCL) zaaktualizowane.

ADIE – zezwolenie na wywołanie przerwania przez przetwornik, gdy pomiar zostanie zakończony. Jako, że narazie nie mówiłem o przerwaniach, to nie będziemy ustawiali tego bitu, ale użycie przerwania, zamiast czekanie w pętli na ustawienie flagi ADIF jest lepszym rozwiązaniem.

ADPS2,1,0- bity odpowiedzialne za wybór preskalera dla zegara przetwornika preskaler musimy wybrać tak, aby częstotliwość dla pomiaru 10-bitowego zawierała się w przedziale 50-200khz , lub dla pomiaru z dokładnością 8-bitów ,delikatnie powyżej 200khz.W swojej atmedze częstotliwość mam ustawioną na domyślną( 1mhz), więc wybrałem preskaler 8 co daje częstotliwośc 1mhz/8 =125khz.

Gdy wiemy co nieco o przetworniku to pora napisać pierwszy program

Ćwiczenie 1 – regulacja jasności świecenie diody LED

temat ćwieczenia może wydawać się wam znajomy, ponieważ podobne ćwiczenie wykonaliśmy w poprzedniej części kursu, ale tym razem jasność świecenia diody LED będziemy regulowali za pomocą potencjometru .Wyjście naszego potencjometru będzie podłączone do wejścia przetwornika A/C, do regulacji jasności świecenia diody użyjemy PWM.

Tak będzie to wyglądało na schemacie:

a teraz pora na program:

.nolist
.include "m32def.inc"
.list

.cseg
.org 0

cli							//wyłączenie przerwań
ldi R16, HIGH(RAMEND)		//implementacja stosu
out SPH, R16
ldi R16, LOW(RAMEND)
out SPL, R16


//konfiguracja PWM
sbi DDRB, 3												//ustawienie jako wyjście linii na której będzie generowany sygnał PWM
ldi R16, (1<<WGM00)|(1<<WGM01)|(1<<COM01)|(1<<CS00)		//załadownie do R16 wartości która posłuży do konfiguracji timera0
out TCCR0, R16											//załadowanie wartośći z R16 do TCCR0

sbi DDRC,0 												//ustawienie stanu niskiego na PC0(katoda diody LED)
cbi PORTC, 0


//konfiguracja ADC
ldi R16,(1<<REFS0)|(1<<ADLAR)							//załadowanie do R16 wartości która posłuży do konfiguracji rejestru ADMUX					
out ADMUX, R16											//załadowanie wartośći z R16 do ADMUX

ldi R16,(1<<ADEN)|(1<<ADPS1)|(1<<ADPS0)|(1<<ADSC)	
out ADCSRA, R16


main: 					//program glowny
sbic ADCSRA, ADIF		//jeśli bit ADIF w rejestrze ADCSRA nie jest ustawiony(konwersja nie gotowa) to pomiń następną instrukcje
rcall load_OCR			//wywołaj poprogram który załaduje wartość z przetwornika ADC do rejestru OCR0
rjmp main				//instrukcja zostanie wykonana gdy konwersja nie jest jeszcze gotowa ,tym samym pętla ta wykonuje się, gdy oczekujemy na konwersje

load_OCR:				//podprogram ładujący wartość z rejestru ADCH do rejestru OCR0
in R16, ADCH			//załaduj wartośc z ADCH do R16
out OCR0, R16			//załaduj wartośc z R16 do OCR0
sbi ADCSRA, ADSC		//ustaw bit ADSC w rejestrze ADCSRA , powoduje to wyzwolenie kolejnej konwersji ADC
ret						//powrót do podprogramu main

Idea programu jest bardzo prosta, najpierw przetwornik dokonuje pomiaru napięcia na wyjściu potencjometru, następnie wartość tego pomiaru ładowana jest do rejestru OCR0, więc czym większe napięcie na wejściu przetwornika tym większe wypełenienie PWM, co przekłada się na jaśniejsze światło diody LED.

Ćwiczenie 2 - obsługa transopotora odbiciowego:

W tym ćwiczeniu zajmiejmy się obsługą transoptora obciciowego(cny70), czyli czujnika jaki często używany jest w konstrukcjach robotów FTL.Najpierw zmontujmy układ wg. poniższego schematu:

zmontowany układ wygląda tak:

gdy zmontujemy układ przechodzimy do programu, jego zadaniem będzie zapalenie diody LED, kiedy czujnik umieścimy nad czarną powierzchnią(w moim przypadku jest to kawałek izolacji) i gaszenie jej gdy czujnik umieścimy nad białą powierzchnią(kartka). Program który wykona to zadanie prezentuje się tak:

.nolist
.include "m32def.inc"
.list

.cseg
.org 0

.def calib = R17			//nadanie symbolicznej nazwy rejestrowi R17

cli							//wyłączenie przerwań
ldi R16, HIGH(RAMEND)		//implementacja stosu
out SPH, R16
ldi R16, LOW(RAMEND)
out SPL, R16

sbi DDRC, 0					//ustawienie jako linia wyjściowa PC0


//konfiguracja ADC
ldi R16,(1<<REFS0)|(1<<ADLAR)|(1<<MUX0)|(1<<MUX1)|(1<<MUX2)	//załadowanie do R16 wartości która posłuży do konfiguracji rejestru ADMUX,wybranie kanału 8 przetwornika(bity mux)					
out ADMUX, R16												//załadowanie wartośći z R16 do ADMUX

ldi R16,(1<<ADEN)|(1<<ADPS1)|(1<<ADPS0)|(1<<ADSC) 			//załadowanie do R16 wartości która posłuży do konfiguracji ADCSRa
out ADCSRA, R16												//załadowaine R16 do ADCSRA

calibration:			//podprogram który kalibruje nasz czujnik
sbis ADCSRA, ADIF		//jeśli bit ADIF jest ustawiony(pomiar gotowy) pomiń następną instrukcje
rjmp calibration		//jeśli bit ADIF jest wyzerowany(pomiar nie jest gotowy) instrukcja zostanie wykonany
in calib, ADCH			//załaduj wartośc rejestru ADCH do rejestru calib_value(R17)
sbi ADCSRA, 6			//uruchom następny pomiar

main:					//program główny
sbis ADCSRA, ADIF		//jeśli bit ADIF w rejstrze ADCSRA jest ustawiony(pomiar gotowy)pomiń następną instrukcje
rjmp main				//instrukcja zostanie wykonana, jeśłi bit ADIF jest wyzerowany(pomiar trwa)
in R16, ADCH			//załaduj wartość pomiaru(znajdującej się w ADCH) do R16
cp calib, R16			//porównaj wartośc R16 z calib_value(R17)
brge LED_on				//skocz do etykiety LED_off jeśli wynik porównania to większy, bądź równy
rjmp LED_off			//jeśli wartość aktualnego pomiaru jest mniejsza od calib(R17),skocz do podprogramu wylaczajacego diode


LED_on:					//podprogram zapalajacy diode i wyzwalający następny pomiar
sbi PORTC,0 			//zapal diode
sbi ADCSRA, ADSC		//ustaw bit ADSC w rejestrze ADCSRA- wyzwala to kolejny pomiar
rjmp main				//skocz do programu głównego

LED_off:				//podprogram zapalający diodę i wyzwalający następny pomiar
cbi PORTC,0				//wygas diode
sbi ADCSRA, ADSC		//ustaw bit ADSC w rejestrze ADCSRA - wyzwala to kolejny pomiar
rjmp main				//skocz do programu głównego

Jak widzimy w programie występuje etykieta o nazwie "calibration" jest to podprogram, który po uruchomieniu zasilania lub resecie mikrokontrolera mierzy wartość z jaką będzie porównywał póżniejsze wyniki, więc aby cały program działał poprawnie podczas uruchomienia mikrokontrolera lub po jego resecie musimy czujnik trzymać ok. 2mm nad powierzchnią kartki.

W podprogramie "calibration" możemy również zobaczyć ładowanie zmierzonej wartości do rejestru o nazwie "calib" jest to nazwa która nadałem rejestrowi R17 poprzez dyrektywę:

".def "

Nowe instrukcje, które pojawiły się w programie:

BRGE - Branch if Greater or Equal- skocz jeśli większe, bądź równe "do etykiety"

IN – in port – w wolnym tłumaczeniu - załaduj "do rejestru ogólnego przeznaczenia" " z rejestru systemowego"

CP – compare – porównaj "nazwa rejestru ogólnego przeznaczenia" z "nazwa rejestru ogólnego przeznaczenia"

Na koniec - efekt działania naszego programu:

To by było tyle na dzisjaj.

Aha no i oczywiście- jeśli ktoś zauważy jakieś błędy w artykule to proszę pisać 🙂

Pozdrawiam 😉

adc.thumb.jpg.91fa97b1a1c6dc0709e8caea992c529b.jpg

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

Zaloguj się, aby obserwować  

×
×
  • Utwórz nowe...