当前位置: 首页 > article >正文

STM32蜂鸣器播放音乐

STM32蜂鸣器播放音乐

STM32蜂鸣器播放音乐 Do, Re, Mi, Fa,

1. 功能概述

本系统基于STM32F7系列微控制器,实现了以下功能:

  1. 通过7个按键控制蜂鸣器发声,按键对应不同的音符。
  2. 每个按键对应一个音符(Do, Re, Mi, Fa, Sol, La, Si),按下按键时蜂鸣器播放对应音符的声音。
  3. 利用PWM技术控制蜂鸣器发声频率,实现不同音符的效果。
  4. 按键松开时蜂鸣器停止发声,防止连续触发。

在这里插入图片描述

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 主程序流程

主程序主要流程如下:

  1. 初始化系统时钟和HAL库。
  2. 配置GPIO用于按键输入和蜂鸣器输出。
  3. 初始化TIM3定时器的PWM功能。
  4. 主循环中:
    • 调用按键扫描函数Keypad_Read()更新按键状态。
    • 检查每个按键状态,对应播放音符。
    • 播放音符后延时一定时间避免重复触发。
    • 调用Stop_Tone()停止蜂鸣器播放。
3.2.2 按键扫描流程

按键扫描采用循环遍历方式:

  1. 定义GPIOB引脚的数组 pins[],保存每个按键对应的GPIO引脚。
  2. 遍历引脚数组,调用HAL_GPIO_ReadPin()读取每个按键状态。
  3. 如果引脚电平为低(GPIO_PIN_RESET),表示按键被按下。
  4. 更新 KeyStatus[] 数组。
3.2.3 音符播放流程

音符播放利用PWM实现:

  1. 根据音符频率计算PWM的自动重装载值(ARR)。
  2. 调用__HAL_TIM_SET_AUTORELOAD()更新TIM3的ARR值。
  3. 调用__HAL_TIM_SET_COMPARE()设置占空比。
  4. 开始PWM输出,蜂鸣器发声。
  5. 延时指定时间后停止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**********************/


http://www.kler.cn/a/601070.html

相关文章:

  • 【Linux-驱动开发-GPIO子系统】
  • ECharts实现数据可视化
  • xiaozhi-esp32-server在linux下源码安装
  • msyql--基本操作之运维篇
  • SOLIDEDGE 至 STL 转换:数字化设计制造的关键衔接
  • 使用UDP消息与iptables实现TCP端口敲门安全技术
  • Rust从入门到精通之进阶篇:20.项目实践
  • 算法-动态规划二
  • 软件性能效率测试工具有哪些?专业第三方软件检测机构推荐
  • PyTorch 深度学习实战(24):分层强化学习(HRL)
  • Sqoop-试题
  • 结合DrRacket学习《如何设计程序,第二版》
  • 基于Python的机器学习入门指南
  • Blender配置渲染设置并输出动画
  • 在转换不同格式时,保持正确的宽高比可以避免画面变形
  • Python FastApi(5):请求体、查询参数和字符串校验
  • k8s存储介绍(四)hostpath
  • 智能汽车图像及视频处理方案,支持视频实时拍摄特效能力
  • uv - pip 接口
  • 【多媒体交互】Unity+普通摄像头实现UI事件分析