Szyna I2C w Bascom

To forum jest dla wszystkich pasjonatów mikrokontrolerów AVR Atmela. Wymiana doświadczeń i pomoc dla początkujących w pisaniu programów zarówno w C, Asemblerze jak i BASCOM. Zapraszam znawców tematu, aby pomogli wszystkim początkującym!
ODPOWIEDZ
Piotrek_PxF
-
Posty: 9
Rejestracja: 26 lis 2005, 22:52

Szyna I2C w Bascom

Post autor: Piotrek_PxF » 25 gru 2006, 23:56

Witam.

Mam taki mały problem z którym się nie mogę uporać. Otóż robię sobie zegarek z wyświetlaczem LED, na procesorze ATmega8 i z zewnętrznym zegarem RTC. Wszystko idzie ładnie, gładko i świątecznie, ale nie mogę się uporać z jedną drobnostką - przesyłaniem I2C. Mam układ RTC - DS1340. Czy mógłby ktoś mi wytłumaczyć jak napisać prawidłowo komunikację i2c w bascomie?
Załączam tabelę rejestrów w zegarze DS1340 i kod mojego programu. Procedura przetwarzania danych z RTC (kod BCD itd) jest dobra, bo jak się poda jej dane w formie takiej, jakie powinna przykładowo otrzymać z i2c, to wyświetla cyfry dobrze. Jak sądzę, to RTC po włączeniu do zasilania powinien zliczać od godziny 00:00:00 (nie jestem tego pewien, może to jest przyczyną złego działania całego układu). Nie ustawiałem jeszcze tego zegarka, tylko próbuję go uruchomić. Układ ma wyświetlać godziny, minuty i sekundy. Na wyświetlaczu po włączeniu układu pojawiają się zupełnie dziwne znaki, których nie ma w tablicy cyfr, którą zdefiniowałem na początku kodu. Nie wiem dlaczego tak się dzieje. Czy mógłby ktoś mi pomóc?

Obrazek

Kod: Zaznacz cały

$regfile = "m8def.DAT"
$crystal = 8000000

Config Portb = Output
Config Portc = Output
Config Scl = Portd.2
Config Sda = Portd.3
I2cinit

Declare Sub Wyswietl(c1 As Byte , C2 As Byte , C3 As Byte , C4 As Byte , C5 As Byte , C6 As Byte , Lp As Byte , Po1 As Byte , Po2 As Byte)

' Tablica znaków na wyświetlaczu 7-segmentowym
Dim Led_c(10) As Byte
Led_c(1) = &B11000000
Led_c(2) = &B11111001
Led_c(3) = &B10100100
Led_c(4) = &B10110000
Led_c(5) = &B10011001
Led_c(6) = &B10010010
Led_c(7) = &B10000010
Led_c(8) = &B11111000
Led_c(9) = &B10000000
Led_c(10) = &B10010000

 ' PROCEDURA GŁÓWNA
Do    

Dim I_sec As Byte , I_min As Byte , I_hou As Byte , I1_sec As Byte , I1_min As Byte , I1_hou As Byte

'  VVV  - Z tym mam problem - odbiór danych przez I2C
I_sec = 1
I_min = 2
I_hou = 3
I2creceive &B1101000 , I_sec , 1 , 1
I2creceive &B1101000 , I_min , 1 , 1
I2creceive &B1101000 , I_hou , 1 , 1


' wiem, że te shifty trochę głupio wyglądają, ale dobrze dzielą kod bcd na poszczególne cyfry
I1_sec = I_sec
I1_min = I_min
I1_hou = I_hou
Shift I1_sec , Left , 1
Shift I1_sec , Right , 5
Shift I_sec , Left , 4
Shift I_sec , Right , 4
Shift I1_min , Left , 1
Shift I1_min , Right , 5
Shift I_min , Left , 4
Shift I_min , Right , 4
Shift I1_hou , Left , 2
Shift I1_hou , Right , 6
Shift I_hou , Left , 4
Shift I_hou , Right , 4

Dim Nul As Byte , Tr As Byte
Nul = 0
Tr = 1

Call Wyswietl(i1_hou , I_hou , I1_min , I_min , I1_sec , I_sec , Tr , Nul , Nul)

Loop                                                       
 'Koniec PROCEDURY GŁÓWNEJ


Sub Wyswietl(c1 As Byte , C2 As Byte , C3 As Byte , C4 As Byte , C5 As Byte , C6 As Byte , Lp As Byte , Po1 As Byte , Po2 As Byte)
'a to jest procedura wyświetlania cyfr na wyświetlaczu LED
' ta procedura musi być wywoływana co chwilę
Local Led_addr As Byte
Local Led_enn As Byte

