A Soros Periféria Illesztő modulok használata

A fejezet tartalma:

Az SPI kommunikációs csatorna

Az SPI (soros periféria illesztő = Serial Peripheral Interface) busz kétirányú szinkron soros kommunikációt valósít meg két eszköz között. A kommunikációban résztvevő eszközök között master/slave (mester/szolga) viszony áll fenn.  Az SPI buszt "négyvezetékes" busznak is szokták nevezni, bár az SPI kommunikáció egyes változatai 3, 5, vagy ötnél is több vezetéket használnak. Az SPI busz kiterjeszthető: egy master több slave eszközhöz is kapcsolódhat, ám a kommunikációra kiválasztott slave eszközt egyedi választó vonallal (Slave Select) hardveresen kell kijelölni.

5. ábra: Több SPI eszköz kezelése külön választó vonalakkal

Az SPI busz kiterjesztésének egy másik módja az eszközök soros felfűzése (daisy chain). Ezt a módszert csak azoknál az eszközöknél használhatjuk, amelyek támogatják a felfűzést. Ilyen SPI eszközök például az AD5204 digitális potenciométerek, a MAX7219 LED vezérlő IC-k, MAX5233 dual 10-bites DAC-ok.

6. ábra: Felfűzött SPI eszközök vezérlése

Az SPI órajel polaritása és fázisa

Az SPI buszon az adatátvitel szinkronizálását a busz vezérlő eszköze (SPI master) által keltett szinkron órajel (SCK) biztosítja. Azon túlmenően, hogy a master megszabja az órajel frekvenciáját, a megszólított slave eszközhöz illeszkedően biztosítani kell az órajel megfelelő polaritását és fázisát. Az adatlapok tartalmazzák azt az információt, hogy az adott SPI eszköz milyen üzemmódban vagy üzemmódokban képes működni. Az alábbi ábrán az SPI busz jeleinek a különböző üzemmódokhoz tartozó jelalakjait szemléltetjük.

SCK - Az SPI busz szinkronizálást biztosító órajele. A master eszköz állítja elő.
SS - Slave select, azaz a slave eszköz kiválasztására szolgáló jel, melynek '0' állapota aktivizál. A master eszköz állítja elő. Slave eszköz esetében az SPI modul speciális kivezetését kell használnunk az eszköz kiválasztásához, de Master eszköznél elvileg bármilyen GPIO lábat használhatunk a slave eszköz megszólítására.
MOSI - A master eszköz kimeneti adatvonala (Master out, Slave in). A master eszköz állítja elő.
MISO - A slave eszköz kimeneti adatvonala, melyet a master olvas (Master in, Slave out). A slave eszköz állítja elő.

A két, szeparált adatvezeték lehetővé teszi, hogy szinkronizáltan kétirányú kommunikáció folyjon, tehát amíg a master bitenként kiléptet egy adatbájtot a MOSI vonalon,  ugyanakkor  bitenként beolvas egy másik bájtot, amit a slave eszköz küldött (egy előzőleg kapott parancsra válaszul).


7. ábra: Az SPI busz jelalakjai a különböző üzemmódokban

A lehetséges üzemmódok az órajel polaritásának és fázisának kombinációjaként állnak elő. Az órajel polaritását (CPOL) egyszerű megérteni: CPOL = 0 azt jelenti, hogy inaktív állapotban SCK alacsony szintű, CPOL = 1 esetén pedig az SCK órajel inaktív állapotban magas szinten van.

A fázis (CPHA) esete valamivel bonyolultabb: CPHA = 0 esetén az adatvonalak bekapuzása az órajel páratlan számú átmenetein történik (az ábrán rózsaszín vonalakkal jelölt időpontokban). CPHA = 1 esetén pedig a páros számú átmeneteknél (kék vonalakkal jelölt időpontokban) történik az adatvonalak bekapuzása.

Fentiek alapján az órajel polaritás és fázisa az alábbi négy üzemmódot jelenti:
Üzemmód
CPOL
CPHA
0
0
0
1
0
1
2
1
0
3
1
1

A FRDM-KL25Z kártya SPI perifériái

Az MKL25Z128VLK4 mikrovezérlő két SPI modult tartalmaz (SPI0 és SPI1), amelyek az alábbi tulajdonságokkal rendelkeznek:

Az SPI portok konfigurálása

