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

ATMEGA 8 i 24CXX: Optymalizacja kodu C dla odczytu Random Read i2c

Mad Bekon 19 Cze 2005 09:51 2901 18
REKLAMA
  • #1 1589246
    Mad Bekon
    Poziom 23  
    Posty: 794
    Pomógł: 31
    Ocena: 36
    Chciałbym zapytać, czy ktoś ma może procedurkę do obsługi takich pamięci dla C.
    Mam WinAVR, w którym możemy znaleść przykład twitest.c.
    Niestety, po skompilowaniu, zajmuje troszke za dużo, znacznie za dużo bo ok połowę pamięci kości.
    Nawet po okrojeniu ze zbędnych rzeczy i tak dużo zajmuje.

    Czy ma ktoś taką, która zajmuje niezbyt wiele? Czy w C jest na to szansa, czy pozostaje tylko ASM?

    Dodano po 4 [godziny] 42 [minuty]:

    Udało mi się dokonać zapisu do eeprom.

    Mam problemy z odczytem Random Read
    Procek się wiesza, nie wiem gdzie jest błąd.
    i2c.c:
    void i2c_init (void)
    {
    	TWBR = 0x10;
    }
    
    unsigned char i2c_start (void)
    {
    	/*writing a one to TWINT clears it, TWSTA=Start, TWEN=TWI-enable*/
    	TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
    	/*wait, until start condition has been sent --> ACK*/
    	while (!(TWCR & (1<<TWINT)));
    	return TWSR; }
    
    void i2c_stop (void)
    {
    	/*writing a one to TWINT clears it, TWSTO=Stop, TWEN=TWI-enable*/
    	TWCR = (1<<TWINT) | (1<<TWSTO) | (1<<TWEN);	}
    
    unsigned char i2c_send (unsigned char send)
    {
    	
    	TWDR = send;
    	/*send content of TWDR*/
    	TWCR = (1<<TWINT) | (1<<TWEN);
    	/*wait, until address has been sent --> ACK*/
    	while (!(TWCR & (1<<TWINT)));
    	return TWSR; }
    
    unsigned char i2c_read (unsigned char ack)
    {
    	/*send content of TWDR; TWEA = enable ACK*/
    	TWCR = (1<<TWINT) | (ack<<TWEA) | (1<<TWEN);
    	/*wait, until byte has been received --> ACK*/
    	while (!(TWCR & (1<<TWINT)));
    	return TWDR; }
    
    


    main.c:
    
    #include <avr/io.h>
    #define F_CPU        1000000      
    #include "inne.c" //<-Usart
    #include "i2c.c" //Obsługa i2C
    void main(void)
    {
    unsigned char k=10;
    USART_Init(12);
    
    i2c_start();
    i2c_send(0b10100000);//<- adres fizyczy 00, 1 bit adresu słowa 0 + Write
    i2c_send(0b00000000);//<- reszta adresu słowa, więc 1 znak w pamięci
    i2c_start(); //<-powtóżony start
    i2c_send(0b10100001);// <-Powtórzony adres fizyczny + Read
    k=i2c_read(0);<-Tu się procek wiesza
    i2c_stop();
    USART_Transmit(k);
    }
    


    Nie wiem gdzie błąd :(
  • REKLAMA
  • #2 1590082
    GienekS
    Poziom 32  
    Posty: 1971
    Pomógł: 139
    Ocena: 15
    Z TWI na ATmega 32 i 24c64 nie mam żadnych problemów. A w którym miejscu ci się wiesza ??
  • #3 1590132
    LordBlick
    VIP Zasłużony dla elektroda
    Posty: 5438
    Pomógł: 549
    Ocena: 69
    Widzę że ustawiasz flagę 1<<TWINT. A jak wygląda obsługa przerwania od TWI - signal(SIG_2WIRE_SERIAL)... ? ;)
  • REKLAMA
  • Pomocny post
    #5 1590289
    zumek
    Poziom 39  
    Posty: 3352
    Pomógł: 695
    Ocena: 52
    Mad Bekon napisał:

    ...
    Nie wiem gdzie błąd :(


    Bo i skąd możesz wiedzieć , skoro nie sprawdzasz co Ci mówi TWSR :(
    Np.
    
    i2c_send(0b10100000);//<- adres fizyczy 00, 1 bit adresu słowa 0 + Write
    

    A skąd wiadomo , że SLAVE odpowiedział ACK :?:
    Nie przypadkowo , wszystkie funkcje i2c (prócz i2c_init i i2c_stop) zwracają nam zawartość rejestru TWSR.

    Pozdrawiam
    Piotrek
  • REKLAMA
  • #6 1590312
    LordBlick
    VIP Zasłużony dla elektroda
    Posty: 5438
    Pomógł: 549
    Ocena: 69
    Mad Bekon napisał:
    Light'I, szczerze mówiąc nie wiem o co Ci chodziło.
    Pomyliłem TWINT z TWIE... ;) Po prostu jest przerwanie od TwoWire i na nim można alternatywnie się oprzeć.
  • REKLAMA
  • #7 1590636
    GienekS
    Poziom 32  
    Posty: 1971
    Pomógł: 139
    Ocena: 15
    Ja odczyt z TWI robię tak:
    /*************************************************/
    // Procedura odczytu bajtu z zewnętrznego EEPROM-a
    // AT32c32 *** DZIAŁA ! ***
    char ReadTW (u16 a, char *d)
    {
    char e = 0;
        TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
        e++;
      	while (!(TWCR & (1<<TWINT)));
    //  	if ((TWSR & 0xF8) != 0x08)	 //START
    //	   return e; // ERROR
    	TWDR = 0xa0; // wysłanie adresu do zapisu
    	TWCR = (1<<TWINT) | (1<<TWEN);
    	e++;
    	while (!(TWCR & (1<<TWINT)));
      	if ((TWSR & 0xF8) != 0x18)	 //MT_SLA_ACK)
    	   return e; // ERROR
    	TWDR = a >> 8; // wysłanie adresu Hi
    	TWCR = (1<<TWINT) | (1<<TWEN);
    	e++;
    	while (!(TWCR & (1<<TWINT)));
      	if ((TWSR & 0xF8) != 0x28)	 //MT_SLA_ACK)
    	   return e; // ERROR
    	TWDR = a & 255; // wysłanie adresu Lo
    	TWCR = (1<<TWINT) | (1<<TWEN);
    	e++;
    	while (!(TWCR & (1<<TWINT)));
      	if ((TWSR & 0xF8) != 0x28) 	 //MT_DATA_ACK)
    	   return e; // ERROR
        TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
        e++;
      	while (!(TWCR & (1<<TWINT)));
    //  	if ((TWSR & 0xF8) != 0x08)	 //START
    //	   return e; // ERROR
    	TWDR = 0xa1; // wysłanie adresu do odczytu
    	TWCR = (1<<TWINT) | (1<<TWEN);
    	e++;
    	while (!(TWCR & (1<<TWINT)));
    //  	if ((TWSR & 0xF8) != 0x38)	 //MT_SLA_ACK)
    //	   return e; // ERROR
    	TWCR = (1<<TWINT) | (1<<TWEN);
    	e++;
    	while (!(TWCR & (1<<TWINT)));
    //  	if ((TWSR & 0xF8) != 0x28)
    //	   return ++e; // ERROR
    	*d = TWDR;  // dane odczytane ?
        TWCR = (1<<TWINT) | (1<<TWSTO) | (1<<TWEN);
        return 0; // OK!
    }
    
    i przynajmniej wiem jak staje to w którym miejscu. Raczej nie staje tylko zwraca numer błędu.
  • #8 1591558
    Mad Bekon
    Poziom 23  
    Posty: 794
    Pomógł: 31
    Ocena: 36
    Troszeczke pomieszałem w kodzie, i wszystko mi działa. Mam tylko jedno pytanie.
    Mam instrukcję która zapisuje bajt pod wskazany adres.
    unsigned char read_24c_byte(unsigned char page, unsigned char address){
    unsigned char byte;
    i2c_start();
    i2c_send(0b10100000|(page<<1));
    i2c_send(address);
    i2c_start();
    i2c_send(0b10100001|(page<<1));
    byte=i2c_read(0);
    i2c_stop();
    
    return byte;
    }
    
    void write_24c_byte(unsigned char page, unsigned char address, unsigned char byte){
    i2c_start();
    i2c_send(0b10100000|(page<<1));
    i2c_send(address);
    i2c_send(byte);
    i2c_stop();
    delay(1000);
    
    }

    Jeśli chce to robić w pentli np:
    for(k=0;k<255;k++){write_24c_byte(0,k,k);}
    for(k=0;k<255;k++)USART_Transmit(read_24c_byte(0,k));

    muszę mieć to opóźnienie
    przy mniejszym np zapisuje mi co drugi bajt.
    czy to normalne?
  • Pomocny post
    #9 1591608
    GienekS
    Poziom 32  
    Posty: 1971
    Pomógł: 139
    Ocena: 15
    Każdy zapis ma wymagany czas. Chyba że urzywasz trybu stronicowego to tylko raz odmierzasz ten czas na stronę.
  • #10 1591990
    Mad Bekon
    Poziom 23  
    Posty: 794
    Pomógł: 31
    Ocena: 36
    Procedury write_24c_byte i read_24c_byte działają.

    Chcialbym teraz napisać funkcję która by przeszukiwała pamięć w poszukiwaniu pewnego ciągu.

    FindStr( ciąg ); a zwracać będzie adres np pierwszego bajtu tego ciągu.

    Nie za bardzo wiem jak się do tego zabrać.

    Myslałem o wykorzystaniu funkcji read_24c_byte, ale w niej jest troche za dużo Startów i stopów.

    A jakby tak wykorzystać odczytywanie sekwencyjne?
    START->
    Adres na 0->
    odczytujemy 1 bajt i porównujemy z 1 w ciągu->ACK
    Jeśli tak to 2 bajt z 2 w ciągu, a jeśli nie to 2 z 1 pierwszym ->ACK itd.
    na końcu pamięci ->STOP

    jeżeli znajdziemy w pamięci cały ciąg to przechowujemy w zmiennej ten adres.

    Myśle, że to już jest ciekawy problem do rozwiązania.
  • #11 1592563
    LordBlick
    VIP Zasłużony dla elektroda
    Posty: 5438
    Pomógł: 549
    Ocena: 69
    Pamietaj tylko o tym, ze odczyt sekwencyjny należy zakończyć po ostatnim odczytanym bajcie nie podając bitu potwierdzenia, za to generując warunek stopu.
  • #12 1592580
    GienekS
    Poziom 32  
    Posty: 1971
    Pomógł: 139
    Ocena: 15
    Light'I napisał:
    Pamietaj tylko o tym, ze odczyt sekwencyjny działa tylko w obrębie jednego bloku i ulega zapętleniu do jego początku...

    Z tym twierdzeniem się nie zgodzę. Po wykonaniu zapisu adresu startowego dla odczytu można odczytać całą pamięć. Robiłem tak osobiście i jeszcze mnie to nie zawiodło. Wspomniane stronicowanie dotyczy się tylko zapisu.
  • #13 1593156
    Mad Bekon
    Poziom 23  
    Posty: 794
    Pomógł: 31
    Ocena: 36
    No z tego co się doczytałem, o ile dobrze zrozumiałem, to sekwencyjny wraca do początku dopiero na końcu pamięci.
    Cytat:
    When the memory address limit is reached, the data work adress will "roll over"


    I tu się pojawia moje pytanie, bo za bardzo tego nei wiem. Jak jako argument funkcji podać całą tablicę. Z tego ci wiem nie ma zmiennych typu string i muszę ciąg zapisywać w tablicy np:
    unsigned char ciag[16].
    Z tego co zauważyłem to tablice się rozszerzają, więc mogę np napisac ciag[17]='4';

    Dodano po 1 [godziny] 19 [minuty]:

    Napisałem coś takiego, tylko mam z tym jeden problem:

    unsigned char find_24c_bytes(unsigned char page, unsigned char address){
    unsigned char count,licz=0;
    unsigned char ciag[16]="12345";
    i2c_start();
    i2c_send(0b10100000|(page<<1));
    i2c_send(address);
    i2c_start();
    i2c_send(0b10100001|(page<<1));
    for(count=0;count<255;count++){
    
    
    if(i2c_read(1)==ciag[licz++]){
    if(licz==5){
    return count-licz+1;
    }
    }
    else licz=0;
    
    }
    i2c_stop();
    }
    


    Problem 1. Jak narazie kod przeszukuje tylko 256 bajtów pamięci, ale to raczej żaden problem.
    Problem 2. Tutaj w funkcji ciąg jest zadeklarowany jako zmienna tablicowa. Jak go przekazać z wywołania funkcji na zasadzie:
    find_24c_bytes(0,0,"12345)";
    Problem 3. w if(licz==5) jest podana stała długość ciągu.
    chodzi o to, jak zamiast 5 podać długość ciągu. sizeof() to chyba nie to... :/
  • #14 1594339
    GienekS
    Poziom 32  
    Posty: 1971
    Pomógł: 139
    Ocena: 15
    Najlepiej jako wskaźnik.
  • #15 1595558
    Mad Bekon
    Poziom 23  
    Posty: 794
    Pomógł: 31
    Ocena: 36
    Niestety pojęcia wskaźnika jeszcze niezbyt rozumiem. Ale chyba zrozumiem ;-). Jeśli się uda to napisze. Myśle że ten wątek może kogoś zainteresować. Sam szukałem a nie znalazłem.
  • #16 1596172
    GienekS
    Poziom 32  
    Posty: 1971
    Pomógł: 139
    Ocena: 15
    Ja zastosowałem podobną składnie do wydruku na LCD
    tak wygląda wywołanie:
    PrintStrLCD ("Anulowany.",0,4)

    a tak procedura:
    /********************************************************/
    /* Procedura wydruku łańcucha S od współrzędnych x, y  	*/
    /* generatorem znaku określonym przez pGENERATOR 		*/
    /* na wyświetlacz LCD				  					*/
    void PrintStrLCD (char *S, u08 x, u08 y)
    {
    u08 z, PozX, i;
    	z = *S; PozX = x;
    	while ((z != 0) && (PozX < 128))
    	{
    		PrintZLCD (ASCII_Generator (z), PozX, y);
    		PozX += Szerokosc (pGENERATOR, ASCII_Generator(z));
    		for (i=0; i < WYSOKOSC_ZNAKU; i++)
    			ClrPikLCD (PozX, y+i); //czyszczenie odstępu pomiędzy znakami
    		PozX++;	
    		S++; 
    		z = *S;
    	}
    }
  • #17 1596861
    Mad Bekon
    Poziom 23  
    Posty: 794
    Pomógł: 31
    Ocena: 36
    Dzięki pomocy kumpla który tchnął troszke świeżości kod wygląda tak:
    
    unsigned char find_24c_bytes(unsigned char kod[]){
    unsigned char licz=0,count;
    
    i2c_start();
    i2c_send(0b10100000);
    i2c_send(0);
    i2c_start();
    i2c_send(0b10100001);
    for(count=0;count<255;count++){
    if(i2c_read(1)==kod[licz++]){
    if(licz==strlen(kod)) return count-licz+1;
    }
    else licz=0; }
    i2c_stop();
    return -1; }
    


    Funkcja przeszukuje pamięc od adresu 0 do 255. Jeżeli napotka cały ciąg, zwraca adres jego pierwszego bajtu.
  • #18 1599090
    zumek
    Poziom 39  
    Posty: 3352
    Pomógł: 695
    Ocena: 52
    Mad Bekon napisał:
    ...Funkcja przeszukuje pamięc od adresu 0 do 255. Jeżeli napotka cały ciąg, zwraca adres jego pierwszego bajtu.

    Chciałbym tylko przestrzec , że Twoja funkcja może Cię niemile zaskoczyć i nie znaleźć ciągu mimo jego istnienia :(
    Dla przykładu:

    pamięć =1,2,3,4,1,2,3,4,5,...
    szukany ciąg=1,2,3,4,5


    Po wykryciu niezgodności na 5-tej pozycji , wyzeruje zmienną "licz" i pobierze kolejny bajt z pamięci.
    I klops :(

    Piotrek
  • #19 1599485
    Mad Bekon
    Poziom 23  
    Posty: 794
    Pomógł: 31
    Ocena: 36
    Zgadza się. Też o tym myślałem już wcześniej ale narazie tego w kodzie nie uwzględniłem. Później pewnie dopiszę fragment który będzie sprawdzał pod tym kątem. ;-) Ale oczywiście masz racje.

    Z tego ci mi się wydaje to wystarczy dodatkowo tylko sprawdzać, czy znak który powoduje resetowanie zmiennel licz nie jest przypadkiem 1 znakiem ciągu. Jeśli tak to licz++ Mam racje?

    Dodano po chwili zastanowienia:
    
    unsigned char find_24c_bytes(unsigned char kod[]){
    unsigned char licz=0,count,byte;
    i2c_start();
    i2c_send(0b10100000);
    i2c_send(0);
    i2c_start();
    i2c_send(0b10100001);
    for(count=0;count<255;count++){
    byte=i2c_read(1);
    if(byte==kod[licz++]){
    if(licz==strlen(kod)) return count-licz+1;
    }
    else {if(byte==kod[0])licz=1;
    else licz=0; }}
    i2c_stop();
    return -1; }
    

Podsumowanie tematu

✨ Dyskusja dotyczy optymalizacji kodu w języku C dla mikrokontrolera ATmega8 do obsługi pamięci EEPROM 24CXX przez interfejs I2C (TWI). Autor poszukiwał lekkiej procedury do odczytu i zapisu, gdyż przykładowy kod twitest.c z WinAVR zajmował zbyt dużo pamięci. Problemem był zwłaszcza odczyt typu Random Read, który powodował zawieszanie się procesora. Wskazano na konieczność sprawdzania statusu TWSR po każdej operacji I2C, aby wykryć błędy i potwierdzenia ACK od slave’a. Zaproponowano także alternatywne podejście oparte na przerwaniach TWI. Przedstawiono przykładową implementację odczytu bajtu z EEPROM, uwzględniającą kolejne etapy transmisji i sprawdzanie kodów statusu. Omówiono wymóg stosowania opóźnień po zapisie ze względu na czas zapisu w pamięci EEPROM, z możliwością optymalizacji przy trybie stronicowym. Autor opracował funkcje write_24c_byte i read_24c_byte działające poprawnie. Następnie podjął próbę napisania funkcji find_24c_bytes do wyszukiwania ciągu bajtów w pamięci EEPROM, wykorzystując odczyt sekwencyjny i porównywanie bajtów. Wskazano na problem z prostym resetowaniem licznika dopasowania przy niezgodności, co może powodować pominięcie wystąpień ciągu. Zaproponowano poprawkę uwzględniającą ponowne liczenie, gdy niezgodny bajt jest jednocześnie pierwszym znakiem szukanego ciągu. Dyskusja zawierała także przykłady obsługi łańcuchów znaków w C z użyciem wskaźników oraz wywołania funkcji do wyświetlania tekstu na LCD, co może być pomocne przy dalszej pracy z danymi tekstowymi w pamięci EEPROM.
Podsumowanie AI na podstawie dyskusji. Może zawierać błędy.
REKLAMA