Forum: Mikrocontroller und Digitale Elektronik Selbstbau Synth mit Midi


von Synthbastler (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Zusammen,

momentan versuche ich mit einem ATMEGA16 ein Rechtecksignal zu erzeugen, 
das von einem Midikeyboard angesteuert wird.
Das Ganze soll später mal ein Oszillator für einen Synthesizer werden, 
der die vier grundlegenden Wellenformen (Sinus, Dreieck, Sägezahn, 
Rechteck) generieren kann.
Das Erzeugen der Frequenz ist nicht das Problem, sondern die 
Midi-Schnittstelle will nicht so, wie ich will:
Ich habe mit erstmal ein Programm geschrieben, das wenn ich auf dem 
Keyboard das A4 drücke den entsprechenden Ton mit 440Hz generieren soll. 
Das klappt bis zu einem gewissen Grad auch gut, allerdings nur, so lange 
ich die Taste nicht zu schnell drücke und wieder loslasse.
Dann scheint der ATMEGA nicht mehr schnell genug zu sein und somit 
manche Midi-Messages zu verpassen.

Zur Hardware:
Das Keyboard habe ich über die standart Midi-In Schaltung angeschlossen, 
die ich auch in den Anhang geladen habe.
Allerdings habe ich hinter den Optokoppler noch einen Schmitt-Trigger 
gesetzt, da die Signale am Optokoppler noch nicht sehr sauber waren.
Die Midi-Schnittstelle am Keyboard funktioniert an meinem PC 
einwandfrei, da liegt das Problem also nicht.
An PORTC liegt dann das generierte Signal an, wo ich dann zwei einfache 
PC Speaker angeschlossen habe.

Zur Software:
Das Programm wartet auf einen UART-Interrupt, prüft dann ob es sich um 
die Note-On Nachricht handelt und setzt dann einen int auf 1 oder 0, der 
dann in der Hauptschleife die Tonerzeugung ein - oder ausschaltet.
Ich habe mich bemüht alles gut zu kommentieren und leserlich zu 
gestalten, weshalb es einfach sein sollte das Program zu verstehen.
1
#define F_CPU 20000000UL
2
3
#include <avr/interrupt.h> 
4
5
#include "uart.h"
6
7
8
void generate_sound(uint8_t note, uint8_t velocity);
9
void silence();
10
void delay_us(uint16_t zeit);
11
12
13
uint8_t message = 0, rcvd = 0, note_on = 0, data_rcvd = 0;
14
uint8_t data_buf[2];  
15
volatile uint8_t play_note = 0;
16
volatile uint16_t dauer = 0;
17
18
19
ISR(USART_RXC_vect)
20
{
21
  rcvd = UDR;        // Empfangsregister auslesen
22
23
  if(rcvd > 0x7F)      // Status-Byte
24
  {
25
    message = rcvd >> 4;  // Die Nachricht = die oberen 4 Bit 
26
    
27
    if(message == 0b1001) // Note On
28
    {
29
      // Die nächsten beiden Interrupts sind die zwei Datenbytes
30
      // Also für die Aufzeichnung sorgen
31
      note_on = 1;
32
  }
33
  }
34
  else          // Daten-Byte
35
  {
36
    if(note_on)      // Gehört das Daten-Byte zur Note On Nachricht?
37
    {
38
      data_buf[data_rcvd] = rcvd;  // Daten-Byte ins Array schreiben
39
      data_rcvd++;      // Array-Index erhöhen
40
41
    if(data_rcvd == 2)  // Beide Daten-Bytes empfangen?
42
    {
43
      note_on = 0;    // Note On Nachricht zu Ende
44
      data_rcvd = 0;    // Array-Index resetten
45
46
      if(data_buf[1])    // velocity größer Null?
47
      {
48
        // Einen Klang mit der Note data_buf[0] und der
49
        // velocity data_buf[1] erzeugen
50
        generate_sound(data_buf[0], data_buf[1]);
51
      }
52
      else
53
      {
54
        // Sonderfall velocity = Null bedeutet Note Off
55
        // Also still sein!
56
      silence();
57
    }
58
    }
59
  }
60
  }
61
}
62
63
void generate_sound(uint8_t note, uint8_t velocity)
64
{
65
  uint16_t freq;  
66
67
  play_note = 1;  
68
69
  switch(note)
70
  {
71
    case 69:          // Kammerton A4 = 440Hz
72
    freq = 440;
73
    break;
74
  default:
75
    play_note = 0;
76
  }
77
78
  dauer = (1000000 / freq) / 2;  // Periodendauer / 2 in µS berechnen;
79
  
80
}
81
82
void silence()
83
{
84
  play_note = 0;
85
}
86
87
88
// Wartet bei 20MHz Takt die vorgegebende Anzahl an µS
89
void delay_us(uint16_t zeit)
90
{
91
  for(uint16_t i = 0; i < zeit - 1; i++)
92
  {
93
  asm volatile("nop");
94
  asm volatile("nop");
95
  asm volatile("nop");
96
  asm volatile("nop");
97
  asm volatile("nop");
98
  asm volatile("nop");
99
  asm volatile("nop");
100
  asm volatile("nop");
101
  asm volatile("nop");
102
  asm volatile("nop");
103
  asm volatile("nop");
104
  asm volatile("nop");
105
  asm volatile("nop");
106
  asm volatile("nop");
107
  }
108
}
109
110
int main()
111
{
112
  DDRC = 0xFF;
113
  uart_init();
114
  sei();
115
116
  while(1)
117
  {
118
    while(play_note)
119
    {
120
      PORTC = 0xFF;
121
      delay_us(dauer);
122
      PORTC = 0x00;
123
    delay_us(dauer);
124
    }
125
  }
126
}

Danke für Eure Hilfe!

von Knut B. (Firma: TravelRec.) (travelrec) Benutzerseite


Lesenswert?

Synthbastler schrieb:
> Das klappt bis zu einem gewissen Grad auch gut, allerdings nur, so lange
> ich die Taste nicht zu schnell drücke und wieder loslasse.
> Dann scheint der ATMEGA nicht mehr schnell genug zu sein und somit
> manche Midi-Messages zu verpassen.

Das möchte ich mal ausschließen. Wenn es etwas nicht schafft, dann Dein 
Programm.

Synthbastler schrieb:
> Allerdings habe ich hinter den Optokoppler noch einen Schmitt-Trigger
> gesetzt, da die Signale am Optokoppler noch nicht sehr sauber waren.

Ich denke, dass hier (auch) der Hase im Pfeffer liegt. Schalte an den 
Optokoppler einen solch niederohmigen Arbeitswiderstand (470R...1k), 
dass die Signale am Kollektor sauber sind und lass den Schmitt-Trigger 
weg, der verändert nur das Tastverhältnis der Bits.

EDIT:

Anstelle des 6N138 tut es ein normaler PC817 oder CNY17-1.

von avr (Gast)


Lesenswert?

Synthbastler schrieb:
> while(play_note)
>     {
>       PORTC = 0xFF;
>       delay_us(dauer);
>       PORTC = 0x00;
>     delay_us(dauer);
>     }

Mit diesem Ansatz wirst du nicht über einen Kanal hinwegkommen. Für 
mehrere Kanäle braucht man fast zwingend einen Timer. Die Tonerzeugung 
für mehrere Kanäle gibt es übrigens recht effektiv hier: 
AVR-Synthesizer

von Hobbyorganist (Gast)


Lesenswert?

Darf ich fragen, ob das Projekt fortgesetzt wurde? Ich würde auch MIDI 
implementieren wollen.

von Karl H. (kbuchegg)


Lesenswert?

@Hobbyorganist

Ganz ehrlich.
Ich habe den Eindruck du solltest erst mal mit viel grundlegenderen 
Dingen anfangen.
Dein Projekt in allen Ehren, aber du übernimmst dich. So wird das 
nichts. Dein Können ist noch lange nicht an dem Punkt angelangt, an dem 
man einigermassen sinnvoll ein derartiges Projekt anfangen kann, wobei 
einige Dinge dann erst on-the-job gelernt werden.

Nix für ungut.

Der Anfang jedes µC-Progammierers besteht in
LED einschalten, LED ausschalten, LED blinken lassen.
Erst mit Zeitschleifen, dann mit Timern.
dann kommt irgendwann UART dazu, möglicherweise ein LCD

Und dann, erst dann, ist man auf einem Level, auf dem man sinnvoll 
darüber nachdenken kann, wie man eigentlich Tonerzeugung auf einem AVR 
macht. Wobei es dann immer noch viel zu lernen gibt. Die vorgenannten 
Punkte sind sozusagen die Einstiegshürde. Solange die nicht beherrscht 
werden (und natürlich die Programmiersprache einigermassen sicher 
beherrscht wird), braucht man über ein Synthi-Midi-Projekt gar nicht 
erst nachdenken.

von Thomas (Gast)


Lesenswert?

Hallo,

ich habe mich auch schon mit MIDI beschäftigt. Um einen analogen 
Synthesizer an MIDI-Keyboards zu betreiben, braucht man einen MIDI-to-CV 
Konverter.

Wenn man diesen selbst baut, spart man eine Menge Geld und lernt 
Assemblern (oder C), umd den Mikrocontroller zu betreiben. Es empfiehlt 
sich, ein Flußdiagramm anzulegen, und diesen dann in Code umzusetzen. 
Dafür eignet sich das kostenlose Programm "Diagramm Designer" 
http://www.chip.de/downloads/Diagram-Designer_46463345.html.

Bei youtube 
http://www.youtube.com/watch?v=qOInnuqfsS0&list=UUnJGkAU29GWN9MdDnQ3zNWA 
kann man unter Anderem auch den Aufbau meines MIDI-to-CV Konverters 
Marke Eigenbau sehen.

Ich habe den 80C31, 74HC573, 27C512, DAC0808LCN, µA741 und 4N35 dafür 
verwendet. Taktfrequenz 12 MHz.

Gruß Thomas

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.