/** * This is a digital music synth for the Arduino. * This project uses the RadioShack input pad part number 270-215 (may be * discontinued). It has 8 i/o pins and the matrix is below. * * See http://b1towel.com/images/microcontroller/synth/ * * 22 December 2008 I am in the process of recreating the functionality of the * keypad with 15 pushbuttons because the keypad is finicky. * * 14 March 2009 Redesigned the logic of using the keypad. Building a keypad * from scratch that mimiced the radioshack keypad did not help. The solution * ended up being using the built-in pull-up resistors of the ATmega168. Because * I was not using pull-down or pull-up resistors in the original design, that * caused erratic behavior when trying to read the state of the keypad. * * @author benkillin * @date 14 March 2009 * @version 1.0 * @see http://www.beavisaudio.com/Projects/digital/ArduinoPunkConsole/ for * original inspiration * * Copyright (c) 2009 benkillin * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /********************************************************************** * DATA: **********************************************************************/ /************* * CONSTANTS: *************/ const int start = 2; // start pin for the radioshack pad connector const int end = 9; // end pin for the radioshack pad connector const int digitalOut = 11; // pin that goes out to speaker const int power = 12; // pin connected to power button to toggel audio output // analog pins for frequency, tempo, and duration: const int frequencyPin = 0; const int durationPin = 1; const int tempoPin = 2; // keypad pins const int keyPins = 8; // number of keys (and the number of arrays within the two arrays below): const int numButtons = 15; // this matrix tells which pins are connected for each key. The keypad is a 3x5 // matrix in this format: /* A B C D E F G H I J K L M N O */ #if 0 // this byte matrix is not needed because we now use a boolean array and // the built-in pull-up resistors of the atmega168 // this matrix is still kept, however, because it could prove useful eventually // Pins: 12345678 const byte matrix[numButtons] = { B00101000, // A B00011000, // B B00001100, // C B00100010, // D B00010010, // E B00000110, // F B00100001, // G B00010001, // H B00000101, // I B01100000, // J B01010000, // K B01000100, // L B10100000, // M B10010000, // N B10000100 }; // O #endif // different way of representing above table: const int letters[numButtons][2] = { { 3, 5 }, // A { 4, 5 }, // B { 5, 6 }, // C { 3, 7 }, // D { 4, 7 }, // E { 6, 7 }, // F { 3, 8 }, // G { 4, 8 }, // H { 6, 8 }, // I { 2, 3 }, // J { 2, 4 }, // K { 2, 6 }, // L { 1, 3 }, // M { 1, 4 }, // N { 1, 6 } } ; // O // ammount to add to each of the above valuse to get the real pin number: const int offset = 1; // matrix indicies: typedef enum keys { A = 0, B = 1, C = 2, D = 3, E = 4, F = 5, G = 6, H = 7, I = 8, J = 9, K = 10, L = 11, M = 12, N = 13, O = 14 } Key; /************* * VARIABLES: *************/ // boolean array with the state of the keypad: boolean keysPressedArr[numButtons]; // Set up the array for each sound step int steps[15] = { 20, 40, 60, 80, 100, 120, 140, 160, 180, 200, 220, 240, 260, 280, 300 }; // Initialize the tempo int tempo = 100; // not needed with the addition of the boolean array // keypad value, using bits //byte keyVal = B00000000; // PRNG seed: int seed = 42; // duration of tone int duration = 50; // pitch int pitchval = 1; // do we play? int fPlayMode = true; /********************************************************************** * PROGRAM CODE: **********************************************************************/ /************* * FUNCTIONS: *************/ /** * returns a bit representation of the buttons pressed. */ //byte void readKeypad() { #if 0 /**********************************************************\ TODO: Redesign the method of getting the toggle of audio output working. Current method is stupid: \**********************************************************/ // read power button: if(digitalRead(power)) { fPlayMode = !fPlayMode; } #endif for(int i = 0; i < numButtons; i++) { // set pin to output and set the state to low pinMode((letters[i][0] + offset), OUTPUT); digitalWrite(letters[i][0] + offset, LOW); delayMicroseconds(10); // since we are using pull-UP resistors, 1 means the button was not pressed, // 0 means the button is pressed. keysPressedArr[i] = !( (bool)digitalRead((letters[i][1] + offset)) ); // set pin back to input and enable built-in pull-up resistor pinMode((letters[i][0] + offset), INPUT); digitalWrite((letters[i][0] + offset), HIGH); // delay for the hell of it: delayMicroseconds(10); } } /** * Determines if a particular key was pressed. */ boolean keyPressed(int key) { return keysPressedArr[key]; } /** * Read the frequency knob */ int readFreq() { return analogRead(frequencyPin); } /** * Reads the analog values of the tempo and duration knobs. */ void readTempoDuration() { tempo = (analogRead (tempoPin)); duration = (analogRead (durationPin)); } //freqout code by Paul Badger // freq - frequency value // t - time duration of tone // modified by benkillin void freqOut(long freq, int t, int pin) { int hperiod; //calculate 1/2 period in us long cycles, i; pinMode(pin, OUTPUT); // subtract 7 us to make up for digitalWrite overhead - determined empirically hperiod = (500000 / ((freq - 7) * pitchval)); // calculate cycles cycles = ((long)freq * (long)t) / 1000; // calculate cycles boolean state = true; for (i=0; i<= cycles*2; i++) { // play note for t ms // modification: digitalWrite(pin, ((state) ? HIGH : LOW)); delayMicroseconds(hperiod); state = !state; /* digitalWrite(pin, HIGH); delayMicroseconds(hperiod); digitalWrite(pin, LOW); delayMicroseconds(hperiod - 1); // - 1 to make up for fractional // microsecond in digitaWrite overhead */ } pinMode(pin, INPUT); } /************* * ARDUINO EXECUTION: *************/ void setup() { // setup serial for 9600 baud Serial.begin(9600); // Init the input pins for(int i = start; i <= end; i++) { pinMode(i, INPUT); } // init the output pin for the speaker pinMode(digitalOut, OUTPUT); // init input pin for power button to toggle audio output pinMode(power, INPUT); randomSeed(seed); // we must initialize all the i/o pins to be inputs by default and enable // the ATmega168's built-in pull-up resistors: for(int i = start; i <= end; i++) { pinMode(i, INPUT); digitalWrite(i, HIGH); } } void loop() { for(int i = 0; i < numButtons; i++) { readKeypad(); readTempoDuration(); if(keyPressed(i)) { steps[i] = readFreq(); #if 0 Serial.print("CurKey: "); Serial.print(i); Serial.print("\tTempo: "); Serial.print(tempo); Serial.print("\tDuration: "); Serial.print(duration); Serial.print("\tFrequency: "); Serial.println(steps[i]); #endif } if(fPlayMode) { freqOut(steps[i], duration, digitalOut); } // pause between steps delay(tempo); } }