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

[M16] [C] UART - przerwanie & string

JmL(TM) 02 Lip 2008 00:46 4906 10
  • #1 5304289
    JmL(TM)
    Poziom 24  
    Witam!

    Tradycyjnie fragment kodu:

    #define USART_BAUDRATE	57600
    #define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)
    
    typedef struct
    {
    	unsigned char	rx_int;
    } interrupt_sig;
    
    interrupt_sig flaga;
    
    char rxbuff;
    char string_buffer[10];
    //*========================================================*//
    
    void UART_init(void) {
    	UCSRB |= (1 << RXEN) | (1 << TXEN);
    	UCSRC |= (1 << URSEL) | (1 << UCSZ0) | (1 << UCSZ1);
    
    	UBRRL = BAUD_PRESCALE;
    	UBRRH = (BAUD_PRESCALE >> 8);
    
    	UCSRB |= (1 << RXCIE);
    }
    //*========================================================*//
    
    ISR(USART_RXC_vect) 
    {
    	register u08 c;
    
    	rxbuff = UDR;
    
    	if (bit_is_clear(UCSRA, RXC))
    	{
    		rxbuff = c;
    		flaga.rx_int = 1; // ustaw flage
    	}
    }
    //*========================================================*//
    
    int main(void)
    {
    	UART_init(); // inicjalizacja portu szeregowego
    
    	sei(); // odblokowanie przerwan
    
    	int i=0;
    
    	while(1)
    	{
    		if (flaga.rx_int) // jesli przeslano znak...
    		{
    			flaga.rx_int = 0; // zeruj flage
    
    			if (rxbuff == 0x0D) // jesli przeslany znak to koniec linii
    			{
    				string_buffer[i] = 0; // zakoncz string
    				i = 0;
    			}
    			else
    			{
    				if (i < 10) // rozmiar bufora
    				{
    					string_buffer[i++] = rxbuff;
    				}
    			}
    		}
    
    	}
    }


    Chcialbym w przerwaniu pobrac string i pozniej w programie dalej nim operowac ale niestety nie za bardzo mi to wychodzi. W obsludze przerwania ISR(USART_RXC_vect) pobieram bajt z UART i jak testuje robiac echo w samym przerwaniu to wszystko dziala ale niestety w tej wersji kod zupelnie nie chce dzialac. Ustawiam flagi po odbiorze znaku w przerwaniu i pozniej w glownej petli programu zeruje flage i wpisuje znak do bufora ale niestety w buforze nie pojawia sie zaden znak i nie wiem co jest przyczyna. Czy moglbym prosic o pomoc?
  • Pomocny post
    #2 5304316
    Balu
    Poziom 38  
    
    ISR(USART_RXC_vect)
    {
       register u08 c;
    
       rxbuff = UDR;
    
       if (bit_is_clear(UCSRA, RXC))
       {
          rxbuff = c;
          flaga.rx_int = 1; // ustaw flage
       }
    } 
    

    Tutaj po coś przypisujesz do rxbuff c, które jest niezainicjalizowane...
    ?
  • Pomocny post
    #3 5304318
    markosik20
    Poziom 33  
    Weryfikację końca ramki lepiej robić w przerwaniu.

    ISR(USART_RXC_vect) 
    { 
    	
    	buf_odbioru[poz_znaku_odbioru]=UDR;
    	Czas_UARTA = 50;
    
    
    		
    	if(	buf_odbioru[poz_znaku_odbioru]==0x0A &&
    		buf_odbioru[poz_znaku_odbioru-1]==0x0D 
    		 )
    	{
    		flagi_UARTA.dane_przyszly=1;
    	}
    
    	if(poz_znaku_odbioru<ROZM_BUFORA_RX-1){poz_znaku_odbioru++;}
    	else
    	{
    	 flagi_UARTA.bufor_RX_pelny=1;
    	}
    		   
    }


    i w sekcji while(1);

    if(flagi_UARTA.dane_przyszly)
    	{
    	Weryfikuj_dane_z_UARTA();
    	}
    
    
    	if(flagi_UARTA.bufor_RX_pelny || flagi_UARTA.blad_danych)
    	{
    	flagi_UARTA.bufor_RX_pelny = 0;
    	flagi_UARTA.blad_danych = 0;
    	ZerowanieBuforaOdbioru();
    	}


    Podczas weryfikacji bufora możesz przeładować interesujące dane do tablicy.
  • #4 5304323
    BoskiDialer
    Poziom 34  
    W przerwaniu używasz nie zainicjalizowanej zmiennej "c", zresztą i tak warunek w przerwaniu nie powinien zostać spełniony - po odczytaniu bajtu z UDR flaga RXC jest kasowana. Dodatkowo warto oznaczyć flagę jako volatile, inaczej kompilator może wyciągnąć sprawdzenie przed pętlę i całość szlak trafi...
    #define USART_BAUDRATE   57600 
    #define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1) 
    
    volatile unsigned char rx_complete;
    char rxbuff;
    
    char string_buffer[10]; 
    //*========================================================*// 
    
    void UART_init(void) { 
       UCSRB |= (1 << RXEN) | (1 << TXEN); 
       UCSRC |= (1 << URSEL) | (1 << UCSZ0) | (1 << UCSZ1); 
    
       UBRRL = BAUD_PRESCALE; 
       UBRRH = (BAUD_PRESCALE >> 8); 
    
       UCSRB |= (1 << RXCIE); 
    } 
    //*========================================================*// 
    
    ISR(USART_RXC_vect) 
    { 
       rxbuff = UDR;
       rx_complete = 1;
    } 
    //*========================================================*// 
    
    int main(void) 
    { 
       UART_init(); // inicjalizacja portu szeregowego 
    
       sei(); // odblokowanie przerwan 
    
       int i=0; 
    
       while(1) 
       {
          if (rx_complete) // jesli przeslano znak... 
          { 
             rx_complete = 0; // zeruj flage 
    
             if (rxbuff == 0x0D) // jesli przeslany znak to koniec linii 
             { 
                string_buffer[i] = 0; // zakoncz string 
                i = 0; 
             }
             else 
             {
                if (i < 10) // rozmiar bufora 
                {
                   string_buffer[i++] = rxbuff; 
                }
             }
          }
       }
    }

    Sam ciąg znaków można też składać bezpośrednio w przerwaniu, ale to napiszę z rana.
  • #5 5305316
    JmL(TM)
    Poziom 24  
    Nie sprawdzalem tego jeszcze na uC ale zalozenie mam takie, ze w przerwaniu odbieram znaki, skladam je w buforze i pozniej w glownej petli porownuje z "wzorcem komend" i np. ustawiam czas w zegarze lub budzik z poziomu PC [wlasnie w tym zegarze, w ktorym BoskiDialer mi juz wczesniej pomagal z trybem 12h :D]. I tu kolejne pytanie: czy dobrze mysle zeby tak to rozwiazac? A pozniej tylko sprawdzenie komendy:

    if (!strncmp_P(string_buffer, PSTR("+CLK=?"),6))
    {
    	// ustawianie zegarka
    }
  • Pomocny post
    #6 5305791
    BoskiDialer
    Poziom 34  
    Prawie że na kolanie napisane:
    #include <avr/io.h>
    #include <avr/interrupt.h>
    
    #define USART_BAUDRATE   57600 
    #define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1) 
    
    // maksymalna liczba znaków polecenia
    #define UART_MAXBUFF 16
    
    // bufor znaków polecenia (UART_MAXBUFF znaków + jeden pod znak zero)
    char uart_rx_buffer[UART_MAXBUFF+1];
    // flaga informująca, czy bufor zawiera całe polecenie
    volatile unsigned char uart_rx_complete;
    // zmienna zawierająca długość polecenia w buforze
    volatile unsigned char uart_rx_len;
    
    
    // Inicjalizacja uartu
    void UART_init(void)
    { 
       UBRRH = (BAUD_PRESCALE >> 8); 
       UBRRL = BAUD_PRESCALE; 
    
       UCSRA = 0;
       UCSRC = _BV(URSEL)|_BV(UCSZ0)|_BV(UCSZ1); 
       UCSRB = _BV(RXEN)|_BV(TXEN)|_BV(RXCIE); 
    } 
    
    // sygnał przyjścia bajtu
    SIGNAL(SIG_UART_RECV) 
    { 
       char ch = UDR;
       // jeśli ostatnie polecenie nie zostało przetworzone - odrzuć bajt
       if(uart_rx_complete)
          return;
    
       // jeśli przyszedł znak końca linii - zakończ odbiór
       if(ch == 0x0D)
       {
          uart_rx_buffer[uart_rx_len] = 0;
          uart_rx_complete = 1;
          return;
       }
    
    // opcjonalne odrzucanie innych znaków
    // if(ch < 0x20 || ch >= 0x80)
    //    return;
       
       // jeśli bufor pełny - też zakończ odbiór
       if(uart_rx_len == UART_MAXBUFF)
       {
          uart_rx_buffer[UART_MAXBUFF] = 0;
          uart_rx_complete = 0xFF;
          return;
       }
       
       // można przetworzyć bajt, nie jest to koniec linii i jest miejsce w buforze - dodaj bajt
       uart_rx_buffer[uart_rx_len++] = ch;
    }
    
    int main(void)
    {
       // inicjalizacja portu szeregowego 
       UART_init();
       // odblokowanie przerwań
       sei(); 
    
       while(1) 
       { 
          // jeśli odebrano polecenie
          if(uart_rx_complete)
          { 
             // sprawdzenie ciągu znaków, różne operacje..
             // teraz w uart_rx_buffer znajduje się jedna linia odebrana przez port szeregowy
             // linia ta jest zakończona bajtem zerowym, dodatkowo długość jest zapisana w uart_rx_len
    
             // analiza linii
             // porównanie z "wzorcem komend", ustawienie czasu w zegarze etc...
             
             // zwolnienie buforu
             uart_rx_len = 0;
             uart_rx_complete = 0;
          } 
       }
    }

    Składanie ciągu znaków jest dość proste, więc jest w przerwaniu, w pętli głównej tylko trzeba dane obrabiać. Co do tego jaką funkcją porównywać - tutaj pewnie bym napisał swoją, gdyż porównania będą częściowe - np część to polecenie, część to dane.. jak porównania będą wyglądać to można zacząć dyskutować jak już samo odbieranie ciągów znaków będzie działać
  • #7 5308311
    JmL(TM)
    Poziom 24  
    To dziala idealnie i wlasnie o to mi chodzilo. Dzis probowalem poskladac to w calosc i niestety znow mam problemy :|

    Mianowicie nie wiem gdzie umiescic odczyt zegara I2C i wyswietlanie na LCD. Myslalem, ze odpowiedni do tego bedzie Timer0 i oto wiekszy fragment kodu:

    #define	TIMER0_CLK		0x01	// 1<<CS00
    #define	TIMER0_CLK_8		0x02	// 1<<CS01
    #define	TIMER0_CLK_64		0x03	// 1<<CS01|1<<CS00
    #define	TIMER0_CLK_256		0x04	// 1<<CS02
    #define	TIMER0_CLK_1024		0x05	// 1<<CS02|1<<CS00
    
    // maksymalna liczba znaków polecenia
    #define UART_MAXBUFF 16
    
    // bufor znaków polecenia (UART_MAXBUFF znaków + jeden pod znak zero)
    char uart_rx_buffer[UART_MAXBUFF+1];
    // flaga informująca, czy bufor zawiera całe polecenie
    volatile unsigned char uart_rx_complete;
    // zmienna zawierająca długość polecenia w buforze
    volatile unsigned char uart_rx_len;
    
    unsigned char	clock_set=0;
    //*========================================================*//
    
    // sygnał przyjścia bajtu
    SIGNAL(SIG_UART_RECV)
    {
    	char ch = UDR;
    
    	// jeśli ostatnie polecenie nie zostało przetworzone - odrzuć bajt
    	if (uart_rx_complete)
    		return;
    
    	// jeśli przyszedł znak końca linii - zakończ odbiór
    	if ((ch == 0x0D) || (ch == 0x0A))
    	{
    		uart_rx_buffer[uart_rx_len] = '\0';
    		uart_rx_complete = 1;
    
    		return;
    	}
    
    	// opcjonalne odrzucanie innych znaków
    	// if (ch < 0x20 || ch >= 0x80)
    	//    return;
       
    	// jeśli bufor pełny - też zakończ odbiór
    	if (uart_rx_len == UART_MAXBUFF)
    	{
    		uart_rx_buffer[UART_MAXBUFF] = 0;
    		uart_rx_complete = 0xFF;
    
    		return;
    	}
    
    	// można przetworzyć bajt, nie jest to koniec linii i jest miejsce w buforze - dodaj bajt
    	uart_rx_buffer[uart_rx_len++] = ch;
    }
    //*========================================================*//
    SIGNAL (SIG_OVERFLOW0)
    {
    	if (!clock_set)
    	{
    		GetTime();
    		GetDate();
    
    		lcdClear();
    		lcdGotoXY(0, 0);
    
    		sprintf(bufor, "%.1d:%.2d:%.2d%cm   %.2d-%.2d-20%.2d", czas.godzina, czas.minuta, czas.sekunda, pm, data.dzien, data.miesiac, data.rok);
    		lcdPrintData(bufor, strlen(bufor));
    
    		lcdGotoXY(0, 1);
    
    		strcpy_p(bufor, dni_tyg[data.dzien_tyg - 1]);
    		lcdPrintData(bufor, strlen(bufor));
    	}
    }
    //*========================================================*//
    
    int main (void)
    {
    	TCCR0 = TIMER0_CLK_1024;
    	TIMSK = 1<<TOIE0;
    
    	lcdInit();
    	lcdClear();
    
    	// inicjalizacja portu szeregowego
    	UART_init();
    
    	// inicjalizacja I2C
    	i2c_init();
    
    	// odblokowanie przerwań
    	sei();
    
    	while(1)
    	{
    		// jeśli odebrano polecenie
    		if (uart_rx_complete)
    		{
    			// sprawdzenie ciągu znaków, różne operacje..
    			// teraz w uart_rx_buffer znajduje się jedna linia odebrana przez port szeregowy
    			// linia ta jest zakończona bajtem zerowym, dodatkowo długość jest zapisana w uart_rx_len
    
    			// analiza linii
    			// porównanie z "wzorcem komend", ustawienie czasu w zegarze etc...
    
    			if (clock_set)
    			{
    				sscanf(uart_rx_buffer, "%d:%d:%d %d %d %d-%d-%d", &czas.godzina, &czas.minuta, &czas.sekunda, &czas.pm, &data.dzien_tyg, &data.dzien, &data.miesiac, &data.rok);
    
    				SetTime();
    				SetDate();
    
    				clock_set = 0;
    			}
    
    			if (!strncmp_P(uart_rx_buffer, PSTR("CLK+SET"),7))
    			{
    				clock_set = 1;
    				cbi(PORTA, 0);
    			}
    
    			// zwolnienie buforu
    			uart_rx_len = 0;
    			uart_rx_complete = 0;
    		}
    	}
    }


    Jesli Timer0 jest wylaczony to stringi z przerwania odbierane sa prawidlowo, natomiast jesli wlacze Timer0 i wysylam polecenie ustawienia zegara z PC to raz dziala, raz nie. Jak w takim razie wyswietlac dane na LCD skoro nie moge tego zrobic w glownej petli, gdyz dane wyswietlane sa cyklicznie i zbyt szybko a LCD wtedy "glupieje".
  • Pomocny post
    #8 5308624
    BoskiDialer
    Poziom 34  
    Najpierw zwiększ może rozmiar buforu, bo czas przez Ciebie wysyłany jakkolwiek musi mieć więcej niż 15 znaków a więc jest on ucinany:
    #define UART_MAXBUFF 30

    kolejna sprawa, to usuń komentarze liniowe z linii wykorzystywanych przez preprocesor. Popatrz co się stanie: komentarz również zostanie wstawiony w kod, a tam niestety spowoduje on wykomentowanie średnika i albo kod nie będzie się kompilował, albo nie będzie działał:
    TIMER0_CLK_1024      0x05   // 1<<CS02|1<<CS00
       TCCR0 = TIMER0_CLK_1024; 
       TIMSK = 1<<TOIE0;
    po podstawieniu:
       TCCR0 = 0x05   // 1<<CS02|1<<CS00; 
       TIMSK = 1<<TOIE0;


    Dodano po 6 [minuty]:

    Co do samego pobierania czasu i wypisywania go na wyświetlacz - możesz kod wstawić bezpośrednio przed if(uart_rx_complete) lub zaraz za klamrą zamykającą tamtą sekcję.. ten warunek musi być sprawdzany co jakiś czas, ale sprawdzenie może być wkomponowane gdziekolwiek, nawet do pierwotnej pętli głównej twojego programu.
  • #9 5308707
    JmL(TM)
    Poziom 24  
    WOW! Nie sadzilem, ze cos takiego moze miec miejsce. Sadzilem iz komentarz jest zwyczajnie pomijany przez kompilator a tu jednak niespodzianka. Faktycznie rozmiar bufora byl za maly co przeoczylem. Usunalem rowniez owe komentarze i w dalszym ciagu to samo. Odnosze wrazenie ze Timer0 koliduje z odbiorem danych przez UART w przerwaniu.

    Kurcze zalamalem sie tym co zobaczylem!!!
    Kod do odczytu i wyswietlania godziny umiescilem za sprawdzeniem czy "uart_rx_complete" i przeoczylem jedna linie:



    Stad to "oglupienie" LCD. Ale teraz wykonuje praktycznie ciagly odczyt godziny z RTC na I2C i wyswietlam go na LCD co powoduje znaczne obciazenie uC. Myslalem zeby zadeklarowac zmienna 'x' i zwiekszac jej wartosc o 1 i jak osiagnie jakas wartosc to wyswietlic czas i wyzerowac wartosc zmiennej 'x'. Ale czy nie jest to tworzenie kodu dla samego tworzenia? Czy cokolwiek to zmieni?
  • Pomocny post
    #10 5308846
    BoskiDialer
    Poziom 34  
    Jeśli procesora nie wprowadzasz w tryb uśpienia, to zmiana poboru prądu przez procesor jest porównywalna jak by cały czas wpisywał te dane, jak i co jakiś czas.. jak chcesz, to możesz wykorzystać timer do odmierzania jakiś odcinków czasu np 200ms (w przerwaniu ustawiać jedną flagę, nic więcej) a w pętli głównej jeśli ta flaga jest ustawiona, to aktualizacja zawartości wyświetlacza (i skasowanie flagi) etc.. Pomimo podobnego poboru prądu różnica będzie w tym, że gdyby były jeszcze inne zadania w pętli głównej, to średnie opóźnienie w ich realizacji będzie mniejsze (większość przebiegów pętli będzie tylko sprawdzeniami flag, które były by wyzerowane, a więc procek szybciej zareaguje na ustawienie innej flagi). Jeśli będziesz korzystał z flag, nie zapomnij o oznaczeniu ich jako volatile - uchroni Cię to przed optymalizacją kompilatora.

    Dodano po 6 [minuty]:

    ps. Sprawdziłem owe wstawianie komentarza, nie udało mi się doprowadzić do jego wstawienia, ale lepiej nie mieszać komentarzy C z poleceniami preprocesora, gdyż na innym kompilatorze może się to źle skończyć.
  • #11 5308921
    JmL(TM)
    Poziom 24  
    BoskiDialer dziekuje za rzetelna i wyczerpujaca odpowiedz. Jak zwykle wyprowadziles mnie na dobra droge :D Zrobilem tak jak napisales i juz wszystko smiga poprawnie. Moge juz synchronizowac czas z PC w dowolnym momencie pracy zegara. Teraz zajme sie ustawianiem alarmow i chyba narazie zrobie to programowo i mam nadzieje, ze tu mi juz pojdzie bezproblemowo!

    Jeszcze raz dziekuje za pomoc i pozdrawiam!
REKLAMA