Przejdź do głównej zawartości

Programowanie AVR cz. 3: ATtiny2313, EEPROM AT24C04C i magistrala I2C. Oraz krótko o pewnej zapomnianej obietnicy.

000 wstęp

Głównym bohaterem tego posta jest magistrala I2C, zwana również interfejsem dwuprzewodowym (ang. Two-wire lub 2-wire interface). To proste łącze wykorzystywane jest w wielu ciekawych układach mogących współpracować z mikrokontrolerami i nie tylko. Można tu wspomnieć - żeby nie było, że użyłem słowa "wielu" bez pokrycia - choćby o układach pamięci, zegarach czasu rzeczywistego, przetwornikach AC/CA...
Co prawda "małe" mikrokontrolery nie mają dedykowanych linii dla I2C, ale dzięki środowiskom i bibliotekom dla programistów, zapewniającym pełną implementację m. in. protokołu I2C, mamy możliwość bezproblemowego wykorzystania dobrodziejstw tej magistrali posługując się uniwersalnymi liniami wejścia - wyjścia. Można też podjąć się samodzielnej implementacji obsługi tej magistrali - jeśli ktoś lubi takie wyzwania.

Na początek jednak warto przyjrzeć się samej idei i koncepcji magistrali I2C:
Dobrze by było też przeanalizować kartę katalogową układu AT24C04C (zakładam, że Tiny2313 już dość dobrze znamy):

Układ AT24C04C - jak sugeruje tytuł posta - to pamięć EEPROM. Nie jest to jednak kostka pamięci w klasycznym tego słowa rozumieniu, z wieloma liniami adresowymi i danych oraz liniami sterującymi - mówimy o tym układzie "pamięć szeregowa", ponieważ dostęp do jej zawartości uzyskujemy właśnie poprzez szeregową magistralę I2C. Taka konstrukcja pamięci pozwoliła przede wszystkim na zmniejszenie fizycznych rozmiarów układu do np. typowej kostki w obudowie DIP-8 (dwa rzędy po cztery wyprowadzenia), co sprzyja wykorzystaniu tego układu w małogabarytowych projektach.
Przy okazji przeglądania (analizy!) karty katalogowej AT24C04C, możemy się wiele dowiedzieć na temat samej magistrali, tym razem już konkretnie - głównie chodzi tu o protokół, sposób adresacji urządzeń itp. Ze względu zaś na prostotę operacji - bo w przypadku pamięci mamy wyłącznie zapis i odczyt bez dodatkowych czynności - AT24C04C jest rewelacyjnym środkiem dydaktycznym i znakomitym przykładem na początek przygody z naszą magistralą.

A dlaczego właśnie 24C04, a nie np. 24C08? Proste: taką kostkę akurat miałem...

No to zaczynamy!

001 przygotowanie układu

