logo elektroda
logo elektroda
X
logo elektroda

PIC12F683 i SDCC - cały zegar i termometr na dwóch pinach (bez zewnętrznych bibliotek)

p.kaczmarek2  3 2649 Fajne? (+17)
📢 Słuchaj (AI):
Płytka z mikrokontrolerem PIC i wyświetlaczem LED.
Dzisiaj zrealizuję projekt na malutkim, ośmionóżkowym mikrokontrolerze PIC w obudowie DIP8 oferującym skromne 3.5kB pamięci Flash. Co więcej, do komunikacjami z peryferiami użyję tylko dwóch pinów - całość opierać się będzie o I2C. W oparciu o nie uruchomię kontroler wyświetlacza/klawiatury oraz termometr. Na koniec podejmę pierwszą próbę wzbogacenia całości o odliczanie czasu tak, aby zbudowany układ mógł pełnić rolę zegara. Dostępna też będzie opcja ustawienia bieżącego czasu.

Ten miniprojekt 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.

Powiązany temat o PIC12F683:
PIC12F683 i SDCC - tutorial - tworzymy prosty ściemniacz (czytamy noty katalogowe)

Całość napiszę w SDCC pod PIC12F683 bez zewnętrznych bibliotek.

Ze względów praktycznych nie będę tu powtarzać szczegółowo wiedzy z poprzednich części.


Plan projektu
Zacznę od uruchomienia PIC12F683 w trybie pracy z wewnętrznym oscylatorem. Skupię się tu przede wszystkim na skonfigurowaniu jego pinów od oscylatora w roli GPIO, bo resztę już pokazywałem w poprzedniej części. Potem wykorzystam te dwa piny do komunikacji z kontrolerem wyświetlacza/klawiatury. Najpierw uruchomię pokazywanie poszczególnym cyfr, a potem odczyt klawiszy. Na tym etapie będę mieć już gotowe mniej więcej funkcje programowe do I2C, więc spróbuję je wykorzystać by dodatkowo do tej samej magistrali podłączyć prosty termometr TC74 i z tego też odczytać rezultaty. Na koniec zajmę się sprawami bardziej "zegarkowymi", czyli zaimplementuję proste odmierzanie czasu w oparciu o sprzętowy timer z PICa i ostatecznie zepnę wszystko w całość tak, by można było wyświetlać i czas i temperaturę, oraz by można było nastawiać bieżący czas przyciskami.

Projekt zostanie kontynuowany w drugiej części.

Użyte części
Podsumowując, do projektu użyję:
- PIC12F683 w obudowie DIP08 - główny mikrokontroler
Trzy mikrokontrolery PIC12F683 na drewnianym blacie.
Tabela specyfikacji urządzenia PIC12F683
- wyświetlacz z tunera z sterownikiem typu TM1650/TM1637/HD2015/itd, również podobne moduły można zakupić online, ich protokoły komunikacji są często podobne, lecz nie identyczne
Zbliżenie na płytkę drukowaną z kondensatorami i złączem.
- TC74 (termometr z prostym interfejsem opartym o I2C)
Czujnik temperatury TC74A5 ukazany z bliska
- PICKIT2 do programowania PICa (bądź dowolny inny programator który da radę)
Interfejs programatora PICKIT 2 z aktywnymi diodami LED.
- opcjonalnie, zasilacz 5V do zasilenia gotowego projektu, u mnie ze starego tunera
Elektroniczna płytka z kondensatorami i innymi komponentami
- opcjonalnie, analizator stanów logicznych by podejrzeć co się dzieje na liniach od I2C...
Analizator logiczny podłączony do komputera na drewnianej powierzchni.

