Forum: Mikrocontroller und Digitale Elektronik Attiny2313 Timer16 Bit, Normal Mode, unsauber


von Robert (Gast)


Lesenswert?

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
volatile unsigned char buffer[6] = { 0 };
23
volatile uint8_t pos_w = 0;
24
volatile uint8_t flagon = 0;
25
volatile float ton = 0;
26
volatile uint8_t hightick = 0;
27
volatile uint8_t lowtick = 0;
28
//volatile uint16_t ticks = 10000;
29
int main(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_t mp = 0;
43
  volatile uint8_t key = 0;
44
  sei(); // interrupts on
45
  while (1) { 
46
47
    //Anfang// Key off
48
    switch(flagon){
49
    case 0: 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
    case 1: 
59
      //Welcher key?
60
      mp = (key/12) - 3;
61
                       switch(key%12){
62
                         case 0: ton = C4 * (1 << mp); break;
63
                         case 1: ton = CIS4 *  (1 << mp); break;
64
                         case 2: ton = D4 *  (1 << mp); break;
65
                         case 3: ton = DIS4 *  (1 << mp); break;
66
                        case 4: ton = E4 * (1<<mp); break;
67
                        case 5: ton = F4 *  (1<<mp); break;
68
                        case 6: ton = FIS4 * (1<<mp); break;
69
                       case 7: ton = G4 * (1<<mp); break;
70
                       case 8: ton = GIS4 * (1<<mp); break;
71
                        case 9: ton = A4 * (1<<mp); break;
72
                        case 10: ton = AS4 * (1<<mp); break;
73
                       case 11: ton = H4 * (1<<mp); break;
74
                        }
75
      if(buffer[pos_w] == 0x90){
76
        key = buffer[pos_w+1];
77
      }
78
      //KEY OFF
79
80
      else if(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.

von K. S. (the_yrr)


Lesenswert?

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

: Bearbeitet durch User
von Veit D. (devil-elec)


Lesenswert?

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
const float noten[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
volatile uint32_t ton;
3
volatile uint16_t tick;

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.

von Wolfgang (Gast)


Lesenswert?

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.

von Robert (Gast)


Lesenswert?

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

von S. Landolt (Gast)


Lesenswert?

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?

von S. Landolt (Gast)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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.

von S. Landolt (Gast)


Lesenswert?

> CTC Mode funktioniert mit 2 Compare Matches nicht.
Und bei Modus 12?

von Sanges Knabe (Gast)


Lesenswert?

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.

von S. Landolt (Gast)


Lesenswert?

Da bin ich jetzt doch froh, dass sich ein Zweiter findet: einfache 
Frequenzerzeugung per PWM mit Tastgrad 50 %.

von Sanges Knabe (Gast)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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.

von Sanges Knabe (Gast)


Lesenswert?

Robert schrieb:
> OCR1AH += hightick;
> OCR1AL += lowtick;

Wo ist denn da der Übertrag nach OCR1AH wenn OCR1AL durch
Addition von <lowtick> überläuft?

von S. Landolt (Gast)


Lesenswert?

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.

von S. Landolt (Gast)


Lesenswert?

> Wo ist denn da ...
Vielleicht hier:
> Statt 440hz schwankt es so zwischen 440 und 444hz

von Veit D. (devil-elec)


Lesenswert?

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.

von S. Landolt (Gast)


Lesenswert?

> 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.

von S. Landolt (Gast)


Lesenswert?

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).

von Veit D. (devil-elec)


Lesenswert?

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 ...  :-)

von Robert (Gast)


Lesenswert?

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

von S. Landolt (Gast)


Lesenswert?

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.

von Robert (Gast)


Lesenswert?

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

von S. Landolt (Gast)


Lesenswert?

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.

von S. Landolt (Gast)


Lesenswert?

> 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.

von c-hater (Gast)


Lesenswert?

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...

von Robert (Gast)


Lesenswert?

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)
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 C7 4778
10
#define CIS7 4510
11
#define D7 4257
12
#define DIS7 4018
13
#define E7  3792
14
#define F7 3579
15
#define FIS7 3378
16
#define G7 3189
17
#define GIS7 3010
18
#define A7 2841
19
#define AS7 2681
20
#define H7 2531
21
22
23
24
volatile uint16_t cnt = 0;
25
volatile uint8_t buffer[3] = { 0 };
26
volatile uint8_t pos_w = 0;
27
volatile uint16_t ton[2] = { 0 };
28
volatile uint8_t key[2] = { 0 };
29
//volatile uint16_t tickA = 10000;
30
//volatile uint16_t tickB = 10000;
31
volatile uint16_t t_array[12] = {C7,CIS7,D7,DIS7,E7,F7,FIS7,G7,GIS7,A7,AS7,H7};
32
int main(void) {
33
  DDRB = (1 << DDB0) | (1<< DDB1) | (1<< DDB3);    
34
  UBRRH = (BRC >> 8); // BAUDRATE einstellen
35
  UBRRL =  BRC;
36
  UCSRB = (1 << RXEN) | (1 << RXCIE); // RX 
37
  UCSRC = (1 << UCSZ1) | (1 << UCSZ0); // asynchronous UART
38
39
  //TIMER stuff here
40
  TCCR1A = (1 << COM1A1) | (1 << WGM11);
41
  TCCR1B = (1 << CS10) | (1 << WGM12) | (1 << WGM13);
42
  TIMSK = (1 << TOIE1);
43
  ICR1 = 99;
44
  OCR1A = 0;
45
  sei(); // interrupts on
46
  while (1) { 
47
48
    //Minimalistmidi
49
      if(buffer[pos_w] == 0x90){
50
        key[0] = buffer[(pos_w+1)%3];
51
        buffer[pos_w] = 0;
52
        ton[0] = t_array[key[0]%12];
53
      }
54
      else if(buffer[pos_w] == 0x80){
55
        ton[0] = 0;
56
      }
57
58
59
//Tonerzeugung
60
    if(cnt < ton[0]){
61
      OCR1A = 0;
62
    }
63
    else if(cnt < (ton[0]*2)){
64
      OCR1A = 50;
65
    }
66
//ich schaetze es hakt hier...
67
    else{
68
      cnt = 0;
69
      OCR1A = 0;
70
    }  
71
  }
72
}
73
74
75
76
ISR(USART_RX_vect){
77
        buffer[pos_w] = UDR;
78
  pos_w++;
79
  if(pos_w > 2) pos_w = 0;
80
}
81
82
ISR(TIMER1_OVF_vect){
83
  cnt += 100;
84
}

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

von S. Landolt (Gast)


Angehängte Dateien:

Lesenswert?

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.

von S. Landolt (Gast)


Lesenswert?

PS:
> man muss einen Kompromiss finden ...
Das gilt nur hier bei AD-Wandlung per PWM, nicht bei einem 'richtigen' 
DAC.

von S. Landolt (Gast)


Lesenswert?

Autsch:
> AD-Wandlung
DA natürlich

von Robert (Gast)


Lesenswert?

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:
1
/*
2
3
DDS Teil von:
4
SLdt 210308   (Disclaimer: None. Sue me.)
5
ATtiny2313: DDS 440 Hz Sinus
6
7
*/
8
9
#include <avr/io.h>
10
#include <avr/interrupt.h>
11
#include <avr/pgmspace.h>
12
#define F_CPU 10000000
13
#define ICR 256
14
#define PWM F_CPU/ICR
15
#define A4 440
16
#define A4_Phasendelta 2684   // (A4/PWM*65536)
17
#define BAUD 31250
18
#define BRC ((F_CPU/16/BAUD)-1)
19
20
//Noten Phasendelta
21
#define C4p 439
22
#define CIS4p 465
23
#define D4p 493
24
#define DIS4p 522
25
#define E4p 553
26
#define F4p 586
27
#define FIS4p 621
28
#define G4p 658
29
#define GIS4p 697
30
#define A4p 738
31
#define AS4p 782
32
#define H4p 829
33
34
35
volatile uint16_t cnt = 0;
36
volatile uint8_t buffer[3] = { 0 };
37
volatile uint8_t pos_w = 0;
38
volatile uint16_t ton[2] = { 0 };
39
volatile uint8_t key[2] = { 0 };
40
volatile uint16_t t_array[12] = {C4p,CIS4p,D4p,DIS4p,E4p,F4p,FIS4p,G4p,GIS4p,A4p,AS4p,H4p};
41
uint16_t Phase = 13284;
42
const int8_t Sinus[256] PROGMEM = { 
43
44
   128,  131,  134,  137,  141,  144,  147,  150,  153,  156,  159,  162,  165,  168,  171,  174,
45
46
   177,  180,  183,  186,  188,  191,  194,  196,  199,  202,  204,  207,  209,  212,  214,  216,
47
48
   219,  221,  223,  225,  227,  229,  231,  233,  234,  236,  238,  239,  241,  242,  244,  245,
49
50
   246,  247,  249,  250,  250,  251,  252,  253,  254,  254,  255,  255,  255,  255,  255,  255,
51
52
   255,  255,  255,  255,  255,  255,  255,  254,  254,  253,  252,  251,  250,  250,  249,  247,
53
54
   246,  245,  244,  242,  241,  239,  238,  236,  234,  233,  231,  229,  227,  225,  223,  221,
55
56
   219,  216,  214,  212,  209,  207,  204,  202,  199,  196,  194,  191,  188,  186,  183,  180,
57
58
   177,  174,  171,  168,  165,  162,  159,  156,  153,  150,  147,  144,  141,  137,  134,  131,
59
60
   128,  125,  122,  119,  115,  112,  109,  106,  103,  100,   97,   94,   91,   88,   85,   82,
61
62
    79,   76,   73,   70,   68,   65,   62,   60,   57,   54,   52,   49,   47,   44,   42,   40,
63
64
    37,   35,   33,   31,   29,   27,   25,   23,   22,   20,   18,   17,   15,   14,   12,   11,
65
66
    10,    9,    7,    6,    6,    5,    4,    3,    2,    2,    1,    1,    1,    0,    0,    0,
67
68
     0,    0,    0,    0,    1,    1,    1,    2,    2,    3,    4,    5,    6,    6,    7,    9,
69
70
    10,   11,   12,   14,   15,   17,   18,   20,   22,   23,   25,   27,   29,   31,   33,   35,
71
72
    37,   40,   42,   44,   47,   49,   52,   54,   57,   60,   62,   65,   68,   70,   73,   76,
73
74
    79,   82,   85,   88,   91,   94,   97,  100,  103,  106,  109,  112,  115,  119,  122,  125};
75
76
77
int main(void) {
78
79
  DDRB = (1<< DDB3);    
80
81
        UBRRH = (BRC >> 8); // BAUDRATE einstellen
82
        UBRRL =  BRC;
83
        UCSRB = (1 << RXEN) | (1 << RXCIE); // RX 
84
        UCSRC = (1 << UCSZ1) | (1 << UCSZ0); // asynchronous UART
85
  sei();
86
87
  ICR1 = ICR -1;
88
  TCCR1A = (1<<COM1A1)+(1<<COM1A0)+(1<<WGM11);
89
  TCCR1B = (1<<WGM13)+(1<<WGM12)+(1<<CS10);
90
91
  while (1) { 
92
93
    if(buffer[pos_w] == 0x90){
94
      key[0] = buffer[(pos_w+1)%3];
95
      buffer[pos_w] = 0;
96
      ton[0] = t_array[key[0]%12];
97
    }
98
    else if(buffer[pos_w] == 0x80){
99
      Phase = 13284; // :F Sinus[51]
100
      ton[0] = 0;
101
    }
102
103
104
  Phase += ton[0];
105
  OCR1A = pgm_read_byte(&(Sinus[Phase>>8]));
106
  //OCR1A = pgm_read_byte(&(Sinus[193]));
107
  while (!(TIFR & (1<<TOV1)));
108
  TIFR |= (1<<TOV1);
109
110
  }
111
112
}
113
114
ISR(USART_RX_vect){
115
        buffer[pos_w] = UDR;
116
        pos_w++;
117
        if(pos_w > 2) pos_w = 0;
118
}

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):
1
/*
2
3
SLdt 210308   (Disclaimer: None. Sue me.)
4
5
ATtiny2313: DDS 440 Hz Sinus
6
7
*/
8
9
#include <avr/io.h>
10
11
#include <avr/pgmspace.h>
12
13
#define F_CPU 10000000
14
15
#define ICR 256
16
17
#define PWM F_CPU/ICR
18
19
#define A4 440
20
21
#define A4_Phasendelta 782   // (AS4/PWM*65536)
22
23
uint16_t Phase = 0;
24
25
const int8_t Sinus[256] PROGMEM = { 
26
27
   128,  131,  134,  137,  141,  144,  147,  150,  153,  156,  159,  162,  165,  168,  171,  174,
28
29
   177,  180,  183,  186,  188,  191,  194,  196,  199,  202,  204,  207,  209,  212,  214,  216,
30
31
   219,  221,  223,  225,  227,  229,  231,  233,  234,  236,  238,  239,  241,  242,  244,  245,
32
33
   246,  247,  249,  250,  250,  251,  252,  253,  254,  254,  255,  255,  255,  255,  255,  255,
34
35
   255,  255,  255,  255,  255,  255,  255,  254,  254,  253,  252,  251,  250,  250,  249,  247,
36
37
   246,  245,  244,  242,  241,  239,  238,  236,  234,  233,  231,  229,  227,  225,  223,  221,
38
39
   219,  216,  214,  212,  209,  207,  204,  202,  199,  196,  194,  191,  188,  186,  183,  180,
40
41
   177,  174,  171,  168,  165,  162,  159,  156,  153,  150,  147,  144,  141,  137,  134,  131,
42
43
   128,  125,  122,  119,  115,  112,  109,  106,  103,  100,   97,   94,   91,   88,   85,   82,
44
45
    79,   76,   73,   70,   68,   65,   62,   60,   57,   54,   52,   49,   47,   44,   42,   40,
46
47
    37,   35,   33,   31,   29,   27,   25,   23,   22,   20,   18,   17,   15,   14,   12,   11,
48
49
    10,    9,    7,    6,    6,    5,    4,    3,    2,    2,    1,    1,    1,    0,    0,    0,
50
51
     0,    0,    0,    0,    1,    1,    1,    2,    2,    3,    4,    5,    6,    6,    7,    9,
52
53
    10,   11,   12,   14,   15,   17,   18,   20,   22,   23,   25,   27,   29,   31,   33,   35,
54
55
    37,   40,   42,   44,   47,   49,   52,   54,   57,   60,   62,   65,   68,   70,   73,   76,
56
57
    79,   82,   85,   88,   91,   94,   97,  100,  103,  106,  109,  112,  115,  119,  122,  125};
58
59
/*
60
61
     0,    3,    6,    9,   13,   16,   19,   22,   25,   28,   31,   34,   37,   40,   43,   46,
62
63
    49,   52,   55,   58,   60,   63,   66,   68,   71,   74,   76,   79,   81,   84,   86,   88,
64
65
    91,   93,   95,   97,   99,  101,  103,  105,  106,  108,  110,  111,  113,  114,  116,  117,
66
67
   118,  119,  121,  122,  122,  123,  124,  125,  126,  126,  127,  127,  127,  127,  127,  127,
68
69
   127,  127,  127,  127,  127,  127,  127,  126,  126,  125,  124,  123,  122,  122,  121,  119,
70
71
   118,  117,  116,  114,  113,  111,  110,  108,  106,  105,  103,  101,   99,   97,   95,   93,
72
73
    91,   88,   86,   84,   81,   79,   76,   74,   71,   68,   66,   63,   60,   58,   55,   52,
74
75
    49,   46,   43,   40,   37,   34,   31,   28,   25,   22,   19,   16,   13,    9,    6,    3,
76
77
     0,   -3,   -6,   -9,  -13,  -16,  -19,  -22,  -25,  -28,  -31,  -34,  -37,  -40,  -43,  -46,
78
79
   -49,  -52,  -55,  -58,  -60,  -63,  -66,  -68,  -71,  -74,  -76,  -79,  -81,  -84,  -86,  -88,
80
81
   -91,  -93,  -95,  -97,  -99, -101, -103, -105, -106, -108, -110, -111, -113, -114, -116, -117,
82
83
  -118, -119, -121, -122, -122, -123, -124, -125, -126, -126, -127, -127, -127, -128, -128, -128,
84
85
  -128, -128, -128, -128, -127, -127, -127, -126, -126, -125, -124, -123, -122, -122, -121, -119,
86
87
  -118, -117, -116, -114, -113, -111, -110, -108, -106, -105, -103, -101,  -99,  -97,  -95,  -93,
88
89
   -91,  -88,  -86,  -84,  -81,  -79,  -76,  -74,  -71,  -68,  -66,  -63,  -60,  -58,  -55,  -52,
90
91
   -49,  -46,  -43,  -40,  -37,  -34,  -31,  -28,  -25,  -22,  -19,  -16,  -13,   -9,   -6,   -3 };
92
93
*/
94
95
    
96
97
int main(void) {
98
99
DDRB = (1<< DDB3);    
100
101
ICR1 = ICR -1;
102
103
TCCR1A = (1<<COM1A1)+(1<<COM1A0)+(1<<WGM11);
104
105
TCCR1B = (1<<WGM13)+(1<<WGM12)+(1<<CS10);
106
107
while (1) { 
108
109
Phase += A4_Phasendelta;
110
111
OCR1A = pgm_read_byte(&(Sinus[Phase>>8]));
112
113
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.

von Robert (Gast)


Lesenswert?

Ok,

mit hoeherer Zeitaufloesung (~ISR1 ~128) "verschwindet" der Jitter...
Damit kann ich arbeiten...

Vielen Dank!

von S. Landolt (Gast)


Lesenswert?

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).

von Veit D. (devil-elec)


Lesenswert?

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
const uint16_t ICR = 256;
7
const uint16_t PWM = F_CPU/ICR;
8
const uint16_t A4 = 440;
9
10
uint16_t A4_Phasendelta = 782;   // (AS4/PWM*65536)
11
uint16_t Phase;

Wobei mir der Variablenname 'ICR' eine Spur zu nah an den defines liegt. 
:-)
Große Verwechslungsgefahr.

Wie auch immer, teste mal ...

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

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.

: Bearbeitet durch User
von S. Landolt (Gast)


Lesenswert?

> 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.

von Veit D. (devil-elec)


Lesenswert?

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.

von S. Landolt (Gast)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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
const uint16_t ICR = 256;
3
const uint16_t PWM = F_CPU/ICR;
4
const uint16_t A4 = 440;
5
uint16_t A4_Phasendelta = 782;   // (AS4/PWM*65536)
6
uint16_t Phase;

> Übrigens bin ich steinalt - und das Schlimmste daran ist, dass ich mich
> auch so fühle.
Das kommt dir nur so vor.

: Bearbeitet durch User
von S. Landolt (Gast)


Lesenswert?

> 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

von Veit D. (devil-elec)


Lesenswert?

Hallo,

okay, du hast mit 440Hz und Robert mit 466Hz getestet. Sollte für die 
Hörprobe egal sein. Ich lasse mir was einfallen für einen Aufbau.

von Veit D. (devil-elec)


Lesenswert?

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.

von Robert (Gast)


Lesenswert?

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

von S. Landolt (Gast)


Lesenswert?

> 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.

von S. Landolt (Gast)


Lesenswert?

> 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.

von c-hater (Gast)


Lesenswert?

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.

von S. Landolt (Gast)


Lesenswert?

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.

von Robert (Gast)


Lesenswert?

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

von S. Landolt (Gast)


Lesenswert?

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.

von Robert (Gast)


Lesenswert?

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

von Rainer V. (a_zip)


Lesenswert?

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

von Veit D. (devil-elec)


Angehängte Dateien:

Lesenswert?

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.
1
/*
2
  S.Landolt 08.03.2021   (Disclaimer: None. Sue me.)
3
  ATtiny2313: DDS 440 Hz Sinus
4
  https://www.mikrocontroller.net/topic/513754#6600182
5
*/
6
7
// Arduino Mega2560
8
9
#include <avr/io.h>
10
#include <avr/pgmspace.h>
11
12
const uint16_t ICR {256};
13
//const uint16_t PWM {F_CPU/ICR};     // 16MHz
14
const uint16_t PWM {8000000UL/ICR};
15
const uint16_t TonA4 {440};
16
uint16_t A4_Phasendelta {(uint16_t)(65536UL*PWM/TonA4)};    
17
uint16_t Phase;
18
19
const uint8_t Sinus[256] PROGMEM = {
20
  128,  131,  134,  137,  141,  144,  147,  150,  153,  156,  159,  162,  165,  168,  171,  174,
21
  177,  180,  183,  186,  188,  191,  194,  196,  199,  202,  204,  207,  209,  212,  214,  216,
22
  219,  221,  223,  225,  227,  229,  231,  233,  234,  236,  238,  239,  241,  242,  244,  245,
23
  246,  247,  249,  250,  250,  251,  252,  253,  254,  254,  255,  255,  255,  255,  255,  255,
24
  255,  255,  255,  255,  255,  255,  255,  254,  254,  253,  252,  251,  250,  250,  249,  247,
25
  246,  245,  244,  242,  241,  239,  238,  236,  234,  233,  231,  229,  227,  225,  223,  221,
26
  219,  216,  214,  212,  209,  207,  204,  202,  199,  196,  194,  191,  188,  186,  183,  180,
27
  177,  174,  171,  168,  165,  162,  159,  156,  153,  150,  147,  144,  141,  137,  134,  131,
28
  128,  125,  122,  119,  115,  112,  109,  106,  103,  100,   97,   94,   91,   88,   85,   82,
29
  79,   76,   73,   70,   68,   65,   62,   60,   57,   54,   52,   49,   47,   44,   42,   40,
30
  37,   35,   33,   31,   29,   27,   25,   23,   22,   20,   18,   17,   15,   14,   12,   11,
31
  10,    9,    7,    6,    6,    5,    4,    3,    2,    2,    1,    1,    1,    0,    0,    0,
32
  0,    0,    0,    0,    1,    1,    1,    2,    2,    3,    4,    5,    6,    6,    7,    9,
33
  10,   11,   12,   14,   15,   17,   18,   20,   22,   23,   25,   27,   29,   31,   33,   35,
34
  37,   40,   42,   44,   47,   49,   52,   54,   57,   60,   62,   65,   68,   70,   73,   76,
35
  79,   82,   85,   88,   91,   94,   97,  100,  103,  106,  109,  112,  115,  119,  122,  125
36
};
37
38
int main(void)
39
{
40
  CLKPR = _BV(CLKPCE) | _BV(CLKPS0);    // CPU Clock Div 2
41
42
  // Mode 14, Fast PWM - ICR1
43
  DDRB = (1 << 5);   // OC1A = Arduino Pin 11 = PB5
44
  ICR1 = ICR - 1;
45
  TCCR1A = (1 << COM1A1) | (1 << COM1A0) | (1 << WGM11);
46
  TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS10);
47
48
  while (1)
49
  {
50
    Phase += A4_Phasendelta;
51
    OCR1A = pgm_read_byte(&(Sinus[Phase >> 8]));
52
    while (!(TIFR1 & (1 << TOV1))) {;}    // ohne jitter: sleep plus interrupt
53
    TIFR1 |= (1 << TOV1);
54
  };
55
}