Trzeba pamiętać, że wiele układów cyfrowych przeznaczonych do współpracy z innymi układami za pomocą magistral, posiada wyjścia typu otwarty kolektor (otwarty dren). Żeby się nie rozpisywać - chodzi tutaj o "końcówkę mocy" wyjścia układu; jest ona niepełna, co daje możliwość dostosowania obciążenia układu w dużo większym zakresie, niż ma to miejsce w przypadku klasycznych wyjść cyfrowych. Dostosowanie obciążenia (to w pewnym sensie skrót myślowy - zainteresowanych odsyłam do literatury technicznej dotyczącej podstaw elektroniki) wykonuje się przy pomocy rezystorów podciągających, które stanowią uzupełnienie owego niekompletnego wyjścia układu. W przypadku I2C konieczne jest zastosowanie właśnie takich rezystorów dla obu linii (SCL i SDA); wartość tych rezystorów należy odpowiednio dobrać - w przypadku naszego układu będzie to 4,7kΩ. Oczywiście rezystor podciągający włączamy między zasilanie a linię magistrali. Dodatkowo linie I2C należy podłączyć do mikrokontrolera poprzez rezystory o wartości 300-330Ω.
Następnym krokiem w procesie przygotowania układu testowego jest ustalenie adresu sprzętowego dla kostki pamięci. Adresy urządzeń na magistrali I2C to liczby 7-bitowe. Ósmy bit (a w zasadzie pierwszy, bo na najmniej znaczącej pozycji) to znacznik kierunku transmisji - wartość "1" oznacza odczyt z urządzenia, "0" to zapis do urządzenia. Wynika z tego, że każde urządzenie ma dwa adresy 8-bitowe... Przeglądając dokumentację AT24C04 natkniemy się w którymś momencie właśnie na opis adresowania tego układu. Cztery najstarsze bity adresu 24C04 zawierają "zaszytą" sekwencję "1010", dalej brane są pod uwagę stany zadane na wyprowadzenia A2 i A1 układu (A0 jest ignorowane w przypadku 24C04), następnie mamy znacznik "banku" pamięci P0 i bit kierunku R/W. Co do znacznika banku pamięci - należy zwrócić uwagę, że układ 24C04 ma pojemność 4kb czyli 512B. Ponieważ przez magistralę I2C można przesyłać dane tylko w postaci paczek 8-bitowych, nie ma możliwości "płaskiego" adresowania całej zawartości pamięci - za pomocą liczby 8-bitowej jesteśmy w stanie zaadresować jedynie połowę zawartości tej pamięci, czyli 256B (24C04 ma organizację bajtową). Tutaj właśnie z pomocą przychodzi nam znacznik P0, który stanowi przełącznik dla owych połówek pamięci - np. dla P0=1 komórka pamięci o adresie 00h to tak naprawdę komórka o adresie 100h.
W naszym układzie testowym ustalamy wartość bitów A2 i A1 jako "0" (podłączamy do masy).
Jak się ostatecznie okazuje, nasz układ pamięci ma aż cztery adresy na magistrali I2C! Adresy te, już w postaci liczbowej dziesiętnej, znajdziecie w prezentowanym w dalszej części artykułu listingu programu.
Na koniec zerujemy wejście układu pamięci oznaczone jako WP (Write Protect), żeby zapewnić sobie możliwość swobodnego zapisu i odczytu.

Na poniższym schemacie widzimy już kompletny układ do testowania pamięci 24C04 i magistrali I2C:



Dodatkowo, w celu wizualizacji procesu poprawnego (lub nie) zapisu i odczytu pamięci, dołączyłem do układu testowego wyświetlacz LCD (na schemacie jest to wyświetlacz 16*2 - w rzeczywistości użyłem 16*1a) - klasyczny, na bazie układu kompatybilnego z HD44780 oraz dwie diody LED. Program (przedstawiony dalej) uruchamiany jest przyciskiem, który na schemacie widnieje pod postacią jumpera TEST J1 - tutaj też użyłem symbolu zastępczego (muszę uzupełnić biblioteki Eagle'a).

002 program

Żeby hardware był użyteczny, potrzebny jest software... Na listingu poniżej widnieje program testowy, którego zadaniem jest zapełnienie obu banków pamięci pewnymi wartościami (dla każdego banku jest to inna wartość), a następnie sprawdzenie, czy dane zostały prawidłowo zapisane - czyli odczytanie zawartości obu banków i porównanie liczby odczytanej z zakładaną wartością poprawną.

' Obsługa pamięci EEPROM 24c04
' - przykład + efekty specjalne
$regfile = "attiny2313.dat"
$crystal = 4000000
$hwstack = 40
$swstack = 16
$framesize = 32
' Liczby wpisywane do pamięci w banku 0 i 1 (bit P0 adresu urządzenia)
Const Val_p0 = 220
Const Val_p1 = 225
' Konfiguracja wyświetlacza
Config Lcd = 16 * 1a
' Porty mikrokontrolera (dwie linie portu B), które będą emulować wyprowadzenia magistrali I2C (mikrokontroler to MASTER)
Config Scl = Portb.1
Config Sda = Portb.0
' Do linii portu D podłączone są diody kontrolne oraz przycisk uruchamiający test zapisu i odczytu
Config Portd = &B1110111
Portd = &B0001000
' Pamięć 24c04 (SLAVE):
' - wyprowadzenia A2 i A1 podłączone do masy, stąd adresy jak niżej.
' Pierwsze 256 bajtów - bank 1 (bit P0 = 0)
Const Addrw_p0 = 160
Const Addrr_p0 = 161
' Drugie 256 bajtów - bank 2 (bit P0 = 1)
Const Addrw_p1 = 162
Const Addrr_p1 = 163
Dim Adres As Byte , Value As Byte
Dim Addrw As Byte , Addrr As Byte
Dim Tst_val As Byte
Declare Sub Write_eeprom(byval Adres As Byte , Byval Value As Byte)
Declare Sub Read_eeprom(byval Adres As Byte , Value As Byte)
Declare Sub Error
Declare Sub Read_bank_test(byval Bank_nr As Byte)
Declare Sub Write_bank_test(byval Bank_nr As Byte)
Portd.5 = 0 ' dioda czerwona
Portd.4 = 0 ' dioda niebieska
Portd.3 = 1 ' "przycisk" uruchamiający proces zapisu/odczytu
Cursor Off
Cls
Lcd "2404 test ready"
Do
If Pind.3 = 0 Then
'Zapis pierwszego banku pamięci (256 bajtów o wartości stałej Val_p0)
Write_bank_test 0
Waitms 100
'Zapis drugiego banku pamięci (256 bajtów o wartości stałej Val_p1)
Write_bank_test 1
Cls
' Przerwa - sygnalizacja zmiany trybu testu na odczyt
For Adres = 0 To 2
Waitms 50
Portd.4 = 1
Portd.5 = 1
Waitms 50
Portd.4 = 0
Portd.5 = 0
Next Adres
'Odczyt pierwszego banku pamięci
Read_bank_test 0
Waitms 100
'Odczyt drugiego banku pamięci
Read_bank_test 1
Cls
Lcd "2404 test OK!"
Waitms 500
Cls
Lcd "2404 test ready"
End If
Loop
End
' Zapis bajtu do pamięci 24c04
Sub Write_eeprom(byval Adres As Byte , Byval Value As Byte)
Portd.4 = 1
I2cstart
I2cwbyte Addrw
I2cwbyte Adres
I2cwbyte Value
I2cstop
Waitms 5
Portd.4 = 0
End Sub
' Odczyt bajtu z pamięci 24c04
Sub Read_eeprom(byval Adres As Byte , Value As Byte)
Portd.4 = 1
I2cstart
I2cwbyte Addrw
I2cwbyte Adres
I2cstart
I2cwbyte Addrr
I2crbyte Value , Nack
I2cstop
Waitms 5
Portd.4 = 0
End Sub
' Sygnalizacja błędu
Sub Error
Cls
Lcd "R/W error"
Portd.5 = 1
Waitms 50
Portd.5 = 0
End Sub
' Podprogram odczytu i weryfikacji zawartości wskazanego banku pamięci
Sub Read_bank_test(byval Bank_nr As Byte)
If Bank_nr = 0 Then
Addrr = Addrr_p0
Addrw = Addrw_p0
Tst_val = Val_p0
Else
Addrr = Addrr_p1
Addrw = Addrw_p1
Tst_val = Val_p1
End If
Cls
Lcd "P0=" + Bank_nr + ",R,val=" + Tst_val
For Adres = 0 To 255
Read_eeprom Adres , Value
Waitms 5
If Value <> Tst_val Or Err <> 0 Then
Error
Cls
Lcd "P0=" + Bank_nr + ",R,val=" + Tst_val
End If
Next Adres
End Sub
' Podprogram zapisu zawartości wskazanego banku pamięci
Sub Write_bank_test(byval Bank_nr As Byte)
If Bank_nr = 0 Then
Addrw = Addrw_p0
Tst_val = Val_p0
Else
Addrw = Addrw_p1
Tst_val = Val_p1
End If
Cls
Lcd "P0=" + Bank_nr + ",W,val=" + Tst_val
For Adres = 0 To 255
Write_eeprom Adres , Tst_val
Waitms 5
If Err <> 0 Then
Error
Cls
Lcd "P0=" + Bank_nr + ",W,val=" + Tst_val
End If
Next Adres
End Sub
view raw i2c.bas hosted with ❤ by GitHub


Polecam przeanalizowanie programu, ze szczególnym zwróceniem uwagi na to, jak elegancko zostały zaprojektowane funkcje BASCOMa operujące na magistrali I2C - odzwierciedlają one dość dokładnie "protokół" logiczny tej magistrali.

Należy zdać sobie sprawę, że sporą część kodu stanowią ozdobniki związane z wizualizacją operacji - stąd program po kompilacji zajmuje ok. 1,5kB. Same operacje komunikacji z I2C są tutaj w miarę lekkie.

Ktoś pomyśli: no ok, działa...
...ale jak sprawdzić, czy faktycznie? Bo każde następne uruchomienie spowoduje - w przypadku nieudanego zapisu - odczyt wcześniej zapisanych do pamięci danych, które są oczywiście poprawne. Sprawa jest prosta - trzeba przekompilować program z innymi wartościami Val_p0Val_p1. A jeśli ktoś chce sprawdzić, jak to jest z tymi rezystorami podciągającymi - zachęcam do wyjęcia jednego z nich (lub obu) w trakcie pracy układu. Efekty specjalne gwarantowane (spokojnie, jeśli nie zrobicie gdzieś jakiegoś zwarcia, nic się nie spali).

003 todo

Jeśli nic nie pomyliliśmy, to nasz układ do testowania I2C powinien działać bez problemów. Nie wiem, jak Wy, ale ja zwykle lubię troszkę podrążyć temat (sukces jest najlepszą zachętą) - proponuję więc przyjrzenie się układowi PCF8583P. Jest to zegar czasu rzeczywistego z kalendarzem - oczywiście obsługiwany poprzez I2C i umieszczony, podobnie jak AT24C04, w malutkiej obudowie ośmiowyprowadzeniowej. Podłączamy do niego oscylator (ok. 34kHz), kondensatorek 33pF, zapewniamy ciągły dostęp zasilania (można z baterii) i... Ale o tym być może wkrótce :) Zachęcam również do samodzielnych eksperymentów.

