Elektroda.pl
Elektroda.pl
X
IGE-XAOIGE-XAO
Proszę, dodaj wyjątek dla www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia

p.kaczmarek2 08 Sty 2020 10:57 1149 0
  • Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    Witajcie moi drodzy
    Oto druga część mojego praktycznego tutoriala dla ośmiobitowego mikrokontrolera PIC18F2550 od Microchipa i programatora SDCC. W tym temacie omówię podstawowe operacje cyfrowego IO na prostych przykładach.

    Spis części (osobnych tematów) tutoriala
    Tutorial podzielony jest na osobne tematy i tutaj znajdują się do nich linki.
    Część 1 - Konfiguracja środowiska pracy
    https://www.elektroda.pl/rtvforum/viewtopic.php?p=18304424#18304424
    Część 2 - Blink LED, piny IO, cyfrowe wejścia i wyjścia
    https://www.elektroda.pl/rtvforum/viewtopic.php?p=18389188#18389188
    Część 3 - Ustawienia oscylatora. Oscylator wewnętrzny, zewnętrzny, rezonator kwarcowy, PLL
    [Tu będzie link]
    Część 4 - Timery, przerwania
    [Tu będzie link]
    Spis treści będzie uzupełniany wraz z pisaniem przeze mnie kolejnych części.


    Ten temat to jest część 2, czyli Blink LED, piny IO, cyfrowe wejścia i wyjścia. Zaczynamy.

    Wstęp
    W tej części tutoriala pokażę najprostsze operacje na portach cyfrowego IO mikrokontrolera PIC18F2550 przy użyciu kompilatora SDCC. Dokładnie przeanalizujemy kod migania diodą, tzw. 'blink LED' czyli 'hello world' mikrokontrolerów, posterujemy większą ilością LEDów na różne sposoby, a potem wprowadzimy przycisk, do którego użyjemy raz zewnętrznych, a raz wewnętrznych rezystorów pull-up.
    Wszystko będzie oparte o osobne, praktyczne przykłady z kodem dostępnym do pobrania i prezentacją działania.
    Całość zrealizujemy na płytce stykowej, używając wewnętrznego oscylatora 1MHz.

    Jak programować PIC18F2550?
    W ramach odpowiedzi na te pytanie odsyłam do pierwszej części mojego tutoriala:
    https://www.elektroda.pl/rtvforum/viewtopic.php?t=3635522&highlight=
    Tam jest wszystko opisane (akapit: Zaczynamy działanie z PIC18F2550).

    Uwaga - język C
    W tej części zakładam, że czytelnik ma jakąś podstawową znajomość języka C (albo chociaż jakiegokolwiek podobnego języka programowania). Bez tego może być ciężko a tłumaczenie tu wszystkiego od 0 sprawiłoby, że cały temat naprawdę byłby wielki i też pewnie dla niektórych nudny. Znajomość podstawowej struktury kodu i operatorów z tego języka naprawdę się tu przyda.
    Przyda się też znajomość systemu binarnego i szesnastkowego zapisu liczb.

    Teoria - porty PIC18F2550
    Porty PIC18F2550 są ośmiobitowe (tak jak i sam mikrokontroler), czyli każdy port (A, B, itp.) ma co najwyżej 8 pinów. Każdy bit określa stan na danym pinie (niski lub wysoki). Poszczególne piny każdego portu mogą być indywidualnie ustawione w tryb wejścia (input) lub wyjścia (output).
    Tryb pinu ustawia z pomocą rejestru TRIS* (dla portu A jest to TRISA, dla portu B TRISB, itp). Zapalony bit (1) oznacza tryb wejścia, zgaszony bit (0) oznacza tryb wyjścia.
    Do odczytu wartości z portu służy rejestr PORT* (dla portu A to jest PORTA, dla portu B PORTB, itp).
    Do zapisu wartości służy rejestr LAT* (dla portu A to jest LATA, dla portu B LATB, itp).
    Podsumowując, dla każdego portu mamy trzy rejestry:
    Rodzaj rejestru Przykłady Zastosowanie
    TRIS* TRISA, TRISB, TRISC Określa tryb pinów portu (czy dany pin portu jest wejściem czy wyjściem)
    PORT* PORTA, PORTB, PORTC Pozwala odczytać bieżącą, rzeczywistą zawartość portu
    LAT* LATA, LATB, LATC Do tego portu zapisujemy wartość, na jaką chcemy ustawić port

    Czemu jest osobno PORT* i LAT*? Różnica jest w odczycie. Odczytywanie wartości LAT* zwraca to, co do niej wpisaliśmy. Natomiast odczytywanie PORT* zwraca rzeczywistą, bieżącą wartość pinu. W niektórych sytuacjach to mogą być różne wartości, gdyż zmiana stanu na pinie jednak trochę trwa.
    Porty często są współdzielone z innymi funkcjami i nie działają, gdy te funkcjonalności są włączone (przykładowo porty wejścia przetwornika ADC).
    Funkcje specjalne - takie jak komunikacja UART, USB, SPI, I2C są z reguły tylko na wybranych pinach które też mogą mieć rolę zwykłego IO, lecz nimi się tutaj nie zajmujemy.
    Należy pamiętać o tym, że też są inne rejestry które wpływają na działanie pinów PICa - tutaj chociażby ADCON1, CMCON itp. które są odpowiedzialne za konfigurację trybu ADC i komparatora.
    Przyjrzyjmy się temu jakie porty oferuje PIC18F2550:
    Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    Mamy tutaj port A, port B, port C oraz jeden pin portu E (jeśli wyłączymy funkcję MCLR, w przeciwnym razie ten pin ma rolę RESET).
    Nie wszystkie bity portów są wyprowadzone, np. RC3 w ogóle nie ma (na starszych PICach był, ale przejął tu rolę VUSB).
    Niektóre piny mają ograniczoną funkcjonalność. Mogą być np. tylko w trybie wejścia. Są to na przykład RC4 i RC5:
    Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    Proszę zwrócić uwagę, na powyższym zrzucie ekranu z noty katalogowej widać jasno, że np. RC2 to jest "Digital I/O", ale już RC4 to tylko "Digital input".
    Wynika to stąd, że RC4 i RC5 mogą mieć też funkcję D+/D- (linie sygnałowe od USB):
    Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    Piny współdzielące tryb IO (input/output cyfrowy) z wejściami analogowymi (ADC - Analog to Digital Converter) mogą też wymagać do użycia poprawnego ich skonfigurowania w rejestrze ADCON1.
    Jeśli np. chcemy użyć RB0 jako wejścia, to powinniśmy mu wyłączyć tryb analogowy, gdyż ma on też funkcję AN12.
    Zawartość ADCON1 przedstawia obrazek:
    Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    Najbardziej interesującą nas tu konfigurację, czyli wybór pomiędzy trybem IO pina a trybem analogowym przedstawia tabelka:
    Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    Jak widać, bity PCFG3:PCFG0 określają tryb wejść AN0-AN12. Nie jest możliwa dowolna konfiguracja trybów tych pinów, można tylko ustawić te co są wspierane przez PICa.
    Wbrew pozorom to nie jest nic trudnego - nawet w samej nocie katalogowej są przykłady jak wyłączyć tryb analogowy dla danego portu:
    Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    Co prawda jest to assembler, ale komentarze też wyjaśniają co się dzieje.

    Pokrewny PIC18F4550 ma nieco więcej portów:
    Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    Migracja z PIC18F2550 do PIC18F4550 jest bardzo łatwa i jeśli braknie nam wolnych portów to zapraszam do przeprowadzki.

    Porty PIC18F2550 są w stanie znieść dość duże prądy, ale mimo to należy pamiętać o ich ograniczeniach. Nie można z portu pociągnąć zbyt dużego prądu. Jeden pin jest w stanie znieść do 25 mA, natomiast wszystkie w sumie wszystkie porty nie powinny nieść więcej niż 200 mA. Wszystkie parametry znajdują się w nocie katalogowej:
    Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    Projektując nasz układ musimy o tym pamiętać. Do sterowania czymkolwiek chociaż troszkę prądożernym musimy stosować odpowiednie układy. Nawet większą ilość diod LED (czy tam wyświetlacz) powinniśmy zasilać przez tranzystor, a pinem IO tylko go przełączać.
    Dla zainteresowanych; wewnętrzna struktura portu IO PICa wygląda tak:
    Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    Piny IO mają diody ochronne do VDD i GND.
    O wiele więcej i bardziej szczegółowo o pinach PIC18F2550 można poczytać w jego nocie katalogowej - i to do niej zainteresowanych odsyłam. Sam tutaj skupię się już przede wszystkim na praktycznych przykładach użycia.

    Pin MCLR/VPP czyli RESET
    Jednym ze specjalnych pinów PICa który można tu przy okazji omówić jest pin MCLR/VPP, czyli tzw. RESET.
    Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    Większość czytelników zapewne kojarzy pin RESET z Arduino lub z AVRów - ten RESET ma identyczną funkcję.
    Nazwa MCLR pochodzi od Master Clean.
    Stan wysoki na tym pinie umożliwia działanie mikrokontrolera, natomiast stan niski go resetuje. Stąd właśnie ten pin zazwyczaj podłączony jest przez opornik 10k do zasilania (to wymusza domyślnie stan wysoki), i do masy przez przycisk (to pozwala nam poprzez wciśnięcie przycisku ustawić tam stan niski i zresetować PICa).
    Pin też również bierze udział w programowaniu PICa (razem z PGD/PGC).
    Schemat pokazuje typowe połączenie pinu MCLR w większości konstrukcji:
    Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    Możliwe jest jednak wyłączenie funkcji MCLR tego pinu i użycie go jako jedno z wejść portu E poprzez zmianę bitu konfiguracji MCLRE (MCLR Enabled):
    Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    Niestety nawet wtedy ten pin nie może mieć funkcji wyjścia, a programator i tak dalej do niego się podłącza, więc my nie będziemy używać go w roli PORTE.

    Analizujemy kod 'blink LED'
    Na początek przeanalizujemy kod 'blink LED' (miganie diodą LED) z poprzedniej części.
    Uruchomiliśmy go na PIC18F2550 na płytce stykowej w takim układzie:
    Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    Schemat połączeń ze zdjęcia powyżej:
    Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    (VDD i GND oczywiście zarówno podłączone jest do zasilania z USB jak i do ICSP z PICKIT3, co widać też na zdjeciu wcześniej).

    Dla przypomnienia, tu jest całość kodu:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Całość w formie załącznika (też ze skompilowanym wsadem):
    p18f2550_b...sc1MHz.zip Download (10.14 kB)Punkty: 1 dla użytkownika
    Kod ten ustawia pin RC0 w trybie wyjścia a potem przełącza jego stan co pół sekundy:
    Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    Używa wewnętrznego oscylatora PICa.
    Teraz dokładnie go przeanalizujemy.
    Kod: c
    Zaloguj się, aby zobaczyć kod

    W powyższym fragmencie załączamy nagłówki funkcji z których chcemy korzystać, tak jak normalnie w języku C.
    Nagłówek pic18fregs.h pochodzi z SDCC i mówi kompilatorowi o tym, jakie mamy dostępne rejestry dla naszego PICa (np. TRISB, LATB, ADCON1, itp).
    Nagłówek delay.h pochodzi z SDCC i zawiera funkcje oczekiwania daną ilość cykli (delay1ktcy, delay10ktcy, itp)
    Tutaj też możemy umieszczać własne nagłówki.
    Kod: c
    Zaloguj się, aby zobaczyć kod

    W powyżej zacytowanym fragmencie znajduje się ustawienie bitów konfiguracyjnych PICa z pomocą dyrektyw preprocesora języka C. Ich dokładny opis można znaleźć w datasheet PIC18F2550 oraz w dokumentacji SDCC.
    W tym przypadku ustawiamy:
    - XINST na OFF - wyłączamy extended instructions set. SDCC w bieżącej wersji nie wspiera rozszerzonego zestawu instrukcji.
    - FOSC na INTOSCIO_EC - ustawiamy oscylator na tryb wewnętrznego oscylatora, dzięki czemu PIC nie wymaga oscylatora kwarcowego by ruszyć (w następnej części tutorialu dokładnie omówię to ustawienie)
    - WDT na OFF - wyłączamy watchdog timer. Watchdog timer pozwala automatycznie zresetować mikroprocesor gdy się ten zawiesi. Na ten moment nie jest nam to potrzebne, ale możliwe, że poznamy go szczegółowo w dalszych częściach.
    - LVP na OFF - wyłączamy Low Voltage Programming. Pozwala on wgrywać wsad na PIC bez podania VPP (jakieś 12V) na pin RESET. Dla nas zbędne - zapewne korzystamy z PICKIT2 lub PICKIT3. Co do innych programatorów to się nie wypowiem, gdyż po prostu nie znam wszystkich dostępnych.
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Powyżej jest główna funkcja wsadu, słynne "main" która sama się wykonuje od nowa po wyjściu PICa ze stanu RESET.
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Powyższy fragment ustawia najmłodszy bit (bit 0) z portu C (pin RC0) na tryb wyjścia z pomocą struktury TRISCbits. Równie dobrze można to zrobić z pomocą rejestru TRISC (o tym nieco dalej).
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Powyżej pokazana jest główna pętla (main loop) programu, nieskończona. W pętli negowana jest wartość portu RC0 oraz użyta jest funkcja oczekiwania delay1ktcy(125) z nagłówka delay.h (biblioteka libc18f.lib). Dzięki temu uzyskujemy miganie diody LED co pół sekundy.

    Jak działa delay z delay.h?
    Funkcja delay po prostu odczekuje daną ilość cyklów instrukcji PICa.
    Tutaj trzeba zaznaczyć, że zegar instrukcji PICa działa z 1/4 częstotliwości jego oscylatora.
    Czyli to, ile czasu przeczeka np. wywołanie delay1ktcy(125) zależy od tego, jak mamy skonfigurowany oscylator PICa.
    W przypadku użycia domyślnego, wewnętrznego oscylatora 1MHz (tak jak w kodach w tym temacie) mamy:
    Fosc (częstotliwość oscylatora) = 1MHz
    Finst (częstotliwość instrukcji) = 0.25MHz
    Tinst (czas instrukcji) = 0.004ms
    Zatem ile instrukcji musimy odczekać by minęło 500ms?
    500ms / 0.004ms = 125 000 instrukcji.
    Stąd w naszym kodzie jest:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Czemu '125' a nie '125 000'? Funkcja to jest delay1ktcy, w nazwie jest '1k', czyli jeden tysiąc instrukcji razy 125.
    W delay.h są funkcje:
    - delay10tcy ( unsigned char n ) - odczekuje n razy 10 cykli instrukcji
    - delay100tcy ( unsigned char n ) - odczekuje n razy 100 cykli instrukcji
    - delay1ktcy ( unsigned char n ) - odczekuje n razy tysiąc cykli instrukcji
    - delay10ktcy ( unsigned char n ) - odczekuje n razy 10 tysięcy cykli instrukcji
    - delay100ktcy ( unsigned char n ) - odczekuje n razy 100 tysięcy cykli instrukcji
    - delay1mtcy ( unsigned char n ) - odczekuje n razy milion cykli instrukcji
    UWAGA: Parametrem tej funkcji jest typ unsigned char, ośmiobitowy, więc nie możemy sobie napisać 'delay10tcy(12500)' i liczyć, że zadziała. Znajomość typów z języka C, ich rozmiarów i limitacji jest tutaj bardzo wskazana!

    Same funkcje delay* od strony kompilatora zaimplementowane są już w assemblerze i używają instrukcji nop ("no operation - brak działania") do odczekania danej ilości cykli. Przykładowa implementacja z delay1ktcy.S:
    Kod: asm
    Zaloguj się, aby zobaczyć kod

    Na szczęście to już jest w naszych bibliotekach kompilatora i nie musimy tego sami pisać.

    Miganie diodą LED - różne sposoby
    Do rejestrów związanych z portami (TRISC, LATC, itp) można dostać się też jak do normalnej zmiennej, bez użycia struktur "bits" (TRISCbits oraz LATCbits; analogicznie dla pozostałych rejestrów).
    Te struktury są tylko ułatwieniem dla użytkownika i nie trzeba z nich korzystać.
    Jeśli nie chcemy z nich korzystać, to możemy operować na LATC i TRISC (oraz analogicznie na rejestrach innych portów) tak jak na zmiennych typu integer (liczba całkowita).
    Z punktu widzenia kompilatora takie TRISCbits to jest to samo co TRISC, co zresztą można podejrzeć w jego nagłówkach, np w include/pic16/pic18f2550.h:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Powyższy kod znajduje się w nagłówku dla PIC18F2550 którego powinniśmy mieć już zainstalowanego razem z kompilatorem. Widzimy tu, że TRISCbits jest strukturą (struct) pozwalającą na wygodny dostęp do poszczególnych bitów, a samo TRISC jest typu __sfr, czyli Special Function Register. Oczywiście nie musimy tego kodu sami pisać. Pokazuję go tylko w ramach ciekawostki. Zapis __at(0x0F94) mówi kompilatorowi w którym miejscu pamięci wybrany PIC ma właśnie rejestr odpowiedzialny za TRISC. Wartość ta bierze się z jego noty katalogowej, z sekcji SPECIAL FUNCTION REGISTERS, a dokładniej z tej tabelki:
    Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    Poniżej kod poprzedniego przykładu przerobiony tak by nie korzystać ze struktur "bits":
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Kod ten korzysta z operatora bitowej negacji oraz alternatywy do zgaszenia najmłodszego bitu z rejestru TRISC (w zasadzie trochę niepotrzebnie) a potem za pomocą operatora XOR zmienia jego stan co 500ms.
    Naprawdę bardzo przydaje się tu dobra znajomość operatorów z języka C.

    Miganie diodą LED - za pomocą timera/przerwania
    W jednym z następnych tematów z tej serii pokażę, że diodą LED można też migać z pomocą timera/przerwania. PIC18F2550 posiada kilka timerów. Jednakże na tym etapie tutoriala ich nie wprowadzam, więc ten przykład poznamy dopiero za jakiś czas.

    Wiele diod LED
    Pora na nieco bardziej skomplikowane przykłady - pomigamy teraz większą ilością LEDów.
    Weźmiemy ich 8, gdyż nasz PIC18F2550 jest ośmiobitowy i co za tym idzie jego porty są ośmiobitowe.
    Do każdej diody LED potrzebny jest rezystor ograniczający ich prąd, ja wybrałem wartość 470 omów.
    Przygotowujemy wszystko:
    Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    Podłączamy diody do płytki, ja wybrałem, że będą miały wspólną masę.
    Dla przypomnienia - piny diody LED:
    Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    I całość złożona na płytce stykowej:
    Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    Do wszystkiego przydadzą się też przewody dla płytek stykowych:
    Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    Podłączamy całość do naszego PICa, do jego portu B. Następne kody też będą oparte o ten układ:
    Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    Na obrazku zaznaczyłem które dokładnie piny użyłem (cały port B - osiem jego pinów):
    Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    UWAGA: Dwa z użytych pinów (RB7 i RB6) pełnią też funkcję podłączenia do ICSP (programatora - PGD i PGC). W tym przypadku nie jest to problemem. Nie odłączam programatora na czas działania kodu. Po prostu w trakcie programowania trochę migają sobie podłączone LEDy, ale nie zakłóca to wgrywania wsadu. Wszystko działa poprawnie.

    I na początek - prosty kod migania ośmioma LEDami na PORTB:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Rezultat działania kodu na filmie:

    Kod raczej nie wymaga dodatkowego komentarza. 0xff to jest 255 w notacji szesnastkowej, czyli w binarnej 0b11111111. Tak też można napisać w kodzie.
    Załącznik (miganie diodami na porcie B) do pobrania:
    p18f2550_b...sc1MHz.zip Download (9.2 kB)Punkty: 1 dla użytkownika

    Wiele diod LED - bitowa negacja
    Kod migania ośmioma diodami z poprzedniego przykładu można chcieć trochę uprościć, ale trzeba uważać, by nie popełnić błędu.
    Można próbować skrócić ten jego fragment:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    I zamiast ręcznego ustawienia stanów bitów (na wysoki a potem na niski) dać jedną negację, tak:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Ale to nie zadziała. Nie możemy tutaj użyć operatora !, gdyż on dla wartości PORTB równej 0 zwróci nam 1 (binarnie: 0b00000001) a nie 0b11111111.
    Wtedy kod skutkować będzie miganiem tylko diody LED na RB0:

    Należy tutaj użyć negacji bitowej, czyli operatora ~:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    I od razu jest tak jak powinno:

    Negacja bitowa dla wartości 0b00000000 zwróci nam 0b11111111, czyli to, czego oczekujemy. Po prostu zmienia stan każdego bitu na przeciwny.
    Dobra znajomość operacji na bitach jest tutaj naprawdę ważna.

    Odliczanie binarne
    Z rejestrów portów można korzystać tak jak z każdej innej zmiennej. Można do niej dodawać wartość, itp. W ten sposób łatwo można zrobić odliczanie binarne, czyli zegar który odlicza kolejne liczby i wyświetla je binarnie. Wyświetlanie binarne oczywiście zrealizowane jest z pomocą ośmiu diod LED, gdzie każda dioda odpowiada stanowi jednego z bitów (zapalony lub zgaszony).
    Zmodyfikowany kod:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Załącznik (kod, skompilowany hex, .bat do kompilacji na Windowsa):
    p18f2550_b...sc1MHz.zip Download (9.1 kB)Punkty: 1 dla użytkownika
    Rezultat działania kodu przedstawia filmik:

    Filmik pokazuje działanie od początku przez 30 sekund. Wcześniej PIC nie chodził gdyż w PICKIT3 na komputerze trzymałem go w trybie RESET:
    Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    W ramach małego przypomnienia zamieszczam tu tabelkę pokazującą liczby od 0 do 15 zapisane dziesiętnie i binarnie (aczkolwiek uważam, że powinno się to znać na pamięć):
    DziesiętnieBinarnie
    00000
    10001
    20010
    30011
    40100
    50101
    60110
    70111
    81000
    91001
    101010
    111011
    121100
    131101
    141110
    151111


    Przesunięcie bitowe
    Na rejestrach można wykonywać też oczywiście inne, nieco mniej znane operacje. Na przykład przesunięcie bitowe. W języku C pozwalają na nie operatory >> i <<.
    Przesunięcie bitowe (jak sama nazwa wskazuje) przesuwa bity.
    Przykładowo, mając taką zawartość w zmiennej:
    B7B6B5B4B3B2B1B0
    00000001

    Po jednokrotnym przesunięciu w lewo otrzymamy:
    B7B6B5B4B3B2B1B0
    00000010

    Wypróbujmy to i rozważmy taki kod:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Kod ustawia wartość LATB na 1 (zapalony tylko najmłodszy bit), byśmy mieli co przesuwać, po czym co pół sekundy przesuwa zawartość tego rejestru.
    Działanie obrazuje filmik (te zakłócenie na początku jest od PICKITa, przez jego sygnał z RESET):

    Filmik pokazuje działanie od początku przez 30 sekund. Po przesunięciu 8 razy niestety już nie pali się żadna dioda LED, gdyż przesuwanie bitowe nie zapętla się.
    Oczywiście przesuwać można dowolną kombinację bitów, nie tylko jeden bit.
    Tak samo można od razu przesunąć o dwa bity, trzy, itp..
    Załącznik (kod, skompilowany hex, .bat do kompilacji na Windowsa):
    p18f2550_b...sc1MHz.zip Download (9.34 kB)Punkty: 1 dla użytkownika

    Przesunięcie bitowe - zapętlenie
    Powyższy kod z przesunięciem można bardzo łatwo "zapętlić" - wystarczy dodać warunek, który przywraca stan początkowy portu B po wykonaniu całego przesunięcia.
    Czyli na przykład coś takiego:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Ustawia stan początkowy gdy wszystko zostanie już 'wyprzesuwane' na zewnątrz.
    Cały kod po zmianie:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Rezultat działania na filmiku:


    Załącznik (kod, skompilowany hex, .bat do kompilacji na Windowsa):
    p18f2550_b...sc1MHz.zip Download (9.66 kB)Punkty: 1 dla użytkownika

    Przycisk czyli rezystory pull-up i pull-down
    Do tej pory omówiliśmy tylko tryb wyjścia (output) pinów.
    Ale jest jeszcze tryb wejścia - input. Pozwala on odczytać stan logiczny (zero lub jeden) na danym pinie.
    Potem można użyć np. instrukcji warunkowej if by wykonać coś, jeśli np. przycisk jest wciśnięty.
    Przygotujmy zatem najprostszy układ z przyciskiem.
    Potrzebne nam będzie:
    - przycisk - oczywiście, tzw. tactile switch
    - rezystor rzędu 10k, aby podciągnąć pin mikrokontrolera do masy bądź zasilania.
    Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    Przypominam pinout przycisku:
    Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    Po co rezystor? Rezystor jest potrzebny ponieważ pin mikrokontrolera zostawiony 'w powietrzu' ma stan nieoznaczony i łapie tylko zakłócenia.
    Podłączenie pinu przez rezystor do masy lub zasilania pozwala nadać mu domyślny stan (zero lub jeden), który potem zmienimy przyciskiem.
    Taki rezystor nazywa się pull-up lub pull-down.
    Przycisk z pull-up wygląda tak:
    Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    To jest przycisk z rezystorem pull-up. Pin IO mikrokontrolera podciągnięty jest do zasilania i ma domyślnie stan logiczne 1. Dopiero w momencie wciśnięcia przycisku jest zwierany do masy i przechodzi w stan 0.

    Przycisk z pull-down wygląda tak:
    Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    To jest przycisk z rezystorem pull-down. Pin IO mikrokontrolera podciągnięty jest do masy i ma domyślnie stan logiczne 0. Dopiero w momencie wciśnięcia przycisku jest zwierany do zasilania i przechodzi w stan 1.
    Przez ten rezystor 10k cały czas płynie bardzo mały prąd.

    Prosty przykład z przyciskiem
    Wrócimy teraz do przykładu 'prosty blink', który tylko mruga diodą na pinie RC0 i zmodyfikujemy go tak, by przełączał stan diody jak wciśniemy przycisk.
    Stary kod migania diodą LED (stanowi dla nas punkt startowy):
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Wybierzemy sobie, że przycisk damy na pinie RC2:
    Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    I będzie to przycisk z rezystorem pull-up:
    Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    Przygotowujemy podłączenie na płytce - dodałem rezystor, przycisk i dwie zworki by wszystko połączyć:
    Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    I pora napisać kod.
    Musimy:
    - zainicjować pin RC2 jako wejście
    - w pętli odczytywać stan RC2 i jeśli jest wciśnięty (niski) to zmieniać stan RC0
    Gotowy kod wygląda tak:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Załącznik (kod, skompilowany hex, .bat do kompilacji na Windowsa):
    p18f2550_b...sc1MHz.zip Download (10.21 kB)Punkty: 1 dla użytkownika
    Rezultat na filmie:

    Działa, ale tutaj warto poruszyć bardzo ważną kwestię...

    Problem drgania styków i debouncing styków
    Sprytny czytelnik zauważy, że zastosowane w poprzednim akapicie przeze mnie tu rozwiązanie nie jest idealne - blokuje ono całkiem wykonywanie pętli z funkcji main na całe 500 ms po wciśnięciu przycisku! Na początku nauki można sobie na to pozwolić, zwłaszcza że dzięki temu możemy uciec od tzw. problemu drgań styków i robienia debouncingu, ale w finalnym produkcie takie coś jest niedopuszczalne.
    Problem drgań styków wynika stąd, że w rzeczywistym świecie elementy nie są idealne i przy wciskaniu przycisku nie uzyskujemy pojedynczego, wzorowego przejścia ze stanu niski na wysoki (lub na odwrót), tylko takie przejście w zakłóconej formie.
    Problem ten można rozwiązywać zasadniczo na dwa sposoby:
    - software'owo - czyli w kodzie, poprzez różne sztuczki z ponownym sprawdzeniem przycisku i oczekiwaniem
    - hardware'owo, czyli sprzętowo - np. poprzez zastosowanie kondensatora, filtru
    To jest duży temat, nadający się aż na całkiem osobny post, więc na razie odeślę tylko do dokumentu PDF autorstwa mojego korespondenta z USA Jack Ganssle:
    debounc...pdf Download (333.26 kB)
    Oczywiście umieszczam tu PDFa za jego pozwoleniem.
    W dalszych częściach tutorialu jeszcze do tego tematu wrócę.

    Przycisk i 8 diod LED - program losujący
    W ramach ćwiczenia jeszcze połączymy dwa wcześniej przedstawione przykłady, czyli:
    - przykład z przyciskiem na RC2
    - przykład z 8 diodami LED na porcie B
    Dodatkowo tutaj skorzystamy z faktu, że przyciski nie są idealne (istnieje drganie styków) a człowiek ma kłopot z tym, by trzymać przycisk zawsze idealnie tak samo długo.
    Zrobimy program-kostka. Będzie losować liczbę od 0 do 255, którą wyświetlimy binarnie.
    Dodamy jakąś własną zmienną, ustalimy jej na początku jakąś wartość, którą potem będziemy zmieniać w trakcie trzymania przycisku. Wszystko najprościej jak się da - dla użytkowników i tak rezultat będzie się zdawać losowy.
    Oto kod:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Rezultat działania:

    Wylosowaną liczbę można odczytać binarnie z LEDów.
    Oczywiście kod można by znacznie ulepszyć, zastosować jakiś poprawny algorytm generowania liczb pseudolosowych itp, ale nie to było celem tego przykładu. Miało być proste i jest proste.
    Załącznik (kod, skompilowany hex, .bat do kompilacji na Windowsa):
    p18f2550_b...sc1MHz.zip Download (9.71 kB)Punkty: 1 dla użytkownika

    Wewnętrzne pull up dla portu B
    PIC18F2550 oferuje wewnętrzne, programowalne rezystory weak pull-up na porcie B. Pozwalają nam one uniknąć stosowania rezystorów w naszym układzie i podłączyć przycisk po prostu tak:
    Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    Oczywiście nie oznacza to, że tego rezystora pull-up w tym układzie nie ma - on jest, ale wewnątrz PICa.
    Więc z naszej płytki stykowej możemy usunąć rezystor i zostawić tylko przycisk, który podłączymy na, powiedzmy, RB0:
    Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    Rezystory pull-up na porcie B włącza się wszystkie na raz, nie da się ustawić poszczególnie każdego z nich. Ale nie są one aktywne jeśli dany pin z portu B jest w trybie wyjścia a nie wejścia.
    Odpowiada za nie bit RPBU z rejestru INTCON2:
    Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    Jako że port RB0 ma też funkcję AN12, to również trzeba zadbać (tak jak pisałem na początku tematu) by ją mu wyłączyć.
    Bazując na tym co napisałem można opracować kod:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Kod oczywiście działa poprawnie i pozwala na korzystanie z przycisku bez zewnętrznego rezystora, oto filmik z działania:

    Może ktoś z czytelników podejmie się próby ulepszenia tego kodu i zrobi go w wersji która nie blokuje głównej pętli na pół sekundy?
    Załącznik (kod, skompilowany hex, .bat do kompilacji na Windowsa):
    p18f2550_b...sc1MHz.zip Download (10.64 kB)Punkty: 1 dla użytkownika

    Przycisk i dioda LED na jednym pinie
    Tutaj chciałbym jeszcze pokazać, że tryb każdego pinu (rejestr TRIS*) można modyfikować dowolnie już w trakcie działania programu. Nic nie narzuca, że trzeba to robić poza główną pętlą.
    Spróbujemy wykorzystać tą możliwość by zrealizować obsługę diody LED i przycisku na jednym pinie IO mikrokontrolera.
    Po prostu raz pin będzie w trybie wejścia, a raz w trybie wyjścia.
    Użyjemy pinu RC2.
    Do pinu podłączymy i przycisk, i diodę.
    Proponuję coś takiego:
    Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    Ten układ zrobiony u mnie na płytce stykowej wygląda tak:
    Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    Do tego obsługi potrzebny jest odpowiedni kod. Kod powinien na bieżąco zmieniać tryb pinu, raz na wejście, i raz na wyjście i odpowiednio nim sterować. Przykładowo:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Wartości delay można tu lepiej dobrać, a w ramach ćwiczenia polecam usprawnić ten kod i jakoś lepiej zrealizować oczekiwanie/debouncing.
    I na koniec filmik pokazujący, że to działa:

    Oczywiście, tak zrealizowany układ zapala siłą rzeczy diodę w trakcie działania, ale dla nas to nie jest problem.
    Załącznik (kod, skompilowany hex, .bat do kompilacji na Windowsa):
    p18f2550_b...sc1MHz.zip Download (11.55 kB)Punkty: 1 dla użytkownika

    Dwie diody LED na jednym pinie
    Na koniec jeszcze chciałbym podkreślić, że diodę LED można podłączyć zarówno między pinem wyjścia a masą, jak również między pinem wyjścia a zasilaniem. Piny PIC18F2550 mają te same wartości sink i source current.
    Można też podłączyć dwie diody na raz, w ten sposób:
    Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    W praktyce wygląda to tak:
    Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
    Wtedy możemy użyć niezmienione kodu z 'blink LED' i będzie on migać diodami na przemian:

    Kod: c
    Zaloguj się, aby zobaczyć kod

    Filmik z działania:

    Można też zapalić obie diody na raz, w ten sposób:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Niestety wtedy będą świecić o wiele słabiej, czego oczywiście można się spodziewać po samym ich sposobie podłączenia.

    Podsumowanie
    W tej części omówiliśmy najprostsze operacje cyfrowego IO PIC18F2550 na wielu prostych przykładach. W dalszych częściach tutoriala zapewne zajmiemy się już bardziej szczegółowo powiązanymi z tym kwestiami, np. obsługą klawiatury matrycowej, debouncingiem przycisków, zrobimy charlieplexing dla diod LED, itp. W nadchodzącej niedługo części zapewne poznamy też możliwości konfiguracji oscylatora PICa (do tej pory korzystamy z wewnętrznego 1MHz) i timerów oraz przerwań.
    PS: Ten temat to nie jest finalna wersja. Będzie z czasem uzupełniany i ulepszany.
    Pierwszy temat z serii został zmodyfikowany i zaktualizowany
    , m. in. o schemat połączeń, o definicję ICSP (troszke jej brakowało), itp. Oba tematy będą na bieżąco poprawiane i redagowane, jak ktoś ma jakieś uwagi, zapraszam na PW.

    Fajne! Ranking DIY
    Potrafisz napisać podobny artykuł? Wyślij do mnie a otrzymasz kartę SD 64GB.
  • IGE-XAOIGE-XAO