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

ATMEGA162 - [AVRStudio4] I2C programowo - nie ma odpowiedzi od slave'a

IRFP640 20 Lip 2012 19:43 1711 18
  • #1 20 Lip 2012 19:43
    IRFP640
    Poziom 9  

    Od niedawna próbuję napisać funkcje, które mi umożliwią dogadanie się z układami na I2C - PCF8574 i MCP3421.
    Szyna leży na porcie B, SDA na pinie 1, SCL na pinie 0. Takie coś na razie popełniłem:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Najpierw inicjuje, daje start, wysyłam adres PCF (0x40) i oczekuje na ACK i nie dostaje ACK, funkcja zawsze zwraca 0, układ nie daje prądu na wyjściu na pinach jeśliby wysłać tam następny bajt. Na koniec leci i2c_stop(). Próbowałem, nie daje rady, piszę z nadzieją o pomoc.
    Pozdrawiam

    0 18
  • #2 20 Lip 2012 19:51
    mickpr
    Poziom 39  

    Nie wnikając w kod -> mógłbyś użyć jakiegoś działającego (chce ci się odkrywać koło na nowo?).
    Czy podciągnąłeś obie linie (rezystorami) do stanu "H" - pokaż schemat.

    0
  • #3 20 Lip 2012 20:16
    IRFP640
    Poziom 9  

    Tak, obie linie są podciągnięte do +5V przez rezystory 4.7k

    0
  • #5 20 Lip 2012 20:45
    IRFP640
    Poziom 9  

    Bardzo miło z Twojej strony. Za chwilę się przyjrzę i dam znać czy śmiga (choć czuje, że powinno).
    Pozdrawiam

    Czy za pomocą Twojego kodu jest możliwość zrealizowania powtórzonego startu? (repetet start).

    0
  • #6 20 Lip 2012 21:08
    mickpr
    Poziom 39  

    Kod nie jest mój (dla ścisłości) - gdzieś go znalazłem. Jednak jest tak ładnie ułożony, że powinieneś móc go zmienić wg. swoich potrzeb.

    0
  • #7 20 Lip 2012 23:28
    IRFP640
    Poziom 9  

    Nie działa nadal. Gdzie może kryć się błąd w moim kodzie?

    0
  • Pomocny post
    #9 21 Lip 2012 12:23
    Andrzej__S
    Poziom 28  

    IRFP640 napisał:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Zdecydowanie nie polecam tego typu sterowania magistralą I2C. Jest to magistrala typu "wired-and" i każdy nadajnik i odbiornik powinien mieć I/O typu "open collector".
    W przypadku zewnętrznego podciągnięcia pinów magistrali do Vcc sterowanie magistralą I2C lepiej jest rozwiązać w ten sposób, że bity SDA i SCL w rejestrze PORTx (w Twoim przypadku PORTB) ustawić na stałe na wartość 0, a poziomem na magistrali sterować za pomocą adekwatnego rejestru DDRx (w Twoim przypadku DDRB).
    Ustawienie bitu SDA (lub SCL) w rejestrze DDRx na wartość 0 powoduje ustawienie odpowiedniego pinu w stan wysokiej impedancji co daje na tej linii (poprzez zewnętrzny rezystor pull-up) stan wysoki.
    Ustawienie bitu SDA (lub SCL) w rejestrze DDRx na wartość 1 powoduje ustawienie odpowiedniego pinu jako wyjście w stanie niskim, czyli w efekcie odpowiednia linia zostaje ustawiona także w stan niski.
    Taka technika jest chyba zresztą wykorzystana w kodzie z ostatniego linku podanego przez kolegę mickpr.

    IRFP640 napisał:

    Kod: c
    Zaloguj się, aby zobaczyć kod

    Najpierw inicjuje, daje start, wysyłam adres PCF (0x40) i oczekuje na ACK i nie dostaje ACK, funkcja zawsze zwraca 0...

    Nie analizowałem w całości Twojego kodu, bo raczej zalecałbym (zgodnie z tym co napisałem powyżej) żebyś napisał go od nowa (lub wykorzystał inny sprawdzony). Nie wiem, dlaczego Twój kod nie działa. Chciałem jednak zwrócić Twoją uwagę na fakt, że to odwrotna logika. Układ slave odpowiada ustawiając stan niski na linii SDA po odebraniu swojego adresu, czyli wartość bitu ACK=0 jest właśnie bitem potwierdzenia.
    Odłącz układy od magistrali lub wysyłaj inny (jakiś nieprawidłowy) adres na magistralę i sprawdź, jaką wartość zwraca wtedy funkcja i2c_ack().

    PS. Chciałbym jeszcze dodać, że sprawdzanie bitu potwierdzenia to absolutne minimum sprawdzania poprawności transmisji (wynika to z protokołu). Istnieją jednak inne sytuacje, które mogą powodować błędy i które należałoby uwzględnić pisząc procedury programowej transmisji I2C. Przykładowo uszkodzenie jednego z odbiorników, może wymusić stan niski na jednej z linii (lub na obu), niezależnie od tego, co mikrokontroler będzie próbował na tych liniach ustawić. Czasami układy slave podtrzymują stan niski na linii SCL w sytuacji, gdy np. nie nadążają z odbieraniem danych i w takiej sytuacji układ master powinien czekać na zwolnienie linii przed kontynuowaniem transmisji. Dobry driver magistrali powinien takie sytuacje przewidywać i odpowiednio na nie reagować.

    0
  • #10 21 Lip 2012 15:38
    IRFP640
    Poziom 9  

    Coś takiego napisałem:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Nie działa. Chyba pora wymienić kostkę.

    0
  • #11 21 Lip 2012 16:02
    janbernat
    Poziom 38  

    Albo programistę.
    Z jaką prędkością to ma działać?
    Nigdzie nie podajesz jaki masz zegar.
    Powyżej 100kHz to slave Ci odpowie- albo nie.
    Albo jeszcze lepiej- raz odpowie a pięć razy nie.

    0
  • #12 21 Lip 2012 16:10
    IRFP640
    Poziom 9  

    Zegar 12MHz. Oczekiwana prędkość to 100kHz. Z wartości IIC_WAIT wynika, że prędkość będzie dużo mniejsza, ale z tego co zrozumiałem, to mniejsza częstotliwość na linii SCL nie powinna zaszkodzić komunikacji, jedynie ją spowolni.
    Fusebity są ok, bo USATR działa dla tej częstotliwości kwarcu.

    PS: Janie, ja nie jestem programistą, jestem chemikiem. Ale jako chemik muszę ten problem rozwikłać.

    0
  • Pomocny post
    #13 21 Lip 2012 16:30
    Andrzej__S
    Poziom 28  

    IRFP640 napisał:
    Nie działa. Chyba pora wymienić kostkę.


    Nie działa, bo masz błędy w kodzie. Przeanalizuj jeszcze raz kolejność zmian stanu na poszczególnych liniach Link. Na pewno brakuje ustawienia linii SCL w stan niski pomiędzy warunkiem startu a transmisją adresu (trzeba to zrobić albo na końcu procedury i2c_start() albo na początku procedury i2c_write()). Dalej nie sprawdzałem.

    ...albo może jednak skorzystaj z kodu proponowanego przez kolegę mickpr:
    Link

    0
  • #14 21 Lip 2012 16:53
    janbernat
    Poziom 38  

    A gdzie jest to zdefiniowane:

    Kod: c
    Zaloguj się, aby zobaczyć kod

    Bo ustawienie fusebitów do serial to nijak się do tego nie ma.
    Rozumiem że ma być 100kHz- ale ile jest naprawdę.

    0
  • #15 21 Lip 2012 17:28
    IRFP640
    Poziom 9  

    wait(IIC_WAIT):

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Zajmuje na jakiś czas procesor. Na ile dokładnie tego nie wiem. Z pliku .lss wynika, że pętla to 9 rozkazów, wejście do funkcji 8 i wyjście 5. Nie wiem ile dokładnie taktów zegara to zajmie ale prolog i epilog pomijam, te 9 rozkazów niech zajmie 18 taktów, dla 12MHz żeby zrobić 400kHz trzeba dać 30 taktów opóźnienia. Niech pętla wykona się 4 razy. 400kHz, bo po każdej zianie stanu SDA albo SCL procesor czeka ten sam czas, czyli 4 razy na każdy przesłany bit. Wyprowadźcie mnie z błędu, jeśli źle myślę. Nie mam pomysłów. Płytkę sprawdzałem, nie ma na niej błędów.
    Pozdrawiam zainteresowanych

    0
  • #16 21 Lip 2012 17:41
    LordBlick
    VIP Zasłużony dla elektroda

    Zamiast "cyklinować" w tym przypadku wystarczą przerwania od timera i maszyna stanów.

    0
  • #17 21 Lip 2012 17:57
    IRFP640
    Poziom 9  

    @LodrBrick: Jestem mocno początkujący, takie pojęcia czytałem w mądrych książkach, ale nie chcę porywać się na przerwania w prostym programie. Naprawdę jedynym dużym problemem dla mnie w tej chwili jest I2C. Później będzie z górki.
    Pozdrawiam

    0
  • Pomocny post
    #18 21 Lip 2012 19:30
    Andrzej__S
    Poziom 28  

    IRFP640 napisał:
    Nie mam pomysłów. Płytkę sprawdzałem, nie ma na niej błędów.

    Przecież już napisałem, gdzie masz błąd. Transmisja zaczyna się od funkcji i2c_start(), po której zapewne następuje funkcja i2c_write(). We funkcji i2c_start() generujesz warunek startu (SCL=1, SDA->0). Wchodzisz do funkcji i2c_write() i zaczynasz od ustawienia linii SDA adekwatnie do najstarszego bitu, a powinieneś zacząć od ustawienia linii SCL w stan niski:
    Kod: c
    Zaloguj się, aby zobaczyć kod

    Nie wiem czy to jedyny błąd. Proponowałem, żebyś jeszcze raz przeanalizował całą transmisję, zbocze po zboczu, czy wszystko jest po kolei.

    0
  • #19 21 Lip 2012 22:10
    IRFP640
    Poziom 9  

    Wreszcie z waszymi uwagami napisałem działający kod. Wklejam, żeby było dla inncyh.

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Wyszło przy okazji kilka innych problemów z urządzeniem (zimny lut) i moim niedopatrzeniem (adres to 0x70 a nie 0x40) ale teraz jest ok.
    Dziękuję za pomoc.

    0