Polyphonic Keypad

Inspired by Paul Leamy’s dsPIC musical instrument, the microKey, I’ve just created a little polyphonic instrument of my own using a 12-button keypad that Richard Hayes gave me a couple of months ago.

The code is set up to scan the whole keyboard over and over, one column at a time, so it can detect as many concurrent keypresses as you like. However, the audio output is limited to three simultaneous notes, because I’m using one PWM channel and the two output compare channels to produce three squarewaves at independent frequencies.

The code can probably still use a little tidying up, but it’s working. Here it is:

//
// keyboard.c - Music keyboard example for dsPIC
// Written by Ted Burke
// Last updated 14-11-2012
//

#include <xc.h>
#include <libpic30.h>
#include <stdio.h>

// These are the pins for the keypad.
// Rows are inputs, columns are outputs.
#define ROW1 _RB5
#define ROW2 _RB0
#define ROW3 _RB1
#define ROW4 _RB3
#define COL1 _LATB4
#define COL2 _LATB6
#define COL3 _LATB2

// Configuration settings
_FOSC(CSW_FSCM_OFF & FRC_PLL16); // Fcy = 30 MIPS
_FWDT(WDT_OFF);					 // Watchdog timer off
_FBORPOR(MCLR_DIS);				 // Disable reset pin

// Function prototypes
void scan_keys();

// Array to store state of 12 keys
int keys[] = {0,0,0,0,0,0,0,0,0,0,0,0};
int symbols[] = {'0', '1', '2', '3',
				 '4', '5', '6', '7',
				 '8', '9', '*', '#'};

// Array of frequencies for 12 keys
// C4,C#4,D4,D#4,E4,F4,F#4,G4,G#4,A4,A#4,B4
double f[] =   {261.63, 277.18, 293.66, 311.13,
				329.63, 349.23, 369.99, 392.00,
				415.30, 440.00, 466.16, 493.88};

int main()
{
	int k;
	
	// Configure digital i/o
	COL1 = 1; COL2 = 1; COL3 = 1;
	ADPCFG = 0b01111111; // Disable AN0-AN6
	TRISB = 0b10101011; // columns are outputs
	TRISD = 0b0000; // All outputs on Port D
	CNPU1 = 0b11111100; // enable pull-ups on RB0-5

	// Configure PWM
	PWMCON1 = 0x00FF; // supposedly the default, but seems to need to be set
	PTCONbits.PTCKPS = 2; // prescale=1:16 (0=1:1, 1=1:4, 2=1:16, 3=1:64)
	PTPER = 1875; // 1000Hz
	PDC1 = 0; // Silence initially
	PTMR = 0;
	PTCONbits.PTEN = 1;   // Enable PWM time base	

	// Configure output compare channel 1
	T2CONbits.TCKPS = 0b01; // 1:8 prescale
	T2CONbits.TON = 1;		// Enable Timer 2
	OC1CONbits.OCTSEL = 0;  // Use Timer 2 for OC1
	OC1CONbits.OCM = 0b101; // Continuous pulse mode
	PR2 = 7500; OC1R = 0; OC1RS = 2;
	
	// Configure output compare channel 2
	T3CONbits.TCKPS = 0b01; // 1:8 prescale
	T3CONbits.TON = 1;		// Enable Timer 3
	OC2CONbits.OCTSEL = 1;  // Use Timer 3 for OC2
	OC2CONbits.OCM = 0b101; // Continuous pulse mode
	PR3 = 7500; OC2R = 0; OC2RS = 2;
	
	// Configure UART
	U1BRG = 48;            // 38400 baud @ 30 MIPS
	U1MODEbits.UARTEN = 1; // Enable UART

	while(1)
	{
		// scan keyboard
		scan_keys();
		
		// Print keypad state via UART
		for (k=0 ; k<12 ; ++k)
		{
			if (keys[k]) printf("_%c_", symbols[k]);
			else printf(" %c ", symbols[k]);
		}
		printf("\n");

		// Find and play first note
		k = 0;
		while(k<12 && keys[k]==0) k++;
		if (k<12)
		{
			PTPER = 1875000.0/f[k];
			PDC1 = PTPER;
		}
		else PDC1 = 0;
		
		// Find and play second note
		k++;
		while(k<12 && keys[k]==0) ++k;
		if (k<12)
		{
			PR2 = 3750000.0/f[k];
			OC1RS = PR2/2;
		}
		else OC1RS = 2;
		
		// Find and play third note
		k++;
		while(k<12 && keys[k]==0) k++;
		if (k<12)
		{
			PR3 = 3750000.0/f[k];
			OC2RS = PR3/2;
		}
		else OC2RS = 2;
		
		// LED on if key is pressed
		_LATD2 = 0;
		for (k=0 ; k<12 ; ++k) if (keys[k]) _LATD2 = 1;
		
		// 10ms delay
		__delay32(30000);
	}
}

