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

LCD i Wypisywanie na nim zmiennych typu int i float.

domelfm 22 Lut 2007 20:11 4779 27
REKLAMA
  • #1 3604898
    domelfm
    Poziom 16  
    Posty: 238
    Pomógł: 5
    Ocena: 28
    Witam !

    Na początek pochwale się kodem który wymodziłem z pomoca kilku stronek o tematyce lcd:
    
    #include <avr/io.h>
    //
    #define LCD  PORTA
    #define E  3
    #define RS  2
    //
    #define SET_E   LCD |= _BV(E)
    #define CLR_E   LCD &= ~_BV(E)
    //
    #define SET_RS  LCD |= _BV(RS)
    #define CLR_RS  LCD &= ~_BV(RS)
    
    // funkcja opóźniająca o x*1ms
    void waitms(char x)
    {
    unsigned char a, b; // zmnienne licznikowe
    for( ; x > 0; --x) // ta pętla zostanie wykonana x-razy
      for(b = 10; b > 0; --b) // a ta 10 razy
    	for(a = 100; a > 0; --a) // natomiast ta 100 razy
    	__asm("nop"); // dodatkowa instrukcja opóźniająca o 1 cykl
    	// razem to da opóźnienie ok. x * 1ms
    	// x od 0 do 255
    }
    
    // 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
    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
    waitms(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)
    {
    waitms(15); // czekaj 15ms na ustabilizowanie się napięcia zasilającego
    CLR_E; // E = 0
    CLR_RS; // RS = 0
    char i; // zmianna licznikowa
    for(i = 0; i < 3; i++) // trzykrotne powtórzenie bloku instrukcji
      {
      SET_E; // E = 1
      LCD &= 0x3F; //
      CLR_E; // E = 0
      waitms(5); // czekaj 5ms
      }
    SET_E; // E = 1
    LCD &= 0x2E; //
    CLR_E; // E = 0
    waitms(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
    }
    
    // program główny
    int main(void)
    {
    // konfiguracja portów we/wy
    DDRA = 0xFF;
    DDRD = 0xFC;
    PORTA = 0xFF;
    PORTD = 0xF7;
    // inicjalizacja LCD
    lcd_init();
    // zapisz na LCD przykładowy tekst
    char a=5;
    write_text("Ala ma kota :D                          ");
    write_text("Kot ma wpierdol                         ");
    //write_text("Kot ma w pierdol");
    // petla nieskończona
    while(1);
    return 0;
    }
    


    Jak widać funkcja wpisująca tekst morze wpisywać tylko stringi.
    Teraz moje pytania:
    1.Czy każdą zmienną typu int musze przekształcić na stringa czy istnieją jakieś ułatwienia pomagajace wprowadzić zmienną int?
    2.Czy musze pisać stringi 40 znakowe co by mi kursor przechodził do nowej lini czy jest na to inny sposób, znak nowej lini "\n" nie skutkuje.
    3 Poprosze jeżeli ktoś moze polecić jakąś strone o wyswietlaczach lcd i o ich sterowaniu. Ja znalazłem tą stronke ale mało informacji na niej jest.
    4. Jeżeli ktoś ma opis bibliotek zawartych w winavr20060421 gdzie były by opisane komendy tak jak naprzykład TU byłbym wdzięczny.

    Pozdrawiam

    A tak od siebie to dodam że programowanie uP to zupełnie co innego niż programowanie komputera. Są zupełnie inne biblioteki i to jest problem dla osób znających c/c++ dosyć biegle. A ludzie obecni na foróm mówią ze znajomość c/c++ to 80 % sukcesu . Jak na mój gust tak nie jest. Znajomość bibliotek avr to może być 80 % sukcesu ale nie znajomość c/c++.
  • REKLAMA
  • Pomocny post
    #2 3605028
    master_pablo
    Poziom 16  
    Posty: 276
    Pomógł: 11
    Ocena: 30
    1. Jesli chcesz wyswietlic tylko jedna cyfre, to chyba zadziala write_char((cyfra & 0x0f) | 0x20). Jak cala liczbe, to musisz najpierw dokonac konwersji BIN -> BCD, a potem pare razy write_char. Ale malo sie znam na C, wiec mozliwe, ze jest duzo latwiejszy sposob :).

    2. Nie musisz. Trzeba wyslac do LCD komende Set DDRAM Address (0x80) + adres, pod ktory ma przejsc kursor. Dla LCD 2x16 pierwszy znak drugiej linii to 0x40.

    3. Na elportalu jest elegancki 4-czesciowy artykul (na samej gorze).

    4. Zapewne zainstalowales takie opisy razem z WinAVR. W Menu Start znajdziesz avr-libc Manual, a jak sie otworzy, to Library Reference.[/url]
  • Pomocny post
    #3 3605038
    Alchemik84
    Poziom 14  
    Posty: 100
    Pomógł: 8
    Ocena: 2
    Zmienną typu int najłatwiej zamienisz funkcją itoa znajdująca się w bibliotece stdlib.h.
    Jeżeli chcesz wypisać jedną cyfrę to ja robie tak write_char(5+0x30) - wyświetla '5'.
    Przejście do drugiego wiersza jest możliwe przez wpisanie write_command(0xC0) (wg. twoich procedur)- zakładam że używasz wyświetlacza 2x16
    Wszystkie dostępne biblioteki są dokładnie opisane w pdf który znajduje się w folderze winavr/doc/avr-libc/
  • #4 3605040
    M. S.
    Poziom 34  
    Posty: 2107
    Pomógł: 259
    Ocena: 680
    Np:

    lcd("Wartosc = ");
    lcd (itoa (x, txt,10));


    i funkcja zmieniająca położenie locate (y,x) dla 2 wierszy lcd
    void locate(char y, char x)
    {
    	
    	switch (y) // obliczenie o ile nalezy zwiekszyc x w zaleznosci od wartosci y
    	{
    		case 1:
    		x += 0x7F; // x = x + 0x80 - 1
    		break;
    		case 2:
    		x += 0xBF; // x = x + 0xA8 - 1
    		break;
    	}
    write_command(x);


    Co do znajomości programowania uC to znajomość C to ok. 30% sukcesu niestety. Następne 30% to biblioteki a cała reszta (chyba najważniejsza) to znajomość mikrokontrolera.
    Dla tego wielu amatorów (również ja) korzysta z Bascoma.
  • REKLAMA
  • Pomocny post
    #6 3617271
    markosik20
    Poziom 33  
    Posty: 2261
    Pomógł: 208
    Ocena: 147
    Z floatem jest troche więcej zabawy
    void WpiszLiczbe(unsigned char dx,unsigned char dy,Zmienna *ptr,unsigned char typ)
    {float dana_calkowita;float dana_ulamkowa;Zmienna dana;  
     unsigned char znak;
     unsigned char tab[17]={0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30};
     unsigned char *ptrtab=&(tab+10);
    
     if(typ==float_1||typ==float_2||typ==float_3||typ==float_4||typ==float_5)
    					{   dana.f=ptr->f;
    						if(dana.f<0){znak=*"-";dana.f*=(-1);}
    						else {znak=*" ";}
    
    					   //rozdzielenie części ułamkowej i całkowitej
    					   dana_ulamkowa=modf(dana.f,&dana_calkowita);
    
    					   dana.f=pow(10,typ);//obliczenie doklności
    					   dana_ulamkowa*=dana.f;//przesuniecie przecnika
    
    					   //zaokrąglenie danej ułamkowej 
    					   if(modf(dana_ulamkowa,&dana.f)>0.5){dana_ulamkowa=ceil(dana_ulamkowa);}
    					   else {dana_ulamkowa=floor(dana_ulamkowa);}
    					   
    					   dana.l=dana_ulamkowa;  
    					do{if(dana.l<10){*ptrtab=dana.l+0x30;}
    						else {*ptrtab=(dana.l%10)+0x30;}
    						ptrtab--;dana.l/=10;}	
    					while(dana.l);
    					//przesunięcie wskaźnika na miejsce przecinka wg. liczby cyfr po przecinku
    					while(ptrtab!=&(tab+10-typ)){ptrtab--;}
    					    *ptrtab=*".";ptrtab--;
    						dana.l=dana_calkowita;
    					do{if(dana.l<10){*ptrtab=dana.l+0x30;}
    						else {*ptrtab=(dana.l%10)+0x30;}
    						ptrtab--;dana.l/=10;}	
    					while(dana.l);
    					
    				}
    	if(typ==signed_long||typ==signed_int){		dana.l=ptr->l;
    								if(dana.l<0){znak=*"-";dana.l*=(-1);}
    						        else {znak=*" ";}
    								do{if(dana.l<10){*ptrtab=dana.l+0x30;}
    					   				else {*ptrtab=(dana.l%10)+0x30;}
    					   				ptrtab--;dana.l/=10;}	
    								while(dana.l);
    						  }
    	*ptrtab=znak;ptrtab--;
    		
    	tab[11]=0xFE;//znacznik konca
    	*ptrtab=0xFE;//znacznik początku							 
                //przygotowanie do wyświetlenia
    	ptrtab=&tab;
    	while(*ptrtab!=0xFE){ptrtab++;}ptrtab++;			//wyświeltenie					   
    	do{WriteLcd(dx,dy,*ptrtab);ptrtab++;dy++;}
    	while (*ptrtab!=0xFE);
    		
    }


    I typ zmiennej jaki sobie stworzyłem (taki uniwersalny) :)
    float_1 oznacza że wyświetlamy zmienną float z jednym miejscem po przecinku

    //------------------------------------------------------------ 
    typedef union{
    		float f;
    		signed long l;
    		struct {
    				signed int i1;
    				signed int i0;
    				}i;  
    		struct {
    			unsigned char b3; 	
    			unsigned char b2;
    			unsigned char b1;
    			unsigned char b0;
    				}byte;
    		struct {unsigned char p30 :4 ;
    				unsigned char p31 :4 ;
    				unsigned char p20 :4 ;
    				unsigned char p21 :4 ;
    				unsigned char p10 :4 ;
    				unsigned char p11 :4 ;
    				unsigned char p00 :4 ;
    				unsigned char p01 :4 ;
    			   }polbyte;
    		   	}Zmienna;
    //------------------------------------------------------------


    Funkcja wyświetla na lcd w lini dx począwszy od dy liczbę o rodzaju typ wraz ze znakiem. Wykorzystuje tutaj wbudowane funkcje z biblotek kompilatora

    #include "math.h" 
    #include "stdlib.h"


    Troche to rozbudowane, więc jakby co to moge to szybko zmienić tylko pod liczbe float.
  • #7 3617859
    domelfm
    Poziom 16  
    Posty: 238
    Pomógł: 5
    Ocena: 28
    hymm...

    Thx :)

    Jezeli byłbyś tak miły to jeszcze zobaczył bym sobie tą uproszczoną funkcje :)

    Podziwiam takich którzy na poczekaniu pisza taki kodzik :)
    To się nazywa doświadczenie

    A jezeli możesz to zostaw też ten kod co juz napisałeś żeby był widoczny w poscie.
    Może ten temat przyda się jeszcze komuś poza mną.

    Pozdrawiam
  • REKLAMA
  • Pomocny post
    #8 3618062
    markosik20
    Poziom 33  
    Posty: 2261
    Pomógł: 208
    Ocena: 147
    
    bit WpiszLiczbeFloat(unsigned char dx,unsigned char dy,float *ptr,unsigned char typ)
    {float dana_calkowita;float dana_ulamkowa;
     union {
     		float f;
    		unsigned long l;
    		}dana;  
     unsigned char znak;
     //tablica w której będą umieszczone poszczególne cyfry ASCII
     unsigned char tab[13]={0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30};
     unsigned char *ptrtab=&(tab+10);
    
    		 //warunek ..musi być podany ilość miejsc po przecinku
    		 if(typ<5&&typ>0){
       						dana.f=*ptr;
    						if(dana.f<0){znak=*"-";dana.f*=(-1);}
    						else {znak=*" ";}
    
    					   //rozdzielenie części ułamkowej i całkowitej
    					   dana_ulamkowa=modf(dana.f,&dana_calkowita);
    
    					   //typ=1 jeden znak po przeciniku
    					   //typ=2 dwa znak po przeciniku
    					   //
    					   //typ=5 pięć znaków po przeciniku
    					   dana.f=pow(10,typ);//obliczenie doklności
    					   dana_ulamkowa*=dana.f;//przesuniecie przecnika
    
    					   //zaokrąglenie danej ułamkowej 
    					   if(modf(dana_ulamkowa,&dana.f)>0.5){dana_ulamkowa=ceil(dana_ulamkowa);}
    					   else {dana_ulamkowa=floor(dana_ulamkowa);}
    					   
    					   dana.l=dana_ulamkowa;  
    					do{if(dana.l<10){*ptrtab=dana.l+0x30;}
    						else {*ptrtab=(dana.l%10)+0x30;}
    						ptrtab--;dana.l/=10;}	
    					while(dana.l);
    					//przesunięcie wskaźnika na miejsce przecinka wg. liczby cyfr po przecinku
    					while(ptrtab!=&(tab+10-typ)){ptrtab--;}
    					    //wpisz kropkę
    						*ptrtab=*".";ptrtab--;
    						dana.l=dana_calkowita;
    					do{if(dana.l<10){*ptrtab=dana.l+0x30;}
    						else {*ptrtab=(dana.l%10)+0x30;}
    						ptrtab--;dana.l/=10;}	
    					while(dana.l);
    					
    				
    					ab[11]=0xFE;//znacznik konca wyświetlania
    					*ptrtab=0xFE;//znacznik początku wyswietlania							 
    
    					ptrtab=&tab;
    					//pominięcie cyfr nieznaczacych
    					while(*ptrtab!=0xFE){ptrtab++;}ptrtab++;								   
    					do{WriteLcd(dx,dy,*ptrtab);ptrtab++;dy++;}
    					while (*ptrtab!=0xFE);
    					return 1;
    					}
    					return 0;
    		
    }
    
    


    Jako daną do wyswietlenia trzeba podac wskaźnik do zmiennej. Reszta ja wyżej.
  • #9 3620253
    Adamelek
    Poziom 14  
    Posty: 79
    Pomógł: 7
    Ocena: 1
    domelfm,

    po pierwsze łączę się z Tobą w bólu bracie :)

    Od kilku tygodni siedzę w świecie mikrokonktrolerów i niestety widzę, że jest znacznie gorzej, niż Ci się wydaje. Znajomość języka C to nie 80% sukcesu, ale jakieś 2 do 5%, nie więcej. Tak uważam patrząc na czas jaki poświęcam znikomym problemom pochodzącym od języka C oraz wszystkim innym. Prawdziwym problemem jest brak sensownej dokumentacji. To co musisz mieć to karta katalogowa Twojego mikrokontrolera pobrana skądkolwiek, np. ze strony producenta. Tylko stamtąd dowiesz się o całej architekturze i to ona przerazi Cię dziesiątkami odnośników i niejasności. Drugi dokument, jaki musisz mieć pod ręką to wspomniana już przez Alchemika84 avr-libc, która znajdziesz zainstalowaną razem z WinAVR. Tam dopiero jest dramat. Moim zdaniem jest to opisane źle i niejasno a co gorsza z pominięciem ważnych rzeczy. Do tego znajdziesz listę nazw, które kiedyś obowiązywały a teraz się pozmieniały bo... no właśnie, nie wiem czemu. Przykłady, jakie znajdziesz na różnych stronach będą zawierały często te przestarzałe nazwy i wcale Ci się nie skompilują. Do tego życzliwi ludzie z serca będą Ci podawać kawałki swojego kodu, ale zapomną, że są tam definicje pochodzące z #define lub #include, których już nie odnajdziesz. Słowem jest to koszmar, ale głowa do góry, w końcu to jakoś zaczyna działać.

    Co do LCD to znalazłem w miarę czytelny przykład wśród setek leżących na necie i przystosowałem do własnych potrzeb. Dopisałem sam procedury pisania znaków i ciągów, bo temte mnie wkurzały i wyszedł mo z tego zgrabny zestaw pożytecznych procedur do obsługi LCD. Po skompilowaniu jest to około 600 bajtów, więc nie jest źle.

    Acha na koniec ważne! Nie kasuj tej instalki (winavr20060421), którą masz! Ja od tego zaczynałem, potem przyszła nowsza, więc zupgradowałem i program do tworzenia plików "makefile" w ogóle nie działał, a programy przestały się kompilować. Totalna porażka, nawet nie chce mi się wnikać co zostało poprawione, że zostało zwalone. Wykasowałem, zainstalowałem wspomnianą starszą wersję i działa.

    Powodzenia! :)
  • Pomocny post
    #10 3620749
    zumek
    Poziom 39  
    Posty: 3352
    Pomógł: 695
    Ocena: 52
    markosik20 napisał:
    Z floatem jest troche więcej zabawy

    Owszem , ale jeśli to dotyczy kompilatora innego niż AVR-GCC.Po zmiennej typu bit poznaję , że przytoczona przez Ciebie funkcja dotyczy bardziej MCS-51 , a nie AVR , choć tych drugich oczywiście nie wyklucza.
    Wracając do meritum , czyli zamiany nieszczęsnego float na string , to gdyby tylko zajrzeć do pliku nagłówkowego stdlib.h w GCC , to można tam znaleźć wystarczająco skomentowaną funkcję char *dtostrf(double __val, char __width, char __prec, char *__s); , która to funkcja zrobi to , o co chodziło pytającemu , czyli zamieni float/double w pięknego - na dodatek sformatowanego w/g życzenia - stringa , gotowego do "wyrzucenia" na LCD.
    Dokumentacja dokumentacją , ale poczytanie komentarzy w plikach nagłówkowych standardowych funkcji kompilatora , też dużo może pomóc początkującym w C, do których też siebie zaliczam.Wprawdzie poniższe dotyczy nieśmiertelnego DS18b20 , to myślę że dobrze odda istotę zmiany float na string.
    Przykładzik :
    
    #include <avr/io.h>
    #include <stdlib.h>
    
    unsigned char starszybajt,mlodszybajt;
    char tstring[6];
    int main(void)
    {
    //eeprom_write_byte((uint8_t*)EE_ADDR,0x55);
    mlodszybajt=ow_read(1); 
    starszybajt=ow_read(1); 
    dtostrf(((float)((starszybajt<<8)|mlodszybajt)*10)/16/10,5,1,tstring );
    //LCD_str(tstring);
    //...
    return(0);
    }
    

    Piotrek
  • #11 3621413
    domelfm
    Poziom 16  
    Posty: 238
    Pomógł: 5
    Ocena: 28
    ...
    Zumek thx wielkie...

    A co do c i bibliotek to proponuje szukac info wszędzie byle nie avr-lib :)
    Google bardzo pomaga... Czyli kto szuka nie błądzi.. :)
    A przy okazji nasze forum jest bardzo dużą bazą danych.

    Przy okazji dodam że poszukuje obecnie programu w ce do obsługi klawiatury matrycowej na przerwaniu. Pzerwaniu tzn mam na myśli przerwanie wprowadzane z zewnątrz w skutek zmiany stanu klawiszy.
    Klawiaturke już sobie oprogramowalem ale potrzebna mi obsługa przerwania. Miło by było zalookć jakiś taki opis przerwań jak opis lcd który podał mi kolega @master_pablo.
    Bo przerwania opisane w mikrokontrolery avr w praktyce są be przykładowych implementacji kodu w c. A kod w asm.... Sorry brak mi wytrwałości.
    I odrazu zapytam się o implementacje ale zaznaczam w "c" termometru na ds

    Pozdrawiam
  • #12 3622587
    adamusx
    Poziom 27  
    Posty: 977
    Pomógł: 94
    Ocena: 28
    Cytat:
    Od kilku tygodni siedzę w świecie mikrokonktrolerów i niestety widzę, że jest znacznie gorzej, niż Ci się wydaje. Znajomość języka C to nie 80% sukcesu, ale jakieś 2 do 5%, nie więcej. Tak uważam patrząc na czas jaki poświęcam znikomym problemom pochodzącym od języka C oraz wszystkim innym. Prawdziwym problemem jest brak sensownej dokumentacji. To co musisz mieć to karta katalogowa Twojego mikrokontrolera pobrana skądkolwiek, np. ze strony producenta. Tylko stamtąd dowiesz się o całej architekturze i to ona przerazi Cię dziesiątkami odnośników i niejasności. Drugi dokument, jaki musisz mieć pod ręką to wspomniana już przez Alchemika84 avr-libc, która znajdziesz zainstalowaną razem z WinAVR. Tam dopiero jest dramat. Moim zdaniem jest to opisane źle i niejasno a co gorsza z pominięciem ważnych rzeczy. Do tego znajdziesz listę nazw, które kiedyś obowiązywały a teraz się pozmieniały bo... no właśnie, nie wiem czemu. Przykłady, jakie znajdziesz na różnych stronach będą zawierały często te przestarzałe nazwy i wcale Ci się nie skompilują. Do tego życzliwi ludzie z serca będą Ci podawać kawałki swojego kodu, ale zapomną, że są tam definicje pochodzące z #define lub #include, których już nie odnajdziesz. Słowem jest to koszmar, ale głowa do góry, w końcu to jakoś zaczyna działać.



    Nie martw sie. Poczatki zawszze sa ciezkie:) ja tez na poczatku nie moglem sie oswoic z C na uC, ale z biegiem czasu wszystko sie coraz bardziej rozjasnilo. To jest niestety kwestia pewnego doswiadczenia ktore trzeba nabyc z czasem, nic nie przsychodzi odrazu:) Teraz nie wyobrazam sobie powrotu do bascoma.
    Jesli chodzi biblioteki to w dokumentacji WinAVR jest pdf w ktorym wszystkie podstawowe funkcjie z biblioteki avrlib sa opisane (miedzy innymi ta bardzo fajna funkcja dtostrf :)
  • #13 3623800
    domelfm
    Poziom 16  
    Posty: 238
    Pomógł: 5
    Ocena: 28
    Pochwale się kawałkiem kodu :)

    
    #include <avr/io.h>
    #include <avr/interrupt.h>
    
    //
    #define klaw PORTB
    #define oklaw PINB
    #define dklaw DDRB
    
    #define LCD  PORTA
    #define DLCD DDRA
    #define E  3
    #define RS  2
    //
    #define SET_E   LCD |= _BV(E)
    #define CLR_E   LCD &= ~_BV(E)
    //
    #define SET_RS  LCD |= _BV(RS)
    #define CLR_RS  LCD &= ~_BV(RS)
    
    
    int i=2007;
    char bufor[17];
    char a='s';
    
    
    char przycisk()
    {
    
    klaw=0xEF;
    if(oklaw==0xEE)
    a= '1'; 
    if(oklaw==0xED)
    a= '4';
    if(oklaw==0xEB)
    a= '7';
    if(oklaw==0xE7)
    a= '*';
    
    klaw=0xDF;
    if(oklaw==0xDE)
    a= '2';
    if(oklaw==0xDD)
    a= '5';
    if(oklaw==0xDB)
    a= '8';
    if(oklaw==0xD7)
    a= '0';
    
    klaw=0xBF;
    if(oklaw==0xBE)
    a= '3';
    if(oklaw==0xBD)
    a= '6';
    if(oklaw==0xBB)
    a= '9';
    if(oklaw==0xB7)
    a= '#';
    
    klaw=0x7F;
    if(oklaw==0x7E)
    a= 'A';
    if(oklaw==0x7D)
    a= 'B';
    if(oklaw==0x7B)
    a= 'C';
    if(oklaw==0x77)
    a= 'D';
    
    klaw = 0x0F;
    
    i--;
    return 0;
    }
    
    // funkcja opóźniająca o x*1ms
    void waitms(char x)
    {
    unsigned char a, b; // zmnienne licznikowe
    for( ; x > 0; --x) // ta pętla zostanie wykonana x-razy
      for(b = 10; b > 0; --b) // a ta 10 razy
    	for(a = 100; a > 0; --a) // natomiast ta 100 razy
    	__asm("nop"); // dodatkowa instrukcja opóźniająca o 1 cykl
    	// razem to da opóźnienie ok. x * 1ms
    	// x od 0 do 255
    }
    
    // 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
    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
    waitms(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)
    {
    waitms(15); // czekaj 15ms na ustabilizowanie się napięcia zasilającego
    CLR_E; // E = 0
    CLR_RS; // RS = 0
    char i; // zmianna licznikowa
    for(i = 0; i < 3; i++) // trzykrotne powtórzenie bloku instrukcji
      {
      SET_E; // E = 1
      LCD &= 0x3F; //
      CLR_E; // E = 0
      waitms(5); // czekaj 5ms
      }
    SET_E; // E = 1
    LCD &= 0x2E; //
    CLR_E; // E = 0
    waitms(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
    }
    
    
    
    // program główny
    int main(void)
    {
    
    
    GIMSK=1<<INT0;         //włącz przerwanie zewnętrzne INT0
    MCUCR=0x02;            //przerwania INT0 wyzwalane narastajacym zboczem
    // konfiguracja portów we/wy
    
    DLCD = 0xFF;
    LCD = 0xFF;
    dklaw = 0xF0;
    klaw = 0x0F;
    sei();
    
    // inicjalizacja LCD
    lcd_init();
    // zapisz na LCD przykładowy tekst
    write_command(0x01); // czyść LCD
    write_text("Ala ma kota :D");
    write_command(0xC0);
    itoa(i,bufor,10);
    write_text(bufor);
    // petla nieskończona
    while(1);
    return 0;
    }
    
    SIGNAL(SIG_INTERRUPT0) 
    {
    przycisk();
    
    char tabc[3];
    tabc[0]=a;
    write_command(0x01); // czyść LCD
    write_text("Ala ma kota :D");
    write_command(0xC0);
    itoa(i,bufor,10);
    write_text(bufor);
    write_text(" ");
    write_text(tabc);
    }
    


    Kodzik obsługuje klawiaturke matrycową iwypisuje wcisniety klawisz.:)
  • #15 3650626
    Karol966
    Poziom 31  
    Posty: 2038
    Pomógł: 83
    Ocena: 645
    domelfm napisał:
    Pochwale się kawałkiem kodu :)


    Kodzik obsługuje klawiaturke matrycową iwypisuje wcisniety klawisz.:)


    Witam. To chyba napisane jest w C, a ja tego w ogóle nie rozumiem, czy jest mozliwość zamiany tego na asm? Umiem asemblera.

    Pozdrawiam.
  • #17 3652595
    zumek
    Poziom 39  
    Posty: 3352
    Pomógł: 695
    Ocena: 52
    Karol966 napisał:
    domelfm napisał:
    Pochwale się kawałkiem kodu :)


    Kodzik obsługuje klawiaturke matrycową iwypisuje wcisniety klawisz.:)


    witam. To chyba napisane jest w C a ja tego wogule nie rozumiem, czy jest mozliwość zamiany teg na asm? umiem asemblera

    pozdrawiam

    Jeśli znasz assembler , to popatrz jak kompilator GCC "przetłumaczył" z C na asm :D

    Poza tym , powyższa(domelfm) procedura skanowania klawiatury matrycowej ma tę wadę , że wciskając kilka klawiszy jednocześnie , można "ugotować" proca :(

    Piotrek
    Załączniki:
    • w_asm.txt (21.28 KB) Musisz być zalogowany, aby pobrać ten załącznik.
  • #18 3655203
    domelfm
    Poziom 16  
    Posty: 238
    Pomógł: 5
    Ocena: 28
    @zumek

    Mógłbyś podpowiedzieć mi troszkę co do tej procedurki do obsługi klawiaturki ?
    Jakieś drobne wskazówki dlaczego się by ugotował?
    Według mnie przy wcisniecu 2 klawiszy funkcja by nie zmieniła zminnej zmiennej char ponieważ zaden if nie bedzie spełniony :).
    Ale mam nadzieje ze dowiem się dlaczego :)
    Niechcem ugotować procka.. :)

    THX!!

    Pozdrawiam...
    I wyrazy uznanaia dla twojej wiedzy @zumek

    P.S
    Jak uzyskuje się plik elf (prog w asm zamiast c) ?
    U mnie elf jest nie do odczytu po konwersji przez gcc.
  • REKLAMA
  • #19 3655442
    Adamelek
    Poziom 14  
    Posty: 79
    Pomógł: 7
    Ocena: 1
    domelfm,

    chyba walczysz z kilkoma sprawami naraz, tak się nie da.

    1) listing assemblera po skompilowaniu przez gcc jest zapisywany w katalogu obj pod nazwą main.lst, o ile Twój główny program w 'C' miał nazwę main.c. W przeciwnym wypadku zamień sobie na nazwę Twojego projektu. Czytanie asemblera ma sens jeśli chcesz zobaczyć jak gcc zoptymalizował Ci Twój program.

    2) wypisanie liczby int możesz zrobic funkcją itoa.
    Oto składnia:
    char itoa (int __val, char __s, int __radix)

    a zatem deklarujesz swoją zmienną typu int, np:
    int v;

    zmienną typu char gdzie zostanie przechowany ciąg znaków ASCII dla wyświetlacza, np:
    char c [6];

    a potem w programie wywołujesz funkcję:
    itoa (v, c, 10);

    następnie wyrzucasz na wyświetlacz zmienną c aż do napotkania znaku '\0'. najfaniejesze jest to, że zmieniając parametr radix ta sama funkcja przygotuje Ci zmienną c do wyświetlenia w zadanym systemie liczenia. 10 - to oczywiście system dziesiętny, ale możesz też podać 16 albo 2 i otrzymasz ciąg ASCII zapisany w systemie hexadecymalnym albo binarnym. Pamiętaj tylko aby zadeklarować odpowiednio dużą długość bufora znakowego c. Inaczej program może się zwiesić albo jego działanie będzie nieprzewidywalne.

    3) czy na pewno potrzebujesz pracować z liczbami float? O ile to tylko możliwe znajdź sposób aby tego uniknąć. W tak małym procesorku wkładając obsługę float zjadasz strasznie dużo pamięci i mocy. Prawie zawsze można tego uniknąć. I warto tego unikać.
  • Pomocny post
    #20 3656749
    zumek
    Poziom 39  
    Posty: 3352
    Pomógł: 695
    Ocena: 52
    domelfm napisał:
    @zumek

    Mógłbyś podpowiedzieć mi troszkę co do tej procedurki do obsługi klawiaturki ?

    Jeśli wykonujesz jakieś powtarzalne operacje(wykonywanie identycznego zadania , ze zmiennymi parametrami) , to stosuj pętle lub funkcje , a nie wszystko na piechotę ;)
    
    //...
    #include <avr/pgmspace.h>
    //...
    prog_char tz[][4]={\
    						 {'1','4','7','*'},\
                       {'2','5','8','0'},\
    						 {'3','6','9','#'},\
    						 {'A','B','C','D'}\
    						};
    
    char przycisk()
    {
    unsigned char row,col,k; // lokalne zmienne pomocnicze
    unsigned char m=0x10; // maska do wyboru rzędu
    
    for(row=0;row<4;row++) //4 rzędy
    	{
    	dklaw=m; //wybór rzędu przez zmianę kierunku 
    	k=oklaw; //odczyt kolumn
    	for(col=0;col<4;col++) // 4 kolumny
    		{
    		if(!(k&0x01)) // sprawdzanie czy kolumna = 0
    			{
    			dklaw=0xF0; // wszystkie rzędy - wyjście(wykrycie naciśnięcia dowolnego przyc.)
    			return(pgm_read_byte(&tz[row][col])); // pobieramy kod klawisza z tablicy w pamięci programu
    			}
    		k>>=1; // kolejna kolumna na pozycję 0
    		}
    	m<<=1; // kolejny rząd jako wyjście
    	}
    return 0; // czyżby nic nie wciśnięto ? 
    }
    //...
    int main()
    //...
    dklaw=0x00;
    //...
    


    Funkcja zwraca kod klawisza lub zero , jeśli nie wciśnięty żaden klawisz.
    To tylko przykład sterowania wyborem rzędu/kolumny , przez zmianę kierunku jednego bitu portu :D

    domelfm napisał:
    Jakieś drobne wskazówki dlaczego się by ugotował?

    https://www.elektroda.pl/rtvforum/topic214704.html#1055982

    Piotrek
  • #21 3658288
    JacekCz
    Poziom 42  
    Posty: 8670
    Pomógł: 760
    Ocena: 1462
    Adamelek napisał:
    Od kilku tygodni siedzę w świecie mikrokonktrolerów i niestety widzę, że jest znacznie gorzej, niż Ci się wydaje. Znajomość języka C to nie 80% sukcesu, ale jakieś 2 do 5%, nie więcej.

    Zwłaszcza że stan znajomości C, przekazywany metodą z pokolenia na pokolenie jest tragiczny, nawet ludzie śmią brać kasę za kursy które są antydydaktyką.
    (nie będę się rozwodził: poszukaj mojej ksywki i słowa define)
    Gdyby trzeba było, 50% linii w tym wątku można by zje....ć.


    Jak sprawia trudnośc doprowadzenia programu w C do "parzystej ilości błedów" (*, para idzię w tę stronę, to na pewno myśl nie jest swobodna by fruwać po nóżkach i timingach procesora.
    Jak w C (oczywiście nie tylko w C) kodujesz podświadomie, myślisz o dziedzinie problemu.

    *) To taki żart zawodowy nad amatorskim kodem (głównie amatorskim, ale po latach uczymy się pokory i to mowa o swoim kodzie): przy parzystej ilości błędów kompensują się i nie widać ich na wierzchu. ALE SA NADAL.
  • #22 3658827
    Adamelek
    Poziom 14  
    Posty: 79
    Pomógł: 7
    Ocena: 1
    JacekCz,

    chyba jesteś zawodowym informatykiem, bo za cholerę nie wiem co miałeś na myśli w całej swojej wypowiedzi. :)
  • #23 3658902
    JacekCz
    Poziom 42  
    Posty: 8670
    Pomógł: 760
    Ocena: 1462
    Adamelek napisał:
    JacekCz,

    chyba jesteś zawodowym informatykiem, bo za cholerę nie wiem co miałeś na myśli w całej swojej wypowiedzi. :)


    Jakby pominąć inteligente ;-) wątki poboczne, pozostawmy to: słaba znajomośc, brak swobody w posługiwaniu się językiem, piach w trybach.

    Jak za granicą, jak słabo znasz język, zmęczony i zadowolony jestes że zrozumieliście sobie nawzajem co trzecie słowo (przeważnie nie przekraczajace tematów potrzeb biologicznych). Znasz to: "Ufff aleśmy się nagadali" (Ufff wreszczie się skompilowało->C nie jest np. Javą i to jest baaaardzo niewiele).
    Jak znasz płynnie język obcy, możesz sie przenieść mentalnie do istotnych problemów, negocjowac umowy, coś wspólnie projektowac itd.

    rzekłeś jestem zawodowym "C". Nie ukrywałem swojego wrażenia o kiszeniu się uP C-programistów na bardzo "niskich lotach". Niektórzy mnie zrozumieli i nawet cytowali.

    W powyższych źródłach 'nad-komentowanych', za pierona nie widzę wiedzy o pinach zerach i tajmingach, ze względu na ich nieczytelność.

    Natomiast nie jestem zawodowym PRowcem czy dyplomatą *) i za chaos wypowiedzi cię przepraszam.

    *) mówi żebyś poszedł do diabła, a ty cieszysz się na podróż
    **) lubie ten stary dowcip, odgrzany przez Izwiestiję, tym razem wariant z Wałęsą z drugiej jego strony:
    http://wiadomosci.wp.pl/kat,69074,wid,8742839,wiadomosc.html?ticaid=135a1&_ticrsn=3
  • #24 3658941
    domelfm
    Poziom 16  
    Posty: 238
    Pomógł: 5
    Ocena: 28
    Sorki za program który tworze w oparciu rórzne gotowe procedurki.
    Jeżeli ja bym to robił o chyba wogle nie było by komentarzy.
    Niestety ale na studiach na informatyce jeszcze nienauczyli mnie komentazy i chyba nienauczą.. :(

    Pozdrawiam wytrwałych co mają chęć czytać mój kod.

    Dodano po 2 [godziny] 12 [minuty]:

    @zumek

    Zmianiłem troche przycisk poniewaz ten od Ciebie niezabardzo chciał działać. Tak jest juz leiej czy nadal moge ugotować up?

    char przycisk() 
    { 
    unsigned char row,col,k; // lokalne zmienne pomocnicze 
    unsigned char m=0x10; // maska do wyboru rzędu 
    
    for(row=0;row<4;row++) //4 rzędy 
       { 
       //dklaw=m; //wybór rzędu przez zmianę kierunku
       klaw=~m; 
       k=0xFE; //odczyt kolumn 
       for(col=0;col<4;col++) // 4 kolumny 
          { 
          if(oklaw==(~m&k)) // sprawdzanie czy kolumna = 0 
             { 
             //dklaw=0xF0; // wszystkie rzędy - wyjście(wykrycie naciśnięcia dowolnego przyc.) 
             klaw=0x0F;
    		 return(tabc[0]=tz[row][col]); // pobieramy kod klawisza z tablicy w pamięci programu 
             } 
          k<<=1;
    	  k++; // kolejna kolumna na pozycję 0 
          } 
       m<<=1; // kolejny rząd jako wyjście 
       } 
    //dklaw=0xF0;
    klaw=0x0F; 
    return (tabc[0]='e'); // czyżby nic nie wciśnięto ? 
    } 


    A przy okazji to czym różni się zapis :
    
    
    prog_char tz[4][4]=	{
    	  	{'1','4','7','*'},  
                  		{'2','5','8','0'},  
                  		{'3','6','9','#'},  
                  		{'A','B','C','D'}
    			  				  		};
    .
    .
    .
    return(tabc[0]=pgm_read_byte(&tz[row][col]));
    

    od:
    
    
    char tz[4][4]={
    	     {'1','4','7','*'},  
                  	     {'2','5','8','0'},  
                  	     {'3','6','9','#'},  
                         {'A','B','C','D'}
    	                        };
    .
    .
    .
    return(tabc[0]=tz[row][col]);
    


    Oczywiście pozatym żę w 1 wyadku uzywamy biblioteki <avr/pgmspace.h> a w drugim nie.
    efekt jest ten sam ale dla procesora czy to jest to samo czy 1 pozycja jest szybsza od 2 albo czy 1 zajmuje mniej pamieci?

    Pozdrawiam :)
  • #25 3659093
    JacekCz
    Poziom 42  
    Posty: 8670
    Pomógł: 760
    Ocena: 1462
    domelfm napisał:
    Sorki za program który tworze w oparciu rórzne gotowe procedurki.
    Jeżeli ja bym to robił o chyba wogle nie było by komentarzy.
    Niestety ale na studiach na informatyce jeszcze nienauczyli mnie komentazy i chyba nienauczą.. :(

    Pozdrawiam wytrwałych co mają chęć czytać mój kod.

    Aż tak się nie przejmuj. Kod powinien być czytelny. Trzeba poznać metody (poczytać), rzucę kilkoma:
    a) znaczące nazwy
    b) wcięcia
    c) wyraźny cel kodu (funkcja na jeden cel, nie kilka)
    d) funkcje to czasowniki, dane to rzeczowniki (funkcja przycisk - NIE)

    Są różne szkoły komentowania, przychylam sie do komentowania we/wy funkcji (znajdź - jak chcesz -narzędzie doxygen. bardzo ciekawa idea), struktur danych, i naprawdę trudnych fragemntów kodu.
    Komentarz może utrudnić znaleznie błedu: np jeśli obiecuje, że funckja robi coś (robi, ale czasem nie), intensywna psychologicznie wymowa komentarza utrudni znalezienie błędu.
    Jeśli przyjmiemy "komentuj najtrudniejsze", a mamy skomentowane 80% linijek, czy 80% linijek jest dla autora najtrudniejsze?
    
    for(row=0;row<4;row++) //4 rzędy 
    


    Rozumiem jest (od zawsze) uczelnianą manierą komentowanie wszystkiego (skomentowane znaczy dobre) ;-)

    Byłem (laaata temu) na uczelni na stażu, ale nie pociągnąłem w tą dziecinada atmosfery mnie wyniosła stamtąd.

    domelfm napisał:


    A przy okazji to czym różni się zapis :
    
    
    prog_char tz[4][4]=	{
    	  	{'1','4','7','*'},  
                  		{'2','5','8','0'},  
                  		{'3','6','9','#'},  
                  		{'A','B','C','D'}
    			  				  		};
    .
    .
    .
    return(tabc[0]=pgm_read_byte(&tz[row][col]));
    

    od:
    
    
    char tz[4][4]={
    	     {'1','4','7','*'},  
                  	     {'2','5','8','0'},  
                  	     {'3','6','9','#'},  
                         {'A','B','C','D'}
    	                        };
    .
    .
    .
    return(tabc[0]=tz[row][col]);
    


    Nie wiem czym się rózni, ale powiela problematyczne:
    a) użycie zmiennych globalnych a zwłaszcza podstawianie pod nie. Trudno powiedzieć 'po góralsku' jaką JEDNĄ rzecz robi funkcja. A zwłaszcza w returnie z '='. '=' TO JEST PODSTAWIENIE (posiadające efekt uboczny). Niewprawnemu wyglada na porównanie.
    Użycie 'globala' mógłbym wybaczyć wyłacznie ze względu na ograniczenia uP, zapis do niego ....uważaj, brrrr.
    b) nazwy zmiennych globalych są nie znaczące (mało znaczące). Akurat co to jest 'tz' to nie wiem (nie skomentowane), jaka jest główna idea 'tz'
    c) są globalne a najczęściej powinny byc static (po złączeniu większej ilosci modułow zaczną się problemy)
    d) jak już MUSI byc cos z global: wydzielić funkcjonalnie do modułu, dac static co jest dla niego wewnętrzne (robocze dane) Upublicznić tylko to, co moduł chce prezentować. Dobra literatura o C. Kiepskiej nie czytaj.

    domelfm napisał:

    Oczywiście pozatym żę w 1 wyadku uzywamy biblioteki <avr/pgmspace.h> a w drugim nie.

    W sensie ścisłym nie jest to biblioteka.

    To migawki, hasła. Czytajcie dobre książki i patrzcie TYLKO na dobre kody.

    PS. Zobacz, już sam temat: "LCD i Wypisywanie na nim zmiennych typu int i float." łączy niepotrzebnie 2w1. W dobrym projekcie tu byś fizycznie wyświetlał (nie obchodzi cię co), tam byś montował treść, string do wyświetlania (nie obchodzi cię fizyczne wyświetlanie)
  • #26 3659761
    domelfm
    Poziom 16  
    Posty: 238
    Pomógł: 5
    Ocena: 28
    Niewiem czy to dobre ksiazki ale czytam tylko symfonie c++ grębosza i ansi c B.W.Kernighan i ktos tam..
    Niestety nie do wszystkich zalecen się stosuje np do zmiennych globalnych.. :) Które lubie i przez nie sobie zwracam wartosć.
    TZ to tablica znaków na matrycy znakowej.
    Funkcja zwraca wcisniety znak.
  • #27 3659772
    JacekCz
    Poziom 42  
    Posty: 8670
    Pomógł: 760
    Ocena: 1462
    domelfm napisał:

    Niestety nie do wszystkich zalecen się stosuje np do zmiennych globalnych.. :) Które lubie i przez nie sobie zwracam wartosć.

    Na ile linii (wielkości) programu i ile lat utrzymania programu dasz radę?

    domelfm napisał:

    TZ to tablica znaków na matrycy znakowej.
    Funkcja zwraca wcisniety znak.

    i daje efekt uboczny
    Okłamałeś mnie ;-)
  • #28 5020309
    pawel_5
    Poziom 13  
    Posty: 131
    Ocena: 1
    Pozwolę sobie włączyć się w dyskusję.
    Z mojej strony mogę powiedzieć, że programowanie było dla mnie sprawą dość odległą a już nie mówiąc o języku wyższego poziomu jakim jest C. Zacząłem od assemblera po czym przesiadłem się na C, uważam że to doskonała kolejność. Tak czy inaczej borykam się z problemem zaokrąglenia liczby float. Czy może szanowni koledzy mogli by napisać tylko koncepcję takiej "transformacji"?? ;) Nie pokuszę się zapytać o podprogramik :)
    np.: float = 0.122398734 ---> zaokrąglenie ---> float = 0.122

    Pozdrawiam