A konfigurálás egyik fontos eleme, hogy kivezetéseket rendeljünk az adott perifériához. Az alábbi táblázatban láthatjuk az SPI modulokhoz rendelhető kivezetéseket. A MOSI és MISO oszlop azért közös, mert lényegében megegyeznek, a benne felsorolt kivezetések bármelyike konfigurálható MOSI vagy MISO kivezetésként.

1. táblázat: Az SPI modulokhoz rendelhető kivezetések
SPI modul
SCLK
MOSI/MISO
SS
SPI0
PTA15, PTC5, PTD1
PTA16, PTA17, PTC6, PTC7, PTD2, PTD3
PTC4, PTD0
SPI1
PTB11, PTD5, PTE2
PTB16, PTB17, PTD6, PTD7, PTE1, PTE3
PTB10, PTD4, PTE4
Megjegyzés: Az Arduino kompatibilis bekötéshez a táblázatban kövéren szedett kivezetések tartoznak: MOSI=PTD2, MISO=PTD3, SCLK=PTD1, SS=PTD0

Azt a FRDM-KL25Z Pinouts dokumentumban találjuk meg, hogy az adott funkció kiválasztásához milyen kódot kell megadni a megfelelő portvezérlő regiszter MUX bitcsoportjába.

Az SPI modulok engedélyezése

Az SPI modulok működését a többi soros perifériához hasonlóan a Rendszer-integrációs Modul SIM_SCGC4 regiszterében lehet engedélyezni, a megfelelő bit '1'-be állításával. Az ábrán csak a regiszter második felét mutatjuk.



Az SPI modulok regiszterkészlete

Az SPI modulok azonos regiszterkészlettel rendelkeznek. Az SPI0 modul báziscíme 0x4007 6000, az SPI1 modulé pedig 0x4007 7000. Az alábbi táblázatban az SPIx regisztereknek (ahol x = 0, vagy 1) csak a báziscímekhez képesti eltolási címét (ofszet cím) adtuk meg.
Ofszet cím
Regiszter neve, funkciója
Méret
Elérés
Reset
0x0000
SPIx_C1 control register (1. vezérlő regiszter)
8 bit
R/W
0x04
0x0001
SPIx_C2 control register (2. vezérlő regiszter) 8 bit
R/W
0x00
0x0002
SPIx_BR baud rate register (adatsebesség regiszter) 8 bit
R/W
0x00
0x0003
SPIx_S status register (állapotjelző regiszter) 8 bit
R
0x20
0x0005
SPIx_D data register (adatregiszter) 8 bit R/W 0x00
0x0007
SPIx_M match register (adategyezés regiszter) 8 bit R/W 0x00
Az SPIx_C1 regiszter

A konfigurációs regiszterek közül ez a legfontosabb, mivel ebben engedélyeznünk kell a modult (SPE), itt állítjuk be a master vagy slave módot (MSTR), s az órajel polaritását és fázisát (CPOL, CPHA). A konfiguráció tartamára célszerű letiltani a modult, s csak a konfiguráció legvégén állítsuk '1'-be az SPE bitet!


SPIE - Megszakítás engedélyezése SPI vételi puffer megtelése (SPRF) illetve üzemmód hiba (MODF) események bekövetkeztekor (0: megszakítás tiltás, 1: megszakítás engedélyezés)
SPE - Az SPI modul működésének engedélyezése (0: tiltás, 1: engedélyezés)
SPTIE - Megszakítás engedélyezése ha az SPI adatküldő puffere üres (SPTEF) eseménykor (0: megszakítás tiltás - lekérdezéses mód 1: megszakítás engedélyezés)
MSTR - Master / Slave mód választása (0: slave mód, 1: master mód)
CPOL - Órajel polaritása (0: nyugalmi szint alacsony, 1: nyugalmi szint magas)
CPHA - Órajel fázisa (0: első átmenet a bitidő felénél, 1: első átmenet az első bitidő elejénél)
SSOE - Ez a bit csak master módban és a MODFEN bit '1' állapotában hatásos (0: az SS kivezetés MODFAULT bemenet legyen, 1: az SS kivezetés automatikusan kezelt SS kimenet legyen).
LSBFE - Bitsorrend választása (0: a legmagasabb helyiértékű bit megy ki először - MSB-first mód, 1: a legalacsonyabb helyiértékű bit megy ki először - LSB-first mód)

Az SPIx_C2 regiszter

