Změna výstupní frekvence PWM beze změny vzorkovací frekvence

Zdravím.
mám program pro generování signálu pomocí PWM v ATMEGA 8L.
Program funguje,ale nyní nevím, jak změnit původní výstupní frekvenci 100Hz na menší- přibližně 1-2 Hz. Bohužel při změně hodnoty TCCR1B se mi mění i vzorkovací frekvence, čímž se zhoršuje i kvalita signálu na výstupu.

Program je postavený podle jednoho DDS generátoru, kdy si to hodnoty an výstup PWM bere z tabulky 256 hodnot. ATMEGA 8L je nastavena an Fint = 1MHz

Můžete mi někdo pomoci, jak frekvenci změnit?

Děkuji

[code]#include <avr\io.h>
#include <avr\interrupt.h>
#include <avr/pgmspace.h>

const uint8_t plethysmcurve] PROGMEM= //256 hodnot

{
0x0,0x3,0x6,0x9,0xC,0xF,0x12,0x15,0x18,0x1B,0x1E,0x21,0x24,0x27,0x2A,0x2D,
0x30,0x33,0x36,0x39,0x3C,0x3F,0x42,0x45,0x48,0x4B,0x4E,0x51,0x54,0x57,0x5A,0x5D,
0x60,0x63,0x66,0x69,0x6C,0x6F,0x72,0x75,0x78,0x7B,0x7E,0x81,0x84,0x87,0x8A,0x8D,
0x90,0x93,0x96,0x99,0x9C,0x9F,0xA2,0xA5,0xA8,0xAB,0xAE,0xB1,0xB4,0xB7,0xBA,0xBD,
0xC0,0xC3,0xC6,0xC9,0xCC,0xCF,0xD2,0xD5,0xD8,0xDB,0xDE,0xE1,0xE4,0xE7,0xE9,0xEB,
0xEC,0xEF,0xF0,0xF1,0xF3,0xF4,0xF5,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFC,0xFD,0xFE,
0xFE,0xFF,0xFF,0xFF,0xFF,0xFE,0xFD,0xFD,0xFC,0xFA,0xF9,0xF8,0xF7,0xF6,0xF5,0xF5,
0xF3,0xF2,0xF1,0xF1,0xF0,0xEE,0xEC,0xEB,0xEA,0xE9,0xE8,0xE6,0xE4,0xE2,0xE0,0xDE,
0xDC,0xDA,0xD8,0xD6,0xD4,0xD2,0xD0,0xCE,0xCC,0xCA,0xC8,0xC6,0xC4,0xC2,0xC0,0xBE,
0xBC,0xBA,0xB8,0xB7,0xB5,0xB4,0xB4,0xB5,0xB6,0xB7,0xB8,0xBA,0xBB,0xBD,0xBE,0xC0,
0xC1,0xC3,0xC4,0xC5,0xC6,0xC8,0xC9,0xCB,0xCC,0xCD,0xCE,0xCE,0xCE,0xCD,0xCD,0xCC,
0xCB,0xCA,0xC8,0xC6,0xC6,0xC4,0xC3,0xC1,0xBF,0xBD,0xBC,0xBA,0xB8,0xB6,0xB4,0xB2,
0xB0,0xAE,0xAC,0xAA,0xA8,0xA6,0xA4,0xA2,0xA0,0x9E,0x9C,0x9A,0x98,0x96,0x93,0x90,
0x8D,0x8A,0x87,0x84,0x81,0x7E,0x7B,0x78,0x75,0x72,0x6F,0x6C,0x69,0x66,0x63,0x60,
0x5D,0x5A,0x57,0x54,0x51,0x4E,0x4B,0x48,0x45,0x42,0x3F,0x3C,0x39,0x36,0x33,0x30,
0x2D,0x2A,0x27,0x24,0x21,0x1E,0x1B,0x18,0x15,0x12,0xF,0xC,0x9,0x6,0x3,0x0,
};
uint8_t i=0;
ISR(TIMER1_COMPA_vect)
{
OCR1A=pgm_read_byte(&plethysmcurve*);
i++;
}
int main(void) {

DDRD=0x00; //Port D pins nastaví jako výstupní

PORTD=0xFF; //nastavení interních pull ups odporů

DDRB=0xFF; //nastaví PORTB- B1 pinu jako výstupní

OCR1A=80; // inicializace OCR1A hodnoty
//Output compare OC1A 8 bit non inverted PWM
TCCR1A=0x91; //spustí časovač bez předděličky (start timer without prescaler )
TCCR1B=0x01; //enable output compare interrupt for OCR1A
TIMSK=0x10; //enable global interrups
sei();
while (1)
{
//nekonečná smycka,preruseni pracuje
}
}

