Forum: Mikrocontroller und Digitale Elektronik Softuart um midi zu senden


von Sebastian J. (bassti)


Lesenswert?

Ich wollte mit einem ATMega8 eine oktave stummelpedale midifizieren.
etzt hab ich mir gedacht, machst die uart mal von hand...
Irgendwas funktioniert aber nicht, ich habe mir den Takt von 31,250kHz
mittels timer hergestellt (und mit oszi nachgemessen) und ein
progrämmchen geschrieben, welches ein startbit, 8 datenbits und ein
stopbit auf einen pin schickt:
______________________________________________________________


unsigned char tick=0x00;
unsigned char toggle=0x00;
const unsigned char noteon=0x09;  //90
const unsigned char keynum=0x24;  //24
const unsigned char velocity=0xff;
const unsigned char novelocity=0x00;
// Timer 0 overflow interrupt service routine
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{

   //  if (tick==0x00)
   //  {
  tick++;
   //  }
   //  else
   //  {
   //  tick=0x00;
   //  }
TCNT0=0x30;
}
void midiout (unsigned char byte)
{
unsigned char busy=0x01;
unsigned char bitcount=0x00;
  while (busy==0x01)
  {
  //write startbit
    if (bitcount==0x00 && tick==0x01)
    {
    PORTD=0xFF;
    tick=0x00;
    bitcount++;
    }
  //write stopbit
    else if(bitcount==0x09 && tick==0x01)
          {
          PORTD=0xFF;
          tick=0x00;
          bitcount++;
    }
            else if(bitcount==0x0a && tick==0x01)
          {
          PORTD=0x00;
          tick=0x00;
          bitcount++;
    busy--;
    }
  //write byte
    else if(tick==0x01)
    {
    PORTD=byte;
    byte>>=1;
    bitcount++;
    tick=0;
    }

  }
        PORTC.1^=1;
}

// Timer 1 overflow interrupt service routine
interrupt [TIM1_OVF] void timer1_ovf_isr(void)
{
// Reinitialize Timer 1 value
  TCNT1H=0x70;
  TCNT1L=0x00;
// Place your code here
  PORTC.2^=1;

        toggle++;


}
    /*
// Timer 2 overflow interrupt service routine
interrupt [TIM2_OVF] void timer2_ovf_isr(void)
{
// Place your code here
PORTD.2^=1;
}
  */
// Declare your global variables here

void main(void)
{
// Declare your local variables here

// Input/Output Ports initialization
// Port B initialization
// Func0=In Func1=In Func2=In Func3=In Func4=In Func5=In Func6=In
Func7=In
// State0=P State1=P State2=P State3=P State4=T State5=T State6=P
State7=P
PORTB=0xCF;
DDRB=0x00;

// Func0=Out Func1=Out Func2=Out Func3=Out Func4=Out Func5=Out
Func6=Out
// State0=0 State1=0 State2=0 State3=0 State4=0 State5=0 State6=0
PORTC=0x00;
DDRC=0x7F;

// Port D initialization
// Func0=Out Func1=Out Func2=Out Func3=In Func4=In Func5=In Func6=In
Func7=In
// State0=0 State1=0 State2=0 State3=T State4=T State5=T State6=T
State7=T
PORTD=0x00;
DDRD=0x07;

// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: 8000,000 kHz
TCCR0=0x01;
TCNT0=0x00;

// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 8000,000 kHz
// Mode: Normal top=FFFFh
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
TCCR1A=0x00;
TCCR1B=0x05;
TCNT1H=0x70;
TCNT1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;
/*
// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: 8000,000 kHz
// Mode: Normal top=FFh
// OC2 output: Disconnected
TCCR2=0x01;
ASSR=0x00;
TCNT2=0x00;
OCR2=0x00;
  */
// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
GICR=0x00;
MCUCR=0x00;

// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x45;

// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
// Analog Comparator Output: Off
ACSR=0x80;
SFIOR=0x00;

// Global enable interrupts
#asm("sei")



while (1)
      {



        midiout(noteon);
        midiout(keynum);
        midiout(velocity);

        /*
        if(toggle==0x01)
        {
        midiout(noteon);      //10010000
        midiout(keynum);      //00100100
        midiout(velocity);    //11111111
        toggle++;
        }
        else if (toggle==0x03)
        {
        midiout(noteon);      //10010000
        midiout(keynum);      //00100100
        midiout(novelocity);  //00000000
        toggle=0x00;
        } */

      }

}
____________________________________________________________
Irgendwo ist ein grober fehler drinne, das signal hab ich aufm oszi,
wie es eigentlich sein soll.
Wie muss ein midisignal aussehen, 1 bedeutet doch stromschleife
unterbrochen, für eine taktperiode?
wie ist das mit startbit, kommt da nochmal 0 dazwischen?
das keyboard, das ich angeschlossen hab reagiert nicht, schaltet nur
manchmal das rythmusgerät ein, ist aber definitiv ok, da es mit anderen
quellen tut.
Die Stromschleife passt von den Werten her exakt (nachgemessen)

