Tak, tu je tretí diel. Sú tu popísané základné rutiny programu a ako fungujú, aké
hardvérové funkcie som použil.
Chcel som to napísať tak, aby to bol nejaký návod, akým systémom som to riešil, keby
sa tým dakto chcel inšpirovať. Nechcel som, aby to bol preklad datašítu, teda
vysvetľovať, ako funguje generátor PWM, to si každý podrobne prečíta v datašíte.
Tým návodom som mienil aj to, aby ten, kto sa tým bude chcieť inšpirovať, to nezobral , tak
že toto skopírujem sem, toto tam a pôjde to. Ale len popis princípov činnosti.
Aby ten, kto by to chcel použiť, to bral len ako pomôcku, aké hardvérové funkcie sa na
to dajú použiť a ako ich použiť v softvéri. Aby sám rozmýšľal, čo od toho bude on
chcieť a spravil si to podľa svojich požiadaviek.
autor: Andrej B.
Dostal som podnety na napísanie podrobnejšieho popisu riadiaceho programu, takže som napísal aj tretiu časť článku.
Začnem na začiatku zdrojového kódu, pri hlavičkových súboroch. V programe používam funkcie na obsluhu vstupov a výstupov mikrokontroléra, prerušenia, spánkové režimy a oneskorovacie slučky, takže je potrebné vložiť príslušné hlavičkové súbory:
#include<avr/io.h>
#include<avr/interrupt.h>
#include<avr/sleep.h>
#include<util/delay.h>
Ešte pripomeniem, že je použitý mikrokontrolér ATtiny2313, taktovaný 1 MHz vnútorným RC oscilátorom. Podrobný popis hardvérových funkcií je v datašíte .
Teraz niečo o výstupe regulátora. Ako som vravel, regulátor generuje šírkovo modulovaný signál a ním spína výkonový tranzistor. Na generovanie tohto PWM signálu používam 8 bitový časovač (Timer/Counter 0) a jeho funkciu Output compare v režime Rýchla PWM. Aby som generoval signál a dostal ho na výstup, musím zapnúť časovač, generátor PWM a pripojiť ho k výstupu (nastavením príslušnej V/V nožičky ako výstup).
DDRD = 0x20; // výstup na PD5, zvyšok portu vstup
OCR0B = 127; // nastavenie striedy
TCCR0A = 0x23; // PWM na OC0B, aktívna úroveň 1
TCCR0B = 0x02; // zapnem časovač, delenie taktu 8
Časovač0 má dva PWM kanály, ja som použil B. Aktívna úroveň 1 znamená, že keď je na výstupe µC 1, tak je otvorený aj výkonový tranzistor (pozri schému), tiež že so zväčšovaním hodnoty OCR, sa zväčšuje aj šírka impulzu na výstupe (výkon). Taktovanie časovača odvodzujem od systémových hodín, avšak s delením frekvencie 8, aby som dosiahol f výstupného signálu 488 Hz (tá je = f pretekania časovača). Po týchto príkazoch časovač beží, na nožičke PD5 (výstup OC0B) je signál so striedou 50% určenou hodnotou registra OCR0B. Keď chcem zmeniť šírku impulzu, iba jednoducho mením hodnotu tohto registra. Tu treba pamätať na to, že keď OCR má hodnotu 0, tak na výstupe je signál so striedou 1/256, nie trvalá nula. Dosiahnuť na výstupe nulu je troška zložitejšie, treba vypnúť PWM a pre istotu explicitne nastaviť príslušný výstup na 0.
Regulátor ako vstup používa signál z prijímača, rovnaký ako pre servo. Jedná sa o impulzy, ktoré svojou šírkou určujú nastavenie príkonu motora. Teda potrebujem zmerať ich šírku. Na to používam 16 bitový časovač s funkciou Input capture. Jej princípom je uloženie hodnoty akú mal časovač, keď prišla na vstup hrana signálu. Keďže input capture sa nastavuje na reakciu pri nejakej hrane a v tomto prípade potrebujem merať čas medzi nábežnou a dobežnou hranou, za každou hranou musím zmeniť nastavenie detekcie na opačnú hranu (nábežná -> dobežná a naopak) aby časovač zachytil každú hranu. Hodnoty časovača uložené pomocou input capture do hlavnej funkcie vyberám z ICR v prerušení a tam aj mením nastavenia.
Samotné zapnutie časovača a input capture vyzerá takto:
PORTD |= _BV(PD6); // zvyšovací odpor pre ICP
TCCR1B = 0x81; // IC na dobežnú hranu, odstraňovač šumu, // časovač zapnutý - taktovanie bez delenia
TIMSK = 0x88; // povolené prerušenia input capture, pretečenie 1
sei(); // globálne povolenie prerušení
Zvyšovací odpor na nožičku kde je vstup ICP zapínam, aby v prípade, keby nebol na vstup pripojený Rx, bola na vstupe definovaná a stála úroveň. Pri takomto nastavení taktovania je rozlíšenie merania 1 µs. Potom treba nastaviť aké prerušenia z časovačov používam. Potom už len v obsluhe prerušenia vyberám hodnotu z ICR a ukladám si ju do premenných a zmením detekčnú hranu input capture. (pretečenia využívam ako signalizáciu chybového stavu - vstup bez signálu).
Ďalším vstupom regulátora je napätie pohonného akumulátora. Regulátor zisťuje jeho veľkosť - či je vyššie alebo nižšie ako hranica pre vypnutie. V tomto prípade som použil µC, ktorý nemá ADC, miesto neho som použil analógový komparátor. Jeho použitie mi síce nedáva presnú informáciu o veľkosti U, ale dá sa zaobísť aj bez toho a je oveľa jednoduchšie. Komparátor je predvolene zapnutý (na rozdiel od ostatných periférií), takže jedinou obsluhou je zisťovanie stavu jeho výstupu. V datašíte je odporúčané vypnúť digitálne vstupy nožičiek kde sú pripojené napätia vstupujúce do komparátora, toto urobím ako prvý príkaz v programe. Takže obsluha komparátora môže vyzerať nejako takto:
DIDR = 0x03;
// odpojenie digitálnych vstupov
if(bit_is_clear(ACSR,ACO)) {
// pokiaľ je U aku nízke vykoná
sa
niečo
...
}
Môj regulátor funguje systémom, že väčšinu času procesor spí a o funkcie sa starajú periférie (časovače), zo spánku ho zobúdzajú prerušenia, keď na vstup príde hrana signálu. Potom vyhodnotí vstup, nastaví výstup a znova prejde do spánkového režimu a tak dokola. Spánkový režim sa dá nastaviť buď priamo nastavením hodnoty MCUCR alebo pomocou makier zo sleep.h. Potom ho ešte treba povoliť a µC uspať, po zobudení sa odporúča spánok hneď zakázať:
sleep_enable(); // povolenie spánku
sleep_cpu(); // uspanie µC v nastavenom režime
sleep_disable(); // zakázanie spánku po zobudení
Teraz niečo k počítaniu. Po zapnutí regulátora najprv treba zmerať impulz pri vypnutom motore (stiahnutý plyn), aby regulátor vedel kedy má byť motor vypnutý. To dosiahnem popísaným spôsobom merania impulzov tak, že v obsluhe prerušenia najprv zistím aká hrana prišla a podľa toho príslušnej premennej priradím hodnotu z ICR. V hlavnom programe vypočítam šírku impulzu ako rozdiel týchto hodnôt. Tu je vhodné obmedziť aký impulz regulátor akceptuje (v nejakom intervale hodnôt).
Hlavnou činnosťou regulátora je meranie vstupného signálu a podľa neho výpočet výstupu. Vždy keď sa procesor zobudí, skontroluje aká hrana signálu ho zobudila, pokiaľ to bola nábežná tak ešte nemá odmeraný čas aktuálneho impulzu z Rx a teda výpočet sa nemôže vykonať a spí ďalej. Ak sa cpu zobudil po dobežnej hrane, tak najprv skontroluje Uaku, ak je nižšie ako hranica (ACO == 0), poznačí si to a postupne ráta koľkokrát sa vyskytol pokles U. Ak bol týchto poklesov bez prerušenia určitý počet, aku je vybitý a motor treba vypnúť. Ak sa po niekoľkých poklesoch objaví znova Uaku vyššie ako hranica, bol to len krátkodobý pokles (napr pri pridaní plynu) a počítadlo poklesov vynulujem. Keď je napätie dostatočne vysoké, motor môže bežať a pokračujem výpočtom výstupu.
Najprv vypočítam aký dlhý bol impulz ako rozdiel času na jeho začiatku (pri nábežnej hrane) a na konci. Potom zistím či je v intervale od polohy vypnutý (preto som meral impulz pri zapnutí) po max hodnotu, akú môže mať (2 ms, ale dalo by sa to tiež odmerať, aby bolo nastavovanie presnejšie), ak áno vykonám výpočet, ak nie, je to chybný impulz a ignorujem ho. Keďže nechcem aby motor bežal pri akejkoľvek odchýlke kniplu od nuly, musím si určiť hranicu kedy sa zapne (aj kvôli určitej hysteréze motora pri rozbiehaní a zastavovaní), ja som ju určil ako kalibračný impulz + 60 µs. Teraz nasleduje rozhodnutie či je vstupný impulz >= ako tento minimálny alebo nie. Ak áno vypočítam výstup podľa vstupu, ak nie, výstup bude 0. Výstup určím ako nejaký násobok vstupu tak, aby mi pre možný rozsah vstupných impulzov vychádzal výstup v rozsahu 0 - 255. Tento násobok som nastavil 4/15. Výpočet vyzerá takto:
uint8_t vystup; // šírka výstupného impulzu
uint16_t impulz = koniec - zaciatok; // dĺžka impulzu v µ
s
if((impulz >= kalibracny) && (impulz < 2051)) { /* výpočet iba ak šírka impulzu patrí do tohto intervalu */
if(impulz >= minimalny)
vystup = ((impulz - kalibracny) << 2) / 15;
else // pokiaľ by bol < ako minimálny, motor sa vypne
vystup = 0;
if(impulz > 1940) /* výpočet neni presný, v tomto prípade motor pustím naplno, bez ohľadu na výsledok výpočtu */
vystup = 255;
}
Potom ešte treba hodnotu premennej výstup vložiť do časovača generujúceho výstupný PWM signál. Predtým však treba zistiť či má hodnotu 0, ak áno musím vypnúť PWM generátor aby motor nebežal. (Dôvod je popísaný v časti o output compare). Ak má nenulovú hodnotu, tak PWM generátor zapnem. Potom vložím výstup do OCR0B.
Ešte niečo o vypnutí regulátora v prípade vybitého akumulátora. Vypnem generovanie PWM, výstup nastavím na 0 aby bol motor vypnutý, vypnem prerušenia (aby sa nemohol zobudiť) a uspím µC.
To by bol popis funkčných blokov zdrojového kódu.
Dávam k dispozícii plošný spoj a preložený program pre regulátor. V prípade záujmu pošlite mail na adresu
rcmodely@cevaro.sk
a súbory vám budú obratom doručené do vašej mailovej schránky.
Andrej