Forum: Mikrocontroller und Digitale Elektronik Sinus mit PWM erzeugen


von Ingo L. (grobian)


Lesenswert?

Ich versuche hier einen Sinus als PWM auszugeben. Was mit einer LED auch 
soweit ganz gut aussah (der _delay-Wert war um es sichtbar zu machen 
natürlich höher als jetzt), entpuppte sich mit kürzeren _delay-Wert und 
Oszi am PORT D5 (OC1A) als reine Berg- und Talfahrt auf den 
Scheitelpunkten. Als uP verwende ich einen Mega32 mit 12 Mhz Quarz. Als 
RC-Glied habe ich nach Vorgaben 100K un 22nF eingesetzt und unten 
audfgeführtes Programm geflasht


#include <stdlib.h>
#include <avr/io.h>
#define F_CPU 12000000UL
#include <util/delay.h>
#include <avr/interrupt.h>

unsigned short a;
unsigned char Tabelle[] = {
 0x80,0x83,0x86,0x89,0x8C,0x8F,0x92,0x95,
 0x98,0x9B,0x9E,0xA1,0xA4,0xA7,0xAA,0xAD,
 0xB0,0xB3,0xB6,0xB9,0xBB,0xBE,0xC1,0xC3,
 0xC6,0xC9,0xCB,0xCE,0xD0,0xD3,0xD5,0xD7,
 0xD9,0xDC,0xDE,0xE0,0xE2,0xE4,0xE6,0xE7,
 0xE9,0xEB,0xED,0xEE,0xF0,0xF1,0xF2,0xF4,
 0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,
 0xFC,0xFD,0xFD,0xFE,0xFE,0xFE,0xFE,0xFE,
 0xFE,0xFE,0xFE,0xFE,0xFE,0xFE,0xFD,0xFD,
 0xFC,0xFC,0xFB,0xFA,0xF9,0xF8,0xF7,0xF6,
 0xF5,0xF4,0xF2,0xF1,0xF0,0xEE,0xED,0xEB,
 0xE9,0xE7,0xE6,0xE4,0xE2,0xE0,0xDE,0xDC,
 0xD9,0xD7,0xD5,0xD3,0xD0,0xCE,0xCB,0xC9,
 0xC6,0xC3,0xC1,0xBE,0xBB,0xB9,0xB6,0xB3,
 0xB0,0xAD,0xAA,0xA7,0xA4,0xA1,0x9E,0x9B,
 0x98,0x95,0x92,0x8F,0x8C,0x89,0x86,0x83,
 0x80,0x7C,0x79,0x76,0x73,0x70,0x6D,0x6A,
 0x67,0x64,0x61,0x5E,0x5B,0x58,0x55,0x52,
 0x4F,0x4C,0x49,0x46,0x44,0x41,0x3E,0x3C,
 0x39,0x36,0x34,0x31,0x2F,0x2C,0x2A,0x28,
 0x26,0x23,0x21,0x1F,0x1D,0x1B,0x19,0x18,
 0x16,0x14,0x12,0x11,0x0F,0x0E,0x0D,0x0B,
 0x0A,0x09,0x08,0x07,0x06,0x05,0x04,0x03,
 0x03,0x02,0x02,0x01,0x01,0x01,0x01,0x01,
 0x01,0x01,0x01,0x01,0x01,0x01,0x02,0x02,
 0x03,0x03,0x04,0x05,0x06,0x07,0x08,0x09,
 0x0A,0x0B,0x0D,0x0E,0x0F,0x11,0x12,0x14,
 0x16,0x18,0x19,0x1B,0x1D,0x1F,0x21,0x23,
 0x26,0x28,0x2A,0x2C,0x2F,0x31,0x34,0x36,
 0x39,0x3C,0x3E,0x41,0x44,0x46,0x49,0x4C,
 0x4F,0x52,0x55,0x58,0x5B,0x5E,0x61,0x64,
 0x67,0x6A,0x6D,0x70,0x73,0x76,0x79,0x7C
};