Krok 1: Blink
Na początku zawsze uruchamiam program "blink" czyli najprostsze miganie diodą LED. Pozwala mi to upewnić się, że program rzeczywiście rusza. Staram się uniknąć sytuacji, gdzie np. program jest wgrywany ale nie jest wykonywany, np. na skutek braku podłączenia pinu MCLR (RESET) zgodnie z notą katalogową. Również upewniam się wtedy, czy wgrywam wsad kompatybilny z MCU które mam na stole.
A więc zacząłem zgodnie z poprzednim tematem z serii:
PIC12F683 i SDCC - tutorial - tworzymy prosty ściemniacz (czytamy noty katalogowe)
Tutaj jednak wprowadziłem drobną poprawkę, gdyż zauważyłem, że piny "zegarowe" nie migają wraz z pozostałymi:
Schemat połączeń pinu mikrokontrolera PIC12F683 w obudowie DIP8
Trzeba wyszukać co jest nie tak. Wpisujemy "CLKIN" w wyszukiwarkę w nocie katalogowej i przeglądamy rezultaty:
Tabelka konfiguracji oscylatora dla mikrokontrolera PIC.
Rejestr Config (słowo konfiguracyjne) określa rolę tych pinów. Chcemy ustawić oba z nich w trybie GPIO, więc dopisujemy ustawienie rejestru CONFIG na wewnętrzny oscylator bez CLKOUT:
Kod: C / C++
Zaloguj się, aby zobaczyć kod

Cały kod:
Kod: C / C++
Zaloguj się, aby zobaczyć kod

Działa:




Krok 2: Zdobycie wyświetlacza
Użyty wyświetlacz (a właściwie wyświetlacze, bo projekt najpierw realizowałem na jednej płytce, a po przerwie uruchomiłem na drugiej, podobnej) pochodzą ze starego tunera:
Wnętrze dekodera DVB z widoczną płytą główną, zasilaczem i kondensatorami. Wnętrze starego tunera z widocznymi portami i elektroniką.
W środku zazwyczaj jest główny CPU, pamięć Flash, RAM, osobno głowica, no i interesujący mnie układ obsługujący klawiaturę/wyświetlacz:
Zbliżenie na płytkę drukowaną z układami scalonymi. Zbliżenie na płytkę drukowaną z procesorem EtronTech. Zbliżenie na płytkę elektroniczną z różnymi komponentami. Płytka drukowana z różnymi komponentami elektronicznymi na drewnianym tle.
Jednym z plusów użycia takiego sprzętu jest darmowy zasilacz o napięciu wyjścia z reguły 5V, chociaż i 3.3V na płytce (za przetwornicą step down) też się znajdzie:
Osoba mierząca napięcie płyty głównej multimetrem. Multimetr i płytka elektroniczna z mierzonym napięciem
W tym przypadku zasilacz jest oparty o PN8136:
Zbliżenie na układ scalony PN8136 i inne elementy elektroniczne
Drugim plusem jest często spotykany w takich produktach wyświetlacz ze sterowaniem opartym o protokół zbliżony do I2C (ale z reguły bez adresów układu?):
Schemat wyprowadzeń układu scalonego FD650B w obudowie DIP16. Schemat połączeń wyświetlacza LED z układem sterującym
Komunikacja z układem odbywa się tu tylko po dwóch liniach - SDA i SCL, dane i zegar. Komunikacja jest dwukierunkowa, SDA jest zdolne zarówno wysyłać dane, jak i je odczytywać. Zatem pora podłączyć SDA i SCL na dwie wybrane nóżki PICa i zacząć przygodę...
Jeśli natomiast chcecie taki wyświetlacz kupić, to w sieci da się znaleźć ciekawe moduły:
Zestaw modułów wyświetlacza i klawiszy cyfrowych.