[/code]

:arrow_right: administrator: přejmenováno z “pomoc s PWM”*

Zdarec,
v přerušní neinkrementuj pokaždé, ale jen jednou za několik obsluh (=opakuj hodnotu). Jesli to teď dává 100Hz a chceš 1Hz, budeš opakovat 100x. Výstup ovšem bude schodovitej. S tím nic neuděláš. Buď bys musel dopočítávat vzorky mezi (interpolace), nebo si přegenerovat (zvětšit) tabulku. Další možnost je RC filtr na výstupu pwm.

ISR(TIMER1_COMPA_vect) { static unsigned char j = 1; if(--j == 0) { OCR1A=pgm_read_byte(&plethysmcurve*); j = 100; } }
Mimochodem - to “i” nemusí být globální, taky by stačilo statické lokální v přerušení jako je “j”.*

OK, zkusím to,co to udělá.
na výstupu RC filtr být stejně musí kvůli vyhlazení PWM signálu.

akorát s tou tabulkou hodnot… máš tedy na mysli, místo 256 hodnot jich mít třeba 512/ 1024 hodnot abych se vyhnul při nízkých frekvencích nekvalitnímu signálu??

a jestli eště můžu,jak to udělam, abych měl např místotebou avizovaného 1Hz frekv. 2Hz??

je nějaký způsob jak to jednoduše zajistit např. pomocí 2 tlačítek?
1.tlačítko= 1hz
2. tlačítko= 2Hz

To je totéž, jako když 100x snížíš fekvenci PWM s tím rozdílem, že v Tvém případě bude PWM generovat vyšší frekvenci ale stejně schodovitý signál. Jestliže chci generovat signál 100x pomalejší, musí být LP filtr taktéž posunut o 2 dekády níže. Jinak ten signál nebude vyhlazen.
Rozšiřovat bezhlavě tabulku nemá smysl. Lze si snadno spočtat, že pro sinus v 8bitovém rozlišení nemá smysl pořizovat více než 256 hodnot na půlperiodu. Další rozšiřování povede jen k tomu, že se čísla budou několikrát za sebou opakovat.
Lepších výsledků lze dosháhnout zvýšením rozlišení PWM na 9 či 10 bitů, za cenu nižší frekvence a rozšíření tabulky na 512 či 1024 dvoubajtových hodnot.
Vzájemné vztahy a výpočty mezi frekvencemi, zkreslením a dalšími parametry najdeš na stránkách www.analog.com . Přesné umístění neznám, ale někde tam ješte dodnedávna byl podrobný výklad DDS včetně výpočtů.

Z 1Hz uděláš 2Hz velmi jednoduše - místo opakování hodnoty 100x ji zopakuješ jen 50x :wink:.
S tou tabulkou je to jak pše Technik, na 8b opravdu nemá větší moc smysl. Mimochodem - co je to za průběh (není to symetrické takže čistý sin těžko)?
Ten filtr budeš muset upravit, podle toho jak moc to chceš hladké bys pak musel použít aktivní filtr vyššího řádu.

Edit: Ještě k tý tabulce - nevykresloval jsem si ji, ale vypadá to, že z toho poleze cosi, co se blíží dvoucestně usměrněný sinusovce.

scielo.br/img/revistas/rbti/ … 3fig01.gif

na tomto obrázku je vidět průběh, který potřebuji generovat. Dnes jsem se s použitím toho kousku kódu co mi tu Piityy napsal, dostal někam na řádově 1,6 Hz při zachování velice dobré kvality i se správně nastaveným pasivním RC filtrem. akorát je divný, že při změně tý stovky na jiné menší číslo se to rozhodí natolik, že z toho leze nespecifikovatelnej paskvil, ale i 1,6 Hz je OK.

On ten program je v podstatě upravený program tuším DDS generátor signálu

scienceprog.com/avr-dds-signal-generator-v20/

V podstatě jsem to jen upravil jen pro tuto jednu křivku, kterou jsme stvořil v excelu a hodnoty převedl do HEX podoby a upravil do tabulky.

Ještě se zeptám, je možné nějakým způsobem v tomto programu zajistit změnu frekvence pomocí tlačítka nebo skupiny tlačítek? např že by každé tlačítko mělo přiřazené číslo do tý smyčky, a pomocí tlačítka bych měnil tu zpožďovací smyčku a tím ovlivňil frekvenci ??

:arrow_right: administrator: příspěvek byl upraven

:arrow_right: administrator: přiloženy externí soubory
en_a03fig01.gif

