TWI (I2C) interfejs Atmega328

TWI (I2C) interfejs Atmega328

Wątek przeniesiony 2018-11-08 11:16 z C/C++ przez Marooned.

Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

Potrzebuję pomocy. Zaprogramowałem Atmege328 z myślą o testach interfejsu TWI (I2C). Hardware'owo linie interfejsu (SCL, SDA) mam przez rezystory podciągnięte do +5V i cały procek tymże napięciem zasilam.
Pytanie brzmi dlaczego PB0 nie clear'uje się, innymi słowy : dlaczego procesor ani razu nie wchodzi w procedurę obsługi przerwania od TWI jeśli przecież ma moduł włączony, poprawnie zainicjalizowany a przerwanie od tego jest załączone.
Efekt powinien moim zdaniem być taki że po kliknięciu przycisku podpiętego do PB5 transmisja powinna się rozpocząć a prędzej czy później przerwanie powinno być odpalone.

Kopiuj

#include <avr/io.h>
#include <avr/interrupt.h>

/* define slave (MPU6050) address as 0x68 because AD0 is low */
#define SLAVE_ADDRESS (0x68)
#define WHO_AM_I (0x75)

volatile enum state
{
    IDLE,
    START_BIT_TRANSMIT,
    SLAVE_ADDRESS_TRANSMIT,
    DATA_TRANSMIT
} sm;

void TimerInit()
{
    /* overflow interrupt enable and start timer on frequency 8MHz / 1024 */
    TIMSK0 |= (1 << TOIE0);
    TCCR0B |= (1 << CS00) | (1 << CS02);
}

void TwiInit()
{
    /* 100 kHz frequency of TWI and enable TWI module and interrupt from them */
    TWBR = 8;
    TWCR = (1 << TWEN) | (1 << TWIE);
}

/* TWI interrupt service routine */
ISR(TWI_vect)
{
    PORTB &= ~(1 << PB0);

    switch (sm)
    {
        case IDLE:
        {
            break;
        }

        case START_BIT_TRANSMIT:
        {
            /* clear start bit manually and prepare slave address SLA+W */
            TWCR &= ~(1 << TWSTA);
            TWDR = (SLAVE_ADDRESS << 1);
            TWCR |= (1 << TWINT);

            if ((TWSR == 0x08) || (TWSR == 0x10))
            {
                sm = SLAVE_ADDRESS_TRANSMIT;
            }
            break;
        }

        case SLAVE_ADDRESS_TRANSMIT:
        {
            /* prepare data */
            TWDR = WHO_AM_I;
            TWCR |= (1 << TWINT);

            if ((TWSR == 0x18) || (TWSR == 0x20))
            {
                sm = DATA_TRANSMIT;
            }
            break;
        }

        case DATA_TRANSMIT:
        {
            /* prepare stop bit */
            TWCR |= (1 << TWINT) | (1 << TWSTO);

            if ((TWSR == 0x28) || (TWSR == 0x30))
            {
                sm = IDLE;
            }
            break;
        }

        default:
        {
            break;
        }
    }
}

/* timer overflow interrupt service routine */
ISR(TIMER0_OVF_vect)
{
    PORTD ^= (1 << PD7);
}

int main(void)
{
    /* diodes output */
    DDRB |= (1 << PB0);
    DDRD |= (1 << PD7);

    /* diodes turn off */
    PORTB |= (1 << PB0);
    PORTD |= (1 << PD7);

    /* pull up resistor on PD5 input switch */
    PORTD |= (1 << PD5);

    TimerInit();
    TwiInit();

    sei();

    while (1)
    {
        if (!((PIND >> PD5) & 1))
        {
            if (sm == IDLE)
            {
                sm = START_BIT_TRANSMIT;
                TWCR |= (1 << TWSTA) | (1 << TWINT);
            }
        }
    }
}

edytowany 1x, ostatnio: Adamos19
0

Mikroklocki mogła robić przerwania tylko na określonych portach i chyba nie są to za wysokie tak na moje oko.

Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

Ciekawe co prawisz... Mógłbyś rozwinąć swoją wypowiedź i napisać ją pełnym zdaniem bo przyznać muszę że nic z niej nie rozumiem a bardzo chciałbym. :)

0

Hmm, kiedyś robiłem coś na mikrokontrolerze i tam max 2 porty przerwania obsługiwały i w specyfikacji mirokontrolera było napisane, które to obsługują.

W ogóle u ciebie po wykonaniu przerwania jest xorowane coś.
Taki xor zmienia wartość, ale takie coś powoduje, że co drugie wywołanie jest takie same przy pętli.
switch może być cały czas zależny od pb0.

Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

Wszystko testuję również oscyloskopem i jestem pewien że to przerwanie nie odpala się.
Co masz na myśli pisząc: "i tam max 2 porty przerwania obsługiwały...", albo ja polskiego nie znam albo to nie jest po polsku w ogóle...
Zmieniłem XORa w przerwaniu od TWI na clearowanie:

Kopiuj
PORTB &= ~(1 << PB0);

Niestety dioda podłączona katodą do mikroklocka nie zapaliła się wcale a to świadczy o tym że to przerwanie się nie odpaliło (a powinno!).

0

A nie możesz zamiast tego TWI_vect podać INT0 INT1 tak jak się normalnie podaje, na którym porcie przerwanie ma wystąpić w ISR.

Chodź może oznacza to to samo, ale lepiej te proste nazewnictwo wykorzystać.

Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

Przerwanie od TWI to całkiem inny rodzaj przerwania niż przerwanie od zmiany poziomu czy zbocza na wejściu cyfrowym mikrokontrolera.
Procesor posiada masę różnych źródeł przerwań a nie tylko przerwania od INT0 czy INT1, między innymi przerwanie od wykonania operacji przez moduł TWI który wykorzystuje linie SCL i SDA do transmisji cyfrowej po protokole I2C (nazwa Atmelowska to TWI ale standard ten sam).
Ty mi tu piszesz na temat INT0 i INT1 ale to nie na temat, mimo wszystko dzięki za chęć pomocy.
Polecam przeczytać rozdział dokumentacji o nazwie: "Interrupts", znajdziesz tam pełną listę źródeł przerwań dla danego typu procesora.

edytowany 1x, ostatnio: Adamos19
Sparrow-hawk
  • Rejestracja:prawie 13 lat
  • Ostatnio:3 miesiące
  • Lokalizacja:Katowice
  • Postów:189
0

Dawno nie urzywałem TWI w AVR, tym bardziej z przerwaniami ;-), ale jak patrzę na twój kod vs nota katalogowa, to widzę, że w nocie przy każdym zapisie do rejestru TWCR ustawiany jest bit TWEN.

Bit 2 – TWEN: TWI Enable
The TWEN bit enables TWI operation and activates the TWI interface.

edytowany 1x, ostatnio: Sparrow-hawk
Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

Bit TWEN jest ustawiony na początku w TwiInit() i nic już go nie kasuje. Jestem pewien że on jest ustawiony i że dalej już nie jest konieczne ponowne jego ustawianie bo nic go nie kasuje. Mimo to sprawdziłem jak to wszystko działa jak do każdej instrukcji z TWCR-em dodałem | (1 << TWEN).
Efekt ten sam, przerwanie nie odpala się wcale. Może ja mam procesor popsuty, czy byłby ktoś łaskaw sprawdzić to u siebie z tym kodem?
Do PB0 i PD7 podpinamy katodami diody (anody przez rezystory do plusa), do PD5 przycisk, flash'ujemy i po uruchomieniu naciskamy przycisk i sprawdzamy czy dioda podłączona do PB0 zapaliła się.

Sparrow-hawk
  • Rejestracja:prawie 13 lat
  • Ostatnio:3 miesiące
  • Lokalizacja:Katowice
  • Postów:189
0

Wrzuciłem do Atmega168PB, (Zestaw XPlained Mini) i wygląda na to że kod działa.

Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

Ciekawe co zatem stało się z moim prockiem jeśli licznik i inne przerwania w nim działają, działają porty we/wy, działają inne peryferia a procesor nie chce odpalać przerwania od TWI, być może uszkodzeniu uległo właśnie TWI w czasie testów (może coś przez przypadek zwarłem nie tam gdzie trzeba itp itd).

Czy jest możliwość aby uszkodzić procesor poprzez dotyk gołymi rękami jego nóżek (elektrostatyczne ładunki) ?

Sparrow-hawk
Mało prawdopodobne. Jaki masz oscylator?
Adamos19
Wewnętrzny ustawiony na 8MHz bez podziału. Dodam że dioda obsługiwana w przepełnieniu timera sobie żywo migocze...
Sparrow-hawk
  • Rejestracja:prawie 13 lat
  • Ostatnio:3 miesiące
  • Lokalizacja:Katowice
  • Postów:189
0

Tak w ogóle to w pliku utils/twi.h masz parę przydatnych funkcji i definicji do obsługi TWI. Ponadto twoja maszyna stanów nie będzie działać. Raczej powinno to wyglądać mniej więcej tak:

Kopiuj
/* TWI interrupt service routine */
ISR(TWI_vect)
{
  switch (sm)
  {
    case IDLE:
      break;
    case START_BIT_TRANSMIT:
      /* clear start bit manually and prepare slave address SLA+W */
      if ((TWSR & 0xF8) == TW_START) {
        TWDR = (SLAVE_ADDRESS);
        TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWIE);
        sm = SLAVE_ADDRESS_TRANSMIT;
      } else 
        sm = ERROR;
      break;
    
    case SLAVE_ADDRESS_TRANSMIT:
      /* prepare data */
      if ((TWSR & 0xF8) == TW_MT_SLA_ACK) {
        
        TWDR = WHO_AM_I;
        TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWIE);
        sm = DATA_TRANSMIT;
      } else {
        sm = ERROR;
       
      }
    break;
    
    case DATA_TRANSMIT:
      /* prepare stop bit */
      if ((TWSR & 0xF8) == TW_MT_DATA_ACK) {
        TWCR = (1 << TWINT) | (1 << TWSTO) | (1 << TWEN);
        sm = IDLE;
      } else {
        sm = ERROR;
      }
    break;

    case ERROR:
      while (1) {
        PORTB ^= (1 << PB5);
        _delay_ms(500);
      }
      break;
    
    default:
    {
      break;
    }
  }
}

Co ciekawe zapis do rejestru TWCR z maskowaniem poszczególnych bitów sprawiał, że u mnie transmisja kończyła się na wysłaniu adresu urządzenia slave na magistralę.

Adamos19
To że u Ciebie transmisja kończyła się na wysłaniu adresu urządzenia slave to może dlatego że warunkiem przejścia do wysyłania danych było odebranie potwierdzenia ACK a takiego pewnie nie odbierał bo nie masz takiego slave'a na magistrali zapewne... poza tym dlaczego twierdzisz że moja maszyna stanów nie ma prawa działać?
Sparrow-hawk
Fakt nie mam, dlatego użyłem swojego czujnika BMP180 (I zmieniłem adresy SLAVE I WHO_I_AM) ;-)
Adamos19
ok, za moment zafleszuję klocka tym Twoim kodem i zobaczę czy są jakieś postępy...
Adamos19
Dałeś taką instrukcję: TWDR = (SLAVE_ADDRESS); ale powinieneś wiedzieć że na ostatnim biciku rejestru TWDR jest wskaźnik czy będziemy zapisywać do slavea czy z niego odczytywać. Dlatego ja wolę zrobić przy zapisie tak: TWDR = (SLAVE_ADDRESS << 1); a przy odczycie tak: TWDR = (SLAVE_ADDRESS << 1) | (1 << 0);i makrodefinicję SLAVE_ADDRESS dać tak naprawdę w zakresie adresowania protokołu tj. 0-127.
Sparrow-hawk
  • Rejestracja:prawie 13 lat
  • Ostatnio:3 miesiące
  • Lokalizacja:Katowice
  • Postów:189
0

A jaki ma sens sprawdzenie TWSR zaraz po resecie flagi TWINT?

Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

No np. taki że flaga kasuje "źródło przerwania" natomiast nie kasuje rejestru TWSR z bieżącej wartości. Nigdzie nie znalazłem informacji że należy wpierw sprawdzić status a dopiero potem skasować jedynką TWINT.
Jeśli chodzi o zapis do TWDR to już inna sprawa ale tutaj także błędu nie robię bo zanim napisze jedynkę do TWINT to ustawiam TWDR.

Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

Zafleszowałem takim kodem i dalej przerwanie nie chce się odpalić ani razu! Mam już dosyć. Podejrzewam uszkodzenie procesora... pomocy...

Kopiuj
/*
 * main.c
 *
 *  Created on: 30 wrz 2017
 *      Author: Adam
 */


#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

/* define slave (MPU6050) address as 0x68 because AD0 is low */
#define SLAVE_ADDRESS (0x68)
#define WHO_AM_I (0x75)

volatile enum state
{
    IDLE,
    START_BIT_TRANSMIT,
    SLAVE_ADDRESS_TRANSMIT,
    DATA_TRANSMIT,
    ERROR
} sm;

void TimerInit()
{
    /* overflow interrupt enable and start timer on frequency 8MHz / 1024 */
    TIMSK0 |= (1 << TOIE0);
    TCCR0B |= (1 << CS00) | (1 << CS02);
}

void TwiInit()
{
    /* 100 kHz frequency of TWI and enable TWI module and interrupt from them */
    TWBR = 32;
    TWCR = (1 << TWEN) | (1 << TWIE);
}

/* TWI interrupt service routine */
ISR(TWI_vect)
{
  switch (sm)
  {
    case IDLE:
      break;
    case START_BIT_TRANSMIT:
      /* clear start bit manually and prepare slave address SLA+W */
      if ((TWSR & 0xF8) == 0x08) {
        TWDR = (SLAVE_ADDRESS);
        TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWIE);
        sm = SLAVE_ADDRESS_TRANSMIT;
      } else
        sm = ERROR;
      break;

    case SLAVE_ADDRESS_TRANSMIT:
      /* prepare data */
      if ((TWSR & 0xF8) == 0x18) {

        TWDR = WHO_AM_I;
        TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWIE);
        sm = DATA_TRANSMIT;
      } else {
        sm = ERROR;

      }
    break;

    case DATA_TRANSMIT:
      /* prepare stop bit */
      if ((TWSR & 0xF8) == 0x28) {
        TWCR = (1 << TWINT) | (1 << TWSTO) | (1 << TWEN);
        sm = IDLE;
      } else {
        sm = ERROR;
      }
    break;

    case ERROR:
      while (1) {
        PORTB ^= (1 << PB0);
        _delay_ms(500);
      }
      break;

    default:
    {
      break;
    }
  }
}


/* TWI interrupt service routine */
/*ISR(TWI_vect)
{
    PORTB &= ~(1 << PB0);

    switch (sm)
    {
        case IDLE:
        {
             clear interrupt flag
            TWCR |= (1 << TWINT);
            break;
        }

        case START_BIT_TRANSMIT:
        {
             clear start bit manually and prepare slave address SLA+W
            TWCR &= ~(1 << TWSTA);
            TWDR = (SLAVE_ADDRESS << 1);
            TWCR |= (1 << TWINT);

            if ((TWSR == 0x08) || (TWSR == 0x10))
            {
                sm = SLAVE_ADDRESS_TRANSMIT;
            }
            else
            {
                sm = IDLE;
            }
            break;
        }

        case SLAVE_ADDRESS_TRANSMIT:
        {
             prepare data
            TWDR = WHO_AM_I;
            TWCR |= (1 << TWINT);

            if ((TWSR == 0x18) || (TWSR == 0x20))
            {
                sm = DATA_TRANSMIT;
            }
            else
            {
                sm = IDLE;
            }
            break;
        }

        case DATA_TRANSMIT:
        {
             prepare stop bit
            TWCR |= (1 << TWINT) | (1 << TWSTO);

            if ((TWSR == 0x28) || (TWSR == 0x30))
            {
                sm = IDLE;
            }
            else
            {
                sm = IDLE;
            }
            break;
        }

        default:
        {
            break;
        }
    }
}*/

/* timer overflow interrupt service routine */
ISR(TIMER0_OVF_vect)
{
    //PORTD ^= (1 << PD7);
}

int main(void)
{
    /* diodes output */
    DDRB |= (1 << PB0);
    DDRD |= (1 << PD7);

    /* diodes turn off */
    PORTB |= (1 << PB0);
    PORTD |= (1 << PD7);

    /* pull up resistor on PD5 input switch */
    PORTD |= (1 << PD5);

    TimerInit();
    TwiInit();

    sei();

    while (1)
    {
        if (!((PIND >> PD5) & 1))
        {
            if (sm == IDLE)
            {
                PORTD ^= (1 << PD7);
                sm = START_BIT_TRANSMIT;
                TWCR = (1 << TWSTA) | (1 << TWINT) | (1 << TWEN) | (1 << TWIE);
            }
        }
    }
}

Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

No więc wziąłem i zrobiłem próby dodatkowo na dwóch procesorach typu Atmega8. jak wiadomo cały pinout tych procków jest ten sam co dla Atmegi328, zmiany wymagały nazwy dwóch rejestrów:

  1. Zamiast TCCR0B mamy TCCR0
  2. Zamiast TIMSK0 mamy TIMSK

Efekt ten sam, przerwanie nie odpala się, maszyna stanów utyka dokładnie na stanie START_BIT_TRANSMIT który to ustawiany jest po naciśnięciu przycisku. Nie wiem co z tym dalej zrobić, bo w tym momencie jestem skłonny odrzucić pogląd o popsutym klocku.
Ktoś jest w stanie coś poradzić ?

edytowany 2x, ostatnio: Adamos19
Sparrow-hawk
  • Rejestracja:prawie 13 lat
  • Ostatnio:3 miesiące
  • Lokalizacja:Katowice
  • Postów:189
1
  1. Jesteś w stanie zobaczyć co dzieje się na pinach SDA & SCL analizatorem logicznym?
  2. Czy możesz uruchomić komunikację TWI bez korzystania z przerwania?
  3. Jaki masz ustawiony zegar mikroprocesora?
  4. Ten czujnik na I2C to jakiś moduł? Ma podciąganie liniii SDA & SCL?
  5. Próbowałeś odpalić kod z odłączonym modułem?

Poza tym jest nota: AVR315: Using the TWI Module as I2C Master - Atmel, z którą możesz się zapoznać.

PS. Wieczorem będę miał ATmega328p, więc mogę jeszcze raz na to spojrzeć ;-)

Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0
  1. Na pinach SDA i SCL nie dzieje się nic - tzn. one są podciągnięte do Vcc i obserwuję to oscyloskopem ustawionym na triggerowanie tych linii - jakiekolwiek zbocze opadające wyzwala mi zatrzymanie a do takiego dochodzi tylko wówczas gdy zdejmuję zasilanie z układu :/
  2. Sprawdzę dziś i dam znać
  3. Zegar to wewnętrzny oscylator 8MHz
  4. Podciąganie linii realizuję sobie hardwareowo - dałem tam oporniki 2k7, aktualnie jest tak że czujnik nie jest podpięty pod układ więc mikroprocesor pracuje sobie jeśli chodzi o ten interfejs tylko z tymi rezystorkami
  5. Dopóki to nie ruszy nie ma sensu dawać tam tego czujnika bo on i tak nic nie odpowie a mogłoby mu ta sytuacja jeszcze zaszkodzić bo jeśli coś jest upalone to może zachować się nie po myśli protokołu
Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

Zrobiłem próby związane z TWI bez wykorzystania przerwania i efekt jest mizerny, to znaczy dioda podłączona do PD7 po naciśnięciu przycisku zapaliła się i nie zgasła co świadczy o tym że program utknął na pierwszym while (!(TWCR & (1 << TWINT))); co z kolei świadczy o tym że TWI nie chce wykonać operacji startu. Potwierdzone jest to oscyloskopem - linie SCL, SDA ani drgną a ich stan jest ciągle wysoki (podciągnięte hardware'owo do Vcc). Pomocy proszę... :/

Kopiuj
#include <avr/io.h>
#include <avr/interrupt.h>

/* define slave (MPU6050) address as 0x68 because AD0 is low */
#define SLAVE_ADDRESS (0x68)
#define WHO_AM_I (0x75)

volatile enum state
{
    IDLE,
    START_BIT_TRANSMIT,
    SLAVE_ADDRESS_TRANSMIT,
    DATA_TRANSMIT,
    ERROR
} sm;

void TimerInit()
{
    /* overflow interrupt enable and start timer on frequency 8MHz / 1024 */
    TIMSK |= (1 << TOIE0);
    TCCR0 |= (1 << CS00) | (1 << CS02);
}

void TwiInit()
{
    /* 100 kHz frequency of TWI module */
    TWBR = 32;

    /* enable TWI module without interrupt enable */
    TWCR = (1 << TWEN);
}

/* timer overflow interrupt service routine */
ISR(TIMER0_OVF_vect)
{

}

int main(void)
{
    /* diodes output */
    DDRB |= (1 << PB0);
    DDRD |= (1 << PD7);

    /* diodes turn off */
    PORTB |= (1 << PB0);
    PORTD |= (1 << PD7);

    /* pull up resistor on PD5 input switch */
    PORTD |= (1 << PD5);

    TimerInit();
    TwiInit();

    sei();

    while (1)
    {
        /* if switch pressed */
        if (!((PIND >> PD5) & 1))
        {
            if (sm == IDLE)
            {
                /* turn on the diode and request start TWI operation */
                PORTD &= ~(1 << PD7);
                sm = START_BIT_TRANSMIT;
                TWCR |= (1 << TWSTA) | (1 << TWINT);

                /* waiting for set TWINT - finish requested TWI operation */
                while (!(TWCR & (1 << TWINT)));

                PORTD |= (1 << PD7);

                /* request next TWI operation - send SLA+W */
                sm = SLAVE_ADDRESS_TRANSMIT;
                TWDR = (SLAVE_ADDRESS << 1);
                TWCR &= ~(1 << TWSTA);
                TWCR |= (1 << TWINT);

                /* waiting for set TWINT - finish requested TWI operation */
                while (!(TWCR & (1 << TWINT)));

                /* request next TWI operation - send data */
                sm = DATA_TRANSMIT;
                TWDR = WHO_AM_I;
                TWCR |= (1 << TWINT);

                /* waiting for set TWINT - finish requested TWI operation */
                while (!(TWCR & (1 << TWINT)));

                /* request next TWI operation - send stop bit */
                sm = IDLE;
                TWCR |= (1 << TWINT) | (1 << TWSTO);
            }
        }
    }
}
Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

Zauważyłem teraz coś bardzo dziwnego. Po migracji na Atmege328 (poprzedni kod wykonywał się na Atmedze8) zauważyłem że po naciśnięciu przycisku, dioda się zapala i gaśnie.... uwaga - po około 3-5 sekundach... to jeszcze nic... najlepsze jest to że po około kolejnych kilku sekundach SDA idzie w dół po czym po ściśle nieokreślonym czasie wraca na high. Masakra. Chyba mam te procki upalone. Dziś jeszcze wybieram się do kolegi po zestaw arduino mega168 aby móc to wszystko jeszcze raz przetestować... A Was koledzy proszę o porady jakieś, może ktoś miał takie dziwne doświadczenia z modułem TWI...? Gorąco pozdrawiam!

Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

Właśnie zrobiłem próby na Arduino Uno R3 z udziałem Atmega328 i wszystko gra. Ten sam kod działa zupełnie inaczej niż w czasie poprzednich testów, działa bez zarzutu, po kliknięciu przycisku na oscyloskopie mam ładne przebiegi potwierdzające wysyłanie po I2C.
Zamykam temat bo chyba nieświadomie uszkodziłem klocki, kurcze pieczone :/ ale ze mnie gapa, nawet nie wiem kiedy mogłem to zrobić ale widzicie - wszystko niby w nich działa ale nie działa TWI i to nie tak że w ogóle - wariuje na maxa, zachowuje się nieprzewidywalnie.
Pozdrawiam i dzięki za pomoc!

edytowany 1x, ostatnio: Adamos19
Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

Tu dzieje się coś naprawdę wyjątkowego: mianowicie po zamianie procesora w podstawce w zestawie Arduino na ten niby upalony kod również działa poprawnie. Oznacza to że na płytce stykowej robiłem coś nie tak ale cholera wie co. Miałem przecież podciągnięte wszystko. Podejrzewam że filtrowanie miałem nie tak jak trzeba - zasilanie szło z przejściówki ATB USB RS232 natomiast koło procka miałem tylko 100nF i nic więcej. Nie chce mi się już tego dokładniej analizować co było przyczyną. Pozdrawiam!

WY
Wypierdzisty
Lepiej przeanalizuj, bo nie dostaniesz doświadczenia za zadanie. A za jakiś czas quest może być niedostępny...
Sparrow-hawk
Super, że działa. Pamiętaj, że takie procki mimo wszystko bardzo trudno ubić, więc nie dziwię się, że jednak okazał się sprawny ;-)
Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
1

Panowie dziękuję. Postanowiłem że tego questa tak nie zostawię jednak... i złożyłem wszystko ponownie na stykówce ale dodałem kondensator 33uF dodatkowo i o dziwo wszystko działa! Nie wiem jednak co było nie tak bo jak wyciągam tego 33uF to to tak samo - działa jak należy. Widocznie miałem jakiegoś bubla na stykówce którego nie dostrzegłem. Panowie sory. Najgorsze że nie jestem się w stanie teraz tego dowiedzieć bo poprzedni układ na stykówce został zastąpiony nowym (czyt. został zdemontowany). :) Dzięki Panowie za porady. Każdy z testowanych procków żyje i ma się dobrze! :)

Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

Mój driver działa już jak należy i komunikuje się z czujnikiem MPU6050 (akcelerometr + żyroskop).
Przyspieszenia są odczytywane poprawnie ;)
Mam jedno pytanie do znawców tematyki takich czujników albo do osób które znają temat komunikacji od podstaw, jak to jest, jeśli taki czujnik załóżmy na przyspieszenie XOUT ma dwa rejestry jednobajtowe a ja je protokołem I2C muszę czytać jeden po drugim przecież to czy może wystąpić sytuacja że czytam sobie jeden rejestr a tu bach i czujnik zapisuje mi drugi inną wartością. Chodzi mi o to czy może wystąpić taka sytuacja że powiedzmy ja sobie chcę odczytać przyspieszenie x (czyli przeczytać dwa rejestry) i niech w nich będzie takie coś że w pierwszym 0x01 a w drugim 0xFF, czytam więc 0x01 a tu nagle przyspieszenie się zmieniło i to "o bardzo malutko" bo teraz wynosi załóżmy w pierwszym 0x02 i w drugim 0x00, więc odczytam drugi rejestr jako 0x00. Nietrudno się domyślić że popełniłem znaczny błąd bo około 255. Czy taka sytuacja może mieć miejsce, i co musiałbym zrobić aby jej uniknąć?

MO
  • Rejestracja:prawie 10 lat
  • Ostatnio:około 21 godzin
  • Lokalizacja:Tam gdzie jest (centy)metro...
1

Bazować na mechanizmach danego czujnika. Z zasady posiadają one albo mechanizmy synchronizacyjne np. ustawianie flag zatrzaskujących pomiar, wypełnianie buforów FIFO poprawnymi danymi, rejestr-cień który zapisywany jest stale i propagowany do rejestru odczytywanego w całości.
W przypadku MPU masz....

The accelerometer measurement registers, along with the temperature measurement registers,
gyroscope measurement registers, and external sensor data registers, are composed of two sets of
registers: an internal register set and a user-facing read register set.
The data within the accelerometer sensors’ internal register set is always updated at the Sample
Rate. Meanwhile, the user-facing read register set duplicates the internal register set’s data values
whenever the serial interface is idle. This guarantees that a burst read of sensor registers will read
measurements from the same sampling instant. Note that if burst reads are not used, the user is
responsible for ensuring a set of single byte reads correspond to a single sampling instant by
checking the Data Ready interrupt.


Każdy problem w informatyce można rozwiązać, dodając kolejny poziom pośredniości,z wyjątkiem problemu zbyt dużej liczby warstw pośredniości — David J. Wheeler
Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

Słuchajcie może to głupio brzmieć ale nie potrafię sobie tego dokładnie przetłumaczyć. Szczególnie tego:

Meanwhile, the user-facing read register set duplicates the internal register set’s data values
whenever the serial interface is idle. This guarantees that a burst read of sensor registers will read
measurements from the same sampling instant. Note that if burst reads are not used, the user is
responsible for ensuring a set of single byte reads correspond to a single sampling instant by
checking the Data Ready interrupt.

Czy ktoś mógłby pomóc mi rozszyfrować ten tekst bo z pomocą tłumaczy internetowych nie bardzo mi się to powiodło...
I czy np. gdy będę odczytywał przez I2C nie po jednym bajcie czyli tak:

START SLA+W ACK REG_ADDR ACK START SLA+R ACK DATA NACK STOP (dotyczy pierwszego bajtu)
START SLA+W ACK REG_ADDR ACK START SLA+R ACK DATA NACK STOP (dotyczy drugiego bajtu)

tylko tak:

START SLA+W ACK REG_ADDR ACK START SLA+R ACK DATA ACK DATA NACK STOP (odczyt dotyczy obydwu naraz)

to czy wówczas nie będzie ryzyka tego że nie odczyta mi się to w całości z jednej próbki (sample) , dobrze to rozumiem Panowie?
Pogrubione to to co wysyła mikrokontroler (master), niepogrubionym to co wysyła slave (czyli czujnik).

edytowany 1x, ostatnio: Adamos19
Sparrow-hawk
  • Rejestracja:prawie 13 lat
  • Ostatnio:3 miesiące
  • Lokalizacja:Katowice
  • Postów:189
0

Wydaje mi się, że dobrze to rozumiesz ;-)

Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

Ale czy to znaczy że jeśli będę czytał pojedynczo bajty czyli tak:

START SLA+W ACK REG_ADDR ACK START SLA+R ACK DATA NACK STOP (dotyczy pierwszego bajtu)
START SLA+W ACK REG_ADDR ACK START SLA+R ACK DATA NACK STOP (dotyczy drugiego bajtu)

To czy wówczas jest ryzyko że mi się pierwszy odczyta z pierwszej próbki pomiarowej, a drugi z innej ?
Będzie ktoś łaskaw przetłumaczyć dokładniej fragment:

Meanwhile, the user-facing read register set duplicates the internal register set’s data values
whenever the serial interface is idle. This guarantees that a burst read of sensor registers will read
measurements from the same sampling instant. Note that if burst reads are not used, the user is
responsible for ensuring a set of single byte reads correspond to a single sampling instant by
checking the Data Ready interrupt.

Bardzo mi zależy, proszę o pomoc.

Sparrow-hawk
  • Rejestracja:prawie 13 lat
  • Ostatnio:3 miesiące
  • Lokalizacja:Katowice
  • Postów:189
1

Tak w tym przypadku jest takie ryzyko, o ile czas pomiędzy odczytami kolejnych bajtów będzie wystarczający, aby sensor uznał, że magistrala jest w stanie bezczynności (Idle).

Zwróć uwagę na ten fragment:

set’s data values whenever the serial interface is idle. This guarantees that a burst read of sensor registers will read measurements from the same sampling instant.

Gwarancję, że dane pochodzą z tego samego pomiaru daje tylko i wyłącznie odczyt w trybie burst.

edytowany 1x, ostatnio: Sparrow-hawk
Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

Właśnie rozwiałeś moje wątpliwości kolego i za to Ci dziękuję. Musiałem zdobyć taką pewność gdyż buduję alarm motocyklowy oparty o układ mikrokontroler avr 8bit + mpu6050. Przy zazbrojeniu alarmu układ zapamięta stan przyspieszeń a ich odchyłka od tych zapamiętanych wartości o epsilon będzie alarm wyzwalała, dlatego tak istotne jest abym miał pewność że dane jednego i drugiego rejestru będą pochodzić z tej samej próbki pomiarowej.
Ciekaw jestem czy dotyczy to tylko dwóch rejestrów czy np. wszystkich sześciu (bo ja w trybie burst raczej będę odczytywał wszystkie sześć naraz czyli przyspieszenie w osi X, Y i Z). Czy ktoś może napisać jeszcze jakieś informacje?

edytowany 1x, ostatnio: Adamos19
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)