Postscriptum

Dawno temu obiecałem (tak, czego ja już na tym blogu nie obiecywałem...) program do przekształcenia ATtiny 2313 w układ sterowania linijką świetlną. Oto i on:

' 3 efekty świetlne na 8 LED-ów i mikrokontroler.
$regfile = "attiny2313.dat"
$crystal = 4000000
$hwstack = 40
$swstack = 16
$framesize = 32
' Poniższa dyrektywa musi być aktywna dla avrdude, stk500.exe oraz AVRStudio;
' Programator USBASP z poziomu Bascoma wymaga binarnego wsadu do EEPROM (domyślnie)
'$eepromhex
$eeprom
' Definicje efektów świetlnych
' 1 - Od lewej do prawej i z powrotem
Leftright:
Data 128 , 64 , 32 , 16 , 8 , 4 , 2 , 1 , 2 , 4 , 8 , 16 , 32 , 64
' 2 - Od lewej do prawej czwórkami
Leftrightfour:
Data 129 , 66 , 36 , 24 , 36 , 66
' 3 - Tylko do prawej
Toright:
Data 128 , 64 , 32 , 16 , 8 , 4 , 2 , 1
' 4 - Tylko do lewej
Toleft:
Data 1 , 2 , 4 , 8 , 16 , 32 , 64 , 128
$data
' Czas wyświetlania (dla efektu nie sterowanego timerem)
Const Lr4 = 16
' Port B jako wyjścia
Config Portb = &B11111111
' Stan spoczynkowy portu B to 0
Portb = &B00000000
' Bity 6, 5 i 4 portu D jako wejścia
Config Portd = &B1000111
' Stan spoczynkowy portu D to 1
Portd = &B1111111
' Wyświetlaniem normalnej linijki będzie sterował timer0 - następna dioda zapalana co ok. 16ms
Config Timer0 = Timer , Prescale = 256
' Włączamy obsługę przerwań i przerwanie timera 0
Enable Interrupts
Enable Timer0
' Ustawiamy podprogram obsługi przerwania timera 0
On Timer0 Przesun_linijke
' Funkcja realizująca efekt świetlny (deklaracja)
' address - adres w EEPROM początku tablicy z definicją efektu
' maxcount - maksymalna wartość licznika pętli (liczba elementów w/w tablicy - 1)
Declare Sub Effect(address As Byte , Byval Maxcount As Byte)
Dim I As Byte , Tmp As Byte ' licznik pętli, zmienna pomocnicza do wyznaczania adresu w EEPROM
Dim Iters As Byte ' licznik pętli dla timera Timer0
Dim Addr As Byte ' zmienna przechowująca adres początku tabeli w pamięci EEPROM
' Okazuje się, że port D nie od razu ma wartości spoczynkowe ustalone...
Portd.3 = 1
Portd.4 = 1
Portd.5 = 1
Iters = 0
Do
' W pętli sprawdzane są po kolei bity od najmłodszego do najstarszego,
' więc wyzerowanie kilku na raz będzie zinterpretowane jako naciśnięcie
' jednego (tego, który pierwszy został wyzerowany); może to sprawiać wrażenie
' losowości.
If Pind.3 = 0 Then ' efekt czwórek
Addr = Loadlabel(leftrightfour)
Effect Addr , 5
Elseif Pind.4 = 0 Then ' efekt "do prawej"
Addr = Loadlabel(toright)
Effect Addr , 7
Elseif Pind.5 = 0 Then ' efekt "do lewej"
Addr = Loadlabel(toleft)
Effect Addr , 7
End If
Loop
End
' Funkcja realizująca efekt świetlny (definicja)
Sub Effect(address As Byte , Byval Maxcount As Byte)
Stop Timer0
For I = 0 To Maxcount
Tmp = Address + I
Readeeprom Portb , Tmp
Waitms Lr4
Next I
Start Timer0
End Sub
' Podprogram obługi przerwania - bez pętli itp., maks. krótki
Przesun_linijke:
Addr = Loadlabel(leftright)
Tmp = Addr + Iters
Readeeprom Portb , Tmp
Iters = Iters + 1
Iters = Iters Mod 14
Return
view raw efekty.bas hosted with ❤ by GitHub


