loader
Foto

Portkezelés alapjai a PIC32MX családnál

Pár éve már foglalkoztunk a PIC32MX mikrovezérlővel. Most újra elővesszük ezt a méltán népszerű mikrovezérlő családot, és elindítunk egy sorozatot. Néhány héten át rendszeresen fogunk közölni olyan cikket, amely ennek a mikrovezérlőnek egy adott belső perifériájának az alkalmazását mutatja be. Nem a pár évvel korábbi cikk lesz lemásolva, hanem egy adott témát járunk újra körbe. A sorozatunk első részében egy projekt létrehozását, illetve a portkezelés alapjait mutatjuk be. A sorozat készítésekor a MikroElektronika cég Fusion v7-es fejlesztőpaneljét fogjuk alkalmazni.

A WebElektronika korábbi cikkeiben már foglalkoztunk egy projekt létrehozásával, illetve a portkezeléssel is. A most induló sorozatunkban ugyanakkor olyan alkalmazásokat készítünk, és mutatunk be, amelyek a MikroElektronika Fusion V7-es paneljére készültek. Természetesen ezek a megoldások (minimális) módosítások elvégzése után más fejlesztőpaneleken is futtathatók.

A telepítés után indítsuk el a MpLAB X-et, és hozzunk létre egy projektet. Ezt a "File/New Project" menüpont kiválasztásával lehet (1 ábra) megtenni.

kep
1. ábra    Új projekt létrehozásának első lépése az MpLAB X-nél
 

Ezután a felbukkanó ablakban tudjuk kiválasztani a projektünk típusát. Használjuk a "Microchip Embedded/Standalone Project" sablont (2. ábra).

kep
2. ábra   Az új projekt létrehozásakor kiválasztásra kerül a használni kívánt sablon
 

Ezt követően tudjuk megadni az alkalmazni kívánt mikrovezérlőt, amely később a fejlesztés során természetesen megváltoztatható. Tekintettel arra, hogy MikroElektronika PIC Fusion V7-es paneljét fogjuk használni, amely a PIC32MX795F512L mikrovezérlőt tartalmazza, ezért ezt a családtagot válasszuk ki a legördülő listából (3. ábra).

kep
3. ábra    A PIC3MX795F512L mikrovezérlő kiválasztása
 

Az MpLAB X egy keretkörnyezet, amely alá tudjuk telepíteni a különböző nyelvű/verziójú fordítókat. A fordító kiválasztása a következő feladat, amikor egy új projektet hozunk létre (4. ábra). Természetesen utólag ez is megváltoztatható.

kep
4. ábra   A fordító kiválasztása
 

Az új projekt létrehozásának a végén kell megadni a projekt nevét, illetve az elérési helyét (5. ábra).

kep
5. ábra   A projekt nevének és elérési helyének a megadása a következő lépés
 

A projektünket sikeresen létrehoztuk, viszont még teljesen üres, nem tartalmaz egy file-t sem. Ezért adjunk hozzá egy "C" file-t, amely tartalmazni fogja természetesen a "main()" függvényt, amely a projekt belépési pontja. Tehát, amikor majd a lefordított, a mikrovezérlő memóriájába letöltött alkalmazásunkat elindítjuk, akkor a "main()" függvény fog először elindulni.
Kattintsunk az egér jobb gombjával a "Source Files" könyvtárnévre, majd a "xc32_newfile.c..." menüpontra (6. ábra).

kep
6. ábra   Új "C" file hozzáadása a projekthez
 

A felbukkanó ablakban adjuk meg a file nevét (portkezel1), majd mentsük el (7. ábra).

kep
7. ábra   Új file hozzáadása a projekthez
 

Ezután megjelenik az MpLAB X keretkörnyezetben a "portkezel1' szövegeditor, ahol a "C" kódunkat tudjuk írni. Másoljuk ide be a következő programot.

#include <p32xxxx.h>

main()
{
    TRISAbits.TRISA2 = 1;
    TRISD = 0x0000;
    
    LATD = 0x5555;
    
    while(1);
}

 