Incr C1
Incr C2
Incr C3
Incr C4
Incr C5
Incr C6
Led_addr = &B11111110
For Led_enn = 1 To 7
  Portb = &B11111111
  Portc = Led_addr
  Select Case Led_enn
     Case 1 : Portb = Led_c(c1)
     Case 2 : Portb = Led_c(c2)
     Case 3
       Portb = Led_c(c3)
       If Lp = 1 Then Portb.7 = 0
     Case 4
       Portb = Led_c(c4)
       If Lp = 1 Then Portb.7 = 0
     Case 5
       Portb = Led_c(c5)
       If Po1 = 1 Then Portb.7 = 0
     Case 6
       Portb = Led_c(c6)
       If Po2 = 1 Then Portb.7 = 0
  End Select
  Rotate Led_addr , Left , 1
Next Led_enn

End Sub

End

ZbeeGin

Post autor: ZbeeGin » 26 gru 2006, 10:19

Piotrek_PxF pisze:

Kod: Zaznacz cały

'  VVV  - Z tym mam problem - odbiór danych przez I2C 
I_sec = 1 
I_min = 2 
I_hou = 3 
I2creceive &B1101000 , I_sec , 1 , 1 
I2creceive &B1101000 , I_min , 1 , 1 
I2creceive &B1101000 , I_hou , 1 , 1
Podajesz jako dwa ostatnie parametry ilość danych wysyłanych = 1, ilość danych odbieranych = 1. Myślę, że ta sekwencja wymaga trochę innego podejścia. Najpierw należy ustawić wskaźnik odczytu dla rejestrów, gdyż po każdym odczycie zostanie on zwiększony. Zatem następna próba odczytu w następnym przejściu pętli Do...Loop - bez ustawiania na nowo wskaźnika - spowoduje odczyt trzech następnych rejestrów, które jak widać nie zawierają danych godzinty! A jak czytam w nocie katalogowej wskaźnik po przekroczeniu 07H sam się zeruje. Dlatego przy 3 odczycie z pętli otrzymalibyśmy rok, bajt konfiguracji oraz sekundy...

Moim zdaniem należy to wykonać tak (nie testowałem!):

Kod: Zaznacz cały

'  VVV  - Z tym mam problem - odbiór danych przez I2C 
I2csend &B1101000, 0
' wyślij 0 by ustawić wskaźnik rejestrów na początek
I2creceive &B1101000 , I_sec 
I2creceive &B1101000 , I_min 
I2creceive &B1101000 , I_hou
' odczytaliśmy 3 nstępujące po sobie rejestry 
A jeśli to nie wyjdzie, to spróbujemy "na piechotę": I2CSTART, I2CWBYTE, I2CREAD, I2CSTOP. :wink:
Piotrek_PxF pisze:Jak sądzę, to RTC po włączeniu do zasilania powinien zliczać od godziny 00:00:00 (nie jestem tego pewien, może to jest przyczyną złego działania całego układu). Nie ustawiałem jeszcze tego zegarka, tylko próbuję go uruchomić.
Posiłkując się stwierdzeniem: Unless otherwise specified, the state of the registers is not defined when power is first applied. Sądzę, że dane zegara nie są inicjowane - datasheet milczy, albo nie znalazłem - oprócz niektórych bitów. Dlatego najpierw wypadałoby ustawić tam jakieś sensowne dane.
Do testów możesz wykorzystać dane takie jakie są. Po jednej sekundzie powinny się zmienić, więc będzie to informacja, że odczyt jest poprawny.

Piotrek_PxF
-
Posty: 9
Rejestracja: 26 lis 2005, 22:52

Post autor: Piotrek_PxF » 26 gru 2006, 12:39

Witam.
Procedurę odczytu napisałem tak jak pan polecił. Nic to nie dało. Cały czas na wyświetlaczu widać "krzaki" widoczne na zdjęciu: http://pxf.dlk.pl/images/j/061226_025153
Takich znaków nie ma w tablicy Led_c. Gdy poda się zamiast procedury odczytu z I2C dane w takiej formie jak powinny przyjść z urządzenia, to wyświetla poprawnie, czyli cała procedura wyświetlacza jest dobra. Być może z RTC przychodzą jakieś dziwaczne dane z cyframi większymi od 9 i program wychodzi poza ustaloną tablicę znaków. Taki obraz na wyświetlaczu jest cały czas, a próbowałem już wiele różnych sposobów odczytu I2C (często błędnych). Gdy całkiem odłączę z układu zegar RTC to na wyświetlaczu pojawiają się cyfry: 00 : 08 : 00 (zupełnie nie wiem skąd ta ósemka).

