AVR UART and MIDI

For Adafruit customers who seek help with microcontrollers

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
Duder
 
Posts: 12
Joined: Sat Jun 27, 2009 11:58 am

AVR UART and MIDI

Post by Duder »

Are there any good tutorials on exactly how to have an AVR microcontroller respond to standard MIDI messages? I've found some resources here and there but nothing that pulls it all together. I noticed the xOxbox code does have some clues, but most of it goes way beyond what I'm interested in right now. I'm only interested in receiving, not transmitting.

Basically what I'm interested learning is setting up the UART registers, and then some ideas for C code to take the incoming bytes and process them accordingly (and be able to ignore running status, MIDI clock, etc).

woody1189
 
Posts: 85
Joined: Sat Jun 21, 2008 11:19 pm

Re: AVR UART and MIDI

Post by woody1189 »


Duder
 
Posts: 12
Joined: Sat Jun 27, 2009 11:58 am

Re: AVR UART and MIDI

Post by Duder »

Cool, Thanks. I have read that thread. I have some code based on that and a bunch of other tutorials I've read (although I've discovered the names of the registers are wrong for the ATMega168). These are the functions I came up with for using the UART/USART:

Code: Select all


void USARTInit(uint16_t ubrr_value)   //Initializes the Universal A/Synchonous Receiver/Transmitter
{
	
	UBRR = ubrr_value;                                  //Sets the Baud/Bit rate
	
	UBRRL = MY_UBRR;                                    //Load lower 8-bits of the baud rate value  
                                                        //into the low byte of the register
	
	UBRRH = (MY_UBRR >> 8);                             // Load upper 8-bits of the baud rate value 
                                                        //into the high byte of the register
	
    
    //(These are the proper register names for the ATMega168)	
	UCSR0C |= (0<<UMSEL00)|(0<<UMSEL01)|(0<<UPM01)|(0<<UPM00)|(0<<USBS0)|(0<<UCSZ02)|(1<<UCSZ01)|(1<<UCSZ00);	
	
	
    //Note: MIDI uses 1 start bit, 8 data bits, and 1 stop bit, so 10 bits total per byte message
    //Set data frame format: asynchronous mode,no parity, 1 stop bit, 8 bit size:
    
		
	UCSRB = (1<<RXEN)|(1<<TXEN);							//Enable The receiver and transmitter
	
}



uint8_t USART_ReceiveByte(){            //This function is used to read the available data from USART. 
                                        //This function will wait until data is available.
	//Wait until data is available
	
	while(!(UCSRA & (1<<RXC)))          //Do nothing until data have been recieved and is ready to be read 
	{                                   //from UDR (wait until data is received)
		//Do nothing
	}
	
    //Now USART has received data from host
    //and is available as a buffer
	
	return UDR;
}



void USART_TransmitByte(uint8_t OutByte){       //This fuction writes the given byte to the
                                                // USART which then transmit it via TX line.
    //Wait until the transmitter is ready
	
	while(!(UCSRA & (1<<UDRE)))                 // Do nothing while waiting for the transmit buffer to empty
	{
		//Do nothing
	}
	
    //Now write the data to the USART buffer
	
	UDR = OutByte;
}

What I'd really like to see is how to use these functions and then apply the MIDI spec to make sense of the data stream.

woody1189
 
Posts: 85
Joined: Sat Jun 21, 2008 11:19 pm

Re: AVR UART and MIDI

Post by woody1189 »

Yeah the register names are usually different for different avr variations (mega, tiny, etc.). As for the MIDI, try this document for some help.

http://www.avrfreaks.net/index.php?func ... ks%20Tools

Duder
 
Posts: 12
Joined: Sat Jun 27, 2009 11:58 am

Re: AVR UART and MIDI

Post by Duder »

Thanks I find that to be a useful introduction. The links don't work, but I found the equivalent:

http://www.midi.org/techspecs/midimessages.php

And this is a good reference I've found as well:

http://www.blitter.com/~russtopia/MIDI/~jglatt/

But what I need to figure out is how to process the MIDI data. An example of what I'm thinking about is something like this:

Code: Select all


InByte = USART_ReceiveByte();				//Bounce the serial input from the USART into variable InByte		
	
	//Need some logic that will take "InByte" and make sense of it, with regard to the MIDI spec
	//It will have to recognize status bytes, data bytes, running status, and clock, etc. and make sense of it 
		
        
        if (InByte <= 0x7F){                              // Range: 0 to 127
            //Then it is a Data Byte
            
            
        }

		if (InByte >= 0x80 && InByte <=0xEF) {            // Range: 128 to 239
            //Then it is a Status Byte
            statusByte = InByte;
            
            if (statusByte >= 0x80 && statusByte <= 0x8F){
                //Then it is a Note Off Event
            }
            else if (statusByte >= 0x90 && statusByte <= 0x9F){
                //Then is is a Note On Event
            }
            else if (statusByte >= 0xA0 && statusByte <= 0xAF){
                //Then is is a Polyphonic Aftertouch
            }
            else if (statusByte >= 0xB0 && statusByte <= 0xBF){
                //Then it is a Control Change
            }
            
            //etc...
            
            
        }
        else if (InByte >= 0xF0 && InByte <= 0xF7){       // Range: 240 to 247
            //Then it is a System Common Message
            //If it is a Sysex Begin (240) need to ignore all bytes until Sysex End (247) is received
            // (should give this priority)
            
        }
        else if (InByte >= 0xF8 && InByte <= 0xFF) {      // Range: 248 to 255
            //The it is a System Realtime Message
            //Ignore it
        }

But I'm not sure about some things. Also, do I need some kind of memory queue that will take the USART input and hold it before processing while other data comes down the line?

User avatar
Franklin97355
 
Posts: 23912
Joined: Mon Apr 21, 2008 2:33 pm

Re: AVR UART and MIDI

Post by Franklin97355 »

But I'm not sure about some things.
It would help if you could tell us what those things are.....

Duder
 
Posts: 12
Joined: Sat Jun 27, 2009 11:58 am

Re: AVR UART and MIDI

Post by Duder »

franklin97355 wrote:
But I'm not sure about some things.
It would help if you could tell us what those things are.....
I suppose you're right... :D

O.K. One of the things I'm wondering about is how to separate the lower four bits (bottom nibble) out of a byte so I can have the program recognize the MIDI channel it's sent on.

The other is the logic scheme above, if this is a good way to do it or if I should use "switch" instead of if/else statements?

Another question is do I have to worry about the UDR getting overwritten with a new byte before the last one was processed?

I looked at the code for the xOxbOx, and noticed how things are done - here's an excerpt:

Code: Select all

void do_midi_mode(void) {
  char c;
  uint8_t last_bank;
  uint8_t note, velocity;

  // turn tempo off!
  turn_off_tempo();

  // show midi addr on bank leds
  clear_bank_leds();
  set_bank_led(midi_in_addr);

  read_switches();
  delay_ms(100);
  read_switches();
  delay_ms(100);
  read_switches();
  last_bank = bank;
  prev_note = 255;        // no notes played yet

  while (1) {
    read_switches();
    if (function_changed) {
      midi_notesoff(); // clear any stuck notes
      return;
    }

    if (last_bank != bank) {
      // bank knob was changed, change the midi address
      midi_in_addr = bank;

      // set the new midi address (burn to EEPROM)
      internal_eeprom_write8(MIDIIN_ADDR_EEADDR, midi_in_addr);

      clear_bank_leds();
      set_bank_led(midi_in_addr);

      last_bank = bank;
    }

    // if theres a char waiting in midi queue...
    if (midi_getch()) {
      // if its a command & either for our address or 0xF,
      // set the midi_running_status
      c = midi_getchar();

      if (c >> 7) {       // if the top bit is high, this is a command
	if ((c >> 4 == 0xF) ||    // universal cmd, no addressing
	    ((c & 0xF) == midi_in_addr)) {  // matches our addr
	  midi_running_status = c >> 4;
	} else {
	  // not for us, continue!
	  midi_running_status = MIDI_IGNORE; 
	  continue;
	}
      }

      switch (midi_running_status) {
      case MIDI_IGNORE:
	{
	  // somebody else's data, ignore
	  break;
	} 
      case MIDI_NOTE_ON:
	{
	  if (c >> 7)  // if the last byte was a command then we have to get the note
	    note = midi_getchar();
	  else
	    note = c;  // otherwise, this was a running status, and c is the note

	  velocity = midi_getchar();
	  /*
	    putstring("MIDI note on (note "); putnum_ud(note);
	    putstring(") (velocity "); putnum_ud(velocity);
	    putstring(")\n\r");
	  */

	  midi_note_on(note, velocity);
	  break;
	}
      case MIDI_NOTE_OFF:
	{
	  if (c >> 7) 
	    note = midi_getchar();
	  else
	    note = c;

	  velocity = midi_getchar();
	  /*
	    putstring("MIDI note off (note "); putnum_ud(note);
	    putstring(") (velocity "); putnum_ud(velocity);
	    putstring(")\n\r");
	  */
	  
	  midi_note_off(note, velocity);
	  
	  break;
	} 
      case MIDI_PITCHBEND:
	{
	  //putstring("MIDI Slide\n\r");

	  break;
	}
      default:
	/*putstring("Received Unknown MIDI: 0x"); putnum_uh(c); 
	  putstring("\n\r"); */
	break;
      }
    }
  }
} 

 // midi handling code!

