Hi,
ich spiel grad etwas mit Midi rum, hab ein ganz einfaches
Keyboard, was ausschliesslich die 3-byte KEY ON und 3byte
KEY OFF folge sendet.
Das Ganze geht in den attiny2313 mit 10mhz Quarz auf den
UART rx.
Ich bin auch ziemlicher Anfaenger was das
alles angeht, ihr duerft gerne alles andere auch kritisieren,
wenn ihr etwas Konstruktives beizutragen habt, wahrscheinlich
ist der Code ziemlicher rotz.
Bisher hab ich zur Tonerzeugung(soft-PWM) den Timer benutzt im CTC-Mode
und das hat ganz gut funktioniert.
Da ich im CTC Mode aber nicht sehe wie ich 2 Compares
gleichzeitig gut durchfuehren kann, bin ich jetzt auf den
Normal Mode umgestiegen und habs etwas umgeschrieben.
Hier mein Code:
1
#include<avr/io.h>
2
#include<avr/interrupt.h>
3
#define F_CPU 10000000
4
#define BAUD 31250
5
#define BRC ((F_CPU/16/BAUD)-1)
6
7
8
//Noten definen
9
#define C4 261.626
10
#define CIS4 277.183
11
#define D4 293.665
12
#define DIS4 311.127
13
#define E4 329.628
14
#define F4 349.228
15
#define FIS4 369.994
16
#define G4 391.995
17
#define GIS4 415.305
18
#define A4 440.000
19
#define AS4 466.164
20
#define H4 493.883
21
22
volatileunsignedcharbuffer[6]={0};
23
volatileuint8_tpos_w=0;
24
volatileuint8_tflagon=0;
25
volatilefloatton=0;
26
volatileuint8_thightick=0;
27
volatileuint8_tlowtick=0;
28
//volatile uint16_t ticks = 10000;
29
intmain(void){
30
DDRB|=(1<<DDB0);
31
UBRRH=(BRC>>8);// BAUDRATE einstellen
32
UBRRL=BRC;
33
UCSRB=(1<<RXEN)|(1<<RXCIE);// RX
34
UCSRC=(1<<UCSZ1)|(1<<UCSZ0);// asynchronous UART
35
36
//TIMER stuff here
37
//TCCR1A = (1 << WGM12);
38
TCCR1B=(1<<CS10)|(0<<WGM12);
39
TIMSK=(1<<OCIE1A);
40
OCR1AH=0x33;
41
OCR1AL=0x33;
42
uint8_tmp=0;
43
volatileuint8_tkey=0;
44
sei();// interrupts on
45
while(1){
46
47
//Anfang// Key off
48
switch(flagon){
49
case0:if(buffer[pos_w]==0x90){
50
key=buffer[pos_w+1];
51
flagon=1;
52
}
53
else{
54
flagon=0;
55
}
56
break;
57
//Key on
58
case1:
59
//Welcher key?
60
mp=(key/12)-3;
61
switch(key%12){
62
case0:ton=C4*(1<<mp);break;
63
case1:ton=CIS4*(1<<mp);break;
64
case2:ton=D4*(1<<mp);break;
65
case3:ton=DIS4*(1<<mp);break;
66
case4:ton=E4*(1<<mp);break;
67
case5:ton=F4*(1<<mp);break;
68
case6:ton=FIS4*(1<<mp);break;
69
case7:ton=G4*(1<<mp);break;
70
case8:ton=GIS4*(1<<mp);break;
71
case9:ton=A4*(1<<mp);break;
72
case10:ton=AS4*(1<<mp);break;
73
case11:ton=H4*(1<<mp);break;
74
}
75
if(buffer[pos_w]==0x90){
76
key=buffer[pos_w+1];
77
}
78
//KEY OFF
79
80
elseif(buffer[pos_w]==0x80){
81
if(buffer[pos_w+1]==key){
82
flagon=0;
83
}
84
}
85
//Errechnen wie viele ticks ich brauche
86
//fuer jeweiligen Ton
87
hightick=(((int)(F_CPU/ton))>>8);
88
lowtick=(int)(F_CPU/ton);
89
break;
90
91
}
92
}
93
}
94
95
96
97
98
ISR(USART_RX_vect){
99
buffer[pos_w]=UDR;
100
buffer[pos_w+3]=buffer[pos_w];
101
if(pos_w<2){
102
pos_w++;
103
}
104
else{
105
pos_w=0;
106
}
107
}
108
109
110
ISR(TIMER1_COMPA_vect){
111
112
if(flagon){
113
PORTB^=1;
114
}
115
else{
116
PORTB=0;
117
}
118
OCR1AH+=hightick;
119
OCR1AL+=lowtick;
120
121
}
Meine noetige Anzahl an Ticks fuer eine bestimmte
frequenz wird erzeugt durch
1
hightick=(((int)(F_CPU/ton))>>8);
2
lowtick=(int)(F_CPU/ton);
Das kam im CTC mode bei mir direkt in den OCR1AH/L,
dann hat der Interrupt das richtige Schaltverhalten
am PINB0 gezeigt.
Beim Normalmode hab ich jetzt
1
OCR1AH+=hightick;
2
OCR1AL+=lowtick;
mit in die ISR genommen... also ueber nen
buffer overflow quasi addiert, weiss jetzt ehrlich
gesagt nicht, ob der uC da so berechenbar reagiert
wie ich moechte.
Es funktioniert aber, allerdings ist die Frequenz recht
unsauber geworden. Statt 440hz schwankt es so zwischen
440 und 444hz, je hoeher die frequenz desto heftiger
die Schwankung, es bleibt in den hohen Frequenzen nicht
bei ein paar hz.
Weiss jemand woran es liegt?
lg Robert
ps: Diese grosse verschachtelte Switchanweisung
ist total kacke. Seit sie drin ist, ist die Codegroesse von
500bytes auf 1500 bytes explodiert.
1. Für 16 Bit Register müsste es eine extra Abkürzung geben, entweder
ganz ohne H/L am Ende oder mit W für word wenn ich mich richtig erinner.
Diese in C immer benutzen, der compiler macht dann das Richtige draus.
2. Den ganzen switch case kannst du doch durch ein Array mit C4, CIS4...
als Inhalt ersetzen. Wenn du RAM sparen willst, leg den in den Flash.
Evtl. kennt dwr Compiler schon __flash, sonst über Progmem, dafür gibts
auch fertige lib zum einbinden.
Edit: zu 16 bit Registern gibts ne guten Artikel hier auf dieser Seite.
Auch andere Grundlagen werden gut erklärt, einfach mal durchschauen.
https://www.mikrocontroller.net/articles/AVR_16-Bit-Register
Hallo,
wie K.S. schon schrieb kann man die 16Bit Register vom Timer oder was
auch immer direkt beschreiben. Gilt also auch für das Baudratenregister
etc. Darum kümmert sich der Compiler.
Das wäre dann
1
OCR1A+=tick;
Für die Noten könnte man ein Array aufstellen
1
//Noten definieren
2
constfloatnoten[12]=
3
{
4
261.626
5
277.183
6
293.665
7
311.127
8
329.628
9
349.228
10
369.994
11
391.995
12
415.305
13
440.000
14
466.164
15
493.883
16
}
und wie folgt verwenden
1
ton=noten[key%12]*(1<<mp);
globale Änderung
1
#define F_CPU 10000000UL
2
volatileuint32_tton;
3
volatileuint16_ttick;
und die Berechnung von tick
1
tick=(uint16_t)((1.0*F_CPU/ton)+0.5);
Eigentlich würde ich die Rechnung Ganzahlig machen, aber das sprengt den
Wertebereich von uint32_t. Außer du kannst bei den Noten auch mit
weniger Kommastellen leben. Also das Ziel war eigentlich die Noten als
Ganzzahl und dann F_CPU / ton.
Wegen 2. Compare Match. Wie sollen die beiden Compare Matches
miteinander arbeiten?
Du nutzt aktuell im CTC Mode den Compare Match OCR1A für den
Timercounter TOP Wert. Das heißt du änderst damit den Zählumfang vom
Timer. Sprich dessen Frequenz. Steht OCR1A auf 10, zählt der Timer bis
10, löst einen Interrupt aus, du änderst ihn darin meinet auf 100, Timer
zählt bis 100, löst einen Interrupt aus, du änderst ihn meinetwegen auf
50, Timer zählt bis 50 usw.
Ein 2. Compare Match wäre zwar möglich, musst ihn nur in TIMSK
aktivieren, aber dessen Compare Interrupt kann nur auslösen, wenn der
Timercounter bis zum 2. Comparematch Wert zählt. Steht meinetwegen OCR1B
auf 200 und du lässt den Timer mittels OCR1A aber nur bis 199 laufen,
dann löst OCR1B nie aus. Oder du stellst sicher das die Werte immer
passen. Was du eben machen möchtest.
Robert schrieb:> ps: Diese grosse verschachtelte Switchanweisung> ist total kacke. Seit sie drin ist, ist die Codegroesse von> 500bytes auf 1500 bytes explodiert.
Das liegt nicht an den Switchanweisung sondern an deiner Float-Rechnung
mit den Frequenzen.
Hey,
vielen Dank fuer die 2 Haupthinweise.
Die unsaubere Frequenz ist seit ich das 16bit register
"normal" beschreibe auch weg.
So richtig verstehen tu ich es aber nicht, immerhin
hab ich wie im Datenblatt steht zuerst das H und dann das
L register beschrieben...
Aber moeglicherweise vergehen waehrend des Beschreibens
des hohen Registers nochmal Ticks, sodass OCR1AL ploetzlich
pro Interrupt etwas groesser wird als es sollte.
Ja, die Sache mit den floats und dieser Ganzen Anweisung.
Im Prinzip koennte das alles weg, wenn ich 2^(float) benutzen koennte.
Aber als ich math.h eingebunden hab, hat nichts mehr funktioniert.
Ein sehr guter Hinweis war einen Array in den flash zu speichern,
ich wusste nicht mal, dass das moeglich ist.
Ich probier da weiter rum, vielen Dank.
Zum 2. Compare Match im CTC Mode (Velt. D):
Ja genau, also es funktioniert auf jeden Fall so wie du
schreibst. Aber OCR1B hat dann ja die Schwierigkeit,
dass es immer vom Wert des OCR1A abhaengt, was ja den Reset
des Timers bestimmt.
Wenn die beiden unabhaengig sein sollen finde ich den
Normal Mode unkomplizierter, wo eine feste Groesse an
Ticks bis zum Ueberlauf festgelegt ist.
lg Robert
Vermutlich bin ich heute (nur heute?) etwas schwer von Begriff, aber
warum wird das nicht herkömmlich gelöst mit 'Toggle OC1A/OC1B on Compare
Match' auf Pin OC1A/1B?
Ich hätte ganz naiv diese Tonleiter extern in 16-bit-Werte umgerechnet
und damit, im CTC-Modus 4, OCR1A geladen, Ausgang Pin OC1A; Beispiel:
F4: 10MHz/349.228Hz/2 -1 = 14316.
Hallo,
nachdem alle ihre Informationen niedergeschrieben haben, sortieren wir
das gemeinsame Wissen und bauen es zusammen. :-)
CTC Mode funktioniert mit 2 Compare Matches nicht. Wenn OCR1A erreicht,
wird Counter genullt. Wenn OCR1B größer OCR1A ist, toggelt OCR1B nicht.
Normal Mode und Compare Toggle für OCR1A und OCR1B sollte funktionen,
weil Counter durchlaufen kann und man die Comparewerte in deren ISR
aufaddiert. Der ATtiny2313 scheint auch für alle Timermodi immer Double
Buffered Register zu haben. Im Gegensatz zu vielen ATmegas.
Das sollte zum Ziel führen.
S. Landolt schrieb:> Vermutlich bin ich heute (nur heute?) etwas schwer von Begriff, aber> warum wird das nicht herkömmlich gelöst mit 'Toggle OC1A/OC1B on Compare> Match' auf Pin OC1A/1B?
Mir geht es genau so. Ich finde es schon allein pervers für jede
Flanke eines (Ton-) Signals den Controller zu interrupten. Das
ergibt auf jeden Fall schon "Verzerrungen" wenn Zeichen über
MIDI eintreffen.
S. Landolt schrieb:> Da bin ich jetzt doch froh, dass sich ein Zweiter findet: einfache> Frequenzerzeugung per PWM mit Tastgrad 50 %.
Das wäre einfach zuviel verlangt wenn man sich einen Code aus
dem Internet kopiert hat und froh ist dass es so funktioniert.
Hallo,
laut meiner Auffassung sollte es egal sein ob Normalmode oder CTC Mode
12. Bei letzteren setzt man ICR1 auf MAX. Dann muss man weiterhin für
die Compare Matches A/B den nächsten Wert aufaddieren. Dann werden diese
unabhängig voneinander ausgelöst und erzeugen ihre Frequenzen.
Ein PWM Mode funktioniert laut meiner Auffassung nicht. Es gibt ein
gemeinsames TOP Register was die Frequenz bestimmt. Die Compare Matches
erzeugen nur die Pulsweite.
Im Normalmode kann man aber jederzeit den Wert von den Registern ändern,
weil Double Buffered. Der neue Wert wird erst beim kommenden Compare
übernommen. Bei weiterer Überlegung muss man die Werte in den OCR1x
Register auch nicht in dessen ISR updaten. Kann man auch außerhalb. Den
alten Wert merkt man, addiert den neuen drauf und merkt sich den
aktuellen als alten für die nächste Berechnung. Eine neue Zuweisung in
die Register erfolgt nur bei Ungleichheit neuer <> alter Wert.
Ansonsten sollte der TO nochmal neu beschreiben was die beiden Takte
machen sollen? Sonst wird mir das dann doch zu theoretisch.
Wenn ich es richtig verstanden habe, möchte Robert genau einen Ton
erzeugen; zumindest sehe ich im Programm keinen zweiten. Und diesen Ton,
z.B F4, erzeugt man mit der Hardware, entweder
- Modus 12 (CTC), ICR1=14316, OCR1A=irgendwas
oder
- Modus 13 (fast PWM), ICR1=28634, OCR1A=14316.
Ausgabe an Pin OC1A.
Fsst PWM ergibt eine etwas bessere Genauigkeit.
Hallo,
bei einem Ton gebe ich dir Recht. Alles kein Problem. Dafür hat man
viele Möglichkeiten.
Im Eingangsposting wird als Nebeninformation etwas von einem 2. Compare
Match geschrieben.
> Da ich im CTC Mode aber nicht sehe wie ich 2 Compares gleichzeitig gut> durchfuehren kann, bin ich jetzt auf den Normal Mode umgestiegen und habs> etwas umgeschrieben.
Darüber mache ich mir den Kopf.
> Darüber mache ich mir den Kopf.
Also ich vermute, dass Robert, da
> ziemlicher Anfaenger
die Ton- bzw. Frequenzerzeugung per Hardware nicht kennt und eben in
einem ersten Anlauf den Ausgang per Software umschalten möchte.
Okay, Veit Devil, vermutlich haben Sie doch Recht:
> Bisher ... hat ganz gut funktioniert ...
Und jetzt soll der nächste Schritt bzw. Ton kommen.
Aber zwei Töne sind dann wohl auch nicht so zufriedenstellend, eine
Handvoll sollte es vielleicht schon sein. Und das geht dann wirklich nur
mit Software, und wird alles andere als leicht. Mir fällt da jetzt
dieses elektronische Spielwerk von ElmChan ein, Stichwort DDS, aber das
ist in Assembler (und mangels Speicher auf einem ATtiny2313 wohl ohnehin
nicht machbar).
Hallo S. Landolt,
wir warten mal ab ob Robert sein Vorhaben noch konkretisieren kann
bezüglich ein Ton, zwei Töne oder noch mehr Töne ... sonst übertönt das
hier ... :-)
Gruess euch,
ich komm jetzt erst von der Arbeit,
ich bin eigentlich Baecker und das hier ist
ein Hobby...
Vielen Dank fuer die weiteren Anmerkungen,
also tatsaechlich ist ja meine Hauptfrage schon
geloest.
Sanges Knabe schrieb:> Wo ist denn da der Übertrag nach OCR1AH wenn OCR1AL durch> Addition von <lowtick> überläuft?
Das ergibt Sinn!
Ja, also Tonerzeugung mit PWM mach ich aus dem einfachen
Grund nicht, weil ich in der weiteren Folge mehrere
Toene erzeugen will, daher die Softwareloesung.
Soweit ich weiss kann ich nur eine PWM schalten mit Hardware.
Mit der Genauigkeit bin ich zufrieden, ich werd sehn
wie sich das mit dem 2. Interrupt verhaelt und nem 2. Ton.
Grundsaetzlich probier ich aktuell nur rum wie weit ich komme...
wenn 2 Toene das Maximum sind auf dem attiny2313 dann ist das
wohl so.
Ansonsten werd ich auch mal rumprobieren ob ich mit PWM was
anderes als Rechtecktoene generieren kann.
Ich hab auch noch groessere AVR-uCs rumliegen,
mir macht Hardwarebegrenzung aber Spass, ich will
erstmal den attiny ausreizen bis ich zu was groesserem
uebergehe. Vielleicht lern ich als naechstes auch asm,
bestimmt hilfreich um den uC noch besser zu verstehen.
lg Robert
Landolt irrte:
> mangels Speicher auf einem ATtiny2313 wohl ohnehin nicht machbar
Entschuldigung, Robert, da war ich vorschnell: das geht sehr gut auf
einem ATtiny2313, sowohl was SRAM als auch Flash betrifft; ganz sicher
auch in C. Stichwort ist, wie erwähnt, DDS:
https://de.wikipedia.org/wiki/Direct_Digital_Synthesis
Für einen Anfänger vielleicht nicht ganz einfach, aber letztlich ein
prima Einstieg.
Jetzt meine ich zu verstehen, warum Sie mir
Hardware PWM empfohlen haben Herr Landolt.
Wenn ich das mit der DDS auf dem attiny hinkriege,
haette ich natuerlich gar keinen Bedarf fuer mehrere
Interrupts und den ganzen Softwarepinschaltkram,
dann koennte ich die addierte Welle bei mehreren Toenen
vorher ausrechnen und dann per DDS erzeugen.
Jetzt ist die Frage, ob die PWM schnell genug ist und
ich mit der Rechenleistung des attiny2313 hinkomme.
Vielen Dank fuer den Hinweis.
lg Robert
Bei konstanten Sinustönen ist die benötigte Rechenleistung nicht der
Rede wert, es sind ja nur 16-bit-Additionen; das sieht allerdings ganz
anders aus, falls die Töne ein- und ausschwingen sollen, denn der
ATtiny2313 hat keine Hardwaremultiplikation.
Für eine bessere PWM empfehle ich den Betrieb mit einem 20 MHz-Quarz,
abhängig von der höchsten gewünschten Tonfrequenz. Für erste Versuche
bzw. eine Einarbeitung aber reicht auf jeden Fall das Vorhandene, der
tiny mit 10 MHz.
> ob die PWM schnell genug ist
Angenommen, wir arbeiten mit 11 bit Summe (8 Tonkanäle mit 8 bit) bei 20
MHz, ergibt bei Fast-PWM eine Abtastfrequenz von 9765 Hz - nun ja, bei
CDs sind es 44100 Hz (plus entsprechende Filter), aber für den Anfang
doch ganz brauchbar.
S. Landolt schrieb:> Mir fällt da jetzt> dieses elektronische Spielwerk von ElmChan ein, Stichwort DDS, aber das> ist in Assembler (und mangels Speicher auf einem ATtiny2313 wohl ohnehin> nicht machbar).
Natürlich kann man auf einem 2313 DDS implementieren, nur halt wegen der
begrenzten Menge Flash (und damit der begrenzten Größe der Sinustabelle)
nicht in besonders hoher Qualität. Was aber erstens nicht ganz so
schlimm ist, weil es sowieso kein adäquates Ausgabedevice für eine
bessere Qualität gäbe und zweitens ist das, was DDS-mäßig tatsächlich
geht, immer noch deutlich besser als Rechteck-Gepiepse. Aber ja:
Assembler ist hierfür tatsächlich überaus hilfreich...
Andererseits: wenn Rechteck gut genug ist (offenbar ist es dem TO gut
genug), dann geht auch mit plain old stupid C noch ganz gut was bei zwei
Kanälen via Timer1. Der Trick ist: geändert werden immer nur die
OCR-Register, der Timer läuft frei durch mit maximalem Zählumfang und
maximaler Taktfrequenz (im Mode 0 und mit COMx1:COMx0 = 1 für beide
Kanäle).
Dadurch spielen die variablen Latenzen der ISRs eine sehr viel geringere
Rolle, die eigentliche Ausgabe erledigt ja die Timer-Hardware taktgenau.
Man muß also nur folgendes in jede der beiden OCR-ISRs schreiben:
OCRx = OCRx + HalfPeriodx
Und das muß nur so rechtzeitig erfolgen, das noch nicht HalfPeriodx
Takte seit dem letzten ISR-Aufruf vergangen sind. Das sollte in C bei
8MHz Systemtakt für den Audio-Bereich noch problemlos machbar sein.
In main() ist also nur HalfPeriodx für die beiden Kanäle zu setzen, den
Rest macht sich das System aus ISR und Timer selbst...
Hi,
ja also 2 Ton Rechteckgedudel via Timer1 hat ganz gut
funktioniert. War auch sauber.
Ich hab mir auch die Ratschlaege von hier zu Herzen genommen
und die Divisionen und floats entfernt, das macht alles
gleich viel schlanker.
Aber wie Landolt schon weise vorraussah, wenn man erstmal 2 Toene
hat will man natuerlich mehr und ein paar andere Formen.
Jetzt hab ich mich an die DDS Variante drangesetzt
und kriege es leider nicht hin, dass die gefilterte
Frequenz (ich hab sie mit 2 RC-Filtern geglaettet)
ganz sauber ist. Manche Frequenzen sind
relativ sauber, andere springen total (z.b zwischen
1.72khz und 1.78khz).
Hier mal eine reduzierte Variante meines Codes (die
aber mein ungeloestes Problem genauso enthaelt)
Ich hatte erst vor die Tonerzeugung in der ISR selbst zu machen,
aber sobald ich mehr als eine Variable in if Anweisungen da reinpacke
funktioniert gar nichts mehr... Ich nehme an, dass ich nur eine
gewisse Anzahl an Takten fuer die ISR habe.
Ich vermute, dass mein cnt Uebertrag fuer die Unsauberkeit
verantwortlich
ist. Allerdings habe ich schon recht viel ausprobiert.
statt cnt = 0 wurden schon cnt - (ton[0]*2) probiert oder auch
cnt % (ton[0]*2), aber das macht es eher schlimmer.
Die anderen PWM Modi haben jetzt auch nichts geaendert.
Den PWM Modus mit der ICR1 find ich ganz angenehm, weil ich
PWM Schnelligkeit gegen Genauigkeit in der Amplitude eintauschen
kann.
lg Robert
Hallo Robert, vorab: ich kenne mich in diesem Bereich nur mittelmäßig
aus, aber solange kein Anderer einspringt, will ich es versuchen.
Sie erzeugen nach wie vor Rechtecksignale, auch wenn sie nach dem
doppelten RC-Filter irgendwie dreieckförmig aussehen, abhängig von der
Frequenz. Intention bei DDS ist aber eine beliebige Kurvenform; bei
Ihnen, für den Anfang, würde ich Sinus bevorzugen. Dessen Werte
fortlaufend zu berechnen ist zeitlich auf einem AVR8 unmöglich, folglich
wird das einmalig extern gemacht und die Werte als Tabelle im
Programmcode hinterlegt; erstmal, und für den ATtiny2313 mit seinen 2
KiB Flash, muss ein int8-Array mit 256 Elementen reichen (belegt also
1/8).
Damit sind wir beim nächsten Kriterium: man muss einen Kompromiss
finden zwischen Zeit- und Amplitudenauflösung. Derzeit haben Sie bei
letzterer nur 1 bit (Rechteck eben), dafür ist die Zeitauflösung mit 100
kHz (ICR1=99) unnötig hoch; was Ihnen übrigens ja nur 100 Takte pro
Durchlauf für die Programmausführung erlaubt. Also: dieses int8 (8 bits,
d.h. 256 Stufen Amplitude) ergibt rund 39 kHz (10 MHz/256) Zeitauflösung
(bzw. Abtastfrequenz) - für einen Tonkanal, aber damit könnten Sie ja
erstmal beginnen. Für acht, addiert, wie oben erwähnt: 10 MHz/ (8*256)
= 4.9 kHz (da wäre dann doch ein 20 MHz-Quarz besser). Übrigens haben
Sie hier nun bequeme 2048 Takte pro Durchlauf.
Im Anhang finden Sie ein simples Beispiel für einen Tonkanal mit dem
festen Ton A4 - simpel sowohl was den Umfang als auch meine C-Kenntnisse
betrifft; aber es sollte ausreichen, um das Prinzip zu verstehen und
eventuell darauf aufzubauen.
Hallo Herr Landolt,
vielen Dank fuer den C-Code und die Erklaerung,
mir sind tatsaechlich nochmal einige Sachen beim
Vorgehen klargeworden.
S. Landolt schrieb:> Sie erzeugen nach wie vor Rechtecksignale, auch wenn sie nach dem> doppelten RC-Filter irgendwie dreieckförmig aussehen, abhängig von der> Frequenz.
Tatsaechlich war das (kompliziert erzeugte) Rechteck bewusst
gewaehlt, ich hatte auch schon andere Formen ausprobiert gehabt,
aber das Rechteck war erstmal das einfachste, um an meinem
Jitterproblem zu arbeiten.
Ich entschuldige an dieser Stelle, dass ich meine Informationen
eher knapp halte und auch das richtige Vokabular noch nicht
so recht verwenden kann.
Der Code von Ihnen funktioniert gut, ich habe einen ersten
Fusionsversuch unternommen:
Mit dem Code kann ich den Sinus zwischen C4 und H4 erzeugen...
Tatsaechlich... ist der Jitter aber immernoch nicht ganz raus.
Einige Toene sind ziemlich sauber... D4 z.B, andere klingen
schrecklich wie AS4... (insgesamt aber schon deutlich besser als
in meinem alten Code).
Ich habe auch mal probehalber das AS4 in Ihrem Code ohne
den anderen Schnickschnack ausprobiert (also nur den Phasendeltateil
ersetzt):
while(!(TIFR&(1<<TOV1))){};// ohne jitter: sleep plus interrupt
114
115
TIFR|=(1<<TOV1);
116
117
};
118
}
Auch hier ein Jitter von +-7hz oder so, sehr hoerbar leider.
Die Frage ist jetzt, ob das besser wird, wenn ich die Frequenz durch
ICR1 erhoehe und somit die Zeitaufloesung, (wo ich jetzt aber den ganzen
Sinusarray neu berechnen muesste) oder mir den 20Mhz Quarz kaufe...
Vielleicht ist hier auch einfach die Grenze des Attiny2313 erreicht
was Genauigkeit bei DDS angeht oder mein avr-gcc compiled anders als
gedacht.
Vielleicht ist es auch jetzt an der Zeit, dass ich mal avr-asm lerne,
ich bin mit C jetzt haeufig an Merkwuerdigkeiten gestossen, weil
ich die Hardware nicht richtig kenne.
Hallo Robert,
merkwürdig, mein (ungeschultes) Ohr hört keinen Jitter, auch kann ich
solchen weder auf dem Oszilloskop noch dem Frequenzzähler sehen. Liegt
das vielleicht an Ihrer Stromversorgung, wie sieht diese aus?
> wo ich jetzt aber den ganzen Sinusarray neu berechnen muesste
Das sollte mit einer beliebigen Tabellenkalkulation kein Problem sein.
Ich selbst verwende ein Progrämmchen auf einem Taschenrechner.
Nur am Rande: wenn Sie den Hinweis "Längeren Sourcecode nicht im Text
einfügen, sondern als Dateianhang" befolgen, werden die eigentlichen
Beiträge lesbarer.
> Damit kann ich arbeiten...
Prima. Sollten noch Fragen entstehen, helfe ich gerne (soweit ich kann).
Robert schrieb:> Vielleicht ist es auch jetzt an der Zeit, dass ich mal avr-asm lerne,> ich bin mit C jetzt haeufig an Merkwuerdigkeiten gestossen, weil> ich die Hardware nicht richtig kenne.
Kleiner Kommentar. Es ist erstmal egal ob du Assembler oder C/C++
programmierst. Die Hardware musst du hier in beiden Fällen kennen. Du
programmierst schließlich hier auch in C/C++ hardwarenah, weil du direkt
die Register verwendest. Und in Assembler musst du die Hardware erst
recht richtig kennen.
Zu den "Merkwürdigkeiten". Ich habe da eine Vermutung, weil mich mein
Compiler davor warnt, nämlich den Wertebereichsüberlauf des Sinus Array.
Der Datentyp sollte uint8_t sein und nicht int8_t. Wenn die anderen
auskommentierten Werte mit verwendet werden sollen dann eher int16_t.
Folgende kleine Änderungen würde ich auch noch vorschlagen. Damit sauber
gerechnet wird.
1
#define F_CPU 10000000UL
2
3
#include<avr/io.h>
4
#include<avr/pgmspace.h>
5
6
constuint16_tICR=256;
7
constuint16_tPWM=F_CPU/ICR;
8
constuint16_tA4=440;
9
10
uint16_tA4_Phasendelta=782;// (AS4/PWM*65536)
11
uint16_tPhase;
Wobei mir der Variablenname 'ICR' eine Spur zu nah an den defines liegt.
:-)
Große Verwechslungsgefahr.
Wie auch immer, teste mal ...
Um sinusähnliche Töne zu produzieren, die frequenzstabil sind, lohnt
sich ein Studium der AN_1982 von Microchip, früher bekannt als AVR314.
https://www.microchip.com/wwwAppNotes/AppNotes.aspx?appnote=en591264
Die Applikation erzeugt zwei Sinustöne, bekannt als 'Touchtones' oder
DTMF. Die Aufgabe bestünde darin, MIDI zu iplementieren und die Tabelle
auf Noten umzurechnen.
Da auch eine Skalierung vorgesehen ist, wäre auch Lautstärke
kontrollierbar.
> Der Datentyp sollte uint8_t sein und nicht int8_t.
Richtig, das hatte ich übersehen. Galt nämlich ursprünglich für die
auskommentierte Tabelle.
Bleibt allerdings die Frage, warum für mich hier alles gut klingt.
S. Landolt schrieb:> Bleibt allerdings die Frage, warum für mich hier alles gut klingt.
Weil du vielleicht älter als Robert bist? :-) :-) :-)
Spass beiseite. Ich weiß es auch nicht.
Und dass Sie das vielleicht mal eben schnell aufbauen und reinhören?
Übrigens bin ich steinalt - und das Schlimmste daran ist, dass ich mich
auch so fühle.
Hallo,
mit schnell aufbauen ist bei mir leider nicht. Ob ich überhaupt einen
geeigneten Piepser habe? Ich müßte erstmal überlegen wie ich das
ansonsten machen könnte.
Vorab gefragt, ist AS4 gleich A4 in der Rechnung? Weil ich wenn dann
16MHz habe.
1
#define F_CPU 10000000UL
2
constuint16_tICR=256;
3
constuint16_tPWM=F_CPU/ICR;
4
constuint16_tA4=440;
5
uint16_tA4_Phasendelta=782;// (AS4/PWM*65536)
6
uint16_tPhase;
> Übrigens bin ich steinalt - und das Schlimmste daran ist, dass ich mich> auch so fühle.
Das kommt dir nur so vor.
> AS4 gleich A4
Nee, da war Robert schreibfaul. Wenn Sie ganz an den Anfang gehen, dann
steht dort u.a.
> #define GIS4 415.305> #define A4 440.000> #define AS4 466.164> #define H4 493.883
Hallo,
ich sehe gerade etwas beim Codeabgleich für meinen Mega2560. Ihr nehmt
FastPWM? Hat der ATtiny2313 vielleicht ebenfalls das Problem mit FastPWM
und OCRnx = 0 mit Nadelimpulsen? Nicht das die Robert hört und S.Landolt
nicht mehr? FastPWM erzeugt bei den alten ATmegas nämlich Nadelimpulse
mit Compare 0. Nicht das die der ATtiny auch hat. Müßtet ihr euch auf
dem Oszi anschauen.
Alle anderen Timer Mode haben das Problem nicht. Zumindestens beim
ATmega und Verwandte.
Ansonsten, was passiert wenn man im Sinus Array alle '0' durch '1'
ersetzt? Dann wären die Nadelimpulse, falls vorhanden, erstmal weg.
Oh, das tut mir leid,
das A4 hab ich einfach nicht entfernt.
C-Code werde ich in Zukunft als Anhang posten.
Nochmal zum Jitter:
Natuerlich muss man, wenn man einen relativen Anfaenger
wie mich beraet, davon ausgehen, dass auch an ganz
anderer Stelle etwas nicht ganz sauber laeuft.
Von daher kann es sehr gut sein Herr Landolt, dass
es bei ihnen sauber klingt und bei mir nicht,
ich vermute ehrlich gesagt, dass ich zu kleine Kondensatoren
in meinem RC-Filter verwendet habe (weswegen die schnellere
Frequenz das Problem geloest hat)... aber sowas faellt mir
dann auch erst im Prozess auf.
Aber ja... es tut ja jetzt eigentlich was es soll,
also wenn es jemand zusammensteckt sollte es ja funktionieren,
vor allem wenn er weiss, wie er das ganze Drumrum zu gestalten hat.
Mein naechstes Projekt ist jetzt den Sinus zu verstaerken und
ihn auf meinem Lautsprecher in Zimmerlautstaerke auszugeben,
denn nach meinem RC-Filter hat er eine ziemlich hohe Impedanz.
Dazu fummel ich grade mit BJTs und rum und probier damit
einen Emitter-Follower zu bauen, was bisher ziemlich verzerrt.
Spannend ist fuer mich auch zu sehen wie das Signal bei Last
sich voellig verzerrt, ich kann daraus aber noch nicht viel
ableiten.
Nunja, ich tauch wieder ab in eine laengere Probierphase,
ich poste wenn ich fertig bin dann den Code,
vielleicht hilft es ja jemandem in Zukunft.
lg Robert
> Ihr nehmt FastPWM?
Ja, denn die 10 MHz Systemtakt sind eh schon recht wenig; angenehmer
wäre ein Tiny mit PLL auf 64 MHz.
Das mit den Nadelimpulsen muss ich mir morgen genauer anschauen, heute
wird das nichts mehr.
Einen schönen Abend wünsche ich.
> RC-Filter
Nun, nach kurzer Überschlagsrechnung im Kopf habe ich aus der
Bastelkiste 2.7 kOhm und 100 nF gegriffen, danach sieht das Bild auf
einem Analogoszilloskop recht gut aus. Dahinter dann ein altes
Platinchen mit eigenem RC-Filter, Dimensionierung müsste ich erst
nachschauen, und TDA7052A, der zwei parallele Miniaturlautsprecherboxen
ansteuert.
Robert schrieb:> Mit dem Code kann ich den Sinus zwischen C4 und H4 erzeugen...> Tatsaechlich... ist der Jitter aber immernoch nicht ganz raus.
Bist du sicher, dass das Jitter ist?
> Die Frage ist jetzt, ob das besser wird, wenn ich die Frequenz durch> ICR1 erhoehe und somit die Zeitaufloesung
Höhere zeitliche Auflösung ist in jedem Fall anzustreben, bei
PWM-Ausgabe ganz besonders. Macht schlicht die Filterung der Ausgabe
viel einfacher.
> (wo ich jetzt aber den ganzen> Sinusarray neu berechnen muesste)
Häh? Wieso muss man die Sinustabelle neu berechnen, wenn man die
Abtastfrequenz ändert? Nein, das muß man natürlich nicht. Nur die
Increments für den/die Phasenakku(s) muss man dann neu berechnen. Das
kann man komfortabel zur Compilezeit mit einem entsprechenden Makro
erledigen. In C und Asm gleichermaßen übrigens.
> oder mir den 20Mhz Quarz kaufe...
Ich würde eher den Kauf eines besser geeigneten Tiny vorschlagen,
nämlich eines mit Highspeed-Timer, z.B. ATtiny 45 oder 85. Damit kann
man eine wesentlich bessere PWM-Ausgabe realisieren. Bei deiner
Anwendung wäre eine 10 Bit-Ausgabe mit 62,5kHz PWM-Frequenz und 31,25kHz
Samplefrequenz machbar. Die hätten dann auch genug Flash für eine
adäquate Sinustabelle.
> Vielleicht ist es auch jetzt an der Zeit, dass ich mal avr-asm lerne,> ich bin mit C jetzt haeufig an Merkwuerdigkeiten gestossen, weil> ich die Hardware nicht richtig kenne.
Das hat mit C oder Asm nix zu schaffen, auch in C programmiert man
direkt die Register. Wenn du die Hardware nicht richtig kennst, musst du
in beiden Fällen einfach die Datenblätter lesen, um sie kennen zu
lernen.
Der Vorteil von Asm ist einzig, dass man recht leicht diverse, sehr
wirksame Optimierungstricks anwenden kann, die in C nur schwierig oder
garnicht realisierbar sind.
Übrigens gibt es ein fertiges (Asm-) Projekt, was schon alles
Wesentliche kann, was du für deine Anwendung brauchst.
Beitrag "Westminster Soundgenerator mit ATtiny85"
Das müßte natürlich erheblich umgebaut werden, aber dieser Umbau
bestünde eigentlich zu großen Teilen nur daraus, für deine Anwendung
überflüssiges Zeug rauszuwerfen (insbesondere das besonders "teure", was
dann die höhere PWM- und Samplefrequenz im Vergleich zur Vorlage
ermöglichen würde).
Falls du dich da ranwagen willst, noch folgender Tipp: Ein
Synthesizer-Kanal entspräche in deiner Anwendung einer Stimme einer
mehrstimmigen Melodie aus reinen Sinustönen.
Veit Devil schrieb:
> Nadelimpulse mit Compare 0
In der Tat, steht so im Datenblatt des ATtiny2313 'If the OCR1x is set
equal to BOTTOM (0x0000) the output will be a narrow spike', und sehe
ich auch auf dem Oszilloskop. Aber warum sollte das Störungen
verursachen, das (gefilterte) Analogsignal geht eben nicht vollständig
auf 0 herunter.
Hallo c-hater,
c-hater schrieb:> Bist du sicher, dass das Jitter ist?
Nein... ich weiss nicht was es war, aber es war
eindeutig hoerbar (tonfrequenzabhaengig) und
auch das Osziloskop hat "gezittert".
c-hater schrieb:> Häh? Wieso muss man die Sinustabelle neu berechnen, wenn man die> Abtastfrequenz ändert? Nein, das muß man natürlich nicht. Nur die> Increments für den/die Phasenakku(s) muss man dann neu berechnen. Das> kann man komfortabel zur Compilezeit mit einem entsprechenden Makro> erledigen. In C und Asm gleichermaßen übrigens.
Das mit den Phasenakkus stimmt natuerlich. Zusaetzlich:
Wenn ich ICR1 auf 100 setze, dann kann ich in OCR1A nur noch Werte
von 0 bis 100 festlegen... also muesste ich auch die neu berechnen.
Ich war so faul und hab ICR1 auf 128 gesetzt und aus
OCR1A = bla ein OCR1A = bla/2 gemacht... und die Phasenakku auch
halbiert glaube ich, laeuft jedenfalls, aber so ein typischer
Pfusch von mir, den ich hier nicht posten wollen wuerde.
In der finalen Version mach ich das mal raus, aber um das als Makro
zur Compilezeit zu erledigen, haette ich die gesamte Sinustabelle
auch als Makro definieren muessen...
c-hater schrieb:> Ich würde eher den Kauf eines besser geeigneten Tiny vorschlagen,> nämlich eines mit Highspeed-Timer, z.B. ATtiny 45 oder 85. Damit kann> man eine wesentlich bessere PWM-Ausgabe realisieren. Bei deiner> Anwendung wäre eine 10 Bit-Ausgabe mit 62,5kHz PWM-Frequenz und 31,25kHz> Samplefrequenz machbar. Die hätten dann auch genug Flash für eine> adäquate Sinustabelle.
Ja... ich glaube das tu ich tatsaechlich einfach mal,
aber dafuer muesste ich mich erst wieder in den attiny85 einlesen.
Ich versteh nicht so recht, wie er die hohe PWM Frequenz erzeugt
und was ich dafuer brauche, damit es gut laeuft.
Ich finde dein Projekt uebrigens ziemlich bemerkenswert,
es ist fuer mich aber noch eine Stufe zu komplex glaube ich.
c-hater schrieb:> Das hat mit C oder Asm nix zu schaffen, auch in C programmiert man> direkt die Register. Wenn du die Hardware nicht richtig kennst, musst du> in beiden Fällen einfach die Datenblätter lesen, um sie kennen zu> lernen.
Ja das Ding ist nur, dass C einem etwas vorgaukelt, was unter Umstaenden
nicht da ist. Datenblaetter sind fuer mich nichts, was ich von
vorne bis hinten durchlese und danach denke 'Alles klar, Operation
wird ausgefuehrt', ich bin ja keine Maschine, sondern ein maessig
intelligenter Mensch.
Die Information 'avr kann hardwaremaessig nicht dividieren' ist in
asm sehr ersichtlich, steht irgendwo im 270 Seiten Datenblatt und
wird einem in avr-C erstmal nicht unter die Nase gehalten.
Danke jedenfalls fuer den Hinweis auf dein Projekt.
Sollte ich asm erstmal ein wenig beherrschen, werd ich gern
mal reinschauen und gucken, ob ich etwas daraus lernen kann.
lg Robert
Guten Morgen, Robert,
also wenn, dann würde ich eher zum ATTiny861 raten, der 85er mit seinen
8 Pins ist meist selbst für Minimalisten denn doch zu - nun -
minimalistisch. Beide haben aber das Problem, dass sie nicht über einen
UART, sondern nur über dieses USI verfügen, da hätten Sie also eine
weitere Baustelle - etwas viel für den Anfang.
S. Landolt schrieb:> Guten Morgen, Robert,> also wenn, dann würde ich eher zum ATTiny861 raten, der 85er mit seinen> 8 Pins ist meist selbst für Minimalisten denn doch zu - nun -> minimalistisch. Beide haben aber das Problem, dass sie nicht über einen> UART, sondern nur über dieses USI verfügen, da hätten Sie also eine> weitere Baustelle - etwas viel für den Anfang.
Guten Morgen auch,
ugh... also doch der 20mhz Quarz... es ist ja am Midi
das Schoene, dass mans einfach direkt an den UART RX klemmen
kann... und ich weiss nicht, wie kompliziert es ist UART rx
auf USI umzumodeln oder wie gut Soft-UART geht...
Danke jedenfalls fuer die Info und einen schoenen Tag
S. Landolt schrieb:> Aber warum sollte das Störungen> verursachen, das (gefilterte) Analogsignal geht eben nicht vollständig> auf 0 herunter.
Das sehe ich auch so. Nadelpulse bei jedem Zählerdurchgang bringen halt
einen DC-Anteil nach dem Filter undzwar einen minimalen, sonst wären es
keine Nadeln! Hörbar sollten die so oder so nicht sein und Jitter ist
natürlich etwas ganz Anderes. Die schwierige Frage ist, was im
Ausgangssignal entspricht dem, was der TO hört - um es mal so
auszudrücken. Vielleicht könnte man das Signal gezielt, also gewollt,
verformen, um die hörbaren Auswirkungen näher zu bestimmen. Auch wenn es
in der Hörfähigkeit der Generationen sicher erhebliche Unterschiede
gibt, sollte z.B. einen Jitter auch ein älterer Mensch hören können.
Altersbedingt verschlechtert sich hauptsächlich die Höhenwahrnehmung,
was für die Sprache bedeutet, dass die Zischlaute, die scharfen Laute
usw. verwischen, verschleifen...alles Effekte, die hier kaum eine Rolle
spielen dürften.
Bin gespannt auf Weiteres.
Gruß Rainer
Hallo,
Nadelimpulse, okay, ich wußte nicht ob und welche Filter ihr drin habt.
Habe das mal aufgebaut, allerdings bekomme ich 7,5kHz statt 440Hz. Muss
an der Eingangsformel etwas korrigiert werden? Mein Board hat 16 statt
8MHz.
Trotz niedrigen Tiefpass 2,2k + 100nF bleibts eher Dreieck statt Sinus.
Dafür ist der Pegel gleich passend für den NF Eingang.
Dann habe ich den CPU Takt per Software halbiert und erhalte 1,4kHz.
Sind immer noch keine 440Hz. :-) Das sieht allerdings wie ein richtig
guter Sinus aus. Ich staune.
Aus dem Lautsprecher höre ich jeweils einen konstanten Ton. Auch wenn
man sich den nicht lange antun kann.
Robert schrieb:> Ja das Ding ist nur, dass C einem etwas vorgaukelt, was unter Umstaenden> nicht da ist. Datenblaetter sind fuer mich nichts, was ich von> vorne bis hinten durchlese und danach denke 'Alles klar, Operation> wird ausgefuehrt', ich bin ja keine Maschine, sondern ein maessig> intelligenter Mensch.> Die Information 'avr kann hardwaremaessig nicht dividieren' ist in> asm sehr ersichtlich, steht irgendwo im 270 Seiten Datenblatt und> wird einem in avr-C erstmal nicht unter die Nase gehalten.
Will ja nichts sagen, aber stellst du dir damit nicht eigene Hürden auf
die es gar nicht gibt und lenkst unfreiwillig vom Thema ab? Wie möchtest
du in Assembler dividieren wenn es eigentlich nicht geht? Deine Logik
dazu ist mir zu konfus.
Ich sehe aktuell nur Divisionen für Vorabberechnungen. Die
Timerberechnungen sind nur Addition und Bit schubsen. Und Datenblätter
musst du trotzdem lesen, auch für Assembler. Du musst ja schließlich
Register richtig behandeln und beschreiben.
Ich meine wenn du nicht C/C++ programmieren möchtest okay, ist mir
vollkommen egal. Wenn die lieber Assembler magst, okay, ist mir völlig
egal. Lebt alles friedlich parallel nebeneinander. Aber dann schreibe
bitte nicht solchen Unsinn über C/C++. Bleibe lieber bei Thema DDS. Das
musste ich einfach mal loswerden bevor mir das zu bunt wird.
Hallo Veit Devil,
jetzt bin ich es, der schmunzelt: dieser "S.Landolt 08.03.2021"
schreibt in Ihrem Programm
1
A4_Phasendelta{(uint16_t)(65536UL*PWM/TonA4)}
während ich als "SLdt 210308" schreibe
1
A4_Phasendelta738// (A4/PWM*65536)
> Assembler dividieren
Da bezog sich Robert wohl auf seine Divisionen in seinem
Ursprungsprogramm, deren zeitfressende Eigenschaft in C nicht so
offensichtlich ist.
Danke für den Hinweis. Jetzt habe ich auch 440Hz. Alles prima. :-)
Und wegen Assembler Division nochmal. Will euch damit aber nicht weiter
belasten. Wie man das nun auch dreht und wendet. Ich meine Assembler
kann doch dabei auch nicht zaubern. Wenn ich dividieren muss, dann muss
ich dividieren. Egal welche Sprache. Ich kann mir nur vorher Gedanken
machen ob ich Rundungsfehler in irgendeiner Art und Weise abmildern
kann. Das ist ja nun einmal unabhängig jeder Sprache.
> Wenn ich dividieren muss, dann muss ich dividieren.
Korrekt. Aber wenn die Division nicht ganz so einfach verfügbar ist,
überlege ich mir vielleicht etwas länger, ob ich sie wirklich benötige
oder ob es nicht doch eine Möglichkeit gibt, sie zu vermeiden.
Da gibt es im täglichen Leben viele Analogien, vom Brötchenholen beim
angesagten Bäcker, 7 km entfernt, bis zum Zugunglück (Ritzau: Kriterien
der Schiene, Fall Guntershausen).
S. Landolt schrieb:> also wenn, dann würde ich eher zum ATTiny861 raten, der 85er mit seinen> 8 Pins ist meist selbst für Minimalisten denn doch zu - nun -> minimalistisch.
Nunja, für MIDI-In->Audio-Out hätte der 85 ja bereits mehr als genug
Pins. Sogar noch Reserven, man muss ja nicht unbedingt beide Timerkanäle
verwenden, der PWM-Treiber kann auch nur einen Kanal benutzen. Schon
sind zwei Pins mehr frei.
Für den 861 müsste man auch erheblich umprogrammieren, allerdings wäre
der PWM-Treiber sehr viel einfacher, weil der Timer des 861 von Hause
aus 10 Bit kann. Dadurch würde auch die Interruptlast erheblich sinken.
Also, spätestens, wenn aus irgendwelchen Gründen noch für andere Sachen
Pins gebraucht werden, dann würde ich auf jeden Fall auch den 861
vorziehen.
> Beide haben aber das Problem, dass sie nicht über einen> UART, sondern nur über dieses USI verfügen, da hätten Sie also eine> weitere Baustelle - etwas viel für den Anfang.
Ja, das ist wahr, da ist der 2313 deutlich im Vorteil. Ideal wäre für
die Anwendung einer der neuzeitlichen Tinys mit Typ-D-Timer und USART.
Da hätte man beide Sachen in einem Teil.
Veit D. schrieb:> Und wegen Assembler Division nochmal. Will euch damit aber nicht weiter> belasten. Wie man das nun auch dreht und wendet. Ich meine Assembler> kann doch dabei auch nicht zaubern.
Natürlich nicht. Deswegen ist der Haupttrick, richtige Divisionen zur
Laufzeit zu vermeiden.
> Wenn ich dividieren muss, dann muss> ich dividieren.
Der Witz ist ja gerade, dass man sehr oft zur Laufzeit garnicht wirklich
dividieren muss, wenn man mal genauer über das konkrete Problem
nachdenkt. Asm-Programmierer haben es förmlich im Blut, darüber
nachzudenken, weil sie die Kosten der Division genau kennen...
Da C die Kosten versteckt, ist diese tief verwurzelte Instinkt halt bei
C-lern nicht derart stark ausgeprägt...
Hallo,
okay ihr Beiden, soweit so gut. :-)
S. Landolt schrieb:> Heißt das nun, dass auch Sie etwelche Störungen weder hören noch sehen?
Genau. Weder sehe ich Störungen noch höre ich Störungen.
Haut mal 22nF Kondensatoren rein,
wenn es mein RC-Filter war, dann lag es daran.
Und dann ein AS4...
(ich find es ja urig, dass wir weiter nach dem Jitter
suchen, der bei mir schon raus ist).
Eigentlich bin ich euch da eigentlich auch Bilder schuldig
und ne Tonaufnahme, aber jetzt muss ich erstmal in einen
Arbeitsmarathon durchstarten...
Wenns am Wochenende keiner reproduziert hat,
dann mach ich mal Tonaufnahme + Bilder fuer die Reihe
'Anfaengerfehler reproduzieren'.
c-hater schrieb:> Der Witz ist ja gerade, dass man sehr oft zur Laufzeit garnicht wirklich> dividieren muss, wenn man mal genauer über das konkrete Problem> nachdenkt. Asm-Programmierer haben es förmlich im Blut, darüber> nachzudenken, weil sie die Kosten der Division genau kennen...>> Da C die Kosten versteckt, ist diese tief verwurzelte Instinkt halt bei> C-lern nicht derart stark ausgeprägt...
Grundsaetzlich denke ich auch so. Ich haette in meinem Fall gar nicht
dividieren muessen. Da ich es konnte, hab ich es aber einfach gemacht
ala "Grundrechenoperation ist Grundrechenoperation".
Veit D. schrieb:> Ich meine wenn du nicht C/C++ programmieren möchtest okay, ist mir> vollkommen egal. Wenn die lieber Assembler magst, okay, ist mir völlig> egal. Lebt alles friedlich parallel nebeneinander.
Ich will gar nicht C fertig machen, ich kann auch gar nicht Assembler
lieber moegen, ich kenns ja noch kaum.
Meine versoehnlichen Gedanken zu dem Thema waeren naemlich statt
"wenn ich dividieren muss, dividiere ich" ein "wenn ich weiss was
ich tue, weiss ich was ich tue".
Und man kann sowohl in C wissen was man tut, als auch in asm.
Am Ende des Tages habe ich mich darueber geaergert, dass ich
Dinge nicht wusste. Da kann keine Programmiersprache was fuer.
lg Robert
> Haut mal 22nF Kondensatoren rein
Bei welchem Widerstand?
Oder umgekehrt: versuchen Sie mal, mit Ihrem Material auf eine
Zeitkonstante von 200 us zu kommen.
Veit D. schrieb:> okay ihr Beiden, soweit so gut. :-)
Das geht übrigens auf einem Classic-Tiny sogar noch weiter, es betrifft
dort auch die Multiplikation, denn das können die ebenfalls nicht in
Hardware. Auch hier versteckt C die relativ hohen Kosten...
In Asm mach man dann halt sowas, speziell auf die Anwendung
zugeschnittenes wie mein MULSU10x8-Macro.
Du möchtest nicht wissen, wieviele Takte mehr bei einer stupiden
C-Umsetzung mit uint8_t_Variable * int16_t_Variable herauskommen...
Das ist der blanke Horror! Und es zeigt überdeutlich, warum Asm auch in
dieser Hinsicht so massiv Vorteil ist...
Hallo,
@ Robert:
gut, dann sagen wir es war unglücklich ausgedrückt und ein
Missverständnis. Alles gut. :-) Abgehakt.
@ c-hater:
Ja, in ASM kann man hier und da noch paar Takte rausholen. Sehe ich ja
alles ein.
Hallo Robert,
die alte Sinustabelle hatte, pardon, eine ganz leichte Asymmetrie,
versuchen mal diese hier, vielleicht klingt sie in Ihren Ohren besser
(in meinen nicht, das "alte" Thema).
Hallo S.Landolt,
hast du interesse am Code für unseren AVR128DB48? Ist aber C++. Da es
aber nur aus Registerkonfigurationen besteht, sollte das lesbar sein.
Habe das mal für den Timer TCD0 mit PLL 48MHz zum laufen gebracht.
Kleiner Haken an der Sache. PLL mit onboard Crystal Osci funktioniert
nicht, steht auch im Errata. Mit internen Oscillator funktioniert es.
Ein kleinen Schönheitsfehler gibts noch. Bei ca. 45° vom Sinus gibts
eine kleine Zacke die den Sinus leicht wobbeln veschiebt und ich weiß
nicht warum. Ansonsten ist das ein Sinus mit der eingestellten Frequenz.
Ich weiß nun nicht ob noch irgendeine Anpassung an die 12 Bit für TCD
gemacht werden muss. Ich denke eigentlich nicht, weiß es aber nicht.
Wenn du möchtest, räume ich den Code auf und schaufel alle Funktionen
aus Libs in den Hauptcode.
Schuster, bleib bei deinem Leisten, oder: Landolt, bleib bei Assembler.
Andererseits - interessieren würde mich Ihr Programm schon, trotz C++;
wenn es also nicht zuviel Mühe macht, nur her damit.
Haben Sie vielleicht sogar eine Version mit dem internen DAC? Hat zwar
leider nur 10 bit, sollte aber im Endeffekt mindestens so gut sein wie
die Pulsbreitenmodulation mit 48 MHz.
Hallo,
mit dem DAC habe ich noch nichts gemacht. Aber wer weiß. :-)
Wenn du deinen obigen C Code schreiben konntest, dann kannste den auch
lesen. Da bin ich mir sicher. :-) Gut, dann fang ich mal an mit hübsch
machen ... obs heute noch wird kann ich nicht versprechen. Ich machs dir
so leicht wie möglich. :-)
S. Landolt schrieb:> Haben Sie vielleicht sogar eine Version mit dem internen DAC? Hat zwar> leider nur 10 bit, sollte aber im Endeffekt mindestens so gut sein wie> die Pulsbreitenmodulation mit 48 MHz.
Da würde ich nicht drauf wetten...
> mit dem DAC habe ich noch nichts gemacht
Das geht erstaunlich einfach (wie schon sein 'Register Summary' vermuten
lässt). Man darf nur nicht so wie ich eine halbe Ewigkeit benötigen um
zu erkennen, dass die Daten linksbündig verlangt werden.
S. Landolt schrieb:> Das geht erstaunlich einfach (wie schon sein 'Register Summary' vermuten> lässt). Man darf nur nicht so wie ich eine halbe Ewigkeit benötigen um> zu erkennen, dass die Daten linksbündig verlangt werden.
Naja, Fehler passieren.
Aber egal, hast du mal vermessen, was dieser DAC (bei korrekter
Ansteuerung) produziert? Ich habe (an zehn Exemplaren AVR128DA28 im
DIL-Gehäuse aus einem Reel).
Das ist nicht schick, wirklich nicht. Die Linearität des Wandlers läßt
allgemein deutlich zu wünschen übrig und streut obendrein über die
Exemplare recht erheblich.
PWM via Typ-D-Timer liefert weitaus bessere Ergebnisse.
> hast du mal vermessen ...
Ja, an einem AVR128DB28, sah ganz gut aus, wie ich mich erinnere. Aber
nicht so gründlich wie Sie.
> PWM via Typ-D-Timer liefert weitaus bessere Ergebnisse
Hier ist der Höreindruck (und um diesen geht es Robert) stark abhängig
von der Filterung.
Aber wie ich schon weiter oben schrieb, sind meine Kenntnisse in diesem
Bereich nur mittelmäßig.
Hallo,
vorweg, darf natürlich jeder anschauen und testen wer möchte. :-)
Von C++ ist nichts mehr zu sehen. Ich hoffe das ist leserlich und
selbsterklärend für nicht C Programmierer. Habe überall die Standard
Bitnamen verwendet.
Die Umschaltung zwischen PLL und onboard Quarz erfolgt durch
aus/einkommentieren von Zeile 14.
Grundsätzlich funktioniert das.
Auf dem Oszi sehe ich jedoch eine Zacke bei ca. 45°, die sich bei
genauer Betrachtung am Oszi zeitlich verschiebt. Wenn ich die
Triggerspannung absenke, dann wackelt die obere Welle von Sinus. Ich
habe aktuell noch keine Idee warum. Auf einem Mega2560 funktioniert der
Code.
Sieht wer einen Schnitzer?
Im Anhang .zip als gesamtes AS7 Projekt und main einzeln für
Vorabeinsicht etc.
Hallo,
gern geschehen. Irgendwann mach ich was mit dem DAC. :-)
Wegen der Tabellenberechnung. Habe mich in DDS eingelesen. Zum erstellen
der Tabelle berechnet man den Sinus Einheitskreis und skaliert diesen
entsprechend auf die gewünschte Auflösung? Richtig?
Ich lasse in Excel das Sinus Bogenmaß berechnen, multipliziere mit 127
und hebe es auch mit 127 an, sodass alle Werte zum Bsp. zwischen 0 ...
255 liegen. Die Werte sehen plausibel aus. Oder gibts noch etwas
zubeachten? DDS ist ein cooles Thema. :-)
Zusatzfrage. Ist der Timertyp D für Motorsteuerungen gedacht?
> Die Werte sehen plausibel aus
Dann wird's wohl stimmen. Ich lasse die Tabelle durch meinen
Taschenrechner erstellen, da sieht der Rechenweg etwas anders aus. Und
ist natürlich abhängig vor der gewünschten Zeit- und
Amplitudenauflösung.
> Timertyp D für Motorsteuerungen
Nehme ich an, kenne mich aber nicht aus; wohl auch für
Brückenansteuerung. Bin ohnehin mit den vielfältigen Möglichkeiten des
TCD etwas überfordert, bekäme, wenn ich sie nicht schon hätte, graue
Haare nur beim Anblick des 'Register Summary'.
Jetzt hoffe ich allerdings, dass Robert mit dem ATtiny2313 demnächst
zurückkehrt, um den Thread hier bald abschließen zu können.
> Irgendwann mach ich was mit dem DAC.
Hey, das ist jetzt der richtige Zeitpunkt: PWM und DAC parallel laufen
lassen und direkt mit dem Oszilloskop vergleichen.
(der DAC ist ein Kinderspiel verglichen mit dem TCD)
Hallo,
ja die neuen Timer Typen sind komplett anders als ich (wir) bis jetzt
von der MCU Klasse gewohnt sind. Aber genau das macht es interessant da
durchzusteigen. :-)
Gut, ich teste noch etwas rum, dann widme ich mich dem DAC.
Danke soweit.
S. Landolt schrieb:> Datenblatt:> – One 12-bit PWM Timer/Counter type D (TCD) optimized for power control> • Waveform Generation> • Half-Bridge and Full-Bridge Output Support
Das war die Intention. Für Audio-Out funktionieren sie aber auch sehr
gut. ;o)
Nur braucht man dafür einige Features des Timers einfach nicht, die bei
Power-Anwendungen durchaus Sinn ergeben. Nun: niemand zwingt einen,
diese zu benutzen. Also soweit kein Problem.
Nervig ist allerdings teilweise die Benamsung der Register.
Insbesondere: Die Aktivierung der Ausgänge in ein Register namens
"FAULTCTRL" zu legen, ist schon etwas krass. Da wackelt der Schwanz ein
wenig mit dem Hund...
Hallo,
ich habe nun den gefilterten TCD Sinus (2,7k + 100nF) mit dem
ungefilterten DAC Sinus verglichen. Ich kann keinen Unterschied
feststellen. (Steckbrettaufbau vom Filter und Freiluftverdrahtung)
Das Einzigste was ich dabei feststellen musste ist, dass VREF_VDD ein
Problem mit der Stabilisierung hat. Selbst wenn man das delay auf 100µs
verdoppelt. Hin und wieder hat man nach Reset nur 1,024V. Wählt man
alternativ 4,096V klappt das stabil.
Der Vorteil vom DAC ist, man kann die Amplitudenhöhe direkt
beeinflussen, weil man nicht vom nachgeschalteten Tiefpass abhängig ist.
Außer man möchte das DAC Signal unbedingt nochmal filtern. Nur laut
meinem Oszi sehe ich da keinen Grund.
gelb: TCD
blau: DAC
> Amplitudenhöhe direkt beeinflussen ... Tiefpass abhängig
?
Liegt vermutlich an
> (2,7k + 100nF)
Eine Zeitkonstante von 270 us - kam zugegeben von mir, ist aber zu groß:
eine 3 dB-Grenzfrequenz von 590 Hz ist (selbst für Robert mit seinen 39
kHz Abtastfrequenz) deutlich zu niedrig. Deshalb sieht Ihr PWM-Sinus
auch so phantastisch aus, so ganz ohne PWM-Reste. Versuchen Sie einmal
15 nF (40 us), Sie haben ja nach oben genug Luft mit Ihren 187.5 kHz.
Das ist nun leider nicht sehr fachmännisch erklärt&gerechnet - wo
bleiben denn die Spezialisten für diesen Bereich?
> Außer man möchte das DAC Signal unbedingt nochmal filtern
Versuchen Sie das ruhig mal - ohne relativ kleinen pull-down am
DAC-Ausgang werden Sie Ihr "blaues" Wunder erleben mit dem AVR128DB28.
Hallo,
wir wollten doch den DAC vergleichen, denke ich. Und wenn der DAC schon
ohne Filter so fantastisch aussieht, wirds wohl mit Filter kaum besser
werden. Zudem der DAC noch einen Impedanzwandler benötigt wenn ein RC
dranhängt, sonst sackt der komplett ein. Hatte ich schon kurz
angetestet.
Also mit höherer RC Filter Grenzfrequenz für die TCD Methode, gewinnt
der DAC immer mehr. So würde ich das aktuell zusammenfassen.
> wir wollten doch den DAC vergleichen
Richtig. Nur war mir dies
> der DAC noch einen Impedanzwandler benötigt
im ersten Moment nicht klar, zumal sich das Datenblatt an dieser Stelle
vornehm zurückhält.
> Impedanzwandler
Den integrierten OPAMP testen?
> gewinnt der DAC immer mehr
Nun ja, ganz sicher bin ich mir da nicht, das Oszilloskop zeigt mehr als
wir hören können - vielleicht will ja c-hater noch etwas dazu sagen.
Auf jeden Fall fand ich es einen interessanten Exkurs, danke für den
Dialog.
Ah,
wie ich sehe gibts meinen Thread noch,
von mir war ja laenger nix zu hoeren.
Ich bin in eine Hoehle abgestiegen und habe mir ein wenig
Assembler zu Gemuete gefuehrt und mein C Programm in
Assembler nachprogrammiert.
Das funktioniert auch schon halbwegs... also es wird jeweils
je nach Tastendruck die richtige 8bit Dreieckswelle
(wie ich es in meiner LUT angegeben habe) ausgegeben.
Beim Loslassen der Tasten hat es aktuell noch nicht das richtige
Verhalten. Ich moechte, dass die PWM erst aufhoeren soll,
wenn die zuletzt gedrueckte Taste losgelassen wird.
Aktuell hoert die PWM bei jeder losgelassenen Taste auf.
Aber ich muss mich erstmal in asm einfuehlen, ich sitz
hier mit der Instruktionstabelle und pick mir immer das
raus, was jetzt wohl passen koennte, macht auch Spass,
aber es hakt noch an einigen Stellen.
Generell finde ich es aktuell ziemlich tricky mehrere Bytes (in meinem
Falle sind es immer 3) zu empfangen... das hat vor allem
weiter unten im Code zu bloeden Wiederholungen gefuehrt:
1
cpi rudr, 0x90
2
breq keyon
3
cpi rudr2, 0x90
4
breq keyon2
5
cpi rudr3, 0x90
6
breq keyon3
7
8
[...]
9
10
keyon: Das eine
11
keyon2: fast das gleiche
12
keyon3: und nochmal nur die reihenfolge vertauscht
Wahrscheinlich werd ich eher mal gucken den UDR Buffer in den
SRAM zu legen... ich ahne, dass mein Fehler darin liegt einen
zu kleinen Buffer zu haben, der nur aus Registern besteht...
Beim SRAM gibts auch praktische Inkrementierbefehle wie ich sehe...
Soweit von mir, nur ein kleiner Zwischenstand,
vielleicht hat ja jemand etwas praktische Kritik fuer mein
neues asm Programm ueber, ansonsten meld ich mich erst wieder,
wenns halbwegs fertig ist.
lg Robert
Robert schrieb:> Beim Loslassen der Tasten hat es aktuell noch nicht das richtige> Verhalten. Ich moechte, dass die PWM erst aufhoeren soll,> wenn die zuletzt gedrueckte Taste losgelassen wird.> Aktuell hoert die PWM bei jeder losgelassenen Taste auf.
Statemachine der einfachsten Form. Hier: nur ein Zähler, der bei KeyOn
hochzählt und bei KeyOff runter. Nur, wenn der Zähler null erreicht,
wird die PWM abgeschaltet.
> Aber ich muss mich erstmal in asm einfuehlen
Das ist so wie in jeder neuen Sprache. Die ersten Werke werden sehr weit
weg von einer optimalen Nutzung der Sprache sein. Für einen ersten
Versuch sieht das wirklich garnicht schlecht aus.
Aber leider: die ISR ist falsch. Konkret dies:
uartrx_isr3:
;nur bei flag 2
cpi rflag, 2
brne uartrx_isr
^^^^^^^^^^
Das bildet erstens eine Schleife (was eher selten im Sinne einer ISR
ist) und zweitens auch noch zu einer gefährlichen Stelle. Du zerstörst
in dem Moment, wenn sich diese Schleife schliesst, die geretteten Flags.
Und stellst dann irgendwann später bei Verlassen der ISR die zerstörten
Flags wieder her statt der ursprünglich geretteten.
> Generell finde ich es aktuell ziemlich tricky mehrere Bytes (in meinem> Falle sind es immer 3) zu empfangen
Die Frage ist für mich (keine Detailahnung vom MIDI-Protokoll): Warum
empfängst du überhaupt drei Bytes? Auswerten tust du sie jedenfalls alle
auf dieselbe Weise. Also würde es eigentlich genügen, immer nur ein Byte
zu empfangen, womit sich dann dieses Problem von alleine erledigt hätte:
>... das hat vor allem> weiter unten im Code zu bloeden Wiederholungen gefuehrt:
Hi,
ich bin auch schon wieder weitergekommen...
c-hater schrieb:> in dem Moment, wenn sich diese Schleife schliesst, die geretteten Flags.
Der Hinweis mit den Flags ist wertvoll, du hast recht!
c-hater schrieb:> Warum> empfängst du überhaupt drei Bytes
3 bytes sind immer genau eine Taste an oder eine Taste aus.
Wenn keine anderen Anweisungen dazwischenpfuschen wie midiclock
oder sysex-Zeug(was bei mir der Fall ist),
dann besteht ein Tastendruck aus. 0x90(Keyon, chan0), 0xKK (Taste), 0xVV
(lautstaerke)... von daher muss ich immer 3 bytes auswerten in der
richtigen Reihenfolge...
Ich habs jetzt tatsaechlich auch anders geloest mit dem SRAM
wie oben beschrieben.
Mein neuer Code im Anhang.
Der funktioniert jetzt ziemlich gut, ich muss mich nur noch
um den Uebertrag meines Buffers kuemmern... (es fehlt hin und
wieder eine Note aktuell).
c-hater schrieb:> Das ist so wie in jeder neuen Sprache. Die ersten Werke werden sehr weit> weg von einer optimalen Nutzung der Sprache sein. Für einen ersten> Versuch sieht das wirklich garnicht schlecht aus.
Danke, macht auch wirklich Spass auf dieser low-level Ebene
Register rumzuschieben... vor allem ist alles bis ins letzte
Transparent.
lg Robert
Hallo Robert,
da Sie nun tatsächlich in diese Tiefen hinabgestiegen sind, wollen Sie
nicht noch einen Schritt tiefer gehen und case-sensitive arbeiten? Zwar
weiß ich natürlich, dass selbst Microchip nichts ferner liegt, auch dass
c-hater, logisch, nichts davon hält, ich persönlich aber finde es nur
konsequent.
Gruß aus dem Schwarzwald
Hallo Herr Landolt,
ich werde mich bemuehen mehr darauf zu achten...
Mir ist auch erst im Laufe des Prozesses klargeworden,
dass case dem Assembler scheinbar egal ist,
das verfuehrt mich leider auch sehr da nicht so genau
zu sein.
Allerdings muss man natuerlich ein System ausmachen,
an welches man sich haelt...
Ich hab bisher versucht:
Konstanten gross,
Ports gross,
Register klein,
Instruktionen und Direktiven klein....
Ich muss mir mal andere Assemblerprogramme anschauen...
lg Robert
Also ich selbst arbeite mit dem avrasm2, und habe grundsätzlich die
Option '-c' eingeschaltet - was bei Ihrem Programm zu diversen
Fehlermeldungen führt. Kann aber durchaus verstehen, dass z.B. c-hater
eher eine gewisse Freizügigkeit schätzt.
Hm...
ich nutze aktuell avra und er meckert nicht,
er hat aber auch keine Casefunktion...
Vielleicht muss ich nochmal den Assembler wechseln,
ich will auch keinen Code produzieren, der nur bei mir
laeuft und ueberall kleine Casefehler hat...
lg Robert
> avra ... meckert nicht ... keine Casefunktion
Aha - nun gut, vielleicht sollte ich es dann auch etwas lockerer sehen.
Falls es Sie interessiert, avrasm2:
low, high, byte1 &c.
x y z
Letzteres steht so im 'AVR® Instruction Set Manual'; im Gegensatz zu ZH
ZL, dies ist so im jeweiligen _def.inc definiert.
Ok,
also sollte ich low(16bittigeszeug) verwenden
und ein ld r20, x+ einem ld r20, X+ bevorzugen richtig?
Dann kann ich aber auch gleich zl und zh statt ZL und ZH
schreiben... ich hab die Register ja eh klein eigentlich.
Vielleicht mach ich dann die Instruktionen alle gross...
lg Robert
clr r27 ; Clear X high byte ldi r26,$60 ; Set X low byte to $60
2
ld r0,X+ ; Load r0 with data space loc. $60(X post inc)
3
ld r1,X ; Load r1 with data space loc. $61
4
ldi r26,$63 ; Set X low byte to $63
5
ld r2,X ; Load r2 with data space loc. $63
6
ld r3,–X ; Load r3 with data space loc. $62(X pre dec)
In den Erklaerungen und den Tabellen darueber sind die
Instruktionen gross, die Register klein und X wiederrum gross...
So richtig einheitlich wirkt mir das alles nicht...
Schrieb ich doch:
> dass selbst Microchip nichts ferner liegt
Manchmal kann man sich ja ohnehin nicht des Eindrucks erwehren, dass die
Bearbeitung von Datenblättern bei Microchip/Atmel Praktikanten
überlassen wird.
Andererseits - wenn ich jetzt in ein steinaltes Datenblatt für den
AT90S8515 schaue, dann sind dort im 'Instruction Set Summary' die
Indexregister auch groß geschrieben; vielleicht ist die Kleinschreibung
eine Eigenart des avrasm(2). Wie dem auch sei - ich für mich bleibe
dabei, und bei Programmen von Ihnen nehme ich einfach die Option '-c'
heraus.
Okay, ich gebe mich endgültig geschlagen: 'Low' und 'LOW', aber mit '-c'
akzeptiert der hier beschriebene avrasm(2) ausschließlich 'low'.
(Aber: eine derartige Beliebigkeit geht mir denn doch gegen den Strich)
Gut...
ein schaebbig implementiertes Minimalmidi und 2 Toene,
die unabhaengig von einander gut klingen und zusammen
wie Darth Vaders Laserschwert...
Irgendwie ueberlagern sich die beiden DDS Wellen nicht so gut...
Meine erste Idee einfach ein paar Rechteckwellen an den Ports
zu erzeugen klang jedenfalls besser, aber egal.
Ich wuerde sagen mein Programm ist fertig, ich kann in
Konkurrenz gehen mit 2Euro Kinderkeyboards.
Falls jemand noch qualifizierte Kommentare hat, nur zu!
Danke soweit von mir,
lg Robert
Robert schrieb:> ein schaebbig implementiertes Minimalmidi und 2 Toene,> die unabhaengig von einander gut klingen und zusammen> wie Darth Vaders Laserschwert...
Ich hab's mir nicht angesehen, aber wenn jeder Ton für sich gut klingt,
die Summe aber beschissen, dann ist wohl nach den Gesetzen der Logik bei
der Summenbildung der Fehler. Vermutlich: numerischer Überlauf. Die
Summe von zwei Werten kann halt im Extremfall doppelt so groß sein, wie
jeder der Einzelwerte. Wenn schon die Einzelwerte den Bit-Umfang ihres
"Typs" voll ausnutzen, wird es knallen, wenn man für die Summe denselben
"Typ" benutzt.
Vielleicht schaust du mit diesem Hinweis selbst nochmal nach...
Hallo Robert,
so hübsch 'Endlicher Automat' gezeichnet ist: stimmt er?
Wenn ich die Summenbildung 'add rmp, rmp2' auskommentiere, so entsteht
ab dem zweiten Ton trotzdem noch diese merkwürdige Schwebung; das heißt
doch, dass der zweite Ton in den ersten hineinstört, was ja nicht sein
darf.
Und noch etwas, was Ihnen vermutlich nicht auffällt: gebe ich
nacheinander key-ons mit derselben Note, so ändert sich über mehrere
Stufen die Lautstärke und Tonqualität, hin&wieder ist im Oszillogramm
eine üble Zacke zu sehen; das darf ja auch nicht sein, auch wenn es in
der Praxis wohl nie auftritt.
Entschuldigen sie die Nachfrage,
aber koennen sie den Output von meinem
Programm etwa simulieren?
Schwebung beim 2. Ton... ich werd nochmal hingucken.
Ja ob mein endlicher Automat stimmt oder nicht...
das ist ne gute Frage. Ich hab ihn mir am Anfang
recht leicht vorgestellt und nach vielen Stunden
des Debuggens war ich doch etwas abgenervt.
lg Robert
c-hater schrieb:> Robert schrieb:>>> ein schaebbig implementiertes Minimalmidi und 2 Toene,>> die unabhaengig von einander gut klingen und zusammen>> wie Darth Vaders Laserschwert...>> Ich hab's mir nicht angesehen, aber wenn jeder Ton für sich gut klingt,> die Summe aber beschissen, dann ist wohl nach den Gesetzen der Logik bei> der Summenbildung der Fehler. Vermutlich: numerischer Überlauf. Die> Summe von zwei Werten kann halt im Extremfall doppelt so groß sein, wie> jeder der Einzelwerte. Wenn schon die Einzelwerte den Bit-Umfang ihres> "Typs" voll ausnutzen, wird es knallen, wenn man für die Summe denselben> "Typ" benutzt.>> Vielleicht schaust du mit diesem Hinweis selbst nochmal nach...
Das passiert an einer Stelle tatsaechlich auch...
Ich komm auf 256... deswegen war da diese Zacke nach unten im Oszi...
ich hab ja grundsaetzlich drauf geachtet :F
> Programm ... simulieren
?
Das läuft hier, wie bei Ihnen, auf einem ATtiny2313 mit 10 MHz.
Lediglich Ihr Keyboard muss ich - nun ja, sagen wir emulieren: ein
ATmega644P, der Buchstaben vom PC erhält und als MIDI-note-on/off
weitergibt.
> Automat
Der ist für mein Gefühl viel zu kompliziert (wie würde der bei zum
Beispiel acht Tönen aussehen?) - mag aber auch daran liegen, dass ich
ihn noch immer nicht so richtig verstanden habe.
Oh,
das ist dann aber sehr nett von Ihnen!
Ja also seitdem der numerische Ueberlauf weg ist
(in der Datentabelle die 128er Werte auf 127 verringert),
klingt das Ding schon viel besser.
Zu den Schwebungen: Ich habe tatsaechlich keine Schwebung
beim 2. Ton, wenn ich die besagte Zeile auskommentiere.
Ich habe nur grundsaetzlich starke Schwebungen bei gewissen
Frequenzen... z.B Taste 0x34, ein E4... das wackelt
auf meinem Oszi hin und her... ich bin ja wieder auf
ICR=256... auf ICR=128 wars besser...
Aber insgesamt bin ich schon recht zufrieden grade...
Ich kauf mir jetzt mal den 20mhz Quarz.
Ja, also der Automat ist sehr kompliziert, da geb ich
Ihnen recht... Ich bin jetzt nicht sonderlich geuebt darin
die Dinger zu machen...
Das Problem ist fuer mich ein bisschen, dass dieser endliche
Automat einfach unglaublich viele Eingabemoeglichkeiten hat...
88x Ein und 88x Aus und eigentlich muesste ich ja sonst fuer
jede Moeglichkeit einen eigenen state definieren.
> Automat
Ich hätte nur eine kleine state-machine in der UART-ISR, um die drei
MIDI-Bytes zuzuordnen. Dort werden dann die Tonkanäle
aktiviert/deaktiviert: Eintrag von Notennummer und Phasendelta. In der
Hauptschleife werden, ganz simpel, die aktiven Tonkanäle
weitergeschaltet (Phase akkumuliert) und schließlich summiert.
Tonkanäle: im SRAM, Anzahl nach Wunsch:
- 1 Byte: Notennummer (z.B. $FF für inaktiv)
- 2 Bytes: Phase
- 2 Bytes: Phasendelta
> jetzt nicht sonderlich geuebt
Das kommt schon noch, Geduld. Ihre Lernkurve ist ganz gut (wenn ich mir
die Bemerkung erlauben darf).
Hallo Robert,
"verworren" schon, aber Sie sagen es ja: so ist das nun einmal zu
Beginn, kein Grund, sich schlechter zu machen als man ist.
Und "für die Katz" - au contraire: Haben Sie im Verlauf nicht eine
Menge gelernt? Selbst ich konnte das eine oder andere mitnehmen (sonst
wäre ich nicht mehr dabei).
Gruß aus dem Schwarzwald
Natuerlich,
ich neige dazu meine Aussagen manchmal etwas negativ zu formulieren,
grundsaetzlich ist das ein Projekt an dem ich eine Menge lerne.
Es schwebt mir nur im Hinterkopf manchmal der Vergleich mit den
'ich lade mir da jetzt ne Libary rein' Leuten, bei denen Sachen
dann oft einfach sofort funktionieren, wohingegen ich gerne 2
Wochen an einem Sinuston rummache...
Also wenn ich diese ganze Midi-Automaten-Kommunikationsgeschichte
neu schreiben soll, dann macht es vielleicht doch Sinn jetzt mal
auf den Attiny85/Attiny861 umzusteigen... Der hat auch nen adc,
wo ich wiederrum weiteres Potential sehe meine Toene auch noch
zu modulieren...
Da waere dann USI ein Feature was ich verstehen muesste und
diese super schnelle 500khz pwm das andere.
liebe Gruesse aus dem platten Niedersachsenland,
Robert
> platten Niedersachsenland
?
"Dort wo die Kathedralen
wie Felsgebirge stehn
wo Kirchen schwarz und schweigend
in Himmel übergehn ..."
Ich würde erstmal beim 2313 bleiben, umsteigen, z.B. auf einen
ATtiny861, können Sie früh genug. Ist aber meine ganz persönliche
Meinung. Nur vom ATtiny85 würde ich abraten, in der frühen Lernphase
wird der schnell unangenehmer als ein zu eng geschnittener Anzug.
PS:
Der ATtiny861 bringt mit seiner 64 MHz-PLL natürlich klanglich ein
besseres Resultat (und damit vielleicht ein schöneres Erfolgserlebnis),
aber in der jetzigen Lernphase (DDS) sind seine Vorteile eher gering.
Hallo,
eine Frage zwischendurch. Ich habe von Tonerzeugung keine Ahnung. Ein
Musikton oder wie man das nennen soll ist ja ein Gemisch aus
verschiedenen Frequenzen. Bsp. ein BaseDrum oder ein Schlagzeug
'klopfen'. Wenn ich das richtig mitbekommen habe mischt man dafür
verschiedene Sinusfrequenzen zeitlich gleicher Länge. Und dann kommt
irgendein schöner Ton raus. Soweit korrekt? Nur woher weiß man aus
welchen Grundtönen man ein BaseDrum oder ein Schlagzeug zusammensetzen
muss? Also wie entstehen die Arraytabellen für richtige Musik?
Hmm...
Ich schaetze man nimmt ein Schlagzeug und dann sampled man das
einfach...
Ich koennte jetzt ja auch im Grunde jede Klaviertaste meines
Klaviers aufnehmen, dann ein bisschen rumfiltern und jedes Einzelsample
auf je eine der 88 Midikeyboardtasten legen.
Ansonsten ist die andere Methode experimenteller Natur:
Ein fetter (Soft-/Hardware)Synthesizer mit 123 Reglern und Knoepfen:
Irgendwann hat man seine Einstellung und dementsprechend einen
Klang, der einem gefaellt.
Ich hab jetzt mein merkwuerdiges Flimmern auf dem Oszi rausbekommen
uebrigens, indem ich einen 22nF Kondensator vom PWMport gegen
VCC gesteckt hab. Das hat saemtliche Stoerfrequenzen auch rausgefiltert.
Die Frage ist: Warum gegen VCC? Ich hab zig Kondensatoren
gegen GND versucht und die haben die Stoerfrequenzen nur noch schlimmer
gemacht...
dabei muessten nach meinem Verstaendnis kleine Kondensatoren gegen GND
einen wunderbaren Low-pass Filter ergeben, wo das ganze hochfrequente
Zeug,
was in meinen Sinuswellen vorher war, abgefuehrt wird. Stattdessen
klappt 22nF gegen VCC...
Auf jeden Fall freut es mich, dass mein Sinus immer sauberer wird.
Jetzt sind da nur noch auf manchen Frequenzen so ein paar richtig
fiese Obertoene.
lg Robert
So,
bevor meine Arbeitswoche losgeht, hier nochmal
die Idee von Herr Landolt implementiert mit 3 Keys gleichzeitig...
So gut liefs noch nie.
Zumindest wenn ich nicht allzu schnell spiele :D...
Es haengt sich grade relativ gerne ein Ton auf, d.h. er geht nicht
mehr aus, aber nur wenn ich mehrere Tasten spiele.
Wenn ich meine "weniger Tonklack beim ausmachen" Sache reinkommentiere,
wirds noch viel schlimmer.
Ich schaetze es ist irgendein Problem bei der statemachine.
Ich hab jetzt in alle states nochmal den test auf keyoff (0x80)
reingenommen, es ist besser geworden gefuehlt, aber nicht ganz weg.
Kann auch sein, dass mir ein anderer Midibefehl noch reinpfuscht,
ich hab glaube ich noch aftertouch (0xA0) an diesem Midikeyboard...
Ich muss mir das alles genauer anschauen
(meine typische Loesung waere eher irgendwo nen Watchdog hinzusetzen).
Ansonsten bin ich relativ erstaunt wie gut der neue Code mit
der 5Byte Geschichte im SRAM funktioniert... und ja, noch mehr Keys
adden ist jetzt kein Problem mehr...
Ziemlich gut das Ganze... fuer nen attiny2313!
Ich hab uebrigens nen 26,8mhz Quarz gefunden... also ueber
der Spezifikation des attiny2313... overclocked quasi.
Starten tut der attiny2313 aber... ich weiss jetzt nur nicht,
ob der Dauerbetrieb fuer ihn so gesund ist... oder ob er so
zuverlaessig ist.
Ich lass das boese Quarz erstmal weg und warte bis meine
20mhz Quarze da sind.
lg Robert
Ok,
Problem 1 war bei meinem Antiknackcode,
dass ich ein cpi rmp, 5 drin hatte,
wo rmp aber schon kaputtaddiert war.
Das andere Problem war im Prinzip, dass bei zu viel Daten meine
Midileitung zusammengebrochen ist...
Denn was ich euch verschwiegen habe ist, dass ich als
Optokoppler (ich wollte das Midikeyboard nicht direkt anschliessen,
ist nicht meins) zwei IR-LEDs benutzt habe und sie dann
mit 2 Transistoren (nicht ganz nach allen Regeln der Kunst vermutlich)
verstaerkt habe... das habe ich jetzt nochmal verbessert.
Was aber unweigerlich zu einem Problem bei Midi fuehrt:
Was tun, wenn eine Anweisung wie 0x80 mal verloren geht?
Dann spielt der Ton so vor sich hin, der Nutzer kann nicht nochmal
0x80 eingeben, weil 0x80 nur beim Loslassen der Taste gesendet
wird.
Gut, jetzt geht meine Arbeitswoche aber tatsaechlich los und
ich hoere auf den Thread zuzuspammen.
Es waren sehr viele tolle und hilfreiche Hinweise hier dabei,
ich danke euch allen!
lg Robert
Veit D. schrieb:> eine Frage zwischendurch. Ich habe von Tonerzeugung keine Ahnung. Ein> Musikton oder wie man das nennen soll ist ja ein Gemisch aus> verschiedenen Frequenzen. Bsp. ein BaseDrum oder ein Schlagzeug> 'klopfen'. Wenn ich das richtig mitbekommen habe mischt man dafür> verschiedene Sinusfrequenzen zeitlich gleicher Länge. Und dann kommt> irgendein schöner Ton raus. Soweit korrekt?
Nicht ganz, das ist nur die vereinfachte Variante der Sache. Für
realitätsnahe Klänge müssen diese einzelnen Frequenzen dann auch oft
noch über die Zeit in ihrer jeweiligen Lautstärke moduliert werden. Das
ist, was in meinem Code diese envelope-Geschichte tut.
> Nur woher weiß man aus> welchen Grundtönen man ein BaseDrum oder ein Schlagzeug zusammensetzen> muss? Also wie entstehen die Arraytabellen für richtige Musik?
Für meinen Code habe ich halt die Klänge am PC analysiert. Zuerst die
relevanten Sinusfrequenzen aus dem Frequenzbrei ermittelt, natürlich
mittels FFT.
Anfangs mit einem Tool namens Wavanal (gibt's im Internet), aber das war
mir dann nicht interaktiv genug, außerdem "endet" es auch bereits nach
diesem ersten Schritt. Deswegen habe ich mir selbst was geschrieben, was
diesen ersten Schritt etwas interaktiver macht (was insbesondere für die
Analyse der Attack-Phase recht nützlich ist). Das Ergebnis dieses ersten
Schritts ist jedenfalls das, was sich in meinem Code in den beiden
Macros NOTEFREQ und HARMONICFREQ manifestiert.
Der Rest ist dann die Gewinnung der Hüllkurven, also der Daten für die
Amplitudenmodulation der einzelnen Sinusfrequenzen. Da habe ich es mir
in zweierlei Hinsicht einfach gemacht. Erstens habe ich das nur noch für
BigBen getan (weil sowieso nur dafür der vollständige Verlauf in den
verfügbaren Quelldaten war) und zweitens habe ich auch wieder nur eine
FFT genutzt, nämlich die mit der bestmöglichen zeitlichen Auflösung, die
noch sicher stellt, dass die Frequenzen getrennt bleiben. Die Wahl
dieser FFT-Parameter findet sich letztlich in der Konstanten ENVRATE im
Code wieder.
Was da denn rauskommt wurde mittels Tiefpass gefiltert, um die
FFT-Artefakte loszuwerden (und um Speicherplatz zu sparen). Um das
wiederum durch den Tiefpass eingeführte Delay loszuwerden, habe ich
einfach die Möglichkeit genutzt, dass die Analyse nicht in Echtzeit
passiert. Dadurch ergibt sich die Möglichkeit, den Delay loszuwerden,
indem man denselben Tiefpass einfach nochmal durch die Daten rauschen
lässt, diesmal aber von hinten nach vorne.
Naja, und zuletzt habe ich die die Hüllkurvendaten mit Huffman
komprimiert, damit der Kram in den Flash des Tiny passt.