W tym temacie zbuduję od 0 prosty ściemniacz LED sterowany dwoma przyciskami. Projekt będzie oparty o PIC12F683, którego tu zaprogramuję bez użycia zewnętrznych bibliotek - GPIO i PWM zostanie skonfigurowane wedle informacji z noty katalogowej. Do tego dobiorę kilka zewnętrznych elementów z elektrośmieci tak, aby mój kontroler LED był w pełni funkcjonalny i dobrze współpracował z paskiem LED na 12V. Na koniec wszystko zlutuję na wierconej płytce prototypowej.
Ten temat jest nieco powiązany z moim tutorialem SDCC dla PIC18F2550 i będę tu częściowo bazować na opisanych tam krokach:
Część 1 - Konfiguracja środowiska pracy
https://www.elektroda.pl/rtvforum/topic3635522.html#18304424
Część 2 - Blink LED, piny IO, cyfrowe wejścia i wyjścia
https://www.elektroda.pl/rtvforum/topic3647884.html#18389188
Część 3 - Ustawienia oscylatora. Oscylator wewnętrzny, zewnętrzny, rezonator kwarcowy, PLL
https://www.elektroda.pl/rtvforum/topic3657704.html
Część 4 - Timery, przerwania
https://www.elektroda.pl/rtvforum/topic3676645.html#18580858
Część 5 - Obsługa wyświetlacza siedmiosegmentowego
https://www.elektroda.pl/rtvforum/topic3676650.html#18580877
Część 6 - Sterownik wyświetlacza LED MM5450
https://www.elektroda.pl/rtvforum/topic3845301.html
Spis treści będzie uzupełniany wraz z pisaniem przeze mnie kolejnych części.
Co obejmie projekt?
Przedstawiony tu tutorial/miniprojekt obejmie następujące kroki:
- uruchomienie PIC12F z SDCC poprzez operacje bezpośrednio na rejestrach PICa
- skonfigurowanie w ten sposób PWM służącego do kontroli jasności LEDów
- dobranie i podłączenie tranzystora by można było sterować całym paskiem, a nie tylko jednym LED
- przykładowe zlutowanie całości układu na wierconej płytce prototypowej
Przy okazji zobaczymy jak to całe magiczne PWM działa i wygląda na oscyloskopie.
Wybrany MCU i podłączenie programatora
Tym razem wybór padł na PIC12F683 - mały, 8-bitowy MCU w obudowie DIP08 pracujący na napięciu 5V, oferujący 6 GPIO, 2048 słów Flash, 128 bajtów SRAM oraz 256 bajtów EEPROM.
Programuje się go podobnie jak PIC18F2550. Pod ręką miałem akurat PICKIT2, chociaż mógłby to być też klon:
Prosty klon PICKIT2 (programator PIC na USB) z łatwo dostępnych elementów
Podłączyć trzeba MCLR, zasilanie, PGD oraz PGC:
Zrealizowane podłączenie:
Na początku uruchomiłem program PICKIT2 ale programator nie był widziany - okazała się być winna myszka, która musiałem przepiąć do innego portu USB. Wtedy dopiero ruszyło:
Minimalne wymagane podłączenie
Programator już widzi PICa, ale czy o czymś nie zapomnieliśmy? Na pewno o odsprzęganiu zasilania - przyda się jakiś kondensator ceramiczny 100nF między masą a zasilaniem. Bez niego MCU może pracować niestabilnie i nieprzewidywalnie. Ale to nie wszystko:
Potrzebny jest jeszcze rezystor 1k (lub większy) na pinie MCLR (inaczej RESET). Bez tego PIC nie ruszy, choć programator będzie go widział.
Pierwszy program
Teraz pora uruchomić jakiś program. Wszystko tak jak w tutorialu o 18F2550:
Tutorial PIC18F2550 + SDCC - Część 2 - Blink LED, piny IO, wejścia i wyjścia
Bierzemy blink pod PIC18F2550 i przerabiamy. Trzeba załączyć odpowiedni nagłówek stąd:
https://github.com/pfalcon-mirrors/sdcc/blob/master/device/non-free/include/pic14
Można to zrobić bezpośrednio:
Kod: C / C++
lub automatycznie:
Kod: C / C++
Nagłówek pic16regs.h po prostu sprawdza preprocesorem jaki typ układu zdefiniowaliśmy i załącza jego rejestry za nas, można to podejrzeć tutaj:
https://github.com/pfalcon-mirrors/sdcc/blob/master/device/include/pic14/pic16regs.h
Załóżmy, że chcemy po prostu zamigać diodą LED.
Patrzymy do noty katalogowej - tu nie mamy osobno portów A, B, itd. tak jak w PIC18F2550, więc nie ma rejestru TRISA, TRISB, itd.
Jest po prostu TRISIO oraz GPIO:
Zapalony bit w TRISIO sprawia, że dany pin jest wejściem, zgaszony natomiast oznacza wyjście. A więc chcemy tam wpisać zera. A potem będziemy manipulować rejestrem GPIO by kolejno ustawiać stany niskie i wysokie na pinach.
Jeszcze trzeba skonfigurować wewnętrzny oscylator - nie potrzebujemy zewnętrznego. Za to znów odpowiada OSCCON:
Na próbę wpisałem tam 0x71, czyli binarnie 0b01110001 - patrząc powyżej to jest IRFC 8MHz oraz włączone Internal oscillator is used for system clock.
Kod: C / C++
Jeszcze brakuje nam oczekiwania - nie chciałem liczyć ile to w ms, więc nazwałem funkcję delay_smth. Ta funkcja po prostu wykonuje w pętli komendy nop, czyli zasadniczo "marnuje" czas procesora. Najprościej jak się da:
Kod: C / C++
Podłączyłem oscyloskop na GPIO by sprawdzić, czy program działa:
181Hz - czyli coś mruga, ale szybko. Nie będę jednak tego tu poprawiać.
Uruchamiamy PWM
Nie poprawiłem "machania pinem" gdyż nie jest to nam potrzebne - mamy tu sprzętowe PWM. PWM to skrót od Pulse Width Modulation, czyli modulacja szerokości impulsu. W prostych słowach, PWM pozwala nam ustawić częstotliwość oraz wypełnienie sygnału prostokątnego na określonym wyjściu PICa. Do kontroli jasności LEDów często przyjmuje się częstotliwość 1kHz, natomiast wypełnienie tego sygnału zmienia się w zależności od tego jaki poziom jasności chcemy uzyskać. Czyli zasadniczo szybko włączamy i wyłączamy LEDy te 1000 razy na sekundę by uzyskać pożądaną jasność.
W tym PICu PWM może mieć wyjście tylko na jeden wybrany pin:
Zapoznajemy się z odpowiednią sekcją noty katalogowej:
Dużo tych rejestrów jest... PR2, T2CON, CCPR1L, CCP1CON... i nie zapominajmy też o ustawieniu trybu wyjścia dla pinu PWM w TRISIO.
Wbrew pozorom jednak to nie jest takie trudne.
PR2 to zasadniczo jest ilość cykli zegara (po użyciu preskalera/postskalera) określająca częstotliwość PWM.
CCPR1L to ilość cykli w którym sygnał jest w stanie wysokim (po nim następuje przełączenie na stan niski). Więc jak mamy PR2 = 40 i chcemy wypełnienie 50% to CCPR1L ustawiamy na 20, a jeśli wypełnienie chcemy ustawić na 75%, to CCPR1L powinno wynosić 30.
Został T2CON:
W T2CON ustawiamy poskaler i preskaler oraz włączamy timer. Ten timer (timer 2) będzie użyty przez PWM. Jeszcze pytanie to skalują te "skalery" - ich wejściem jest główny zegar Fosc/4:
Czyli to my musimy dobrać preskaler/postskaler tak aby potem móc wybrać wartość PR2 odpowiadającą 1kHz.
Skoro Fosc to 4MHz, to Fosc/4 to 1MHz. Częstotliwość 1MHz odpowiada okresowi 0.001ms. Jeśli zastosujemy preskaler 1:4:
Kod: C / C++
To wtedy Timer2 zwiększa się o 1 co 0.004ms.
Do ilu w takim razie musimy Timer2 liczyć by mieć 1kHz?
1kHz to okres 1ms.
1ms dzielone na 0.004ms daje nam 250.
0xFA to 250. Do PR2 trzeba wpisać 0xFA.
Zostaje jeszcze CCPR1L - tam będziemy wpisywać od 0 do 0xFA aby zmieniać wypełnienie sygnału.
To tyle z najważniejszej konfiguracji, ale mamy jeszcze CCP1CON. Tam już na szczęście nie będziemy nic liczyć:
Wpisujemy 0b00001100, by włączyć PWM w trybie active-high (jeśli chcemy odwrócić sygnał, to można wpisać 0b1110.
Cały kod:
Kod: C / C++
Piękny 1kHz:
Przyciski i kontrola wypełnienia
Teraz trzeba jeszcze móc zmieniać wypełnienie tego PWM. Tak jak już pisałem, sprowadza się to do zmiany wartości CCPR1L w zakresie od 0 do PR2+1. Dla wygody zdecydowałem się zrealizować to w oparciu o dwa przyciski.
Aby uruchomić przycisk, należy:
- upewnić się, że role ADC/komparatory są wyłączone
- ustawić odpowiedni bit rejestru TRIS na 1
Informacja z noty katalogowej:
Jak zapomnimy, to wszystkie operacje digital read będą zwracać zero!
Potem można odczytywać stan przycisku poprzez np. bit GP1 dla GPIO 1. Tutaj nie ma osobnych portów!
Dodatkowo należy zadbać, by ustalić jakiś stan gdy przycisk nie jest zwarty - przykładowo podłączając przycisk między GPIO a VDD należy też dać rezystor pull-down, tak by domyślnie przycisk miał stan 0. W momencie wciśnięcia będzie 1.
Kod: C / C++
Nieco dalej, w pętli:
Kod: C / C++
Po prostu zwiększam wartość duty o dobraną wartość oraz upewniam się czy nie przekracza ona PR2+1, potem wynik zapisuję. Dodatkowo blokuję wykonanie głównej pętli na jakiś czas, bo inaczej te duty by było zwiększane tak szybko jak tylko MCU wykonuje instrukcje. Ogólnie lepiej jest zrobić to bez blokowania całego programu, ale to i tak jest tylko prosty przykład...
Cały kod:
Kod: C / C++
Dobór tranzystora
Niestety nie możemy podłączyć paska LED bezpośrednio do GPIO mikrokontrolera. Nie pozwala na to jego zdecydowanie za mała wydajność prądowa - diodę LED to byśmy jedną z GPIO zaświecili (z rezystorem ograniczającym prąd), ale na cały pasek prądu nie starczy. Szczegółowe informacje na ten temat są w nocie katalogowej, przykładowo dla PIC18F2550:
25mA to za mało, my chcemy z 1A-2A, więc przyda się jakiś tranzystor. Najlepiej MOSFET, ponieważ taki jest sterowany napięciowo.
W tym celu czasem złomuję sprzęty:
W układzie przetwornicy zasilającej świetlówki tego monitora znalazłem dwie ciekawe pary MOSFETów:
Steruje nimi OZ9938DN.
Wszystko w obudowach DIP. Troszkę topnika i spoiwa na piny, a układy aż same wypadają z płytki:
To AOP609 - dwa MOSFETy w jednej obudowie, N i P. Para komplementarna:
Ja tylko wykorzystam jednego z nich, ten z kanałem typu N. Jego parametry wyglądają obiecująco, RDS(on) (rezystancja w stanie przewodzenia) dla Vgs (napięcie bramka-źródło) 4.5V ma być mniejsze niż 75mΩ, ale tu jeszcze warto spojrzeć na charakterystyki:
Wygląda na to, że ten MOSFET będzie pasować - 5V z PICa będzie w stanie go wysterować. Nie każdy MOSFET by się do tego nadawał.
Swoją drogą takich fajnych MOSFETów dużo jest na PCB z TV:
Tu odzyskałem aż 8 sztuk D606!
AOD606, znów para komplementarna, ale z nieco lepszym odprowadzeniem ciepła:
Test z oscyloskopem
Złożyłem wszystko na próbę - źródło MOSFET podłączyłem do masy, bramkę do wyjścia sygnału przez rezystor 1k, między dren a 12V+ podłączyłem pasek LED. Uruchomiłem to w oparciu o zasilacz laboratoryjny, do wglądu przebieg pokazuje oscyloskop:
Bardzo ładnie tu widać, jak jasność paska zmienia się wraz z wypełnieniem sygnału.
Przenosimy na płytkę
Do szybkiego złożenia prototypu służy mi gotowa jednostronna płytka wiercona. Wygodnie się na niej lutuje, ale trzeba nieco obniżyć temperaturę grota i używać topnika, bo inaczej pady po przegrzaniu szybko odpadają.
Swego czasu znalazłem wyrzucony przez jakąś firmę worek końcówek DC jack, jedna z nich będzie jak znalazł:
Składamy:
PIC wymaga 5V, a pasek zasilany jest z 12V, więc przyda się 7805, oczywiście z elektrośmieci:
Postęp:
Test, widać również dodane kondensatory (niezbędne do stabilnej pracy, dosłownie bez tego 100nF widziałem że mi się MCU resetował):
Prawie gotowe, dodany rezystor na MCLR, rezystor bramkowy:
Dodane przyciski oraz ledwo widoczne rezystory SMD pull-down od przycisków:
Finalny test
Użyty MOSFET jest dość słaby w porównaniu do innych kontrolerów paska, ale ja tylko potrzebowałem obsłużyć 2-3 metry LEDów biorące w sumie do 2A. Całodniowy test pokazał, że przy pełnym obciążeniu MOSFET jest tylko lekko ciepły. Wygląda na to, że dobrze sprawdzi się w swojej roli.
Co można by zrobić lepiej?
Kolejność przypadkowa:
- 7805 mógłby być zastąpiony czymś mniejszym, ale to było pod ręką
- zegar PICa można by znacznie zwolnić, to by też oszczędziło nieco energii
- można dać mocniejszy MOSFET
- luty, itd. - wiadomo
- można by lepiej wykorzystać tego PICa bo w zasadzie on dużo nie robi teraz ale coś chciałem w ramach tutorialu praktycznego wymyśleć...
Wnętrza fabrycznych kontrolerów LED
Jeśli chcecie z kolei zobaczyć jak wyglądają ściemniacze dostępne na rynku, to opisywałem je już w kilku tematach. Warto też sprawdzić, jakie MOSFETy są w środku - jest to w tematach uwzględnione:
Nietypowy kontroler paska LED Miboxer FUT037W+ z TuyaMCU - protokół komunikacji, OpenBeken
Kontroler paska LED RGB (tylko na pilota) za 5 zł - BKL1013
Pasek LED WiFi/IR - WX300P - tryb muzyczny - ukryte przyciski [W800-C400]
Warto zobaczyć też ściemniacz bez MCU:
Prosty analogowy ściemniacz LED z potencjometrem - bez WiFi - Bowi 002066 12V 8A manualny
Podsumowanie
Mam nadzieję, że ten jednodniowy projekt się komuś spodobał. Być może w kolejnej części nieco go rozwinę, tylko co jeszcze można dodać do takiego prostego kontrolera paska LED? Macie jakieś pomysły? A może w ogóle by zrobić coś innego w kolejnej części, też na tym małym PICu w obudowie DIP08, tylko co?
PS: Przyda się też jakaś obudowa - ale to może osobno jako wydruk 3D.
Fajne? Ranking DIY Pomogłem? Kup mi kawę.
