Forum: Mikrocontroller und Digitale Elektronik MIDI/UART lesen. Was mache ich falsch?


von T. S. (elektrowiesel)


Angehängte Dateien:

Lesenswert?

Hallo,
ich versuche mithilfe eines ATmega328 ein Midi Leser zu bauen.
Für den Anfang wollte ich die Richtigkeit der Empfangenen Daten 
feststellen.
Ich habe eine Standard Eingangsschaltung (siehe Wikipedia: Midi) 
verwendet und den Ausgang direkt auf den UART Pin geschaltet.

Immer wenn ein Byte empfangen wird, wird eine Interruptroutine 
ausgeführt, die einen Wert verändert und das Byte kopiert, sodass es in 
der Hauptschleife abgearbeitet werden kann.

Noch in der ISR überprüfe ich, ob das empfangene Byte ein Active Sense 
Signal (0xFE), was alle 200ms vom Keyboard geschickt wird, ist. Das 
funktioniert.
Deshalb glaube ich, dass alle Einstellungen richtig sein müssten (siehe 
Bild activeSense.jpg)

Wenn ich aber in der Hauptschleife mit dem Byte arbeite kommen immer 
falsche Dinge raus (siehe Bild note_on.jpg).

Immer wenn das Byte mit einer 1 anfängt sollte PB2 für 100µS an sein.

Lese ich die Daten vielleicht falsch herum?
Das kann eigentlich nicht sein, da ich die Active Sense Nachrichten per 
Vergleich ignorieren kann.

Einstellungen Logic analyzer UART decoder:
Baudrate: 31250
Datenbits: 8
Parität: keine
Parität prüfen: Nein
Stop bits: 1,0
Bit reihenfolge: LSB first
Invertiertes Signal: Nein


Code:
1
#define F_CPU 20000000
2
3
#include <avr/io.h>
4
#include <avr/interrupt.h>
5
#include <util/delay.h>
6
7
volatile uint8_t receiveComplete = 0;
8
volatile uint8_t recByte;
9
10
11
int main( void )
12
{
13
  // Port init.
14
  DDRB |= (1<<PB2);
15
  
16
  // UART init.
17
  UCSR0A = 0;
18
  UCSR0B = 0b10010000;
19
  UCSR0C = 0b00000110;
20
  UBRR0L = 39;
21
  UBRR0H = 0;
22
  
23
  sei();
24
  
25
    while( 1 )
26
    {
27
    if( receiveComplete )
28
    {
29
      if( (recByte & 0b10000000) )
30
      {
31
          PORTB |= (1<<PB2);
32
          _delay_us(100);
33
          PORTB &= ~(1<<PB2);
34
      }
35
36
      recByte = 0;
37
      receiveComplete = 0;
38
    }
39
    }
40
}
41
42
ISR( USART_RX_vect )
43
{
44
  // Ignore the active sense messages.
45
  if( UDR0 != 0xFE )
46
  {
47
    recByte = UDR0;
48
    receiveComplete = 1;
49
  }
50
}

von Elias K. (elik)


Lesenswert?

Ich weiß nicht, wieso es nicht geht. Aber vielleicht hilft das bei der 
Fehlersuche: Ich würde in der ISR (wenn genug Zeit) eine Funktion 
machen, die das empfangene Datenwort mittel Bitbanging seriell zu einem 
freien Pin rausgibt. Dort den Logic Analyzer ran hängen und gucken, ob 
die Daten zusammen passen. Das hilft idR, um den Fehler zu lokalisieren.

Pseudo-Code:
1
char PinNr = ...;
2
char Data = vom UART gelesenes Datenwort;
3
4
setPin( PinNr, 0 ); 
5
wait(1us oder 10 Takte oder ähnlich);
6
for (int i=0; i<8;  i++) {
7
  setPin( PinNr, Data & 1); 
8
  Data = Data >> 1;
9
  wait(1us oder 10 Takte oder ähnlich);
10
}
11
setPin( PinNr, 1 );

von S. Landolt (Gast)


Lesenswert?

Läuft der uC wirklich mit 20 MHz? Ich kann in den Bildern die 100 us 
nicht finden. Evtl. mal UCSR0A.FE0 abfragen.

von Mario M. (thelonging)


Lesenswert?

S. Landolt schrieb:
> Läuft der uC wirklich mit 20 MHz?

Der angebliche 100µs-Impuls dauert ca. 6 Bitzeiten, also ungefähr 200µs. 
Im Übrigen ist das mehrfache Lesen von UDR0 zumindest schlechter Stil, 
also besser erst temporär speichern und dann auswerten.

von Maxim B. (max182)


Lesenswert?

Hallo,
für mich ist gleich merkwürdig, daß du USART ohne Puffer machst.

Als Einregung möchte ich hier ein bißchen Code geben. Das ist für AT 
Mega 1284P, aber notwendige Anpassung ist unkompliziert.
1
// usart.h
2
#ifndef USART_H
3
#define USART_H 1
4
5
#define USART_BAUD_MIDI (((F_CPU/16)/31250)-1)
6
#define USART0_SIZE_RX   16
7
#define USART0_SIZE_TX   16
8
void usart0_rxpuff_null(void); // RX puffer => 0
9
void usart0_txpuff_null(void); // TX puffer => 0
10
unsigned char usart0_rxsymbol_count(); // Zustand von RX Puffer
11
unsigned char usart0_txsymbol_count(); // Zustand von TX Puffer
12
void usart0_rxpuffein(unsigned char data); // byte aus RX0 in puffer lesen
13
unsigned char usart0_rxpuffaus(void);  // byte aus Puffer lesen
14
void usart0_txpuff_null(void); // TX puffer = 0
15
void usart0_txpuffein(unsigned char data); // byte in TX puffer lesen
16
void usart0_txpuffaus(void);// byte aus TX Puffer senden, in interrupt tx
17
18
static inline void usart0_init(void){
19
  UBRR0H = (uint8_t)(USART_BAUD_MIDI>>8);
20
  UBRR0L = (uint8_t)USART_BAUD_MIDI;
21
  UCSR0A = 0;
22
  UCSR0B = (1<<RXCIE0)|(1<<RXEN0)|(1<<TXEN0);
23
  UCSR0C = (1<<UCSZ01)|(1<<UCSZ00);
24
  usart0_rxpuff_null();
25
  usart0_txpuff_null();
26
}
27
28
#endif  /* USART_H */
29
30
// usart.c
31
#include <avr/io.h>      // Muss immer sein
32
#include <inttypes.h>     // Fuer Datentyp
33
#include <avr/pgmspace.h>   // Fuer Flash 
34
35
#include "main.h"   // Allgemeine Programmablauf 
36
#include "usart.h"   // USART 
37
#include "lcd.h"  // LCD
38
39
static unsigned char rx_kopf;
40
static unsigned char rx_schwanz;
41
static unsigned char rx_symbol_count;
42
static unsigned char rx_puffer[USART0_SIZE_RX];
43
44
static unsigned char tx_kopf;
45
static unsigned char tx_schwanz;
46
static unsigned char tx_symbol_count;
47
static unsigned char tx_puffer[USART0_SIZE_TX];
48
49
void usart0_rxpuff_null(void){ // puffer = 0
50
  
51
  rx_schwanz = 0; 
52
  rx_kopf = 0;
53
  rx_symbol_count = 0;
54
}
55
56
void usart0_txpuff_null(void){ // puffer = 0
57
  
58
  tx_schwanz = 0; 
59
  tx_kopf = 0;
60
  tx_symbol_count = 0;  
61
}
62
63
unsigned char usart0_rxsymbol_count(){
64
65
  return rx_symbol_count;
66
}
67
68
unsigned char usart0_txsymbol_count(){
69
70
  return tx_symbol_count;
71
}
72
73
static void count_inc(unsigned char *count, unsigned char wert){
74
  (*count)++;
75
  if(*count >= wert){
76
      *count = 0;
77
    }
78
}
79
80
void usart0_rxpuffein(unsigned char data){ // byte aus RX0 in puffer lesen
81
82
  if(rx_symbol_count < USART0_SIZE_RX){  
83
  // Wenn puffer noch Platz hat
84
85
    rx_puffer[rx_schwanz] = data;  
86
    rx_symbol_count++;
87
88
    count_inc(&rx_schwanz, USART0_SIZE_RX);
89
  }  
90
  FLAGG &= ~(1<<F_RX0);  // Flag fuer Empfang auf Null
91
}
92
93
void usart0_txpuffein(unsigned char data){ // byte in TX puffer legen
94
95
  if(tx_symbol_count < USART0_SIZE_TX){  
96
  // Wenn puffer noch Platz hat
97
98
    tx_puffer[tx_schwanz] = data;  
99
    tx_symbol_count++;
100
101
    count_inc(&tx_schwanz, USART0_SIZE_TX);
102
  }
103
}
104
105
unsigned char usart0_rxpuffaus(void){  // byte aus RX Puffer lesen
106
107
  unsigned char data = 0;
108
  data = rx_puffer[rx_kopf];
109
  rx_symbol_count--;
110
  count_inc(&rx_kopf, USART0_SIZE_RX);
111
112
  return data;;
113
}
114
115
void usart0_txpuffaus(void){  // byte aus TX Puffer senden
116
117
  UDR0 = tx_puffer[tx_kopf];
118
  tx_symbol_count--;
119
  count_inc(&tx_kopf, USART0_SIZE_TX);
120
}
121
122
// midi.h
123
#ifndef MIDI_H
124
#define MIDI_H 1
125
126
#define ACTIVE_SENSING 0xfe
127
128
void midi_lcd(unsigned char midi_byte); 
129
// Gibt auf Indikator Leerzeichen und Ziffer
130
131
void midi_puff_schieb(unsigned int data ); 
132
// schieben fuer MIDI: int=2x char und Leerzeichen
133
#endif  /* MIDI_H */ 
134
135
// midi.c
136
#include <avr/io.h>      // Muss immer sein
137
#include <inttypes.h>     // Fuer Datentyp
138
#include <avr/pgmspace.h>   // Fuer Flash 
139
140
#include "main.h"   // Allgemeine Programmablauf
141
#include "lcd.h"  // LCD
142
#include "midi.h"   // MIDI
143
#include "led.h"  // LED, fuer LED_F_NUM
144
145
void midi_lcd(unsigned char midi_byte){ // Gibt auf Indikator Leerzeichen und Ziffer
146
  if(midi_byte != ACTIVE_SENSING){ // Synchro-Byte ausschliessen
147
    unsigned int midizif;
148
    midizif = lcd_ziffer(midi_byte);
149
    midi_puff_schieb(midizif);
150
  } else { // Led F kurz einschalten, wenn ACTIVE_SENSING
151
    led_ein_time((1<<LED_F_NUM),50);
152
  }
153
}
154
155
void midi_puff_schieb(unsigned int data ){ // schieben fuer MIDI: int=2x char und Leerzeichen
156
  unsigned int a;
157
  unsigned char b;
158
  a = (data >> 8);
159
  b = (unsigned char)a;
160
  lcd_puff_schieb_links(b);  // Dec
161
  b = (unsigned char)data;
162
  lcd_puff_schieb_links(b);  // Eins
163
  lcd_puff_schieb_links(' ');  // Leerzeichen
164
}
165
166
// main.h
167
#ifndef MAIN_H
168
#define MAIN_H 1
169
170
#ifndef  F_CPU  // Quarz 16 MHz.
171
#define F_CPU 16000000UL    // CPU_F fuer Verzoegerungen
172
#endif 
173
174
#ifndef FLAGG
175
  #define FLAGG GPIOR0 /* fuer Ereignisse */
