Generovanie 3x Sinus signálu s fázovým posunom
Čo-to o SCTimeri alebo elegantné riešenie ako pomocou SCTimera a DMA prenosu generovať sínusovku
EdizonTN, 05.08.2015-11:52
Niektoré ARM Procesory LPC od fy.NXP, obsahujú perifériu s názvom SCTimer - State Configurable Timer.
Náznaky o fungovaní časovača sa objavili už v
komentároch pri predstavení rodiny LPC8xx no nič konkrétne.
Skúsim len letmo - viacej sa dá nájsť na
stránke NXP venovanej SCTimeru, alebo v
kuchárke a samozrejme aj v manuále príslušného mikrokontroléra.
SCTimer je kombinácia klasického časovača s programovateľnou logikou.
Táto logika zavádza nový pojem "Event" (udalosť).
Event je vlastne stavebným prvkom SCTimera. Dá sa naň pozerať ako na jeden bit, ktorý priamo dokáže ovplyvňovať chovanie nielen SCTimera samotného, ale fyzicky dotiahnutý aj k ďalším perifériám MCU.
Event môže nastaviť dokonca aj IO pin a naopak zmena na IO pine môže vygenerovať event.
Event môže nastavovať prerušenie NVIC, môže spúšťať DMA prenos, samozrejme ovplyvňovať samotný časovač - nulovať, pozastaviť, zmeniť smer počítania.
Ďaľším pojmom je "State" (stav), ktorý môže nadobúdať hodnoty 0 až n , pričom n je závislé od konkrétneho MCU. Dnes sa pohybuje od 2 do 32.
Tento stav ďalej komplikuje použiteľnosť SCTimera, pretože násobí použitie eventov - stav je menený od eventov, ale naopak event môže nastať len v konkrétnych stavoch (alebo pri všetkých)
Tým, že je pomocou konfiguračných registrov "hardwérovo" prepojiteľný ku svojmu okoliu, nepotrebuje pri práci spoluprácu MCU jadra. Pracuje nezávisle.
Kombinácia jeho vlastností robí z tejto periférie veľmi silný nástroj.
Príklad.
Pri generovaní obdĺžnikováho signálu sú potrebné dva eventy - jeden event nastavuje Log.1 na IO pine, druhý zasa nastavuje Log.0.
SCTimer je skonfigurovaný tak, že pri dopočítaní k žiadanej hodnote generuje Event 1 (ktorému sme povedali čo má robiť), a pri dosiahnutí maxima (pretečení) generuje Event 0 s možnosťou automaticky znulovať časovač a znovu spustiť počítanie.
Pomocou týchto eventov je teda možné okamžite spraviť to, čo pri bežných časovačoch musí vykonať obslužná rutina.
Možno aj z príkladu je teda zrejmé, že obmedzenie pre SCTimer je hlavne počet Eventov, ktorými disponuje.
Pre mňa zaujímavé je HW prepojenie na ďalšie periférie MCU.
V jednej aplikácii som generoval sínusový signál tvorený PWM (a nejaký ten hardwér v podobe RC článku) s klasickým časovačom, no réžia jadra bola príliš veľká. Jadro sa veľmi silno venovalo len obsluhe časovača - reload zo sínusovej tabuľky (predpočítaná).
Takže po viacerých pokusoch, sa mi podarilo prepojiť SCTimer s DMA kanálmi, čo malo za následok autonómne generovanie sínusového signálu zo sínusovej tabuľky kompletne bez zásahu jadra MCU.
To má prácu len pri zmene frekvencie signálu, kedy znovu prepočíta hodnoty pre časovač a uloží ich do sínusovej tabuľky.
Následne som test rozšíril až na tri PWM výstupy (generovanie 3 sínusových signálov) s možnosťou nastaviť fázový posun medzi nimi - opäť fungujúce autonómne.
Len jadro dostáva prácu už aj pri zmene fázového posunu - zmenou adries SRC pre DMA prenos....
Pre jeden signál s nastaviteľnou frekvenciou je konfigurácia jednoduchá.
DMA prenos je skonfigurovaný s pomocou jedného descriptora, kde SRC ukazuje na koniec sin. tabuľky, a počet prenesených hodnôt je veľkost DMA tabuľky.
Takže samotný DMA transfer nastavuje Transfer descriptor s príznakom RELOAD (ano, po použití ho znova použi), SRCINC=1 (inkrementuj zdrojovú adresu, DSTINCR=0 (destination nemeň)XFERCOUNT je počet hodnôt v sin. tabuľke.
Chip_DMA_Table[DMA_SCTRELOAD_0].xfercfg =
DMA_XFERCFG_CFGVALID // Channel descriptor is considered valid
| DMA_XFERCFG_RELOAD
| DMA_XFERCFG_WIDTH_32 // 8,16,32 bits allowed
| DMA_XFERCFG_SRCINC_1 // increment src address
| DMA_XFERCFG_DSTINC_0 // constant dest address
| DMA_XFERCFG_XFERCOUNT(SIN_STEPS);
Chip_DMA_Table[DMA_SCTRELOAD_0].source = ((uint32_t) ( &SinTable[SIN_STEPS])); // set source
Chip_DMA_Table[DMA_SCTRELOAD_0].dest = ((uint32_t)(&LPC_SCT->MATCHREL[1].U)); // destination = address of SCTimer -> reload register
Chip_DMA_Table[DMA_SCTRELOAD_0].next = ((uint32_t)(&Chip_DMA_Table[DMA_SCTRELOAD_0])); // next descriptor = still same one
Trigger je hardwérový (zo vstupného PINMUX-u), prenos je typu burst s burstpower = 1 (prenes všetko ale po jednom) - trigger edge (trigger aktivuj hranou):
LPC_DMA->DMACH[DMA_SCTRELOAD_0].CFG =
DMA_CFG_HWTRIGEN
| DMA_CFG_TRIGTYPE_EDGE // edge sensitive
| DMA_CFG_TRIGPOL_HIGH // rising edge
| DMA_CFG_TRIGBURST_BURST // burst transfer
| DMA_CFG_BURSTPOWER_1 // one transfer
| DMA_CFG_DSTBURSTWRAP // dst address without change
| DMA_CFG_CHPRIORITY(3); // - with priority 3/7
Týmto mám zaručený prenos pri každom triggeri postupne jednej reload hodnoty zo sin. tabuľky do reload registru timera.
Ešte nastaviť trigger na SCTimer event:
LPC_DMATRIGMUX->DMA_ITRIG_INMUX[DMA_SCTRELOAD_0] = (uint32_t) DMATRIG_SCT0_DMA1; // spustanie od SCT_DMA1 - používam DMA kanál 1
No a samozrejme nezáživné omáčky:
LPC_DMA->CTRL = 0; // Disable DMA controller
LPC_SYSCTL->SYSAHBCLKCTRL = (1 << SYSCTL_CLOCK_DMA) | (LPC_SYSCTL->SYSAHBCLKCTRL & ~SYSCTL_SYSAHBCLKCTRL_RESERVED); // DMA initialization - enable DMA clocking and reset DMA if needed
LPC_DMA->SRAMBASE = ((uint32_t) (Chip_DMA_Table));
DMA povolím a nastavím samotný SCTimer.
LPC_DMA->DMACH[DMA_SCTRELOAD_0].XFERCFG = Chip_DMA_Table[DMA_SCTRELOAD_0].xfercfg; // nastavenie descriptora v HW registri
LPC_DMA->DMACOMMON[0].SETVALID = (1 << DMA_SCTRELOAD_0); // nasavenie validity DMA
LPC_DMA->CTRL = 1; // a povolenie DMA kontroléra
No a nastavenie SCTimera
LPC_SCT->CONFIG = SCT_CONFIG_32BIT_COUNTER | SCT_CONFIG_CLKMODE_BUSCLK | SCT_CONFIG_AUTOLIMIT_L | SCT_CONFIG_AUTOLIMIT_H; // jedne timer, BUS Clocked, auto - reload pri MATCHREL[0]
LPC_SCT->REGMODE_U = 0x0000; // 0: pracujeme ako MATCH, nie ako CAPTURE register
LPC_SCT->CTRL_U = SCT_CTRL_HALT_L | SCT_CTRL_HALT_H | SCT_CTRL_CLRCTR_L | SCT_CTRL_CLRCTR_H; // zastav počítadlo - malo by sa zastaviť pri každej konfigurácii
LPC_SCT->CTRL_U |= SCT_CTRL_BIDIR_L(0) | // počítaj smerom hore
SCT_CTRL_PRE_L(0); // set prescale to n+1
Init SCTimera a pripojenie GPIO na OUT_0:
Chip_Clock_EnablePeriphClock(SYSCTL_CLOCK_SWM); // Enable SWM clock before altering SWM
Chip_SWM_MovablePinAssign(SWM_SCT_OUT0_O, GPIO.Pin); // Connect SCT output 0 to MCU Pin
Chip_Clock_DisablePeriphClock(SYSCTL_CLOCK_SWM);
Chip_SCT_Init(LPC_SCT); // povol STC periferiu, nastav CLOCK a daj RESET
A nasavenie Eventov samotných:
// EVENT 0 - zakladna cela perioda PWM - ukoncenie/start cyklu - nastavuje vystup na log. 1
LPC_SCT->MATCHREL[EV_0].U = 0; // znuluj match register - reload by user
LPC_SCT->EV[EV_0].STATE = 0xFF; // Event moze nastat kedykolvek - pri akomkoľvek STATE
LPC_SCT->EV[EV_0].CTRL = 0x00 | // Event - MATCHSEL: 0x00. Nastane pri zhode s MATCH[0]
(0 << 4) | // - HEVENT: 0. Vyvolava sa od L countera (alebo unified ako v tomto prípade)
(1 << 5) | // - OUTSEL: 1. Vybera vystup nastaveny v IOSEL
((0x01 << (OUT_0 )) << 6) | // - IOSEL: 0x01. nastav vystup c.0 [0..5]
(0x00 << 10) | // - IOCOND: 0x00. nepouzite
(0x01 << 12) | // - COMBMODE: 0x01. Event nastava len pri MATCH zhode
(0 << 14) | // - STATELD: pripocitaj/prepis hodnotu stavu. 1: prepisujeme state hodnotu
(0x00 << 15) | // - STATEV: 0x01. Novy STATE po tomto evente
(0 << 20) | // - MATCHMEM: 0. Meni porovnavanie z = na >=.
(0x01 << 21) | // - DIRECTION:0x01. Event vznikne len pocas pocitania smerom hore.
SCT_EVn_CTRL_RESERVED; // - ............................................................
LPC_SCT->OUT[OUT_0].SET = (1 << EV_0); // Event 0 nastavuje fizicky Out 0 - tuto mením stav GPIO
LPC_SCT->DMAREQ1 = 1 << EV_0 ; // Aktivuj DMA1 prenos od match[0] - zabezpečí reload hodnoty
// EVENT 1 - D.C PWM signálu nastavuje výstup do log.0
LPC_SCT->MATCHREL[EV_1].U = 1; // znuluj match register - reloaduje ho DMA
LPC_SCT->EV[EV_1].STATE = 0xff; // Event moze nastat kedykolvek
LPC_SCT->EV[EV_1].CTRL = 0x01 | // Event - MATCHSEL: 0x01. Nastane pri zhode s MATCH[1]
(0 << 4) | // - HEVENT: 0. Vyvolava sa od L countera
(1 << 5) | // - OUTSEL: 1. Vybera vystup nastaveny v IOSEL
((0x01 << (OUT_0 )) << 6) | // - IOSEL: 0x01. nastav vystup c.0 [0..5]
(0x00 << 10) | // - IOCOND: 0x00. nepouzite
(0x01 << 12) | // - COMBMODE: 0x01. Event nastava len pri MATCH zhode
(0 << 14) | // - STATELD: pripocitaj/prepis hodnotu stavu. 1: prepisujeme state hodnotu
(0x00 << 15) | // - STATEV: 0x02. Novy STATE po tomto evente
(0 << 20) | // - MATCHMEM: 0. Meni porovnavanie z = na >=.
(0x01 << 21) | // - DIRECTION:0x01. Event vznikne len pocas pocitania smerom hore.
SCT_EVn_CTRL_RESERVED; // - ............................................................
LPC_SCT->OUT[OUT_0].CLR |= (1 << EV_1); // Event 1 nuluje Out 0
A spusti celý cirkus:
LPC_SCT->CTRL_L &= ~SCT_CTRL_HALT_L; // start Low/unified counter
Týtmo je dosiahnuté generovanie sínusového signálu na pine GPIO.Pin, podľa tabuľky SinTable s frekvenciou nastavenou v LPC_SCT->MATCHREL[0].U
Tento príspevok má slúžiť len ako nasmerovanie pre podobné hračky. Nechcem (a nemôžem) zobraziť kompletný zdrojový kód, ale myslím že aj tieto fragmenty pomôžu pri pochopení funkcie SCTimeru....
tento článok je z mikroZONE
( https://www.mikrozone.sk/pluginy/content/content.php?content.135 )