Krok 3: Komunikacja I2C
FD650 już ktoś na forum uruchamiał:
Sterownik 4x7seg LED - FD650B + AVR (Bascom)
FD650 nie jest zgodny ze standardem I2C, gdyż nie ma osobno swojego adresu, tylko bezpośrednio "wystawia" rejestry na magistralę. Tych wystawionych rejestrów jest kilka, więc skan I2C będzie go widzieć jako kilka urządzeń. Dodatkowo oznacza to, że nie podłączymy na jedną magistralę I2C dwóch FD650B, tak jak to można np. z MCP23017 gdy się im ustawi różne adresy...
Swoją drogą, przypomina on bardzo mocno popularne TM1650/HD2015/CH455H które już omawiałem. Odsyłam do poniższych tematów po szczegóły:
Uruchamiamy sterownik wyświetlacza/przycisków HD2015 po inżynierii wstecznej, porównanie z TM1637itd
Czytamy notę katalogową i piszemy sterownik wyświetlacza 7-segmentowego LED CH455H w Arduino
Wyświetlacze 7-segmentowe na TM1637 - 4 i 6 cyfr - Arduino, protokół
Adresy rejestrów są zgodne. Bazując na powyższych tematach, mamy:
- 0x48 - rejestr konfiguracyjny
- 0x68, 0x6A, 0x6C, 0x6E - rejestry znaków
Zatem przepisałem do SDCC mój kod ze wspomnianych powyżej tematów:
Kod: C / C++
Zaloguj się, aby zobaczyć kod

Działa, migają segmenty pierwszej cyfry.
Płyta elektroniczna z podłączonymi przewodami i wyświetlaczem LED
W celu sprawdzenia poprawności kodu całość podejrzałem na analizatorze logicznym z tematu Analizator stanów logicznych Salae 24MHz za 40 zł - analiza nieznanego protokołu wyświetlacza LED:
Zrzut ekranu z analizatora logicznego PulseView pokazujący przebiegi sygnałów I2C.


Krok 4: Odliczanie i znaki
W tym przypadku też pasowała mapa znaków którą opracowałem w jednym z poprzednich tematów. Jest ona też zgodna z dostępnymi komercyjnie modułami TM1650/TM1638, itd:
Kod: C / C++
Zaloguj się, aby zobaczyć kod

Do segmentów danej cyfry (a nawet szerzej - znaku, bo mam aż do F znaki) dostajemy się po prostu indeksując tablicę daną wartością.
Na początek odpaliłem najprostsze "odliczanie":
Kod: C / C++
Zaloguj się, aby zobaczyć kod

Rezultat:



Na bazie tego można zrobić funkcję rozdzielającą liczbę na cyfry za pomocą operacji dzielenie całkowitego i modulo; poniżej wersja trzycyfrowa:
Kod: C / C++
Zaloguj się, aby zobaczyć kod

A tu wersja "zegarkowa", czyli dwie osobne liczby od 0 do 99:
Kod: C / C++
Zaloguj się, aby zobaczyć kod

Z powyższą funkcją mogę już wyświetlać czas w formie HH:MM bądź MM:SS.

Krok 5: Skan I2C
Następnie postanowiłem spróbować podłączyć drugie urządzenia na moją magistralę I2C. Nie byłem pewny jak zachowa się tutaj kontroler wyświetlacza, który zasadniczo nie ma swojego pojedynczego adresu wywołania, tylko od razu odpowiada na adresy rejestrów. Czy za każdym razem będzie odpowiadać na ack?
Skaner I2C miałem z tego tematu:
Budujemy zegar na PIC18F2550, krok po kroku - część 1, BMP280, TC74, 74HCT164, I2C
Przeportowałem go do siebie i zmodyfikowałem tak, by wyświetlał odpowiadające adresy:
Kod: C / C++
Zaloguj się, aby zobaczyć kod




Teraz przyszła pora na dwa osobne skany:
- pierwszy bez TC74 na magistrali
- drugi z TC74, tak aby poznać jego adres
Po prostu wykonałem skany i porównałem wyniki.
Otrzymałem adresy 72, 74, 76, 78, 104 i 106 bez TC74, a sam TC74 wniósł dodatkowo adres 154. Czyli jest widziany, pora na odczyt.

Uwaga organizacyjna
Ze względu na czynniki zewnętrzne musiałem projekt nieco rozłożyć w czasie i ostatecznie drugi jego etap realizowałem na nieco innej płytce od tunera. Rejestry cyfr się nie zmieniły, ale rejestr np. przycisków tak. Reszta kodu jest bez zmian. Użytą płytkę prezentowałem już w jednym z tematów:
Czytamy notę katalogową i piszemy sterownik wyświetlacza 7-segmentowego LED CH455H w Arduino


