generování PWM v CTC 328P

Dobrý den,

potřeboval bych poradit protože už 14 dnů řeším problém na arduinu UNO kde je čip ATmega328P. Potřebuji z TIMER2 dostatávát 300kHz PWM signál s činitelem plnění 50%. Zároveň k němu generovat inverzní signál takže OCR2A i OCR2B používám. Po sem jsem to zvládnul, nyní ale nastal jiný problém. Teď potřebuji generovat sadu 10 impulsů zasebou pak deset impulsů venechat (log0) a pak zase generovat sadu impulsů a to takhle stále dokola. Tvoření rádoby radioimpulsů. Chtěl bych to úpožívat na buzení ultrazvukových sond. Problém jsem se snažil řešit tím že jsem výstupní piny pomocí DDR měnil na vstupní a nebo jsem nastavoval děličku na 0. ani v jendom případě to nefungovalo a ve chvíly kdy mělo dojít ke změně signál se mezi sadami deformoval. Vypadalo to jak kdyby tam byl kondík co se vybíjel z log1 pozvolna až do log0. Nenapadá Vás v čem by mohl být problém?

int x;
int y;
int z;

ISR(TIMER2_COMPA_vect )
{
x++;
if(x==10)
{
y=~y;
z=~z;
DDRD=(y<<3);
DDRB=(z<<3);
x=0;
}
}

void setup() {

TCCR2A=(0<<COM2A1)|(1<<COM2A0)|(0<<COM2B1)|(1<<COM2B0)|(0<<WGM21)|(0<<WGM20);
TCCR2B=(1<<FOC2A)|(0<<FOC2B)|(0<<WGM22)|(0<<CS22)|(0<<CS21)|(1<<CS20);
TCCR2B=(0<<FOC2A)|(0<<FOC2B)|(0<<WGM22)|(0<<CS22)|(0<<CS21)|(1<<CS20);

TCCR2A=(0<<COM2A1)|(1<<COM2A0)|(0<<COM2B1)|(1<<COM2B0)|(1<<WGM21)|(0<<WGM20);
TCCR2B=(0<<WGM22)|(0<<CS22)|(0<<CS21)|(1<<CS20);
//TIFR2 |= (1 << OCF2A);
TIMSK2 |= (1 << OCIE2A);

OCR2A=25;
OCR2B=25;
DDRD|=(1<<3);
DDRB|=(1<<3);
interrupts();

}

void loop() {

}

Mám k Tobě dvě věci :

Nepoužívej termíny, které nevíš, co znamenají. Najdi si, co je PWM. Ty prostě potřebuješ obdélníkový signál se střídou 1:1 a kmitočtem 300kHz.

y=~y; z=~z;

DDRD=(y<<3); DDRB=(z<<3);

Víš, co to dělá a co vlastně do DDR registrů zapisuješ ?


A teďka ke Tvému problému :

  1. Výše zmíněnými střípky kódu zapisuješ do DDRD a DDRB blbosti. Ale to by ještě nemusel být až tak velký problém, protože směr na pinech střídáš, jen těch pinů měníš víc, než si myslíš.

  2. Jak jsi správně spočítal, tak OCR2A=25; i OCR2B=25; je správně, ale při prescaleru 1 máš na provedení přerušení jenom 26 cyklů a to včetně zavolání přerušení a návratu z něj.

[code]int x;
int y;
int z;

ISR(TIMER2_COMPA_vect )
{
x++;
if(x==10)
{
y=~y;
z=~z;
DDRD=(y<<3);
DDRB=(z<<3);
x=0;
}
}[/code]

Jenže obsluha tohoto přerušení (tak, jak je napsaná) potřebuje mnohem více času, než máš k dispozici. Pokud není splněna podmínka if(x==10), trvá obsluha 52 cyklů (dvojnásobek času), pokud je splněna podmínka, pak obsluha trvá 80 cyklů (více, než trojnásobek času).