von tester (Gast)


Lesenswert?

Ist schon eine Weile her, seit dem ich mich das letzte mal mit Midi
beschäftigte. Schließ doch mal statt eines Keyboards die Midi-Signale
an Deine PC-Soundkarte an. Ich kann mich noch dunkel an ein Programm
(Midimon?) erinnern, das die Bytes am Bildschirm anzeigte.

Solche Projekte gibt es wie Sand am Meer, oft mit PIC-Prozessoren.

Polarität des Midi-Signals weiß ich nicht auswendig, ich denke aber mal
TTL high = V24 low = Midi no current (nur eine Vermutung, da sonst bei
abgestecktem Midi immer Startbits gesehen würden).

Freut mich immer, wenn jemand was mit Midi macht...
Kennst Du den ZEL Midi-Compiler (www.zelsoft.com)?
Super high-density-Programm für Puristen!

von tester (Gast)


Lesenswert?


von Sebastian J. (bassti)


Lesenswert?

Danke erstmal für die Hilfe, aber
die projekte, die ich bisher gesehen hab, sind alle in asm -kann ich
nicht.
Es tut ja an sich alles was ich will... signal hab ich ja aufm oszi.
es ist irgendwie der fehler im systen... hi/lo verwechselt, byte
verkehrtrum gesendet? steht doch nirgends...steht nur überall startbit
datenbyte stoppbit.
oder muss man da noch irgendwelche config messages bringen?
aus dem keyboard seh ich immer nur die 3 bytes noteon/kanal, keynumber/
velocity...beim loslassen halt mit velocity=00

von mmerten (Gast)


Lesenswert?

natürlich mußt du auch während der Ausgabe eines Bytes über den Soft
UART dafür sorgen, daß keine weitere Interrupt Routine ausgeführt
wird.
Sonst kommt ja da das Bit-Timing durcheinander.

von tester (Gast)


Lesenswert?

Versuche doch mal, genau die gleiche Bitfolge zu erzeugen, wie aus dem
Keyboard beim Drücken einer Taste kommt. Dann muss es gehen.

Habe ich von 20 Jahren schonmal gemacht: ein einfachster Sequenzer: ich
zeichnete das Byte und die Zeit zum nächsten auf. Damit konnte ich
gespielte Stücke wiederholt abspielen.

Hast Du mal den Midimon ausprobiert?
Hast Du eine Soundkarte mit Midi-In/Out?

von Sebastian J. (bassti)


Lesenswert?

Ich hab jetzt mal doch die USART des atmels benutzt -die bitfolge ist
von der frequenz her auch so wie von meiner softuart...
ich kann nur die vom keyboard schlecht angucken, weil ich kein
speicheroszi hab.
habs jetzt mal versucht mit der soundkarte als audio aufzuzeichen
auch nicht ideal mit 44.1kHz sampling nur, mehr macht die irgendwie
net.
das problem ist eher dass ich nirgends gefunden hab, wie das signal
aussehen soll.
ich hab ein programm nahmens midispy, das hex anzeigt, was empfangen
wird (nix).
Ich begreif das midisignal nicht.
HAb wo gelesen, dass logisch 1 strom aus und logisch null strom an
sei.
ist das startbit dann logisch 0?
das signal vom keyboard, so gut ichs erkennen kann ist strom aus, wenn
nix gesendet wird.
irgendwie ist der fehler im system.
signal fällt ja raus, nur das falsche, und ich weiss nicht was falsch
ist.

