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; // 定时器实例为 TIM3
htimx.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 模式 1
sConfigOC.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**********************/