A konfigurációs regiszterek közül ez kevésbé fontos számunkra, hagyjuk a RÉSET során beállított 0x00 értéken!



SPMIE - Megszakítás engedélyezése adategyezés esetén (0: megszakítás tiltás, 1: megszakítás engedélyezés)
TXDMAE - DMA adatküldés engedélyezése (0: tiltás, 1: engedélyezés)
MODFEN - Master módban "módbeállítás hiba" fogadásának engedélyezése
BIDIROE - Kétirányú mód esetén (amikor SPC0 = 1) az adatáramlás irányát állítja be (0: bemenet, 1: kimenet)
RXDMAE - Adatfogadás DMA támogatással. Ha ez a bit '1', akkor DMA átvitel történik, ha SPRF és SPE egyaránt '1'.
SPISWAI - SPI leállítása Stop módban (0: az SPI WAIT módban is működik, 1: az SPI leáll, amikor az MCU leáll)
SPCO - Kétirányú adatvonal engedélyezése (0: szétválasztott adatvonalak, 1: közös, kétirányú adatvonal)

Az SPIx_BR regiszter

Ebben a regiszterben állíthatjuk be az SPI modul órajelét (az SCK jel frekvenciáját). A 8 bites regiszter két felében egy előosztási és egy osztási  arányt állíthatunk be.



SPPR - Előosztási arány (0 - 7 közötti értéket írhatunk bele)
SPR - Frekvenciaosztási arányt írhatunk bele (csak a 0 - 8 közötti értékek érvényesek!)


Az adatsebesség:  fSPI = fIN /((SPPR +1) * 2(SPR+1)) , ahol fIN az SPI0 modul esetén a buszfrekvenciát, SPI1 esetén pedig a CPU frekvenciát jelenti.

Például 48 MHz-es CPU frekvencia és 24 MHz-es buszfrekvencia esetén az 0x53 érték beírása az alábbi adatsebességeket eredményezi:
SPI0->BR = 0x53;  // fspi = 24 MHz / (6 * 2^4) = 24 MHz/ (6*16) = 0.25 MHz = 250 kHz
SPI1->BR = 0x53; // fspi = 48 MHz / (6 * 2^4) = 48 MHz/ (6*16) = 0.50 MHz = 500 kHz

Az SPIx_S regiszter

Az állapotjelző regiszter az alábbi négy bit beállításával jelzi az SPI modulhoz kapcsolódó események bekövetkezését:


SPRF - SPI vételi puffer megtelt (0: nincs kiolvasni való adat, 1: adatbeolvasás befejeződött, a vett adat kiolvasható). A jelzőbit automatikusan törlődik, ha olvassuk az SPRF jelzőbitet, majd kiolvassuk az SPIx_D adatregisztert.
SPMF - Adategyezés jelzése. Ez a bit akkor áll '1'-be, ha beérkezett egy bájt (SPRF = 1) és a beérkezett adat megegyezik azzal, amit az SPIx_M match regiszterbe írtunk. (0: nincs egyezés, 1: egyezés van)
SPTEF - Az SPI adatküldő puffere üres, újabb írásra kész (0: az adatküldő regiszter foglalt 1: az adatregiszter felszabadult)
MODF - Üzemmód beállítási hibajelzés érkezésének jelzése  (0: nem érkezett hibajelzés, 1: hibajelzés érkezett)

Az SPIx_D regiszter


Az adatregiszter íráskor és olvasáskor valójában fizikailag két, különálló regisztert ír vagy olvas. Küldéskor ebbe írjuk a kiküldendő bájtot, olvasáskor pedig ezen a címen olvashatjuk ki a beérkezett adatot. Az adat kiolvasása egyúttal automatikusan törli az SPRF jelzőbitet is.

Az SPIx_M regiszter

Ebbe a regiszterbe írhatjuk bele azt a számot, ami a hardveres adategyezés figyelésének az alapja. Ha az SPI modul által vett adat megegyezik az itt beállított értékkel, akkor az SPIx_S állapotjelző regiszter SPMF bitje '1'-be áll. Az SPIx_C2 vezérlőregiszter SPMIE bitjének '1'-be állításával megszakítást is engedélyezhetünk az adategyezési eseményhez.

Mintaprogramok

Program8_1: Adatküldés az SPI0 csatornán

Ezen az egyszerű mintapéldán keresztül bemutatjuk az SPI0 csatorna konfigurálását és az adatküldést. Hogy láthassuk is az eredményt, két 74HC595 shift regiszterből (léptetőregiszter) építettünk egy SPI perifériát, amellyel 2 db. HD1131R típusú 7 szegmenses LED számkijelzőt vezérelünk.




A bemutatott kapcsolás közös anódú kijelzőkhöz készült, de könnyen átalakítható közös katódú kijelzőkhöz is.



A kapcsolást hevenyészett kivitelben egy 3x7 cm-es próbapanelon is megépíthetjük. Kicsit sok és csúnya a vezetékezés, de a hátoldalon senkit nem zavar...




Bekötések
  1. A 74HC595 shift regiszter soros bemenete a 14. láb (SER), kimenete pedig a 9. láb (QH*). A shift regiszterek tetszőleges hosszúságban felfűzhetők. Arra kell csak ügyelnünk, hogy n db. IC felfűzése esetén n db. adatbájtot küldjünk ki, s a küldést a láncban a legtávolabb eső IC-be küldeni kívánt bájttal kezdjük. A láncban ez első IC SER bemenetére vezetjük ki a mikrovezérlő SPI0 moduljának MOSI kimenő adatvezetékét (PTD2 kivezetés)
  2. A shift regiszter léptetését az SCK jel végzi, s mivel a léptetés szinkronban történik, ezt a jelet a felfűzött lánc minden tagjához el kell vinni. Ide kötjük a mikrovezérlő SPI0 moduljának órajelét (PTD1 kivezetés). Sok IC felfűzése esetén a közösített órajel miatt ezt a vonalat pufferelni kell. Esetünkben a két IC meghajtása nem okoz gondot a mikrovezérlőnek.
  3. Az SS (Slave Select) eszközkiválasztó jel, amit itt az RCK bemenetekre kötöttünk rá, valójában nem az eszköz kiválasztására szolgál, hanem a kiválasztó jel visszaállásakor (felfutó él) a kettős pufferelésű 74HC595 shift regiszter párhozamos kimeneti adattároló regiszterébe kapuzzuk át az adatokat. A soros regiszteren átáramló adatok tehát mindaddig nem befolyásolják a kimenetek állapotát, amíg az SS jel alaphelyzetbe nem áll (nyugalmi szintje magas). A kettős pufferelés helyes működéséhez tehát fontos követelmény, hogy csak a felfűzött regiszterek számának megfelelő adatmennyiség kiküldése után állhat vissza az SS jel alaphelyzetbe, amikor minden bit a megfelelő helyiértékre került. Ezért az ilyen, több-bájtos tranzakcióknál nem használhatjuk az SS jel automatikus vezérlését, mert az minden bájt után alaphelyzetbe állítaná azt, ami esetünkben a kijelzőn zavaró villózásokhoz vezetne.
  4. A 74HC595 IC 13. lába a kimenetek engedélyezésére szolgál, alacsony szintre kell húzni (mi fixen a GND-re kötöttük).
  5. A 74HC595 IC 10. lába (SCL) alacsony szintre húzásával az adatregisztereket törölhetnénk. Ezt a lehetőséget sem használjuk, az SCL lábakat fixen magas (VCC) szintre kötöttük.
Az alábbi programban a számokat 0-tól 99-ig íratjuk ki, kiírásonként fél másodpercet várunk.  A rendszer órajelét CPU: 48 MHz, Busz: 24 MHz-r állítottuk. Az SPI frekvenciát szokatlanul alacsony értékre, 125 kHz-re állítottuk (/6 és /32 osztók), hogy egy PICkit2 programozó logikai analizátor funkciója segítségével a jeleket vizsgálhassuk.

A hétszegmenses kijelzők meghajtásához a a számjegyeket kódolni kell. A kódolást most szoftveresen oldjuk meg, egy 10 elemű táblázat segítségével. A kódolandó számjegyet indexként használjuk, s a táblázatból kiolvasott tömbelem egyes bitjei a  kijelző szegmensek  vezérlésére szolgálnak (a, b, c, d , e, f, g  és tizedespont). például az 1-es kódja 0b01100000, tehát csak a b és c szegmens világít.