von Veit D. (devil-elec)


Lesenswert?

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.

: Bearbeitet durch User
von S. Landolt (Gast)


Lesenswert?

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_Phasendelta 738   // (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.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

ach Mist. Formel falsch umgestellt. "Kann er also auch nicht."  :-)  :-)
Um Rundungsfehler abzumildern multipliziere ich vor einer Division.
1
uint16_t A4_Phasendelta {(uint16_t)(TonA4*65536UL/PWM)};   // 461

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.

: Bearbeitet durch User
von S. Landolt (Gast)


Lesenswert?

> 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).

von c-hater (Gast)


Lesenswert?

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.

von c-hater (Gast)


Lesenswert?

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...

von S. Landolt (Gast)


Lesenswert?

Veit Devil freute sich:
>  Alles prima.
Heißt das nun, dass auch Sie etwelche Störungen weder hören noch sehen?

von Veit D. (devil-elec)


Lesenswert?

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.

von Robert (Gast)


Lesenswert?

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

von S. Landolt (Gast)


Lesenswert?

> Haut mal 22nF Kondensatoren rein
Bei welchem Widerstand?
Oder umgekehrt: versuchen Sie mal, mit Ihrem Material auf eine 
Zeitkonstante von 200 us zu kommen.

von S. Landolt (Gast)


Lesenswert?

Pardon, das war denn doch übertrieben - 100 us reichen, eigentlich sogar 
50.

von c-hater (Gast)


Lesenswert?

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...

von Veit D. (devil-elec)


Lesenswert?

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.

von S. Landolt (Gast)


Angehängte Dateien:

Lesenswert?

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).

von Veit D. (devil-elec)


Lesenswert?

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.

von S. Landolt (Gast)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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.  :-)

von S. Landolt (Gast)


Lesenswert?

Sehr schön, vielen Dank schon mal.

> obs heute noch wird ...
Spielt keine Rolle, ich habe alle Zeit der Welt (meiner Welt).

von c-hater (Gast)


Lesenswert?

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...

von S. Landolt (Gast)


Lesenswert?

> 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.

von c-hater (Gast)


Lesenswert?

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.

von S. Landolt (Gast)


Lesenswert?

> 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.

von Veit D. (devil-elec)


Angehängte Dateien:

Lesenswert?

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.

von S. Landolt (Gast)


Lesenswert?

TCD_SYNCEOC_bm statt des TCD_SYNC_bm!

von Veit D. (devil-elec)


Lesenswert?

Hallo,

sauber, passt, Zacke weg. Vielen Dank fürs drüber schauen.

von S. Landolt (Gast)


Lesenswert?

Danke für Ihre Mühe.

von Veit D. (devil-elec)


Lesenswert?

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?

: Bearbeitet durch User
von S. Landolt (Gast)


Lesenswert?

> 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.

von S. Landolt (Gast)


Lesenswert?

Datenblatt:
– One 12-bit PWM Timer/Counter type D (TCD) optimized for power control
• Waveform Generation
• Half-Bridge and Full-Bridge Output Support

von S. Landolt (Gast)


Lesenswert?

> 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)

von Veit D. (devil-elec)


Lesenswert?

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.

von c-hater (Gast)


Lesenswert?

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...

von Veit D. (devil-elec)


Lesenswert?

Hallo,

das stimmt, der Name FAULTCTRL hatte mich auch ins grübeln gebracht.
Muss man mit leben.  ;-)

von Veit D. (devil-elec)



Lesenswert?

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

: Bearbeitet durch User
von S. Landolt (Gast)


Lesenswert?

> 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?

von S. Landolt (Gast)


Lesenswert?

> 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.

von Veit D. (devil-elec)


Lesenswert?

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.

von S. Landolt (Gast)


Angehängte Dateien:

Lesenswert?

> 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.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

probiere das einmal mit dem DAC aus. Wenn das tut, ist alles i.O.  :-)
1
const uint8_t EKG[256] PROGMEM = {
2
     73,  74,  75,  75,  74,  73,  73,  73,  73,  72,  71,  69,  68,  67,  67,  67,
3
     68,  68,  67,  65,  62,  61,  59,  57,  56,  55,  55,  54,  54,  54,  55,  55,
4
     55,  55,  55,  55,  54,  53,  51,  50,  49,  49,  52,  61,  77, 101, 132, 169,
5
    207, 238, 255, 254, 234, 198, 154, 109,  68,  37,  17,   5,   0,   1,   6,  13,
6
     20,  28,  36,  45,  52,  57,  61,  64,  65,  66,  67,  68,  68,  69,  70,  71,
7
     71,  71,  71,  71,  71,  71,  71,  72,  72,  72,  73,  73,  74,  75,  75,  76,
8
     77,  78,  79,  80,  81,  82,  83,  84,  86,  88,  91,  93,  96,  98, 100, 102,
9
    104, 107, 109, 112, 115, 118, 121, 123, 125, 126, 127, 127, 127, 127, 127, 126,
10
    125, 124, 121, 119, 116, 113, 109, 105, 102,  98,  95,  92,  89,  87,  84,  81,
11
     79,  77,  76,  75,  74,  73,  72,  70,  69,  68,  67,  67,  67,  68,  68,  68,
12
     69,  69,  69,  69,  69,  69,  69,  70,  71,  72,  73,  73,  74,  74,  75,  75,
13
     75,  75,  75,  75,  74,  74,  73,  73,  73,  73,  72,  72,  72,  71,  71,  71,
14
     71,  71,  71,  71,  70,  70,  70,  69,  69,  69,  69,  69,  70,  70,  70,  69,
15
     68,  68,  67,  67,  67,  67,  66,  66,  66,  65,  65,  65,  65,  65,  65,  65,
16
     65,  64,  64,  63,  63,  64,  64,  65,  65,  65,  65,  65,  65,  65,  64,  64,
17
     64,  64,  64,  64,  64,  64,  65,  65,  65,  66,  67,  68,  69,  71,  72,  73
18
};

: Bearbeitet durch User
von S. Landolt (Gast)


Lesenswert?

Hübsch.
Wenn ich nun noch Ihr 'Phasendelta' kennte, ließe sich entscheiden, ob 
Maus oder Wal.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

das Phasendelta darfst du nach eigenen ermessen wählen.
Ich befinde mich laut Definition im mittlerem Alter.  :-)

: Bearbeitet durch User
von Robert (Gast)


Angehängte Dateien:

Lesenswert?

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

von c-hater (Gast)


Lesenswert?

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:

von Robert (Gast)


Angehängte Dateien:

Lesenswert?

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

von S. Landolt (Gast)


Lesenswert?

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

von Robert (Gast)


Lesenswert?

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

von S. Landolt (Gast)


Lesenswert?

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.

von Robert (Gast)


Lesenswert?

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

von S. Landolt (Gast)


Lesenswert?

> 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.

von Robert (Gast)


Lesenswert?

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

von Robert (Gast)


Lesenswert?

Hmmmmm....

Also in dem 
http://ww1.microchip.com/downloads/en/devicedoc/atmel-0856-avr-instruction-set-manual.pdf 
,
beispielsweise Seite 108 im example wirds so gemacht:
1
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...

von S. Landolt (Gast)


Lesenswert?

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.