int main(void)
{
  DDRD = 0xFF;
  PORTD = 0x00;


  TCCR1A |= (1<<WGM10);  // MODE
  TCCR1B |= (1<<CS11);     //PRE
  TIMSK  |= (1<<OCIE1A);

  sei();

  while(1)
  {
    for(a = 1; a < 255; a++)
    {
      OCR1A = Tabelle[a];
      _delay_us(10);
    }
}

  return 0;
}

ISR(TIMER1_COMPA_vect)
{
  PORTD ^= (1<<PD5);
}


Was haut denn hier jetzt nicht hin ?

Gruß aus Berlin
Ingo

von Falk B. (falk)


Lesenswert?

@  Ingo Laabs (grobian)

>Scheitelpunkten. Als uP verwende ich einen Mega32 mit 12 Mhz Quarz. Als
>RC-Glied habe ich nach Vorgaben 100K un 22nF eingesetzt und unten
>audfgeführtes Programm geflasht

12 MHz und 8-Bit PWM macht 46,8kHz PWM-Frequenz. 100K und 22nF macht 
72Hz Grenzfreqeunz. Sollte erstmal passen. ABER

>  TCCR1A |= (1<<WGM10);  // MODE
>  TCCR1B |= (1<<CS11);     //PRE
>  TIMSK  |= (1<<OCIE1A);

Ne Hardware-PWM braucht keinen Output Compare Overflow. Das kann die 
Hardware alleine.

>      OCR1A = Tabelle[a];
>      _delay_us(10);

Ähhhhh, bei 48kHz (~20us) PWM-Frequenz ist ein Reload von 10us wenig 
sinnvoll. Besser wäre es, den Reload im Timer Overflow Interrupt zu 
machen, dann ist es nämlich immer synchron und es gehen keine Werte 
verloren.

MFG
Falk

von Michael H. (mah)


Lesenswert?

ein screenshot sagt mehr als Gschichterln ala "reine Berg- und Talfahrt 
auf den Scheitelpunkten" - die Hellseherei hat heute geschlossen

-mah

von Ingo L. (grobian)


Lesenswert?

Michael Haberler schrieb:
> ein screenshot sagt mehr als Gschichterln ala "reine Berg- und Talfahrt
> auf den Scheitelpunkten" - die Hellseherei hat heute geschlossen
>
> -mah

werde gleich Foto machen...moment

von Ingo L. (grobian)


Angehängte Dateien:

Lesenswert?

so Foto da.....Glaskugel wird nicht gebraucht.
Ist zwar ein wenig undeutlich, aber man erkennt das Problem..denke ich 
doch

von Falk B. (falk)


Lesenswert?

Ist doch sinusähnlich ;-)

von Falk B. (falk)


Lesenswert?

Mach mal das Delay länger, so 100..200us.

von Gast (Gast)


Lesenswert?

1
uint8_t a;  //läuft automatisch über
2
3
while(1)
4
  {
5
    OCR1A = Tabelle[a];
6
    _delay_us(10);
7
    a++;
8
 }

Ist bestimmt etwas besser.

von Ingo L. (grobian)


Lesenswert?

Falk Brunner schrieb:
> Mach mal das Delay länger, so 100..200us.

ich war schon bei 1 ms ohne Wirkung.

ich denke mal die Aussage, dass Werte ausgelassen werden oder verloren 
gehen erklärt -denke ich mal mein Problem.

Das blöde ist nur..ich muss jetzt jedesmal in den Keller rennen um die 
Schaltung am Oszi anzuschließen, weil kein Rechner im Keller...stöhn

von avr (Gast)


Lesenswert?

Ich würde mal folgendes versuchen:

- Vorteiler nur 1 nicht 8, CS11 nicht setzen, Freq ist so nur
  2,9.. kHz

- Pin OC1A auf Ausgang

- Mode SET, COM1A1/A0 setzen

- Interrupt weglassen

- Wenn Vorteiler 1, OCR1A ca alle 30-50 ms nachladen

gruß avr

von Falk B. (falk)


Lesenswert?

@  Ingo Laabs (grobian)