Tvoje křivka ovšem nebude hladká jako na tvém obrázku - při opakování máš dole ostrou špičku. Ta je žádoucí? Ta křivka má vyšší harmonické, je ten filtr nastaven tak aby prošly? Pokud ti to nejde nad určitou mez, tak to pravděpodobně bude už ten filtr ořezávat.[code]#include <avr\io.h>
#include <avr\interrupt.h>
#include <avr\pgmspace.h>

#define TLACITKA_PORT PORTB
#define TLACITKA_PIN PINB
#define TL1 PB0
#define TL2 PB1

#define TLACITKO_1 (!(TLACITKA_PIN & (1<<TL1)))
#define TLACITKO_2 (!(TLACITKA_PIN & (1<<TL2)))

volatile unsigned char repeate = 100;
const uint8_t plethysmcurve] PROGMEM= //256 hodnot
{
0x0,0x3,0x6,0x9,0xC,0xF,0x12,0x15,0x18,0x1B,0x1E,0x21,0x24,0x27,0x2A,0x2D,
0x30,0x33,0x36,0x39,0x3C,0x3F,0x42,0x45,0x48,0x4B,0x4E,0x51,0x54,0x57,0x5A,0x5D,
0x60,0x63,0x66,0x69,0x6C,0x6F,0x72,0x75,0x78,0x7B,0x7E,0x81,0x84,0x87,0x8A,0x8D,
0x90,0x93,0x96,0x99,0x9C,0x9F,0xA2,0xA5,0xA8,0xAB,0xAE,0xB1,0xB4,0xB7,0xBA,0xBD,
0xC0,0xC3,0xC6,0xC9,0xCC,0xCF,0xD2,0xD5,0xD8,0xDB,0xDE,0xE1,0xE4,0xE7,0xE9,0xEB,
0xEC,0xEF,0xF0,0xF1,0xF3,0xF4,0xF5,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFC,0xFD,0xFE,
0xFE,0xFF,0xFF,0xFF,0xFF,0xFE,0xFD,0xFD,0xFC,0xFA,0xF9,0xF8,0xF7,0xF6,0xF5,0xF5,
0xF3,0xF2,0xF1,0xF1,0xF0,0xEE,0xEC,0xEB,0xEA,0xE9,0xE8,0xE6,0xE4,0xE2,0xE0,0xDE,
0xDC,0xDA,0xD8,0xD6,0xD4,0xD2,0xD0,0xCE,0xCC,0xCA,0xC8,0xC6,0xC4,0xC2,0xC0,0xBE,
0xBC,0xBA,0xB8,0xB7,0xB5,0xB4,0xB4,0xB5,0xB6,0xB7,0xB8,0xBA,0xBB,0xBD,0xBE,0xC0,
0xC1,0xC3,0xC4,0xC5,0xC6,0xC8,0xC9,0xCB,0xCC,0xCD,0xCE,0xCE,0xCE,0xCD,0xCD,0xCC,
0xCB,0xCA,0xC8,0xC6,0xC6,0xC4,0xC3,0xC1,0xBF,0xBD,0xBC,0xBA,0xB8,0xB6,0xB4,0xB2,
0xB0,0xAE,0xAC,0xAA,0xA8,0xA6,0xA4,0xA2,0xA0,0x9E,0x9C,0x9A,0x98,0x96,0x93,0x90,
0x8D,0x8A,0x87,0x84,0x81,0x7E,0x7B,0x78,0x75,0x72,0x6F,0x6C,0x69,0x66,0x63,0x60,
0x5D,0x5A,0x57,0x54,0x51,0x4E,0x4B,0x48,0x45,0x42,0x3F,0x3C,0x39,0x36,0x33,0x30,
0x2D,0x2A,0x27,0x24,0x21,0x1E,0x1B,0x18,0x15,0x12,0xF,0xC,0x9,0x6,0x3,0x0,
};

ISR(TIMER1_COMPA_vect)
{
static unsigned char j = 1, i;
if(–j == 0)
{
OCR1A=pgm_read_byte(&plethysmcurve*);
j = repeate;
}
}