Jeżeli chodzi o elektronikę, do portu B mikrokontrolera podłączamy osiem diod LED poprzez rezystor 220Ω (podobnie jak na schemacie pokazanym wcześniej), zaś do wskazanych na listingu wyprowadzeń portu D - przyciski zwierające do masy. Do tego oczywiście oscylator 4MHz z dwoma kondensatorkami 33pF (również jak na wcześniejszym schemacie).

Po wgraniu programu można pobawić się efektami świetlnymi (proszę zwrócić uwagę, że efekt "spoczynkowy" jest generowany przez pracujący w tle timer, natomiast definicje efektów umieszczone są w wewnętrznej pamięci EEPROM mikrokontrolera).
Udanych eksperymentów zatem!

ERRATA czy też konieczny, acz zapomniany dopisek

Zagłębiając się w adresowanie układów na magistrali I2C i podłączanie ich do niej, zapomniałem o czymś, co wprawdzie łatwo można znaleźć w kodzie programu (pierwszy listing), ale co nie rzuca się specjalnie w oczy. A powinno.
Otóż jako wyjścia magistrali I2C mikrokontrolera ATtiny 2313 mogą zostać użyte dowolne dwie linie portów wejścia-wyjścia. U mnie, m. in. ze względu na konieczność podłączenia wyświetlacza, jako wyprowadzenia I2C "robią" linie (wyjścia) PORTB.1 (SCL) i PORTB.0 (SDA). Konfiguracji tych wyjść można dokonać z poziomu środowiska BASCOMa (Options -> Compiler -> I2C):