von Bastian S. (alisa_1387)


Lesenswert?

MIDI Spezifikationen gibt´s unter http://www.midi.org

unter http://www.google.de sind jede Menge Seiten mit Infos zu finden,
wie die verschiedensten MIDI-Strings aufgebaut sind.

von tester (Gast)


Lesenswert?

Zuerst musst Du mal klären, ob es an der HW oder der SW liegt, ich
vermute mal, mit der HW stimmt was noch nicht.

Kannst Du mal versuchen, das Signal im Empfänger nach dem ersten
Optokoppler zu messen.
Wenn Du Dein Oszi auf Single oder AUTO stellst und die Triggerung und
den Slope richtig einstellst, kannst Du erreichen, dass der Strahl
immer mit dem Startbit an der linken Seite der Schirms beginnt.

Wenn die Umgebung dunkel ist, sieht man den Strahl immer noch
ausreichend lange nachleuchten.

Natürlich wäre ein Speicheroszi besser, hat ein Bekannter vielleicht
eines zum Leihen?

Dann kannst Du auch die Polarität und die Bit-Dauer (ca.31µs) messen.
Vergleiche sie mit einem Midi-Signal (aus dem PC oder einem anderen
Gerät oder mit einem Midi-Loop-Stecker sogar aus dem selben Gerät).

Das sollte doch zu schaffen sein.

von TravelRec. (Gast)


Lesenswert?

@ Bassti:

Hast Du an Deiner Soundkarte keinen MIDI-Eingang? Falls doch, guckst Du
"MIDI-OX" - kleines Programm, was Dir alles zeigt, was über MIDI ´rein
kommt. In puncto Midi: Startbit (0V / 0mA), Datenbits LSB zuerst, MSB
zuletzt, Stopp-Bit (5V / 5 mA). Falls Du ´nen Optokoppler benutzt,
diesen über 3x 220 Ohm und ´ne Sicherheitsdiode antiparallel zu LED
zwischen Port-AusgangsPin und VCC klemmen, so daß beim StopBit die LED
aus ist. Am Ausgang des Optos einen PullUp und daran die
MIDI-Schnittstelle der Soundkarte. Achtung, die Pegel und
Anstiegs-/Abfallzeiten sind kritisch und können, falls falsch
eingestellt oder verwaschen, zu Bitfehlern führen. Übrigens sind
MIDI-Befehle nur sehr selten ein einziges Byte. Kanalmeldungen und
Ereignismeldungen (Note/Anschlag) müssen immer mitübertragen werden,
sonst reagiert der angeschlossene "Slave" nicht. Nutz doch für
Testzwecke erstmal einen  Hardware-Uart, bei dem Du von 4 oder 8MHz die
Taktfreqens auf 31250Hz herunterteilst. Bei dem stimmen dann
zwangsläufig zumindest Datenformat und Timing und Du kannst den Fehler
eventuell woanders suchen.

von Sebastian J. (bassti)


Lesenswert?

also wie gesagt, hab ich zu hardwaredebugging mal die hardwareuart des
atmels verwendet, mit 8MHz auf die 31,250kHz eingestellt.
Passt von der frequenz her.
Mir ist das mit dem startbit nicht klar was das soll... wenn nix kommt,
dann ist doch sowieso 0 Strom??
Datenbits 1=Strom oder kein Strom? -also wirklich in der
stromschleife...
ich hab jetzt von optokopplern mal abgesehen kommt später, wenns mal
soweit tut.
versorgt potentialfrei, also keine probleme mit massen und  so.
ist das alles richtig so? code vom wizzard:
_________________________________________________________________
#include <mega8.h>

#define RXB8 1
#define TXB8 0
#define UPE 2
#define OVR 3
#define FE 4
#define UDRE 5
#define RXC 7

#define FRAMING_ERROR (1<<FE)
#define PARITY_ERROR (1<<UPE)
#define DATA_OVERRUN (1<<OVR)
#define DATA_REGISTER_EMPTY (1<<UDRE)
#define RX_COMPLETE (1<<RXC)

// USART Transmitter buffer
#define TX_BUFFER_SIZE 64
typedef unsigned char byte;
char tx_buffer[TX_BUFFER_SIZE];
unsigned char tx_wr_index,tx_rd_index,tx_counter;
const byte noteon=0x90;
const byte keynum=0x24;
const byte velocity=0x7F;
const byte novelocity=0x00;
byte toggle=0x00;
// USART Transmitter interrupt service routine
#pragma savereg-
interrupt [USART_TXC] void uart_tx_isr(void)
{
#asm
    push r26
    push r27
    push r30
    push r31
    in   r26,sreg
    push r26
#endasm
if (tx_counter)
   {
   --tx_counter;
   UDR=tx_buffer[tx_rd_index];
   if (++tx_rd_index == TX_BUFFER_SIZE) tx_rd_index=0;
   };
#asm
    pop  r26
    out  sreg,r26
    pop  r31
    pop  r30
    pop  r27
    pop  r26
#endasm
}
#pragma savereg+

#ifndef DEBUG_TERMINAL_IO
// Write a character to the USART Transmitter buffer
#define ALTERNATE_PUTCHAR
#pragma used+
void putchar(char c)
{
while (tx_counter == TX_BUFFER_SIZE);
if (tx_counter || ((UCSRA & DATA_REGISTER_EMPTY)==0))
   {
   tx_buffer[tx_wr_index]=c;
   if (++tx_wr_index == TX_BUFFER_SIZE) tx_wr_index=0;
   #asm("cli")
   ++tx_counter;
   PORTC.2^=1;  //schaun
   #asm("sei")
   }
else UDR=c;
}
#pragma used-
#endif

// Standard Input/Output functions
#include <stdio.h>

// Timer 1 overflow interrupt service routine
interrupt [TIM1_OVF] void timer1_ovf_isr(void)
{
// Place your code here
toggle++;

TCNT1H=0x70;
TCNT1L=0x00;
}

// Declare your global variables here

void main(void)
{
// Declare your local variables here

// Input/Output Ports initialization
// Port B initialization
// Func0=In Func1=In Func2=In Func3=In Func4=In Func5=In Func6=In
Func7=In
// State0=P State1=P State2=P State3=P State4=T State5=T State6=P
State7=P
PORTB=0xCF;
DDRB=0x00;

// Port C initialization
// Func0=Out Func1=Out Func2=Out Func3=Out Func4=Out Func5=Out
Func6=Out
// State0=0 State1=0 State2=0 State3=0 State4=0 State5=0 State6=0
PORTC=0x00;
DDRC=0x7F;

// Port D initialization
// Func0=In Func1=In Func2=In Func3=In Func4=Out Func5=In Func6=In
Func7=In
// State0=T State1=T State2=T State3=T State4=0 State5=T State6=T
State7=T
PORTD=0x00;
DDRD=0x10;

// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: Timer 0 Stopped
TCCR0=0x00;
TCNT0=0x00;

// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 7,813 kHz
// Mode: Normal top=FFFFh
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
TCCR1A=0x00;
TCCR1B=0x05;
TCNT1H=0x70;
TCNT1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;

// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer 2 Stopped
// Mode: Normal top=FFh
// OC2 output: Disconnected
TCCR2=0x00;
ASSR=0x00;
TCNT2=0x00;
OCR2=0x00;

// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
GICR=0x00;
MCUCR=0x00;

// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x04;

// USART initialization
// Communication Parameters: 8 Data, 1 Stop, No Parity
// USART Receiver: Off
// USART Transmitter: On
// USART Mode: Asynchronous
// USART Baud rate: 31250
UCSRA=0x00;
UCSRB=0x48;
UCSRC=0x86;
UBRRL=0x0F;
UBRRH=0x00;

// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
// Analog Comparator Output: Off
ACSR=0x80;
SFIOR=0x00;

// Global enable interrupts
#asm("sei")

while (1)
      {
      putchar(novelocity);
      // Place your code here
      // if(toggle==0x01)
     /* {
        putchar(noteon);      //10010000
        putchar(keynum);      //00100100
        putchar(velocity);    //11111111
        toggle++;
        }
       else if (toggle==0x03)
        {
        putchar(noteon);      //10010000
        putchar(keynum);      //00100100
        putchar(novelocity);  //00000000
        toggle=0x00;
        } */
      };
}
_____________________________________________

hab jetzt einfach nen dauersignalstrom drinne, um die frequenz mal
anzuschauen... ansonsten immer abwechlungsweise die drei / sechs
messages Noteonereignis/Kanalnummer  Keynummer  und velocity, mal volle
pulle, mal 0.  -was anderes tut das keyboard auch nicht schicken.

das Signal hab ich dann von dem pin genommen und doppelt invertiert.
bzw. die last der 2. emitterstufe ist dann die midistromschleife.
kann man aufm oszi auch anschaun.  ich bin echt am verzweifeln.
sind die einsen beim datenbyte strom oder nicht strom?

von tester (Gast)


Lesenswert?

Also könntest Du bitte mal das tun, was wir dir anraten, sonst wird das
nichts.
Ich bin mir nicht ganz sicher:
Ruhe = 0 mA
Startbit = 5 mA (TravelRec behauptet das Gegenteil)
Dlow = 5mA
Dhigh = 0 mA
Stopbit = 0mA  (TravelRec behauptet das Gegenteil)

Ist die Bitdauer richtig (MESSEN!)
sind die Kanten scharf (MESSEN!)
Ist das Signal genauso wie das Keyboard es sendet?

HAST DU EINEN MIDI-EINGANG AM PC???

von Sebastian J. (bassti)


Lesenswert?

Ruhe = 0 mA             -richtig.
Startbit = 5 mA         -richtig.
Dlow = 5mA              -richtig.
Dhigh = 0 mA            -richtig.
Stopbit = 0mA           -richtig.

Ist die Bitdauer richtig (MESSEN!)
-bei der hardwareusart ja, hab versucht bei der softwareusart mal die
dauern zu variieren um vielleicht irgendwann mal die richtige zu
treffen, denn die vom keyboard gesendeten stimmen auch nicht.

sind die Kanten scharf (MESSEN!)
-ja, sind sie.(imo wirklich scharf genug, scharf ist relativ)

Ist das Signal genauso wie das Keyboard es sendet?
nein, denn mit der hardwareusart krieg ich das mit den start/stoppbits
nicht hin -dient das stopbit als platzhalter?
ansonsten ja sinnlos, weil dann strom eh 0..
mit der softuart hab ich dann mal versucht das signal genausolang zu
machen, wie das vom keyboard mit meinem speicheroszi (oszi + digicam)
es will nicht tun.

HAST DU EINEN MIDI-EINGANG AM PC???
-ja, und der empfängt genausowenig wie das keyboard

von Sebastian J. (bassti)


Lesenswert?

Freudentränen laufen mir über die backen...
geht doch, GEHT DOCH!!
Das tool, das ich verwendet hab war einfach n sch....
mit der hardwareuart tuts jetzt, einfach das falsche gesendet.
das geniesse ich jetzt erstmal, bevor ich mich wieder entmutigen lass.
ich melde mich dann wieder von der front, wenn ich was neues hab.
Dickes DANKE erstmal für eure Hilfe!
Hoffentlich kann ich das auch zurückgeben.

von TravelRec. (Gast)


Lesenswert?

Na super! Ja, habe da oben ein bissi Quark geschrieben - kommt von der
dauernden Invertiererei mit den Optos - irgendwann hat man dann ´nen
Knoten im Hirn. Wichtig ist, es geht, bei mir tut´s ja auch alles geh´n
trotz des Hirnknotens ;-). Zum Klarstellen nochmals: Das  U(S)ART ist
gegenüber Masse logisch HIGH, solange nix passiert, geht auf logisch 0
für das Startbit, toggelt je nach Daten zwischen 0 und 1 rum, wobei das
kleinstwertige Bit zuerst gesendet wird und geht mit dem Stopp-Bit
wieder auf logisch 1. Parity gibt´s dann auch noch, aber eben nicht bei
MIDI - schön, daß wir uns alle einig sind :-).

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.