Full Version : Capacitive Keyboard MIDI (AVR)
avr >>SOUND & MUSIC PROJECTS >>Capacitive Keyboard MIDI (AVR)


AVR_Admin- 05-09-2006
Capacitive sensor based keyboard with MIDI

Siddharth Gauba (sg334) and Byron Singh (bs262)

The objective of this project was to build a keyboard based on capacitive sensors and then use the MCU to create MIDI encodings for all notes played. The output from the sensors is detected by the MCU using its ADC capability. The sound is played directly by the MCU via a piezo speaker using Pulse-width modulation (PWM). The MCU communicates via serial protocol with an executable interface that has the capability to record MIDI format songs, play them using an integrated Windows Media Player control, and modify them using a hex editor interface.

High level design

The design consists of an MCU, a front end Visual Basic interface, an LCD display, capacitive sensing based keyboard, a piezo speaker, and an LED display panel. A block diagram schematic is displayed below. This idea grew out of an idea to make a full-scale computer keyboard using capacitive sensors. It was deemed that it would be nice to have a way of recording one's own music compositions and playing in a convenient, portable MIDI file.

The project is a musical instrument with capacitive sensor input. When a person’s finger is brought close to the electrode attached to the capacitive sensor, a voltage is produced by the capacitive sensor to indicate a button press. The input interface of this musical instrument is electrodes with different area sizes. These different areas produce different capacitive sensor reading and can be detected by the ADC in MEGA32 to decide on the keys pressed. The keyboard consists of 8 keys corresponding to 8 notes on 1 octave. There is an additional button on the top left side of the keyboard which allows user to switch to a different octave.

The notes played will then be converted into midi format and sent to the PC through serial communication. At the PC, a visual basic program is used to receive midi data and save these data in a midi file. The midi file can be played using any software capable of playing the standard midi format file.

As an add-on to the instrument, there is an LCD screen which shows the current and previous notes pressed. It also displays useful information about calibration status, ADC reading, etc. To make our project more interesting, there is a visualization add-on built from LEDs. When a note is pressed, corresponding LEDs light up and decay slowly.

Link: http://instruct1.cit.cornell.edu/courses/e...bs262/index.htm

CODE

#pragma regalloc-
#pragma optsize-
                   
#include <Mega32.h>  
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <delay.h>  


#define lineTime 1018 // a general counter
#define treshold 50  
#define zero 0x00
#define one 0xff
#define muxdelay 15   // time to switch channel in capacitive sensor

#define th1 20       // treshold for the keys
#define th2 60
#define th3 100
#define th4 140
#define range 20

#define begin {
#define end   }

#pragma regalloc+

int LineCount;
char Ain0;   //raw A to D number
char Ain1;
char lastAin0[5];       //array to store previous 5 ADC values
char lastAin1[5];
int lcdnotes[4];        //array to store previous 4 notes pressed
char lcd_buffer[5];     //string used for lcd display

unsigned char t_index;  //current string index
unsigned char t_ready;  //flag for transmit done
unsigned char t_char;  //current character

//variables for midi encoding
int ntt;        //note time
int nt;         //note
int nton;       //note on/off status
int lastnt;     //previous note
int lastnton;   //previous note on/off status
int index;      //general index
int preindex;
int muxcounter; //counter for switching channel
int time;       //time
int c1pressed;  //channel 1 pressed
int c2pressed;  //channel 2 pressed

//variables for calibration
int calibration;//boolean for calibration mode
int caltime;    //counter for calibration
int caltemp;    //temperory value for calibration
int calnote[8]; //array for tresholds after calibration

int i;          //used for loops
int octave;     //1 = high octave, 0 = low octave

#asm
   .equ __lcd_port=0x15
#endasm
#include <lcd.h> // LCD driver routines

//Musical note values
//C below middle C to  C above middle C
//zeros are rests

flash char notes[] = {0,240,212,188,180,160,142,126,120,106,94,90,80,71,63,60};
char note, musicT;
char channel;
                   
//**********************************************************
//  -- nonblocking print: initializes ISR-driven
// transmit. This routine merely sets up the ISR, then
//send one character, The ISR does all the work.  
void puts_int(void)
begin
 t_ready=0;
 t_index=0;
 UCSRB.5=1;
end

/**********************************************************/
//UART xmit-empty ISR
interrupt [USART_DRE] void uart_send(void)
begin
UDR = t_char;     //send the char
UCSRB.5=0; //kill isr
t_ready=1; //transmit done
end

//==================================
//This is the sync generator and raster generator. It MUST be entered from
//sleep mode to get accurate timing of the sync pulses
interrupt [TIM1_COMPA] void t1_cmpA(void)  
begin

 //update the curent scanline number
 LineCount ++;  

 //start new frame after line 262
 if (LineCount==263)
 {  
    time++;
    caltime++;
    LineCount = 1;
 }
   
