Ugrás a kezdőlapra

© Mohos Pál

Atmega 128 programozása C-ben

Utolsó módosítás: 2014. november 27. csütörtök   15:01

1.      Bevezető:

·         Készítsünk projektet, mint az ASM programnál, de GCC fordítót választva. (Ékezet nélkül, C meghajtóra)

·         Írjuk, vagy másoljuk bele a programunkat.

·         Fordítás után se Error, se warning ne legyen!    
main() előtt int kell! Meg return 0 a program végére.

2.      Gomb4-0 és LED7-0 használata

2.1        Gombállapot LED-re

·         G0..4 állapotának kijelzése a LED0..4 –en. (Amíg nyomjuk, világít a megfelelő LED)

2.1.1          Algoritmus:

1.       LED-ek és Gombok meghajtói irány beállítás. (Ledeké ki. Gomboké be)
Felhasznált adat:  Gomb 3-0 = PortG 3-0
                             LED   3-0 = PortB 7-4;   
                             LED      4 = PortD 4

2.       Gombok állapotának beolvasása.

3.       Kijelzés a LED-re

4.       Ugrás 2. -ra

2.1.2          C program:

2.1.2.1           Algoritmus alapján

·        

2.1.2.2           Egyszerűsítve:

·        

2.2        Disassembler:

·         Érdemes nézegetnünk az .lss kiterjesztésű disassambly fájlunkat. Az assembly programozásunkat biztosan segíti.

·        

2.3        Header fájlok1

·         Figyeljük a header fájlok tartalmát! Nem ördöngösség.
Fél óra nézegetés után jelentősen csökken a C nyelv misztikussága.

2.3.1          avr/io.h:

·         Ha az  atmega128 a projektben beállított, akkor beszerkeszti az iom128.h-t

 

·         Minden esetben még hozzárendeli a következő header fájlokat:

·          

2.3.2          Iom128.h

·         Tartalmazza a chipre jellemző konstansokat, protokat. Például:

ahol

Mivel
 
Ezért látható: DDRB=0x17 + 0x20 = 0x37 a memóriacíme a DDRB portnak.
A fordító a címből tudja, hogy pl. out vagy sts utasítással írhat.
Lásd a fenti *.lss listában:

2.4        Futófény oda-vissza:

2.4.1          Algoritmus:

·         Az időzítéshez a _delay_ms() függvényt használjuk.

·         A sorrend fontos. Az F_CPU konstanst a delay.h előtt definiáljuk!

#define F_CPU 4000000UL

#include <util\delay.h>

1.       LED portjainak iránya = ki

2.       végtelen ciklusban: a=1,2,4,…128, majd visszafelé. Minden a érték kijelzése, 800 msec-ig.

2.4.2          Program:

/*Mohos Pál*/

#include "avr\io.h"

#define F_CPU 4000000UL

#include <util\delay.h>

 

void led(unsigned char x)

{    PORTD=x;     //7-4 bitek LED-re

     PORTB=x<<4;  //3-0 bitek LED-re

     _delay_ms(800);

}

 

int main()

{    unsigned char a;

     DDRB= 0xF0; //LED-es Port irány ki.

     DDRD= 0xF0; //LED-es Port irány ki.

     while(1)

     {    for(a=1;  a!=0; a=a<<1) led(a);

          for(a=128;a!=0; a=a>>1) led(a);

     }

        return 0;

}

 

 

3.      RGB dióda használata:

3.1        Kigyújtás egymás után

·         Egymás után ismételve gyújtsuk ki az RGB színeket egyenként, majd egyszerre mindhármat.

·         Minden szín kigyújtás után legyen sötét szakasz.

3.1.1          Algoritmus:

·         PORTC7 és PORTE3..2 bit kimenet legyen

·         Végtelen ciklusban:
minden szin portbitjét egyenként 1-be állítjuk x ideig, majd 0-ba x ideig. (RGB színek egymás után)
minden szin portbitjét egyszerre 1-be állítjuk4* x ideig, majd 0-ba 4*x ideig. (fehérszerű)

·         Ugrás a Végtelen ciklus sorra

3.1.2          Program:

/*Mohos Pál*/

#include "avr\io.h"

#define F_CPU 16000000UL

#include <util\delay.h>

 

int main()

{      int x=1000;

    DDRC=128; DDRE=0b1100;

       while (1){

            PORTC=  0x80;_delay_ms(x); //Piros, de nálam ZÖLD

            PORTC=     0;_delay_ms(x);

            PORTE=0b0100;_delay_ms(x); //KÉK

            PORTE=     0;_delay_ms(x);

            PORTE=0b1000;_delay_ms(x); //PIROS

            PORTE=     0;_delay_ms(x);

            PORTC=0x80;PORTE=0b11000;_delay_ms(4*x);

        PORTC=0   ;PORTE=0;      _delay_ms(4*x);

       }

    return 0;

}

 

3.2        Színkeverés 24 biten.

·         Megoldás impulzus kitöltési tényezők beállításával.

·         Gyakorlatban ezt majd Interrupttal vezéreljük.

·         Az elvi működés megértéséért mutatjuk be ezt a számlálós példát.     
0-255.ig ismételgetve, felszámolva, 0-nál kigyújtjuk mindhárom színt.           
Minden értéknél komparálunk a színekhez rendelt RGB 8-8 bites (0—255) értékkel.            
Ha egyezik az érték, onnét leoltjuk az aktuális szint, a periódus végéig.

3.2.1          Program:

4.      Kijelző (7 szegmenses) használata:

·         A portra kiküldött adat folyamatosan kint marad. Tehát mindig az utoljára kiküldött értéket látjuk.

·         Egy időben csak 1 szegmens világíthat a HW kialakítása miatt. A szem tehetetlenségét kihasználva folyamatosnak látható a 4 szegmens és a kettőspont, ha az öt kijelzés felvillanási sorozata <20 msec (>50 Hz) alatt ismétlődik.

4.1        HW adatok:

·         PORTA        kifelé mind a 8 bit. (DDRA=0xFF)

·         PORTA7      Világítás bekapcsolása

·         PORTA6..4  szegmenscím.   (0=jobb szélső …3. Kettőspont a 4-es címen értékfüggetlen)

·         PORTA3..0   kijelzett érték. (0…9)

4.2        Algoritmus egy digithez:

·         HW inicializálás. (PORTA bitek kifelé.)

·         PORTA bitekre adat kiküldése.

4.3        Egyjegyű szám kijelzése:

 

/*Mohos Pál

Feladat:  x=3-at jelezzük ki a 0. szegmensen

 HW: PORTA7 =1 bekapcsolva a szegmens, ezért világít.

     PORTA6..4 -en szegmens címe=0..3 kettőspont=4

     PORTA3..0 biteken van a kijelzett érték, mely 0..9 lehet.*/

#include "avr\io.h"

 

int main()

{char x=3;

 DDRA=0xFF; PORTA=128 + (0<<4) + x;

 return 0;

}

//FONTOS! a 0<<4 –et zárójelezzük, mert nélküle a laborban 4+x-el shiftel!

4.4         Integer szám 0..9999 kijelzése:

4.4.1          Algoritmus:

Főprogram:

·         X=kijelzendő érték

·         PORTA bitek kifelé

·         SzegmBIN(x) meghívása

 

SzegmBIN program:

·         Integer szám szétbontása 4 db változóba a számjegyek szerint 10-es számrendszer alapján.
Először papíron próbálkozzunk 2 jegyű, majd 4 jegyű szám esetén egyenként, hogy számolnánk ki.
Aztán ebből készítsünk algoritmust, mely ciklusban dolgozik 4 szer.

·         Végtelen ciklusban az egyes számjegyek kijelzése.
Minden számjegynek címét (0..3) és értékét (x[i]) egyenként a PORTA-ra írjuk,
plusz a PORTA7-el engedélyezzük a világítást.
PORTA=128 + cím<<4 + x[i].  (Jelen esetben a bitenkénti VAGY ( | )  azonos a +-al. )
A kiírás után 5 msec-ig engedjük világítani az aktuális szegmenst.

4.4.2          Megjegyzés:

·         Ez a megoldás nem gyakorlatias, mivel a kijelzés a teljes processzoridőt igénybe veszi.

·         Gyakorlatban egy pl. 5 msec -ként érkező interruptra tesszük ki a következő szegmens adatait PORTA-ra.

4.4.3          Program:

/*Mohos Pál

  int x=0...9999 jelezzük ki a7 szegmense kijelzőnkön.

  x BIN kódban van szegmBIN(x) fv-el

 

 HW: PORTA7=1 világít a

     PORTA6..4 -en címzett kijelző, mely adata a

        PORTA3..0 biteken van.*/

#include "avr\io.h"

#define F_CPU 16000000UL

#include <util\delay.h>

 

void szegmBIN(int z)

{   int k; unsigned char x[4],i;

    for(i=0,k=1;  i<4;  i++,k=k*10) x[i]=(z/k)%10;  //z 4 db decimális számjegye x[]-be

  

    while(1)                           //4 digit kijelzése

      {     for(i=0;i<4;i++)

              { PORTA=128+(i<<4)+x[i]; //digitek kijelzése

                _delay_ms(5);

              }

            //menürendszernél: if(PING) {PORTA=0;break;}                

            //Ha lenyomtam egy menügombot elalszanak a LED-ek és kilépek a ciklusból. 

      }

}

 

int main(void)

{   int x=5678;

    DDRA=0xFF;

    szegmBIN(x);

    return 0;

}

4.5        Számláló 4 számjegyre:

/*Mohos Pál

  int x=0...9999 jelezzük ki a7 szegmense kijelzőnkön: szegmegmBIN(x) fv-el

  HW: PORTA7=1 világít a

      PORTA6..4 -en címzett kijelző, mely adata a

      PORTA3..0 biteken van.*/

 

#include "avr\io.h"

#define F_CPU 16000000UL

#include <util\delay.h>

 

void szegmBIN(unsigned int z)

{    int k; unsigned char x[4],i,j=25; //j-szer gyújtjuk ki mind a 4 szegmenst

    //integer számjegyeit tömb elemeibe teszem.

    for(i=0,k=1;i<4;i++,k=k*10) x[i]=(z/k)%10; // 4 db számjegy

   

     //4 digit kijelzése j alkalommal (j*20msec)  

     while(j--)     // a kijelzés 25*5*4=500 msec

     {    for(i=0;i<4;i++)

         {   PORTA=128+(i<<4)+x[i];  //digitek kijelzése

             _delay_ms(5);

         }       

     }

}

 

int main(void)

{unsigned int x=1234;

 DDRA=0xFF;

 while (1) szegmBIN(x++); //folyamatosan számlál

 return 0;

}

 

5.      3x4 –es billentyűzet lekérdezése:

5.1        HW kialakítás:

5.2        Működés:

·         Logikai 1 szinttel kijelölök egy-egy sort egymás után. (PORTC3, PORTC4…PORTC6)

·         Minden aktív sor esetén megnézem, hogy lenyomott bill. -re az oszlopban végigfutó 1 hatására kinyit-e az oszlophoz rendelt tranzisztor.
Példa: PINC=0, ha PORTC3=1 és az 1-es gomb le van nyomva. Egyébként PINC=1

·         A SW-ben vegyük figyelembe, hogy
A tranzisztor kinyitása később van, mint a PORTC x-re kiadott 1 szint. Jelterjedés és tranzisztor bekapcsolási idő miatt.
A tranzisztor bezárása  később van, mint a PORTC x-re kiadott 0 szint. Jelterjedés és tranzisztor kikapcsolási idő miatt.

5.2.1          Tranzisztor bekapcsolási idő mérése:

1.       Lenyomva tartom a mátrix 1-es bill.-jét.

2.       Elindítom a trbe() függvényt.

3.       Kijelölök egy nem figyelt sort, pl az alsót a PORTC6-al. 1 msec-ig.

4.       PORTC6 helyett a PORTC3-at teszemaktívvá.

5.       Figyelem PINC0 értékét, s amíg az 1, tehát amíg nem kapcsol be a tranzisztor addig számlálok felfelé a sor változóban. Egy számlálás egy kiolvasás, egy maszkolás, egy feltételvizsgálat és egy inkrementálás, tehát 4 óraperiódus ideig tart. Ez 16 MHZ-nél 250 nsec.

6.       EREDMÉNY: a sor szló az én panelemen mindig 0. 
Tehát a jelterjedés és a bekapcsolás < 250 nsec.

char trbe()

     {char sor=0;

           PORTC=1<<(6);_delay_ms(1);

           PORTC=1<<(3);

           while(PINC & 0b0001)++sor;//számlál, míg a tranyó nem kapcsol be COLL=1

          

      return sor;

     }

·          

5.2.2          Tranzisztor kikapcsolási idő mérése:

1.       Elindítom a trki() függvényt.

2.       A trki() a mátrix 0. sorát 1-be teszi. 1-es gomb lenyomásra vár.

3.       Lenyomom az 1-es gombot. Bekapcsol a PINC0-n a tranzisztor. (o szint lesz PINC0-n)

4.       Várok a teljes bekapcsolásig, a prellek lezajlásáig. (1-5 msec)

5.       Elektronikusan kikapcsolom a tranzisztort. (Következő sor lesz 1-es)

6.       Amíg nem kapcsol ki növelem a sor számlálót. Tehát, amíg PINC0=0 addig számlálok.
Egy számlálás ideje:egy PINC kiolvasás, egy maszkolás, egy feltételvizsgálat és egy inkrementálás, tehát 4 óraperiódus ideig tart. Ez 16 MHZ-nél 250 nsec.

7.       Eredmény az én panelem 40*250 nsec = 10usec. Tehát tranzisztor kikapcsolódásáig a processzor a 160 utasítást tud végrehajtani!
Ez az érték panelenként eltérő, mert erősen függ azt adott tranzisztor bétájától  ICB0 szivárgási áramtól, s a bázis tértöltési kapacitásától. Ezen értékek a hőmérséklettől is erősen függenek.
(Kivittem a panelt 0 fokos udvarra 4 percig, a kikapcsolási idő 750usec-al csökkent.)
Az én panelemen kicsi a tranzisztorok közti szórás: 39, 40, 40 *a 250nsec a kikapcsolási idő.

 

char trki()

     {char sor=0;

      PORTC=1<<(3);

      while(1)                        //lenyomásra   vár

           {if((~PINC) & 0b0001)      //ha bekapcsolt a tranyó

                      {_delay_ms(5);  //prell et kivárom

                       PORTC=1<<(1+3);//kikapcsolom a tarnyót

                       while(((~PINC) & 0b0001))++sor;// számlál a valós kikapcsolásig

                       break;

                      }        

           }

      return sor;

     }

5.3        c=getchm() készítése.

·         A program várakozik, míg egy bill-t lenyomunk.

·         Visszatérési érték egy tömb billyntyűgomb függő eleme. Tehát 0..9 vagy azok ASCII kódja, stb

 

5.3.1.1           Program.

/*Mohos Pál Billentyűzet beolvasása

Sor:

Oszlop:

 

*/

#include "avr\io.h"

#define F_CPU 4000000UL

#include <util\delay.h>

char t[]= {1,2,3,4,5,6,7,8,9,10,0,11};

char sor, x, oszlop, ascii;

char getch(void) {

    sor=0;

    while(1) {             //lenyomott bill-t keresünk 0…3 sorokban

        PORTC=(1<<(sor+3));//Aktív sor=1. PORTC6..3 biteken. 0b0xxx_x000 (6-3 bit kifelé)

        _delay_ms(50);     //Fesz. szintek terjedéséhez

        x=(~PINC)&7;       //A lenyomott=0, ezért negálok ~-el.

        if (x) break;      //Találtam 1 db lenyomott billentyűt

        sor++;

        if (sor==4) sor=0;

    }

    oszlop=0;

    while((x=x>>1)) oszlop++;    //oszlop=0,1,2  x=1,2,4 lehetett.

    ascii=t[3*sor+oszlop];       //Ahol t[] ASCII kódokkal egy tömb.

    return(ascii);

}

void led(unsigned char a) {

    PORTD=a;     //7-4 bitek LED-re

    PORTB=a<<4;  //3-0 bitek LED-re

//_delay_ms(800);

}

void hwinic() {

    DDRB= 0xF0; //LED-es Port irány ki.

    DDRD= 0xF0; //LED-es Port irány ki.

    DDRC= 0x78; //PPORTC6-3 ki 2-0 Be. 0b0111 1000

}

int main() {

    hwinic();

    while(1) led(getch());

}

5.4        Adott oszlop billentyűleütésre vár:

 

5.5        Adott sor billentyűleütésre vár:

6.      LCD kijelző (HD44780) használata:

6.1        Demo tanulmányozás:

Jobb egérgombbal tölthetők le a hex fájlok. Kedvcsinálónak biztosan jók.

·         mohosdemo1.hex

·         LCD ékezetes megjelenítés .hex

·         Szöveg shiftelve jobbra-ballra .hex

·         A labor paneljein (T-Bird és a bővítője) kipróbálható. Ha valakinél nem működne, kérek visszajelzést a fórumban. Köszönöm.

6.2        Kész drivert használva:

·         Programunkba szerkesszük bele az intézetünkben szokásos lcd.h és az lcd.c fájlokat! Letöltés után tegyük ezt a két fájlt az aktuális projekt mappájába.

·         A  jobb egérgombbal az lcd.c-t adjuk hozzá.

6.2.1          Betű megjelenítése:

 

6.2.2          String megjelenítése:

·         A kijelző megjelenítés memória pozíciói a tesztadatokból jól követhetők.

·         Első-harmadik, (a HW ezt 1. sornak tekinti), majd a    
második-negyedik sor. (a meghajtó HW chip ezt a 2. kijelzési sornak tekinti.)

#include "avr\io.h"

#include "lcd.h"

 

void LCD_puts( char *s)

        {while(*s) LCD_data(*s++);        

        }

 

int main()

        {LCD_init();

         LCD_puts("123456789101214161820222426283032343638404244464850");

 

         while(1);

         return(0);

        }

6.2.3          Szöveg és szám megjelenítése:

·         sprintf() fv-t használunk az átalakításra.

·         Növeld az lcd.c fájlban a clock() időzítéseit (5-ről 6-ra, 7-re), ha a kép szétesik. Az egyszerűség miatt időzítés van a foglaltsági jel vizsgálata helyett.

#include "avr\io.h"

#include "lcd.h"

#include <string.h>

 

void LCD_puts( char *s)

        {while(*s) LCD_data(*s++);         

        }

 

int main()

        {unsigned char s[81],uc=244;

         LCD_init();

         sprintf(s," Output=%3d V.  Ennek fele=%3d V        Masodik sor     Negyedik",uc,uc/2);

         LCD_puts(s);

 

         while(1);

         return(0);

        }

 

6.2.4           A driver függvényei:

6.2.4.1           LCD_data('A') ;   // unsigned char val

·         függvény az aktuális cursor pozícióra írja az A betűt, s következő pozícióra lép.
Az LCD meghajtóban 80 Byteos memória van a megjelenítendő karaktereknek.       
Az első-     harmadik sor a memória 1  -32. byteját jeleníti meg. 33-40 nem látszik, csak, ha shiftelünk.     
A második-negyedik sor a memória 41-72. byteját jeleníti meg.

6.2.4.2           LCD_init();

·         Alaphelyzetbe állítja a HW LCD-re vonatkozó részét.

LCD_goto(sor,oszlop);   // unsigned char row, unsigned char col

·         sor     =0,1,2,3.

·         oszlop=0….15.

6.2.4.3           LCD_command();  // unsigned char val

·         Parancsok használatához a meghajtó chip működése ismerete szükséges. Lásd lentebb.

6.3        Mi készítjük az LCD drivert. (szerkesztés alatt)

·         Ehhez az LCD megjelenítő, a Meghajtő chipje, és a konkrét bekötéseket kell ismernünk.

6.3.1          HW adatok:

6.3.1.1           Bekötés:

·         Részletesen a HW segédpanel fejezetben tárgyaltam

·         PORTE7..4= 2x4 bites Adat/ Parancs (I/O) átvitel

·         PORTF3 =E (Output)
LCD-be íráshoz __-__ Pozitív pulzus kell. (Lefutó élre veszi be az adatot, s kezdi a belső feldolgozást) Mindig Output irányú
két pulzus (lefutó él) __-__-__ kell, hogy a 2x4 bitnyi=8 bites adatot beírjunk.
Kiolvasásnál egy 3 állapotú meghajtót engedélyez az E=1 szint. E=0 nagy impedanciás a kimenet.

·         PORTF2 = R/-W (Output)   Olvasás =1;  Irás       =0; lesz az E pulzusra

·         PORTF1 = RS     (Output)   Karakter=1;  Parancs=0; átvitel lesz E pulzusra

6.3.2          Algoritmus:

1.       Inicializálás: Portirány, 4 bites adatátvitel, LCD alapbeállításai.

2.       String kivitele a DDRAM-ba. (Szöveg megjelenítése)

3.       Szöveg mozgatása

·         Megjegyzés: Következő adatbevitelre időzítéssel várok, nem a BUSY flag figyelésével.

6.3.3          Port irányok beállítása:

·         Induláskor minden felhasznált port bit kifelé állítandó.           
DDRE=DDRE | 0xF0 és            
DDRF=DDRF | 0xE0

6.3.4          4 bites adatátvitel beállítása

·         A program akár 8 akár 4 bites adatátviteli kezdeti beállítás esetén is tudja a 4 bitesre állítást.
(mivel a bekapcsolási HW Reset 8 bitesre állít, ha csak újra indítom a programot, akkor már a 4 bites mód az aktív.)
Ezért 3-szori paranccsal kiadjuk a 8 bitesre állítást (a felső 4 bit adat állítása a döntő). Ezáltal a chip biztos, hogy 8 bites módba kerül.
Ezután 1 paranccsal a 4 bitesre állítást végezzük.

Konstans név

A parancskód hatása

Érték

FINIC  

8 bites adatátvitel

0x30

FUNKC

4 bites adatátvitel a vezérlő 2 soros megjelenítő módban legyen

0x28

6.3.5          LCD chip alapbeállításai:

Konstans név

A parancskód hatása

Érték

CLR

AC=0, (adatbeírás a 0. DDRAM címtől kezd) a DDRAM a space kódjára töltődik e parancsra fel. ( Ezért jóval több időt igényel, mint a többi parancs)

1

HOME

Megszüntetése az eltolt ablaknak (megjelenítésnek). AC=0

2

ENTRY

Adatbeírás után automatikusan AC++ legyen;

6

BEKAPCS

Kijelzés bekapcsolva. Cursor aláhúzva, s villogó

15

6.3.6          Szöveg kiírása a displayre:

·         A DDRAM-ba például egy stringet kiírok, az alapbeállítások után.

6.3.7          Szöveg körbe shiftelése:

·         A MEGJ_ABLAK_JOBBRA konstanssal parancs kiadása 40-szer.

Konstans név

A parancskód hatása

Érték

MEGJ_ABLAK_JOBBRA

A 2 soros szövegvezérlő soronként balra csúsztat egy karakternyit. 40 karakterhely 1 kör.

Az 1. vezérlősor= 1. és 3. displaysor + 8 karakter.

A   2. vezérlősor= a 2. és 4. displasor+8 karakter.

24

6.4        Inicializáláshoz adatok:

/*LCD Vezérlő Parancsok**************************************************

Parancsok:                             7 6 5  4   3   2   1  0   HEXA

      Funkció                          0 0 1 8/4 2/1 t/7  x  x   $20-3F  FINIC 0x30 //8 bites adatátv

                                                                         FUNKC 0x28 //4 bites 2 soros

      Kijelző törlés                   0 0 0  0   0   0   0  1   $01     CLR      1 //0x20 feltöltés

      Kijelző és kurzor HOME -be       0 0 0  0   0   0   1  x   $02-03  HOME     2 //AdatCounter=0

      Karakter beviteli mód            0 0 0  0   0   1  I/D S   $04-07  ENTRY    6 //AC++ adatiráskor

      Kijelző Be/Ki és Kurzor          0 0 0  0   1   D   U  B   $08-0F  BEKAPCS 12 //CURKI 8

      Kijelző/kurzor eltolás           0 0 0  1  D/C R/L  x  x   $10-1F  JTOL      0x1C  BTOL  0x18

                                                                         CURMOZ 0x10

      CGRAM cím beállítás:   AC reg    0 1 A  A   A   A   A  A   $40-7F 

      Kijelző címének beállítása AC    1 A A  A   A   A   A  A   $80-FF 

      A kurzor az AC érték pozíciójában van.

     

      I/D: 1=>Adatbeírás után AC++, 0=> Adatbeírás után AC--

      S: 1=Adatíráskor kijelző eltolása be,     0=Ki

      D: 1=Kijelzés bekapcsolva,    0=Kijelzés kikapcsolva

      U: 1=Kurzor aláhúzás,         0=Cursor:nincs aláhúzás

      B: 1=Kurzor villog,           0=Cursor folyamatos

      D/C: 1=Kijelző eltolás, 0=kurzormozgatás 

            R/L:1=Jobbra tol, 0=Balra tol

      8/4: 1=1*8-bites adatkapcsolat, 0=2*4 bites adatkapcsolat

      2/1: 1=2 soros kijelzés, 0=1 soros kijelzés

      10/7: 1=5×10 pixel, 0=5×7 pixel

 

 Portbitek:

    PORTE7..4 = 2x4 bites Adat/ Parancs  (I/O) átvitel.

    PORTF3    = Lefutó éle írja be az adatot.

    PORTF2    = R/-W (Output)    Olvasás=1; Irás=0;

    PORTF1    = RS   (Output)    Parancs=0; Karakter=1

 

Irányregiszterek:

    DDRE=0xF0

    DDRF=0x0E

 */

 

6.5        String kijelzése:

·         Ebben a megoldásban nem figyelem a foglat (busy) bitet, helyette időzítéssel dolgozom. Ez a megoldás nem biztos, hogy működne egy lassabb órafrekvenciájú LCD vezérlővel, viszont picit egyszerűbb.

·         Sajnos sokan alkalmazzák. Hordozhastósáha hasonló Hw.hez nehézkes. Ezért javaslom a későbbiekben alkalmazott BUSY flag figyeléses adattovábbítást.

·         Figyelem: az első parancsokat, hogy a 4 bites átvitelt beállítsuk, csak időzítéssel oldhatjuk meg, mivel addíg nem tudható hogy hol van a BUSY flag.

 

//*****************************************************

//Mohos Pál********************************************

#include <avr/io.h>

#define F_CPU 16000000UL

#include <util/delay.h>

#include <avr/interrupt.h>

 

//*************Makrtó definíciók***********************

#define t(x)  (_delay_ms((x)))

#define tu(x) (_delay_us((x)))

#define swap(c) (((c & 0xf0) >> 4) | ((c & 0x0f) << 4));

#define E0    (PORTF=(PORTF & 0b11110111)) //beírójel E pin=0

#define RW0   (PORTF=(PORTF & 0b11111011)) //Írás RW pin=0

#define RS0   (PORTF=(PORTF & 0b11111101)) //IR parancsregiszter kijelölése. RS pin=0

#define E1    (PORTF=(PORTF | 0b00001000)) //beírójel=1

#define RW1   (PORTF=(PORTF | 0b00000100)) //Olvasás

#define RS1   (PORTF=(PORTF | 0b00000010)) //DR adatregiszer kijelölése

#define IRW RS0;RW0;E0;                    //parancsregiszter írás vezérlőjelek

#define DRW RS1;RW0;E0;                    //adartregiszer írás vezérlőjelek

 

//*************Parancs konstansok**********************

#define FINIC 0x30

#define FUNKC 0x28

#define CLR      1

#define HOME     2

#define ENTRY    6

#define BEKAPCS 15

#define CURKI 0x0C

#define CURJOBBRA 0x24

#define BTOL  0x18

 

#define EGY   0x80 //LCD 1. sor címe

#define KETTO 0xC0 //LCD 2. sor címe

#define HAROM 0x90 //LCD 3. sor címe

#define NEGY  0xD0 //LCD 4. sor címe

 

//**************Időzítés konstansok********************

#define TN 3  //millisec időzítés

#define TK 3  //mikorsec időzítés (nem ismert az LCD vez. órajel frekije)

 

//**************Globális változók*********************

typedef unsigned char u8;

//         0123456789ABCDEF0123456789ABCDEF01234567|123456789ABCDEF

u8 i, s[]="   MOHOS PAL         Minta               2011. Nov. 20";

 

//**************Saját függvények***********************

 

//**************Beíró pulzus***************************

void wr()

      { t(TN);

        E1;            //Írás előkészítés EN=1

        tu(TK);

        E0;            //Beírás IR-be lefutó élre. PORTF3= 1-->0;

        tu(TK);

       

      }

 

//******IR vagy DR regiszter írása 4 bites üzemmódban**

void lcd(u8 x)

      { PORTE= (PORTE & 0x0F)| (x & 0xF0);

        wr(); //felső 4 bit írása       

        PORTE= (PORTE & 0x0F)|((x&15)<<4);

        wr(); //alsó 4 bit ki.

    }

 

//**************FŐPROGRAM******************************

int main()

 

//************PORTIRÁNY********************************

      {DDRE=0xF0;  DDRF=0x0E; //-F3=E él beír; F2=RW=olvas -F2=ír  -F1=RS=parancs F1=ada

 

//************NÉGYBITES ADATÁTVITEL BEÁLLÍTÁSA*********

       IRW;                   //RW0;RS0;E0;  Parancsregiszterbe (IR) író jelek

             PORTE= (PORTE & 0x0F)| (FINIC & 0xF0); //Felső 4 bit módosítás

             wr(); //8 bitesre állás E=_-____

             wr(); //8 bitesre állás           //ha a kezdet 4 bites volt...

             PORTE= (PORTE & 0x0F)| (0X20 & 0xF0); wr();  // 4 bitesre állás

                  

//************LCD vezérlő alapbeállításai***************                  

             lcd(FUNKC);

             lcd(CLR);                    

             lcd(HOME);

             lcd(ENTRY);

//             lcd(BEKAPCS);

             lcd(CURKI);

//             lcd(CURJOBBRA);

             lcd(EGY);     

 

//************Szöveg megjelenítésa*********************            

       DRW;                         // DR adatregiszterbe írás

             i=0; while(s[i]) lcd(s[i++]); // string ki a displayre

 

//*************Szöveg mozgatása Eltolása***************

       IRW;                         // parancs (IR) regiszterbe írás

             for(i=1;i<81;i++)

                  {t(800);lcd(BTOL);}    //Szöveget balra tolja

    

       while(1);  

       return 0;

}

 

6.6        Stringtömb (4 soros) kijelzése:

·         Ez házi feladat.

6.7        Kijelző méreténél hosszabb string kijelzése:

·         Ez házi feladat.

6.8        CGRAM Saját karakterforma definiálása:

·         A string, amibe a karaktergeneráror (CGRAM) adatai vannak:

 

const u8 cgram[]={

0b10001  //0 Nulladik

0b11011  //1

0b10101  //2

0b10101  //3

0b10001  //4                 

0b10001  //5

0b10001  //6

0b00000  //7

0b01110  //0 Első

0b10001  //1

0b10001  //2

0b10001  //3

0b10001  //4

0b10001  //5

0b01110  //6

0b00000  //7

0b10001  //0 Második

0b10001  //1

0b10001  //2

0b11111  //3

0b10001  //4

0b10001  //5

0b10001  //6

0b00000  //7

0b01110  //0 Harmadik

0b10001  //1

0b10001  //2

0b10001  //3

0b10001  //4

0b10001  //5

0b01110  //6

0b00000  //7

0b00001  //0 Negyedik

0b10000  //1

0b10000  //2

0b01110  //3

0b00001  //4

0b00001  //5

0b11110  //6

0b00000  //7

0b1  //0 Ötödik

0b1  //1

0b1  //2

0b1  //3

0b1  //4

0b1  //5

0b1  //6

0b00000  //7

0b1  //0 Hatodik

0b1  //1

0b1  //2

0b1  //3

0b1  //4

0b1  //5

0b1  //6

0b00000  //7

0b1  //0 Hetedik

0b1  //1

0b1  //2

0b1  //3

0b1  //4

0b1  //5

0b1  //6

0b00000  //7

};

 

 

 

·         A karaktergenerátor írásakor az időzítésekre a programozónak kell ügyelnie.

·        

6.9        Saját karakterek megjelenítése

6.10    Függőleges szövegbeúsztatás (CGRAM programozással):

6.10.1      HW adatok:

·         A 0..7 értékű karakterkód az általunk definiált karakterképet jeleníti meg.

·         Csak a 0—7 kódú karaktereket használhatok.

·         A   CGRAM (karaktergenerátor) 8 betű*8képsor=64 Byte méretű

·          

6.10.2      Algoritmus:

·         Folyamatosan változtatom a CGRAM tartalmát úgy, hogy a karakterképek az üresből felfelé csússzanak.

·         Ezért a 8 soros (0..7) karakterképet először 0-zom,

·         .

·         majd csak
a 7. sorába írom a karakterkép 0. sorát. Egy karakter képe  a cgram[sor] tömbbe van a programban.
a 6..7 -be a  0..1
a 5..7 -be a  0..2
…..
0..7-be a 0..7 sorát.
Tehát for(i=0; i<8;i++)
        for(j=0;j<=i;j++ )
  {CGRAMSOR=}

·         Fenti beírást mind a 8 CGRAM karakterképre elvégzem.

6.10.3      Program:

 

6.11    lcd.h készítése:

·         lcdinic

·         lcd(x);

·         IRW

·         DRW

·         lcdget()

·         IRR

·         cgramki(s)

·         cgramvshift(s)

7.      Interrupt kezelés:

7.1        Általános forma:

#include "avr\io.h"

#include "avr\interrupt.h"

char i;   // Glob. vált. Interruptnak.(volatile ...)

//***********************************************************

int main()

        {//Hw inicializálások, + nem IT progik

         sei();      //globális IT engedély

         while(1);

         return 0;

        }

//***********************************************************

ISR(TIMER0_OVF_vect)  //0x20 IT        

        {

        }

7.2        Hétszegmenses megjelenítés:

·         Timer0 háromszög módban dolgozik.

·         244Hz at IT

·         244/4=61 Hz a képfrekvencia (nem villog)

#include "avr\io.h"

#include "avr\interrupt.h"

 

unsigned char het[4],i;

void hetszt(unsigned int z)

        {for(i=0;i<4;i++)

            {het[i]=z%10;

             z/=10;

            }

        }

int main()

        {unsigned int z=5876;

         DDRA=0xff;  //7szegm kij. meghajtója en+poz<<4+érték

                //Timer0 128 előosztás TCCR0|=5;

                //Timer0 háromszög (512 osztás) TCCR0|=64;

                //IT freki 16Mhz/256/512=244 HZ   

                //IT  TIMSK = 1  TOV

         TCCR0=64|5; //(1<<6)=64 haromszög,+ 128 előosztó (101) (lásd az assembly leírásnál 6.2.1)  TCCR0=0b01000101   

         TIMSK=1;    //TIMER0_OVF it engedély

        

         hetszt(z);

 

         sei();      //globális IT engedély

         while(1);

         return 0;

        }

 