Krok 6: Odczyt z TC74
Kod odczytu TC74 mam z tematu o zegarze:
Budujemy zegar na PIC18F2550, krok po kroku - część 1, BMP280, TC74, 74HCT164, I2C
TC74 jest bardzo prosty w użyciu, temperatura jest jako liczba 8-bitowa, jeden bajt, odczytujemy ją z rejestru o adresie 0x00:
Tabela rejestrów i konwersji temperatury dla TC74
Najpierw wysyłamy do termometru adres rejestru, tutaj 0x00, poprzedzony adresem termometru z bitem W (najmłodszy bit zgaszony), a potem wysyłany odczyt bajtu, też poprzedzony adresem termometru, ale z bitem R (najmłodszy bit zapalony):
Kod: C / C++
Zaloguj się, aby zobaczyć kod

Dla uproszczenia nie wykonałem w kodzie na razie poprawnego I2C "restart", tylko osobny "stop" i "start".
Rezultat:
Płytka elektroniczna z wyświetlaczem pokazującym 01:78.
Działa, u mnie w pracowni jest około 17°C.

Weryfikacja z analizatorem stanów logicznych Salae:
Diagram sygnału I2C przedstawiający odczyt danych.

Krok 7: Przyciski
Zgodnie z wcześniejszą analizą:
Czytamy notę katalogową i piszemy sterownik wyświetlacza 7-segmentowego LED CH455H w Arduino
Stan przycisków jest w rejestrze o adresie 0x4F, tyle, że nie jest to po prostu mapa bitowa. Zresztą sprawdźmy:
Kod: C / C++
Zaloguj się, aby zobaczyć kod

Na filmiku widać, że z pozoru jest coś nie tak::



Odczytane ośmiobitowe słowo okazuje się być zakodowaną informacją o jednym przycisku, a dokładniej jego kod i osobno stan wciśnięcia - wciśnięty lub zwolniony.
Kod: C / C++
Zaloguj się, aby zobaczyć kod

Program wgrałem i zbadałem reakcje na przyciski. Na mojej płytce są trzy, ich kody to kolejno 7, 15, 23. W oparciu o to dopisałem obsługę przycisków badającą co jest wciśnięte i dla przykładu zmieniające jedną zmienną:
Kod: C / C++
Zaloguj się, aby zobaczyć kod

Analogicznie zostanie zrobiona wkrótce edycja bieżącego czasu.

Zwyczajowo, podgląd transakcji w Pulse View:

Wykres transmisji danych I2C pomiędzy mikrokontrolerem a urządzeniem peryferyjnym.

