logo elektroda
logo elektroda
X
logo elektroda
REKLAMA
REKLAMA
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

[Rozwiązano] [Atmega 16] [C] Nieprawidłowe działanie I2C - przy starcie I2C wysyłany dwa razy mniejszy adres

Uran. 26 Kwi 2024 12:56 375 6
REKLAMA
  • #1 21061011
    Uran.
    Poziom 12  
    Cześć,
    Postanowiłem zbudować sobie miernik poziomu CO2 w powietrzu, wykorzystując czujnik SCD41, komunikujący się po magistrali I2C. Według datasheetów Atmegi 16 oraz SCD41 powinno to być dość proste do ogarnięcia, jednak czujnik cały czas milczał jak zaklęty. Po podsłuchaniu analizatorem stanów logicznych co też się dzieje na magistrali wychodzi na to, że Atmega 16 z jakiegoś powodu przekręca nadawany adres, przesuwając go o jeden bit w prawo - z 0x62 robi się 0x31. Niby mógłbym wołać czujnik pod 0xC4 (wtedy nadaje 0x62), ale też chciałem EEPROM I2C 24C02 użyć, a on ma już adres 0xA0 - nie da się go pomnożyć razy dwa, bo się 8-bitowa zmienna "przekręca". Kod obsługujący start:
    uint8_t I2C_Start(uint8_t write_address)/* I2C start function */
    {
        uint8_t status;		/* Declare variable */
        TWCR=(1<<TWSTA)|(1<<TWEN)|(1<<TWINT); /* Enable TWI, generate START */
        while(!(TWCR&(1<<TWINT)));	/* Wait until TWI finish its current job */
        status=TWSR&0xF8;		/* Read TWI status register */
        if(status!=0x08)		/* Check weather START transmitted or not? */
        return 0;			/* Return 0 to indicate start condition fail */
        TWDR= write_address;		/* Write SLA+W in TWI data register */
       TWCR=(1<<TWEN)|(1<<TWINT);	/* Enable TWI & clear interrupt flag */
       while(!(TWCR&(1<<TWINT)));	/* Wait until TWI finish its current job */
        status=TWSR&0xF8;		/* Read TWI status register */	
       if(status==0x18)		/* Check for SLA+W transmitted &ack received */
        return 1;			/* Return 1 to indicate ack received */
        if(status==0x20)		/* Check for SLA+W transmitted &nack received */
        return 2;			/* Return 2 to indicate nack received */
        else
        return 3;			/* Else return 3 to indicate SLA+W failed */
    }
    

    Kod obsługujący wysyłanie
    
    uint8_t I2C_Write(uint8_t data)	/* I2C write function */
    {
        uint8_t status;		/* Declare variable */
        TWDR=data;			/* Copy data in TWI data register */
        TWCR=(1<<TWEN)|(1<<TWINT);	/* Enable TWI and clear interrupt flag */
        while(!(TWCR&(1<<TWINT)));	/* Wait until TWI finish its current job */
        status=TWSR&0xF8;		/* Read TWI status register */
        if(status==0x28)		/* Check for data transmitted &ack received */
        return 0;			/* Return 0 to indicate ack received */
        if(status==0x30)		/* Check for data transmitted &nack received */
        return 1;			/* Return 1 to indicate nack received */
        else
        return 2;			/* Else return 2 for data transmission failure */
    }


    Kod wziąłem stąd: https://www.electronicwings.com/avr-atmega/atmega1632-i2c więc zakładam że powinien działać, no i zgadza się z przykładami z noty katalogowej Atmegi. Czyżby powodem dziwnego działania była niska częstotliwość zegara (1,8432 MHz) ?
  • REKLAMA
  • Pomocny post
    #2 21061123
    oscil1
    Poziom 20  
    Uran. napisał:
    wychodzi na to, że Atmega 16 z jakiegoś powodu przekręca nadawany adres


    Jak się nie czyta dokumentacji tylko przegląda po łebkach - to tak to jest. Jak wysyłasz adres to bit 0 w TWDR jest bitem kierunku a bity 1-7 to adres. Twój adres EEPROM jest już 8 bitowy :) bo tak to producent w DSie zrobił (uwzględnił to że bit 0 jest kierunkiem)

    [Atmega 16] [C] Nieprawidłowe działanie I2C - przy starcie I2C wysyłany dwa razy mniejszy adres

    Inni producenci po prostu podają 7 bitowy adres, który musisz sobie przesunąć w lewo o jeden bit. Tak masz podany adres w DS-ie czujnika co2

    Nie ma reguły, każdy producent podaje jak chce - trzeba dokładnie czytać dokumentację.

    Uran. napisał:
    Kod wziąłem stąd: https://www.electronicwings.com/avr-atmega/atmega1632-i2c więc zakładam że powinien działać

    Założenie całkowicie błędne. Kod i wiedza w internecie niestety w swojej zdecydowanej większości ma bardzo małą wartość.
  • REKLAMA
  • #3 21061425
    Uran.
    Poziom 12  
    Ano racja, sporo rzeczy w internecie jest oszukanych bądź w inny sposób kiepskich, założyłem jednak (jak widać błędnie) że słowo "standard" w określeniu do standardu I2C coś znaczy, ale widać że się pomyliłem. Dzięki za podpowiedź, teraz już i 24C02 i SCD41 są wykrywane przez Atmegę - po wywołaniu 24C02 adresem 0xA0 oraz SCD41 adresem 0xC4 zwracane jest ACK oraz analizator stanów logicznych pokazuje adres 0x50 i 0x62 (odpowiednio dla 24C02 i SCD41). Pozostał problem jednak że SCD41 zwraca mi bzdury, muszę się wgryźć w temat głębiej. Tak więc wielkie dzięki za podpowiedź :D

    Edit: No i nadal nie nadaje sygnału ACK po wysłaniu mu polecenia odczytu, a w nocie katalogowej narysowano że powinien go wysyłać :
    Diagram przedstawiający sekwencje komend I2C dla SCD4x. Zrzut ekranu z analizatora stanów logicznych przedstawiający sygnał I2C z adresem 0xC4.
  • Pomocny post
    #4 21061503
    oscil1
    Poziom 20  
    Uran. napisał:
    że słowo "standard" w określeniu do standardu I2C coś znaczy


    Znaczy, tylko po prostu ktoś podaje z bitem kierunku a inny nie. Generalnie w standardzie adres jest 7 (albo 10) bitowy a kierunek to już jest dopiero na magistrali i nie ma nic wspólnego z adresem.

    Pokaż całą sekwencję. Bo na tym masz ze musisz:

    1. Najpierw zrobić zapis 16 bitowej komendy albo adresu
    2. Następnie zrobić repeated START z adresem i R (czyli LSBit = 0)
    3. I dopiero wtedy czytać dane z czujnika
  • REKLAMA
  • #5 21061580
    Uran.
    Poziom 12  
    Tak więc najpierw, przy uruchomieniu, nadaję czujnikowi taką sekwencję:
    I2C_Start(0xC4);
    I2C_Write(0x21);
    I2C_Write(0xB1);
    I2C_Stop();

    która moim zdaniem powinna realizować wysłanie "command sequence" o wartości 0x21B1, co powinno włączyć odczyt co 5 sekund. O ile zwykle (screenshot z wcześniej więcej się nie powtórzył, mimo kilkukrotnego włączenia i wyłączenia układu) zwraca ACK po drugim bajcie (0xB1), to nie zawsze tak robi.
    Sam odczyt pomiarów wygląda tak:
    I2C_Start(0xC4);
    I2C_Write(0xEC);
    I2C_Write(0x05);
    _delay_ms(5); //aby czujnik zdążył przeliczyć odpowiedź
    I2C_Repeated_Start(0xC4);

    SCD41[0]=I2C_Read_Ack();
    SCD41[1]=I2C_Read_Ack();
    SCD41[2]=I2C_Read_Ack();
    SCD41[3]=I2C_Read_Ack();
    SCD41[4]=I2C_Read_Ack();
    SCD41[5]=I2C_Read_Ack();
    SCD41[6]=I2C_Read_Ack();
    SCD41[7]=I2C_Read_Ack();
    SCD41[8]=I2C_Read_Nack();
    I2C_Stop();
    i wywołuję go raz na 8 sekund. Analizator stanów logicznych pokazuje niestety że czujnik odpowiada... swoim adresem i brakiem ACK po powtórzonym starcie.
    Edit: sprawdziłem co się dzieje na zasilaniu - czujnik jak mierzy to pobiera spory prąd raz na 5s, więc powinno lekko zasilanie przysiadać - i faktycznie raz na 5s pojawia się prostokątny sygnał z amplitudą ok. 10 mV (w sensie 5V przysiada o jakieś 10 mV) na zasilaniu czujnika, czyli chyba pomiar działa...
    Edit 2: Aaaaj, ja gamoń ! Twoja sugestia że LSbit=1 oznacza tak naprawdę, że repeated start ma być wywołany od adresu 0xC5 - chyba w AVRach w takim razie w adresie ostatnia cyfra 0 oznacza ramkę "write", a ostatnia cyfra 1 oznacza "read", czyli adres 0x62 w nocie SCD41 oznacza 0xC4 dla nadawania i 0xC5 dla odbioru :D ! Teraz czujnik coś pokazuje sensownego ! Wielkie dzięki za pomoc :D !
  • REKLAMA
  • Pomocny post
    #6 21061914
    tmf
    VIP Zasłużony dla elektroda
    Uran. napisał:
    chyba w AVRach w takim razie w adresie ostatnia cyfra 0 oznacza ramkę "write", a ostatnia cyfra 1 oznacza "read", czyli adres 0x62 w nocie SCD41 oznacza 0xC4 dla nadawania i 0xC5 dla odbioru

    Nie w AVR tylko ogólnie w I2C. Najmniej znaczący bit określa typ operacji zapis/odczyt. 7 bardziej znaczących bitów to adres urządzenia. Autorzy not urządzeń I2C stosują różne konwencje - niektórzy podają po prostu 7-bitowy adres - musisz go przesunąć o jeden bit w lewo i określić typ operacji. Inni podają dla urządzenia dwa 8-bitowe adresy - jeden do zapisu, jeden do odczytu - jak łatwo się domyślić różnią się one wartością najmniej znaczącego bitu. Niezależnie od konwencji, finalny adres jest zawsze taki sam.
  • #7 21067487
    Uran.
    Poziom 12  
    Tak więc rady Kolegów okazały się pomocne - faktycznie po doczytaniu dokumentacji wyszło że zero na końcu to zapis, a jeden - odczyt - nie wiem skąd mi się przywidziało że jest odwrotnie (wniosek: nie czytać dokumentacji będąc mocno zmęczonym :D) Jeszcze raz dziękuję :D
REKLAMA