Tento text slouží jako podpůrný návod jak budit 7 segmentový displej se společnou anodou pro SŠ kurzy MIT. Existuje mnoho způsobů buzení LED displejů. Přímé buzení displeje z MCU je vcelku pracný způsob a do kurzu MIT ho zařazujeme hlavně proto, že si při něm vystačíte jen se základními znalostmi a nepájivým polem. V praxi se LED displeje řídí pomocí integrovaných obvodů - driverů/budičů. Řešení s nimi jsou fyzicky menší, obsahují méně součástek, využívají méně vývodů mikrokontroléru, obsluhují se elegantnějším programem a jednodušeji se navrhují. Aby jste je mohli využívat musíte umět číst datasheety, vyrábět desky plošných spojů, osazovat SMD součástky. a ovládat sběrnice I2C nebo SPI. A protože to zatím (většina) neumíte nezbývá vám než to zkusit bez nich. Ve vzorovém příkladu budeme ovládat malý žlutozelený 4 místný 7 segmentový displej CA25-12GWA (vy budete mít ve svém zapojení zajisté jiný).
Co by jste měli vědět z dřívějšího studia:
Nyní se pokusíme stanovit velikost předřadných odporů k segmentům (katodám).
Tím je náš návrh skoro u konce. Poslední co zbývá je připojit našich 7+4 vývodů k MCU. Při výběru preferujte I/O mikrokontroléru označené jako "High Sink" (HS), ty jsou určeny k větším odběrům (i tak je naše aplikace napíná k mezi jejich možností). Já ve vzorovém příkladu zapojil vývody podle následující tabulky. Ale vy si je můžete zapojit libovolně jinak a pak jen změnit příslušné definice ve zdrojovém kódu.
Vývod displeje | Anoda 1 | Anoda 2 | Anoda 3 | Anoda 4 | Segment A | Segment B | Segment C | Segment D | Segment E | Segment F | Segment G |
I/O MCU | PD6 | PD5 | PD4 | PA3 | PC5 | PB5 | PC6 | PC7 | PC3 | PB4 | PC4 |
Pro snadnou přenositelnost je zapojení všech vývodů displeje nadefinováno pomocí maker #define. Ta stačí změnit a můžete ovládat displej připojený na jiné I/O ("piny"). Tak například makro A1_GPIO definuje port (GPIOD) kam je na MCU připojen tranzistor (Q1) aktivující anodu 1 (cifru/digit 1). Makro A1_PIN pak definuje na který pin (GPIO_PIN_6) tohoto portu je tranzistor připojen. Anodu 1 tedy podle těchto definic ovládáme pinem PD6 (což koresponduje s tabulkou a mým zapojením). Z těchto maker jsou pak pomocí knihovních funkcí připraveny "funkce" na aktivaci a deaktivaci příslušných anod a segmentů. Dále program musí obsahovat "znakovou sadu". Tedy pole definující které segmenty mají svítit při cifře 1,2,3 at. Tou je ve vzorovém programu pole charset[10]. Každý bit má význam jednoho segmentu v pořadí a-b-c-d-e-f-g a log.1 znamená že segment svítí. Například hodnota 0b1110000, znamená že svítí segmenty a,b,c a tvoří cifru 7. Z velmi dobrých důvodů jsou znaky seřazeny vzestupně (tedy nultý prvek pole obsahuje cifru 0, první prvek cifru 1 atd.). Úkolem programu je stále dokola popořadě rozsvěcet první, druhou, třetí a čtvrtou cifru a zobrazovat na každé jiný (volitelný) znak. Znaky(čísla) které se mají na každé cifře zobrazovat jsou uloženy v poli cisla[4].
Funkce která přepíná obsah displeje se jmenuje zobraz a jejím prvním argumentem je číslo mezi 0-3 určující kterou cifru/digit chci aktivovat, druhým argumentem je číslo/znak, který tam chci rozsvítit. Například argumenty (0,6) znamenají, že na první(nulté) cifře displeje chci rozsvítit "šestku". Funkce jako první deaktivuje všechny cifry - tedy zhasne celý displej (to je nutné). Pak podle znakové sady aktivuje příslušné segmenty a nakonec aktivuje vybranou cifru. Během "přepisování" je displej zhasnutý. Celá akce trvá několik mikrosekund. tuto funkci je potřeba volat v pravidelných intervalech tak často aby okem nebylo patrné postupné rozsvěcení jednotlivých cifer. Na jedno "celé" zobrazení displeje potřebujeme funkci zavolat celkem čtyřikrát. Blikání s frekvencí přibližně 100Hz už není okem patrné. Je tedy nutné volat funkci alespoň 400x za sekundu. Toho lze docílit několika způsoby. Ten nejelegantnější je využít časovače.
Protože časovače ještě neumíte, budeme se na jeho nastavení koukat jako na kouzelnou formuli. Jakmile ho spustíme bude se nám automaticky s periodou jakou si vybereme volat funkce NTERRUPT_HANDLER(TIM2_UPD_OVF_BRK_IRQHandler, 13)
V ní budeme pravidelně zobrazovat jednu cifru za druhou. Frekvenci s jakou se funkce volá (a tedy s jakou se cifry postupně přepínají) volíme pomocí makra PERIODA. Vztah mezi hodnotou PERIODA a časem mezi dvěma voláními naší funkce je
t = (1024 * PERIODA) / 16000000
inverzní vztah je (jak jistě sami umíte odvodit)
PERIODA = 16000000 * t / 1024
Takže pro plynulé zobrazování volte t = 1 / 400 = 2.5ms , tedy PERIODA=39. Schválně si zkuste zvolit periodou dlouho aby bylo patrné jak program pracuje a jak zobrazuje jednu cifru po druhé (např PERIODA=3900).
Funkcí init_7seg nastavujeme všechny potřebné I/O jako výstupy. Nastavujeme je do log.1 - tedy stavu kdy jsou všechny cifry i segmenty deaktivované.
#include "stm8s.h" #include "mit_lib.h" // pro přehlednost tabulka zapojení 7segment displej se společnou anodou // Anody (číslice): // A1 - D6 // A2 - D5 // A3 - D4 // A4 - A3 // Katody (segmenty): // A - C5 // B - B5 // C - C6 // D - C7 // E - C3 // F - B4 // G - C4 // Program pro řízení 4 místného 7 segmentového displeje se společnou anodou // tuhle část modifikujte podle aktuálního zapojení segmentů a anod #define A1_GPIO GPIOD #define A1_PIN GPIO_PIN_6 #define A2_GPIO GPIOD #define A2_PIN GPIO_PIN_5 #define A3_GPIO GPIOD #define A3_PIN GPIO_PIN_4 #define A4_GPIO GPIOA #define A4_PIN GPIO_PIN_3 #define SEGA_GPIO GPIOC #define SEGA_PIN GPIO_PIN_5 #define SEGB_GPIO GPIOB #define SEGB_PIN GPIO_PIN_5 #define SEGC_GPIO GPIOC #define SEGC_PIN GPIO_PIN_6 #define SEGD_GPIO GPIOC #define SEGD_PIN GPIO_PIN_7 #define SEGE_GPIO GPIOC #define SEGE_PIN GPIO_PIN_3 #define SEGF_GPIO GPIOB #define SEGF_PIN GPIO_PIN_4 #define SEGG_GPIO GPIOC #define SEGG_PIN GPIO_PIN_4 #define PERIODA 39 // perioda přepínání cifer t=1024*PERIODA/16000000 // konec části která se má upravovat // funkce pro aktivování a dektivování anod (číslic) a katod (segmentů) - do nich nehrabat #define A1_ON (GPIO_WriteLow(A1_GPIO,A1_PIN)) #define A1_OFF (GPIO_WriteHigh(A1_GPIO,A1_PIN)) #define A2_ON (GPIO_WriteLow(A2_GPIO,A2_PIN)) #define A2_OFF (GPIO_WriteHigh(A2_GPIO,A2_PIN)) #define A3_ON (GPIO_WriteLow(A3_GPIO,A3_PIN)) #define A3_OFF (GPIO_WriteHigh(A3_GPIO,A3_PIN)) #define A4_ON (GPIO_WriteLow(A4_GPIO,A4_PIN)) #define A4_OFF (GPIO_WriteHigh(A4_GPIO,A4_PIN)) #define SEGA_ON (GPIO_WriteLow(SEGA_GPIO,SEGA_PIN)) #define SEGA_OFF (GPIO_WriteHigh(SEGA_GPIO,SEGA_PIN)) #define SEGB_ON (GPIO_WriteLow(SEGB_GPIO,SEGB_PIN)) #define SEGB_OFF (GPIO_WriteHigh(SEGB_GPIO,SEGB_PIN)) #define SEGC_ON (GPIO_WriteLow(SEGC_GPIO,SEGC_PIN)) #define SEGC_OFF (GPIO_WriteHigh(SEGC_GPIO,SEGC_PIN)) #define SEGD_ON (GPIO_WriteLow(SEGD_GPIO,SEGD_PIN)) #define SEGD_OFF (GPIO_WriteHigh(SEGD_GPIO,SEGD_PIN)) #define SEGE_ON (GPIO_WriteLow(SEGE_GPIO,SEGE_PIN)) #define SEGE_OFF (GPIO_WriteHigh(SEGE_GPIO,SEGE_PIN)) #define SEGF_ON (GPIO_WriteLow(SEGF_GPIO,SEGF_PIN)) #define SEGF_OFF (GPIO_WriteHigh(SEGF_GPIO,SEGF_PIN)) #define SEGG_ON (GPIO_WriteLow(SEGG_GPIO,SEGG_PIN)) #define SEGG_OFF (GPIO_WriteHigh(SEGG_GPIO,SEGG_PIN)) // znaková sada (může obsahovat i další znaky krom číslic. např. symboly jako "-" atd.) // bitový formát abcdefg (a-g jsou segmenty) const uint8_t charset[10]={ 0b1111110, // 0 (svítí všechny segmenty krom g) 0b0110000, // 1 (svítí jen segmenty b,c) 0b1101101, // 2 0b1111001, // 3 0b0110011, // 4 0b1011011, // 5 0b1011111, // 6 0b1110000, // 7 0b1111111, // 8 0b1111011, // 9 }; // deklarace použitých funkcí void init_7seg(void); void zobraz(uint8_t digit, uint8_t znak); // deklarace proměnných volatile uint8_t cisla[4]={0,1,2,3}; // pole se zobrazovanými ciframi/znaky main(){ clock_16MHz(); // taktujeme čip na 16MHz (z naší knihovny mit_lib.h) init_7seg(); // nastavíme I/O jako výstupy // -------- začátek kouzelné formule -------- TIM2_TimeBaseInit(TIM2_PRESCALER_1024,PERIODA); // nastavím periodu (čas) časovače TIM2_ITConfig(TIM2_IT_UPDATE,ENABLE); // povolíme přerušení od přetečení (tedy až vyprší čas) enableInterrupts(); // povolíme přerušení globálně TIM2_Cmd(ENABLE); // spustíme časovač // -------- konec kouzelné formule -------- while (1){ // tady může náš program dělat nějakou další užitečnou činnost } } // rutina přerušení - kouzelná funkce, která se zavolá každých 2.5ms (nebo s jinou periodou pokud ji změníte) INTERRUPT_HANDLER(TIM2_UPD_OVF_BRK_IRQHandler, 13){ static uint8_t pozice=0; // tady si budeme pamatovat kterou cifru zrovna zobrazujeme // -------- začátek kouzelné formule -------- TIM2_ClearFlag(TIM2_FLAG_UPDATE); // vyčistím vlajku časovače // -------- konec kouzelné formule -------- if(pozice<3){pozice++;}else{pozice=0;} // procházím všechny cifry/digity/číslice zobraz(pozice,cisla[pozice]); // zobrazím příslušnou číslici } // funkce zobrazí vybraný znak ze znakové sady na vybrané pozici // "znak" - hodnota 0 až 9 (případně více pokud máte znakovou sadu rozšířenou o další znaky) // "digit" - aktivuje vybranou číslici (digit), čísluje se od nuly (0-3) void zobraz(uint8_t digit, uint8_t znak){ if(znak>sizeof(charset)){return;} // zkontroluje jestli "znak" není mimo rozsah znakové sady, pokud ano, ukončí funkci // deaktivujeme všechny číslice/digity/pozice/anody A1_OFF; A2_OFF; A3_OFF; A4_OFF; // aktivujeme vybrané segmenty (dle znakové sady) if(charset[znak] & 0b1<<0){SEGG_ON;}else{SEGG_OFF;} if(charset[znak] & 0b1<<1){SEGF_ON;}else{SEGF_OFF;} if(charset[znak] & 0b1<<2){SEGE_ON;}else{SEGE_OFF;} if(charset[znak] & 0b1<<3){SEGD_ON;}else{SEGD_OFF;} if(charset[znak] & 0b1<<4){SEGC_ON;}else{SEGC_OFF;} if(charset[znak] & 0b1<<5){SEGB_ON;}else{SEGB_OFF;} if(charset[znak] & 0b1<<6){SEGA_ON;}else{SEGA_OFF;} // aktigujeme vybranou číslici/digit/pozici/anodu if(digit==0){A1_ON;} else if(digit==1){A2_ON;} else if(digit==2){A3_ON;} else if(digit==3){A4_ON;} } // nastavíme všechny piny segmentového displeje jako výstupy // a dáme je do log.1 (tím se všechny segmenty zhasnou a anody/digity/číslice deaktivují) void init_7seg(void){ GPIO_Init(A1_GPIO,A1_PIN,GPIO_MODE_OUT_PP_HIGH_SLOW); GPIO_Init(A2_GPIO,A2_PIN,GPIO_MODE_OUT_PP_HIGH_SLOW); GPIO_Init(A3_GPIO,A3_PIN,GPIO_MODE_OUT_PP_HIGH_SLOW); GPIO_Init(A4_GPIO,A4_PIN,GPIO_MODE_OUT_PP_HIGH_SLOW); GPIO_Init(SEGA_GPIO,SEGA_PIN,GPIO_MODE_OUT_PP_HIGH_SLOW); GPIO_Init(SEGB_GPIO,SEGB_PIN,GPIO_MODE_OUT_PP_HIGH_SLOW); GPIO_Init(SEGC_GPIO,SEGC_PIN,GPIO_MODE_OUT_PP_HIGH_SLOW); GPIO_Init(SEGD_GPIO,SEGD_PIN,GPIO_MODE_OUT_PP_HIGH_SLOW); GPIO_Init(SEGE_GPIO,SEGE_PIN,GPIO_MODE_OUT_PP_HIGH_SLOW); GPIO_Init(SEGF_GPIO,SEGF_PIN,GPIO_MODE_OUT_PP_HIGH_SLOW); GPIO_Init(SEGG_GPIO,SEGG_PIN,GPIO_MODE_OUT_PP_HIGH_SLOW); }
Home
| V1.00 23.3.2020 /
| By Michal Dudka (m.dudka@seznam.cz) /