Napisałem też na początku kodu procedurę zapisu jakichś tam danych do tego zegarka, ale to też nic nie zmieniło. Jak bym nie próbował z tymi I2C, to cały czas wyświetla te same dziwaczne dane... Wymieniałem nawet procesor, ale drugi zachowuje się tak samo.

Kod: Zaznacz cały

Dim Ax(10) As Byte
Ax(1) = 4
Ax(2) = 8
Ax(3) = 5
Ax(4) = 1
Ax(5) = 1
Ax(6) = 1
Ax(7) = 6
Ax(8) = 0
Ax(9) = 0
Ax(10) = 0
I2cstart
I2cwbyte &B1101000
I2cwbyte &H00
I2cwbyte Ax(1)
I2cwbyte Ax(2)
I2cwbyte Ax(3)
I2cwbyte Ax(4)
I2cwbyte Ax(5)
I2cwbyte Ax(6)
I2cwbyte Ax(7)
I2cwbyte Ax(8)
I2cwbyte Ax(9)
I2cwbyte Ax(10)
I2cstop
Waitms 50

ZbeeGin

Post autor: ZbeeGin » 26 gru 2006, 13:17

Piotrek_PxF pisze:Shift I1_sec , Left , 1
Shift I1_sec , Right , 5
Shift I_sec , Left , 4
Shift I_sec , Right , 4

Shift I1_min , Left , 1
Shift I1_min , Right , 5
Shift I_min , Left , 4
Shift I_min , Right , 4

Shift I1_hou , Left , 2
Shift I1_hou , Right , 6
Shift I_hou , Left , 4
Shift I_hou , Right , 4
Odbiegając nieco od tematu. Dlaczego wykonujesz te polecenia - zanaczone? Przecież w sumie nie zmieniają one zawartości tych zmiennych...

ps. Właśnie próbuje w symualtorze ten program. Może będę wiedział skąd się bierze to "8".

Piotrek_PxF
-
Posty: 9
Rejestracja: 26 lis 2005, 22:52

Post autor: Piotrek_PxF » 26 gru 2006, 13:37

ZbeeGin pisze:Odbiegając nieco od tematu. Dlaczego wykonujesz te polecenia - zanaczone? Przecież w sumie nie zmieniają one zawartości tych zmiennych...
Shift przesuwa bity w prawo lub w lewo i wypełnia puste miejsce zerami. Załóżmy, że mamy dane 01110101. To jest kod BCD, czyli po 4 bity na jedną cyfrę. Potrzeba "odfiltrować" prawą połówkę (0101), czyli przesunąć w lewo o 4 i w prawo o 4:

Kod: Zaznacz cały

01110101  - dane
11101010  - 1 przesunięcie w lewo
11010100  - 2 przesunięcie w lewo
10101000  - 3 przesunięcie w lewo
01010000  - 4 przesunięcie w lewo

01010000  - dane z poprzedniej operacji
00101000  - 1 przesunięcie w prawo
00010100  - 2 przesunięcie w prawo
00001010  - 3 przesunięcie w prawo
00000101  - 4 przesunięcie w prawo
Jeśli dobrze zrozumiałem, to działa w ten sposób...

Cały czas nie mogę uporać się z tym zegarkiem. Może trzeba mu jakoś ustawić odpowiednie bity, żeby się włączył czy coś?

ZbeeGin

Post autor: ZbeeGin » 26 gru 2006, 13:58

Piotrek_PxF pisze:Shift przesuwa bity w prawo lub w lewo i wypełnia puste miejsce zerami.
Święta, i nie pomyślałem o tym... :mrgreen:
Piotrek_PxF pisze:Może trzeba mu jakoś ustawić odpowiednie bity, żeby się włączył czy coś?
Wg noty, to bity sterujące odpowiedzialne za pracę zegarka są ustawiane poprawnie po włączeniu zasilania. Jak chciałbyś sprawdzić czy transmisja do Dallasa działa i czy działa oscylator, należałoby ustawić się na rejestr 07H i ustawić bit FT. Jeśli na wyprowadzeniu FT/OUT pojawi się sygnał 512Hz to znaczy, że oscylator działa i transmisja także.

W kwestii sprzętowej. Na liniach SCL i SDA masz resystory podciągające?

Piotrek_PxF
-
Posty: 9
Rejestracja: 26 lis 2005, 22:52

Post autor: Piotrek_PxF » 26 gru 2006, 15:44