A "main()" függvényben először az "A.2" bitet beállítjuk bemenetre, majd a "D" port alsó 16 bitjét pedig kimenetre. Az "A" port többi bitjének az irányát nem állítjuk be.
Ezután a "D" port alsó 16 bitjének értéket adunk úgy, hogy a "LATD" regisztert írjuk. Hexadecimális formában kap értéket, de lehetőségünk van használni a kettes, a nyolcas, illetve akár a tízes számrendszert is. Mi törekszünk a tízenhatos számrendszer használatára.
Végül egy végtelen ciklus következik, amely megakadályozza azt, hogy a vezérlésünk kilépjen a "main()" függvényből. Ez azért fontos, mert ha kilépünk abból, akkor olyan memóriaterületről olvasnánk be a következő programrészletet, amelyet nem használunk, általunk nem ismert tartalmat futtatnánk.

A 8. ábrán látható az első programunk, ahol nem olvassuk még be a bemeneti értéket, noha az "A.2" bitet már beállítottuk bemenetre.

kep
8. ábra   A projekt első portkezelését megvalósító programja
 

Ezután fordítsuk le a projektünket, amely még egy szöveges file-t tartalmaz. Ebből kell nekünk ún. hexa file-t készíteni, amelyet már le tudunk tölteni a mikrovezérlő programmemóriájába. A fordításra több lehetőségünk is van, kattinthatunk a "kalapács" ikonra (9. ábra), de akár az "F11" funkcióbillentyűt is megnyomhatjuk.

kep
9. ábra   Fordítás
 

Egészítsük ki most az előbb bemutatott programot, vagy adjunk hozzá egy új file-t a projekthez. Fontos, hogy a projektünk csak egy "main()" függvényt tartalmazhat (a projekt csak egy helyről indulhat el), ezért az előző programot vegyük ki a projektből.

#include <p32xxxx.h>

main()
{
    TRISAbits.TRISA2 = 1;
    TRISD = 0x0000;
    
    while(1)
    {
        if(PORTAbits.RA2)
            LATD = 0xAAAA;
        else
            LATD = 0x5555;
    }
}

 

Az "A.2" bitet az előbb nem használtuk fel, de most alkalmazni fogjuk. Figyeljük ennek a portbitnek az állapotát a végtelen ciklusban. Azaz, állandóan beolvassuk az "A.2" portbit értékét. Ha a beolvasott érték logikai "1", akkor a "D" portra a 0xAAAA"-t (0B1010101010101010) tesszük ki, ha az "A.2" értéke "0", akkor a 0x5555-öt (0B0101010101010101).

Látható a "main()" függvény törzsében a különböző speciális file regisztereknek (SFR) az értékadása. Viszont mi van akkor, ha egy másik fejlesztőpanelt használunk, ahol nem a "D" porton helyezkednek el a LED-ek? Illetve az "A" port 2. bitjére nincsen nyomógomb kötve? Ekkor át kell írni az alkalmazásunkat, módosítani kell a speciális file regisztereknek a nevét, amely egy nagyobb méretű projektnél már nem szerencsés. Ezért használjunk definíciókat, illetve makrókat.

Nevezzük el az "A.2" bitet "BTN"-nek (Button), a 16 db LED-et meghajtó "D" portot "LEDs"-nek, illetve hozzuk létre azokat a definíciókat is, amelyek a portregiszterek irányának a beállításáért felelnek (BTN_Tr, LEDs_Tr). Továbbá, a későbbi alkalmazások miatt adjunk nevet a "D" port legkisebb helyiértékének is, a "D.0"-nak.

Ezeknek megfelelően módosítsuk az előbbi programot.

#include <p32xxxx.h>

#define BTN         PORTAbits.RA2
#define BTN_Tr      TRISAbits.TRISA2
#define LEDs        LATD
#define LEDs_Tr     TRISD
#define LEDD0       LATDbits.LATD0
#define LEDD0_Tr    TRISDbits.TRISD0

#define LEDsA()     { LEDs = 0xAAAA; }
#define LEDs5()     { LEDs = 0x5555; }


main()
{
    BTN_Tr = 1;
    LEDs_Tr = 0x0000;
    
    while(1)
    {
        if(BTN)
            LEDs = 0xAAAA;
        else
            LEDs = 0x5555;
    }
}

 

Látható, hogy a "main()" függvényben már nem szerepelnek a speciális file regiszterek nevei, hanem azoknak az elnevezései. A "LATD = 0xAAAA;" értékadás helyett már a "LEDs = 0xAAAA;" szerepel. 
Ha ezt az alkalmazást áttesszük egy másik fejlesztőpanelre, akkor elég átírni az adott definíciókat, a "main()" függvény törzsében lévő programsorokhoz nem kell nyúlnunk.

Természetesen alkalmazhatunk makrókat is, amelyek segítségével a "main()" függvényben a konkrét értékadások elhagyhatók. Módosítsuk újra az alkalmazásunkat a következő program szerint.

#include <p32xxxx.h>

#define BTN         PORTAbits.RA2
#define BTN_Tr      TRISAbits.TRISA2
#define LEDs        LATD
#define LEDs_Tr     TRISD
#define LEDD0       LATDbits.LATD0
#define LEDD0_Tr    TRISDbits.TRISD0

#define LEDsA()     { LEDs = 0xAAAA; }
#define LEDs5()     { LEDs = 0x5555; }
#define BTN_In()    { BTN_Tr = 1; }
#define LEDs_Out()  { LEDs_Tr = 0x0000; }


main()
{
    BTN_In();
    LEDs_Out();
    
    while(1)
    {
        if(BTN)
        {
            LEDsA();
        }
        else
            LEDs5();
    }
}

 

Látható, hogy itt már nincsenek értékadások (csak a makrókban), a "main()" függvényben csak makróhívások találhatók. A makróhíváskor nem történik stack művelet, tehát gyorsabban kerül végrehajtásra, mint a függvényhívás.

Módosítsuk újra az alkalmazásunkat, írjunk újabb makrókat (és egy függvényt), mert a feladatunk most az, hogy a "D.0" portbiten lévő LED-et villogtassuk. Tekintettel arra, hogy szeretnénk látni ennek a LED-nek a villogását, ezért késleltetést kell alkalmaznunk. A legegyszerűbb késleltetést (amely a legrosszabb megoldás is) egy szoftveres megoldással tudjuk megvalósítani. Egy for ciklus segítségével valósítjuk meg a késleltetést úgy, hogy 0-tól el fogunk számolni egy adott értékig. 

Módosítsuk újra a programunkat a következő példa szerint.

#include <p32xxxx.h>

#define BTN         PORTAbits.RA2
#define BTN_Tr      TRISAbits.TRISA2
#define LEDs        LATD
#define LEDs_Tr     TRISD
#define LEDD0       LATDbits.LATD0
#define LEDD0_Tr    TRISDbits.TRISD0

#define LEDsA()     { LEDs = 0xAAAA; }
#define LEDs5()     { LEDs = 0x5555; }
#define BTN_In()    { BTN_Tr = 1; }
#define LEDs_Out()  { LEDs_Tr = 0x0000; }
#define LED0_Tg()   { LEDD0 = ~LEDD0; }

void villog(unsigned int hatar);

main()
{
    LEDD0_Tr = 0;
    
    while(1)
        villog(10000);
}

void villog(unsigned int hatar)
{
    int k;
    for(k = 0; k < hatar; k++);
    //LATDbits.LATD0 = ~LATDbits.LATD0;
    //LEDD0 = ~LEDD0;
    LED0_Tg();
}

 

Látrehoztunk egy "villog()" nevű saját függvényt, amely rendelkezik egy "unsigned int" típusú paraméterrel (hatar). Amikor ezt a függvényt tehát meghívjuk a végtelen ciklusban, akkor átadunk egy előjelnélküli egész számot, ez lesz a "hatar" nevű paraméter értéke. A "main()" függvény előtt szerepel ennek a sajátfüggvénynek a szignatúrája.

A "villog()" függvény törzsében kapott helyett a szoftveres késleltetés megvalósítása, illetve itt hívjuk meg a "LED0_Tg()" nevű makrót is, amelynek a feladata annyi, hogy a "D" port 0. bitjének az értékét negálja. 
Látható, hogy a makróhívás előtt két programsor is található, amelyek kommentelésre kerültek, azaz nem kerülnek végrehajtásra. Azért szerepelnek a függvény törzsében, mert ha nem alkalmaznánk ezt a makrót, akkor használhatnánk a két programsor egyikét is.

Vajon meg tudjuk oldani ezt a feladatot átláthatóbban is? Noha ennek az alkalmazásnak egyszerű, átlátható a felépítése, de sajnos ez nincs mindig így. Ez a projekt egyszerű, az a feladata, hogy egy LED-et villogtasson, de egy bonyolultabb feladatot nem érdemes egy file-ban megvalósítani. 
Nézzük tehát meg azt, hogy hogyan lehet a makrókat, illetve a sajátfüggvényt külön file-okban elhelyezni, mert a célunk most az, hogy a "main()" függvény egy olyan file-ban legyen, amely nem tartalmaz más függvényt, illetve makrót.

Ezért adjunk hozzá a projekt "Header Files" virtuális könyvtárához egy file-t. Ehhez a jobb egérgombbal kell kattintani a könyvtárnévre (10. ábra).

kep
10. ábra    Új header file hozzáadása a projekthez
 

A felbukkanó ablakban adjunk nevet (panel.h) az új header file-nak (11. ábra).

kep
11. ábra    A "panel.h" header file hozzáadása a projekthez
 

A korábbi programunkból másoljuk át a definíciókat, illetve a makrókat a "panel.h" file-ba. Ezután töröljük ki ezeket a forrásfile-ból. Ezt követően a "Source Files" könyvtárhoz adjunk hozzá egy file-t (fv.c), amelyben elhelyezzük a "villog()" nevű sajátfüggvényünket (12. ábra).

kep
12. ábra   Új file (fgv.c) hozzáadása a projekthez
 

A "villog()" függvényt másoljuk be a "fgv.c" file-ba. Tekintettel arra, hogy ebben a függvényben meghvásra kerül egy makró, amely a "panel.h" file-ban van, ezért ezt a header file-t be kell hívnunk a "fgv.c" file-ban.

#include <p32xxxx.h>
#include "panel.h"

void villog(unsigned int hatar)
{
    int k;
    for(k = 0; k < hatar; k++);
    //LATDbits.LATD0 = ~LATDbits.LATD0;
    //LEDD0 = ~LEDD0;
    LED0_Tg();
}

 

Noha nem kötelező, de a sajátfüggvény szignatúráját (prototípus) is elhelyezhetjük egy másik header file-ban (fgvproto.h).

void villog(unsigned int hatar);

 

A projektünk tartalmaz tehát két forrásfile-t (külön vannak a "main()" és a "villog()" függvények), illetve két header file is megtalálható a projektben. Ezt a két header file-t is be kell olvasnunk a file legelején.
Látható a legújabb megoldásnál, hogy a "main() függvényünket tartalmazó forrásfile jelentősen egyszerűbb lett. Megtalálható a két header file beolvasása, illetve a "main()" függvény belsejében a "LEDD0_Tr" kap értéket, azaz, a "D" port 0. bitje digitális kimenet lesz. Ezután már a "while(1)" végtelen ciklus következik, amelynek a magjában egy másik file-ban implementált sajátfüggvény kerül meghívásra.

#include <p32xxxx.h>
#include "panel.h"
#include "fgvproto.h"

main()
{
    LEDD0_Tr = 0;
    
    while(1)
        villog(10000);
}

 

A 13. ábrán látható a projektünk végleges felépítése, illetve a "main()" függvény végleges verziója. Fontosnak tartjuk megjegyezni azt, hogy a sajátfüggvény prototípusát tartalmazó header file meghívása kikerült a végleges változatból.

kep
13. ábra   A "portkezeles" projekt felépítése és a "portkezel6.c" file tartalma
 

A 14. ábrán a "panel.h" file tartalma látható.

kep
14. ábra   A "panel.h" file-ban látható definíciók és makrók
 

Végül a 15. ábrán látjuk az "fgv.c" file-ban található "villog()" nevű sajátfüggvényt.

kep
15. ábra   A "villog()" függvény az "fgv.c" file-ban
 

 



Egyéb cikkek

További cikkeink ebben a témakörben

Régebbi cikkeink

Október közepén elindítjuk az Atmel 8 bites mikrovezérlőkről szóló sorozatunkat. Ehhez használnunk kell természetesen egy fejlesztőkörnyezetet is. Több ilyen is létezik, például a WinAVR, vagy az Atmel Studio. Mi az Atmel Studio-t fogjuk használni, e. . . .

A PIC18F mikrovezérlők ma is népszerű a fejlesztők körében. Noha 8 bites architektúráról beszélünk, számos érdekes és hasznos alkalmazás megvalósítható vele. Elég csak az USB-re, vagy akár az Ethernetre gondolnunk. Ezért a Szerkesztőség egy sorozat k. . . .

A Microchip által javasolt fejlesztőkörnyezet az MPLABX, amely felváltja az MPlab-ot. Használata nehézkesnek tűnhet, ezért megnézzük ennek az IDE környezetnek a használatát, készítünk egy egyszerű projektet, amely egy PIC32 mikrovezérlőre épül.. . . .