Podsumowanie tematu

✨ Dyskusja dotyczy programowania wyświetlaczy LCD w mikrokontrolerach AVR z użyciem języka C, ze szczególnym uwzględnieniem wyświetlania zmiennych typu int i float. Omówiono metody konwersji liczb całkowitych na ciągi znaków ASCII do wyświetlania, m.in. funkcję itoa z biblioteki stdlib.h oraz przesyłanie komend do LCD, takich jak ustawianie kursora (np. 0x80 dla pierwszej linii, 0xC0 dla drugiej). W przypadku zmiennych float zaproponowano bardziej złożone funkcje rozdzielające część całkowitą i ułamkową liczby, wykorzystujące funkcje modf i pow do formatowania zadaną liczbą miejsc po przecinku. Wskazano także na istnienie funkcji dtostrf w avr-libc, która konwertuje float na sformatowany string gotowy do wyświetlenia. Poruszono kwestie praktyczne, takie jak obsługa klawiatury matrycowej, implementacja przerwań w C oraz optymalizacja i czytelność kodu. Dyskusja zawierała przykłady kodu, sugestie dotyczące dokumentacji (karty katalogowe mikrokontrolerów, manuale avr-libc) oraz uwagi o trudności nauki programowania mikrokontrolerów i znaczeniu doświadczenia. Poruszono także temat konwersji kodu C na asembler oraz problemy z wielokrotnym wciskaniem klawiszy w klawiaturze matrycowej. Wskazano na przydatność pętli i funkcji zamiast powtarzania kodu oraz na znaczenie komentarzy i czytelności programu.
Wygenerowane przez model językowy.
REKLAMA