[PIC programování] – 6. díl
Vítám vás u šestého dílu série PIC programování!
V tomto díle jsem se rozhodl seznámit vás s moduly Timer2/4/6. Ty nám od tohoto dílu sloužit jako časovače našich programů. Co timer umí? Nic moc. Jen podle toho, jakou frekvenci mu nastavíme, odpočítává, a když dojde do konce, tak se zresetuje a dá nám vědět, že dopočítal. Jeho výstup buď můžeme sledovat programově, nebo jej vyvést na nějaký z portů mikrokontroléru. Mikrokontrolér PIC16F1705 má celkem 4 osmibitové časovače a jeden šestnáctibitový. My budem pracovat s modulem Timer2, který je osmibitový, což pro naše účely bohatě postačí. Pokud se naučíte s Timerem 2, budete zároveň umět nastavit Timer 4 a 6.
Schéma časovače vypadá následovně: (datasheet na straně 257)
Z diagramu je patrné, že základní frekvencí pro časovač je frekvence oscilátoru děleno 4. V našem případě, pokud máme vnitřní oscilátor nastaven na 4 MHz, budeme počítat s hodnotou 1 MHz. Dále se celá frekvence dělí tzv. prescalerem. Můžeme buď nechat základní frekvenci, nebo dělit čtyřmi, šestnácti nebo šedesáti čtyřmi. Pokud signál projde prescalerem, inkrementuje se (zvýší se o jedničku) registr TMR2. Jeho hodnota se pomocí komparátoru porovnává s hodnotou registru PR2. Pokud se tyto dvě hodnoty rovnají, signál projde dál. Pro nás to znamená, že můžeme frekvenci po úpravě prescalerem ještě podělit hodnotou v registru PR2. Pokud se hodnoty registrů TMR2 a PR2 rovnají, resetuje se registr TMR2 (nastaví se na nulu). Poté můžeme buď signál přivést do některých periferií (např. nějaká sériová linka, třeba SPI, ale o tom až později) nebo může jít ještě skrze tzv. postscaler. V postscaleru se frekvence ještě podělí. Dělit můžeme hodnotou od 1 do 16. Teprve až když projde signál i postscalerem, nastaví se bit TMR2IF v registru PIR1 na jedničku. My tedy budeme testovat tento bit, abychom zjistili, zda můžeme v programu pokračovat.
Pojďme se pokusit si popsat strukturu našeho kódu. Ze začátku samozřejmě nastavíme konfigurační bity, vnitřní oscilátor atd. Poté nastavíme prescaler, postscaler a registr PR2. Součet jejich hodnot nám dá celkový dělitel frekvence. Pro náš příklad (a také pro pozdější využití) si nastavíme frekvenci 100 Hz. Pojďme tedy počítat:
Začínáme na frekvenci 4 MHz, ta se podělí čtyřmi. K prescaleru “dorazí” frekvence 1 MHz. Nyní jí podělíme čtyřmi. Máme tedy frekvenci 250 kHz. Poté nastavíme registr PR2 na hodnotu 25010, čímž dostaneme frekvenci pouhý 1 kHz. V postscaleru podělíme frekvenci deseti. Tím dostaneme 100 Hz.
Ano vím, nejspíš je to trochu složité, ale nebojte, až si projdete kód, tak to pochopíte. Proto, abychom mohli nastavit prescaler a postscaler, musíme znát registr T2CON:
Sedmý bit (tedy první zleva) je neimplementovaný. Ať ho nastavíme, jak ho nastavíme, procesor ho neřeší. Následuje skupina čtyř bitů pojmenovaná jako T2OUTPS. Pomocí této skupiny nastavíme postscaler. Postscaler jsme chtěli nastavit na hodnotu deset. V seznamu pod tabulkou je pro hodnotu deset uvedena hodnota 10012. Dále máme jeden bit pojmenovaný TMR2ON. Pokud chceme, aby timer počítal, musíme tento bit nastavit na jedničku. Zbývá skupina dvou bitů pojmenovaná T2CKPS. Tou se nastavuje prescaler. Ten jsme chtěli nastavit na čtyřku, volíme tedy hodnotu 012. Poté už jen nastavíme registr PR2 na hodnotu 25010. Pokud je čítač platný (doběhl do konce), nastaví se bit TMR2IF na jedničku. Tento bit musíme ručně vynulovat, nebo zůstane v jedničce.
Pojďme si to ukázat v kódu:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
... ;nastavení vnitřního oscilátoru na frekvenci 4MHz movlw b'01101010' banksel OSCCON ;je třeba vybrat správnou banku movwf OSCCON ;nastavení časové základny (timer2) banksel T2CON movlw b'01001101' ;1:10 postscaler, timer on, 1:4 prescaler movwf T2CON ;REGISTER 26-1, strana 259; TIMER 100Hz movlw .250 ;komparátor porovnává T2CON s PR2, resetne movwf PR2 ;T2CON, když se rovnají HLAVNI_SMYCKA banksel PIR1 ;PIR1 obsahuje bit TMR2IF, který signalizuje btfss PIR1,TMR2IF ;platnost timeru; musí se ručně vynulovat goto HLAVNI_SMYCKA ;čekat, dokud není timer platný ;čítač je platný ;1Hz bcf PIR1,TMR2IF ;vynulování TMR2IF ; sem patří nějaká naše rutina, která se bude opakovat s frekvencí 100 Hz goto HLAVNI_SMYCKA ;po provedení našeho kódu se vracíme na začátek a opět čekáme na to, až timer doběhne do konce end |
Po pročtení komentářů by mělo snad být vše jasné. Pokud ne, pročtěte si jej znovu i s popisem, není to tak těžké.
Komentářem jsem označil místo, kam budem umisťovat náš kód. Pojďme si třeba rozblikat LEDku. Než tak uděláme, budeme potřebovat značně nižší frekvenci. Pokud naší frekvenci podělíme stem, dostaneme se na frekvenci 1 Hz. To bude pro nás optimální. Do kódu přidáme jednu proměnnou, kterou nastavíme na 100 a budeme jí tak dlouho dekrementovat (snižovat o jedničku) až dojdeme do nuly. Poté jí opět nastavíme na 100 a provedeme naší rutinu. Upravený kód bude vypadat takto:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
... #define CITAC 0x20 ;dekrementující čítač, výchozí hodnota .100 ... ;nastavení vnitřního oscilátoru na frekvenci 4MHz movlw b'01101010' banksel OSCCON ;je třeba vybrat správnou banku movwf OSCCON ;nastavení dekrementujícího čítače movlw .100 ;čítač začíná na hodnotě .100 banksel CITAC movwf CITAC ;nastavení časové základny (timer2) banksel T2CON movlw b'01001101' ;1:10 postscaler, timer on, 1:4 prescaler movwf T2CON ;REGISTER 26-1, strana 259; TIMER 100Hz movlw .250 ;komparátor porovnává T2CON s PR2, resetne movwf PR2 ;T2CON, když se rovnají HLAVNI_SMYCKA banksel PIR1 ;PIR1 obsahuje bit TMR2IF, který signalizuje btfss PIR1,TMR2IF ;platnost timeru; musí se ručně vynulovat goto HLAVNI_SMYCKA ;čekat, dokud není timer platný ;čítač je platný ;1Hz bcf PIR1,TMR2IF ;vynulování TMR2IF banksel CITAC decfsz CITAC ;je čítač na nule? goto HLAVNI_SMYCKA ;není, vracíme se zpět na začátek movlw .100 ;je movwf CITAC ;čítač opět nastavíme na .100 ; sem patří nějaká naše rutina, která se bude opakovat s frekvencí 100 Hz goto HLAVNI_SMYCKA ;po provedení našeho kódu se vracíme na začátek a opět čekáme na to, až timer doběhne do konce end |
Zde jen vysvětlím příkaz decfsz [DECrement F and Skip if Zero]. Tento příkaz odečte z registru F jedničku. Pokud je registr v nule, přeskočí se následující instrukce. Pokud bychom registr CITAC nevynulovali, pak pokračuje na svojí maximální hodnotě (111111112 = 25510). Tomu se říká přetečení (angl. overflow). Totéž by se dělo při inkrementaci na hodnotě 255, akorát že by se začalo od nuly. Více nemám co dodat, komentáře snad mluví jasně.
Nyní už nás čeká samotná rutina pro blikání LEDky. LEDku si necháme z minulého dílu na portu RC3. Schéma zapojení bude vypadat tedy takto:
Velikost odporu u LEDky nechám opět na vás…
Pojďme si ještě do kódu (někam nad hlavní smyčku) nastavit port RC3 jako výstup a vynulovat ho:
1 2 3 4 5 |
;nastavení portu RC3 banksel TRISC bcf TRISC,3 banksel LATC bcf LATC,3 |
Teď už jen zbývá hlavní rutina. Nebudeme dělat nic jiného, než negovat bit RC3. Pokud bude v jedničce, nastavíme ho do nuly. Pokud bude v nule, nastavíme ho do jedničky. To budeme tedy provádět s naší frekvencí 1 Hz, což znamená, že by LEDka měla svítit jednu sekundu a být zhasnuta jednu sekundu (a tak pořád dokola).
1 2 3 4 5 6 7 8 |
; sem patří nějaká naše rutina, která se bude opakovat s frekvencí 100 Hz banksel LATC btfss LATC,3 ;je bit v jedničce? goto $+3 ;není, skočím o tři instrukce dopředu a dám ho do jedničky bcf LATC,3 ;je, dám ho do nuly goto $+2 ;a skočím o dvě instrukce dopředu, abych zabránil opětovnému nastavení do jedničky bsf LATC,3 ;zde bit nastavím na nulu a pokračuji dál ; konec naší rutiny |
Opět, komentáře by měly být jasné, příkazy taktéž. Celý kód bude vypadat následovně:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
... #define CITAC 0x20 ;dekrementující čítač, výchozí hodnota .100 org 0 ;začínáme na adrese 0 ;nastavení vnitřního oscilátoru na frekvenci 4MHz movlw b'01101010' banksel OSCCON ;je třeba vybrat správnou banku movwf OSCCON ;nastavení dekrementujícího čítače movlw .100 ;čítač začíná na hodnotě .100 banksel CITAC movwf CITAC ;nastavení časové základny (timer2) banksel T2CON movlw b'01001101' ;1:10 postscaler, timer on, 1:4 prescaler movwf T2CON ;REGISTER 26-1, strana 259; TIMER 100Hz movlw .250 ;komparátor porovnává T2CON s PR2, resetne movwf PR2 ;T2CON, když se rovnají ;nastavení portu RC3 banksel TRISC bcf TRISC,3 banksel LATC bcf LATC,3 HLAVNI_SMYCKA banksel PIR1 ;PIR1 obsahuje bit TMR2IF, který signalizuje btfss PIR1,TMR2IF ;platnost timeru; musí se ručně vynulovat goto HLAVNI_SMYCKA ;čekat, dokud není timer platný ;čítač je platný ;1Hz bcf PIR1,TMR2IF ;vynulování TMR2IF banksel CITAC decfsz CITAC ;je čítač na nule? goto HLAVNI_SMYCKA ;není, vracíme se zpět na začátek movlw .100 ;je movwf CITAC ;čítač opět nastavíme na .100 ; sem patří nějaká naše rutina, která se bude opakovat s frekvencí 100 Hz banksel LATC btfss LATC,3 ;je bit v jedničce? goto $+3 ;není, skočím o tři instrukce dopředu a dám ho do jedničky bcf LATC,3 ;je, dám ho do nuly goto $+2 ;a skočím o dvě instrukce dopředu, abych zabránil opětovnému nastavení do jedničky bsf LATC,3 ;zde bit nastavím na jedničku a pokračuji dál ; konec naší rutiny goto HLAVNI_SMYCKA ;po provedení našeho kódu se vracíme na začátek a opět čekáme na to, až timer doběhne do konce end |
Pokud vám LEDka bliká s frekvencí 1 Hz, tak vám gratuluji! Právě jste se naučili jednu z nejužitečnějších věcí o programování PIC mikrokontrolérů. Nyní si už můžete podle sebe časovat vaše programy.
V příštím díle se hlouběji ponoříme do časování, vytvoříme si několik smyček o různých frekvencích a ukážeme si, jak vytvářet podprogramy, čímž se nám vše podstatně zjednoduší. Pokud jste vydrželi až sem, tak vám děkuji a prosím o sdílení a také o zpětnou vazbu v komentářích. Do příštího dílu se mějte!
Parádní články na téma PIC. Bude další pokračování? Díky Jirka
Zatím jsem neplánoval, ale mohl bych tam přidat pár věcí, třeba sériovou komunikaci, mTouch a další…
Taky bych se přimlouval za další články o programování PIC. Je to moc hezky psáno a hlavně polopatisticky.
Jsem rád, že to někdo sleduje, tak se pokusím dneska udělat díl na PWM. Snad ještě najdu ten samej mikrokontrolér někde…
Moc pěkně napsáno a vysvětleno. Je to i čtivé. Prošel jsem všechny kapitoly, velmi se mi tento styl výuky zamlouvá a přimlouvám se za pokračování.
Krásné napsané články, čtivé, perfektně vysvětlené, stále jsem věřil že bude pokračování, ale asi už dál nic nebude po takové době. Moc velká škoda že to nepokračuje. Nic lepšího jsem doposud na PIC nenašel. Kdyby se přece něco změnilo, dejte vědět, rád budu číst dál.
Děkuji, mám už asi půl roku rozepsaný sedmý díl, bohužel jsem nějak neměl čas jej dokončit. Pokusím se přes prázdniny něco sesmolit.
Tak, série zase pokračuje!
Moc děkuji za články o programování PIC, jsem důchodce (68) “ajťák” a v důchodu jsem se vrátil ke koníčku z mládí : elektronika. Docela mě láká programování PIC a vaše články jsou pro mě inspirací a výukou zároveň. Moc děkuji.
Dobrý den, bohužel Microchip před několika lety koupil AVR a od té doby stihl zlikvidovat (skončil vývoj a podpora) kompilátoru MPASM. Nyní je k dispozici pouze XC8 (Cčkový) kompilátor a jeho XC8 PIC Assembler. Osobně jsem v PIC ztratil zálibu vzhledem k chování Microchipu, uzavřenosti platformy a mizerným vývojovým nástrojům (SW i HW, tedy MPLAB X IDE, PicKit 4). Osobně preferuji STM32 čipy. Ty sice asi nebudete ručně programovat v assembleru, nicméně můžete použít otevřený kompilátor jako je GCC. Taky je velmi pěkný čip RP2040 od Raspberry Pi Ltd. Ten je navržený hlavně pro výuku, ale zároveň je to docela slušný a levný čip (když jsem se díval posledně, stál asi 20 kč, ale potřebujete externí paměť). Dá se programovat v C/C++/MicroPythonu/Rustu a pravděpodobně i dalších jazycích. Má velmi zajímavou periferii PIO, což je v assembleru programovatélné IO, lidé s tím umí vyrobit sběrnici CAN, náhrandí UART, dokonce někdo i vyvíjí Ethernet rozhraní (zatím snad na velmi pomalé přenosy). Nicméně pokud potřebujete levný čip (na koruny) na jednoduchou logiku, tak stejně vždycky šáhnete po AVR nebo po PIC. STM taky dělá 8-bity, ale tenkrát jsem nějak neviděl cenový rozdíl oproti 32-bitům.