von S. Landolt (Gast)


Angehängte Dateien:

Lesenswert?

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)

von Robert (Gast)


Angehängte Dateien:

Lesenswert?

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

von c-hater (Gast)


Lesenswert?

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...

von S. Landolt (Gast)


Lesenswert?

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.

von Robert (Gast)


Lesenswert?

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

von Robert (Gast)


Lesenswert?

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

von S. Landolt (Gast)


Lesenswert?

> 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.

von Robert (Gast)


Lesenswert?

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.

von S. Landolt (Gast)


Lesenswert?

> 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).

von S. Landolt (Gast)


Lesenswert?

Apropos Tonkanäle im SRAM mit 5 Bytes: da lernen Sie dann, die Befehle 
ldd und std zu schätzen.

von Robert (Gast)


Lesenswert?

Die Idee ist bestechend gut...

Verdammt... so viel verworrener Nudelcode von mir fuer die Katz...
Na egal, so ist das halt.


lg Robert

von S. Landolt (Gast)


Lesenswert?

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

von Robert (Gast)


Lesenswert?

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

von S. Landolt (Gast)


Lesenswert?

> 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.

von S. Landolt (Gast)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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?

von Robert (Gast)


Lesenswert?

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

von Veit D. (devil-elec)


Lesenswert?

Hallo,

Danke, dass war der entscheidende Hinweis.

von Robert (Gast)


Angehängte Dateien:

Lesenswert?

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

von Robert (Gast)


Lesenswert?

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

von c-hater (Gast)


Lesenswert?

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.

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.