mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Sinus mit PWM erzeugen


Autor: Ingo Laabs (grobian)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Michael Haberler (mah)
Datum:

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

-mah

Autor: Ingo Laabs (grobian)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Ingo Laabs (grobian)
Datum:
Angehängte Dateien:

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

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ist doch sinusähnlich ;-)

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mach mal das Delay länger, so 100..200us.

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
uint8_t a;  //läuft automatisch über

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

Ist bestimmt etwas besser.

Autor: Ingo Laabs (grobian)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: avr (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht 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!
    TCCR1A = 0x81;          // non-inverted PWM on OC1A, 8 Bit Fast PWM 

MfG
Falk

Autor: Ingo Laabs (grobian)
Datum:

Bewertung
0 lesenswert
nicht 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;
}

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Ingo Laabs (grobian)
Datum:

Bewertung
0 lesenswert
nicht 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 ;-)

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@  Ingo Laabs (grobian)

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

>Wassn das ??

Quick & Dirty.

Autor: Ingo Laabs (grobian)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@falk
>> Overflow Interrupt

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

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Ingo Laabs (grobian)
Datum:

Bewertung
0 lesenswert
nicht 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;
}

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Ingo Laabs (grobian)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Ingo Laabs (grobian)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
joooo...extern

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht 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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.