První průšvih je, že int je 16-bitová hodnota a AVRko je 8-bitový mcu. Pokud nepotřebuješ 16 bitů, pak je mnohem lepší nadefinovat proměnné jako char nebo unsigned char.
Dalším problémem by mohlo být načítání od nuly do nějaké hodnoty. V asm je dosažením nuly nastaven příznak Z ve stavovém slově mcu. Pokud je překladač dostatečně inteligetní, dokáže toho využít. Z hlediska procesoru je tedy lepší počítat od nějaké hodnoty do nuly.


[code]unsigned char x;
unsigned char y;
unsigned char z;

ISR(TIMER2_COMPA_vect)
{
if(–x==0)
{
y=~y;
z=~z;
DDRD=(y<<3);
DDRB=(z<<3);
x=10;
}
}[/code]

Tady bohužel ani tohle nepomohlo - dostal jsem se na 38 a 58 cyklů. Vzhledem k tomu, že mcu stejně nic jinýho nedělá, byl tedy další krok vyřadit úschovu a obnovu registrů a vynechání reti instrukce (ta se musí ručně doplnit, jinak by to nechodilo).


[code]unsigned char x;
unsigned char y;
unsigned char z;

ISR(TIMER2_COMPA_vect, ISR_NAKED)
{
if(–x==0)
{
y=~y;
z=~z;
DDRD=(y<<3);
DDRB=(z<<3);
x=10;
}
reti();
}[/code]

Tady už jsme se dostali při nesplněné podmínce na 15 cyklů, ale při splněné jsme pořád vysoko - na 35 cyklech.


Tady už jsem opustil Tvoje řešení a zkusil jsem svoje :

[code]unsigned char x;

ISR(TIMER2_COMPA_vect, ISR_NAKED )
{

if(–x==0) x=40;
if (x<21)
{
PIND=(1<<3);
PINB=(1<<3);
}
reti();
}[/code]

Tady jsem se dostal na 20-22 cyklů podle splněných podmínek, takže je to s odřenýma ušima, ale stíhá to. Zkusil jsem ještě nadeklarovat x jako proměnnou trvale usazenou v registru :

[code]register unsigned char x asm(“r18”);

ISR(TIMER2_COMPA_vect, ISR_NAKED )
{

if(–x==0) x=40;
if (x<21)
{
PIND=(1<<3);
PINB=(1<<3);
}
reti();
}[/code]

A tady už jsem na 15-17 cyklech, takže se slušnou rezervou. Lepší (efektivnější, rychlejší) by to šlo už napsat jenom v assembleru.

Tady máš výchozí nastavení pinů procesoru před spuštěním čítače:

[code]void setup()
{
DDRD=(1<<3);
DDRB=(1<<3);
DDRA=0;
PORTD=255;
PORTA=255;
PORTB=~(1<<3);

x=1;

plus nastavení čítače do CTC režimu.

}[/code]

Čítač nastav na CTC režim s OCR2A=25; s přerušením od OC2A.

Nevím, co z toho zvládne compiler pro uino. Tohle je odzkoušený kód v AVR Studiu 4.19+WinAVR.

Balů mockrát děkuji :wink: co se týče samotné délky int a char, bohužel jsem v arduinu nenašel proměnou “bit” (jen boolean) co jsem třeba používat v codevisionu takže už pro mě jen řešení tohot byl děsnej oříšek. V tomhle nejsem kovanej a i takovýhle základy jsou pro mě děsnej problém, takže ještě jednou mockrát děkuju. Co se týče tvých úprav musím zkrátka říci že na to jsem moc malej pán, to jsou věci co by mě v životě nedošli, jediný co jsem věděl, bylo to že jak jsi sám psal 25cyklů bude moc krátká doba a to se mi potvrdilo, ale já to neuměl ani dopočítat, ale rád bych věděl jak jsi to počítal jestli přímo ve vývojovým prostředí? DDR znám pouze jako funkci kterou deklaruji zda bude port vstupní nebo výstupní, ale vesměs z datasheetu používám jen tu známou tabulku Port Pin Configurations. Co se týče proměnných y a z vím co to dělá a včem byl problém, ale nic lepšího jsem nevymyslel-bohužel. Takže děkuji mockrát za hodinu du do labiny a vyzkouším vše podle tvé “help” příručky. Myslíš Balů, že se kdyžtak na tebe můžu obracet v případě dalších dotazů? co jsem tak pročítal tady fórum máš takový nejvíc pečlivý ba dokonce otcovský přístup k vysvětlení problematiky xD

Tak jsem to zkusil. Ještě nevím, jak se zapisuje v Arduinu přímo do registru na určitou pozici takže požívám zatím ten delší kód, nicméně to nefunguje. Je to konstantní pwm jak kdyby neodskočil do přerušení, já si ale myslím, že se tam dostane, ale asi se mu něco nebude líbit. Část kódu “if(–x==0) x=40;” znamená, že když x zmenším o jedna a rovná-li se nule tak x nastav na hodnotu 40? Ještě jsem se nesetkal s --x a strejda google mě chce utopit v informacích co nemaj co společnýho s programováním. A ještě jsem se chtě zeptat u nastavování pinů před samotnou inicializací TIMER2 je v kódu “DDRA=0; PORTA=255;” to je jen kvůli tomu aby bylo jasný, co se na portu bude dít? tak je lepší ho obsloužit? Nebo to má i nějaký jiný mě ukrytý význam? V Arduino Uno totiž nemám přístup k PORTA. Výsledný kód vypadá takto:

unsigned char x;

ISR(TIMER2_COMPA_vect, ISR_NAKED )
{

if(–x==0) x=40;
if (x<21)
{
PIND=(1<<3);
PINB=(1<<3);
}
reti();
}

void setup() {

DDRD=(1<<3);
DDRB=(1<<3);
DDRC=0;                 //tady bylo původně DDRA
PORTD=255;
PORTC=255;            //tady bylo původně PORTA
PORTB=~(1<<3);     //tady dělám negaci jen pro B? neměl bych dělat i                                     pro D?

x=1;

TCCR2A=(0<<COM2A1)|(1<<COM2A0)|(0<<COM2B1)|(1<<COM2B0)|(0<<WGM21)|(0<<WGM20);
TCCR2B=(1<<FOC2A)|(0<<FOC2B)|(0<<WGM22)|(0<<CS22)|(0<<CS21)|(1<<CS20);
TCCR2B=(0<<FOC2A)|(0<<FOC2B)|(0<<WGM22)|(0<<CS22)|(0<<CS21)|(1<<CS20);

TCCR2A=(0<<COM2A1)|(1<<COM2A0)|(0<<COM2B1)|(1<<COM2B0)|(1<<WGM21)|(0<<WGM20);
TCCR2B=(0<<WGM22)|(0<<CS22)|(0<<CS21)|(1<<CS20);
TIMSK2 |= (1 << OCIE2A);

OCR2A=25;
OCR2B=25;
interrupts();

}

void loop() {

}

ale nešel mi sem vložiz proto posílám link na podívání se:
imgup.cz/image/L9h3
jyxo.info/uploads/E5/e57518e971d … abdec7.png

Zajímavé je, že tato chyba vzniká pouze, když u části kódu TIMSK2=(1<<OCIE2A); vynechám | před =. Tím zůstává na OCIE2B nula? a chová se to takto nestandardně, ale nevím proč.

Samozřejmě, že není problém se poradit. Od toho tady to fórum je. Co se týká bitových proměnných, zkus se podívat na téma C a flagy.

Co se týká portu A, byl to překlep. Má tam být C. ATmega328 port A nemá, proto k němu ani nemůžeš mít přístup. Negace je jenom na B3. Chtěl jsi přece, aby signály byly proti sobě invertované, ne ? Proto na začátku nastavuju jeden na log. 1, druhý na log. 0. V přerušení se jenom invertujou, takže kmitají v protifázi. Jinak piny, které nejsou použity je doporučeno nastavit jako vstupní a zapnout k nim interní pull-up odpory (DDRxy = 0, PORTxy = 1).

Program jako takový funguje a piny kmitají jak maji, jenže Ty jsi nastavil (v TCCR2A) piny tak, že nejsou připojené ke standartním výstupním registrům, ale k OCR2x signálům. Proto se to vypadá, že jedou celou dobu 300kHz obdélníky bez přerušení. Nech je jako standartní piny. WGM2x nastav na CTC a COM2x nech na “Normal port operation” a dostaneš 10 obdélníků a 10 mezer.

Na OC2B se můžeš v klidu vykašlat, ten se nepoužívá. Bity FOC2x nech taky být, ten je jenom na přenesení OCRx okamžité hodnoty. Jeho nastavování na log. 1 nemá žádný smysl.

if(–x==0) x=40; se dá rozepsat jako :

x = x - 1; (nebo také x -= 1;) if (x==0) { x=40; }

Ale POZOR !!!

když napíšeš
if(x–==0) x=40; je to úplně něco jiného :

if (x==0) { x=40; } x = x - 1;

Co Ti píše uino, když chceš deklarovat x jako registr ? Já netvrdím, že to musí jít, nebo že to půjde přímo tak, jak jsem to napsal. Já to překládal v AVR Studiu 4.19+WinAVR (nadstavba Cčko). V tom jsem ten kód také zkoušel a krokoval. Proto vím, jak byl přeložený a kolik mi to trvalo cyklů. Ale záleží na efektivitě překladu uina, jestli to zvládne. Pravda je, že WinAVR to přeložil celkem efektivně, ale rezerva tam ještě je. Proto jsem také napsal, že efektivněji už to zvládne jenom assembler - resp. napsat to v assembleru.

Já tím FOC2 právě generuju protifázi :wink: jestli chceš tak ti pošlu screen. Když necháš ten můj původní kód a vymažeš jen všechno co se týká přerušení tak to generuje 300kHz signá na PORTB3 a na PORTD3 to generuje 300kHz v protifázi. Ok zkusím ještě teď co jsi mi napsal na flagy kouknu, ale zítra musím odjed mimo školu takže se k osciloskopu asi nedostanu. Tak to snad stihnu ještě dnes!

Díky za rady

Tak jo :smiley: ještě stále mě to nefunguje, ale ještě to budu zkoušet dál. Všechny dotazy jsem tentokrát vložil do kódu jako komentáře snad to nevadí. Dal jsem tam i ty výňatky z datasheetu proč jsem nastavoval ten FOC a proč tím pádem jsem nastavoval časovač jednou s FOC a podruhé bez něj. Bylo to kvuli tomu že jsem si tím vynutil nasatvení log1. Nyní kód vypadá takto:

unsigned char x;

ISR(TIMER2_COMPA_vect, ISR_NAKED )
{

x=x-1;
if(x==0)
{
x=40;
}
if (x<21)
{
PIND=(1<<3);
PINB=(1<<3);
}
reti();
}

void setup()
{
DDRD=(1<<3);
DDRB=(1<<3);
DDRC=0;
PORTD=255;
PORTC=255;
PORTB=~(1<<3);

x=1;
//podle datasheetu je normal port operation pro 0<<COM2x jenže zároveň je u toho napsané že v takovém případě je OC2x odpojen
TCCR2A=(0<<COM2A1)|(0<<COM2A0)|(0<<COM2B1)|(0<<COM2B0)|(1<<WGM21)|(0<<WGM20);
TCCR2B=(0<<WGM22)|(0<<CS22)|(0<<CS21)|(1<<CS20);
TIMSK2=(1<<OCIE2A); //mám nastavovat aji OCIE2B?? proč to nedělá chybu při |= respektivě při požití OR?

OCR2A=25;
OCR2B=25; 
//OC2x je označení pinů a OCR2x je označení registru 


/*
  Forcing compare match will not set the OCF1x
  Flag or reload/clear the timer, but the OC1x pin will be updated as if a real compare match had occurred
  (the TCCR1A.COM1x[1:0] bits define whether the OC1x pin is set, cleared or toggled). str.129
 
  The easiest way of setting the OC2x value is to use the Force Output Compare (FOC2x) strobe bit
  in Normal mode. The OC2x Register keeps its value even when changing between Waveform Generation
  modes. str.130
*/

}

void loop() {
// put your main code here, to run repeatedly:

}

Pokud bys generoval těch 300kHz trvale bez vypínání, pak samozřejmě není důvod OC2x signály odpojovat a procesor můžeš použít bez problémů tak, jakoby se žádný signál negeneroval. Když ale generuješ 10 obdélníků, pak 10x pauza, tak používáš CTC režim timeru jenom pro nastavení kmitočtu a signály generuješ softwarově.

OCIE2B nenastavuj. Stačí Ti přerušení od OCIE2A. Stejně bys to nestihnul obsloužit. Nevím, jakou chybu to hlásí, tím pádem Ti na tuhle otázku nemohu odpovědět.

OCR2B je zbytečné nastavovat.

Ano - OC2A a OC2B jsou signály na pinech PB3 a PD3 (když je generuješ pomocí SW, můžou být na jakýchkoliv pinech), OCR2A a OCR2B jsou registry.

Chybu to nevypisuje ale mám na jednom pinu jen log1 a na druhém pinu log0.

Jo - tak to jsem Tě nepochopil.
Mám takový pocit, že Ti v setup chybí povolení přerušení.

Bože já jsem vůl! Jestli to zítra pojede (dneska už tam nejdu -_- ) tak jsem ti/Vám dlužen. Pokud jseš z Brna tak škopek jestli ne tak moje vřelé díky :smiley:

Stále nic :confused: někde něco dělám blbě Very Happy je divný aby to tobě fungovalo a mě ne. Stále to drží na jednom pinu log0 a na druhém log1. Teď valím, ale mimo město takže až příští týden. Tak díky moc za rady a v pondělí napíšu jestli mě zatím něco nedošlo kde dělám chybu!

A pozrel si ci to arduino nevyuziva nejake citace pre seba a bude to robit bordel?

Takžeeeee, děkuji za rady. Nicméně jsem to zkoušel horem spodem a stále nic. Takže jak bylo nastíněno, je možné že si arduino nějakou funkci blokuje, ale nic takového jsem nenašel na na TIMER2 jen že využívá fuknci Tone (možná něco v knihovnách arduina). Dnes se mi dostala do ruky ATmega8 a ještě 328, takže je jasné co udělám. Zkusím na nepájivém poli a pokud mi to ani zde nebude fungovat tak skáču z okna :smiley:. Tak jak to bude oživený tak napíšu

[code]#include <default.h>

register unsigned char x asm(“r18”);

ISR(TIMER2_COMPA_vect, ISR_NAKED )
{

if(–x==0)
{
x=40;
reti();
}
if (x<21)
{
PIND=(1<<3);
PINB=(1<<3);
}
reti();
}

int main(void)
{
DDRD=(1<<3);
DDRB=(1<<3);
DDRC=0;
PORTD=255;
PORTC=255;
PORTB=~(1<<3);

x=1;

OCR2A=25;
TIMSK2=(1<<OCIE2A); //mám nastavovat aji OCIE2B?? proč to nedělá chybu při |= respektivě při požití OR?
TCCR2A=(0<<COM2A1)|(0<<COM2A0)|(0<<COM2B1)|(0<<COM2B0)|(1<<WGM21)|(0<<WGM20);
TCCR2B=(0<<WGM22)|(0<<CS22)|(0<<CS21)|(1<<CS20);


sei();

while(1);

return 0;

}
[/code]

Tohle prostě šlape (viz přiložený obrázek). Přikládám i HEX soubor, který, když nasypeš pro procesoru, tak by měl běžet.
Test.hex (631 Bytes)

Tak jsem se konečně vrátil z dovolené a už zkouším v labině =) a funguje to, ale jak jsem psal musím použít čistou ATmegu a ne arduino. Asi by se to dalo dohledat někde v jejich knihovnách čím to je, ale teď po tom pátrat nebudu. Ještě bych chtěl všem poděkovat =) takže moc děkuji a to hlavně Balů za trpělivost :smiley: