STM32蜂鳴器播放音樂
STM32蜂鳴器播放音樂 Do, Re, Mi, Fa,
1. 功能概述
本系統基于STM32F7系列微控制器,實現了以下功能:
- 通過7個按鍵控制蜂鳴器發聲,按鍵對應不同的音符。
- 每個按鍵對應一個音符(Do, Re, Mi, Fa, Sol, La, Si),按下按鍵時蜂鳴器播放對應音符的聲音。
- 利用PWM技術控制蜂鳴器發聲頻率,實現不同音符的效果。
- 按鍵松開時蜂鳴器停止發聲,防止連續觸發。
2. 硬件接線
2.1 按鍵與GPIO連接
系統使用STM32F7的GPIOB端口,連接7個按鍵,其中:
- 按鍵1: GPIO_PIN_0
- 按鍵2: GPIO_PIN_1
- 按鍵3: GPIO_PIN_3
- 按鍵4: GPIO_PIN_4
- 按鍵5: GPIO_PIN_5
- 按鍵6: GPIO_PIN_6
- 按鍵7: GPIO_PIN_7
每個按鍵的另一端連接到地(GND),GPIO引腳配置為上拉輸入模式(GPIO_PULLUP)。
2.2 蜂鳴器與GPIO連接
蜂鳴器通過GPIOA的PA6引腳與STM32連接:
- PA6配置為TIM3_CH1通道的PWM輸出。
- 蜂鳴器的一端連接到PA6,另一端連接到GND。
2.3 電源
- STM32主控板通過USB供電。
- 所有按鍵和蜂鳴器的電源均由STM32供電。
3. 軟件實現原理
3.1 功能模塊
軟件設計主要包含以下功能模塊:
3.1.1 PWM模塊
- 利用定時器TIM3的PWM功能產生控制蜂鳴器的信號。
- 根據不同音符的頻率設置PWM的周期(ARR)值,調整占空比控制音量。
3.1.2 按鍵掃描模塊
- 周期性讀取GPIOB的引腳狀態,判斷按鍵是否按下。
- 通過數組
KeyStatus[]
保存每個按鍵的狀態(按下為1,松開為0)。
3.1.3 音符播放模塊
- 根據按鍵狀態決定是否播放音符。
- 使用
Play_Tone()
函數設置PWM頻率并播放音符。 - 使用
Stop_Tone()
函數停止蜂鳴器播放。
3.2 軟件流程
3.2.1 主程序流程
主程序主要流程如下:
- 初始化系統時鐘和HAL庫。
- 配置GPIO用于按鍵輸入和蜂鳴器輸出。
- 初始化TIM3定時器的PWM功能。
- 主循環中:
- 調用按鍵掃描函數
Keypad_Read()
更新按鍵狀態。 - 檢查每個按鍵狀態,對應播放音符。
- 播放音符后延時一定時間避免重復觸發。
- 調用
Stop_Tone()
停止蜂鳴器播放。
- 調用按鍵掃描函數
3.2.2 按鍵掃描流程
按鍵掃描采用循環遍歷方式:
- 定義GPIOB引腳的數組
pins[]
,保存每個按鍵對應的GPIO引腳。 - 遍歷引腳數組,調用
HAL_GPIO_ReadPin()
讀取每個按鍵狀態。 - 如果引腳電平為低(GPIO_PIN_RESET),表示按鍵被按下。
- 更新
KeyStatus[]
數組。
3.2.3 音符播放流程
音符播放利用PWM實現:
- 根據音符頻率計算PWM的自動重裝載值(ARR)。
- 調用
__HAL_TIM_SET_AUTORELOAD()
更新TIM3的ARR值。 - 調用
__HAL_TIM_SET_COMPARE()
設置占空比。 - 開始PWM輸出,蜂鳴器發聲。
- 延時指定時間后停止PWM輸出。
主函數代碼:
#include "stm32f7xx.h"
#include "main.h"
#include "./tim/bsp_basic_tim.h"
#include "./led/bsp_led.h"
#include "./usart/bsp_usart.h"
#include "./beep_music/beep_music.h"
TIM_HandleTypeDef htimx; // 定義一個全局定時器句柄void SystemClock_Config(void);
void PWM_Init(void);
void Set_PWM_Frequency(uint32_t frequency);
void Play_Music(int *tune, float *duration, int length);
/*** @brief 主函數* @param 無* @retval 無*/
int main(void)
{/* 初始化系統時鐘為216MHz */SystemClock_Config();/* 初始化LED */LED_GPIO_Config();/* 初始化基本定時器定時,1s產生一次中斷 *///TIMx_Configuration();PWM_Init(); // 初始化 PWM// int length = sizeof(tune) / sizeof(tune[0]); // 獲取音符數量
// Play_Music(tune, duration, length);play_music(); while(1){ }
}/*** @brief System Clock 配置* System Clock 配置如下 : * System Clock source = PLL (HSE)* SYSCLK(Hz) = 216000000* HCLK(Hz) = 216000000* AHB Prescaler = 1* APB1 Prescaler = 4* APB2 Prescaler = 2* HSE Frequency(Hz) = 25000000* PLL_M = 25* PLL_N = 432* PLL_P = 2* PLL_Q = 9* VDD(V) = 3.3* Main regulator output voltage = Scale1 mode* Flash Latency(WS) = 7* @param 無* @retval 無*/
void SystemClock_Config(void)
{RCC_ClkInitTypeDef RCC_ClkInitStruct;RCC_OscInitTypeDef RCC_OscInitStruct;HAL_StatusTypeDef ret = HAL_OK;/* 使能HSE,配置HSE為PLL的時鐘源,配置PLL的各種分頻因子M N P Q * PLLCLK = HSE/M*N/P = 25M / 25 *432 / 2 = 216M*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;RCC_OscInitStruct.HSEState = RCC_HSE_ON;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLM = 25;RCC_OscInitStruct.PLL.PLLN = 432;RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;RCC_OscInitStruct.PLL.PLLQ = 9;ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);if(ret != HAL_OK){while(1) { ; }}/* 激活 OverDrive 模式以達到216M頻率 */ ret = HAL_PWREx_EnableOverDrive();if(ret != HAL_OK){while(1) { ; }}/* 選擇PLLCLK作為SYSCLK,并配置 HCLK, PCLK1 and PCLK2 的時鐘分頻因子 * SYSCLK = PLLCLK = 216M* HCLK = SYSCLK / 1 = 216M* PCLK2 = SYSCLK / 2 = 108M* PCLK1 = SYSCLK / 4 = 54M*/RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; /* 在HAL_RCC_ClockConfig函數里面同時初始化好了系統定時器systick,配置為1ms中斷一次 */ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7);if(ret != HAL_OK){while(1) { ; }}
}/*** @brief 配置系統時鐘* @retval 無*//*** @brief 初始化 PWM* @retval 無*/
void PWM_Init(void)
{// 開啟定時器和 GPIO 時鐘__HAL_RCC_TIM3_CLK_ENABLE(); // 使用 TIM3 作為示例__HAL_RCC_GPIOA_CLK_ENABLE(); // 使用 GPIOA 作為示例// 配置 PWM 輸出引腳GPIO_InitTypeDef GPIO_InitStruct = {0};GPIO_InitStruct.Pin = GPIO_PIN_6; // 使用 PA6(TIM3_CH1)作為 PWM 輸出GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 復用推挽模式GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);// 配置定時器htimx.Instance = TIM3; // 定時器實例為 TIM3htimx.Init.Prescaler = (SystemCoreClock / 1000000) - 1; // 定時器頻率分頻到 1MHz (1μs 精度)htimx.Init.CounterMode = TIM_COUNTERMODE_UP; // 向上計數模式htimx.Init.Period = 1000 - 1; // 默認周期,產生 1kHz 方波htimx.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;htimx.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;HAL_TIM_PWM_Init(&htimx); // 初始化定時器 PWM// 配置 PWM 通道TIM_OC_InitTypeDef sConfigOC = {0};sConfigOC.OCMode = TIM_OCMODE_PWM1; // PWM 模式 1sConfigOC.Pulse = 500; // 默認占空比 50%sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; // 高電平有效sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;HAL_TIM_PWM_ConfigChannel(&htimx, &sConfigOC, TIM_CHANNEL_1); // 配置通道 1// 啟動 PWM 輸出HAL_TIM_PWM_Start(&htimx, TIM_CHANNEL_1);
}/*** @brief 設置 PWM 輸出頻率* @param frequency 頻率 (Hz)* @retval 無*/
void Set_PWM_Frequency(uint32_t frequency)
{if (frequency == 0) // 如果頻率為 0,停止 PWM 輸出{__HAL_TIM_SET_COMPARE(&htimx, TIM_CHANNEL_1, 0);return;}uint32_t period = 1000000 / frequency; // 周期 = 1秒(1000000us) / 頻率__HAL_TIM_SET_AUTORELOAD(&htimx, period - 1); // 設置自動重裝載值__HAL_TIM_SET_COMPARE(&htimx, TIM_CHANNEL_1, period / 2); // 設置占空比為 50%
}/*** @brief 播放音樂* @param tune 音符數組* @param duration 音符持續時間數組* @param length 音符數量* @retval 無*/
void Play_Music(int *tune, float *duration, int length)
{for (int i = 0; i < length; i++){if (tune[i] == 0) // 如果音符為 0,停止播放,表示間隔{Set_PWM_Frequency(0); // 停止 PWM}else{Set_PWM_Frequency(tune[i]); // 設置音符對應的頻率}HAL_Delay(duration[i] * 1000); // 持續時間(將秒轉換為毫秒)}Set_PWM_Frequency(0); // 播放完成后停止 PWM
}/*********************************************END OF FILE**********************/