Dodatkowo zalecałbym wstawienie do kodu, przed deklaracjami zmiennych, dwóch linijek, których znaczenie jest to samo, jak powyższych ustawień środowiska dla I2C:
Config Scl = Portb.1
Config Sda = Portb.0
Dublowanie konfiguracji nie jest szkodliwe, a w tym przypadku wręcz pożądane (uniezależnimy się od konfiguracji środowiska - czasem po prostu możemy o niej zapomnieć).

Komentarze

Popularne posty z tego bloga

Niesamowicie prosty czujnik zmierzchowy.

Tym razem zero programowania, będzie natomiast nostalgiczno-wspomnieniowy układzik, lekko zmodyfikowany. Otóż kilka dni temu rozmawialiśmy w gronie znajomych o różnego rodzaju czujnikach zmierzchowych i czujnikach ruchu. Ponieważ należę do tych wariatów, co to hołdują jeszcze owej przestarzałej i kompletnie odrealnionej dziś zasadzie: "po co kupować, gdy można zrobić", stwierdziłem, że poskładam takie coś (czujnik zmierzchowy; sensor ruchu faktycznie lepiej nabyć, choćby ze względu na rozmiary ;)) i być może podłączę do jakiegoś mikrokontrolera. Przypomniało mi się też przy okazji, że znalazłem ostatnio w elektronicznych śmieciach stary fotorezystor (dla niewtajemniczonych: element zmieniający rezystancję, czyli opór elektryczny, pod wpływem działania strumienia światła) RPP130, jeden z kilku pozostałych po montowanych wieki temu układach tranzystorowych do zdalnego sterowania pracą urządzeń za pomocą latarki... No OK, nie było to specjalnie rozbudowane zdalne sterowanie ;) ...

Aktualizacja oprogramowania układowego w ESP-01 do najnowszej wersji NodeMCU

Oprogramowanie i projekt NodeMCU cieszą się niesłabnącym zainteresowaniem świata konstruktorów urządzeń IoT, zatem co jakiś czas warto odświeżyć sobie firmware w naszych płytkach ESP. Osobiście jestem przeciwnikiem zmienienia czegoś, co dobrze działa, tylko dla zasady czy z chęci cieszenia się świadomością posiadania najnowszej wersji, ale tym razem chodzi jednak o coś innego – zwiększenie funkcjonalności i zapewnienie poprawnego działania oraz kompatybilności z najnowszymi projektami i bibliotekami. W tytule tego artykułu jest mowa o najprostszych płytkach z układem ESP8266 – ESP-01 . To właśnie w oparciu o ten model opracowałem płytkę prototypową, o której pisałem w poprzednim rozdziale. Dotychczas wszystkie moje płytki ESP miały na pokładzie oprogramowanie NodeMCU w wersji 0.9.5 . Zorientowani choć trochę w temacie od razu zauważą (Google? Bing?), że wersja ta ma już co najmniej dwa lata... Najwyższy czas zatem na aktualizację.

Płytka prototypowa na bazie ESP8266 (ESP-01)

To nie jest kolejny artykuł traktujący od początku do... nieco dalej (bo na pewno nie do końca) o płytkach ESP8266 . Żeby się dowiedzieć, co to takiego, odwiedźcie proszę np. tę stronę (oraz wiele innych – poproście o pomoc Waszą ulubioną wyszukiwarkę): http://www.esp8266.com/wiki/doku.php?id=esp8266-module-family . No ale żeby nie było, ESP8266 to układ zawierający na pokładzie wydajny mikrokontroler z rdzeniem RISC-owym, taktowany zegarem 40MHz (wersja, o której jest ten wpis) lub 80MHz, 512KB pamięci flash i podsystem komunikacji przez sieć WiFi . Jest powszechnie wykorzystywany jako swego rodzaju karta sieciowa do połączeń bezprzewodowych naszych urządzeń IoT , które budujemy w zaciszu domowych laboratoriów (i nie tylko). Układ montowany jest na płytkach występujących w kilku wersjach, różniących się przede wszystkim liczbą wyprowadzeń uniwersalnych, czyli GPIO – im większa liczba, tym większe możliwości wykorzystania układu (więcej urządzeń peryferyjnych itp.). Są też pewne ...