piano 4 octaves, 8 notes, 3 threads, 2 outputs, no bug, clean code, comments, remove old .h
Fork of Nucleo_piano_CS435 by
main.cpp@10:a5b01cfb5097, 2018-07-05 (annotated)
- Committer:
- aknin001
- Date:
- Thu Jul 05 01:11:40 2018 +0000
- Revision:
- 10:a5b01cfb5097
- Parent:
- 9:9a36b66869fa
remove old .h
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
bcostm | 0:5701b41769fd | 1 | #include "mbed.h" |
Aliened | 3:ca0d5d72f842 | 2 | #include "TextLCD.h" |
aknin001 | 5:824785b64822 | 3 | #include <string.h> |
aknin001 | 5:824785b64822 | 4 | #include <math.h> |
aknin001 | 7:d2fe1a5e79ed | 5 | |
Aliened | 3:ca0d5d72f842 | 6 | // Define screen |
Aliened | 3:ca0d5d72f842 | 7 | TextLCD lcd(PA_8, PA_9, PC_7, PB_6, PA_7, PA_6, PA_5); // RS, RW, E, D4-D7 |
Aliened | 3:ca0d5d72f842 | 8 | |
aknin001 | 5:824785b64822 | 9 | // Define Bus In for Buttons (Do, Re, Mi, Fa, Sol, La, Si, Do) |
aknin001 | 5:824785b64822 | 10 | BusIn Bus_In(PA_0, PA_1, PA_4, PB_0, PA_10, PB_3, PB_5, PB_4); |
aknin001 | 9:9a36b66869fa | 11 | |
Aliened | 3:ca0d5d72f842 | 12 | // Define the PWM speaker |
Aliened | 3:ca0d5d72f842 | 13 | PwmOut speaker(PB_10); |
aknin001 | 9:9a36b66869fa | 14 | |
aknin001 | 9:9a36b66869fa | 15 | // Define Bus In for Octave Button |
aknin001 | 9:9a36b66869fa | 16 | BusIn Bus_In_Octave(PB_9); |
aknin001 | 9:9a36b66869fa | 17 | |
aknin001 | 9:9a36b66869fa | 18 | // Define Serial |
aknin001 | 9:9a36b66869fa | 19 | Serial pc(PA_2, PA_3); |
aknin001 | 7:d2fe1a5e79ed | 20 | |
Aliened | 3:ca0d5d72f842 | 21 | //Define variables for sound |
aknin001 | 9:9a36b66869fa | 22 | struct NoteReference { // struct note reference |
aknin001 | 9:9a36b66869fa | 23 | char name[4]; // note name |
aknin001 | 9:9a36b66869fa | 24 | int mask; // note mask for pressed of not |
aknin001 | 9:9a36b66869fa | 25 | double frequency; // note base frequency (octave 0) |
aknin001 | 9:9a36b66869fa | 26 | double add[4]; // add value for octave balancing (from octave 0 to 3) |
aknin001 | 5:824785b64822 | 27 | }; |
aknin001 | 9:9a36b66869fa | 28 | |
aknin001 | 9:9a36b66869fa | 29 | // NoteReference tab for 8 notes (1 octave) |
aknin001 | 8:781b03221397 | 30 | NoteReference noteReference[8] = { {"Do", 0x1, 262.0, {0.0, -1.0, -1.5, -3.0}}, |
aknin001 | 8:781b03221397 | 31 | {"Re", 0x2, 294.0, {0.0, -1.0, -1.0, -3.0 }}, |
aknin001 | 8:781b03221397 | 32 | {"Mi", 0x4, 330.0, {0.0, -1.0, -1.5, -3.0}}, |
aknin001 | 8:781b03221397 | 33 | {"Fa", 0x8, 349.0, {0.0, 0.5, -1.0, 2.0}}, |
aknin001 | 8:781b03221397 | 34 | {"Sol", 0x10, 392.0, {0.0, 0.0, 0.0, 0.0}}, |
aknin001 | 8:781b03221397 | 35 | {"La", 0x20, 440.0, {0.0, 0.0, 0.0, 0.0}}, |
aknin001 | 8:781b03221397 | 36 | {"Si", 0x40, 494.0, {0.0, 0.0, -1.0, -1.0}}, |
aknin001 | 8:781b03221397 | 37 | {"Do", 0x80, 523.0, {0.0, 0.0, 0.0, 0.0}} |
aknin001 | 8:781b03221397 | 38 | }; |
aknin001 | 9:9a36b66869fa | 39 | volatile int stateButtons = 0; // State of notes buttons |
aknin001 | 9:9a36b66869fa | 40 | volatile int stateButtonOctave = 0; // State of octave button |
aknin001 | 9:9a36b66869fa | 41 | volatile int octave = 0; // octave value |
aknin001 | 9:9a36b66869fa | 42 | |
aknin001 | 7:d2fe1a5e79ed | 43 | //Define variable for display |
aknin001 | 9:9a36b66869fa | 44 | char bufferOutput[30] = ""; // buffer for notes and octave |
aknin001 | 9:9a36b66869fa | 45 | |
aknin001 | 7:d2fe1a5e79ed | 46 | //Define variables for synchronization |
aknin001 | 9:9a36b66869fa | 47 | Mutex lockBufferOutput; // Mutex for BufferOutput variable |
aknin001 | 9:9a36b66869fa | 48 | Mutex lockOctave; // Mutex for octave variable |
aknin001 | 9:9a36b66869fa | 49 | |
aknin001 | 9:9a36b66869fa | 50 | /** |
aknin001 | 9:9a36b66869fa | 51 | Function that refresh the value of |
aknin001 | 9:9a36b66869fa | 52 | stateButtons depending on data input from |
aknin001 | 9:9a36b66869fa | 53 | Bus_In. |
aknin001 | 9:9a36b66869fa | 54 | **/ |
aknin001 | 9:9a36b66869fa | 55 | void refresh_state_buttons() |
Aliened | 3:ca0d5d72f842 | 56 | { |
aknin001 | 8:781b03221397 | 57 | stateButtons = Bus_In & Bus_In.mask(); // read the bus and mask out bits not being used |
Aliened | 3:ca0d5d72f842 | 58 | } |
aknin001 | 9:9a36b66869fa | 59 | |
aknin001 | 9:9a36b66869fa | 60 | /** |
aknin001 | 9:9a36b66869fa | 61 | Function that output notes on the buzzer. |
aknin001 | 9:9a36b66869fa | 62 | **/ |
aknin001 | 5:824785b64822 | 63 | void play_music(int notes, double frequency) |
Aliened | 3:ca0d5d72f842 | 64 | { |
aknin001 | 9:9a36b66869fa | 65 | speaker.period(1.0 / frequency); // set the period to 1 / frequency of notes |
aknin001 | 9:9a36b66869fa | 66 | while (stateButtons == notes) { // while button is pressed |
aknin001 | 9:9a36b66869fa | 67 | refresh_state_buttons(); // refresh stateButtons |
Aliened | 3:ca0d5d72f842 | 68 | } |
Aliened | 3:ca0d5d72f842 | 69 | } |
aknin001 | 8:781b03221397 | 70 | |
aknin001 | 9:9a36b66869fa | 71 | /** |
aknin001 | 9:9a36b66869fa | 72 | Function that generate a frequency depending on pressed notes |
aknin001 | 9:9a36b66869fa | 73 | and actual octave. |
aknin001 | 9:9a36b66869fa | 74 | **/ |
aknin001 | 9:9a36b66869fa | 75 | double generate_frequency(double frequency, int actualOctave) |
aknin001 | 8:781b03221397 | 76 | { |
aknin001 | 9:9a36b66869fa | 77 | frequency = 0.0; // init frequency to 0 |
aknin001 | 9:9a36b66869fa | 78 | lockBufferOutput.lock(); // lock lockBufferOutput Mutex for writing new notes on bufferOutput variable |
aknin001 | 9:9a36b66869fa | 79 | strcpy(bufferOutput, ""); // init bufferOuput to empty string |
aknin001 | 9:9a36b66869fa | 80 | for (int i = 0; i < 8; i++) { // iterate on all element of noteReference tab |
aknin001 | 9:9a36b66869fa | 81 | if (!(stateButtons & noteReference[i].mask)) // if note is pressed |
aknin001 | 8:781b03221397 | 82 | { |
aknin001 | 9:9a36b66869fa | 83 | // add note frequency to frequency (base frequency * 2^octave + add) |
aknin001 | 9:9a36b66869fa | 84 | frequency += noteReference[i].frequency * pow(2.0, (double)actualOctave) + noteReference[i].add[actualOctave]; |
aknin001 | 9:9a36b66869fa | 85 | strcat(bufferOutput, noteReference[i].name); // concatenate bufferOutput and new note name |
aknin001 | 9:9a36b66869fa | 86 | strcat(bufferOutput, " "); // concatenate with space separator |
aknin001 | 8:781b03221397 | 87 | } |
aknin001 | 8:781b03221397 | 88 | } |
aknin001 | 9:9a36b66869fa | 89 | lockBufferOutput.unlock(); // unlock bufferOutput at the end of the loop, end of writing |
aknin001 | 9:9a36b66869fa | 90 | return (frequency); // return the frequency to play on buzzer |
aknin001 | 8:781b03221397 | 91 | } |
aknin001 | 9:9a36b66869fa | 92 | |
aknin001 | 9:9a36b66869fa | 93 | /** |
aknin001 | 9:9a36b66869fa | 94 | Check if buttons if pressed and play notes |
aknin001 | 9:9a36b66869fa | 95 | otherwise set duty cycle to 0. |
aknin001 | 9:9a36b66869fa | 96 | **/ |
aknin001 | 9:9a36b66869fa | 97 | void check_buttons(double frequency, int actualOctave) |
Aliened | 3:ca0d5d72f842 | 98 | { |
aknin001 | 9:9a36b66869fa | 99 | if (stateButtons == 0xFF) // if nothing pressed |
aknin001 | 5:824785b64822 | 100 | { |
aknin001 | 9:9a36b66869fa | 101 | speaker = 0; // duty cycle equal to 0 |
aknin001 | 9:9a36b66869fa | 102 | lockBufferOutput.lock(); // lock BufferOutput Mutex for writing on bufferOutput variable |
aknin001 | 9:9a36b66869fa | 103 | strcpy(bufferOutput, ""); // set bufferOutput to empty string |
aknin001 | 9:9a36b66869fa | 104 | lockBufferOutput.unlock(); // unlock BufferOutput Mutex |
aknin001 | 6:58bab17100a2 | 105 | } |
aknin001 | 9:9a36b66869fa | 106 | else { // if buttons pressed |
aknin001 | 9:9a36b66869fa | 107 | lockOctave.lock(); // lock lockOctave Mutex for reading octave value |
aknin001 | 9:9a36b66869fa | 108 | actualOctave = octave; // read octave value |
aknin001 | 9:9a36b66869fa | 109 | lockOctave.unlock(); // unlock lockOctave Mutex |
aknin001 | 9:9a36b66869fa | 110 | frequency = generate_frequency(frequency, actualOctave); // generate frequency depending on notes and octave |
aknin001 | 9:9a36b66869fa | 111 | speaker = 0.5; // set duty cycle to 0.5 for volume |
aknin001 | 9:9a36b66869fa | 112 | play_music(stateButtons, frequency); // play notes on buzzer |
Aliened | 3:ca0d5d72f842 | 113 | } |
Aliened | 3:ca0d5d72f842 | 114 | } |
Aliened | 3:ca0d5d72f842 | 115 | |
aknin001 | 9:9a36b66869fa | 116 | /** |
aknin001 | 9:9a36b66869fa | 117 | Thread for playing notes. |
aknin001 | 9:9a36b66869fa | 118 | check states of buttons notes and play music. |
aknin001 | 9:9a36b66869fa | 119 | **/ |
aknin001 | 7:d2fe1a5e79ed | 120 | void run_music() |
aknin001 | 7:d2fe1a5e79ed | 121 | { |
aknin001 | 9:9a36b66869fa | 122 | double frequency = 0.0; // double for total frequency |
aknin001 | 9:9a36b66869fa | 123 | int actualOctave = 0; // int for actual octave |
aknin001 | 7:d2fe1a5e79ed | 124 | |
aknin001 | 9:9a36b66869fa | 125 | while (true) // Infinte loop to catch buttons notes events |
aknin001 | 7:d2fe1a5e79ed | 126 | { |
aknin001 | 9:9a36b66869fa | 127 | refresh_state_buttons(); // refresh state_buttons |
aknin001 | 9:9a36b66869fa | 128 | check_buttons(frequency, actualOctave); // check if buttons are pressed and play music |
aknin001 | 9:9a36b66869fa | 129 | Thread::wait(100); // Thread wait 100 for scheduling |
aknin001 | 7:d2fe1a5e79ed | 130 | } |
aknin001 | 7:d2fe1a5e79ed | 131 | } |
Aliened | 3:ca0d5d72f842 | 132 | |
aknin001 | 9:9a36b66869fa | 133 | /** |
aknin001 | 9:9a36b66869fa | 134 | Function that will display notes and gammes on LCD screen and |
aknin001 | 9:9a36b66869fa | 135 | PC device. |
aknin001 | 9:9a36b66869fa | 136 | Only called when a change on BufferOutput occurs. |
aknin001 | 9:9a36b66869fa | 137 | **/ |
aknin001 | 9:9a36b66869fa | 138 | void refresh_display_notes(char *old_buffer) |
aknin001 | 9:9a36b66869fa | 139 | { |
aknin001 | 9:9a36b66869fa | 140 | lcd.cls(); // clear LCD screen |
aknin001 | 9:9a36b66869fa | 141 | lockOctave.lock(); // lock lockOctave Mutex for reading octave variable |
aknin001 | 9:9a36b66869fa | 142 | if (strcmp(bufferOutput, "")) // if buffer not empty |
aknin001 | 9:9a36b66869fa | 143 | { |
aknin001 | 9:9a36b66869fa | 144 | lcd.printf("%s- O[%d]", bufferOutput, octave); // display notes and actual octave on LCD screen |
aknin001 | 9:9a36b66869fa | 145 | pc.printf("Play notes: %s with octave %d\n", bufferOutput, octave); // display notes and actual octave on PC device |
aknin001 | 9:9a36b66869fa | 146 | } |
aknin001 | 9:9a36b66869fa | 147 | else |
aknin001 | 9:9a36b66869fa | 148 | { |
aknin001 | 9:9a36b66869fa | 149 | lcd.printf("Octave = %d", octave); // only display octave on screen if no button pressed |
aknin001 | 9:9a36b66869fa | 150 | pc.printf("Release notes\n"); // display release message on PC device |
aknin001 | 9:9a36b66869fa | 151 | } |
aknin001 | 9:9a36b66869fa | 152 | lockOctave.unlock(); // unlock lockOctave |
aknin001 | 9:9a36b66869fa | 153 | strcpy(old_buffer, bufferOutput); // save value bufferOutput in old_buffer |
aknin001 | 9:9a36b66869fa | 154 | } |
aknin001 | 9:9a36b66869fa | 155 | |
aknin001 | 9:9a36b66869fa | 156 | /** |
aknin001 | 9:9a36b66869fa | 157 | Function that will display the octave on screen |
aknin001 | 9:9a36b66869fa | 158 | if a change in octave occurs. |
aknin001 | 9:9a36b66869fa | 159 | **/ |
aknin001 | 9:9a36b66869fa | 160 | int refresh_display_octave(int old_octave) |
aknin001 | 9:9a36b66869fa | 161 | { |
aknin001 | 9:9a36b66869fa | 162 | lockOctave.lock(); // lock lockOctave for reading octave variable |
aknin001 | 9:9a36b66869fa | 163 | if (old_octave != octave) // if change occurs |
aknin001 | 9:9a36b66869fa | 164 | { |
aknin001 | 9:9a36b66869fa | 165 | lcd.cls(); // clear screen |
aknin001 | 9:9a36b66869fa | 166 | lcd.printf("Octave = %d", octave); // display new octave on LCD screen |
aknin001 | 9:9a36b66869fa | 167 | pc.printf("[INFO] - Change octave %d to octave %d\n", old_octave, octave); // display new octave on PC device |
aknin001 | 9:9a36b66869fa | 168 | old_octave = octave; // save the new value to old_octave |
aknin001 | 9:9a36b66869fa | 169 | } |
aknin001 | 9:9a36b66869fa | 170 | lockOctave.unlock(); // Unlock lockOctave |
aknin001 | 9:9a36b66869fa | 171 | return old_octave; // return old_octave |
aknin001 | 9:9a36b66869fa | 172 | } |
aknin001 | 9:9a36b66869fa | 173 | |
aknin001 | 9:9a36b66869fa | 174 | /** |
aknin001 | 9:9a36b66869fa | 175 | Thread display function that output |
aknin001 | 9:9a36b66869fa | 176 | notes and octaves on the LCD screen |
aknin001 | 9:9a36b66869fa | 177 | and the PC device by Serial. |
aknin001 | 9:9a36b66869fa | 178 | **/ |
aknin001 | 7:d2fe1a5e79ed | 179 | void run_display() |
aknin001 | 7:d2fe1a5e79ed | 180 | { |
aknin001 | 9:9a36b66869fa | 181 | char old_buffer[30] = ""; // buffer of old value BufferOutput variable, init to empty string by default |
aknin001 | 9:9a36b66869fa | 182 | int old_octave = 0; // value for old octave variable, init to zero by default |
aknin001 | 7:d2fe1a5e79ed | 183 | |
aknin001 | 9:9a36b66869fa | 184 | lockOctave.lock(); // Lock lockOctave Mutex for reading octave variable |
aknin001 | 9:9a36b66869fa | 185 | lcd.printf("Octave = %d", octave); // Display default octave on screen until event occurs |
aknin001 | 9:9a36b66869fa | 186 | lockOctave.unlock(); // Unlock lockOctave Mutex |
aknin001 | 9:9a36b66869fa | 187 | while(true) // Infinite loop to run display |
aknin001 | 7:d2fe1a5e79ed | 188 | { |
aknin001 | 9:9a36b66869fa | 189 | lockBufferOutput.lock(); // Lock lockBufferOutput Mutex for reading BufferOutput |
aknin001 | 9:9a36b66869fa | 190 | if (strcmp(old_buffer, bufferOutput)) // if bufferOutput has change |
aknin001 | 9:9a36b66869fa | 191 | refresh_display_notes(old_buffer); // output notes |
aknin001 | 9:9a36b66869fa | 192 | else |
aknin001 | 9:9a36b66869fa | 193 | old_octave = refresh_display_octave(old_octave); // else output gamme if change |
aknin001 | 9:9a36b66869fa | 194 | lockBufferOutput.unlock(); // Unlock lockBufferOutput Mutex |
aknin001 | 9:9a36b66869fa | 195 | Thread::wait(100); // Thread wait 100 for synchronization |
aknin001 | 7:d2fe1a5e79ed | 196 | } |
aknin001 | 7:d2fe1a5e79ed | 197 | } |
aknin001 | 9:9a36b66869fa | 198 | |
aknin001 | 9:9a36b66869fa | 199 | /** |
aknin001 | 9:9a36b66869fa | 200 | Function that refresh the value of |
aknin001 | 9:9a36b66869fa | 201 | stateButtonOctave variable based on the |
aknin001 | 9:9a36b66869fa | 202 | input data from Bus_In_Octave. |
aknin001 | 9:9a36b66869fa | 203 | **/ |
aknin001 | 9:9a36b66869fa | 204 | void check_state_button_octave() |
aknin001 | 7:d2fe1a5e79ed | 205 | { |
aknin001 | 9:9a36b66869fa | 206 | stateButtonOctave = Bus_In_Octave & Bus_In_Octave.mask(); |
aknin001 | 7:d2fe1a5e79ed | 207 | } |
aknin001 | 7:d2fe1a5e79ed | 208 | |
aknin001 | 9:9a36b66869fa | 209 | /** |
aknin001 | 9:9a36b66869fa | 210 | Thread for Octave. |
aknin001 | 9:9a36b66869fa | 211 | |
aknin001 | 9:9a36b66869fa | 212 | Catch octave button state and increment |
aknin001 | 9:9a36b66869fa | 213 | the octave value if button has been pressed. |
aknin001 | 9:9a36b66869fa | 214 | **/ |
aknin001 | 9:9a36b66869fa | 215 | void run_octave() |
aknin001 | 7:d2fe1a5e79ed | 216 | { |
aknin001 | 9:9a36b66869fa | 217 | while(true) // Infinite loop for octave Thread |
aknin001 | 7:d2fe1a5e79ed | 218 | { |
aknin001 | 9:9a36b66869fa | 219 | check_state_button_octave(); // refresh state_button_octave |
aknin001 | 9:9a36b66869fa | 220 | if (stateButtonOctave == 0x0) // if button is pressed |
aknin001 | 7:d2fe1a5e79ed | 221 | { |
aknin001 | 9:9a36b66869fa | 222 | lockOctave.lock(); // lock lockOctave Mutex, enter in critical section |
aknin001 | 9:9a36b66869fa | 223 | octave = (octave == 3) ? 0 : octave + 1; // increment octave or set to 0 |
aknin001 | 9:9a36b66869fa | 224 | lockOctave.unlock(); // unlock lockOctave Mutex, end of critical section |
aknin001 | 9:9a36b66869fa | 225 | while (stateButtonOctave == 0x0) // continue check button state until it is unpressed |
aknin001 | 9:9a36b66869fa | 226 | check_state_button_octave(); // refresh state_button_octave |
aknin001 | 7:d2fe1a5e79ed | 227 | } |
aknin001 | 9:9a36b66869fa | 228 | Thread::wait(100); // Thread wait 100 for scheduling |
aknin001 | 7:d2fe1a5e79ed | 229 | } |
aknin001 | 7:d2fe1a5e79ed | 230 | } |
aknin001 | 9:9a36b66869fa | 231 | |
aknin001 | 9:9a36b66869fa | 232 | /** |
aknin001 | 9:9a36b66869fa | 233 | Main function Piano Project. |
aknin001 | 9:9a36b66869fa | 234 | |
aknin001 | 9:9a36b66869fa | 235 | Launch 3 threads for playing music, display notes |
aknin001 | 9:9a36b66869fa | 236 | and octaves. |
aknin001 | 9:9a36b66869fa | 237 | **/ |
bcostm | 0:5701b41769fd | 238 | int main() |
bcostm | 0:5701b41769fd | 239 | { |
aknin001 | 9:9a36b66869fa | 240 | Thread music_thread; // Thread for playing music |
aknin001 | 9:9a36b66869fa | 241 | Thread display_thread; // Thread for display notes and octave |
aknin001 | 9:9a36b66869fa | 242 | Thread octave_thread; // Thread for octave |
aknin001 | 7:d2fe1a5e79ed | 243 | |
aknin001 | 9:9a36b66869fa | 244 | lcd.printf("CS435\nPiano Project"); // Print welcome string on LCD screen |
aknin001 | 7:d2fe1a5e79ed | 245 | wait(3); |
aknin001 | 9:9a36b66869fa | 246 | lcd.cls(); // Clear the screen |
aknin001 | 7:d2fe1a5e79ed | 247 | |
aknin001 | 9:9a36b66869fa | 248 | music_thread.start(run_music); // Start music thread |
aknin001 | 9:9a36b66869fa | 249 | display_thread.start(run_display); // Start display thread |
aknin001 | 9:9a36b66869fa | 250 | octave_thread.start(run_octave); // Start octave thread |
aknin001 | 9:9a36b66869fa | 251 | |
aknin001 | 9:9a36b66869fa | 252 | while(1); // Infinite loop for main thread |
aknin001 | 7:d2fe1a5e79ed | 253 | } |