176
  #define F_PULS 0  /* Puls 1000 Hz, timer 3 interrupt */
177
  #define F_RX0 1    /* MIDI-Nachricht gekommen */
178
  #define F_TX0 2    /* MIDI-Nachricht versandt */
179
#endif
180
181
/**********************************************************/
182
183
184
#endif  /* MAIN_H */
185
186
// main.c
187
#include <avr/io.h>  // Muss immer sein
188
#include <stdio.h>  // Die Ein- und Ausgabefunktionen, 
189
// Typen und Makros
190
#include <stdlib.h>  // Funktionen zur Umwandlung von Zahlen, 
191
// fuer Speicherverwaltung und aehnliche Aufgaben.
192
#include <string.h>  // Funktionen fuer Zeichenketten
193
#include <inttypes.h>     // Fuer Datentyp
194
#include <avr/interrupt.h>   // Fuer Interrupts
195
#include <avr/eeprom.h>    // Fuer EEPROM
196
#include <avr/pgmspace.h>   // Fuer Flash
197
#include <util/delay.h>    // Fuer Verzoegerungen
198
199
#include "main.h"   // Allgemeine Programmablauf
200
#include "led.h"  // LED
201
#include "knopf.h"   // Knopf
202
#include "drehgeber.h"   // Drehgeber
203
#include "timer.h"   // Systempuls
204
#include "spi.h"   // SPI
205
#include "lcv1024.h"   // NVRAM
206
#include "lcd.h"  // LCD
207
#include "usart.h"   // USART  
208
#include "midi.h"   // MIDI
209
#include "logik.h"  // Logik
210
211
ISR(USART0_RX_vect){ // USART0, Rx Complete
212
213
  FLAGG |= (1<<F_RX0);  // Flag fuer Empfang einsetzen
214
  usart0_rxpuffein(UDR0);  // In Puffer
215
}
216
217
int main(void){
218
219
/******* init *******/
220
221
  system_init();
222
223
  sei();  // Interrupts erlauben 
224
225
/* * * */
226
227
while(1){ 
228
229
  // Bedienung von Puls 1 ms
230
  if( FLAGG & (1<<F_PULS) ){
231
232
    FLAGG &= ~(1<<F_PULS); // Flag fuer Puls = 0
233
    
234
    /* * etwas, was jede 1 ms gemacht sein sollte * */
235
  }
236
237
  // Pruefen, ob TX-Puffer etwas hat.
238
  // Wenn TX-Puffer nicht leer und fruehere Sendung zu Ende
239
  if((usart0_txsymbol_count()) && (UCSR0A & (1<<UDRE0))){    
240
    usart0_txpuffaus();
241
  }
242
243
  
244
245
  /* RX bedienen */ 
246
  if(usart0_rxsymbol_count()){ // wenn rx-Puffer nicht leer
247
    unsigned char data;
248
    data = usart0_rxpuffaus();
249
    if(data != ACTIVE_SENSING){
250
      midi_puff_schieb(lcd_ziffer(data));
251
    } else {
252
      led_ein_time((1<<LED_F_NUM),70);
253
    }
254
  }
255
256
}
257
return 0;
258
}

: Bearbeitet durch User
von T. S. (elektrowiesel)


Lesenswert?

Das Projekt ist ja offensichtlich noch lange nicht fertig :)
Puffer und so kommen später.

Der Controller läuft wirklich mit 20MHz ich habe das flasche Bild 
angehängt bzw. den flaschen Code gepostet. Der Puls auf PB2 ist 
tatsächlich 100µS lang.

Das testen des Frame Error Bits hat auch nichts ergeben. Das scheint zu 
funktionieren.

Ich werde jetzt versuchen die Daten wieder auszugeben.

von T. S. (elektrowiesel)


Lesenswert?

Halt!
Ich habe schon wieder das falsche Programm in den Controller geladen.
Es liegt wirklich nicht an Frame Errors, sondern daran, dass UDR0 zwei 
mal gelesen wird.

Mit dem geänderten Code funktioniert es dann...

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.