PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Tonausgabe über µC



Animus94
03.06.2012, 19:42
Hi,
Bräuchte mal eure Hilfe.
Ich habe an meiner C-Control Mega 128 ein Lautsprecher angeschlossen. Er befindet sich an einem PWM Ausgang mit Tiefpass.
In einem Array hab ich eine Sinustabelle angelegt, die den Sinusverlauf als PWM-Werte (0-255) wiedergibt.
Eigentlich muss ich jetzt ja nur noch die Frequenz es Sinus für verschiedene Töne ändern... Aber genau hier liegt das
Problem. Im Prinzip gibt es ja zwei möglichkeiten das zu tun entweder ein Sleep zwischen dem Abfragen der Sinus-werte oder mann überspringt einfach ab und zu
ein paar Werte des Arrays.
Aber irgendwie klappt das nicht so, ich meine ich kann keine verschiedenen Töne erzeugen.


int delval; // globale Variablendeklaration
float i;
int counter;
float rate;

int Sinustabele[256] = {
127,130,133,136,139,143,146,149,152,155,158,161,16 4,167,170,173,
176,178,181,184,187,190,192,195,198,200,203,205,20 8,210,212,215,
217,219,221,223,225,227,229,231,233,234,236,238,23 9,240,242,243,
244,245,247,248,249,249,250,251,252,252,253,253,25 3,254,254,254,
254,254,254,254,253,253,253,252,252,251,250,249,24 9,248,247,245,
244,243,242,240,239,238,236,234,233,231,229,227,22 5,223,221,219,
217,215,212,210,208,205,203,200,198,195,192,190,18 7,184,181,178,
176,173,170,167,164,161,158,155,152,149,146,143,13 9,136,133,130,
127,124,121,118,115,111,108,105,102, 99, 96, 93, 90, 87, 84, 81,
78, 76, 73, 70, 67, 64, 62, 59, 56, 54, 51, 49, 46, 44, 42, 39,
37, 35, 33, 31, 29, 27, 25, 23, 21, 20, 18, 16, 15, 14, 12, 11,
10, 9, 7, 6, 5, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0,
0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 9,
10, 11, 12, 14, 15, 16, 18, 20, 21, 23, 25, 27, 29, 31, 33, 35,
37, 39, 42, 44, 46, 49, 51, 54, 56, 59, 62, 64, 67, 70, 73, 76,
78, 81, 84, 87, 90, 93, 96, 99,102,105,108,111,115,118,121,124};

#define Led1 5


void main(void)
{

Port_DataDirBit(Led1,1);
Port_WriteBit(Led1,1);

counter = 0;
rate = 2;

while(1)
{
Timer_T0PWM(Sinustabele[counter],PS0_256);
counter = counter + rate;

if(counter > 256)
{
counter = 0;
}
}

Port_WriteBit(Led1,0);
}

Eigentlich müsste sich doch jetzt über die variable "rate" die frequenz irgendwie verändern lassen, weil je nachdem gibt es ja verschieden viele Ticks.
Aber es Funktioniert nicht , der Ton bleibt immer gleich.
Achso , angeschlossen ist der Lautsprecher wie folgt : --> http://www.mikrocontroller.net/articles/Klangerzeugung#Digital-Analog-Wandlung_.28DAC.29

Hoffe ihr habt eine Idee das ich verändern muss ^^

Danke im vorraus

radbruch
03.06.2012, 20:05
Hallo

Gleich vorweg: Ich kenne weder das c-control noch dessen spezielle C-Variante. Deshalb kann ich mit der Funktion Timer_T0PWM() nichts anfangen. Aber das Thema reizt mich zu einer kleinen Überlegung:

Wenn der Ton mit 1kHz klingen soll und deine Tabelle für eine Periode 256 Werte besitzt, dann mußt du alle 1/1000/256 Sekunde oder nach 0,00000390625 Sekunden einen neuen Wert aus der Tabelle ausgeben. Mal angenommen, du verwendest einen 16MHz-Takt, dann wären das 62,5 Takte pro Wert. Wie soll der Timer in 62 Takten auf 254 zählen? Wenn du nur jeden zweiten Wert verwendest kommst du auf 124 Takte und das gilt jetzt ja nur für 1kHz. (Was wird eigentlich bei Tabellenwert 0 ausgegeben?)

Besser wäre wohl dieser Ansatz: http://www.google.de/search?q=R-2R-Widerstandsleiter
Top: http://www.avr-asm-tutorial.net/avr_de/avr_dac.html

Gruß

mic

Animus94
03.06.2012, 20:32
Hi,

na es gibt ja noch mehr Vorteiler für die Pwm und einige sind in ihren Ticks viel schneller

Vorteiler (prescaler) Zeitbasis (Dauer eines Ticks)
------------------------------------------------
PS0_1 67,8 ns
PS0_8 542,5 ns
PS0_32 2,17 µs
PS0_64 4,34 µs
PS0_128 8,68 µs
PS0_256 17,36 µs
PS0_1024 69,44 µs
-----------------------------------------------
Bei PS0_128 dauert ein Tick 8,68 µs , also ein kompletter Sinus 2,222 µs das sind immerhin 450 Hz
und das bei voller Auflösung. Damit sollte man doch eiglich schon einige Töne hinbekommen.
Das mit dem Tabellenwert 0 hab ich mir noch garnicht überlegt ^^ PWM kann nicht auf null geregelt werden oder ?

So ein Widerstandsnetzwerk ist doch eigl. auch nichts anderes als eine PWM mit Tiefpass oder ?
Ein DA Wandler mit dem die Spannung am Verbraucher verändert wird

radbruch
03.06.2012, 20:43
Hallo

Auch ohne Prescaler kann der Timer nicht schneller als der Kontroller zählen.

Das Widerstandsnetzwerk ist ein variabler Spannungsteiler den man in 2er-Potenzen ansteuern kann.

Letztlich ist das Sinussignal ja für einen Lautsprecher gedacht. Aber der verschleift das Signal wohl sowieso, deshalb kannst du dir die Mühe vermutlich sparen.

Gruß

mic

PICture
03.06.2012, 21:51
Hallo!

Ausserdem ist ein Unteschied zwischen Sinus und Dreieck aus Lautsprecher nur für Musiker deutlich erkennbar. Für Frequenzen über ca. 10 kHz lässt sich sogar kein Unterschied zwischen Sinus und Rechteck nur mit dem Ohr erkennen. ;)

Animus94
03.06.2012, 22:51
Hmm ok also ein Widerstandsnetzwerk ...
trotzdem versteh ich nicht warum sich die töne überhaupt nicht ändern ...
hier hat jemand das selbe versucht und auch hinbekommen
--> http://www.mikrocontroller.net/topic/110106
leider kenne ich mit der Programmiersprache von dem nicht genügen aus um das nachzuvollziehen

radbruch
04.06.2012, 07:36
Hallo

Ich habe den gezeigten Thread durchgelesen, aber irgendwie scheint mir das nicht schlüssig. Der Timer läuft ohne Vorteiler im Mode 1 (WGM10 im TCCR1A ist gesetzt). Mode1 bedeutet: PWM, Phase Correct, 8-bit. Beim Match wird die ISR aufgerufen und OCR1A wird mit dem nächsten Wert aus der Tabelle geladen:

ISR(TIMER1_COMPA_vect){
OCR1A=pgm_read_byte(&sinewave[tone][(i>>8)]);
i += scale;
}

Es werden je nach Größe von scale einige Werte in der Tabelle übersprungen, so dass die Dauer einer Periode durch die Anzahl der Werte pro Periode beeinflusst werden kann. Soweit, so gut. Aber PhaseCorrekt bedeutet, dass der Counter von 0 nach 255 und wieder zurück zählt. Beim Aufwärtszählen wird beim Match der Ausgang zurückgesetzt, beim Abwärtszählen wird beim Match der Ausgang wieder gesetzt (COM1A1:0 in TCCR1A ist 10: Clear OC1A/OC1B on compare match when
up-counting. Set OC1A/OC1B on compare match when downcounting.) Der neue OCR1A-Wert wird zwar in der Match-ISR geschrieben, aber erst bei TOP upgedatet (Mode1: Update of OCR1x). Dazu kommt noch, dass nur jeder zweite aus der Tabelle gelesene und ins OCR1A geschriebene Wert auch wirklich Wirkung zeigt.

Es gibt noch viel zu erforschen.

Gruß

mic

Animus94
04.06.2012, 11:04
Hi,
hab heut morgen noch mal experimentiert und kann jetzt unterschiedliche Töne erzeugen.
Gut tun sie sich aber noch nicht wirklich anhören, aber immer hin lässt sich der Ton überhaupt verändern ^^

Ich hab die Liste runter gekürzt auf 128 Werte und den Vorteiler verändert.

int delval; // globale Variablendeklaration
int i;
int k;
float rate;

int Sinustabele[128] = {

0 , 0 , 0 , 1 , 2 , 3 , 5 , 7 , 9 , 12 , 14 , 17 , 21 , 24 , 28 , 32,
36 , 41 , 46 , 51 , 56 , 61 , 66 , 72 , 78 , 83 , 89 , 95 , 101 , 107 , 114 , 120,
127 , 133 , 139 , 145 , 151 , 157 , 163 , 169 , 175 , 181 , 186 , 192 , 197 , 202 , 207 , 212,
216 , 221 , 225 , 228 , 232 , 235 , 238 , 241 , 244 , 246 , 248 , 250 , 251 , 252 , 253 , 253,
253 , 253 , 253 , 252 , 251 , 250 , 248 , 246 , 244 , 241 , 239 , 236 , 232 , 229 , 225 , 221,
216 , 212 , 207 , 202 , 197 , 192 , 187 , 181 , 175 , 169 , 164 , 158 , 151 , 145 , 139 , 133,
127 , 120 , 114 , 108 , 102 , 96 , 90 , 84 , 78 , 72 , 67 , 61 , 56 , 51 , 46 , 41,
37 , 33 , 28 , 25 , 21 , 18 , 15 , 12 , 9 , 7 , 5 , 3 , 2 , 1 , 0 , 0};

#define Led1 5


void main(void)
{

Port_DataDirBit(Led1,1);
Port_WriteBit(Led1,1);

rate = 6;

while(1)
{

Sound(35.3,1500); //vermutlich 700 Hz

Sound(6,1500);
}

Port_WriteBit(Led1,0);
}


void Sound(float Rate, int Time)
{
for(k=0; k<(1/(128/Rate))*Time; k++) // Berechne zyklen
{
for(i=0; i<128; i=i+Rate)
{
Timer_T0PWM(Sinustabele[i],PS0_8);
}

}
}

Kann sich einer Vortstellen wie man die Frequenz berechnen kann?
Ich hab sie heute jetzt mit Headset und Audacity gemessen und komme auf Frequenzen zwischen 570 und locker 900 Hz...
Ich weiß nur nicht ob das stimmt ^^