Krok 8: Odliczanie czasu
Odliczanie czasu jest nieco trudniejsze, niż mogłoby się to wydawać. Najlepiej by było użyć do tego zewnętrznego układu RTCC (Real-Time Clock/Calendar), takiego jak np. MCP7940N:
Schemat blokowy I²C RTCC z komponentami oscylatora i połączeniami.
Plusem takiego rozwiązania jest też podtrzymanie czasu nawet po utracie zasilania. Służy do tego często mała bateryjka bądź ogniwo. Nowsze PICe mają też wbudowany RTCC.
Tutaj jednak w celu uproszczenia sprawy spróbuję odmierzać czas timerem:
Tutorial PIC18F2550 + SDCC - Część 4 - Timery, przerwania
Weźmy na przykład Timer 0:
Fragment dokumentacji technicznej dotyczący modułu Timer0 w mikroprocesorze.
Schemat blokowy preskalera TIMER0/WDT
Wewnętrzny oscylator ustawiłem wcześniej na 4MHz, więc Fosc/4 to 1MHz.
Teraz można wybrać preskaler w OPTION_REG:
Tabela konfiguracji preskalera dla Timer0 i WDT
Wybrałem 1:256, więc inkrementacja timera nastąpi nie z częstotliwością 1MHz, lecz z 3.90625 kHz, czyli okres to 0.256ms.
Teraz jeszcze trzeba ustawić od jakiej wartości timer będzie odliczać:
Fragment dokumentacji przedstawiający rejestr INTCON i jego funkcje.
Przerwanie wywoływane jest gdy wartość TMR0 się przepełni (przekroczy 255, bo to 8-bitowy rejestr), więc ustawienie TMR0 na 0 sprawi, że przerwanie wywoła się co 256 cykl timera. Skoro wiemy, że okres timera to 0.256ms, to okres 256 cykli wynosić będzie 65.53600 ms...
No i tu jest mały problem - troszkę ciężko się takie liczy dodaje, o wiele łatwiej by było móc ustawić jakiś lepiej sumujący się okres, ale w tym celu trzeba by zmienić zegar np. na klasyczne 32.768kHz.
32768Hz to nie jest wartość przypadkowa, to jest wartość 2 do potęgi 15. Jakbyśmy mieli Fosc = 32.768kHz, to Fosc/4 wynosiłaby 8.192kHz. Potem dodając preskaler 1:256 byśmy otrzymali inkrementację timera co 32Hz, więc ustawienie TMR0 na 32 by dało nam przerwanie co sekundę...
Jednakże na początek zdecydowałem się na te mniej precyzyjne, gorsze rozwiązanie i ostatecznie wyszło coś takiego - inicjalizacja timera i przerwania:
Kod: C / C++
Zaloguj się, aby zobaczyć kod

Przerwanie timera:
Kod: C / C++
Zaloguj się, aby zobaczyć kod

Tu widać m. in. dlaczego lepszy byłby rezonator zegarkowy - po prostu gubi się tu część czasu. Można by zliczać w częściach dziesiętnych ms, ale i tak daleko do ideału...
Kod: C / C++
Zaloguj się, aby zobaczyć kod

W takiej sytuacji liczymy 65.5ms i na samych obliczeniach tracimy 0.036ms co sekundę. Dzień ma 86400 sekund, więc dziennie tracimy 3.1104 sekund... 90 sekund w miesiąc.
Można by próbować wprowadzić korektę programowo...
Kod: C / C++
Zaloguj się, aby zobaczyć kod

Jednakże testy pokazały, że nie jest to idealne rozwiązanie, pewnie dlatego, że nie wziąłem pod uwagę niedokładności wewnętrznego rezonatora oraz czasu wykonywania instrukcji... Eksperymentalnie zmniejszyłem wartość 536 do 200, za każdym razem testując osobno zegar przez kilka godzin ze stoperem. Teraz w skali kilku godzin nie jestem w stanie zaobserwować spóźniania/śpieszenia, na początek starczy, bo i tak planuję zewnętrzne RTCC..

Można by też przenieść tę kalibrację do pamięci EEPROM i po prostu przez kilka dni się pobawić by to odpowiednio ustawić. Zawartość pamięci EEPROM jest pamiętana po utracie zasilania, a użyty PIC ma jej 256 bajtów.

Krok 8: Ustawienie czasu
Ustawienie czasu zrealizowałem w oparciu o pokazany wcześniej kod przycisków. Wprowadziłem zmienną określającą stan zegara (normalna praca, zmiana godziny, zmiana minuty):
Kod: C / C++
Zaloguj się, aby zobaczyć kod

a potem osobno podłączyłem zdarzenia przycisków:
Kod: C / C++
Zaloguj się, aby zobaczyć kod

Docelowo można tu też zapętlać wartości, godzina będzie z zakresu [0,23] a minuty z zakresu [0,59].
Dodatkowo w głównej pętli wykonuję miganie bieżąco edytowaną wartością (godzinami lub minutami):
Kod: C / C++
Zaloguj się, aby zobaczyć kod

Dzięki temu wiemy czy i jaki tryb edycji jest włączony.


Krok 9: Dwukropek
Eksperymentalnie określiłem, że dwukropek jest podpięty do rejestru 0x6C i ma kod bitowy 32. Odpowiednio zmieniłem kod wyświetlania by zawsze ten bit zapalał:
Kod: C / C++
Zaloguj się, aby zobaczyć kod



