pl:avrc:art:0x03
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revision | |||
pl:avrc:art:0x03 [2012/11/17 18:45] – mkucia | pl:avrc:art:0x03 [2012/11/17 20:09] (current) – [Przykład 1] mkucia | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | Wymagania: | ||
+ | - Jak poprzednio + przyciski typu microswitch | ||
+ | ====== Wejście ====== | ||
+ | Poprzednio opisałem sposób na " | ||
+ | ===== Hardware ===== | ||
+ | {{: | ||
+ | ===== Software ===== | ||
+ | <code c>/* | ||
+ | * main.c | ||
+ | * | ||
+ | | ||
+ | | ||
+ | | ||
+ | */ | ||
+ | |||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | // | ||
+ | //Do pinów 2 i 3 podpięte są 2 przyciski podciągnięte (pull-up) do Vcc przez rezystory 10 [kOhm] | ||
+ | |||
+ | #define LED_DIR DDRB | ||
+ | #define LED_P PORTB | ||
+ | #define LEDA 0 | ||
+ | #define LEDB 1 | ||
+ | |||
+ | #define SW_DIR DDRB | ||
+ | //Rejestr zapisu (pull-up) | ||
+ | #define SW_W PORTB | ||
+ | //Rejestr odczytu | ||
+ | #define SW_R PINB | ||
+ | #define SWA 2 | ||
+ | #define SWB 3 | ||
+ | |||
+ | //Uwaga: | ||
+ | // | ||
+ | // | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | // | ||
+ | |||
+ | //2 pierwsze piny jako wyjcia | ||
+ | LED_DIR |= ( (1<< | ||
+ | //na początku diody się nie mają swiecic | ||
+ | LED_P &= ~( (1<< | ||
+ | |||
+ | //SEKCJA A | ||
+ | // | ||
+ | SW_DIR &= ~( (1<< | ||
+ | //Brak podciagniecia | ||
+ | SW_W &= ~( (1<< | ||
+ | //KONIEC A | ||
+ | |||
+ | //Pętla glowna | ||
+ | while(1) | ||
+ | { | ||
+ | //Leda zapalamy wtedy gdy przycisk jest naciniety | ||
+ | if ( (SW_R & (1<< | ||
+ | LED_P &= ~(1<< | ||
+ | else | ||
+ | LED_P |= (1<< | ||
+ | |||
+ | // | ||
+ | if (SW_R& | ||
+ | LED_P& | ||
+ | else | ||
+ | LED_P|=(1<< | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ===== Modyfikacja ===== | ||
+ | Powyższą konfigurację możemy uprościć. Rezystory podciągające przyciski do Vcc możemy zastąpić wewnętrznym podciągnięciem, | ||
+ | Zmodyfikujmy program: | ||
+ | <code c>// | ||
+ | // | ||
+ | SW_DIR& | ||
+ | //Tym razem podciągamy | ||
+ | SW_W|=(1<< | ||
+ | //KONIEC A | ||
+ | </ | ||
+ | |||
+ | ====== Dzień bez całki dniem straconym ====== | ||
+ | Niestety pomimo że poruszane do tej pory zagadnienia są bardzo proste (bo logika 0 1 jest prosta a konkretniej: | ||
+ | * main.c | ||
+ | * | ||
+ | | ||
+ | | ||
+ | | ||
+ | */ | ||
+ | |||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | #define LED_DIR DDRB | ||
+ | #define LED_P PORTB | ||
+ | #define LEDA 0 | ||
+ | #define LEDB 1 | ||
+ | |||
+ | #define SW_DIR DDRB | ||
+ | #define SW_W PORTB | ||
+ | #define SW_R PINB | ||
+ | #define SWA 2 | ||
+ | #define SWB 3 | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | //Problem jest lepiej widoczny gdy zapala się tylko 1 dioda | ||
+ | //Więc pin LEDB nie zostaje ustawiony jako wyjście | ||
+ | LED_DIR |= ( (1<< | ||
+ | LED_P &= ~( (1<< | ||
+ | SW_DIR& | ||
+ | SW_W|=(1<< | ||
+ | |||
+ | char stan_poprzedni = 1; | ||
+ | char nowy_stan; | ||
+ | //Pętla glowna | ||
+ | while(1) | ||
+ | { | ||
+ | nowy_stan = ((SW_R& | ||
+ | |||
+ | if (stan_poprzedni != nowy_stan) | ||
+ | { | ||
+ | // | ||
+ | if(!nowy_stan) | ||
+ | { | ||
+ | if((LED_P& | ||
+ | LED_P& | ||
+ | else | ||
+ | LED_P++; | ||
+ | } | ||
+ | stan_poprzedni = nowy_stan; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ===== Napięcia a stany logiczne ===== | ||
+ | Przed przejściem do setna problemu przyjrzyjmy się poniższemu wykresowi (pochodzi ze strony [[http:// | ||
+ | {{: | ||
+ | * V< | ||
+ | |||
+ | * V< | ||
+ | * V< | ||
+ | |||
+ | * V< | ||
+ | * V< | ||
+ | |||
+ | * V< | ||
+ | |||
+ | Wykres przedstawia różne rodziny układów cyfrowych i poziomy napięć przy jakich następują zmiany.\\ | ||
+ | W rozdziale 25.2 __DC Characteristics__ noty katalogowej układu ATmega8A opisane są następujące poziomy napięć (przy napięciu zasilania 5 [V]): | ||
+ | ^Symbol ^Wartość min [V] ^ Wartość max [V] ^ | ||
+ | | V< | ||
+ | | V< | ||
+ | | V< | ||
+ | | V< | ||
+ | Uwaga: | ||
+ | Wnioski: | ||
+ | * Stanu logicznemu " | ||
+ | * Co więcej istnieje zakres napięć dla którego nie możemy przewidzieć jak zostanie on zinterpretowany! | ||
+ | * Różne rodziny układów charakteryzują się różnymi poziomami napięć. | ||
+ | |||
+ | ===== Drgania styków ===== | ||
+ | Problem jaki występuje tutaj to drgania styków przełącznika. Zamiast jednego naciśnięcia uC odczytuje kilka! Problem jest stosunkowo dobrze opisany np. na tej stronie: [[http:// | ||
+ | {{: | ||
+ | Górny wykres przedstawia napięcie a wykres dolny interpretację logiczną tego napięcia. Dobrze widać że zamiast skoku jednostkowego otrzymaliśmy oscylacje. Sytuacja taka jest bardzo niepożądana (przede wszystkim frustrująca dla użytkownika który nie może uzyskać dokładnego cyfrowego nastawu!). Zwróćmy uwagę na fakt że rozbieżność w parametrach przełączników jest dosyć duża, jednak większość przełączników zmieściła się w zakresie czasu oscylacji 10 [ms] (patrz strona [[http:// | ||
+ | ==== Sprzętowy ==== | ||
+ | Pomiędzy nóżki swicha wstawiamy kondensator tworząc //układ całkujący// | ||
+ | {{: | ||
+ | Skoro z oscylogramu wynika że drgania trwają 8 [ms] załóżmy (zgodnie z zasadą: lepiej raz a dobrze) że mogą trwać 20 [ms] (jest to czas zarazem wystarczający jak i nieobciążający użytkownika koniecznością zbyt długiego przytrzymywania przycisku). Wartość pojemności dobieramy zgodnie z wzorem na stałą czasową obwodu RC (τ=RC -> C=τ/R). Podczas prób wybrałem (bez jakichkolwiek obliczeń) kondensator 100 [nF] (z zestawu), całkowicie spełnił swoją rolę. Zauważmy że stała czasowa dla takiego układu (załóżmy że R podciągający ma wartość 40 [kΩ]) to tylko 4 [ms] (tak jak napisałem wyżej microswitche mają dobre parametry i taka stała czasowa wystarcza w tym prostym przypadku), jeżeli faktycznie drgania są długie (i przede wszystkim mamy dostęp do różnych kondensatorów) musimy zastosować odpowiednio większy kondensator (dla 20 [ms] przy 40 [kΩ] rezystorze będzie to około 1 [μF]). | ||
+ | ==== Programowy ==== | ||
+ | Rozwiązanie programowe jest również proste i ma tą zaletę że nie wymaga dodawania żadnych elementów do układu (mniej elementów -> mniejsza cena, wielkość itd.). Algorytm jest bardzo prosty: //wykryłem zmianę -> czekam pewien czas -> jeżeli zmiana dalej się utrzymuje przyjmuje że nie jest ona przypadkowa// | ||
+ | { | ||
+ | nowy_stan = ((SW_R& | ||
+ | |||
+ | if (stan_poprzedni != nowy_stan) | ||
+ | { | ||
+ | _delay_ms(10); | ||
+ | if (nowy_stan != ((SW_R& | ||
+ | // | ||
+ | if(!nowy_stan) | ||
+ | { | ||
+ | if((LED_P& | ||
+ | LED_P& | ||
+ | else | ||
+ | LED_P++; | ||
+ | } | ||
+ | stan_poprzedni = nowy_stan; | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | ==== Inne ==== | ||
+ | W większości przypadków przedstawione rozwiązania wystarczają, | ||
+ | * Dodatkowego bufora z [[wp> | ||
+ | * Specjalistycznych układów scalonych np. [[http:// | ||
+ | * Przerzutników RS | ||
+ | |||
+ | ====== Przerwania ====== | ||
+ | Przerwanie jest to specjalny podprogram który zostaje uruchomiony przez pewien sygnał. W wyniku pojawienia się takiego sygnału aktualnie wykonywany program zostaje wstrzymany i układ zaczyna wykonywać odpowiedni podprogram. Przerwania są na sztywno ustawione w pamięci układu: na samym początku pamięci programu znajduje się tablica zawierająca adresy wszystkich przerwań (nawet tych które nie istnieją, wtedy prowadzą one do początku programu (reset) ale taka sytuacja występuje tylko w przypadku błędu, przerwania których nie zdefiniowaliśmy nie powinny być wywoływane). W środowisku WinAVR obsługa przerwań jest bardzo prosta (kompilator sam dba o to żeby np. przerwanie nie zakłóciło wykonywania głównego programu, a tablica wektorów przerwań jest tworzona automatycznie). Aby obsługiwać przerwania należy skorzystać z nagłówka [[http:// | ||
+ | <code c># | ||
+ | |||
+ | ISR(NAZWA_przerwania) | ||
+ | { | ||
+ | // kod | ||
+ | }</ | ||
+ | <code c>cli(); //(clear interrupts) Wyłącza przerwania | ||
+ | sei(); //(set interrupts) Włącza przerwania | ||
+ | </ | ||
+ | ===== Przerwania zewnętrzne ===== | ||
+ | Są wyzwalane przez piny INT0 INT1 (porty PD2 PD3). Oba piny ustawione jako źródło przerwań mogą reagować na 1 z 4 sposobów (Nota tabela 13-1) w zależności od ustawień w rejestrze MCUCR (MicroController Unit Control Register): | ||
+ | |||
+ | ^ MCUCR - MCU Control Register ^^^^^^^^^ | ||
+ | ^Bit | 7| 6| 5| 4| 3| 2| 1| 0| | ||
+ | ^Nazwa bitu | /SE/ | /SM2/ | /SM1/ | /SM0/ | ISC11 | ISC10 | ISC01 | ISC00 | | ||
+ | ^Znaczenie | ||
+ | ^ ::: | W tym przypadku nie istotne | ||
+ | |||
+ | ^A ^B ^Opis ^ | ||
+ | |0 |0 |Przerwanie jest generowane przez logiczne 0| | ||
+ | |0 |1 |Jakakolwiek zmiana stanu logicznego generuje przerwanie| | ||
+ | |1 |0 |Przerwanie jest generowane przez opadające zbocze| | ||
+ | |1 |1 |Przerwanie jest generowane przez rosnące zbocze| | ||
+ | <code c>Ex: MCUCR |= (1<< | ||
+ | |||
+ | Aby przerwanie działało nie tylko wystarczy odblokować przerwanie globalnie ('' | ||
+ | |||
+ | ^ GICR - General Interrupt Control ^^^^^^^^^ | ||
+ | ^Bit | 7| 6| 5| 4| 3| 2| 1| 0| | ||
+ | ^Nazwa bitu | INT1 | INT0 | -xx- | -xx- | -xx- | -xx- | /IVSEL/ | /IVCE/ | | ||
+ | |||
+ | <code c>GICR |= (1<< | ||
+ | ==== Przykład 1 ==== | ||
+ | === Hardware === | ||
+ | {{: | ||
+ | === Software === | ||
+ | <code c>/* | ||
+ | * main.c | ||
+ | * | ||
+ | | ||
+ | | ||
+ | | ||
+ | */ | ||
+ | |||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | // | ||
+ | //Do pinów INT0 i INT1 portu D są podpięte 2 przyciski | ||
+ | |||
+ | #define LED_DIR DDRB | ||
+ | #define LED_P PORTB | ||
+ | #define LEDA 0 | ||
+ | #define LEDB 1 | ||
+ | |||
+ | #define SW_DIR DDRD | ||
+ | #define SW_W PORTD | ||
+ | #define SW_R PIND | ||
+ | |||
+ | //Zmienna globalna | ||
+ | //Zauważmy że tym razem zużycie pamięci danych nie jest 0 lecz wynosi 1 bajt! | ||
+ | //slowo kluczowe volatile oznacza ulotny. Kompilator musi sie liczyc z tym ze ta zmienna | ||
+ | //bedzie sie zmieniac w trakcie pracy programu! | ||
+ | volatile char LED=2; | ||
+ | |||
+ | //0 | ||
+ | ISR (SIG_INTERRUPT0) | ||
+ | { | ||
+ | //Zapalamy diode B kiedy przerywanie jest wykonywane | ||
+ | LED_P |= (1<< | ||
+ | LED = 1; | ||
+ | } | ||
+ | |||
+ | //1 | ||
+ | ISR (SIG_INTERRUPT1) | ||
+ | { | ||
+ | //Zapalamy diode B kiedy przerywanie jest wykonywane | ||
+ | LED_P |= (1<< | ||
+ | LED = 2; | ||
+ | } | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | //Port przycisków jako wejcie | ||
+ | SW_DIR = 0; | ||
+ | // | ||
+ | SW_W = 0xFF; | ||
+ | |||
+ | //Port LED, jako wyjscie i na poczatku ustalamy ze diody sie nie swieca | ||
+ | LED_DIR|=(1<< | ||
+ | LED_P& | ||
+ | |||
+ | // | ||
+ | //UWAGA: Kiedys rejestr nazywał się GIMSK, nazwe zmieniono! | ||
+ | GICR |= (1<< | ||
+ | |||
+ | // | ||
+ | sei(); | ||
+ | |||
+ | //Pętla główna | ||
+ | while(1) | ||
+ | { | ||
+ | //Gasimy diode B | ||
+ | LED_P &= ~(1<< | ||
+ | if(LED==1) LED_P |= (1<< | ||
+ | } | ||
+ | }</ | ||
+ | === Wnioski === | ||
+ | Co robi nasz program? Jeden przycisk zapala diodę drugi ją gasi. Co dzieje się z drugą diodą? Świeci się cały czas kiedy trzymamy przycisk! Co to oznacza? W pętli głównej gasimy diodę B, więc jeżeli pali ona się cały czas oznacza to że przerwanie jest wyzwalane cały czas. Dzieje się tak ponieważ nie ustawiliśmy rejestru MCUCR, zgodnie z [[|tabelką]] przerwanie jest generowane przez stan logiczny niski, czyli cały czas podczas przyciskania przycisku. Zmodyfikujmy program w następujący sposób: | ||
+ | <code c> | ||
+ | //UWAGA: Kiedys rejestr nazywał się GIMSK, nazwe zmieniono! | ||
+ | //INT0 wyzwalany rosnącym zboczem | ||
+ | //INT1 zboczem opadającym | ||
+ | MCUCR |= (1<< | ||
+ | GICR |= (1<< | ||
+ | ==== Przykład 2 ==== | ||
+ | Hardware tak jak w poprzednim przykładzie. Tym razem zadaniem jest sprawdzenie i zrozumienie działania poniższego programu. | ||
+ | <code c> /* | ||
+ | * main.c | ||
+ | * | ||
+ | | ||
+ | | ||
+ | | ||
+ | */ | ||
+ | |||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | // | ||
+ | //Do pinów INT0 i INT1 portu D są podpięte 2 przyciski | ||
+ | |||
+ | #define LED_DIR DDRB | ||
+ | #define LED_P PORTB | ||
+ | #define LEDA 0 | ||
+ | #define LEDB 1 | ||
+ | |||
+ | #define SW_DIR DDRD | ||
+ | #define SW_W PORTD | ||
+ | #define SW_R PIND | ||
+ | |||
+ | volatile char period=0; | ||
+ | |||
+ | // | ||
+ | ISR (SIG_INTERRUPT0) | ||
+ | { | ||
+ | // | ||
+ | if(period< | ||
+ | } | ||
+ | |||
+ | //Jak już jest warto wykozystac do czegos | ||
+ | ISR (SIG_INTERRUPT1) | ||
+ | { | ||
+ | period = 0; | ||
+ | } | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | uint8_t i=0; | ||
+ | uint8_t delay=0; | ||
+ | |||
+ | //Port przycisków jako wejcie | ||
+ | SW_DIR = 0; | ||
+ | // | ||
+ | SW_W = 0xFF; | ||
+ | |||
+ | //Port LED, jako wyjscie i na poczatku ustalamy ze diody sie nie swieca | ||
+ | LED_DIR|=(1<< | ||
+ | LED_P& | ||
+ | |||
+ | // | ||
+ | //UWAGA: Kiedys rejestr nazywał się GIMSK, nazwe zmieniono! | ||
+ | // | ||
+ | MCUCR |= (1<< | ||
+ | GICR |= (1<< | ||
+ | |||
+ | // | ||
+ | sei(); | ||
+ | |||
+ | //Pętla główna | ||
+ | while(1) | ||
+ | { | ||
+ | //Warto preinkrementowac ++i a nie postinkrementowac i++ !! | ||
+ | for(i=0; | ||
+ | { | ||
+ | if(period> | ||
+ | else LED_P &= ~(1<< | ||
+ | } | ||
+ | |||
+ | //Warunek opuzniajacy, | ||
+ | // | ||
+ | if(++delay==10) | ||
+ | { | ||
+ | delay=0; | ||
+ | if(period> | ||
+ | } | ||
+ | |||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ===== Lista wszystkich przerwań układu ATMega8A ===== | ||
+ | Niższa liczba porządkowa oznacza wyższy priorytet. | ||
+ | ^ Lp. ^ Nazwa ^ Źródło/ | ||
+ | | 1 | (RESET) | Pin, Brownout, Watchdog, Power-on Reset | | ||
+ | | 2 | (INT0) SIG_INTERRUPT0 | Zewnętrzny pin | | ||
+ | | 3 | (INT1) SIG_INTERRUPT1 | Zewnętrzny pin | | ||
+ | | 4 | SIG_OUTPUT_COMPARE2 | ||
+ | | 5 | SIG_OVERFLOW2 | Timer/ | ||
+ | | 6 | SIG_INPUT_CAPTURE1| Timer/ | ||
+ | | 7 | SIG_OUTPUT_COMPARE1A| Timer/ | ||
+ | | 8 | SIG_OUTPUT_COMPARE1B| | ||
+ | | 9 | SIG_OVERFLOW1| Timer/ | ||
+ | | 10| SIG_OVERFLOW0| Timer/ | ||
+ | | 11| SIG_SPI| SPI,STC Transmisja szeregowa zakończona | | ||
+ | | 12| SIG_UART_RECV| USART Odbiór zakończony | | ||
+ | | 13| SIG_UART_DATA| USART Rejestr danych pusty | | ||
+ | | 14| SIG_UART_TRANS| USART Nadawanie zakończone | | ||
+ | | 15| SIG_ADC| ACD konwersja dobiegła końca | | ||
+ | | 16| SIG_EEPROM_READY| Pamięć EEPROM gotowa | | ||
+ | | 17| SIG_COMPARATOR| Komparator analogowy | | ||
+ | | 18| SIG_2WIRE_SERIAL| Interfejs TWI(I2C) | | ||
+ | | 19| SIG_SPM_READY| Pamięć FLASH gotowa (bootloader)| | ||
+ | ====== Zadania ====== | ||
+ | * Wiedza zdobyta w rozdziałach W02 i W03 pozwala już zbudować całą gamę urządzeń! | ||
+ | * Zbuduj zamek szyfrowy. | ||
+ | * Zbuduj grę Simon [[http:// | ||
+ | * Zastanów się jak za pomocą 1 portu przerwań obsłużyć kilka przycisków | ||
+ | ====== Uwagi ====== | ||
+ | * Eclipse z jakiegoś powodu nie zapisuje automatycznie edytowanego pliku, warto wyrobić nawyk częstego zapisywania ( Ctrl+S ) | ||
+ | * Warto sprawdzać dostępność aktualizacji __Help-> | ||
+ | |||
+ | |||
pl/avrc/art/0x03.txt · Last modified: 2012/11/17 20:09 by mkucia