//
// This function scans the entire keyboard and
// stores the state of each key to an array.
//
// The columns are activated one at a time,
// with a 100us delay after each activation
// and deactivation to allow relavant digital
// input voltages to change before reading.
//
void scan_keys()
{
	// Scan first column
	COL1 = 0; __delay32(3000);
	if (!ROW1) keys[1] = 1; else keys[1] = 0;
	if (!ROW2) keys[4] = 1; else keys[4] = 0;
	if (!ROW3) keys[7] = 1; else keys[7] = 0;
	if (!ROW4) keys[10] = 1; else keys[10] = 0;
	COL1 = 1; __delay32(3000);

	// Scan second column
	COL2 = 0; __delay32(3000);
	if (!ROW1) keys[2] = 1; else keys[2] = 0;
	if (!ROW2) keys[5] = 1; else keys[5] = 0;
	if (!ROW3) keys[8] = 1; else keys[8] = 0;
	if (!ROW4) keys[0] = 1; else keys[0] = 0;
	COL2 = 1; __delay32(3000);

	// Scan first column
	COL3 = 0; __delay32(3000);
	if (!ROW1) keys[3] = 1; else keys[3] = 0;
	if (!ROW2) keys[6] = 1; else keys[6] = 0;
	if (!ROW3) keys[9] = 1; else keys[9] = 0;
	if (!ROW4) keys[11] = 1; else keys[11] = 0;
	COL3 = 1; __delay32(3000);
}
This entry was posted in Uncategorized and tagged , , , , , , , , , , , , , , , , , , , . Bookmark the permalink.

3 Responses to Polyphonic Keypad

  1. paulleamy says:

    Hi Ted,

    Just took a quick video of the keyboard working in class today!

    Having too much fun with this, would love to give it a shot using more of the output compare pins. Had too make a few changes in the “scan_keys” function to suit my own keyboard but no problems now, was messing about with a pitch bend function for a bit more expression. Here is the scan_keys function with the changes,

    
    void scan_keys()
    {
    	// Scan first column
    	COL1 = 0; __delay32(3000);
    	if (!ROW1) keys[0] = 1; else keys[0] = 0;
    	if (!ROW2) keys[3] = 1; else keys[3] = 0;
    	if (!ROW3) keys[6] = 1; else keys[6] = 0;
    	if (!ROW4) keys[9] = 1; else keys[9] = 0;
    	COL1 = 1; __delay32(3000);
    
    	// Scan second column
    	COL2 = 0; __delay32(3000);
    	if (!ROW1) keys[1] = 1; else keys[1] = 0;
    	if (!ROW2) keys[4] = 1; else keys[4] = 0;
    	if (!ROW3) keys[7] = 1; else keys[7] = 0;
    	if (!ROW4) keys[10] = 1; else keys[10] = 0;
    	COL2 = 1; __delay32(3000);
    
    	// Scan first column
    	COL3 = 0; __delay32(3000);
    	if (!ROW1) keys[2] = 1; else keys[2] = 0;
    	if (!ROW2) keys[5] = 1; else keys[5] = 0;
    	if (!ROW3) keys[8] = 1; else keys[8] = 0;
    	if (!ROW4) keys[11] = 1; else keys[11] = 0;
    	COL3 = 1; __delay32(3000);
    }
    
    

    Again really appreciate you working out the “poly” end of things!

  2. paulleamy says:

    For some reason half the stuff I had in that last post never showed up!

    Code change:

    void scan_keys()
    {
    // Scan first column
    COL1 = 0; __delay32(3000);
    if (!ROW1) keys[0] = 1; else keys[0] = 0;
    if (!ROW2) keys[3] = 1; else keys[3] = 0;
    if (!ROW3) keys[6] = 1; else keys[6] = 0;
    if (!ROW4) keys[9] = 1; else keys[9] = 0;
    COL1 = 1; __delay32(3000);

    // Scan second column
    COL2 = 0; __delay32(3000);
    if (!ROW1) keys[1] = 1; else keys[1] = 0;
    if (!ROW2) keys[4] = 1; else keys[4] = 0;
    if (!ROW3) keys[7] = 1; else keys[7] = 0;
    if (!ROW4) keys[10] = 1; else keys[10] = 0;
    COL2 = 1; __delay32(3000);

    // Scan first column
    COL3 = 0; __delay32(3000);
    if (!ROW1) keys[2] = 1; else keys[2] = 0;
    if (!ROW2) keys[5] = 1; else keys[5] = 0;
    if (!ROW3) keys[8] = 1; else keys[8] = 0;
    if (!ROW4) keys[11] = 1; else keys[11] = 0;
    COL3 = 1; __delay32(3000);
    }

    and video link

    http://m.youtube.com/#/watch?feature=youtube_gdata_player&v=R3SMv_boe-E&desktop_uri=%2Fwatch%3Fv%3DR3SMv_boe-E%26feature%3Dyoutube_gdata_player&gl=GB

  3. batchloaf says:

    Great video Paul! I think your updated polyphonic version sounds brilliant. I’m currently investigating how to generate simple synth drum sounds with the dsPIC, so that I can add a drum machine mode to my keypad.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s