end

//=================================
//a fucntion to encode time value to be stored in midi format
void WriteVarLen (long value)
{
   long buffer;
   buffer = value & 0x7f;
   while ((value >>= 7) > 0)
   {
       buffer <<= 8;
       buffer |= 0x80;
       buffer += (value & 0x7f);
   }
   while (1)
   {    
       char output;
       output = buffer&0xff;
       if(output == 0)
         output = zero;
       while(t_ready == 0)
       {;}
       t_char = output;
       puts_int();
       if (buffer & 0x80)
           buffer >>= 8;
       else
           break;
   }
}

//==================================
//function for lighting LED
void light(int note)
{
 if(note == 1)
   PORTB.0 = 1;
 else
   PORTB.0 = 0;

 if(note == 2)
   PORTB.1 = 1;
 else
   PORTB.1 = 0;

 if(note == 3)
   PORTB.2 = 1;
 else
   PORTB.2 = 0;

 if(note == 4)
   PORTB.4 = 1;
 else
   PORTB.4 = 0;

 if(note == 5)
   PORTB.5 = 1;
 else
   PORTB.5 = 0;

 if(note == 6)
   PORTB.6 = 1;
 else
   PORTB.6 = 0;

 if(note == 7)
   PORTB.7 = 1;
 else
   PORTB.7 = 0;
}


//=========================
//function to send the appropriate encoded characters
//after each note press
void shownote()
{
 char t,ton;
 
 if(nt == 0)
    t = 0x3c;
 else if (nt == 1)
    t = 0x3c;
 else if (nt == 2)
    t = 0x3e;
 else if (nt == 3)
    t = 0x40;
 else if (nt == 4)
    t = 0x41;  
   
 else if (nt == 5)
    t = 0x43;
 else if (nt == 6)
    t = 0x45;
 else if (nt == 7)
    t = 0x47;
 else if (nt == 8)
    t = 0x48;
   
 if(octave == 0)
 {
   t -= 0x0c;
 }
       
 if(nton == 0)
    ton = zero;
 else
    ton = 0x40;
   
 WriteVarLen(ntt);
 while(t_ready == 0){;} t_char = 0x90; puts_int();
 while(t_ready == 0){;} t_char = t;    puts_int();
 while(t_ready == 0){;} t_char = ton;  puts_int();

}
//==================================
// print note on LCD
void printlcd(int i)
{
 if(i == 1)
   lcd_putsf("C");
 if(i == 2)
   lcd_putsf("D");
 if(i == 3)
   lcd_putsf("E");
 if(i == 4)
   lcd_putsf("F");
 if(i == 5)
   lcd_putsf("G");
 if(i == 6)
   lcd_putsf("A");
 if(i == 7)
   lcd_putsf("B");
 if(i == 8)
   lcd_putsf("C");
 if(i == 0)
   lcd_putsf("X");    
}