ZbeeGin pisze:W kwestii sprzętowej. Na liniach SCL i SDA masz resystory podciągające?
Na SCL i SDA mam rezystory 4,3kΩ podpięte do zasilania. Zasilanie 4,8V.

[ Dodano: 2006-12-28, 13:18 ]
Problemu komunikacji nadal nie rozwiązałem, ale znalazłem jeszcze jeden ciekawy problem w procedurze Wyswietl(). Chodzi o to dlaczego po odłączeniu zegara RTC na wyświetlaczu zamiast 00:00:00 wyświetlało się 00:08:00. Ta ósemka to wynik zadeklarowania zmiennej lokalnej:

Kod: Zaznacz cały

Sub Wyswietl(c1 As Byte , C2 As Byte , C3 As Byte , C4 As Byte , C5 As Byte , C6 As Byte , Lp As Byte , Po1 As Byte , Po2 As Byte)
'a to jest procedura wyświetlania cyfr na wyświetlaczu LED
' ta procedura musi być wywoływana co chwilę
Local Led_addr As Byte
Local Led_enn As Byte

Incr C1
Incr C2
Incr C3
Incr C4
Incr C5
Incr C6
Led_addr = &B11111110
For Led_enn = 1 To 7
  Portb = &B11111111
  Portc = Led_addr
  Select Case Led_enn
     Case 1 : Portb = Led_c(c1)
     Case 2 : Portb = Led_c(c2)
     Case 3
       Portb = Led_c(c3)
       If Lp = 1 Then Portb.7 = 0
     Case 4
       Portb = Led_c(c4)
       If Lp = 1 Then Portb.7 = 0
     Case 5
       Portb = Led_c(c5)
       If Po1 = 1 Then Portb.7 = 0
     Case 6
       Portb = Led_c(c6)
       If Po2 = 1 Then Portb.7 = 0
  End Select
  Rotate Led_addr , Left , 1
Next Led_enn

End Sub 
Chodzi tutaj o Led_addr i Led_enn. Na symulatorze to dobrze widać jak się przegląda prodedurę krok po kroku. Wygląda to tak, jakby dwie zmienne kolidowały ze sobą. Wraz ze zmianą Led_enn zmienia się lokalna C4, która zawiera w sobie wartość czwartej cyfry na wyświetlaczu. Najpierw się zmienia na &B11111111 a następnie na &B00000000. Gdy te same zmienne zadeklaruje się poprzez Dim problem znika. Nie wiem dlaczego tak jest.

Ma ktoś jakiś pomysł na tą I2C?

Awatar użytkownika
marcing
Użytkownik
Posty: 868
Rejestracja: 14 lut 2006, 14:13
Lokalizacja: z pociągu...
Kontakt:

Post autor: marcing » 05 sty 2007, 12:49

A LOCAL variable is a temporary variable that is stored on the frame.
When the SUB or FUNCTION is terminated, the memory will be released back to the frame.
BIT variables are not possible because they are GLOBAL to the system.
Czyli - zmienna typu LOCAL umieszczana jest w tzw. ramce. Jest to zmienna czasowa - i na razie wszystko się zgadza.
Ale - zauważ jedno - obsługa ramki częściowo podobna jest do obsługi stosu. Czyli ostatni umieszczony element odczytywany jest jako pierwszy. I tu jest Twój problem - spójrz na kolejność odczytu zmiennych w programie...

Przy deklaracji pojedyńczej zmiennej tego typu nie ma problemu.

Można spróbować zadeklarować odpowiednie zmienne w programie, a w deklaracji procedury użyć polecenia BYVAL.

Piotrek_PxF
-
Posty: 9
Rejestracja: 26 lis 2005, 22:52

Post autor: Piotrek_PxF » 05 sty 2007, 17:28

Aha, z tymi zmiennymi to już rozumiem, ale nadal nie zrobiłem tego zegarka. Po poprawie wyświetlania na wyświetlaczu pojawiają się normalnie cyfry (a nie jakieś bezsensowne znaki), ale nie zmieniają się (to chyba jest 39:76:39). Tak jakby zegarek miał bezsensowne dane i nie startował przy włączeniu. Wyczytałem coś takiego, ale nie wiem czy dobrze zrozumiałem, że zegarek ma pamięć osobną do liczenia godziny, a jak ma przesłać dane, to przenosi je do innej pamięci i z niej przesyła żeby nie zakłócać pracy zegarka. Może tu gdzieś trzeba coś zrobić. Czy mógłby ktoś jeszcze przejrzeć notę katalogową? Jak trzeba, to wieczorem mogę dać schemat całego układu.

ODPOWIEDZ