generování PWM v CTC 328P

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.