1. lista: Program8_1/main.c listája
/* program8_1: Adatküldés az SPI0 csatornán
* A program 0-tól 99-ig írja ki a számokat egy kétjegyű kijelzőre.
*
* Hardver követelmények:
* Két sorbakötött 74HC595 shift regiszter felhasználásával
* két darab hetszegmenses LED kijelzőt vezérlünk. Az általunk használt
* közös anódú kijelzők miatt a kimenő adatokat komplementálva kell kiküldeni.
*
* Arduino kompatibilis lábkiosztást használunk:
* D13 (PTD1): SPI SCK
* D12 (PTD3): SPI MISO (ebben a programban nem használjuk)
* D11 (PTD2): SPI MOSI
* D10 (PTD0): SPI SS
*
* A program forrása: Mazidi et al., Freescale ARM Cortex-M Embedded Programming
* http://www.microdigitaled.com/ARM/Freescale_ARM/Code/Freescale_ARM_codes.htm
*
* A tankönyvi programot alaposan átdolgoztuk, csak távolról hasonlít az eredetire...
* A rendszer órajelek beállítása: CPU 48 MHz, Bus 24 MHz
*/

#include "MKL25Z4.h"
// Számjegy kódok 7-szegmenses kijelzöhöz
const unsigned char digit [10] = {
0xFC, // 0b11111100 - 0
0x60, // 0b01100000 - 1
0xDA, // 0b11011010 - 2
0xF2, // 0b11110010 - 3
0x66, // 0b01100110 - 4
0xB6, // 0b10110110 - 5
0xBE, // 0b10111110 - 6
0xE0, // 0b11100000 - 7
0xFE, // 0b11111110 - 8
0xF6}; // 0b11110110 - 9

void SPI0_init(void);
void SPI0_write(unsigned char data);

//---------------------------------------
// Késlelteto függvény 48 MHz órajelhez
//---------------------------------------
void delayMs(int n) {
int i, j;
for(i = 0 ; i < n; i++)
for (j = 0; j < 8010; j++);
}


int main(void) {
unsigned char n, d0, d1;
SPI0_init(); // Az SPI0 modul konfigurálása
while(1) {
for(n=0; n<100; n++) {
d1 = ~digit[n/10]; // Elso számjegy szegmensei
d0 = ~digit[n%10]; // Második számjegy szegmensei
PTD->PCOR = 1; // SS aktiválása
SPI0_write(d0); // Egyesek kiküldése
SPI0_write(d1); // Tízesek kiküldése
PTD->PSOR = 1; // SS deaktiválása
delayMs(500);
}
}
}

void SPI0_init(void) {
SIM->SCGC5 |= 0x1000; // Port D engedélyezése
PORTD->PCR[1] = 0x200; // PTD1 legyen SPI SCK */
PORTD->PCR[2] = 0x200; // PTD2 legyen SPI MOSI */
PORTD->PCR[0] = 0x100; // PTD0 legyen GPIO módban
PTD->PDDR |= 0x01; // PTD0 kimenet legyen (SPI SS)
PTD->PSOR = 0x01; // Kezdetben '1' legyen

SIM->SCGC4 |= 0x400000; // Az SPI0 modul engedélyezése
SPI0->C1 = 0x10; // SPI letiltása, master mód
SPI0->C1 |= 0x01; // LSBFE = 1 (elöször LSB-t küldjük)
SPI0->C2 = 0; // Alapértelmezett beállítások
SPI0->BR = 0x54; // Baud rate = 125 kHz (/6 és /32 osztók)
SPI0->C1 |= 0x40; // Az SPI modul engedélyezése
}

void SPI0_write(unsigned char data) {
volatile char dummy;
while(!(SPI0->S & 0x20)) { } /* wait until tx ready */
SPI0->D = data; /* send data byte */
while(!(SPI0->S & 0x80)) { } /* wait until tx complete */
dummy = SPI0->D; /* clear SPRF */
}
A program által generált SPI jeleket egy logikai analizátorral is megvizsgálhatjuk. Az egyik lehetőség erre egy PICkit2 programozó készülék használata, ha van kéznél egy. Ennek logikai analizátor funkciója elég korlátozott (max 1 MHz mintavételi frekvencia), de cserébe könnyen használható.

Az alábbi ábrán azt a pillanatot kaptuk el, amikor a kiküldött szám 41 volt, az ezekhez tartozó szegmensvezérlő kódok 0b01100110, illetve 0b01100000. Az SPI0 MOSI kimenentén ezt komplementálva és fordított bitsorrendben kellett kiküldeni (a kapcsolás kialakítása diktálta így). A kiküldött adatsor binárisan felírva 11111001_10011001, s ezt látjuk a logikai analizátoron is. A jelek felcímkézését és a kiolvasott adatok értelmezését (1, vagy 0) utólag szerkesztettük bele az ábrába, a jobb érthetőség kedvéért.




Program8_2: számkijelző vezérlése MAX7219 IC-vel

Ebben a programban egy Maxim Integrated MAX7219típusú LED vezérlővel ellátott, 8-digites, hétszegmenses számkijelző modult használunk, ami SPI illesztőfelülettel rendelkezik. Az előző programban használt kijelző csupán két számjegyű volt, mégis alkatrész temetőnek bizonyult, mert minden számjegyhez kellett egy-egy shift regiszter, s minden szegmenshez egy-egy áramkorlátozó ellenállás. Többjegyű (4-8 digites) kijelzők megvalósítása ilyen módon nem hatékony. Ilyen esetekben célszerű speciális LED vezérlő IC-ket használni.



Az általunk használt MAX7219 IC 2x8 kimenettel rendelkezik, melyek közül SEGA, SEGB,..SEG_DP szegmensvezérlő lábak áramforrásként, a DIG0, DIG1..DIG6 számjegyvezérlő kimenetek pedig áramnyelőként működnek. Fentiekből következően közös katódú 7-szegmenses kijelzők meghajtására alkalmas. A kijelzett számjegyek, illetve képpontok száma természetesen tovább növelhető, ha több MAX7219 meghajtót felfűzünk az SPI buszra (daisy chain).


9. ábra: A MAX7219 LED vezérlő IC tokrajza és tipikus alkalmazása

A MAX7219 IC további kellemes tulajdonságai közé tartozik, hogy:
Hardver követelmények:
A program elején inicializáljuk az SPI0 modult (1 MHz adatsebesség, "elsőként MSB" mód), majd konfiguráljuk a MAX7219 IC-t:
Ezután 1 másodpercre engedélyezzük a teszt módot (minden szegmens kigyullad), majd újabb 1 másodpercre a letiltott kijelző módot (0x0F értékek beírásával "szóközöket" jelenítünk meg minden számjegyen).

A főprogram végtelen ciklusában először sorra megjelenítjük a számjegyeket 0-tól 7-ig, majd egyenként kitöröljük azokat ("szóköz" beírásával).

2. lista: Program8_2/main.c listája
/* program8_2: Számkijelző vezérlése MAX7219 IC-vel
*
* A egy nyolc jegyű hétszegmenses számlálót vezérel
* MAX7219 IC-vel, ami SPI buszon kommunikál
*
* Hardver követelmények:
* MAX7219 IC, nyolcjegyű (pl. 2x4) kijelzővel
*
* Arduino kompatibilis lábkiosztást használunk:
* D13 (PTD1): SPI SCK
* D12 (PTD3): SPI MISO (ebben a programban nem használjuk)
* D11 (PTD2): SPI MOSI
* D10 (PTD0): SPI SS
*
* A program forrása: Mazidi et al., Freescale ARM Cortex-M Embedded Programming
* http://www.microdigitaled.com/ARM/Freescale_ARM/Code/Freescale_ARM_codes.htm
*
* A tankönyvi programot kibővítettük (több számjegy, dinamikus kiírás)
* A rendszer órajelek beállítása: CPU 48 MHz, Bus 24 MHz
*/

#include "MKL25Z4.h"

void SPI0_init(void);
void max7219_write(unsigned char command, unsigned char data);
void delayMs(int n);

// MAX7219 parancskódok:
#define NO_OP 0
#define DECODE 9
#define INTENSITY 10
#define SCANLIMIT 11
#define SHUTDOWN 12
#define TESTMODE 15

int main(void) {
unsigned char i;
SPI0_init(); // SPI0 konfigurálása
max7219_write(DECODE, 0xFF); // dekódolás 8 számjegyre
max7219_write(SCANLIMIT, 7); // pásztázás 8 jegyre
max7219_write(INTENSITY, 8); // Kitöltési arány = 17/32
max7219_write(TESTMODE, 1); // Teszt mód engedélyezése
max7219_write(SHUTDOWN, 1); // Megjelenítés engedélyezése
for(i=1; i<9; i++) { // Számjegyek törlése
max7219_write(i,0x0F); // Blank karakter
}
delayMs(1000);
max7219_write(TESTMODE, 0); // Teszt mód letiltása
delayMs(1000);

while(1) {
for(i=1; i<9; i++) { // Számjegyek kiírása
max7219_write(i,i-1);
delayMs(500);
}
delayMs(1000);
for(i=1; i<9; i++) { // Számjegyek törlése
max7219_write(i,0x0F); // Blank karakter
delayMs(500);
}
}
}

void max7219_write(unsigned char command, unsigned char data) {
volatile char dummy;

PTD->PCOR = 1; // SS aktiválás

while(!(SPI0->S & 0x20)) { } // TX kész jelre vár
SPI0->D = command; // Parancs küldése
while(!(SPI0->S & 0x80)) { } // Átvitel végére vár
dummy = SPI0->D; // SPRF törlése
while(!(SPI0->S & 0x20)) { } // TX kész jelre vár
SPI0->D = data; // Adat küldése
while(!(SPI0->S & 0x80)) { } // Átvitel végére vár
dummy = SPI0->D; // SPRF törlése

PTD->PSOR = 1; // SS deaktiválása
}




//---------------------------------------
// Késleltető függvény 48 MHz órajelhez
//---------------------------------------
void delayMs(int n) {
int i, j;
for(i = 0 ; i < n; i++)
for (j = 0; j < 8010; j++);
}

//---------------------------------------
// SPI0 modul konfigurálása
//---------------------------------------
void SPI0_init(void) {
SIM->SCGC5 |= 0x1000; // Port D engedélyezése
PORTD->PCR[1] = 0x200; // PTD1 legyen SPI SCK */
PORTD->PCR[2] = 0x200; // PTD2 legyen SPI MOSI */
PORTD->PCR[0] = 0x100; // PTD0 legyen GPIO módban
PTD->PDDR |= 0x01; // PTD0 kimenet legyen (SPI SS)
PTD->PSOR = 0x01; // Kezdetben '1' legyen

SIM->SCGC4 |= 0x400000; // Az SPI0 modul engedélyezése
SPI0->C1 = 0x10; // SPI letiltása, master mód, MSB elöször
SPI0->C2 = 0; // Alapértelmezett beállítások
SPI0->BR = 0x51; // Baud rate = 1 MHz (/6 és /4 osztók)
SPI0->C1 |= 0x40; // Az SPI modul engedélyezése
}



Program8_3: 8x8 LED mátrix vezérlése MAX7219 IC-vel

Az alábbi egyszerű mintapéldában egy 8x8-as LED mátrixot vezérlünk MAX7219 IC-vel. A program az SPI periféria konfigurálásával és a MAX7219 LED vezérlő inicializálásával kezdődik.  Inicializáláskor a LED vezérlő teszt üzemmódját is engedélyezzük. Ekkor minden LED kigyullad. Egy másodperc eltelte után letiltjuk a teszt üzemmódot, és nullára törölt adatregiszterekkel normál megjelenítési módba kapcsolunk (minden LED elalszik). Ezután végtelen ciklusban felváltva két karakter (H és W) jelenítünk meg.



8. ábra: 8x8 LED mátrix MAX7219 vezérléssel, mint SPI periféria


Hardver követelmények:
Bekötési vázlat:
MAX7219
FRDM-KL25Z
Megjegyzés
VCC
3V3
Tápfeszültség (3,3 V)
GND
GND
Tápegység közös pontja ("föld")
DIN
D11 (PTD2)
MOSI adatkimenet
CS
D10 (PTD0)
Chip select jel
CLK
D13 (PTD1)
SCLK szinkron órajel
Megjegyzés: A FRDM-KL25Z kártyánál elsődlegesen az Arduino kompatibilis kimenetek neveit tüntettük fel.

3. lista: Program8_3/main.c listája
/* program8_2: 8x8 LED mátrix vezérlése MAX7219 IC-vel
*
* A program egy 8x8-as LED mátrix kijelzőt vezérel
* MAX7219 IC-vel, ami SPI buszon kommunikál
*
* Hardver követelmények:
* 8x8 LED mátrix MAX7219 vezérlővel
*
* Arduino kompatibilis lábkiosztást használunk:
* D13 (PTD1): SPI SCK
* D12 (PTD3): SPI MISO (ebben a programban nem használjuk)
* D11 (PTD2): SPI MOSI
* D10 (PTD0): SPI SS
*
* A rendszer órajelek beállítása: CPU 48 MHz, Bus 24 MHz
*/

#include "MKL25Z4.h"