>ich war schon bei 1 ms ohne Wirkung.

Ich sagte bereits, deine PWM ist keine PWM. Du togglest nur!
Mach es richtig wie im Datenblatt bzw bei LED-Fading PWM per 
Hardware!
1
    TCCR1A = 0x81;          // non-inverted PWM on OC1A, 8 Bit Fast PWM

MfG
Falk

von Ingo L. (grobian)


Lesenswert?

Hallo Forumsgemeinde,

nachdem ich nun meinen Sinus mit unten aufgefürtem Programm "glatt" 
hinbekommen habe, möchte ich jetzt, anstatt der Werte für den Sinus eine
Wave Datei (natürlich mit 8 Bit) benutzen. Ist das in dieser Form so 
möglich (das ich dabei die Zählschleife entsprechend anpassen muss ist 
mir dabei klar).
Wie ändere ich dabei den delay-Wert.
Gilt dabei delay= 1/Samplingfrequenz ?



#include <stdlib.h>
#include <avr/io.h>
#define F_CPU 12000000UL
#include <util/delay.h>
#include <avr/interrupt.h>


unsigned short a;
unsigned char Tabelle[] = {
0x80,0x83,0x86,0x89,0x8c,0x8f,0x92,0x95,
0x98,0x9c,0x9f,0xa2,0xa5,0xa8,0xab,0xae,
0xb0,0xb3,0xb6,0xb9,0xbc,0xbf,0xc1,0xc4,
0xc7,0xc9,0xcc,0xce,0xd1,0xd3,0xd5,0xd8,
0xda,0xdc,0xde,0xe0,0xe2,0xe4,0xe6,0xe8,
0xea,0xec,0xed,0xef,0xf0,0xf2,0xf3,0xf5,
0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfc,
0xfd,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,
0xfd,0xfc,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7,
0xf6,0xf5,0xf3,0xf2,0xf0,0xef,0xed,0xec,
0xea,0xe8,0xe6,0xe4,0xe2,0xe0,0xde,0xdc,
0xda,0xd8,0xd5,0xd3,0xd1,0xce,0xcc,0xc9,
0xc7,0xc4,0xc1,0xbf,0xbc,0xb9,0xb6,0xb3,
0xb0,0xae,0xab,0xa8,0xa5,0xa2,0x9f,0x9c,
0x98,0x95,0x92,0x8f,0x8c,0x89,0x86,0x83,
0x80,0x7c,0x79,0x76,0x73,0x70,0x6d,0x6a,
0x67,0x63,0x60,0x5d,0x5a,0x57,0x54,0x51,
0x4f,0x4c,0x49,0x46,0x43,0x40,0x3e,0x3b,
0x38,0x36,0x33,0x31,0x2e,0x2c,0x2a,0x27,
0x25,0x23,0x21,0x1f,0x1d,0x1b,0x19,0x17,
0x15,0x13,0x12,0x10,0x0f,0x0d,0x0c,0x0a,
0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x03,
0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,
0x02,0x03,0x03,0x04,0x05,0x06,0x07,0x08,
0x09,0x0a,0x0c,0x0d,0x0f,0x10,0x12,0x13,
0x15,0x17,0x19,0x1b,0x1d,0x1f,0x21,0x23,
0x25,0x27,0x2a,0x2c,0x2e,0x31,0x33,0x36,
0x38,0x3b,0x3e,0x40,0x43,0x46,0x49,0x4c,
0x4f,0x51,0x54,0x57,0x5a,0x5d,0x60,0x63,
0x67,0x6a,0x6d,0x70,0x73,0x76,0x79,0x7c
};


int main (void)

{

DDRD = (1 << PD5 );

TCCR1A = (1<<COM1A1) | (0<<COM1A0) | (0<<WGM11)  | (1<<WGM10) ;
TCCR1B = (0<<CS12)   | (0<<CS11)   | (1<<CS10);

 while(1)
  {
    for(a = 1; a < 255; a++)
    {
      OCR1A = Tabelle[a];
      _delay_us(100);
    }
  }

 return 0;
}

von Falk B. (falk)


Lesenswert?

@  Ingo Laabs (grobian)

>Wie ändere ich dabei den delay-Wert.
>Gilt dabei delay= 1/Samplingfrequenz ?

Im Prinzip ja, ABER!
Das mit dem Delay ist nur ein Q&D Test. Das macht man solide mit einem 
Timer, oder wie bereits gesagt im Overflow Interrupt der PWM. Dann gibt 
es natürlich nur ganzzahlige Teiler der maximalen Samplingfrequenz. 
Sollte aber für deine Anwendung reichen.

Ach ja, als Filter ist ein einfacher RC-Filter natürlich 
grottenschlecht. Hier sollte schon mind. ein 4poliger, aktiver Filter 
rein, Ob nun Thebycheff, Bessel oder wasauchimmer ist dann eine Frage 
der Anwendung.

Grössere Daten packt man aber direkt in den Flash und liest sie von 
dort, denn RAM ist immer knapp. Wie das geht steht im GCC-Tutorial.

MFG
Falk

P.S. Ist kompakt geworden dein Programm, nicht wahr? ;-)

MFG
Falk

von Ingo L. (grobian)


Lesenswert?

Falk Brunner schrieb:
>
> Im Prinzip ja, ABER!
> Das mit dem Delay ist nur ein Q&D Test.

Wassn das ??

> Das macht man solide mit einem
> Timer, oder wie bereits gesagt im Overflow Interrupt der PWM. Dann gibt
> es natürlich nur ganzzahlige Teiler der maximalen Samplingfrequenz.
> Sollte aber für deine Anwendung reichen.



> Grössere Daten packt man aber direkt in den Flash und liest sie von
> dort, denn RAM ist immer knapp. Wie das geht steht im GCC-Tutorial.

mit progmem...
ist hier aber in der Versuchsphase noch nicht relevant geworden
>
> MFG
> Falk
>
> P.S. Ist kompakt geworden dein Programm, nicht wahr? ;-)

jepp...dank dir ;-)

von Falk B. (falk)


Lesenswert?

@  Ingo Laabs (grobian)

>> Das mit dem Delay ist nur ein Q&D Test.

>Wassn das ??

Quick & Dirty.

von Ingo L. (grobian)


Lesenswert?

@falk
>> Overflow Interrupt

kannst Du das mal genauer erläutern..versteh das jetz nicht ganz

von Falk B. (falk)


Lesenswert?

@  Ingo Laabs (grobian)

>>> Overflow Interrupt

>kannst Du das mal genauer erläutern..versteh das jetz nicht ganz

Der Timer 1 läuft als 8-Bit Timer und macht bei 255 einen Überlauf 
(Overflow). Die PWM-Periode ist zu Ende und beginnt neu. Gleichzeitig 
wird ein Interrupt ausgelöst. Dort schreib man die neuen PWM-Daten. Hat 
den Vorteil, dass a) das ganze synchron passiert, somit keine Daten 
verschluckt oder abgehackt werden und man b) eine genaue Zeitbasis hat. 
Man müsste ggf. aber auf Phase Correct PWM wechseln, damit die Hardware 
die neuen PWM Daten immer korrkt am Ende der nächsten PWM-Periode 
verarbeitet. Sonst kann es komische Sprünge geben.

MFG
Falk

von Ingo L. (grobian)


Lesenswert?

Also, ich habe jetzt ne Wave-Datei reinkopiert (die ersten 44Byte 
natürlich entfernt) und es klingt alles so wie ich es mir vorgestellt 
habe.
Nächster Schritt wird jetz wohl ein EEPROM sein, weil Flash ist zu 
klein, noch besser wäre ne SD. Würde da die Möglichkeit bestehen da ne 
Wave-Datei über Windows aufzuspielen und die dann mit dem AVR 
abzuspielen ?
Kennt sich einer mit SD hier aus ? Im Tutorial finde ich auf Anhieb 
nichts.
Gruß aus Berlin

Ingo

#include <stdlib.h>
#include <avr/io.h>
#define F_CPU 12000000UL
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>

