← vissza a mikrovezérlős jegyzetekhez

MicLab · EFM8BB1

7. hét · SPI

Négy számjegy a hétszegmenses kijelzőn. Hardverbeállítások, rövid kódrészletek és magyarázatok az SPI-s kijelzéshez.

07 · Gyorstalpaló

Számból világító szegmensek

8051

A number értékét számjegyekre bontjuk. A segments táblából kikeressük a számjegy bitmintáját, SPI-n elküldjük, majd a dekóderrel kiválasztjuk a kijelzőhelyet. A négy hely gyors ismétlése adja a folyamatosnak látszó kijelzést.

1

Konfigurálás · lábkiosztás és push-pull

A 4 × 7 szegmenses shieldhez három jel tartozik a shift regiszterhez, három pedig a dekóderhez. A Hardware Configurator portbeállításaiban mind a hat legyen digitális, push-pull kimenet.

A kijelző hat vezérlőjele
MCU-lábJelSzerepMód
P0.0DispClock / SPI-SCKShift órajelPush-pull
P0.2DispData / SPI-MOSISoros adatPush-pull
P0.3DispOutEnable / OE_RCLKKimenetvezérlés, tárolásPush-pull
P0.4DECODER_CC címbemenetPush-pull
P1.1DECODER_AA címbemenetPush-pull
P1.2DECODER_BB címbemenetPush-pull
  • Clock Control: a lenti számpéldákhoz HFOSC0, 24,5 MHz, osztás 1 rendszerórajel tartozik. Más órajel is használható, de az SPI-osztót és a Timer2 újratöltését hozzá kell igazítani.
  • Port I/O: a fenti lábak digitális módja legyen engedélyezve, a kimeneti módjuk push-pull. A GPIO-kat foglaló lábakat a crossbar hagyja ki: P0.3, P0.4, P1.1 és P1.2.
  • Crossbar: engedélyezd az SPI0 kivezetését és magát a crossbart; a kiosztásban ellenőrizd a P0.0 = SCK és P0.2 = MOSI párt.
  • A P0.1 az SPI MISO bemenete lehet, a shield kijelzéséhez nincs rá szükség. Ennél a kiosztásnál ne hagyd ki a crossbarból, mert akkor a MOSI másik lábra kerülhet.
  • A P0.2 a fejlesztőpanelen BTN0-ként is szerepel; ebben a bekötésben SPI-adatkimenet. A P0.4-et a dekóder használja, ezért az UART TX funkcióját itt ne engedélyezd.
Push-pull bitek · a portbeállítás megfelelőjeC · EFM8BB1
// P0.0, P0.2, P0.3 es P0.4
P0MDOUT |= 0x1D;

// P1.1 es P1.2
P1MDOUT |= 0x06;

Ez a két sor csak a push-pull módot mutatja: a digitális módot, a crossbart és az SPI-t külön is be kell állítani. Ha ezeket a konfigurátor generálja, ugyanazt nem szükséges kézzel is megismételni.

2

SPI0 · master, órajel és engedélyezés

A mikrovezérlő küldi az adatot és az órajelet. Beállítás: master mód, 3-wire üzem, NSS nélkül, MSB-first bitsorrend. Felfutó élre mintavételező shift regiszterhez használható a mode 0: CKPOL = 0, CKPHA = 0.

SPI-beállítási példa · SYSCLK = 24,5 MHzC · EFM8BB1
// A portok es a crossbar mar legyenek beallitva.
SPI0CN0 = 0x00;       // SPI kikapcsolva, NSS nelkuli mod
IE_ESPI0 = 0;         // Megszakitas helyett SPIF figyelese
SPI0CFG = 0x40;       // Master; CKPOL = 0, CKPHA = 0
SPI0CKR = 24U;        // 24,5 MHz / (2 * (24 + 1)) = 490 kHz
SPI0CN0_SPIEN = 1;    // SPI engedelyezese

A 490 kHz egy beállítási példa. Más rendszerórajelhez újra kell számolni: f_SCK = SYSCLK / (2 × (SPI0CKR + 1)). A jelzőbites write_spi mellé SPI-megszakítás nem szükséges; ebben a változatban maradjon kikapcsolva.

3

Timer2 · 1 ms-os frissítési időalap

A Timer2 két kijelzőhely közötti várakozást adja. A konfigurátorban válassz 16 bites auto-reload módot, körülbelül 1 ms periódust és engedélyezett futást. A lentebbi ciklus a TF2H jelzőbitet figyeli, ezért Timer2-megszakítást nem használ.

Újratöltési érték számításaC · EFM8BB1
N = f_Timer2 * 0.001
reload = 65536 - N

// Pelda: SYSCLK = 24 500 000 Hz, Timer2-orajel = SYSCLK
N = 24500
reload = 65536 - 24500 = 41036 = 0xA04C

A 0xA04C érték közvetlen 24,5 MHz-es Timer2-órajelhez tartozik. Ha a konfigurátorban SYSCLK/12 vagy más rendszerórajel szerepel, ezt az értéket nem lehet változatlanul használni. A kezdő számlálóérték és az újratöltési érték is ugyanaz legyen.

Kézi Timer2-példa · csak 24,5 MHz SYSCLK eseténC · EFM8BB1
TMR2CN0_TR2 = 0;     // Timer2 megallitasa
IE_ET2 = 0;          // Megszakitas helyett flag figyelese
TMR2CN0 = 0x00;      // 16 bites auto-reload; capture kikapcsolva

CKCON0 = (CKCON0 & 0xCF) | 0x10; // Timer2 orajele = SYSCLK

TMR2RLH = 0xA0;      // Ujratoltes felso bajtja
TMR2RLL = 0x4C;      // Ujratoltes also bajtja
TMR2H = 0xA0;        // Elso periodus kezdoerteke
TMR2L = 0x4C;

TMR2CN0_TF2H = 0;
TMR2CN0_TR2 = 1;     // Timer2 inditasa
  • A rendszerórajelet előbb ellenőrizd a konfigurátorban. A fenti számítás 24,5 MHz-et feltételez, nem állítja be az oszcillátort.
  • Auto-reload módban a kezdőértéket a hardver újratölti; a TF2H jelzőbitet a program törli.
  • A generált beállításokat az enter_DefaultMode_from_RESET(); aktiválja. A kézi példa a generált Timer2-beállítás alternatívája.
  • A watchdog legyen kiszolgálva, vagy legyen kikapcsolva a projekt konfigurációjában; különben a program várakozás közben is újraindulhat.
Rövid várakozás a Timer2 jelzőbitjévelC · EFM8BB1
while (!TMR2CN0_TF2H);
TMR2CN0_TF2H = 0;

Ez a következő túlcsordulásig vár. Az első várakozás a már futó időzítő fázisától függően rövidebb is lehet; a folyamatos frissítés ezután az 1 ms-os időalapot követi.

4

Beszédes nevek a lábaknak

A #define a megadott név helyére a portbitet helyettesíti. Így a vezérlőjelekre a programban könnyebben olvasható nevekkel hivatkozhatunk.

LábkiosztásC · EFM8BB1
#define OE_RCLK   P0_B3
#define DECODER_A P1_B1
#define DECODER_B P1_B2
#define DECODER_C P0_B4

A DECODER_A, DECODER_B és DECODER_C a dekóder három címbemenete. Az OE_RCLK a kijelzéshez tartozó közös vezérlőjel; a szintváltásainak sorrendje is számít.

5

segments const · számjegyből bitminta

A tömb indexe a kiírandó számjegy, az értéke az elküldendő szegmensminta. Így a segments[2] értéke 0x5B, amely a 2-es számjegyhez tartozik.

Szegmenstábla · 0–9C · EFM8BB1
static const uint8_t segments[10] =
{
  0x3F,  // 0
  0x06,  // 1
  0x5B,  // 2
  0x4F,  // 3
  0x66,  // 4
  0x6D,  // 5
  0x7D,  // 6
  0x07,  // 7
  0x7F,  // 8
  0x6F   // 9
};
  • uint8_t: egy elem 8 bites, előjel nélküli érték.
  • const: a tábla értékeit a program nem módosítja.
  • static: fájlszinten ez a tábla csak ebben a forrásfájlban érhető el.
  • Ezekben a mintákban a 0–6. bit az a–g szegmensnek felel meg, az 1-es bit bekapcsolt szegmenst jelöl. Más bekötéshez más táblázat kellhet.
Két tömbindex szerepeC · EFM8BB1
numbers[index]           // az adott helyen álló számjegy
segments[numbers[index]] // a számjegy szegmensmintája

// Példa: index = 1, numbers[1] = 1
// segments[numbers[1]] = segments[1] = 0x06

A (uint8_t) típuskonverzió ennél a uint8_t tömbnél elhagyható: az elem eleve ilyen típusú.

6

write_spi · egy bájt elküldése

A függvény egy szegmensmintát kap a dat paraméterben. Elküldi SPI-n, megvárja az átvitel végét, majd vezérli a tárolóregisztert.