//==================================
// set up the ports and timers
void main(void)
{                    
 DDRA=0xf8;
 DDRD=0x0f;
 DDRB=0xff;
 PORTC = channel;
 //enable ADC
 ADMUX = 0b11100000;
 ADCSR = 0b11000111;
                   
 UCSRB = 0x18;
 UBRRL = 103;      
 
 //init timer 1 to generate sync
 OCR1A = lineTime;  //One NTSC line
 TCCR1B = 9;   //full speed; clear-on-match
 TCCR1A = 0x00; //turn off pwm and oc lines
 TIMSK = 0x10;  //enable interrupt T1 cmp
 
 //initialize synch constants
 LineCount = 1;
 
 lcd_init(16);       //initialize the display
 lcd_clear();         //clear the display
 //init musical scale
 note = 0;
 musicT = 0;
 //use OC0 (pin B.3) for music  
 DDRB.3 = 1;    

 t_ready=1;
 t_char = 250;
 time = 0;    
 index = 1;
 preindex = 0;
 calibration = 1;
 
 lastnt = 0;
 lastnton = 0;
 muxcounter = 0;

 channel = 0x01;
 lastAin0[0] = 0;
 lastAin0[1] = 0;
 lastAin0[2] = 0;
 lastAin0[3] = 0;
 lastAin0[4] = 0;
 lastAin1[0] = 0;
 lastAin1[1] = 0;
 lastAin1[2] = 0;
 lastAin1[3] = 0;
 lastAin1[4] = 0;  
 Ain0 = 255;
 Ain1 = 255;
 
 #asm ("sei");
 lcd_putsf("line 3");  //string from flash

 //calibration for the 8 keys
 for(i=0; i<8;i++)
 {
   lcd_clear();
   lcd_gotoxy(0,1);
   lcd_putsf("Calibrating");
   caltime = 0;
   while(caltime < 50)
   {
     lcd_gotoxy(0,0);
     lcd_putsf("Ready");
   }
   lcd_gotoxy(0,0);
   if(i == 0)
   {
     channel = 0x01;
     lcd_putsf("Press C");
   }
   if(i == 1)
     lcd_putsf("Press D");
   if(i == 2)
     lcd_putsf("Press E");
   if(i == 3)
     lcd_putsf("Press F");
   if(i == 4)
   {
     channel = 0x02;
     lcd_putsf("Press G");
   }
   if(i == 5)
     lcd_putsf("Press A");
   if(i == 6)
     lcd_putsf("Press B");
   if(i == 7)
     lcd_putsf("Press C");              
   PORTA.3 = 0;
   PORTA.4 = 0;
   PORTA.5 = 0;
   PORTA.6 = channel & 0x02;
   PORTA.7 = channel & 0x01;
   caltime = 0;
   caltemp = 120-(i%4)*30;
   while(caltime < 200)
   {
     if (LineCount==231)
     {
       Ain0 = ADCH;
       if(Ain0 < 10)
 sprintf(lcd_buffer,"00%d",Ain0);
 else if(Ain0 < 100)
 sprintf(lcd_buffer,"0%d",Ain0);
 else
 sprintf(lcd_buffer,"%d",Ain0);
    lcd_gotoxy(12,0);
      lcd_puts(lcd_buffer);
       caltemp = (caltemp + Ain0)/2; //gets average value
       if(caltemp < 10)
 sprintf(lcd_buffer,"00%d",caltemp);
 else if(caltemp < 100)
 sprintf(lcd_buffer,"0%d",caltemp);
 else
       sprintf(lcd_buffer,"%d",caltemp);  
    lcd_gotoxy(12,1);
      lcd_puts(lcd_buffer);          
       ADCSR.6=1;
     }
     if(caltime < 40)
     {
       //do nothing
     }
     else if(caltime < 80)
     {
       lcd_gotoxy(7,0);
       lcd_putsf(".");
     }
     else if(caltime < 120)
     {
       lcd_gotoxy(8,0);
       lcd_putsf(".");
     }
     else if(caltime < 160)
     {
       lcd_gotoxy(9,0);
       lcd_putsf(".");
     }
     else if(caltime < 200)
     {
       lcd_gotoxy(10,0);
       lcd_putsf(".");
     }
   }
   calnote[i] = caltemp;
 }
 
 lcd_clear();
 lcd_gotoxy(0,0);
 lcd_putsf("Notes:");

 //the main loop
 while(1)
 {  
   if (LineCount==231)
   {
       //Play a note
      //TCCR0 bits:
      //bit 7 no force:
      //bit 3 CTC on:
      //bit 5,4 toggle OC0 pin  
      //bit 6 no PWM
      //bit 2-0 prescaler 256 so 1 tick is 16 microsec
     musicT = 0;          
     TCCR0 = 0;
     if(notes[note] != 0)
       TCCR0 = 0b00011100;
     OCR0 = notes[note+7*octave];  //get the appropriate octave value
 
     
      //codes for channel 3
      //channel 3 is used for switching between high and low octave
      if(channel == 0x03 && muxcounter > muxdelay-5)    
      {
        if(ADCH > 50)
        {          
          octave = 1;
          lcd_gotoxy(14,0);
          lcd_putsf("H");
        }
        else
        {
          octave = 0;
          lcd_gotoxy(14,0);
          lcd_putsf("L");
        }
      }
      //codes for channel 1
      //channel 1 is used for input of the first 4 notes
      if(channel == 0x01 && muxcounter > muxdelay-5)    
      {
        lastAin0[4]=lastAin0[3];
        lastAin0[3]=lastAin0[2];
        lastAin0[2]=lastAin0[1];
        lastAin0[1]=lastAin0[0];
        lastAin0[0]=ADCH;

        if(lastAin0[0] < lastAin0[1] && lastAin0[1] < lastAin0[2] && lastAin0[2] < lastAin0[3])
        {
          //do nothing
        }
        else if(lastAin0[0] > lastAin0[1] && lastAin0[1] > lastAin0[2] && lastAin0[2] > lastAin0[3])
        {
          //do nothing
        }
        else
        {
          Ain0 = lastAin0[0];
          if(Ain0 < 10)
    sprintf(lcd_buffer,"00%d",Ain0);
    else if(Ain0 < 100)
    sprintf(lcd_buffer,"0%d",Ain0);
    else
    sprintf(lcd_buffer,"%d",Ain0);  
    lcd_gotoxy(10,0);
         lcd_puts(lcd_buffer);
        }
             
        if(0<Ain0 && Ain0<(calnote[3]+calnote[2])/2)
        {
          note=4;
          c1pressed = 1;
        }
        else if((calnote[3]+calnote[2])/2<Ain0 && Ain0<(calnote[2]+calnote[1])/2)
        {
          note=3;      
          c1pressed = 1;
        }
        else if((calnote[2]+calnote[1])/2<Ain0 && Ain0<(calnote[1]+calnote[0])/2)
        {
          note=2;      
          c1pressed = 1;
        }
        else if((calnote[1]+calnote[0])/2<Ain0 && Ain0<200)
        {
          note=1;      
          c1pressed = 1;
        }
        else
        {
          c1pressed = 0;    
        }
      }
      //codes for channel 2
      //channel 2 is used for input of the last 4 keys
      if(channel == 0x02 && muxcounter > muxdelay-5)      
      {
        lastAin1[4]=lastAin1[3];
        lastAin1[3]=lastAin1[2];
        lastAin1[2]=lastAin1[1];
        lastAin1[1]=lastAin1[0];
        lastAin1[0]=ADCH;
        if(lastAin1[0] < lastAin1[1] && lastAin1[1] < lastAin1[2] && lastAin1[2] < lastAin1[3])
        {
          //do nothing
        }
        else if(lastAin1[0] > lastAin1[1] && lastAin1[1] > lastAin1[2] && lastAin1[2] > lastAin1[3])
        {
          //do nothing
        }
        else
        {
          Ain1 = lastAin1[0];  
          if(Ain1 < 10)
    sprintf(lcd_buffer,"00%d",Ain1);
    else if(Ain1 < 100)
    sprintf(lcd_buffer,"0%d",Ain1);
    else
    sprintf(lcd_buffer,"%d",Ain1);
    lcd_gotoxy(10,0);
         lcd_puts(lcd_buffer);
        }
       
        if(0<Ain1 && Ain1<(calnote[7]+calnote[6])/2)
        {
          note=8;
          c2pressed = 1;
        }
        else if((calnote[7]+calnote[6])/2<Ain1 && Ain1<(calnote[6]+calnote[5])/2)
        {
          note=7;      
          c2pressed = 1;
        }
        else if((calnote[6]+calnote[5])/2<Ain1 && Ain1<(calnote[5]+calnote[4])/2)
        {
          note=6;      
          c2pressed = 1;
        }
        else if((calnote[5]+calnote[4])/2<Ain1 && Ain1<200)
        {
          note=5;      
          c2pressed = 1;
   
        }
        else
        {
          c2pressed = 0;    
        }
      }
      if(c1pressed == 0 && c2pressed == 0)
        note = 0;      
 
      light(note);   //display lights on LED panel
     
      //the following codes determine whether notes have to be sent to serial
      //comm. for midi endocing
      if(note == 0)
      {
         if(lastnton == 0)
         {
              //do nothing
         }                
         else
         {
              ntt = time;
              nt = lastnt;
              nton = 0;
              lastnton = 0;
              lastnt = nt;
              shownote();
              time = 0;
         }
      }              
      else
      {
         if(lastnt == note && lastnton == 1)
         {
              //do nothing
         }                
         else
         {
              ntt = time;
              nt = lastnt;
              nton = 0;
              shownote();
              time = 0;
              ntt = time;
              nt = note;
              nton = 1;
              lastnton = 1;
              lastnt = note;
              shownote();
              time = 0;
         
              lcdnotes[3] = lcdnotes[2];
              lcdnotes[2] = lcdnotes[1];
              lcdnotes[1] = lcdnotes[0];
              lcdnotes[0] = note;
             
              lcd_gotoxy(2,1);
              printlcd(lcdnotes[3]);
              lcd_gotoxy(4,1);
              printlcd(lcdnotes[2]);
              lcd_gotoxy(6,1);
              printlcd(lcdnotes[1]);
              lcd_gotoxy(8,1);
              printlcd(note);
         }
      }
 
     
      //increment timer for channel switching
      muxcounter++;
      //start another conversion
      ADCSR.6=1;
    }  //line 231
    if(muxcounter == muxdelay)
    {
      muxcounter = 0;
      channel += 0x01;
      if(channel > 0x03)
        channel = 0x01;  
 
      //set appropriate values for capacitive sensor channel selection  
      PORTA.3 = 0;
      PORTA.4 = 0;
      PORTA.5 = 0;
      PORTA.6 = channel & 0x02;
      PORTA.7 = channel & 0x01;    
    }
 }  //while
}  //main





Forumer™ is Voted #1 Free Forum Hosting provider
Build your own community today with the largest message board hosting company.