unsigned short a;
unsigned char pgmTabelle[] PROGMEM = {
    0x7C, 0x7C, 0x78, 0x75, 0x75, 0x69, 0x6C, 0x7B, 0x79, 0x74, 0x7E,
    0x84, 0x88, 0x8D, 0x8E, 0x93, 0x92, 0x86, 0x7B, 0x7B, 0x74, 0x6E,
    0x79, 0x7F, 0x7B, 0x76, 0x7C, 0x7A, 0x71, 0x72, 0x73, 0x79, 0x7B,
    0x77, 0x75, 0x6D, 0x67, ………ganz, ganz viele Daten…
    0x82, 0x85, 0x90, 0x92, 0x86, 0x87, 0x8F, 0x8B, 0x85, 0x7C
};
int main (void)
{

DDRD = (1 << PD5 );
TCCR1A = (1<<COM1A1) | (0<<COM1A0) | (0<<WGM11)  | (1<<WGM10) ;
TCCR1B = (0<<CS12)   | (0<<CS11)   | (1<<CS10);

  while(1)
  {
    for(a = 1; a < 14674; a++)
    {
      OCR1A = pgm_read_byte (&pgmTabelle[a]);
      _delay_us(100);
    }
  }
  return 0;
}

von Karl H. (kbuchegg)


Lesenswert?

Ingo Laabs schrieb:

>   while(1)
>   {
>     for(a = 1; a < 14674; a++)
>     {
>       OCR1A = pgm_read_byte (&pgmTabelle[a]);
>       _delay_us(100);
>     }
>   }


Den _delay solltest du ganz schnell loswerden, sonst wird das nichts. Du 
musst das Nachladen in einen Interrupt verbannen, damit es auch dann 
geschieht, wenn du von der SD-Karte den nächsten Datenblock einliest. 
Ansonsten wird dein Musikstück ständig stottern.

Libraries die SD Karten ansteuern können, gibt es viele. Einfach danach 
goggeln.

von Ingo L. (grobian)


Lesenswert?

Hallo Karl Heinz,
naja soweit bin ich ja noch nicht. Mühsam ernahrt sich das Eichhörnchen.
Erstmal soll es mit einem EEPROM geschehen. Die ganze Sache soll ja auch 
einen gewissen Lerneffekt haben. Ich denke mal, wenn ich jetzt erstmal 
ein EEPROM rangetüddelt habe, wird die sache mit der SD-Karte aktuell. 
Ich will ja nicht per copy und paste einen Code zurechtkopieren den ich 
dann letzendlich nicht verstehe. Das ist irgendwie nicht Sinn der sache.

von Karl H. (kbuchegg)


Lesenswert?

Ingo Laabs schrieb:
> Hallo Karl Heinz,
> naja soweit bin ich ja noch nicht. Mühsam ernahrt sich das Eichhörnchen.
> Erstmal soll es mit einem EEPROM geschehen. Die ganze Sache soll ja auch
> einen gewissen Lerneffekt haben. Ich denke mal, wenn ich jetzt erstmal
> ein EEPROM rangetüddelt habe,

Na ja.
So gross ist der Lerneffekt dann auch wieder nicht.
pgm_read_byte
durch die entsprechende eeprom Funktion ersetzen und fertig. Oder meinst 
du ein externes EEPROM?

von Ingo L. (grobian)


Lesenswert?

joooo...extern

von Falk B. (falk)


Lesenswert?

@  Ingo Laabs (grobian)

>joooo...extern

Nimm mal lieber externen Flash, der ist grösser und schneller. Gibts bei 
CSD, IT-WNS und Co. Und mit 4..32 Mbit kann man schon einiges anfangen. 
Dazu noch 20 Mbit/s per SPI und alles wird easy.

Das Ganze per SD-Karte wird DEUTLICH anspruchsvoller, weil man auf jeden 
Fall einen Softwarepuffer braucht, welcher mindestens eine Sektor (=512 
Bytes) zwischenspeichern kann.

MFG
Falk

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.