void main(void)
{
TLACITKA_PORT |= 1<<TL1 | 1<<TL2; // zapnout pull-upy

DDRD=0x00;   //Port D pins nastaví jako výstupní <<<<<<<< chyba: v "0" jsou piny vstupní

PORTD=0xFF;   //nastavení interních pull ups odporů 

DDRB=0xFF;   //nastaví PORTB- B1 pinu jako výstupní 

OCR1A=80;   // inicializace OCR1A hodnoty 
		 //Output compare OC1A 8 bit non inverted PWM 
TCCR1A=0x91; //spustí časovač bez předděličky (start timer without prescaler ) 
TCCR1B=0x01; //enable output compare interrupt for OCR1A 
TIMSK=0x10; //enable global interrups 
sei(); 
for(;;) 
{
	//nekonečná smycka,preruseni pracuje 
	if(TLACITKO_1) repeate = 100;
	if(TLACITKO_2) repeate = 50;
} 

}[/code]*
krivka.gif

no, to koukam, to je rychlost :open_mouth:
kdybych tomu taky tak rozumněl :frowning: je nějaká dobrá literatura, kde se lze rozumným zbůsobem může člověk naučit pochopit tohle programování?

Zejtra to vyzkoušim a dam vědět :slight_smile:
ten filtr je nastavenej jako dolní propust do 3Hz jestli si správně pamatuju.

tento program by měl sloužit ke generování signálu pro simulátor jedné životní ffunkce. V podstatě podle tohodle signálu přes proudový zdroj budou napájeny diody, takže jejich svit by měl korespondovat s touto křivkou. protože fyziologycky lze předpokládat, že tep u člověka bude přes 180 pulzů za minutu jen ve výjmečných případech, stačí mě pro potřeby toho simulátoru např. 3 hodnoty třeba v bodech 60, 120 a 180 tepů/min= frekvenci do 3 Hz. ohledně tý špičky toho ssignálu- neměla by vadit, pro detekci pulzů se hledají maxima amplitud ( špičky). Ale je to dobrá připomínka,asi až budu mít chvilku, křivku přenastavim. ostatně aproximace tohoto signálu se na straně testovaného přístoje vyhladí…

snad jsem ted trochu vysvětlil, k čemu celá tahle sranda slouží

jinak velice moc děkuji za ten kód, zítra dám vědět, jak to celé funguje :slight_smile:

DP 3Hz je pro tenhle průběh málo, proto ti to už při vyšší opakovačce nechodí. Teď tu nemám nic, co by mi to spektrum ukázalo. Zkus kdyžtak ten filtr zvednout aspoň na 5-10 Hz.
Co se týká literatury - zkus se porozhlédnout tu na fóru jesli by ti něco vyhovovalo.

Ahoj,
vše funguje, je to super, moc děkuji.
Jen bych měl eště dotaz, jak to udělat, pro Vás to bude sranda.

mám ještě nápad, který bych potřeboval realizovat, napadli mi 2 možnosti jak to udělat. ve střučnosti popíšu celej problém.

mám senzor, v něm svítí střídavě v určitý čas 2 diody- infračervená a červená. jejich střídu nemá cenu řešit, ale chtěl bych detekovat svit té infračervené, a v závislosti na ní svítit signál co leze z mojí ATMEGA8 PWM tabulky. akorát, že na výstupu nyní potřebuju místo jedné diody, která mi stačila do ted, diody 2. takže bych potřeboval synchroně dostávat signál z ATMEGY na 2 výstupy, abych si mohl dál zpracovávat signál pro každou zvlášť. takže první dotaz, půjde z ATMEGA8L dostat 2 výstupy z PWM?
pokud ano, potřeboval bych pomoct s výše avizovaný problém. nabízí se mi 2 varianty.
1// nechat jeden výstup PWM1 stále generovat signál a ve chvíli, kdy bude moje infradioda detekovat infra záření ze senzoru, což bych připojil na nějaký vstup mCPU, by se na určitý čas po který by bylo detekováno napětí na té infradiodě odpojil výstup PWM1 a aktivoval by se výstup PWM2 a ve chvíli, kdy by mi napětí z infradiody zmizelo ( zářič v senzoru by přestal svítit, by se PWM2 vyplo a opět by běžel výstup PWM1.

2// Pokud ATMEGA8 nepodporuje 2 výstupy PWM, tak opět v závislosti na detekci napětí z infradiody by se mi snížila amplituda výstupní PWM. Bylo by fajn, kdyby se nechali opět nadefinovat třeba 2 tlačítka se dvěmi úrovněmi amplitud.

Nyní používáš pin OC1A jako pwm výstup. Úplně stejným způsobem můžeš využívat pin OC1B. Stačí ho aktivovat při nastavování čítače tak jak je to pro OC1A a do registru OCR1B zapsat požadovanou pwm hodnotu. Tento pwm jede ze stejného timeru.

no, já si to myslel, ale mohl bys mi to specifikovat blíž? třeba přímo v kódu? :frowning: a nemohl bys mi poradit jak to udělat aby to reagovalo na log1 na PB6 ( kde by byla ta infradioda) ?? já vim, že to dohromady sám nedam :frowning:

:arrow_right: administrator: příspěvek byl upraven
Předchozí příspěvky se necitují.

On je ten popis dost chaotickej, moc jsem to z něj nepochopil.
Ta současná pwm má jet bezezměny a podle úrovně na některém pinu spouštět druhou stejnou pwm? Má startovat od nuly, tam kde je první pwm nebo jinde?
Nechtěl bys místo PB6 použít některý pin, co dokáže generovat přerušení (ICP1, INT0, INT1)? Bylo by to elegantnější než stále v mainu očuchávat pin, což by při použití PB6 bylo třeba.

jasně. zkusim tgo napsat jednoduše.
na OCRA pojede PWM (označím PWM1)
na OCRB pojede PWM korespondující s PWM1 (označím PWM2, ale budou mmít stejnej počátek- 0)

PWM1 pojede pořád, dokud nepřijde přerušení z venka. Přerušení vyvolám spínacím tlačítkem( ve skutečnosti infradioda). po dobu stisku tlačítka PWM1 nebude funkční, ale v tuhle chvíli se po dobu stisku tlačítka( ext. přerušení) bude generovat PWM na OCB. Důležitý je, aby ta PWM2 na port OCB naskočila kdekoli během jejího přůběhu, aby neečala PWM2 na začátek tabulky. prostě kdekoli v průběhu. ve chvíli, kdy uvolním spínač, aby se mi opět spustila na OCA PWM 1, opět kdekoli …jednoduše aby na sebe ty křivky byli schopné navazovat a nečekali na počátek periody. Prostě když budeš mít na OCA sinusovku a ve 24° sepneš spínač,tak aby se v tu chvíli ta sinusovka jakoby úpřepla na OCB a jela od 25°dokud nepovolíš tlačítko. v tu chvíli bude sinusovka třeba na 165°ana OCB a bude pokračovat na 166°na OCA

můj spíánač bude realizovaný fotodiodou s úpravou napětí na 5V, aby odpovídala log1.

pokud bude elegantnější verze s jiným pinem určeným pro ext. přerušení, proč ne.

snad je to ted pochopitelnější

Jo, to už je lepší :wink: Tady nebude přerušení potřeba, pin si budeš moct vybrat kterejkoli. Za chvíli něco smotnu a uvidíme.

edit: Upravuju ten program a nestačím se divit. Chudák procesor má tlačítka na výstupních pinech (jeden z nich je PWM). Koukám na nastavení timeru a nestačím se divit komentářům. Teprve pak jsem si všim, že jsou posunutý o řádek vejš… No nádhera.

:smiley: neboj, komentáře neřeš, nastavení tlačítka na PB0 tušim jsem si předělal na jinej PIN. nastavení časovače nevím, co je na tom špatně,ale po úpravě hodnot pro TL1 a TL2 repeat 100 a 5O jsem si upraviil na hodn. 60 a 30, kdy mi to generuje signál s tepovou frekv. 60 a 120 pulzů/minutu, což je ideální pro mě. ještě si tam asi dodělam jedno tlačítko pro tep 180 :slight_smile: to už zvládnu. ale něco vymyslet… jo, to je pro mě problem :frowning:

PWM A = PB1
PWM B = PB2

INFRA = PB0 (0=>jede PWM A)
TLACITKO1 = PB6 // tlacitka pripojena proti zemi
TLACITKO1 = PB7

Není to vyzkoušený, tak uvidíš. Když budeš chtít další tlačítko, budeš pravděpodobně potřebovat další port, na zbývajících je ISP. Né že by to tam nešlo nalepit, ale radši to teď neřeš.
main.c (3.75 KB)

ahoj,
tak to ted zkoušim a nějak to nefunguje.

po spuštění běží PWMA normálně. Při stisku tlačítka ( log 1 na infra) tak se spustí PWMB, ale ta generuje ss napětí o 3V asi (možná to generuje dle posl. hodnoty kde se to přeplo.- PWM o jedné střídě). Při uvolnění spínače se to navrátí zpět na PWMA ,ale běží to pořád na PWMB (ss signál).

Při krokování programu se program zastaví na nekonečné smyčce for v hlavním programu asi z důvodu, že nedojde k přerušení.

Další pokus :slight_smile:
Teď jedou stále obě pwm, jen se zapínají/vypínají výstupy. Taky je to kratší.
main.c (2.94 KB)

děkuju, snad to bude chodit, dam vědět v o pondělí, nechal sme to v práci. tak pak dam vědět 8)
děkuju moc :wink: