Drive a speaker using PWM
Dependencies: mbed
Dependents: PwmSpeaker-Example
Speaker.cpp@1:10005e388826, 2015-11-12 (annotated)
- Committer:
- asmellby
- Date:
- Thu Nov 12 21:20:23 2015 +0000
- Revision:
- 1:10005e388826
- Parent:
- 0:b7dd35e61bb1
Update docs.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
asmellby | 0:b7dd35e61bb1 | 1 | #include "Speaker.h" |
asmellby | 0:b7dd35e61bb1 | 2 | |
asmellby | 0:b7dd35e61bb1 | 3 | static int notes[] = {0, |
asmellby | 0:b7dd35e61bb1 | 4 | NOTE_C4, NOTE_CS4, NOTE_D4, NOTE_DS4, NOTE_E4, NOTE_F4, NOTE_FS4, NOTE_G4, NOTE_GS4, NOTE_A4, NOTE_AS4, NOTE_B4, |
asmellby | 0:b7dd35e61bb1 | 5 | NOTE_C5, NOTE_CS5, NOTE_D5, NOTE_DS5, NOTE_E5, NOTE_F5, NOTE_FS5, NOTE_G5, NOTE_GS5, NOTE_A5, NOTE_AS5, NOTE_B5, |
asmellby | 0:b7dd35e61bb1 | 6 | NOTE_C6, NOTE_CS6, NOTE_D6, NOTE_DS6, NOTE_E6, NOTE_F6, NOTE_FS6, NOTE_G6, NOTE_GS6, NOTE_A6, NOTE_AS6, NOTE_B6, |
asmellby | 0:b7dd35e61bb1 | 7 | NOTE_C7, NOTE_CS7, NOTE_D7, NOTE_DS7, NOTE_E7, NOTE_F7, NOTE_FS7, NOTE_G7, NOTE_GS7, NOTE_A7, NOTE_AS7, NOTE_B7 |
asmellby | 0:b7dd35e61bb1 | 8 | }; |
asmellby | 0:b7dd35e61bb1 | 9 | |
asmellby | 1:10005e388826 | 10 | /** |
asmellby | 1:10005e388826 | 11 | * Set output frequency of the speaker. |
asmellby | 1:10005e388826 | 12 | * |
asmellby | 1:10005e388826 | 13 | * \param frequency The frequency to set |
asmellby | 1:10005e388826 | 14 | */ |
asmellby | 0:b7dd35e61bb1 | 15 | void Speaker::set_frequency(uint32_t frequency) { |
asmellby | 0:b7dd35e61bb1 | 16 | _pwm.period(1.0/frequency); |
asmellby | 0:b7dd35e61bb1 | 17 | } |
asmellby | 0:b7dd35e61bb1 | 18 | |
asmellby | 1:10005e388826 | 19 | /** |
asmellby | 1:10005e388826 | 20 | * Enable or disable speaker output. |
asmellby | 1:10005e388826 | 21 | * |
asmellby | 1:10005e388826 | 22 | * \param enable Whether to enable or disable the speaker |
asmellby | 1:10005e388826 | 23 | */ |
asmellby | 0:b7dd35e61bb1 | 24 | void Speaker::enable(bool enable) { |
asmellby | 0:b7dd35e61bb1 | 25 | *this = enable; |
asmellby | 0:b7dd35e61bb1 | 26 | } |
asmellby | 0:b7dd35e61bb1 | 27 | |
asmellby | 1:10005e388826 | 28 | /** |
asmellby | 1:10005e388826 | 29 | * Enable or disable the speaker output. |
asmellby | 1:10005e388826 | 30 | * |
asmellby | 1:10005e388826 | 31 | * \param setting 0 to disable speaker, other values to enable speaker. |
asmellby | 1:10005e388826 | 32 | */ |
asmellby | 0:b7dd35e61bb1 | 33 | Speaker& Speaker::operator=(int setting) { |
asmellby | 0:b7dd35e61bb1 | 34 | if (setting) { |
asmellby | 0:b7dd35e61bb1 | 35 | _pwm = 0.5; |
asmellby | 0:b7dd35e61bb1 | 36 | } else { |
asmellby | 0:b7dd35e61bb1 | 37 | _pwm = 1; |
asmellby | 0:b7dd35e61bb1 | 38 | } |
asmellby | 0:b7dd35e61bb1 | 39 | return *this; |
asmellby | 0:b7dd35e61bb1 | 40 | } |
asmellby | 0:b7dd35e61bb1 | 41 | |
asmellby | 1:10005e388826 | 42 | /** |
asmellby | 1:10005e388826 | 43 | * Play an old-school ringtone in RTTTL language. |
asmellby | 1:10005e388826 | 44 | * |
asmellby | 1:10005e388826 | 45 | * \param p Char array in RTTTL format |
asmellby | 0:b7dd35e61bb1 | 46 | */ |
asmellby | 0:b7dd35e61bb1 | 47 | void Speaker::play_rtttl(char * p) { |
asmellby | 0:b7dd35e61bb1 | 48 | // Absolutely no error checking in here |
asmellby | 0:b7dd35e61bb1 | 49 | |
asmellby | 0:b7dd35e61bb1 | 50 | uint8_t default_dur = 4; |
asmellby | 0:b7dd35e61bb1 | 51 | uint8_t default_oct = 6; |
asmellby | 0:b7dd35e61bb1 | 52 | int bpm = 63; |
asmellby | 0:b7dd35e61bb1 | 53 | int num; |
asmellby | 0:b7dd35e61bb1 | 54 | long wholenote; |
asmellby | 0:b7dd35e61bb1 | 55 | long duration; |
asmellby | 0:b7dd35e61bb1 | 56 | uint8_t note; |
asmellby | 0:b7dd35e61bb1 | 57 | uint8_t scale; |
asmellby | 0:b7dd35e61bb1 | 58 | |
asmellby | 0:b7dd35e61bb1 | 59 | // format: d=N,o=N,b=NNN: |
asmellby | 0:b7dd35e61bb1 | 60 | // find the start (skip name, etc) |
asmellby | 0:b7dd35e61bb1 | 61 | |
asmellby | 0:b7dd35e61bb1 | 62 | while(*p != ':') p++; // ignore name |
asmellby | 0:b7dd35e61bb1 | 63 | p++; // skip ':' |
asmellby | 0:b7dd35e61bb1 | 64 | |
asmellby | 0:b7dd35e61bb1 | 65 | // get default duration |
asmellby | 0:b7dd35e61bb1 | 66 | if(*p == 'd') |
asmellby | 0:b7dd35e61bb1 | 67 | { |
asmellby | 0:b7dd35e61bb1 | 68 | p++; p++; // skip "d=" |
asmellby | 0:b7dd35e61bb1 | 69 | num = 0; |
asmellby | 0:b7dd35e61bb1 | 70 | while(isdigit(*p)) |
asmellby | 0:b7dd35e61bb1 | 71 | { |
asmellby | 0:b7dd35e61bb1 | 72 | num = (num * 10) + (*p++ - '0'); |
asmellby | 0:b7dd35e61bb1 | 73 | } |
asmellby | 0:b7dd35e61bb1 | 74 | if(num > 0) default_dur = num; |
asmellby | 0:b7dd35e61bb1 | 75 | p++; // skip comma |
asmellby | 0:b7dd35e61bb1 | 76 | } |
asmellby | 0:b7dd35e61bb1 | 77 | |
asmellby | 0:b7dd35e61bb1 | 78 | printf("ddur: %d\n", default_dur); |
asmellby | 0:b7dd35e61bb1 | 79 | |
asmellby | 0:b7dd35e61bb1 | 80 | // get default octave |
asmellby | 0:b7dd35e61bb1 | 81 | if(*p == 'o') |
asmellby | 0:b7dd35e61bb1 | 82 | { |
asmellby | 0:b7dd35e61bb1 | 83 | p++; p++; // skip "o=" |
asmellby | 0:b7dd35e61bb1 | 84 | num = *p++ - '0'; |
asmellby | 0:b7dd35e61bb1 | 85 | if(num >= 3 && num <=7) default_oct = num; |
asmellby | 0:b7dd35e61bb1 | 86 | p++; // skip comma |
asmellby | 0:b7dd35e61bb1 | 87 | } |
asmellby | 0:b7dd35e61bb1 | 88 | |
asmellby | 0:b7dd35e61bb1 | 89 | printf("doct: %d\n", default_oct); |
asmellby | 0:b7dd35e61bb1 | 90 | |
asmellby | 0:b7dd35e61bb1 | 91 | // get BPM |
asmellby | 0:b7dd35e61bb1 | 92 | if(*p == 'b') |
asmellby | 0:b7dd35e61bb1 | 93 | { |
asmellby | 0:b7dd35e61bb1 | 94 | p++; p++; // skip "b=" |
asmellby | 0:b7dd35e61bb1 | 95 | num = 0; |
asmellby | 0:b7dd35e61bb1 | 96 | while(isdigit(*p)) |
asmellby | 0:b7dd35e61bb1 | 97 | { |
asmellby | 0:b7dd35e61bb1 | 98 | num = (num * 10) + (*p++ - '0'); |
asmellby | 0:b7dd35e61bb1 | 99 | } |
asmellby | 0:b7dd35e61bb1 | 100 | bpm = num; |
asmellby | 0:b7dd35e61bb1 | 101 | p++; // skip colon |
asmellby | 0:b7dd35e61bb1 | 102 | } |
asmellby | 0:b7dd35e61bb1 | 103 | |
asmellby | 0:b7dd35e61bb1 | 104 | printf("bpm: %d\n",bpm); |
asmellby | 0:b7dd35e61bb1 | 105 | |
asmellby | 0:b7dd35e61bb1 | 106 | // BPM usually expresses the number of quarter notes per minute |
asmellby | 0:b7dd35e61bb1 | 107 | wholenote = (60 * 1000L / bpm) * 4; // this is the time for whole note (in milliseconds) |
asmellby | 0:b7dd35e61bb1 | 108 | |
asmellby | 0:b7dd35e61bb1 | 109 | printf("wn: %d\n", wholenote); |
asmellby | 0:b7dd35e61bb1 | 110 | |
asmellby | 0:b7dd35e61bb1 | 111 | |
asmellby | 0:b7dd35e61bb1 | 112 | // now begin note loop |
asmellby | 0:b7dd35e61bb1 | 113 | while(*p) |
asmellby | 0:b7dd35e61bb1 | 114 | { |
asmellby | 0:b7dd35e61bb1 | 115 | // first, get note duration, if available |
asmellby | 0:b7dd35e61bb1 | 116 | num = 0; |
asmellby | 0:b7dd35e61bb1 | 117 | while(isdigit(*p)) |
asmellby | 0:b7dd35e61bb1 | 118 | { |
asmellby | 0:b7dd35e61bb1 | 119 | num = (num * 10) + (*p++ - '0'); |
asmellby | 0:b7dd35e61bb1 | 120 | } |
asmellby | 0:b7dd35e61bb1 | 121 | |
asmellby | 0:b7dd35e61bb1 | 122 | if(num) duration = wholenote / num; |
asmellby | 0:b7dd35e61bb1 | 123 | else duration = wholenote / default_dur; // we will need to check if we are a dotted note after |
asmellby | 0:b7dd35e61bb1 | 124 | |
asmellby | 0:b7dd35e61bb1 | 125 | // now get the note |
asmellby | 0:b7dd35e61bb1 | 126 | note = 0; |
asmellby | 0:b7dd35e61bb1 | 127 | |
asmellby | 0:b7dd35e61bb1 | 128 | switch(*p) |
asmellby | 0:b7dd35e61bb1 | 129 | { |
asmellby | 0:b7dd35e61bb1 | 130 | case 'c': |
asmellby | 0:b7dd35e61bb1 | 131 | note = 1; |
asmellby | 0:b7dd35e61bb1 | 132 | break; |
asmellby | 0:b7dd35e61bb1 | 133 | case 'd': |
asmellby | 0:b7dd35e61bb1 | 134 | note = 3; |
asmellby | 0:b7dd35e61bb1 | 135 | break; |
asmellby | 0:b7dd35e61bb1 | 136 | case 'e': |
asmellby | 0:b7dd35e61bb1 | 137 | note = 5; |
asmellby | 0:b7dd35e61bb1 | 138 | break; |
asmellby | 0:b7dd35e61bb1 | 139 | case 'f': |
asmellby | 0:b7dd35e61bb1 | 140 | note = 6; |
asmellby | 0:b7dd35e61bb1 | 141 | break; |
asmellby | 0:b7dd35e61bb1 | 142 | case 'g': |
asmellby | 0:b7dd35e61bb1 | 143 | note = 8; |
asmellby | 0:b7dd35e61bb1 | 144 | break; |
asmellby | 0:b7dd35e61bb1 | 145 | case 'a': |
asmellby | 0:b7dd35e61bb1 | 146 | note = 10; |
asmellby | 0:b7dd35e61bb1 | 147 | break; |
asmellby | 0:b7dd35e61bb1 | 148 | case 'b': |
asmellby | 0:b7dd35e61bb1 | 149 | note = 12; |
asmellby | 0:b7dd35e61bb1 | 150 | break; |
asmellby | 0:b7dd35e61bb1 | 151 | case 'p': |
asmellby | 0:b7dd35e61bb1 | 152 | default: |
asmellby | 0:b7dd35e61bb1 | 153 | note = 0; |
asmellby | 0:b7dd35e61bb1 | 154 | } |
asmellby | 0:b7dd35e61bb1 | 155 | p++; |
asmellby | 0:b7dd35e61bb1 | 156 | |
asmellby | 0:b7dd35e61bb1 | 157 | // now, get optional '#' sharp |
asmellby | 0:b7dd35e61bb1 | 158 | if(*p == '#') |
asmellby | 0:b7dd35e61bb1 | 159 | { |
asmellby | 0:b7dd35e61bb1 | 160 | note++; |
asmellby | 0:b7dd35e61bb1 | 161 | p++; |
asmellby | 0:b7dd35e61bb1 | 162 | } |
asmellby | 0:b7dd35e61bb1 | 163 | |
asmellby | 0:b7dd35e61bb1 | 164 | // now, get optional '.' dotted note |
asmellby | 0:b7dd35e61bb1 | 165 | if(*p == '.') |
asmellby | 0:b7dd35e61bb1 | 166 | { |
asmellby | 0:b7dd35e61bb1 | 167 | duration += duration/2; |
asmellby | 0:b7dd35e61bb1 | 168 | p++; |
asmellby | 0:b7dd35e61bb1 | 169 | } |
asmellby | 0:b7dd35e61bb1 | 170 | |
asmellby | 0:b7dd35e61bb1 | 171 | // now, get scale |
asmellby | 0:b7dd35e61bb1 | 172 | if(isdigit(*p)) |
asmellby | 0:b7dd35e61bb1 | 173 | { |
asmellby | 0:b7dd35e61bb1 | 174 | scale = *p - '0'; |
asmellby | 0:b7dd35e61bb1 | 175 | p++; |
asmellby | 0:b7dd35e61bb1 | 176 | } |
asmellby | 0:b7dd35e61bb1 | 177 | else |
asmellby | 0:b7dd35e61bb1 | 178 | { |
asmellby | 0:b7dd35e61bb1 | 179 | scale = default_oct; |
asmellby | 0:b7dd35e61bb1 | 180 | } |
asmellby | 0:b7dd35e61bb1 | 181 | |
asmellby | 0:b7dd35e61bb1 | 182 | scale += _offset; |
asmellby | 0:b7dd35e61bb1 | 183 | |
asmellby | 0:b7dd35e61bb1 | 184 | if(*p == ',') |
asmellby | 0:b7dd35e61bb1 | 185 | p++; // skip comma for next note (or we may be at the end) |
asmellby | 0:b7dd35e61bb1 | 186 | |
asmellby | 0:b7dd35e61bb1 | 187 | // now play the note |
asmellby | 0:b7dd35e61bb1 | 188 | |
asmellby | 0:b7dd35e61bb1 | 189 | if(note) |
asmellby | 0:b7dd35e61bb1 | 190 | { |
asmellby | 0:b7dd35e61bb1 | 191 | //printf("\027\033H"); |
asmellby | 0:b7dd35e61bb1 | 192 | //printf("Playing: %d %d (%d) %d\n", scale, note, notes[(scale - 4) * 12 + note], duration); |
asmellby | 0:b7dd35e61bb1 | 193 | set_frequency(notes[(scale - 4) * 12 + note]); |
asmellby | 0:b7dd35e61bb1 | 194 | enable(true); |
asmellby | 0:b7dd35e61bb1 | 195 | wait_ms(duration); |
asmellby | 0:b7dd35e61bb1 | 196 | enable(false); |
asmellby | 0:b7dd35e61bb1 | 197 | } |
asmellby | 0:b7dd35e61bb1 | 198 | else |
asmellby | 0:b7dd35e61bb1 | 199 | { |
asmellby | 0:b7dd35e61bb1 | 200 | //printf("Pausing: %d\n", duration); |
asmellby | 0:b7dd35e61bb1 | 201 | wait_ms(duration); |
asmellby | 0:b7dd35e61bb1 | 202 | } |
asmellby | 0:b7dd35e61bb1 | 203 | } |
asmellby | 0:b7dd35e61bb1 | 204 | } |