ISR(TIMER0_OVF_vect)  //0x20 IT        

        {PORTA=128 | (i<<4) | het[i++];

         if(i>3) i=0;

        }

 

7.3        Timer1: 

·         1msec-os IT megvalósítása órához.

 

1 msec előállítása

 

1 msec előállítása

 

TCCRnB
alsó 3 bitje

előosztás

Órafreki (Khz)

előosztás

osztás

 

Órafreki (Khz)

előosztás

osztás

 

_001

1

16000

1

16000,0000

 

8000

1

8000,0000

 

_010

8

 

8

2000,0000

 

 

8

1000,0000

 

_011

64

 

64

250,0000

 

 

64

125,0000

 

_100

256

 

256

62,5000

 

 

256

31,2500

 

_101

1024

 

1024

15,6250

 

 

1024

7,8125

 

_000

stop

 

·         HW katalgus itt.

·         Beállítandó regiszterek: 8 Mhz esetén
TCCR1A: 0;                        Komparálási mód legyen normál
TCCR1B:  0b00001011;      64-es előosztás (0b 011) komparáláskor timer törlése (0b xxxx 1xxx) ez a CTC mód
OCR1A:   125 osztás

·         TIMSK:   0b00010000;     Timer1  IT1 COMPARE CTC engedélyezése

 

Secundum számláló 0..9

#include <avr/io.h>

#include <avr/interrupt.h>

volatile int  msec;         // globális változó, msec szlo. az IT rutinnak

volatile int  ido;          // globális változó secundum

int main()

{

      DDRA  = 0xFF;     // 7 szegmenses kijelzo meghajtás (KIMENET)

 

      TCCR1B = 0b00001011;// IT1-re CLK/64 eloosztást állítok be; alsó 3 bit '011',

                                   // alulról a 4. bit CTC (Clear Timer on Compare Match) mód

      TCCR1A = 0;           // alulról a 0. és a 1. bitet kell nézni (0)

      OCR1A = 125;            // 8 Mhz / 64 =125000 => 125000/125 (1ms-os IT1 osztás), de mivel '0'-ról kezd számolni, ezért '125-1=124'-et írunk be

      TIMSK = 0b00010000;     // Timer1  IT1 COMPARE CTC engedélyezése

      sei();                       // Processzor fogadja az interrup-ot

 

    while(1);                // végtelen ciklus

    return 0;

}

 

// ----IT1 interrupt kezelés ----------------------------

ISR(TIMER1_COMPA_vect)       // ISR : Interrupt Service Routine, 0x18 memóriacím TIMER1_COMPA_vect

 

{       msec++;

        if(msec>=1000)

            { ido++; msec=0;   //ido= secundumonként nő csak.

              PORTA=128 | 0 | (ido%10);

            }

}

 

8.      Összetett feladat:

8.1        Alap beállítások:

#include "avr\io.h"

#include "avr\interrupt.h"

 

#define F_CPU 16000000UL

#include <util\delay.h>  //_delay_ms();  _delay_us();

 

int main()

{      DDRG=0;          //G4-0 befelé

       DDRD=0xF0;       //LED 7-4 (baloldali 4 led)

       DDRB=0xF0;       //LED 3-0 (Jobboldali 4 led)

       DDRA=0xFF;       //Hetsz=EN | scim<<4 | data

       DDRC=0x78;       //MARTIX 6(alsó),5,4,3 sorok (1=aktív)

                        //       2(jobb),1,0 oszlop  (0=lenyomott)

       //DDRC=DDRC&128 | 0x78;      //Piros LED nincs változtatva.

       //DDRC=0xF8;        //Piros LED + bill. mátrix

       DDRC=128 | DDRC&0x7f;  //Piros(zöld)LED

       DDRE=0b1100 | DDRE&0b11110011; //Zöld(piros) Kék LED (LCD változatlan)

//TIMER 0

       TCCR0=64 |5;     //háromszögmód + 128 előosztó (Lásd Assembly programozás)

                        //16Mhz/512/128=244 HZ

       TIMSK=1;         //TIMER0_OVF_vect eng.

//Timer 1

       TCCR1B = 0b00001011;// IT1-re CLK/64 eloosztást állítok be; alsó 3 bit '011',

       TCCR1A = 0;     // alulról a 0. és a 1. bitet kell nézni (0)

       OCR1A  = 250;   // 16 Mhz / 64 =250000 => 250000/250 (1ms-os IT1 osztás) 

       TIMSK = 0b00010000; // Timer1  IT1 COMPARE CTC engedélyezése     

      

       sei();

       while(1);

       return 0;

}

//ISR címeket lásd a iom128.h fájlban

ISR(TIMER0_OVF_vect)         // IT0-ás interrupt megjövetele

{  PORTC=128;              

}

 

 

8.2        Számológép:

·         Készítsen kétműveletes számológépet, amely 1, 4, 7 számokkal végez összeadást vagy szorzást, ahol

·         az összeadás műveletét # jel, a szorzás műveletét a * szolgálja.

·         Az operanduszok bevitelére használja a mátrix billentyűzetet.

·         Az első operandus a 3. digiten

·         a műveleti jel a LED-eken,

·         a második operandus a 2. digiten,

·         az eredmény a 0. és 1. digiteken jelenjen meg a hétszegmenses kijelzőn:

·         G0: Első operandus bevitele (bevitelre csak 1,4,7 billentyű használható).

·         G1: Műveleti jel bevitele (bevitelre csak a *, # billentyűk használhatók).

·         G2: Második operandus bevitele (bevitelre csak 1,4,7 billentyű használható).

·         G3: Eredmény megjelenítése vezető nulla kikapcsolásával.

·         G4: A gomb lenyomásának idejére villogjon 1 másodperces ütemben a 3 színű LED-en a piros szín!

8.3        Megoldás:

#include "avr\io.h"

#define   F_CPU 16000000UL

#include <util\delay.h>

#include "avr\interrupt.h"

 

char matrix()

      {char sor,m[]={1,4,7};

       for(sor=0;;sor++)

            {PORTC=1<<(sor+3);

             _delay_ms(1);

             if(!(PINC&1)) break;  //m[sor] adja bill értékét

             if (sor==2) sor=-1;   //bill lenyomásra vár

            }

       return m[sor];

      }

 

char muv()

        {PORTC=1<<6;           //alsó sor #0* figyelése

         while(1)

                {if ((~PINC)&1) break; //* figyelése 0b0001 

                 if ((~PINC)&4) break; //# figyelése 0b0100

                }

         return ((~PINC)&5);           //~ mert 0 a lenyomott 0b0101      

        }

 

int main()

{char x,y,z;

 DDRC=0x78;  //Bill Matrix 0b_0_1111_000  en_sor4_cim3

 DDRA=0xFF;  //7 szegm kijelző portja

 DDRG=0;     //G4-0 gombok portja

 DDRB=0xF0;  //LED 3-0 a PORTB7-4

 

 while(1)

        {if(PING&1) {x=matrix(); PORTA=128+ (3<<4) +x;}

         if(PING&2)  PORTB=muv()<<4;

         if(PING&4) {y=matrix(); PORTA=128+ (2<<4) +y;}

         if(PING&8)

                 {z=x+y; //Ha nem szorzás, akkor összeadás kell.

                  if(PORTB&(1<<4)) z=x*y; //PORTB feltolva 1*

                  while(1)

                        {PORTA=128 + (0<<4) + z%10;_delay_ms(10);

                         PORTA=128 + (1<<4) + (z/10)%10;_delay_ms(10);

                        }

                 }      

        }

 return 0;

}

8.4        Kétszámjegyű (szűrt 2,5,8) adat beolvasása, kijelzése:

·         A matrix2() fv-ben először megvárom, míg nem lesz lenyomott billentyű a figyelt oszlopban.

·         Utána billentyű lenyomásig várok.

·         Kijelzéskor a vezető 0 ne világítson.

/*

G0: Elso 2 jegyű operandus bevitele (bevitelre csak 2,5,8,0 billentyu használható).

.

*/

#include "avr\io.h"

 

#define F_CPU 16000000UL

#include <util\delay.h>  //_delay_ms();  _delay_us();

char matrix2()

       {char sor=0,t[]={2,5,8,0},flag;

       

        do      //bill felengedésig vár

           {for(sor=0,flag=0;sor<4;sor++)

                    { PORTC=1<<(sor+3); _delay_ms(1);

                      if((~PINC) & 2) flag=1;   //le van még nyomva a bill

                    }

        }while (flag);

       

       

        while(1)

                {PORTC=1<<(sor+3); _delay_ms(1);

                 if((~PINC) & 2) break;  //2,5,8,0

                 if(++sor>3) sor=0;

                }

        return t[sor];

       }

 

int main()

{      char x,y,z;

       DDRG=0;          //G4-0 befelé

       DDRA=0xFF;       //Hetsz= EN | scim<<4 | data

       DDRC=0x78;       //MARTIX 6(alsó),5,4,3 sorok (1=aktív)

                        //       2(jobb),1,0 oszlop  (0=lenyomott)

       while(1)

            {if(PING & 1){z=matrix2(); z=10*z+matrix2();

                            while(1)

                              {PORTA=128+(0<<4)+z%10;_delay_ms(5);

                               if(z>9)PORTA=128+(1<<4)+z/10;_delay_ms(5);

                              }

                          }

            }

       return 0;

}

 

8.5        Három számjegyű adat beolvasás – IT-s kijelzés:

9.      Házi feladatok:

·         G0..4 lenyomás kombinációja jelenjen meg a LED0..4 –en

·         Futófény oda-vissza a LED soron

·         Futófényx: Két szélről induló LED-ek befelé mozogva világítanak, majd egymást keresztezve tovább, s ismételve.

·         Futófény G0 nyomogatására 1-1-et lépjen.

·         Számláló a LED soron. BCD kódban. (0..99-ig. 4 db LED csak 0..9 értéket jelezhessen ki. Ez a BCD.)

·         7 szegmenses kijelző 1 digitjére készítsünk számlálót 0..9-ig. G0..G3 lenyomásával a kijelzés helyét változtassuk.

·         7 szegmenses kijelzőre x=0..99 kijelzése.

·         7 szegm. kijelzőre x=0…9999 bármely értékének kijelzése.

·         G0 lenyomásainak számlálása, s kijelzése a LED-ekre.

·         G0 lenyomásainak számlálása, s kijelzése a 7-szegmenses kijelzőre.
a.) 0..9-ig
b.) 0..99-ig

·         Stopper. G0-ra indul, G0 újbóli lenyomására megáll, secundumban kijelzi az időt.

·         Stopper. G0-ra indul, G0 újbóli lenyomására megáll, perc:sec-ban jelzi ki az időt 7-szegmenses kijelzőre.

·         RGB LED 3 színének egymás utáni kigyújtása, köztük sötét szakaszban.
b.) G0 lenyomva tartása gyorsítsa, G4 lenyomva tartása lassítsa a gyújtogatás sebességét. Változtatás +-10% legyen. Max időköz= konstans1, Minimális 100 msec legyen.

·         RGB LED Szivárványskála ismétlődve.

·         Bill. Mátrixból beolvasott érték kijelzése a 7 szegmenses kijelzőn.
b.) következő szám beolvasásakor a korábbi értékek balra shiftelődjenek át.

·         Kapunyitó kódfelismerő. RGB piros. Jó kód után ZÖLD. (folyamatábrát is kérek).

·         LCD kijelzőre írjuk ki a saját nevünket.