Forum: Mikrocontroller und Digitale Elektronik Tonerzeugung mit einfachem I/O Pin


von AVR-Toaster (Gast)


Lesenswert?

Hallo Community,

da mir Google keine Antwort auf meine Frage liefern konnte, stelle ich 
sie mal hier. Es geht darum, dass ich mit einem AtMega8 ein Tonsignal 
(ca. 1kHz, Rechtecksignal) erzeugen möchte, das auf einem Lautsprecher 
ausgegeben werden soll. Dies macht man ja im Normalfall mit einem der 
Timer im PWM-Modus. Es ist aber so, dass ich keinen Timer mehr frei 
habe, weshalb ich eine Möglichkeit suche, das Signal anderweitig zu 
erzeugen.
Kann man hierzu einfach einen normalen I/O Pin schnell hintereinander 
an- und ausschalten, um das Signal zu erzeugen? Und wenn ja, was nehme 
ich als Zeitbasis?

Mfg
AVR-Toaster

von Sam .. (sam1994)


Lesenswert?

Du wirst einen Timer mit Compareregister brauchen. Im Wiki gibt es einen 
Artikel über Softwarepwm. Die dort optimierteste Methode wird 
wahrscheinlich nötig sein. Es wird jedoch nicht einfach werden.

Versuch lieber einen Timer freizubekommen, das ist sicher möglich. Liste 
mal für was die Timer benötigt werden. Welche Sprache programmierst du?

von Thomas E. (thomase)


Lesenswert?

AVR-Toaster schrieb im Beitrag #2407792:
> Kann man hierzu einfach einen normalen I/O Pin schnell hintereinander
> an- und ausschalten, um das Signal zu erzeugen? Und wenn ja, was nehme
> ich als Zeitbasis?
Du nimmst einfach einen deiner schon benutzten Timer, der einen 
Interrupt auslöst, zählst in der ISR eine Variable rauf und toggelst 
einen Portpin, wenn diese den passenden Wert erreicht hat. Wenn das dann 
900 oder 1100 Hz sind, stört das sicherlich auch keinen grossen Geist.

mfg.

von MaWin (Gast)


Lesenswert?

> Kann man hierzu einfach einen normalen I/O Pin schnell
> hintereinander an- und ausschalten

Nstürlich kann man

while(1)
{
    PORTB|=1;
    _delay_us(500);
    PORTB&=~1;
    _delay_us(500);
}

aber da dein Prozessor wohl dazwischen noch was zu tun hat,
wirst du dir mehr Gedanken um parallele Abläufe machen müssen.

von Sam .. (sam1994)


Lesenswert?

Vergiss mein kompliziert, bei konstantem Signal wird es leicht werden.

von AVR-Toaster (Gast)


Lesenswert?

Der Code ist in C geschrieben.

Ich nutze den 16-bit Timer1 im CTC-Modus, der Überlauf findet im 200Hz 
Takt statt. Timer0 (Überlauf ca. alle 10ms) ist für das Auslesen und 
Entprellen der Taster verantwortlich und Timer2 generiert aktuell das 
PWM-Signal für die Soundausgabe. Das funktioniert soweit auch alles. Nun 
möchte ich aber noch die Hintergrundbeleuchtung eines LCDs per PWM 
regeln, wozu ich gerne Timer2 nutzen würde.

von Sam .. (sam1994)


Lesenswert?

Timer1: 200Hz
Timer0: 100Hz

lässt sich in einen Timer schreiben:
1
volatile uint8_t counter = 1;
2
3
ISR(TIMER1_COMPA_vect)
4
{
5
  if(counter-- == 0)
6
  {
7
    counter = 1;
8
    //wird im 100Hz Takt aufgerufen
9
    //key_update();
10
  }
11
}

Man kann auch mehrere Funktionalitäten eines Timers gleichzeitig nutzen:
Timer2 kann das Soundsignal erzeugen und gleichzeitig über einen 
(anderen) PWM-Kanal die Leds dimmen. Die Frequenz ist dabei auf 1Khz 
festgelegt.

Es wäre auch möglich mit einem Timer alles zu erledigen. Die Frequenz 
wäre der gemeinsame Nenner (1kHz). Darin verschachtelt ein 5er Teiler 
und ein 2er Teiler.

von Krapao (Gast)


Lesenswert?

In einem anderen Thread nannte jemand die Seite www.romanblack.com

Dort sind ein paar interessante Elektronik- und µC-Projekte. Unter 
anderem auch eine Umwandlung von WAV-Dateien in das sog. 1-Bit Format 
und wie man das 1-Bit-Format auf einer einfachen Hardware abspielt: 
http://www.romanblack.com/picsound.htm

von Thomas E. (thomase)


Lesenswert?

AVR-Toaster schrieb im Beitrag #2407816:
> Nun möchte ich aber noch die Hintergrundbeleuchtung eines LCDs per PWM
> regeln, wozu ich gerne Timer2 nutzen würde.

Dafür braucht man keinen Timer.

volatile unsigned char nPwmCt = 0;

void Backlight(unsigned char nPwm)
{
    if (nPwm <= nPwmCt++) PortAus(); else PortAn();
}

Das rufst du regelmässig im Hauptprogramm auf. Die Frequenz ist der 
LED-Beleuchtung egal. Kannst aber auch noch einen Prescaler davor 
setzen. Dann schaltet der Port nicht so oft.

volatile unsigned char nPwmCt = 0;
volatile unsigned char nPrescaler = 0;

void Backlight(unsigned char nPwm)
{
  if(!nPrescaler++)
  {
    if (nPwm <= nPwmCt++) PortAus(); else PortAn();
  }
}

mfg.

von Sam .. (sam1994)


Lesenswert?

Thomas Eckmann schrieb:
> Das rufst du regelmässig im Hauptprogramm auf.

Das kann ins Auge gehen, wenn das Hauptprogramm mal länger arbeiten 
muss.

von Thomas E. (thomase)


Lesenswert?

Samuel K. schrieb:
> Das kann ins Auge gehen, wenn das Hauptprogramm mal länger arbeiten
> muss.
Paranoia.
Solange die Beleuchtung dabei nicht sichtbar flackert, ist das völlig 
egal.

mfg.

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.