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

[ATmega128A][avr-gcc] - TWI slave - uszkodzenie pamięci

rexina 29 Sie 2016 20:32 1992 23
  • #1 15899495
    rexina
    Poziom 17  
    Witajcie!

    Robię bibliotekę mającą w zamyśle być modułem do I2C slave, z użyciem atmegi 128A.
    Wyizolowałem najmniejszą możliwą część błędu który doświadczyłem, może ktoś z Was będzie miał pomysł co może być przyczyną.

    Żebyśmy wiedzieli o czym dyskusja, kod programu:

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Urządzenie to podpinam do drugiego mikrokontrolera, robiącego za I2C mastera - i mającego w założeniu wymieniać dane. W tym momencie układ będzie odpowiadał na każdy bajt wartosią 0xA2, ignorując wszystkie wysłane mu bajty.

    Jak można zauważyć, z pozoru zbędne jest porównanie
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
    jako że ta zmienna bierze z
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
    to nigdy nie powinnien wykonać się kod tej instrukcji warunkowej.
    Jednakże, po kilku/kilkunastu/więcej transferów na liniach I2C wartość zostaje zmieniona - i program wpada w if'a zawieszając procesor.

    Co ciekawe, w programie jest zmienna 'counter'. Zakomentowanie jej inkrementowania w pętli while() powoduje że błąd znika.

    Ten sam kod wgrany na ATmegę 644P działa poprawnie.

    Assembler dla zainteresowanych:
    TL;DR: dostęp do interesujących zmiennych jest atomowy.
    Kod: AVR assembler
    Zaloguj się, aby zobaczyć kod


    Opcje kompilacji:
    avr-g++ -O0 -std=gnu++1y -Wall -Wextra -Winline -mmcu=atmega128a

    Testowałem również z -O2, asm podobny, zachowanie takie samo.

    Próbowałem również za pomocą atmel studio 6.2 - wynik jest identyczny.

    Na początku obsługa TWI była na przerwaniach - indentyczne zachowanie.


    Przykład problemu:
    Program wgrany na atmegę128a - na płytce easyAVR128. Podłączony do niego drugi procesor wysyła i odbiera z niego dane.
    Po kilku transmisjach program wpada w tą pętle.
    Zrzut z analizatora logicznego (Saleae 16-ch):
    [ATmega128A][avr-gcc] - TWI slave - uszkodzenie pamięci
    [ATmega128A][avr-gcc] - TWI slave - uszkodzenie pamięci

    Dołączyłbym również plik z capturem danych z saleae - jednak jest to niedozwolone rozszerzenie załącznika. W zamian tego wrzuciłem na serwer: Klik - pobierz.

    Macie jakiekolwiek pomysły co robię źle?
    Dzięki z góry za pomoc!
  • #2 15899584
    Konto nie istnieje
    Konto nie istnieje  
  • #3 15899620
    rexina
    Poziom 17  
    Tak, ten kod nie ma sensu. Jest po to by pokazać problem.

    Owszem, nie odczytuje flag statusu. Robiłem to, w przerwaniu, ale zredukowałem ilość kodu, bo to nic nie zmienia z tego co rozumiem.

    Z datasheetu, strona 207:
    Cytat:

    In Transmit mode, TWDR contains the next byte to be transmitted. In receive mode, the TWDR
    contains the last byte received. It is writable while the TWI is not in the process of shifting a byte.
    This occurs when the TWI interrupt flag (TWINT) is set by hardware.


    Czyli dowolny zapis który wykonuje się w momencie gdy TWINT nie jest ustawiony jest ignorowany.
    Gdy jednak jest ustawiony:
    1) jest to slave receiver - zapis jest ignorowany - możemy odczytać TWDR, ale nie mamy takiej potrzeby,
    2) jest to slave transmitter - zapis ten oznacza następny bajt do wysłania na magistralę.

    Gdy zapiszemy (lub hardware zignoruje to) to następuje reset bitu TWINT (w przypadku gdy była transmisja to zatwierdzenie bajtu wpisanego do TWDR).
  • #4 15899798
    Konto nie istnieje
    Konto nie istnieje  
  • #5 15899822
    rexina
    Poziom 17  
    Zgodnie z tym co napisałem w opisie problemu - to że transmisja na liniach I2C powoduje uszkodzenie innej komórki pamięci. Bo prawem I2C nie jest zmiana wartości pamięci wewnętrznej procesora, chyba że się mylę...

    Nie wiem jak to czy odczytuje wewnątrz uC dane czy nie miało by wpływać na transmisję I2C - która działa i jest zgodna ze standardem - co widać na zrzucie z analizatora.
  • #6 15899922
    Konto nie istnieje
    Konto nie istnieje  
  • #7 15899927
    rexina
    Poziom 17  
    Dokładnie, tak być powinno.

    W którym w takim razie gdzie jest błąd że zachowuje się tak a nie inaczej?
  • #8 15899993
    Konto nie istnieje
    Konto nie istnieje  
  • #9 15900000
    rexina
    Poziom 17  
    Piotrus_999 napisał:
    cos innego w programi Ci zmienia.

    Ok dzięki.
    Gdybym miał najmniejsze podejżenia co to może być to bym się nie pytał na forum... Dlatego zamieściłem cały kod programu, łącznie z assemblerem. Nie ma żadnego odwołania do zmiennej cmd_ready_flag (adres 0x100), a jedyne przypisanie do flag_now (adres 0x103) to "000000B3 STS 0x0103,R24".

    Jako że to jest slave to nie ma "mogę" transmitować. Ja tylko zaznaczam że obsłużyłem przerwanie od sprzętu. Owszem, robiłem też tak że ustawiałem tą flagę gdy była ustawiona - to nic nie zmienia. Cytat z datasheetu to potwierdza.
  • #10 15900006
    Konto nie istnieje
    Konto nie istnieje  
  • #11 15900013
    rexina
    Poziom 17  
    Z datasheetu, strona 205:
    Cytat:

    Bit 7 – TWINT: TWI Interrupt Flag
    This bit is set by hardware when the TWI has finished its current job and expects application
    software response. If the I-bit in SREG and TWIE in TWCR are set, the MCU will jump to the
    TWI interrupt vector. While the TWINT flag is set, the SCL low period is stretched.
    The TWINT flag must be cleared by software by writing a logic one to it. Note that this flag is not
    automatically cleared by hardware when executing the interrupt routine. Also note that clearing
    this flag starts the operation of the TWI, so all accesses to the TWI Address Register (TWAR),
    TWI Status Register (TWSR), and TWI Data Register (TWDR) must be complete before clearing
    this flag.


    Obsłużenie przerwania od TWI - ustawienie bitu TWINT w rejestrze TWCR. Zeruje to tą flagę (która by oznaczała skok pod przerwanie).
    Robię to za każdym obiegiem pętli. Jako że przerwania są wyłączone to procesor nie skacze pod wektor obsługi. A moduł TWI ma informacje że może już wystawić kolejny bajt.

    Jak już mówiłem, I2C nie jest problemem - to działa. I tak, napisałem cały moduł z obsługą wszystkich przypadków. Ale go tu nie zamieszczam bo ma się nijak do problemu który przedstawiłem.
    Jednak - nie z tego powodu napisałem ten temat. Problemem jest wchodzenie w pętlę w którą w teorii procesor nie powinien nigdy wpadać.
  • #12 15900030
    Konto nie istnieje
    Konto nie istnieje  
  • #13 15900044
    rexina
    Poziom 17  
    Piotrus_999 napisał:
    Skoro nie jest i nie zamieszczasz kodu to po co pytasz?

    Zamieściłem kod w pierwszym poście - i odnośnie jego mam pytanie, również tam napisane. I nie dotyczy ono obsługi I2C. I jeszcze w żaden sposób się do niego nie odniosłeś.

    Piotrus_999 napisał:
    To co zamieściłeś jest całkowicie błedne

    Proszę, wskaż mi w którym miejscu (i potwierdź to jakoś, żebym wiedział dlaczego) kod obsługi I2C jest zły. Bo patrząc po datasheecie wszystko jest w porządku. Jeśli się nie mylę to kod ten robi za TWI slave z adresem 0x2B który będzie odpowiadał na swój adres ACK oraz przy żądaniu SLA+R za każdym bajtem będzie wysyłał 0xA2.

    Ale załóżmy na chwilę że masz rację, i obsługa I2C jest błędna. Dlaczego w takim razie analizator logiczny pokazuje że transmisja przebiega w pełni poprawnie - z ACK, z wysyłaniem odpowiednich bajtów w obie strony?


    Piotrus_999 napisał:
    nie zamieściłeś z jakiegoś powodu, to znaczy że musisz się zwrócić do wróżki.

    Owszem, nie zamieściłem go dlatego że nie o niego się pytam. Tylko dokładnie o kod który podałem w pierwszym poście.

    Piotrus_999 napisał:
    Zerowanie bitu nie jest obsłużeniem przerwania.

    Zdefiniuj "obsłużenie przerwania" gdy przerwania są wyłączone. (bit I w rejestrze SREG). Jeśli mówimy o sytuacji w której moduł procesora (tutaj TWI) czeka na kod użytkownika (datasheet strona 205):
    Cytat:

    expects application software response

    to ustawienie (w konsekwencji wyzerowanie) flagi TWINT jest dla modułu TWI niczym nierozróżnialne od tego gdybyśmy ustawili tą flagę w przerwaniu.
  • #14 15900169
    Linoge
    Poziom 27  
    Fajny przykład jak nie należy programu pisać.
    Moim zdaniem coś jeździ Ci po pamięci. Możliwe przyczyny:
    - zły wybór uc w ustawieniach projektu
    - resety uc
    - błąd w AVR studio
    Zerowanie przerwania "na pałę" jest średnim pomysłem skoro powoduje to uruchomienie szeregu innych rzeczy. Miedzy innymi pewien konflikt w dostępie do magistrali. Przejrzyj jeszcze raz notę. Wpisuj kompletne wartości rejestrów a nie tylko cząstkowe, wyłącz obsługę przerwań, przeczytaj erratę do tego uc.
  • #15 15900292
    JacekCz
    Poziom 42  
    dodam do wypowiadających się kolegów, użycie volatile które mi zapala czerwone światło w głowie.
  • #16 15900549
    rexina
    Poziom 17  
    Linoge napisał:
    Fajny przykład jak nie należy programu pisać.

    Nikt nie powiedział że należy :) To nie jest kod produkcyjny tylko żeby ułatwić debuggowanie. Dlatego pozbyłem się przerwań wszystkich.

    Linoge napisał:
    - zły wybór uc w ustawieniach projektu

    Jest OK, testowałem zarówno kompilowaniem "z palca" avr-g++, jak również Atmel Studio 6.2.

    Linoge napisał:
    - resety uc

    Dzięki, sprawdziłem - nie resetuje się (dodałem wypisywanie znaku na UART w momencie startu procka).

    Linoge napisał:
    - błąd w AVR studio

    Błędu w kompilatorze bym nie szukał, bo jak pisałem, kompilowałem to dwoma (avr-gcc-4.8.2 oraz avr-gcc-4.9.1). Prędzej w tym że jest to chiński scalak (na płytce easyAVR128). Zresztą, assembler wygląda w porządku imho.

    Linoge napisał:
    Zerowanie przerwania "na pałę" jest średnim pomysłem skoro powoduje to uruchomienie szeregu innych rzeczy.

    Mógłbyś rozszerzyć? Zerowanie flagi przerwania (TWINT) jest chyba normalną rzeczą, czy robiona w przerwaniu od TWI czy nie.

    Linoge napisał:
    Wpisuj kompletne wartości rejestrów a nie tylko cząstkowe

    Konkretnie o których przypisaniach/wypisaniach mówisz?

    Linoge napisał:
    przeczytaj erratę do tego

    Bez tego bym przecież na forum nie napisał :)

    JacekCz napisał:
    użycie volatile które mi zapala czerwone światło w głowie.

    Racja, bez volatile to ten kod zostaje usuwany przez kompilator (z -O2) i nie może się już wykonać :)
  • #17 15900689
    Konto nie istnieje
    Konto nie istnieje  
  • #18 15900792
    rexina
    Poziom 17  
    Również nie rozumiem uporu kolegi który twierdzi że mój kod jest zły.
    Proszę wskazać w którym miejscu jest błędny i dlaczego. Bo po prostu nie wiem, a nota katalogowa nie podpowiada.
    Jedyne co do tej porty usłyszałem to:

    Piotrus_999 napisał:
    Ten kod, który zamieściłeś ma prawo się tylko zawiesić.

    Piotrus_999 napisał:
    o co zamieściłeś jest całkowicie błedne

    Linoge napisał:
    Fajny przykład jak nie należy programu pisać.

    Piotrus_999 napisał:
    nie chce napisać obsługi i2c jak należy


    Żadnego merytorycznego odniesienia, czy to do noty katalogowej czy aplikacyjnej.
    I koledzy chyba dalej myślą że ja taki kod napisałem do obsługi I2C... Oraz że takiego pełnego z obsługą wszystkich nie próbowałem. Po prostu uznałem że lepiej przeanalizować kod mający 50 linijek i mający assemblera możliwego do odczytania niż taki mający linijek 200 i masę różnych przypadków. Ale co ja tam wiem.

    W porządku, zamieszczę dziś wieczorem kod z "normalną" obsługą TWI na przerwaniach, jeśli to ma tylko cokolwiek zmienić.
  • #19 15900808
    Konto nie istnieje
    Konto nie istnieje  
  • #20 15900822
    rexina
    Poziom 17  
    Asembler po to, żeby było widać co kompilator zoptymalizował, jakiego typu wstawił dostępy do rejestrów. Tak przynajmniej mi się wydaje. Ale skoro kolega twierdzi że nie musi znać opcji kompilacji/plików wynikowych żeby powiedzieć co sprzęt robi to gratuluję, ja niestety tak dużego doświadczenia w tej dziedzinie nie posiadam żeby znać wszystkie kompilatory i ich smaczki.

    Ja nie twierdzę że kompilator jest zły - dlatego dla pewności sprawdziłem na obu.
    Twierdzę że mój kod jest zły - szukam przyczyny i uzasadnienia.
  • #21 15900830
    Konto nie istnieje
    Konto nie istnieje  
  • #22 15900851
    rexina
    Poziom 17  
    Piotrus_999 napisał:
    Na pewno nie zmienia algorytmu czy wartości zmiennych "po uważaniu".

    Wydawało mi się że specyfikacja gcc mówię trochę coś innego: Link. Nie mówiąc już o optymalizacji w C++ albo LTO.
    [offtop] Nie mówiąc już o procesorach z wbudowanym pipelinem, jak np: Link.

    Piotrus_999 napisał:
    Jak stwierdziłeś że akurat to zawieszenie jest w tej pętli?

    Dwoma metodami - z włączonym serialem patrzyłem na port szeregowy, a bez niego za pomocą debuggera.


    Piotrus_999 napisał:
    Wez sobie jakiś gotowy projekt pracy jako slave i zobaczysz że zadziała

    Tak, od tego kodu wyszedłem - i na nim wykryłem ten problem. Jako że teraz nie mam dostępu do sprzętu to wieczorem wrzucę kod w bardzo podobnym stylu napisany.

    Jednak dalej nikt tu nie poruszył problemu z którym do Was się zwróciłem, wszyscy tylko krytykują kod obsługi I2C - który niewątpliwie... działa. No chyba że analizator logiczny kłamie, kto to wie...
  • #23 15901947
    rexina
    Poziom 17  
    Z uwagi na bardzo dużą krytykę kodu który znajduje się powyżej wrzucam wersję "normalną", z obsługą TWI w przerwaniu.

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Oraz kod assemblera dla zainteresowanych:
    Kod: AVR assembler
    Zaloguj się, aby zobaczyć kod


    Również w tym przypadku procesor zachowuje się identycznie:
    [ATmega128A][avr-gcc] - TWI slave - uszkodzenie pamięci
    [ATmega128A][avr-gcc] - TWI slave - uszkodzenie pamięci.
    Oraz zrzut z analizatora: Link.

    I dokładnie tak samo jak w przypadku poprzednim - kod ten działa na atmedze 644p, na 128a niestety się zawiesza co można zauważyć po zrzutach.

    Jako ciekawostkę dodałem wypisywanie oprócz flag_now wartości cmd_ready_flag w przypadku wystąpienia błędu.
    Jak widać na drugim zrzucie błąd wystąpił, wartość flag_now wyniosła 13, jednak wypisanie cmd_ready_flag daje już wartoć 5 (jak najbardziej poprawną).

    Dla uściślenia dodam też jakie jest zachowanie mastera magistrali, w pęli:
    - start,
    - zapisz 20 bajtów pod adres 0x2B o wartościach 0x64...0x51,
    - repeated start
    - odczytaj 10 bajtów spod adresu 0x2B
    - stop.


    Nie opisałem tego przypadku żeby posłuchać opini na mój temat, tudzież dowiedzieć się że "tak nie należy pisać", że to "jest całkowicie błędne", że "powinienem użyć mniejszego procesora" jak również nie prosiłem o informacje że "brak mi wiedzy" i bym "poczytał kurs". Bardzo dziekuję za krytykę, szkoda że nie wynikło z niej żadne przybliżenie rozwiązania problemu.
    Nie proszę też o poprawienie kodu działania I2C (bo 1) działa zgodnie z założeniami opisanymi na początku, 2) napisałem już wiele razy obsługę I2C slave i nigdy nie miałem z nią problemu).

    Napisałem ten temat jako opis ciekawego zachowania procesora w tym szczególnym przypadku - i szukam wyjaśnienia dlaczego tak się dzieje.
    Żeby nie było - nie sugeruję również by to była wina procesora, kompilatora lub dowolnego innego czynnika nie-mojego.
    Jednak do tej porty poza w/w opiniami nie uzyskałem konkretnej rady/wyjaśnienia co może być przyczyną takiego zachowania.
  • #24 15905062
    Konto nie istnieje
    Konto nie istnieje  
REKLAMA