uint8_t midi_recv_cmd(void) {
  uint8_t c;

  if (midi_getch()) {
    c = midi_getchar();
    if (c >> 7) {       // if the top bit is high, this is a command
      if (c >> 4 == 0xF)     // universal cmd, no addressing
	return c;
      
      if ((c & 0xF) == midi_in_addr) {
	midi_running_status = c >> 4;
	return c;
      }
    }
  }
  return 0;
}

int midi_getch(void) {     // checks if there is a character waiting!
  if (head_idx != tail_idx)
    return 1;
  return 0;
}


int midi_getchar(void) {
  char c;

  while (head_idx == tail_idx);

  cli();
  c = midi_q[head_idx++];
  head_idx %= MIDI_Q_SIZE;
  sei();

  return c;
}

In my case things are a bit simpler since I don't need to respond to notes, I'm only interested in control changes - but I'd like to understand what is going on in that code as I think it would be helpful.

User avatar
karlgg
 
Posts: 212
Joined: Sat Dec 27, 2008 2:41 pm

Re: AVR UART and MIDI

Post by karlgg »

The part that goes "(c & 0xF) == midi_in_addr" is parsing the lower 4 bits to extract the address. "0xF" is hexadecimal for "00001111" (binary), which when AND'd with another byte wipes out the top four bits (with the zeros), leaving the bottom four intact.

Duder
 
Posts: 12
Joined: Sat Jun 27, 2009 11:58 am

Re: AVR UART and MIDI

Post by Duder »

karlgg wrote:The part that goes "(c & 0xF) == midi_in_addr" is parsing the lower 4 bits to extract the address. "0xF" is hexadecimal for "00001111" (binary), which when AND'd with another byte wipes out the top four bits (with the zeros), leaving the bottom four intact.
Ahah I see! Well that's a very cool trick, thanks!

Duder
 
Posts: 12
Joined: Sat Jun 27, 2009 11:58 am

Re: AVR UART and MIDI

Post by Duder »

OK so taking a step back from the MIDI handling code, I've been trying to get the ATMega168 to respond to MIDI signals. I built a MIDI port and hooked it up, tried to configure the USART but nothing happens.

I did successfully flash over some firmware, configure the fuses for external crystal (which I attached) and it seems to be running at 16MHz. I can program all kinds of blinky LED patterns fine but as soon as I program it to respond (flash an LED - just do something so I can tell) if MIDI is received, it does nothing...

Any ideas for a good way to test that the USART is receiving? I

Duder
 
Posts: 12
Joined: Sat Jun 27, 2009 11:58 am

Re: AVR UART and MIDI

Post by Duder »

OK so taking a step back from the MIDI handling code, I've been trying to get the ATMega168 to respond to MIDI signals. I built a MIDI port and hooked it up, tried to configure the USART but nothing happens.

I did successfully flash over some firmware, configure the fuses for external crystal (which I attached) and it seems to be running at 16MHz. I can program all kinds of blinky LED patterns fine but as soon as I program it to respond (flash an LED - just do something so I can tell) if MIDI is received, it does nothing...

Any ideas for a good way to test that the USART is receiving? Here's the code I've been using:

Code: Select all

/* Name: main.c
 * Author: <insert your name here>
 * Copyright: <insert your copyright message here>
 * License: <insert your license reference here>
 */

#include <avr/io.h>
#include <avr/interrupt.h>
#include <inttypes.h>
#include <util/delay.h>

#define F_CPU 16000000					// 16MHz 
#define BAUD 31250						// 3.125 KBaud is Baud Rate or Bit Rate
#define MYUBRR ((F_CPU/16/BAUD)-1)      // UART Baud Rate Register (UBRR) = (F_CPU/16/BAUD_RATE)-1  ( = 31 in this case)



int set_PORTC_bit(int, int);
void USARTInit(uint16_t);
uint8_t USART_ReceiveByte();
void USART_TransmitByte(uint8_t);
int check_Sysex_status(uint8_t);
void delay_ms(uint16_t x); // general purpose delay


int main(void)
{
    
    uint8_t volatile InByte;
    
    
    
    /* insert your hardware initialization here */
    
    USARTInit(MYUBRR);

    //DDRB = 0b11101111; //PB4 = MISO 
    DDRC = 0xFF;		//Note: 0xFF = 11111111 so this sets all bits/pins on Port C as outputs	  
    DDRD = 0b00111110;  //PORTD (RX on PD0)
    
    
    //Startup Test Sequence
    
    PORTC = 0b0001;
    
    delay_ms(800);
    
    PORTC = 0b0011;
    
    delay_ms(800);
    
    PORTC = 0b0111;
    
    delay_ms(800);
    
    PORTC = 0b1111;
    
    delay_ms(1600);
    
    PORTC = 0b0000;
    
    
    
    
    
    for(;;){
        /* insert main loop code here */
        
        
        InByte = USART_ReceiveByte();
        
        if(InByte > 0){
         
            //Do Something!
            
            PORTC = 0b1111;
            
            delay_ms(100);
            
            PORTC = 0b0000;
            
            delay_ms(100);
            
            PORTC = 0b1111;
            
            delay_ms(100);
            
            PORTC = 0b0000;
            
            delay_ms(100);
            
            PORTC = 0b1111;
            
            delay_ms(100);
            
            PORTC = 0b0000;
            
            
        }
            
                
            
       
        
    }
    
    return 0;   /* never reached */
}


int set_PORTC_bit(int position, int value)      // Sets or clears the bit in position 'position'
{                                               // either high or low (1 or 0) to match 'value'.
    // Leaves all other bits in PORTB unchanged.
	if (value == 0)
	{
		PORTC &= ~(1 << position);      // Set bit # 'position' low
	}
	else
	{
		PORTC |= (1 << position);       // Set bit # 'position' high
	}
	return 1;
}





void USARTInit(uint16_t ubrr_value)   //Initializes the Universal A/Synchonous Receiver/Transmitter
{
	
	UBRR0 = ubrr_value;                                 //Sets the Baud/Bit rate
	
	UBRR0L = ubrr_value;                                   //Load lower 8-bits of the baud rate value  
    //into the low byte of the register
	
	UBRR0H = (ubrr_value >> 8);                            // Load upper 8-bits of the baud rate value 
    //into the high byte of the register
	
    
    //UCSR0C |=  (1 << UCSZ01) | (1 << UCSZ00) ;					
	
    
    //Note: MIDI uses 1 start bit, 8 data bits, and 1 stop bit, so 10 bits total per byte message
    //Set data frame format: asynchronous mode,no parity, 1 stop bit, 8 bit size
    
	UCSR0C |= (0<<UMSEL00)|(0<<UMSEL01)|(0<<UPM01)|(0<<UPM00)|(0<<USBS0)|(0<<UCSZ02)|(1<<UCSZ01)|(1<<UCSZ00);	
	
    
	UCSR0B = (1<<RXEN0)|(0<<TXEN0);						//Enable The receiver not  transmitter
	
}


uint8_t USART_ReceiveByte(){            //This function is used to read the available data from USART. 
    
	//Wait until data is available
	
	while(!(UCSR0A && (1<<RXC0)))        //Do nothing until data have been recieved and is ready to be read 
	{                                   //from UDR (wait until data is received)
		//(Do nothing)
	}
	
    //Now USART has received data from host
    //and is available as a buffer
	
	return UDR0;
}



void USART_TransmitByte(uint8_t OutByte){       //This fuction writes the given byte to the
    // USART which then transmits it via TX line.
    //Wait until the transmitter is ready
	
	while(!(UCSR0A && (1<<UDRE0)))               // Do nothing while waiting for the transmit buffer to empty
	{
		//(Do nothing)
	}
	
    //Now write the data to the USART buffer
	
	UDR0 = OutByte;
}


int check_Sysex_status(uint8_t InByte){
    
    int sysex_toggle_state = 0;     //Initialize Flag to indicate Sysex message
    
    if (InByte == 0xF0){            //Sysex Begin Message Received (240)
        
        sysex_toggle_state = 1;     //Flag indicates a Sysex Begin has been received
    }
    else if (InByte == 0xF7){       //Sysex End Message Received (247)
        
        sysex_toggle_state = 0;     //Flag indicates Sysex End received
    }
    else {}                         // toggle state remains 0 
    
    return sysex_toggle_state;
}

//General short delays
void delay_ms(uint16_t x)
{
    uint8_t y, z;
    for ( ; x > 0 ; x--){
        for ( y = 0 ; y < 80 ; y++){
            for ( z = 0 ; z < 40 ; z++){
                asm volatile ("nop");
            }
        }
    }
}




Duder
 
Posts: 12
Joined: Sat Jun 27, 2009 11:58 am

Re: AVR UART and MIDI

Post by Duder »

Well so far my ATMega168 is not correctly receiving MIDI signals. Only with MIDI pin 5 is grounded out and the ISP attached will it recognize incoming bytes, but even so it is not reading the correct byte values!

Duder
 
Posts: 12
Joined: Sat Jun 27, 2009 11:58 am

Re: AVR UART and MIDI

Post by Duder »

Well I thought it might help if I post a schematic of the circuit I'm using (attached).
Attachments
Schematic
Schematic
midi_receiver.gif (112.43 KiB) Viewed 7864 times

Locked
Please be positive and constructive with your questions and comments.

Return to “Microcontrollers”