07 · Gyorstalpaló
Számból világító szegmensek
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.
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.
| MCU-láb | Jel | Szerep | Mód |
|---|---|---|---|
| P0.0 | DispClock / SPI-SCK | Shift órajel | Push-pull |
| P0.2 | DispData / SPI-MOSI | Soros adat | Push-pull |
| P0.3 | DispOutEnable / OE_RCLK | Kimenetvezérlés, tárolás | Push-pull |
| P0.4 | DECODER_C | C címbemenet | Push-pull |
| P1.1 | DECODER_A | A címbemenet | Push-pull |
| P1.2 | DECODER_B | B címbemenet | Push-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.
// 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.
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.
// 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 engedelyezeseA 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.
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.
N = f_Timer2 * 0.001
reload = 65536 - N
// Pelda: SYSCLK = 24 500 000 Hz, Timer2-orajel = SYSCLK
N = 24500
reload = 65536 - 24500 = 41036 = 0xA04CA 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.
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.
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.
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.
#define OE_RCLK P0_B3
#define DECODER_A P1_B1
#define DECODER_B P1_B2
#define DECODER_C P0_B4A 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.
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.
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.
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] = 0x06A (uint8_t) típuskonverzió ennél a uint8_t tömbnél elhagyható: az elem eleve ilyen típusú.
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.
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;
}- Az
OE_RCLK = 1a küldés előtti vezérlőállapotot állítja be. - A
SPI0CN0_SPIF = 0törli a korábbi átvitel kész jelzését. - A
SPI0DAT = datSPI master módban elindítja a bájt átvitelét. - 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. - A jelzőbitet ismét töröljük. A végső
0 → 1váltás a tárolóregiszter RCLK bemenetének felfutó éle: az elküldött minta átkerül a kimeneti tárolóba.
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.
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: 3Pé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.
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.
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.
| index | Binárisan (C B A) | A | B | C |
|---|---|---|---|---|
| 0 | 000 | 0 | 0 | 0 |
| 1 | 001 | 1 | 0 | 0 |
| 2 | 010 | 0 | 1 | 0 |
| 3 | 011 | 1 | 1 | 0 |
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.
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 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_spia 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
indexváltozót: afor (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
whileutá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.