write_spi függvényC · EFM8BB1
void write_spi(uint8_t dat)
{
  OE_RCLK = 1;

  SPI0CN0_SPIF = 0;
  SPI0DAT = dat;

  while (!SPI0CN0_SPIF);
  SPI0CN0_SPIF = 0;

  OE_RCLK = 0;
  OE_RCLK = 1;
}
  1. Az OE_RCLK = 1 a küldés előtti vezérlőállapotot állítja be.
  2. A SPI0CN0_SPIF = 0 törli a korábbi átvitel kész jelzését.
  3. A SPI0DAT = dat SPI master módban elindítja a bájt átvitelét.
  4. A while (!SPI0CN0_SPIF); addig vár, amíg az átvitel befejeződik. A végén lévő pontosvessző szándékos: a ciklus törzse üres.
  5. A jelzőbitet ismét töröljük. A végső 0 → 1 váltás a tárolóregiszter RCLK bemenetének felfutó éle: az elküldött minta átkerül a kimeneti tárolóba.
7

number · szétszedés számjegyekre

A number maga a szám, a numbers pedig a négy külön számjegyet tárolja. Egész osztásnál a tört rész elvész, a % 10U pedig az utolsó tízes számrendszerbeli számjegyet hagyja meg.

Példa · 123 → 0, 1, 2, 3C · EFM8BB1
uint16_t number = 123U; // A kijelzon: 0123
uint8_t numbers[4];
uint8_t index = 0U;

// Ebben a változatban: 0 <= number <= 9999
numbers[0] = number / 1000U;         // ezresek:  0
numbers[1] = (number / 100U) % 10U; // százasok: 1
numbers[2] = (number / 10U) % 10U;  // tizesek:  2
numbers[3] = number % 10U;          // egyesek:  3

Például 123 / 100 = 1, majd 1 % 10 = 1. A tömbben balról jobbra az ezres, százas, tízes és egyes számjegy áll; hogy a fizikai kijelzőn melyik hely melyik címhez tartozik, a bekötéstől függ.

A kijelzőn 0123 jelenik meg, de a C-kódban 123U-t írunk. A 0123U kezdő nullája nyolcas számrendszert jelölne: az tízes számrendszerben 83, így 0083 lenne a kijelzett szám.

8

Dekóder · melyik kijelzőhely világítson?

Itt ugyanannak a dekódernek a három címbemenetét állítjuk. Az index alsó három bitjéből lesz az A, B és C jel: A a legkisebb helyi értékű bit.

Kijelzőhely kiválasztásaC · EFM8BB1
DECODER_A = index & 1U;
DECODER_B = (index >> 1U) & 1U;
DECODER_C = (index >> 2U) & 1U;

Az & 1U csak a legalsó bitet tartja meg. A >> 1U egy, a >> 2U két hellyel tolja jobbra a biteket. Az U előjel nélküli egész konstans jelölése.

A három címjel értéke a négy használt indexnél
indexBinárisan (C B A)ABC
0000000
1001100
2010010
3011110

Például index = 2 esetén A = 0, B = 1, C = 0. A négyhelyes ciklusban a C végig 0, mert az index csak 0–3 közötti értékeket vesz fel. A három kifejezés tehát helyes.

9

Folyamatos frissítés · 0, 1, 2, 3, majd újra

A hardver inicializálása és a számjegyek előállítása után a belső ciklus sorra veszi a négy kijelzőhelyet. A külső while (1) folyamatosan újraindítja ezt a körbejárást.

A kijelzés ismétlődő részeC · EFM8BB1
// A hardver legyen inicializalva:
// enter_DefaultMode_from_RESET();
// A Timer2 itt mar fusson a beallitott idozitessel.
TMR2CN0_TF2H = 0;

while (1)
{
  for (index = 0; index < 4; index++)
  {
    write_spi((uint8_t)segments[numbers[index]]);

    DECODER_A = index & 1U;
    DECODER_B = (index >> 1U) & 1U;
    DECODER_C = (index >> 2U) & 1U;

    OE_RCLK = 0;

    while (!TMR2CN0_TF2H);
    TMR2CN0_TF2H = 0;
  }
}
  • A write_spi a mintát küldi el; a dekóder címe azt választja ki, hol jelenjen meg.
  • Nem kell külön nullázni az index változót: a for (index = 0; ...) minden új kör elején megteszi. A for végén az index 4, a következő körben ismét 0-ról indul.
  • A Timer2 várakozás sebessége az inicializálástól függ. 1 ms-os túlcsordulási periódus mellett négy hely körbejárása nagyjából 4 ms, vagyis a teljes kijelzés körülbelül 250 Hz-en frissül.
  • A jelzőbitet a várakozás után töröljük. A pontosvesszős while után álló utasítás nem a ciklus törzse. A Timer2 legyen elindítva, és a jelzőbitet ne törölje közben egy másik kódrészlet, különben a várakozás beragadhat.

Műszaki háttér: Silicon Labs · EFM8BB1 Reference Manual, 11. fejezet (portok és crossbar), 16. fejezet (SPI), 18. fejezet (időzítők). A konkrét órajelet és bekötést mindig a használt hardverhez kell igazítani.