Skip to content

Commit 4d45d2f

Browse files
committed
Enable speaker sounds
1 parent 51e31c5 commit 4d45d2f

File tree

5 files changed

+121
-2
lines changed

5 files changed

+121
-2
lines changed

src/main.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "serial.h"
1313
#include "setup.h"
1414
#include "solve.h"
15+
#include "speaker.h"
1516
#include "speed.h"
1617

1718
static void competition(void);

src/setup.c

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ void setup_spi_low_speed(void)
214214
* @see Reference manual (RM0008) "TIMx functional description" and in
215215
* particular "PWM mode" section.
216216
*/
217-
static void setup_pwm(void)
217+
static void setup_motor_driver(void)
218218
{
219219
timer_set_mode(TIM3, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE,
220220
TIM_CR1_DIR_UP);
@@ -248,6 +248,44 @@ static void setup_pwm(void)
248248
timer_enable_counter(TIM3);
249249
}
250250

251+
/**
252+
* @brief Setup PWM for the speaker.
253+
*
254+
* TIM1 is used to generate the PWM signals for the speaker:
255+
*
256+
* - Configure channel 3 as output GPIO.
257+
* - Edge-aligned, up-counting timer.
258+
* - Prescale to increment timer counter at TIM1CLK_FREQUENCY_HZ.
259+
* - Set output compare mode to PWM1 (output is active when the counter is
260+
* less than the compare register contents and inactive otherwise.
261+
* - Disable output compare output (speaker is off by default).
262+
* - Enable outputs in the break subsystem.
263+
*
264+
* @see Reference manual (RM0008) "TIMx functional description" and in
265+
* particular "PWM mode" section.
266+
*/
267+
static void setup_speaker(void)
268+
{
269+
gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ,
270+
GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_TIM1_CH3);
271+
272+
rcc_periph_reset_pulse(RST_TIM1);
273+
274+
timer_set_mode(TIM1, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE,
275+
TIM_CR1_DIR_UP);
276+
277+
timer_set_prescaler(TIM1,
278+
(rcc_apb2_frequency / TIM1CLK_FREQUENCY_HZ - 1));
279+
timer_set_repetition_counter(TIM1, 0);
280+
timer_enable_preload(TIM1);
281+
timer_continuous_mode(TIM1);
282+
283+
timer_disable_oc_output(TIM1, TIM_OC3);
284+
timer_set_oc_mode(TIM1, TIM_OC3, TIM_OCM_PWM1);
285+
286+
timer_enable_break_main_output(TIM1);
287+
}
288+
251289
/**
252290
* @brief Configure timer to read a quadrature encoder.
253291
*
@@ -373,7 +411,8 @@ void setup(void)
373411
setup_adc1();
374412
setup_usart();
375413
setup_encoders();
376-
setup_pwm();
414+
setup_motor_driver();
415+
setup_speaker();
377416
setup_mpu();
378417
setup_systick();
379418
}

src/setup.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
/** System clock frequency is set in `setup_clock` */
2828
#define SYSCLK_FREQUENCY_HZ 72000000
29+
#define TIM1CLK_FREQUENCY_HZ 1000000
2930
#define SYSTICK_FREQUENCY_HZ 1000
3031
#define DRIVER_PWM_PERIOD 1024
3132

src/speaker.c

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#include "speaker.h"
2+
3+
uint8_t BASE_NOTE = 9;
4+
uint8_t BASE_OCTAVE = 4;
5+
float BASE_FREQUENCY = 440.;
6+
uint8_t POSITIONS[8] = {9, 11, 0, 2, 4, 5, 7};
7+
8+
/**
9+
* @brief Set the frequency for the speaker.
10+
*
11+
* Frequency is set modulating the PWM signal sent to the speaker.
12+
*
13+
* @param[in] hz Frequency, in Hertz.
14+
*/
15+
static void speaker_set_frequency(float hz)
16+
{
17+
uint16_t period;
18+
19+
period = (uint16_t)(TIM1CLK_FREQUENCY_HZ / hz);
20+
timer_set_period(TIM1, period);
21+
timer_set_oc_value(TIM1, TIM_OC3, period / 2);
22+
}
23+
24+
/**
25+
* @brief Turn on the speaker to play the set frequency.
26+
*/
27+
static void speaker_on(void)
28+
{
29+
timer_enable_counter(TIM1);
30+
timer_enable_oc_output(TIM1, TIM_OC3);
31+
}
32+
33+
/**
34+
* @brief Turn off the speaker.
35+
*/
36+
static void speaker_off(void)
37+
{
38+
timer_disable_counter(TIM1);
39+
timer_disable_oc_output(TIM1, TIM_OC3);
40+
}
41+
42+
/**
43+
* @brief Play a note through the speaker.
44+
*
45+
* Even if this function uses the scientific notation, notes are played with
46+
* the concert pitch (standard pitch). That means A above middle C is the
47+
* reference note, played at 440 Hz.
48+
*
49+
* @param[in] note Which note to play, in scientific notation.
50+
* @param[in] octave Which octave to play, in scientific notation.
51+
* @param[in] accidental Number of semitones to sum to the note.
52+
* @param[in] duration Duration of the note, in seconds.
53+
*/
54+
void speaker_play(char note, uint8_t octave, int8_t accidental, float duration)
55+
{
56+
int16_t sound;
57+
float frequency;
58+
59+
sound = POSITIONS[note - 'A'] - BASE_NOTE + (octave - BASE_OCTAVE) * 12;
60+
sound += accidental;
61+
frequency = pow(2, sound / 12.) * BASE_FREQUENCY;
62+
speaker_set_frequency(frequency);
63+
speaker_on();
64+
sleep_ticks((uint32_t)(duration * 1000));
65+
speaker_off();
66+
}

src/speaker.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#ifndef __SPEAKER_H
2+
#define __SPEAKER_H
3+
4+
#include <math.h>
5+
6+
#include <libopencm3/stm32/timer.h>
7+
8+
#include "setup.h"
9+
10+
void speaker_play(char note, uint8_t octave, int8_t accidental, float duration);
11+
12+
#endif /* __SPEAKER_H */

0 commit comments

Comments
 (0)