Tonerzeugung mit einfachem I/O Pin

Gast #2407792
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
#2407794
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?
#2407797
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.
Gast #2407804
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.
Gast #2407816
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.
#2407820
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.
#2408057
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren