Skocz do zawartości

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


kermit

Pomocna odpowiedź

Witam w kolejnej części kursu programowania mikrokontrolerów AVR w języku assembler, dziś zajmiemy się przerwaniami. Co to są przerwania? Jak nazwa sugeruje ich istota polega przerwaniu czegoś, w naszym wypadku chodzi o przerwanie wykonywania programu i skok do tzw. Procedury obsługi przerwania(ISR)- czyli wydzielonego fragmentu programu do którego wykonywany jest skok, gdy jakiś układ zgłasza przerwanie. Układem, który zgłasza przerwanie może być jeden z modułów peryferyjnych w naszym mikrokontrolerze, np. Przetwornik analogowo - cyfrowy o którym pisałem w poprzedniej części.

Po co nam te przerwania:

-Otóż przerwania mogą bardzo przyspieszyć wykonywanie naszego programu, aby to udowodnić przedstawię przykład z przetwornikiem analogowo-cyfrowym, otóż jak pamiętamy z poprzedniej części kursu, aby odczytać wartość napięcia jaką zmierzy przetwornik musieliśmy czekać w pętli aż wartość w rejestrze ADC zostanie uaktualniona, jako że przetwornik ten działa dość wolno to większość czasu nasz program tkwił w pętli sprawdzając, czy pomiar jest już gotowy, taka pętla bardzo spowalnia wykonywanie naszego programu. W takim wypadku najlepszym rozwiązaniem jest użycie przerwania od przetwornika ADC, gdy przetwornik wykona pomiar zgłasza przerwanie, wykonywanie programu głównego jest wstrzymywane, następuje skok do procedury obsługi przerwania gdzie wartość pomiaru wpisywana jest do rejestru ogólnego przeznaczenia, po napotkaniu rozkazu "reti" dokonywany jest powrót do miejsca w którym był program zanim nastąpiło przerwanie.

Przerwania dzielimy na wewnętrzne, czyli takie które przychodzą od modułów wewnętrznych mikrokontrolera(przetwornik A/C, timery, UART, TWI itp.) oraz przerwania zewnętrzne, czyli takie które wyzwalane jest przez jakiś układ zewnętrzny, przerwanie takie wyzwalane jest poprzez podanie sygnału na odpowiednie wejście mikrokontrolera(wejścia te oznaczane są jako INTn, np. W naszej atmedze32 mamy 3 takie wejścia- INT0(PD2), INT1(PD3) oraz INT2(PB2) ).

jak konfigurujemy przerwania zewnętrzne?

Aby skonfigurować przerwania zewnętrzne musimy skonfigurować rejestry MCUCR oraz GICR

Opis pierwszego z nich możemy znaleźć na stronie 66 noty aplikacyjnej atmegi32

jak widzimy 4 bardziej znaczące bity są zaznaczone szarym kolorem i nie są one przeznaczone do konfiguracji przerwań zewnętrznych

Bity które nas interesują to te o nazwie ISC01 oraz ISC00 służą one do określenia jaki sygnał na wejściu INT1 ma wyzwalać przerwanie, ustawiamy je wg. Tej tabelki:

jako że na wejściu INT0 mamy przycisk, który po wciśnięciu zwiera to wejście do masy to wybieramy wyzwalanie przerwania zboczem opadającym, czyli ustawiamy bit ISC01

bity ISC11 oraz ISC10 odpowiadają za ustawienie przerwania INT1, jako że my nie używamy tego wejścia to pozostawiamy je w spokoju.

Kolejnym rejestrem do ustawienia jest rejestr GICR, którego struktura prezentuje się tak:

I tutaj akurat wielkiej filozofii nie ma, bity INT0,1,2 służą do włączania poszczególnych przerwań zewnętrznych.

Ustawiamy bit INT0.

Ćwiczenie 1:

Gdy wiemy już co musimy poustawiać pora wkońcu coś zrobić :

montujemy układ według powyższego schematu i przystępujemy do pisania programu. W pierwszym programie użyjemy przerwania zewnętrznego, będzie ono wywoływane poprzez zwarcie [przyciskiem] linii PB3 do masy. W procedurze obsługi przerwania znajdzie się instrukcja która zapali naszą diodę, wykona opóźnienie o 1s i powróci do programu głównego.

Możliwe, że niektórym bardziej doświadczonym osobom w tym momencie zapaliła się w głowie czerwona lampka, czytając o tym, że w przerwaniu ma być wykonywane opóźnienie. Otóż przerwania i opóźnienia nie są zbyt dobrym połączeniem i należy pamiętać, że w przerwaniach raczej nie powinno się używać opóźnień, ani długich pętli, wyjątkiem są bardzo proste programy lub takie gdzie używamy tylko jedno przerwanie, czyli w naszym wypadku nie będzie problemu, ale warto zapamiętać tą regułę na przyszłość

Nasz program będzie wyglądał tak:

.nolist 
.include "m32def.inc"
.list 

.cseg
.org 0

//tablica wektorów przerwań	
jmp start			
.org 0x0002	rjmp EXT_INT0; External Interrupt Request 0
.org 0x0004 reti	; External Interrupt Request 1
.org 0x0006	reti; External Interrupt Request 2
.org 0x0008	reti; Timer/Counter2 Compare Match
.org 0x000a	reti; Timer/Counter2 Overflow
.org 0x000c	reti; Timer/Counter1 Capture Event
.org 0x000e	reti; Timer/Counter1 Compare Match A
.org 0x0010	reti; Timer/Counter1 Compare Match B
.org 0x0012	reti; Timer/Counter1 Overflow
.org 0x0014	reti; Timer/Counter0 Compare Match
.org 0x0016	reti; Timer/Counter0 Overflow
.org 0x0018	reti; Serial Transfer Complete
.org 0x001a	reti; USART, Rx Complete
.org 0x001c	reti; USART Data Register Empty
.org 0x001e	reti; USART, Tx Complete
.org 0x0020 reti; ADC Conversion Complete
.org 0x0022	reti; EEPROM Ready
.org 0x0024	reti; Analog Comparator
.org 0x0026	reti; 2-wire Serial Interface
.org 0x0028	reti; Store Program Memory Ready


start: //początek programu
cli	   //wyłączenie przerwań
ldi R16, HIGH(RAMEND)
out SPH, R16
ldi R16, LOW(RAMEND)
out SPH, R16

sei		//włączenie przerwań

sbi DDRC, 0			//ustawienie linii do której podłączona jest dioda jako wyjście	
cbi DDRD, 2			//ustawienie linii PD2 jako wejście, zwarcie tej linii do masy wyzwoli przerwanie 
sbi PORTD, 2		//podciągnięcie linii PD2 do napięcia zasilania 

ldi R16, (1<<ISC01)	 //ustawienie sygnału wyzwalającego przerwanie INT0- zbocze opadające
out MCUCR, R16		

ldi R16, (1<<INT0)	//zezwolenie na przerwanie od INT0
out GICR, R16


main:
cbi PORTC, 0		//zgaś diodę 
rjmp main

EXT_INT0:			//procedura obsługi przerwania INT0
sbi PORTC, 0 		//zapal diode LED

; ============================= 
;    podprogram opózniający
;     	   1 sekunda:
; ----------------------------- 
; delaying 999999 cycles:
         ldi  R17, $09
WGLOOP0:  ldi  R18, $BC
WGLOOP1:  ldi  R19, $C4
WGLOOP2:  dec  R19
         brne WGLOOP2
         dec  R18
         brne WGLOOP1
         dec  R17
         brne WGLOOP0
; ----------------------------- 
; delaying 1 cycle:
         nop
; ============================= 


reti			//rozkaz powrotu z przerwania

Jak widać w naszym programie pojawiło się coś nowego, a mianowicie tablica wektorów(adresów) przerwań, z racji tego, że w programie używamy tylko i wyłącznie jednego przerwania(INT1), to z jednego adresu(0x002) wykonywany jest skok do etykiety "EXT_INT0" pełna tablica wektorów przerwań, którą wklejamy do programu wygląda tak(w zależności od przerwania jakiego używamy, zastępujemy w odpowiednim miejscu rozkaz reti , rozkazem rjmp "etykieta procedury obsługi przerwania"):

jmp start			
.org 0x0002	reti; External Interrupt Request 0
.org 0x0004	reti; External Interrupt Request 1
.org 0x0006	reti; External Interrupt Request 2
.org 0x0008	reti; Timer/Counter2 Compare Match
.org 0x000a	reti; Timer/Counter2 Overflow
.org 0x000c	reti; Timer/Counter1 Capture Event
.org 0x000e	reti; Timer/Counter1 Compare Match A
.org 0x0010	reti; Timer/Counter1 Compare Match B
.org 0x0012	reti; Timer/Counter1 Overflow
.org 0x0014	reti; Timer/Counter0 Compare Match
.org 0x0016	reti; Timer/Counter0 Overflow
.org 0x0018	reti; Serial Transfer Complete
.org 0x001a	reti; USART, Rx Complete
.org 0x001c	reti; USART Data Register Empty
.org 0x001e	reti; USART, Tx Complete
.org 0x0020	reti; ADC Conversion Complete
.org 0x0022	reti; EEPROM Ready
.org 0x0024	reti; Analog Comparator
.org 0x0026	reti; 2-wire Serial Interface
.org 0x0028	reti; Store Program Memory Ready
start:

Wcześniej mówiłem, że w przerwaniu następuje skok do jakiegoś wydzielonego miejsca w programie i wykonanie tzw. Procedury obsługi przerwania(tak nazywamy część programu wykonywaną, gdy nastąpi przerwanie), a teraz chciałbym nadmienić, że skok do procedury obsługi przerwania nie jest bezpośredni, tylko najpierw następuje skok do tablicy wektorów, a dokładniej do którego adresu w tej tablicy, można wyczytać w tabeli 18 noty aplikacyjnej atmegi32

tablica ta wygląda tak:

i jak widać adres do którego zostanie wykonany skok zależy od przerwania jakie ma być wykonane.U nas skok zostaje wykonany do adresu 0x002, skąd dopiero następuje skok do miejsca w którym zawarta jest nasza procedura obsługi przerwania, w naszym programie miejsce to wskazuje etykieta EXT_INT0. W razie gdybyśmy w programie używali innych przerwań wystarczy w odpowiednim miejscu tablicy zastąpić instrukcje "reti" fragmentem który prezentuje się tak: "rjmp" "etykieta procedury obsługi przerwanie".

Ćwiczenie 2 – przerwania wewnętrzne

W tym ćwiczeniu wykorzystamy ten sam układ co w poprzednim, czyli diodę LED podłączoną anodą do portu PC0 oraz przycisk z jednej strony podłączony do PD2 a z drugiej do potencjału masy.

Tym razem w programie wykorzystamy dwa przerwania jedno które już znamy, czyli przerwanie zewnętrzne INT0 oraz jedno przerwanie wewnętrzne, które będzie pochodziło od timera1(16-bitowy, czyli wartość do której zlicza jest równa 65536). Zadanie programu będzie jak zwykle niezbyt skomplikowane- za pomocą przycisku S1 wyzwalamy przerwanie INT0 w którego procedurze obsługi znajdzie się instrukcja zapalenia diody, następnie po przepełnieniu timera1 nastąpi przerwanie w którym dioda zostanie zgaszona. Przepełenienie timera nastąpi po 65535 x 8(wart. Preskalera) = 524280 cykli co przy taktowaniu 1mhz daje ok. 0,52s(po tym czasie dioda zostanie zgaszona )

tak prezentuje się nasz program:

.nolist 
.include "m32def.inc"
.list 

.cseg
.org 0
jmp start			
.org 0x0002	rjmp EXT_INT0; External Interrupt Request 0
.org 0x0004 reti	; External Interrupt Request 1
.org 0x0006	reti; External Interrupt Request 2
.org 0x0008	reti; Timer/Counter2 Compare Match
.org 0x000a	reti; Timer/Counter2 Overflow
.org 0x000c	reti; Timer/Counter1 Capture Event
.org 0x000e	reti; Timer/Counter1 Compare Match A
.org 0x0010	reti; Timer/Counter1 Compare Match B
.org 0x0012	rjmp OVF1; Timer/Counter1 Overflow
.org 0x0014	reti; Timer/Counter0 Compare Match
.org 0x0016	reti; Timer/Counter0 Overflow
.org 0x0018	reti; Serial Transfer Complete
.org 0x001a	reti; USART, Rx Complete
.org 0x001c	reti; USART Data Register Empty
.org 0x001e	reti; USART, Tx Complete
.org 0x0020 reti; ADC Conversion Complete
.org 0x0022	reti; EEPROM Ready
.org 0x0024	reti; Analog Comparator
.org 0x0026	reti; 2-wire Serial Interface
.org 0x0028	reti; Store Program Memory Ready
//koniec tablicy

start: //początek programu
cli	   //wyłączenie przerwań
ldi R16, HIGH(RAMEND)
out SPH, R16
ldi R16, LOW(RAMEND)
out SPH, R16



sbi DDRC, 0		//ustawienie linii do której podłączona jest dioda jako wyjście	
cbi DDRD, 2		//ustawienie linii PD2 jako wejście, zwarcie tej linii do masy wyzwoli przerwanie 
sbi PORTD, 2	//podciągnięcie linii PD2 do napięcia zasilania 

ldi R16, (1<<ISC01)	 //ustawienie przerwań zewnętrnych
out MCUCR, R16

ldi R16, (1<<INT0)
out GICR, R16
sei						//włączenie przerwań

ldi R16, (1<<CS11)		//ustawienie preskalera
out TCCR1B, R16

ldi R16, (1<<TOIE1)		//zezwól na przerwania od przepełnienia timera1
out TIMSK, R16

ldi R16,0 				//ustawienie trybu działania timera w tryb normalny
out TCCR1A, R16

main:					//pętla główna
nop
rjmp main


EXT_INT0:				//procedura obsługi przerwania INT0
sbi PORTC, 0			//zapal diode

clr R16					//wyzerowanie zawartości timera1
out TCNT1H, R16
out TCNT1L, R16
reti					//powrót z przerwania

OVF1:					//procedura obsługi przerwania od przepełnienia timera1,nastąpi ono 65535 x 8= ok.0.52s po wyzerowaniu licznika timera1	
cbi PORTC, 0			//zgaś diodę
reti					//powrót z przerwania

nowe instrukcje które wystąpiły w programie:

sei – "global interrupt enable" – globalne zezwolenie na przerwania

clr – "clear register" – wyczyść "nazwa rejestru ogólnego przeznaczenia"

reti – "interrupt return" - powrót z procedury obsługi przerwania

Na koniec chciałbym poinformować o dwóch ważnych zagadnieniach:

-Przerwania mają swoje priorytety.Znaczy to, że jeśli w tym samym momencie zostaną zgłoszone 2 lub więcej przerwań to jako pierwsze zostanie wykonane to przerwanie, które jest wyżej w tablicy wektorów(czyli przerwanie, które ma niższy adres )

-w przypadku gdy używamy przerwań musimy zaimplementować w programie stos, ponieważ w momencie skoku do procedury obsługi przerwania adres powrotny jest odkładany właśnie na stos i gdy program napotka instrukcję "reti" adres ten jest zdejmowany ze stosu i następuje powrót do miejsca w którym był program zanim nastąpiło przerwanie.

Zakończenie:

Jako, że kończy się wrzesień a wraz z nim niestety, również mój wolny czas, to jak narazie była to ostatnia część naszego kursu, możliwe że za jakiś czas pojawią się następne części, ale jak narazie kurs można uważać za zakończony.Mam nadzieję, że kurs był pomocny przy pierwszych krokach z językiem assembler.W razie jakiś pytań lub jeśli ktoś zobaczył by jakieś błędy - proszę pisać na w temacie lub na PW.

Pozdrawiam!!!

asm.thumb.jpg.b9a3689ebf622ff8abba37b46d6c58c8.jpg

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

Bity które nas interesują to te o nazwie ISC10 oraz ISC11

Chyba coś namieszałeś. Piszesz dobrze, że należą one do INT1, ale potem używasz INT0, więc lepiej od niego dać tabelkę. Bo tak to mamy tabelkę z ISC10 i ISC11, a piszesz o ustawianiu ISC01 w celu aktywowania przy opadającym zboczu. Początkujący mogą się w tym zagubić (jakieś 5 razy przeczytałem, a potem zajrzałem do datasheeta). Wymusza to myślenie, ale nie każdy będzie miał tyle samozaparcia, by odkryć dlaczego tak, a nie inaczej. Czytam dalej, jak coś znajdę to dopiszę.

O tym, że to dobra seria, nie muszę pisać. To oczywiste 😉

Prywata: Miałem dziś ruszyć od zera Twoje arty i programator, ale miałem małe prace remontowe w pokoju, które mam nadzieję jutro skończyć. Wtedy nie zostanie mi nic, jak tylko siedzieć i wtrawiać mózg w mikrokontrolery 😃 (i asm!)

Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

Szkoda tylko, że koledze kończy się czas, a nie opisał najważniejszego zastosowania ASM, nie tylko dla AVRów, czyli jako wstawek do języków wysokiego poziomu, bo to właśnie tam mają największe zastosowanie. Jeżeli chodzi o sterownie, to przydatny jest głównie przy obsłudze WatchDoga, oraz Timera jeżeli nie ma trybu CTC, i w.. no właśnie skomplikowanych obliczeniach, gdzie liczy się szybkość wykonania kodu, a żaden z tych tematów nie został poruszony. czyli mamy serię artykułów o ASM, ale nie poruszających jego zastosowań praktycznych, z punktu widzenia współczesnych jego zastosowań.

Link do komentarza
Share on other sites

BlackJack, mam wrażenie jak byś na siłę chciał znaleźć negatywy w każdym wątku. Ta seria artykułów to kurs programowania asm, a nie opis jego zastosowań. Sądzę, że temat jest zrealizowany bardzo dobrze 🙂 Według mnie najważniejszym zastosowaniem assemblera nie są wstawki w kodach języków wyższego poziomu, a nawet gdyby, to z tym kursem nie powinien być to problem. Nie rozumiem też, dlaczego musisz obsługiwać watchdoga i timery asmem ;o

Link do komentarza
Share on other sites

Nie wiem gdzie zapytac wiec pytam tu...bo nigdzie nie moge znalezdz odpowiedzi...

Ile cykli zajmuje obsluga pustego ISR?? W sensie nastepuje jakies przerwanie kod zostaje przerwany, i od tego momentu do ponownego kontynuowania kodu...?? Gdyby ktos wiedzial i sie podzielil to bylbym wdzieczny...bo tego raczej timerem nie zmierze...😕

Link do komentarza
Share on other sites

A zrobiłem taki program testowy:

#include <avr/io.h>
#include <avr/interrupt.h>

int main(void){

	sei();
}


ISR(INT0_vect){

}

Aby podejrzeć co się wykonuje w przerwaniu trzeba zajrzeć do pliku .lss 

ISR(INT0_vect){
  78:	1f 92       	push	r1
  7a:	0f 92       	push	r0
  7c:	00 90 5f 00 	lds	r0, 0x005F
  80:	0f 92       	push	r0
  82:	11 24       	eor	r1, r1
  84:	cf 93       	push	r28
  86:	df 93       	push	r29
  88:	cd b7       	in	r28, 0x3d	; 61
  8a:	de b7       	in	r29, 0x3e	; 62

}
  8c:	df 91       	pop	r29
  8e:	cf 91       	pop	r28
  90:	0f 90       	pop	r0
  92:	00 92 5f 00 	sts	0x005F, r0
  96:	0f 90       	pop	r0
  98:	1f 90       	pop	r1
  9a:	18 95       	reti

Znając ilość cykli na każde polecenie można wyliczyć ile czasu zajmie takie puste przerwanie. 

  • Lubię! 1
  • Pomogłeś! 1
Link do komentarza
Share on other sites

Dzieki kolego @_LM_ za blyskawiczna odpowiedz😉 rozumiem ze tego pliku przy arduino IDE nie znajde...czego powinienem uzyc zeby miec ten plik .lss? A dlugosc tych polecen w taktach gdzie moge znalezdz? Pod jakim haslem szukac? Bo szczerze...nie mam pojecia...😕 (Ja tylko prosty arduinowiec z wioski😉)

Link do komentarza
Share on other sites

Dnia 14.11.2021 o 20:06, farmaceuta napisał:

A dlugosc tych polecen w taktach gdzie moge znalezdz?

Otwierasz 37 rozdział specyfikacji i sprawdzasz ostatnią kolumnę:

image.thumb.png.caf043f6b954c56830d1333798e50618.png

  • Lubię! 1
  • Pomogłeś! 1
Link do komentarza
Share on other sites

Dnia 14.11.2021 o 18:50, _LM_ napisał:

A zrobiłem taki program testowy:



#include <avr/io.h>
#include <avr/interrupt.h>

int main(void){

	sei();
}


ISR(INT0_vect){

}

Ja wracam do AVRów po latach i nie mam środowiska teraz pod ręką, no i wiedza trochę zardzewiała, ale taki program:

#include <avr/io.h>
#include <avr/interrupt.h>

int main(void){
	sei();
}

ISR(INT0_vect, ISR_NAKED){
	asm volatile("RETI\n\t"::);
}

ile zajmie w ASM?

Spodziewałbym się, że będzie tylko jedna instrukcja reti, która zajmuje 4 cykle zegara. Według mnie to jest minimalna długość obsługi pustego przerwania.

 

Edytowano przez pmochocki
Link do komentarza
Share on other sites

Musiałbym trochę pomyszkować w swoich zbiorach, sama funkcja przerwania faktycznie może zająć te kilka cykli zegara. Ale od zgłoszenia, jak wiadomo, są wykonywane jeszcze instrukcje obsługi stosu, tak samo powrót = zdjęcie ze stosu tego co wcześniej tam wylądowało. W gcc są atrybuty które umożliwiają ręczne napisanie obsługi przerwania. Nie będę teraz zgadywał jakie są ich nazwy, może później 🙂

Link do komentarza
Share on other sites

45 minut temu, pmochocki napisał:

Według mnie to jest minimalna długość obsługi pustego przerwania.

Mi sie obily dwie liczby odnosnie pustego przerwania...raz wyczytalem ze zajmuje ono okolo 15cykli a raz ze 23cykle...od momentu wywolania do momentu powrotu do kodu...dlatego hylem ciekaw jak to jest...probowalem znalezdz ten plik lss na przeroznych srodowiskach ale jakos mi nie wychodzilo...mniejsza z tym, znalazlem inny "problem"...zaraz do-edytuje...

Link do komentarza
Share on other sites

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

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

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.