This project is a microcontroller-based sound generator written in C. It features three buttons that allow the user to play a sound or adjust its pitch up and down. It serves as a practical demonstration of basic GPIO control and button logic.
Step 1: Hardware Configuration1. Create a Common Ground
- Connect a jumper wire from a GND pin on RT-Spark board to the negative (-) rail on your breadboard.
- Connect a jumper wire from a 5V pin on RT-Spark board to the positive (+) rail on your breadboard.
Place your three pushbuttons across the middle ravine of the breadboard.
- SW1 (Frequency Up): Connect one leg to the Ground rail. Connect the other leg to pin PA0.
- SW2 (Play Tone): Connect one leg to the Ground rail. Connect the other leg to pin PA1.
- SW3 (Frequency Down): Connect one leg to the Ground rail. Connect the other leg to pin PA2.
- Connect the Positive (longer leg) of the piezo directly to pin PA8.
- Connect the Negative (shorter leg) of the piezo directly to your GND rail.
Step 2: CubeMX Configuration
- Open STM32CubeMX and start a new project and select STM32F407ZGT6.
- Enable Debugging: Expand System Core, click SYS, and set Debug to Serial Wire.
- Configure Output: Left-click pin PA8 and select
GPIO_Output. - Configure Inputs: Left-click PA0, PA1, and PA2, setting each to
GPIO_Input. - Enable Pull-Ups: Navigate to System Core > GPIO. Select PA0, PA1, and PA2. In the configuration window below, change their GPIO Pull-up/Pull-down setting to Pull-up.
- Go to the Project Manager tab, name your project, ensure the Toolchain is set to STM32CubeIDE, and then Generate Code.
Step 3: CubeIDE Code Setup1. Variables & Function Prototypes
Scroll down to /* USER CODE BEGIN 0 */. This is where we will place our variables, the custom DWT microsecond delay, and the logic functions that read the pins (PA0, PA1, PA2) and toggle the piezo (PA8).
/* USER CODE BEGIN 0 */
volatile uint32_t period_us = 1000;
uint8_t prev_sw1 = 1, prev_sw3 = 1;
void delay_us(uint32_t us) {
uint32_t start = DWT->CYCCNT;
uint32_t ticks = us * (HAL_RCC_GetHCLKFreq() / 1000000);
while ((DWT->CYCCNT - start) < ticks);
}
void play_tone(uint32_t duration_ms) {
uint32_t half_period = period_us / 2;
uint32_t cycles = (duration_ms * 1000UL) / period_us;
for (uint32_t i = 0; i < cycles; i++) {
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_8); // PA8 = piezo
delay_us(half_period);
}
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET);
}
void update_buttons(void) {
uint8_t sw1 = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0); // PA0 = freq up
uint8_t sw2 = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1); // PA1 = play
uint8_t sw3 = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_2); // PA2 = freq down
if (sw1 == GPIO_PIN_RESET && prev_sw1 == GPIO_PIN_SET) {
if (period_us > 100) period_us -= 100;
}
if (sw3 == GPIO_PIN_RESET && prev_sw3 == GPIO_PIN_SET) {
if (period_us < 10000) period_us += 100;
}
prev_sw1 = sw1;
prev_sw3 = sw3;
if (sw2 == GPIO_PIN_RESET) {
play_tone(50);
}
}
/* USER CODE END 0 */2. Enabling the Cycle Counter (USER CODE BEGIN 2)
To make sure our custom delay_us function has access to the CPU's internal clock cycles, we must enable the DWT counter right before the main loop begins. Locate /* USER CODE BEGIN 2 */ inside your main() function:
/* USER CODE BEGIN 2 */
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CYCCNT = 0;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
/* USER CODE END 2 */3. The Main Loop (USER CODE BEGIN WHILE)
Finally, we need to constantly poll the buttons so the device can react instantly. Drop the update_buttons() function directly into the infinite loop.
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1) {
update_buttons();
/* USER CODE END WHILE */Step 4: Physical Test
- Play a Sound: Press and hold down SW2. You should hear a continuous square wave tone coming from your piezo buzzer.
- Sweep Up: While continuing to hold down SW2, repeatedly tap SW1. You will hear the pitch of the tone steadily get higher as the microsecond delay period decreases.
- Drop the Bass: While holding SW2, tap SW3 multiple times. You will hear the pitch drop lower and lower as the period increases.
Walkthrough of the physical circuit, showing how the switches, resistor, capacitor, and speaker connect to the development board.
Live demo of the project: holding SW2 plays the tone while SW1 and SW3 raise and lower the pitch in real time.




Comments