void SPI0_init(void);
void max7219_write(unsigned char command, unsigned char data);
void delayMs(int n);

// MAX7219 parancskódok:
#define NO_OP 0
#define DECODE 9
#define INTENSITY 10
#define SCANLIMIT 11
#define SHUTDOWN 12
#define TESTMODE 15

const unsigned char minta1[]= {
0xFF,0x18,0x18,0x18,0x18,0x18,0x18,0xFF
}; //H
const unsigned char minta2[]= {
0x1F,0x60,0x80,0x40,0x40,0x80,0x60,0x1F
}; //W

int main(void) {
unsigned char i;
SPI0_init(); // SPI0 konfigurálása
max7219_write(DECODE, 0); // Nincs dekódolás
max7219_write(SCANLIMIT, 7); // pásztázás 8 sorra/oszlopra
max7219_write(INTENSITY, 8); // Kitöltési arány = 17/32
max7219_write(TESTMODE, 1); // Teszt mód engedélyezése
max7219_write(SHUTDOWN, 1); // Megjelenítés engedélyezése
for(i=1; i<9; i++) {
max7219_write(i,0); // képpontok törlése
}
delayMs(1000);
max7219_write(TESTMODE, 0); // Teszt mód letiltása
delayMs(1000);

while(1) {
for(i=1; i<9; i++) { // 1. minta kirajzolása
max7219_write(i,minta1[i-1]);
}
delayMs(1000);
for(i=1; i<9; i++) { // 2. minta kirajzolása
max7219_write(i,minta2[i-1]);
}
delayMs(1000);
}
}

//---------------------------------------
// Adatküldés (16 bit) a MAX7219 IC-re
//---------------------------------------
void max7219_write(unsigned char command, unsigned char data) {
volatile char dummy;

PTD->PCOR = 1; // SS aktiválás

while(!(SPI0->S & 0x20)) { } // TX kész jelre vár
SPI0->D = command; // Parancs küldése
while(!(SPI0->S & 0x80)) { } // Átvitel végére vár
dummy = SPI0->D; // SPRF törlése
while(!(SPI0->S & 0x20)) { } // TX kész jelre vár
SPI0->D = data; // Adat küldése
while(!(SPI0->S & 0x80)) { } // Átvitel végére vár
dummy = SPI0->D; // SPRF törlése

PTD->PSOR = 1; // SS deaktiválása
}

//---------------------------------------
// Késlelteto függvény 48 MHz órajelhez
//---------------------------------------
void delayMs(int n) {
int i, j;
for(i = 0 ; i < n; i++)
for (j = 0; j < 8010; j++);
}

//---------------------------------------
// SPI0 modul konfigurálása
//---------------------------------------
void SPI0_init(void) {
SIM->SCGC5 |= 0x1000; // Port D engedélyezése
PORTD->PCR[1] = 0x200; // PTD1 legyen SPI SCK */
PORTD->PCR[2] = 0x200; // PTD2 legyen SPI MOSI */
PORTD->PCR[0] = 0x100; // PTD0 legyen GPIO módban
PTD->PDDR |= 0x01; // PTD0 kimenet legyen (SPI SS)
PTD->PSOR = 0x01; // Kezdetben '1' legyen

SIM->SCGC4 |= 0x400000; // Az SPI0 modul engedélyezése
SPI0->C1 = 0x10; // SPI letiltása, master mód, MSB elöször
SPI0->C2 = 0; // Alapértelmezett beállítások
SPI0->BR = 0x51; // Baud rate = 1 MHz (/6 és /4 osztók)
SPI0->C1 |= 0x40; // Az SPI modul engedélyezése
}
A max7219_write() függvény egy kétbájtos tranzakciót haj végre: aktiválja az SS kimenetet, kiküld két bájtot az SPI buszra (regisztercím és a regiszterbe írandó tartalom), majd inaktív állapotba helyezi az SS kimenetet.

Az Init_MAX7219() függvényben adatlap szerint alaphelyzetbe állítjuk a LED vezérlőt (közben egy másodpercig engedélyezzük a teszt módot, amikor minden LED kigyullad). A fényerőt közepes szintre állítjuk be.

A végtelen ciklusban felváltva a minta1[], illetve a minta2[] tömb tartalmával töltjük fel a LED mátrixot vezérlő IC adatregisztereit, ami egy H, illetve egy W betű 8x8 pixeles rajzolatát képviseli.


10. ábra: Pillanatkép a program működéséről