Krok 10: Wyświetlanie na przemian czasu i temperatury
Na koniec wprowadziłem prosty mechanizm wyświetlania na przemian czasu i temperatury. Opiera się ona na liczniku "klatek"/"odświeżeń", w każdej pętli sprawdzam indeks "klatki" i na jego bazie wyświetlam wybraną wartość. Dodatkowo sprawdzam, czy nie jest aktywny tryb edycji, jeśli tak, to zawsze wyświetlam czas.
Kod: C / C++
Zaloguj się, aby zobaczyć kod


Efekt na ten moment
Zegar wyświetla naprzemiennie bieżący czas i temperaturę. Czas można nastawić, choć przyciski nie rozróżniają momentu wciśnięcia od ciągłego trzymania klawisza:




Podsumowanie
Chyba najciekawszym wnioskiem z całej zabawy jest to, że nawet w przypadku tych chińskich wynalazków takich jak TM1650, TM1637 itp., gdzie brakuje normalnego, zgodnego z I2C adresowania, i tak można na jedną magistralę dać kilka urządzeń. Nie mam tu oczywiście na myśli dwóch TM1650, bo ze względu na te same adresy rejestrów byłoby to niemożliwe, ale np. TM1650 + TC74 da się uruchomić.
Zegar w bieżącej formie działa, pracuje poprawnie na zasilaczu z użytej płytki, a i 5V dodatkowo też można z niej pobrać.
Oczywiście dużo można by jeszcze rozwinąć i ulepszyć, dlatego w kolejnej części planuję:
- próbę podłączenia i oprogramowania jakiegoś RTCC tak aby czas był liczony poza PICem i dodatkowo by był podtrzymywany przez bateryjkę 3V gdy odłączę sam zegar od sieci (bądź zresetuję PICa, każde wgranie nowego wsadu go resetuje)
- próbę przemyślenia i wprowadzenia jakiejś formy alarmu, może z buzzerem, też z zapisem ustawień w pamięci EEPROM, użyty PIC ma jej 256 bajtów
- próbę podłączenia jakiegoś expandera portów na I2C, o ile adresy pozwolą...
- reorganizację kodu, zwłaszcza tego sterowania i odświeżania, by było sensowniej napisane
Na razie to tyle. W kolejnej części zobaczymy na ile jeszcze starczy pamięci Flash i w jakim stopniu będę dalej w stanie ten projekt rozwinąć. Liczę chociażby na te RTCC...

Załączam "projekt" (kod i skrypt wsadowy do kompilacji) do wglądu:
Załączniki:
  • PIC12F683_I2C-20241108.zip (47.41 KB) Musisz być zalogowany, aby pobrać ten załącznik.

O autorze
p.kaczmarek2
Inżynier programista z wieloletnim doświadczeniem embedded i full stack developer. Specjalizuje się w: embedded, Full-Stack Developer p.kaczmarek2 napisał 14408 postów o ocenie 12345 , pomógł 650 razy. Jest z nami od 2014 roku.

Komentarze

Sam Sung 10 Lis 2024 18:17

Wszystko fajnie, ale jak z płyty takiego STB pozbyć się zbędnego obciążenia, tj. SoC i RAM? To jest chyba najtrudniejszy etap, bo trzeba precyzyjnie wylutować gęstonóżkowe układy czy BGA tak, żeby nie... [Czytaj dalej]

p.kaczmarek2 11 Lis 2024 00:07

Jak rozumiem, pytasz o użycie gorącego powietrza do wylutu tak, aby nie było zwarć? Zwarcia usuwam plecionką, dodatkowo używam topnika. Plecionka pozwala skutecznie zebrać spoiwo z padów. Do SoC też... [Czytaj dalej]

XJ_ 20 Lis 2024 17:21

Wau, całkiem stary skool wrx. Kłaniam się, proszę pana !!! Dodano po 3 [minutach]: . Tak jak ja... Jeśli miga, wszystko będzie dobrze ;-) [Czytaj dalej]

%}