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

Avr i C. Zapalanie diod. Jakie wartości wpisać do portu?

Top Gun 29 Kwi 2007 15:05 2757 6
  • #1 3832181
    Top Gun
    Poziom 20  
    Posty: 556
    Pomógł: 2
    Ocena: 26
    Witam.
    Zaczynam zabawe z programowaniem mikrokontrolerow i potrzebuje pomocy (podejrzwam, ze to nie bedzie ostatni raz). Otoz zaczalem prosta zabawe z diodami, podpialem sobie 3 diody i chcialbym aby mi sie zapalala kazda po kolei potem gasla. No i podpatrujac rozne zrodla zrobilem cos takiego:
    
    int main(void)
    {
    	DDRB=0x7c;
    	PORTB = 0xff;
    	_delay_loop_2(0xffff);
    	PORTB = 0x04;
    	_delay_loop_2(0xffff);
    	PORTB = 0xff;
    	_delay_loop_2(0xffff);
    	PORTB = 0x18;
    	_delay_loop_2(0xffff);
    	PORTB = 0xff;
    	_delay_loop_2(0xffff);
    	PORTB = 0x2f;
    	_delay_loop_2(0xffff);
    	while(0);
    	return 0;
    }
    

    Podejrzewam, ze to wszystko oczywiscie mozna pewnie prosciej zrobic ale nie o to na razie chodzi. Chodzi mi o to, ze nie mam pojecia jakie dokladnie wartosci trzeba wpisac do portu. Skad i jak sie je przelicza?? Te co sa teraz wpisane dzialaja lecz nie tak jak tego zakladalem. Myslalem, ze te wartosci hex sa przeliczeniem stanu portu z systemu binarnego jednak albo cos zle robie albo zle rozumuje bo za nic mi nie wychodzi :( Np. ta wartosc 0x7c dla DDRB - nie kumam co ona oznacza :(
  • #2 3832267
    pubus
    Poziom 30  
    Posty: 1289
    Pomógł: 138
    Ocena: 31
    Dlatego właśnie zapis w hex nie jest w tym wypadku najlepszą metodą...
    
    DDRB = 0x7C; 
    to to samo co
    DDRB = 0b01111100; 
    lub
    DDRB = (1 << PB2) | (1 << PB3) | (1 << PB4) | (1 << PB5) | (1 << PB6);
    lub 
    DDRB |= _BV(2) | _BV(3) | _BV(4) | _BV(5) | _BV(6); 
    


    Jest to konfiguracja kierunku/trybu dla poszczególnych pinów portu B...
    "1" oznacza, że pin pracuje jako wyjście typu push-pull...
    "0" oznacza, że pin pracuje jako wejście...
    To tak ogólnie... dokładniejsze informacje znajdziesz w pdf do uC...
    ...
    Na razie muszę zmykać....
    Jak się nikt nie odezwie to dopisze resztę...
  • #3 3832568
    Top Gun
    Poziom 20  
    Posty: 556
    Pomógł: 2
    Ocena: 26
    No kurcze, to jednak dobrze myslalem z przeliczaniem stanu portu na hex-a, ale juz wiem, gdzie robilem blad. Zapomnialem, ze diodami steruje poprzez 0 a nie 1 i dlatego mi nie wychodzilo :) teraz moj programik wyglada tak jak ponizej i spelnia swoje zadanie :)
    
    int main(void)
    {
    	DDRB=0x7C;
    	PORTB = 0xFB;
    	_delay_loop_2(0xffff);
    	PORTB = 0xF7;
    	_delay_loop_2(0xffff);
    	PORTB = 0xEF;
    	_delay_loop_2(0xffff);
    	while(0);
    	return 0;
    }
    


    Heh...przy okazji wyjasnilo mi sie co oznaczaja wlasnie zapisy _BV, ktorych nie potrafilem zajarzyc w pewnym programie do obslugi LCD :) Dzieki serdeczne :) Jeszcze nie raz zapewne poprosze o pomoc ;)

    Dodano po 2 [godziny] 24 [minuty]:

    Jak juz tutaj jestem to mam pytanko: czy LCD podlaczony do portu A w Mega16 bedzie dzialal czy jednak trzeba rezystory podciagajace? Jak czytalem w nocie to niby ma w sobie pull-up no ale nie jestem pewien, bo kurcze juz 3 rozne wersje programow do obslugi LCD probowalem zaimplementowac na nim i ciagle lipa :( I nie moge znalezc szczegolowej dokumentacji HD44780 :(
  • #4 3833274
    pubus
    Poziom 30  
    Posty: 1289
    Pomógł: 138
    Ocena: 31
    Do LCD na HD44780 nie potrzeba rezystorów podciągających...
    Jeżeli port A jest jako "szyna danych" to musi być ustawiony jako wyjście...
    Pamiętaj o inicjalizacji lcd bo bez tego nie ruszy...
  • #5 3833520
    Top Gun
    Poziom 20  
    Posty: 556
    Pomógł: 2
    Ocena: 26
    Oto ostatni moj kod, ktory probowalem zaimplementowac. To bylo na Mega8 z obsluga 1-wire - wycialem to.
    
    // Definicje dotyczace LCD 
    #define RS 0                     // wybor rejestru 
    #define RW 1                     // 1 - odczyt; 0 - zapis 
    #define E 2                      // sygnal zezwalajacy (enable) 
    #define PORTLCD PORTA            // port z podlaczanym LCD 
    #define DDRLCD DDRA             // port z podlaczanym LCD 
    #define PINLCD PINA             // port z podlaczanym LCD 
    // Wykorzystujemy tylko cztery linie przesylowe D4-D7 portu PORTLCD 
    
    // prodedura wytracania czasu 
    void czekaj(unsigned long pt)   //procedura wytracania czasu ok. 1ms 
    { 
            for(;pt>0;pt--) 
                    _delay_loop_2(1000);       // obliczone dla 4MHz 
    } 
    // sprawdzenie zajetosci jesli flaga zero to wolny LCD 
    // funkcja bajt odpowiadajacy stanowi bitu BF 
    unsigned char lcd_busy(void) 
    { 
            unsigned char flaga;            // zmienna pomocnicza 
            PORTLCD &= _BV(RS);                // dostep do rejestru instrukcji 
            PORTLCD |= _BV(RW);                // tryb odczytu  - ustaw bit RW 
            DDRLCD=0x0f;                    // bity D4 - D7 wejsciami        
            PORTLCD |= _BV(E);                 // ustawienie linii strobujacej 
            asm("nop");                     // krotka przerwa 
            flaga = PINLCD&0x80;            // zapamietanie zawartosci bitu BF(D7) 
            PORTLCD &= _BV(E); 
            PORTLCD |= _BV(E);                 // generacja pustego impulsu strobujacego 
            asm("nop");                     // krotka przerwa 
            PORTLCD &= _BV(E); 
            DDRLCD=0xff;                    // caly port wyjsciami 
            return flaga;                   // zwrocenie wartosci odczytanego bitu 
    } 
    
    // funkcja wysyla dana lub rozkaz do wyswietlenia 
    // litera = 0 oznacza slowo sterujace 
    void lcd_trans(unsigned char dana, char litera) 
    { 
            while (lcd_busy());             // oczekiwanie na zwolnienie wyswietlacza 
            PORTLCD &= _BV(RW);                // tryb zapisu - kasuj bit RW 
            PORTLCD |= _BV(E); 
            if (litera==0)                  // rodzaj transmisji zalezny od parametru 
                    PORTLCD &= _BV(RS);        // 0 oznacza slowo sterujace 
            else 
                    PORTLCD |= _BV(RS);        // 1 oznacza znak do wyswietlenia 
            PORTLCD |= _BV(E); 
            PORTLCD=(PORTLCD&0x0f)|(dana&0xf0);  //przygotuj starszy polbajt do LCD 
            asm("nop");             // krotka przerwa 
            PORTLCD &= _BV(E);         //impuls strobujacy 
            PORTLCD |= _BV(E); 
            PORTLCD=(PORTLCD&0x0f)|((dana&0x0f)<<4); 
                                    // przygotuj mlodszy polbajt do LCD 
            asm("nop");             // krotka przerwa 
            PORTLCD &= _BV(E); 
     } 
    
    // USTAWIANIE KURSORA W ZADANYM MIEJSCU 
    void lcd_xy(unsigned char w, unsigned char k) 
    { 
     lcd_trans((w*0x40+k)|0x80,0); 
    } 
    
    // Pisanie tekstu na ekranie 
    void lcd_tekst(char *tekst) 
    { 
            unsigned char i = 0;  while (tekst[i]) lcd_trans(tekst[i++],1); 
    } 
    
    // Inicjowanie LCD 
    void lcd_init(void) 
    { 
            unsigned char i; 
            DDRLCD=0xff;            // PORTLCD - wyjsciem 
            PORTLCD &= _BV(RW); 
            czekaj(45);             // czekamy okolo 45ms - katalogowo 15ms 
       		asm("nop"); 
            for(i=0;i<3;i++)        // 3-krotne wyslanie 3- 
                    { 
                    PORTLCD |= _BV(E); 
                    PORTLCD=(PORTLCD&0x0f)|0x30;//wyslij 3- do LCD 
                    asm("nop");asm("nop");asm("nop"); 
                    PORTLCD &= _BV(E); 
                    czekaj(5);                      // ok. 5ms 
                    } 
            PORTLCD |= _BV(E); 
            PORTLCD=(PORTLCD&0x0f)|0x20;  //wyslij 2- do LCD 
            asm("nop");asm("nop");asm("nop"); 
            PORTLCD &= _BV(E); 
            czekaj(10L);            // od tego momentu sprawdzamy gotowosc LCD 
            lcd_trans(0x28,0);      // interfejs 4-bitowy, 2 linie, znak 5x7 
            lcd_trans(0x08,0);      // wylacz LCD, wylacz kursor, wylacz mruganie 
            lcd_trans(0x01,0);      // czysc LCD nie trzeba czekac !!! 
            lcd_trans(0x06,0);      // zwiekszanie pozycji kursora po kazdym znaku 
            lcd_trans(0x0c,0);      // wlacz wyswietlacz bez kursora 
       } 
    
    
    //*************************************************************** 
    int main (void) 
    { 
            lcd_init();                                     // inicjujmy LCD 
            lcd_tekst('tekst1');      // wypisz *tekst1 
            lcd_xy(1,0); 
            lcd_trans('.',1);       // wyswietl kropke 
            lcd_trans(0xdf,1);      // znak stopni 
            lcd_tekst("C  ");       // duze C - "Celsjusza"  i spacja 
    }


    Znalazlem opis HD44780 - rozumiem, ze na samym poczatku musze zrobic inicjalizacje ta opisana na stronie 45? Mam jeszcze jedna prosbe: ten p[rogram moze sie okazac poprawny a nie chodzic, gdyz czasy chyba byly ustawiane dla kwarcu 4MHz a ja mam u siebie 12. Jak sie wylicza czasy, tak na jakims przykladzie? Np. 15 ms jak ustawic?
  • #6 3833818
    pubus
    Poziom 30  
    Posty: 1289
    Pomógł: 138
    Ocena: 31
    Co do opóźnienia to wygodniejsze w użyciu są poniższe funkcje...
    
    #define F_CPU        16000000
    #define CYCLES_PER_US ((F_CPU+500000)/1000000) 
    
    /****** Funkcje delay *******************************************/
    void delay(unsigned int us) 
    { 
     unsigned int delay_loops;
     register unsigned int  i;
    
     delay_loops = (us+3)/5*CYCLES_PER_US; 
     for (i=0; i < delay_loops; i++) {};
    } 
    
    void delayms(unsigned int ms)
    {
     unsigned int i;
    
     for (i=0;i<ms;i++) {delay(999); asm volatile ("WDR"::);}
    } 
    /***************************************************************/
    


    Zmieniasz tylko F_CPU w zależności od częstotliwości jaką taktujesz uC a ilość cykli na us jest wyliczana podczas kompilacji...
  • #7 3834720
    Top Gun
    Poziom 20  
    Posty: 556
    Pomógł: 2
    Ocena: 26
    No dobra, po ciezkich bojach calosc wyglada nastepujaco:
    
    #include <avr/io.h>
    #include <util/delay.h>
    //#include <inttypes.h>
    
    #define F_CPU	12000000
    #define CYCLES_PER_US ((F_CPU+500000)/1000000) 
    
    /****** Funkcje delay *******************************************/ 
    void delayus(unsigned int us) 
    { 
    	unsigned int delay_loops; 
    	register unsigned int  i; 
    	delay_loops = (us+3)/5*CYCLES_PER_US; 
    	for (i=0; i < delay_loops; i++) {}; 
    }
    
    void delayms(unsigned int ms) 
    { 
    	unsigned int i; 
    	for (i=0;i<ms;i++)
    	{
    		delayus(999);
    		asm volatile ("WDR"::);
    	}
    }
    /***************************************************************/
    
    // Definicje dotyczace LCD 
    #define LCD PORTA 				//port z LCD
    #define RS 0                     // wybor rejestru 
    #define RW 2                     // 1 - odczyt; 0 - zapis 
    #define E 3                      // sygnal zezwalajacy (enable) 
    #define SET_E LCD |= _BV(E) 
    #define CLR_E LCD &= ~_BV(E) 
    #define SET_RS LCD |= _BV(RS) 
    #define CLR_RS LCD &= ~_BV(RS) 
    #define SET_RW LCD |= _BV(RW) 
    #define CLR_RW LCD &= ~_BV(RW) 
    #define DDRLCD DDRA             // port z podlaczanym LCD 
    #define PINLCD PINA             // port z podlaczanym LCD 
    // Wykorzystujemy tylko cztery linie przesylowe D4-D7 portu PORTLCD 
    
    
    // pcodedura zapisu bajtu do wyświetlacza LCD
    // bez rozróżnienia instrukcja/dana
    void write_to_lcd(char x)
    {
    SET_E; // ustaw na E stan wysoki
    LCD = ((LCD & 0x0F) | (x & 0xF0)); // zapis pierwszej połówki bajtu
    CLR_E; // opadające zbocze na E -> zapis do wyświetlacza
    asm("nop");
    SET_E; // ustaw na E stan wysoki
    LCD = ((LCD & 0x0F) | ((x & 0x0F) << 4)); // zapis drugiej połowki bajtu
    CLR_E; // opadające zbocze na E -> zapis do wyświetlacza
    delayms(1); // czekaj 1ms
    }
    
    // procedura zapisu instrukcji do wyświetlacza LCD
    void write_command(char x)
    {
    CLR_RS; // niski stan na RS -> zapis instrukcji
    write_to_lcd(x); // zapis do LCD
    }
    
    // procedura zapisu danej do wyświetlacza LCD
    void write_char(char x)
    {
    	SET_RS; // wysoki stan na RS -> zapis danej
    	write_to_lcd(x); // zapis do LCD
    }
    
    // procedura zapisu tekstu do wyświetlacza LCD
    void write_text(char * s)
    {
    while(*s) // do napotkania 0
      {
      write_char(*s); // zapisz znak wskazywany przez s na LCD
      s++; // zwiększ s (przygotuj nastepny znak)
      }
    }
    
    // procedura inicjalizacji wyświetlacza LCD
    void lcd_init(void)
    {
    delayms(15); // czekaj 15ms na ustabilizowanie się napięcia zasilającego
    CLR_E; // E = 0
    CLR_RS; // RS = 0
    char i; // zmienna licznikowa
    for(i = 0; i < 3; i++) // trzykrotne powtórzenie bloku instrukcji
      {
      SET_E; // E = 1
      LCD &= 0x30; //
      CLR_E; // E = 0
      delayms(5); // czekaj 5ms
      }
    SET_E; // E = 1
    LCD &= 0x2E; //
    CLR_E; // E = 0
    delayms(1); // czekaj 1ms
    write_command(0x28); // interfejs 4-bity, 2-linie, znak 5x7
    write_command(0x08); // wyłącz LCD, kursor i miganie
    write_command(0x01); // czyść LCD
    write_command(0x06); // bez przesuwania w prawo
    write_command(0x0C); // włącz LCD, bez kursora i mrugania
    }
    
    void lcd_xy(unsigned char w, unsigned char k) 
    { 
    	write_command((w*0x40+k) | 0x80); 
    } 
    


    I co najwazniejsze - dziala :) Dziekuje za podpowiedzi.

Podsumowanie tematu

✨ Dyskusja dotyczy programowania mikrokontrolerów AVR w języku C, ze szczególnym uwzględnieniem sterowania diodami LED poprzez porty mikrokontrolera. Omówiono sposób konfiguracji kierunku pinów portu B za pomocą rejestru DDRB, gdzie wartość "1" ustawia pin jako wyjście, a "0" jako wejście. Zwrócono uwagę, że sterowanie diodami odbywa się przez ustawianie odpowiednich bitów portu na "0" (stan niski), co powoduje zapalenie diody, a nie na "1". Przedstawiono różne formy zapisu wartości do portu, w tym zapis binarny, heksadecymalny oraz użycie makr _BV() do ustawiania poszczególnych bitów. Poruszono także temat podłączania wyświetlaczy LCD (HD44780) do portu A mikrokontrolera Mega16, gdzie nie jest wymagane stosowanie rezystorów podciągających, pod warunkiem prawidłowej konfiguracji portu jako wyjścia i inicjalizacji LCD. W dyskusji pojawiły się także przykłady implementacji funkcji opóźnień (delay) dostosowanych do częstotliwości taktowania mikrokontrolera (F_CPU), z wykorzystaniem pętli i makr do precyzyjnego odmierzania czasu. Ostatecznie autor przedstawił poprawiony kod, który realizuje sekwencyjne zapalanie i gaszenie trzech diod LED, wykorzystując odpowiednie wartości portu i opóźnienia.
Podsumowanie AI na podstawie dyskusji. Może zawierać błędy.
REKLAMA