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;                        // 定时器实例为 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**********************/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/73411.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于 OCO - 2 氧气 A 带辐射数据与地面台站气压观测数据构建近地面气压监测算法方案

基于 OCO - 2 氧气 A 带辐射数据与地面台站气压观测数据构建近地面气压监测算法方案 一、数据获取与准备 (一)OCO - 2 氧气 A 带辐射数据 数据下载:从 OCO - 2 官方数据发布平台(如 NASA 的相关数据存储库),按照研究所需的时间范围(例如,近 5 年的数据以获取足够的样本…

程序员英语口语练习笔记

我是一个程序员&#xff0c;专注于Java, Linux和k8s. I’m a programmer specializing in Java, Linux, and Kubernetes. 这个不是我的bug。 I don’t think this bug is caused by my work. 你能帮我看一下这个代码吗&#xff1f; Can you take a look at this code for me?…

网络地址转换技术(2)

NAT的配置方法&#xff1a; &#xff08;一&#xff09;静态NAT的配置方法 进入接口视图配置NAT转换规则 Nat static global 公网地址 inside 私网地址 内网终端PC2&#xff08;192.168.20.2/24&#xff09;与公网路由器AR1的G0/0/1&#xff08;11.22.33.1/24&#xff09;做…

从零开始理解基于深度学习的语义分割模型:RCA与RCM模块的实现

从零开始理解基于深度学习的语义分割模型:RCA与RCM模块的实现 随着深度学习技术的发展,图像分割任务取得了长足的进步。本文将从一个具体的PyTorch代码实例出发,带大家了解一种 novel 的语义分割网络架构——RCA(Rectangular Self-Calibration Attention)和 RCM(Rectang…

【Linux网络-多路转接select】

代码&#xff1a;https://gitee.com/nanyi-c/linux/tree/master/day50 一、I/O多路转接之select 1.初始select 系统提供select函数来实现多路复用输入/输出模型 select系统调用是用来让我们的程序监视多个文件描述符的状态变化的程序会停在select这里等待&#xff0c;直到被…

2025 年中国家电零售与创新趋势解析:以旧换新国补激活需求,AI 技术渗透至研发、供应链、营销

一、产业环境&#xff1a;政策驱动与技术变革下的挑战与机遇 在全球经济波动与国内消费转型的双重背景下&#xff0c;中国家电产业正经历前所未有的变革。2024 年&#xff0c;家电行业面临的 “三座大山”—— 短期消费信心低迷、中期房地产降温、长期人口下行压力 —— 持续施…

SpringBoot分布式项目中MyBatis实战技巧:从配置到性能优化

引言 在分布式系统架构中&#xff0c;SpringBoot与MyBatis的组合已成为企业级开发的黄金搭档。但在实际项目中&#xff0c;开发者常面临多数据源管理、SQL性能优化、分布式事务等挑战。本文将从实战角度出发&#xff0c;分享7个关键技巧和避坑指南。 一、多数据源动态切换实战…

【大模型基础_毛玉仁】4.1 参数高效微调简介

目录 4 参数高效微调4.1 参数高效微调简介4.1.1 下游任务适配1&#xff09;上下文学习&#xff08;In-context learning&#xff09;2&#xff09;指令微调&#xff08;Instruction Tuning&#xff09; 4.1.2 参数高效微调4.1.3 参数高效微调的优势 4 参数高效微调 大语言模型…

Postman使用02、断点、fiddler弱网测试

脚本操作 一、脚本导出 1.导出json脚本 2.打包json文件 3.下载的文件 二 .导入脚本 1.选择文件 2.点击导入 3.导入的接口 三.多接口运行 1.集合右键&#xff0c;点击run &#xff0c;运行多个接口 2.编辑环境&#xff0c;集合&#xff0c;执行次数等 3.运行多个接口 四.运行…

深挖增长内核:好产品驱动增长的全方位解析

年前在老板的带领下深入学习了《增长黑客》&#xff0c;并思考了在CPS站外引流的落地方案&#xff0c;最近刚好在做京东联盟的京粉推客增长体系建设&#xff0c;再次回顾一下增长黑客方法以及记录一下思考。 好产品才是增长的根本。增长黑客理念风靡&#xff0c;“啊哈时刻” 概…

新手小白 react-useEffect 使用场景

useEffect 是 React 中的一个非常重要的 Hook&#xff0c;用于处理组件的副作用&#xff08;side effects&#xff09;。它通常在以下几种场景中使用&#xff1a; 1. 数据获取 当组件加载时&#xff0c;需要从外部 API 获取数据&#xff0c;或者从本地存储中读取数据。示例&a…

MySQL 调优:查询慢除了索引还能因为什么?

文章目录 情况一&#xff1a;连接数过小情况二&#xff1a;Buffer Pool 太小 MySQL 查询慢除了索引还能因为什么&#xff1f;MySQL 查询慢&#xff0c;我们一般也会想到是因为索引&#xff0c;但除了索引还有哪些原因会导致数据库查询变慢呢&#xff1f; 以下以 MySQL 中一条 S…

【操作系统】进程三种状态?进程间状态的切换?挂起态?

进程状态 进程的五种&#xff08;三种&#xff09;状态&#xff1a; 新建&#xff08;New&#xff09;&#xff1a;进程刚被创建&#xff0c;尚未加入到就绪队列&#xff1b;就绪&#xff08;Ready&#xff09;&#xff1a;进程已获得除CPU外的所有资源&#xff0c;等待被调度执…

计算机控制系统的最小拍控制器设计及仿真分析

1题目 开环传递函数 G(s) 2/(s(0.5s1)) &#xff0c;采样周期 T0.5 秒&#xff0c;设计单位速度输入下的最小拍控制器 1.1 方法1 根据课本中的步骤&#xff0c;最小拍控制器的设计步骤如下&#xff1a; 1. 确定对象的离散传递函数G(z)&#xff0c;并确定其零极点。 2. 确定…

哈希--哈希桶

哈希桶是哈希表&#xff08;散列表&#xff09;中的一个概念&#xff0c;是哈希表数组中的每个元素 &#xff0c;用于存储键值对数据。它有以下特点和相关要点&#xff1a; 结构与原理&#xff1a;哈希表底层常由数组构成&#xff0c;数组的每个元素即哈希桶。通过哈希函数计算…

Linux多线程详解

Linux多线程详解 一、Linux多线程概念1.1 什么是线程1.2 进程和线程1.3 进程的多个线程共享1.4 进程和线程的关系 二、Linux线程控制2.1 POSIX线程库2.2 线程创建2.3 获取线程ID pthread_self2.4 线程等待pthread_join2.5 线程终止2.6 线程栈 && pthread_t2.7 线程的局…

Midscene.js自然语言驱动的网页自动化全指南

一、概述 网页自动化在数据抓取、UI 测试和业务流程优化中发挥着重要作用。然而&#xff0c;传统工具如 Selenium 和 Puppeteer 要求用户具备编程技能&#xff0c;编写复杂的选择器和脚本维护成本高昂。Midscene.js 通过自然语言接口革新了这一领域&#xff0c;用户只需描述任…

winstart.wsf 病毒清理大作战

0x00 背景 发现感染了winstart.wsf 病毒如何清理。 0x01 现象 遍历Users下每个目录以及C:\和C:\Windows\Temp 2个目录写入病毒文件。 C:\Users\Administrator\AppData\Local\Temp\winstart.wsf C:\Users\Administrator\AppData\Roaming\Microsoft\Windows\Start Menu\Program…

多路转接Poll

在之前我们讲过select是最古老的多路转接方案&#xff0c;古老就意味着他不是很方便使用&#xff0c;他需要用户手动保存fd_set这个位图结构&#xff0c;来表示读写事件的关注与否或者就绪性。 而且由于fd_set的大小是固定的&#xff0c;这就意味着他能管理的套接字文件描述符是…

多层感知机的简洁实现

《动手学深度学习》-4.3-笔记 import torch from torch import nn from d2l import torch as d2l 导入必要的库和模块 net nn.Sequential(nn.Flatten(),nn.Linear(784, 256),nn.ReLU(),nn.Linear(256, 10))def init_weights(m):if type(m) nn.Linear:nn.init.normal_(m.we…