STM32简易音乐播放器(HAL库)

一、设计描述

        本设计以STM32MP157A单片机为核心控制器,加上其他的模块一起组成基于单片机的音乐盒的整个系统,通过不同频率的PWM使蜂鸣器播放音乐,通过按键中断实现歌曲切换,音量调节,定时器中断实现播放速度调节,LED灯也会随着音乐而闪烁。

        硬件:STM32MP157A

        软件:STM32CUBEIDE

音乐盒的功能:

1、能稳定的播放出音乐,满足使用者对音乐盒的需求。

2、具有开始和暂停的功能,按下暂停键音乐会停止播放,再按下开始,音乐会接着播放。

3、可以通过按下切换键播放下一首歌曲。

4、可以通过按下变速键,实现歌曲由1倍数-1.2倍数-1.5倍数-2倍数-0.8倍数-0.6倍数-1倍数的切换。

5、可以通过按下音量键使音量逐渐变大,到达阈值之后音量变为0实现静音,再按又会逐渐增大。

6、led会随着歌曲的音调而变化。

二、基本配置信息

        通常情况可以采用HAL_Delay()来实现一个音的持续时间,我们将其换成定时器,便于控制。再就是PWM控制蜂鸣器,我们需要设置对应的高中低音。

        定时器:使用TIM2配置出一个0.1s的中断,我们将一个半拍按0.5秒算,这样我们只需要改变   对tim2中断的计数来改变播放速度。

        PWM蜂鸣器:我们先了解一个公式 || (  (PSC+1)/CLK ) * ( ARR+1 ) = 1 / f   ||  f(频率)

        clk:系统时钟(64MHZ)(STM32MP157A)我们在程序中会更改ARR来改变频率,所以我们需要事先确定一个合适的PSC。 频率:一秒执行多少次。

我们取 1/f 中 取中音1频率  1/523.3 = 0.00191094974202178482705904834703

很长一串小数,可能还没算完,那我们可以取小数点后五位数据。我们以 0.00001为基础也就是让

(PSC+1)/CLK )  = 0.00001  已知clk = 64*10^6   得出PSC + 1 = 640

那么ARR是用于干什么的呢。1/523.3 就约等于 0.00001 *( ARR+1 ) 那么 ARR+1就等于191。

总结一下:我们通过计算得出一个基数,PSC配置为 640 - 1 ,明显ARR在最初时没有实际效果,因为音频不同相应的ARR也不同(这里简易 ARR = 999,不建议设置太大)

                                                        音乐盒 架构图

三、STM32CUBEIDE配置

1、定时器--100ms

2、PWM配置(蜂鸣器 -- PB6)

3、按键极LED

4、NVIC

四、程序编写

1. 基础变量

        因为有许多中断,所以将在stm32mp1xx_it.c  和 main.c 之间交换信息。

stm32mp1xx_it.c 基础变量

int time_100ms_cnt = 0; //0.1s计数器
int Beat_speed = 5;		//节拍速度,代表半个节拍需要多少个0.1s
int Beat_speed_n = 0;	//实际执行的节拍数

int Beat_num = 2;		//这个一个音需要多少个 半拍
int flag = 0; 			//当其等于 1 时,表示一个音结束
int EN = 0;				//使能信号,用于开启整个音乐盒
int list = 0;			//音乐列表
int list_max = 0;		//音乐总数
int Low_volume = 5;		//音量大小
#define Low_volume_cnt  3	//音量大小增加数

main.c基础变量

extern int time_100ms_cnt = 0; //0.1s计数器
extern int Beat_speed = 5;		//节拍速度,代表半个节拍需要多少个0.1s
extern int Beat_speed_n = 0;	//实际执行的节拍数

extern int Beat_num = 2;		//这个一个音需要多少个 半拍
extern int flag = 0; 			//当其等于 1 时,表示一个音结束
extern int EN = 0;				//使能信号,用于开启整个音乐盒
extern int list = 0;			//音乐列表
extern int list_max = 0;		//音乐总数
extern int Low_volume = 5;		//音量大小

2. LED随音乐跳动


//将LED设置成全灭
void LED_RESET(){
	  HAL_GPIO_WritePin(GPIOF, GPIO_PIN_1, GPIO_PIN_RESET);
	  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_7, GPIO_PIN_RESET);
	  HAL_GPIO_WritePin(GPIOI, GPIO_PIN_11|GPIO_PIN_10, GPIO_PIN_RESET);
}

void LED(int i){
	if(i >= 1)  HAL_GPIO_WritePin(GPIOF, GPIO_PIN_1, GPIO_PIN_SET);
	if(i >= 2)  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_7, GPIO_PIN_SET);
	if(i >= 3)  HAL_GPIO_WritePin(GPIOI, GPIO_PIN_11,GPIO_PIN_SET);
	if(i >= 4)  HAL_GPIO_WritePin(GPIOI, GPIO_PIN_10, GPIO_PIN_SET);
}
//随音节输入,改变LED状�??
void LED_BEEP(int i){
	LED_RESET();
	switch(i){
	case 1:{
		LED(1);
		break;
	}
	case 2:{
		LED(1);
		break;
	}

	case 3:{
		LED(2);
		break;
	}
	case 4:{
		LED(2);
		break;
	}

	case 5:{
		LED(3);
		break;
	}
	case 6:{
		LED(3);
		break;
	}

	case 7:{
		LED(4);
		break;
	}
	default:{
		LED_RESET();
		  break;
	}
	}
}

3. 配置低中高音


int tone[3][8];
//初始化高中低音频率
void tone_init(){
	tone[1][0] = 0;	//不执行音乐
	tone[1][1] = 191;
	tone[1][2] = 170;
	tone[1][3] = 151;
	tone[1][4] = 143;
	tone[1][5] = 127;
	tone[1][6] = 113;
	tone[1][7] = 101;
    // 低音 (Low)
    for (int i = 0; i < 8; i++) {
        tone[0][i] = tone[1][i] * 2; // 只是低音 近似的值
    }

    // 高音 (High)
    for (int i = 0; i < 8; i++) {
        tone[2][i] = tone[1][i] / 2; // 只是高音  近似的值
    }
}

4.乐谱结构体

#define MAX_unit_num 200 //�??大乐谱数�??
//创建结构体保存乐�??
struct music_unit{
	char name[50];		//乐谱名称
	int unit[MAX_unit_num];		//发什么音
	int unit_HL[MAX_unit_num];	//发高音或者其�??
	int time[MAX_unit_num];		//发音时间
	//int time_4[MAX_unit_num];	//判断是否�??1/4�??
	int num;			//记录有多少个
}music[25];

5. 编写乐谱


//创建乐谱 返回有多少首音乐
int music_init(){
	int cnt = 0;
	//第一首音�?? 生日快乐
	strcpy(music[0].name, "生日快乐"); 				// 使用strcpy复制字符�?? 给音乐命�??
	int music0_unit[29] = {0,0, 5,5,6,5,1,7, 5,5,6,5,2,1,
								5,5,6,3,1,7, 6,4,4,3,1,2,1,
								0,0};		//基础乐谱
	int music0_time[29] = {1,1, 1,1,2,2,2,3, 1,1,2,2,2,3,
								2,2,2,2,2,2, 2,2,2,2,2,2,3,
								1,1};		//乐谱节拍
	music[0].num = 29;										//乐谱总数
	int music0_unit_HL[29] = {1,1,
								0,0,0,0,1,0, 0,0,0,0,1,1,
								0,0,1,1,1,0, 0,1,1,1,1,1,1,
								1,1}; 	//乐谱全为中音

	//第二首音�?? �??闪一闪亮晶晶
	cnt++;
	strcpy(music[1].name, "�??闪一闪亮晶晶"); 					// 使用strcpy复制字符�?? 给音乐命�??
	int music1_unit[44] = {0,
						   1,1,5,5,6,6,5, 4,4,3,3,2,2,1,
						   5,5,4,4,3,3,2, 5,5,4,4,3,3,2,
						   1,1,5,5,6,6,5, 4,4,3,3,2,2,1,
						   0};		//基础乐谱
	int music1_time[44] = {2,
						   2,2,2,2,2,2,3, 2,2,2,2,2,2,3,
						   2,2,2,2,2,2,3, 2,2,2,2,2,2,3,
						   2,2,2,2,2,2,3, 2,2,2,2,2,2,3,
						   2};		//乐谱节拍
	int music1_unit_HL[44] =
						  {1,
						   1,1,1,1,1,1,1, 1,1,1,1,1,1,1,
						   1,1,1,1,1,1,1, 1,1,1,1,1,1,1,
						   1,1,1,1,1,1,1, 1,1,1,1,1,1,1,
						   1}; 		//乐谱全为中音
	music[1].num = 44;											//乐谱总数



	//第三首音�?? 两只老虎
	cnt++;
	strcpy(music[2].name, "两只老虎"); 					// 使用strcpy复制字符�?? 给音乐命�??
	int music2_unit[38] = {0,
						   1,2,3,1, 1,2,3,1, 3,4,5,5, 3,4,5,5,
						   5,6,5,4, 3,1,5,6, 5,4,3,1, 1,5,1,1,
						   1,5,1,1, 0};		//基础乐谱
	int music2_time[38] = {2,
						   1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1,
						   0,0,0,0, 1,1,0,0, 0,0,1,1, 1,1,1,2,
						   1,1,1,2, 2};		//乐谱节拍
	int music2_unit_HL[38] =
						  {1,
					       1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1,
						   1,1,1,1, 1,1,1,1, 1,1,1,1, 1,0,1,1,
						   1,0,1,1, 1}; 		//乐谱�??   中音
	music[2].num = 38;											//乐谱总数


	//第四首音�?? 青花瓷片�??
	cnt++;
	strcpy(music[3].name, "青花瓷片选"); 					// 使用strcpy复制字符�?? 给音乐命�??
	int music3_unit[100] = {0,0,0,0, 0,5,5,3, 2,3,6,2, 3,5,3,2, 2,5,5,3,
						    2,3,5,2, 3,5,2,1, 1,1,2,3, 5,6,5,4, 5,3,3,2,
						    2,2,1,2, 1,1,2,1, 2,3,5,3, 3,3,5,5, 3,2,3,6,
						    2,3,5,3, 2,2,5,5, 3,2,3,5, 2,3,5,2, 1,1,1,2,
						    3,5,6,5, 4,5,3,3, 2,2,5,3, 2,2,2,1, 1,0,0,0};		//基础乐谱

	int music3_time[100] = {0,0,0,0, 0,0,0,0, 0,0,1,0, 0,0,0,2, 0,0,0,0,
							0,0,1,0, 0,0,0,2, 0,0,0,0, 0,0,0,0, 0,0,0,0,
							2,0,0,0, 0,0,0,0, 0,1,0,0, 2,0,0,0, 0,0,0,1,
							0,0,0,0, 2,0,0,0, 0,0,0,1, 0,0,0,0, 2,0,0,0,
							0,0,0,0, 0,0,0,0, 0,2,0,1, 0,0,0,1, 2,1,1,1};		//乐谱节拍

	for(int i =0;i<100;i++)
		music3_time[i] = music3_time[i]+1;

	int music3_unit_HL[100] =
						  { 1,1,1,1, 1,1,1,1, 1,1,0,1, 1,1,1,1, 1,1,1,1,
							1,1,0,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1,
							1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,0,
							1,1,1,1, 1,1,1,1, 1,1,1,0, 1,1,1,1, 1,1,1,1,
							1,1,1,1, 1,1,1,1, 1,1,0,1, 1,1,1,1, 1,1,1,1}; 		//乐谱�??   中音
	music[3].num = 100;											//乐谱总数




	for (int i = 0; i < MAX_unit_num; i++) {
		//将乐谱保存进结构�??
		if(i<music[0].num){//确保数据正确
			music[0].unit[i] =music0_unit[i];
			music[0].unit_HL[i] =music0_unit_HL[i];
			music[0].time[i] =music0_time[i];
		}


		//将乐谱保存进结构�??
		if(i<music[1].num){//确保数据正确
			music[1].unit[i] =music1_unit[i];
			music[1].unit_HL[i] =music1_unit_HL[i];
			music[1].time[i] =music1_time[i];
		}

		//将乐谱保存进结构�??
		if(i<music[2].num){//确保数据正确
			music[2].unit[i] =music2_unit[i];
			music[2].unit_HL[i] =music2_unit_HL[i];
			music[2].time[i] =music2_time[i];
		}


		//将乐谱保存进结构�??
		if(i<music[3].num){//确保数据正确
			music[3].unit[i] =music3_unit[i];
			music[3].unit_HL[i] =music3_unit_HL[i];
			music[3].time[i] =music3_time[i];
		}
	}


	return cnt;
}

6. 音乐实现函数



//播放音乐 N首音乐 音量 X 0 - 10
void play_music(int n, int x){
	static int ni = 0; 		//用于判断 是否换了音乐
	static int cnt = 0;		//记录播放到哪一个 音节
	if(ni != n ){//如果音乐换了
		ni = n;
		cnt = 0;
		__HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_1,0);//设置音量
		HAL_Delay(1000);//
	}

	//
	int value = tone[music[n].unit_HL[cnt]][music[n].unit[cnt]];	//获取频率
	if(flag == 1){	//接受到一个音节结束
		flag = 0;	//复位
		Beat_num = music[n].time[cnt]; 				//这个音需要多少个半拍
		LED_BEEP(music[n].unit[cnt]);				//LED随音节变动

		if(music[n].time[cnt] == 0){// 后面添加的 1/4 拍
			Beat_speed_n = Beat_speed /2;
		}
		else{//如果没有1/4拍
			Beat_speed_n = Beat_speed;
		}

		__HAL_TIM_SET_AUTORELOAD(&htim4,value);		//自动加载频率

		cnt ++; 	//可进行下一次音节
		if(cnt >= music[n].num){ //如果一首音乐播放完毕
			cnt = 0;//重新播放
			//__HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_1,0);//设置音量
			//HAL_Delay(500);//
		}
	}
	//__HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_1,x * (value/100));//设置音量
	__HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_1,(value/10)*x);//设置音量
}

7. 全部中断


void EXTI0_IRQHandler(void)
{
  /* USER CODE BEGIN EXTI0_IRQn 0 */
	if(HAL_GPIO_ReadPin(GPIOG, GPIO_PIN_0) == 0)//确保数据稳定
	{
		//每次按下解决 音量�?? Low_volume_cnt
		Low_volume = Low_volume + Low_volume_cnt;
		if(Low_volume >= 10)
			Low_volume = 0;
	}
  /* USER CODE END EXTI0_IRQn 0 */
  HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
  /* USER CODE BEGIN EXTI0_IRQn 1 */

  /* USER CODE END EXTI0_IRQn 1 */
}

/**
  * @brief This function handles EXTI line1 interrupt.
  */
void EXTI1_IRQHandler(void)
{
  /* USER CODE BEGIN EXTI1_IRQn 0 */
	if(HAL_GPIO_ReadPin(GPIOG, GPIO_PIN_1) == 0)//确保数据稳定
	{
		//控制音乐播放的�?�度
		static int i = 0;
		i++;
		//倍数计算公式 1 + (1 - (新的节拍速度 / 原来的节拍�?�度))
		switch(i){
		case 0:{
			Beat_speed = 5;	//0.5s半个节拍,正常+�?�度
			break;
		}
		case 1:{
			Beat_speed = 4;	//1.2倍数
			break;
		}
		case 2:{
			Beat_speed = 3;	//约等�?? 1.5倍数
			break;
		}
		case 3:{
			Beat_speed = 1;	//约等�?? 2 倍数
			break;
		}
		case 4:{
			Beat_speed = 6;	//约等�?? 0.8 倍数
			break;
		}
		case 5:{
			Beat_speed = 7;	//约等�?? 0.6 倍数
			break;
		}

		default:{
			Beat_speed = 5;	//0.5s半个节拍,正常�?�度
			i=0;
			break;
		}
		}
	}
  /* USER CODE END EXTI1_IRQn 0 */
  HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);
  /* USER CODE BEGIN EXTI1_IRQn 1 */

  /* USER CODE END EXTI1_IRQn 1 */
}

/**
  * @brief This function handles EXTI line2 interrupt.
  */
void EXTI2_IRQHandler(void)
{
  /* USER CODE BEGIN EXTI2_IRQn 0 */
	//按下�??次音乐进入下�??�??
	if(HAL_GPIO_ReadPin(GPIOG, GPIO_PIN_2) == 0)//确保数据稳定
	{
		list++;
		if(list > list_max){
			list = 0;
		}
	}
  /* USER CODE END EXTI2_IRQn 0 */
  HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2);
  /* USER CODE BEGIN EXTI2_IRQn 1 */

  /* USER CODE END EXTI2_IRQn 1 */
}

/**
  * @brief This function handles TIM2 global interrupt.
  */
void TIM2_IRQHandler(void)
{
  /* USER CODE BEGIN TIM2_IRQn 0 */
	if(EN == 1)
		time_100ms_cnt++;
	else
		time_100ms_cnt = time_100ms_cnt;	//其余状�?�不计数

	if(time_100ms_cnt >= Beat_speed_n * Beat_num){	//这个音节结束
		time_100ms_cnt = 0;
		flag = 1;	//发�?�音节结束信�??
	}
  /* USER CODE END TIM2_IRQn 0 */
  HAL_TIM_IRQHandler(&htim2);
  /* USER CODE BEGIN TIM2_IRQn 1 */

  /* USER CODE END TIM2_IRQn 1 */
}

/**
  * @brief This function handles EXTI line9 interrupt.
  */
void EXTI9_IRQHandler(void)
{
  /* USER CODE BEGIN EXTI9_IRQn 0 */
	if(HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_9) == 0)//确保数据稳定
		EN = !EN;
  /* USER CODE END EXTI9_IRQn 0 */
  HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_9);
  /* USER CODE BEGIN EXTI9_IRQn 1 */

  /* USER CODE END EXTI9_IRQn 1 */
}

五、总代码

main.c

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

#include <string.h> // 引入strcpy函数�?�?的头文件
/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
TIM_HandleTypeDef htim2;
TIM_HandleTypeDef htim4;

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_TIM4_Init(void);
static void MX_TIM2_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

//将LED设置成全�??
void LED_RESET(){
	  HAL_GPIO_WritePin(GPIOF, GPIO_PIN_1, GPIO_PIN_RESET);
	  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_7, GPIO_PIN_RESET);
	  HAL_GPIO_WritePin(GPIOI, GPIO_PIN_11|GPIO_PIN_10, GPIO_PIN_RESET);
}

void LED(int i){
	if(i >= 1)  HAL_GPIO_WritePin(GPIOF, GPIO_PIN_1, GPIO_PIN_SET);
	if(i >= 2)  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_7, GPIO_PIN_SET);
	if(i >= 3)  HAL_GPIO_WritePin(GPIOI, GPIO_PIN_11,GPIO_PIN_SET);
	if(i >= 4)  HAL_GPIO_WritePin(GPIOI, GPIO_PIN_10, GPIO_PIN_SET);
}
//随音节输入,改变LED状�??
void LED_BEEP(int i){
	LED_RESET();
	switch(i){
	case 1:{
		LED(1);
		break;
	}
	case 2:{
		LED(1);
		break;
	}

	case 3:{
		LED(2);
		break;
	}
	case 4:{
		LED(2);
		break;
	}

	case 5:{
		LED(3);
		break;
	}
	case 6:{
		LED(3);
		break;
	}

	case 7:{
		LED(4);
		break;
	}
	default:{
		LED_RESET();
		  break;
	}
	}
}


extern int time_100ms_cnt; 	//0.1s计数�??
extern int Beat_speed;		//节拍速度,代表半个节拍需要多少个0.1s
extern int Beat_speed_n;	//实际执行的节�??

extern int Beat_num;		//这个�?? �??要多少个 半拍
extern int flag; 			//当其等于 1 时,表示�??个音结束
extern int EN;				//使能信号,用于开启整个音�??
extern int list ;			//音乐列表
extern int list_max ;		//音乐总数
extern int Low_volume;		//音量大小

int tone[3][8];
//初始化高中低�??
void tone_init(){
	tone[1][0] = 0;	//不执�??
	tone[1][1] = 191;
	tone[1][2] = 170;
	tone[1][3] = 151;
	tone[1][4] = 143;
	tone[1][5] = 127;
	tone[1][6] = 113;
	tone[1][7] = 101;
    // 低音 (Low)
    for (int i = 0; i < 8; i++) {
        tone[0][i] = tone[1][i] * 2; // 只是�??个近似的值,实际值可能不�??
    }

    // 高音 (High)
    for (int i = 0; i < 8; i++) {
        tone[2][i] = tone[1][i] / 2; // 只是�??个近似的值,实际值可能不�??
    }
}


#define MAX_unit_num 200 //�??大乐谱数�??
//创建结构体保存乐�??
struct music_unit{
	char name[50];		//乐谱名称
	int unit[MAX_unit_num];		//发什么音
	int unit_HL[MAX_unit_num];	//发高音或者其�??
	int time[MAX_unit_num];		//发音时间
	//int time_4[MAX_unit_num];	//判断是否�??1/4�??
	int num;			//记录有多少个
}music[25];

//创建乐谱 返回有多少首音乐
int music_init(){
	int cnt = 0;
	//第一首音�?? 生日快乐
	strcpy(music[0].name, "生日快乐"); 				// 使用strcpy复制字符�?? 给音乐命�??
	int music0_unit[29] = {0,0, 5,5,6,5,1,7, 5,5,6,5,2,1,
								5,5,6,3,1,7, 6,4,4,3,1,2,1,
								0,0};		//基础乐谱
	int music0_time[29] = {1,1, 1,1,2,2,2,3, 1,1,2,2,2,3,
								2,2,2,2,2,2, 2,2,2,2,2,2,3,
								1,1};		//乐谱节拍
	music[0].num = 29;										//乐谱总数
	int music0_unit_HL[29] = {1,1,
								0,0,0,0,1,0, 0,0,0,0,1,1,
								0,0,1,1,1,0, 0,1,1,1,1,1,1,
								1,1}; 	//乐谱全为中音

	//第二首音�?? �??闪一闪亮晶晶
	cnt++;
	strcpy(music[1].name, "�??闪一闪亮晶晶"); 					// 使用strcpy复制字符�?? 给音乐命�??
	int music1_unit[44] = {0,
						   1,1,5,5,6,6,5, 4,4,3,3,2,2,1,
						   5,5,4,4,3,3,2, 5,5,4,4,3,3,2,
						   1,1,5,5,6,6,5, 4,4,3,3,2,2,1,
						   0};		//基础乐谱
	int music1_time[44] = {2,
						   2,2,2,2,2,2,3, 2,2,2,2,2,2,3,
						   2,2,2,2,2,2,3, 2,2,2,2,2,2,3,
						   2,2,2,2,2,2,3, 2,2,2,2,2,2,3,
						   2};		//乐谱节拍
	int music1_unit_HL[44] =
						  {1,
						   1,1,1,1,1,1,1, 1,1,1,1,1,1,1,
						   1,1,1,1,1,1,1, 1,1,1,1,1,1,1,
						   1,1,1,1,1,1,1, 1,1,1,1,1,1,1,
						   1}; 		//乐谱全为中音
	music[1].num = 44;											//乐谱总数



	//第三首音�?? 两只老虎
	cnt++;
	strcpy(music[2].name, "两只老虎"); 					// 使用strcpy复制字符�?? 给音乐命�??
	int music2_unit[38] = {0,
						   1,2,3,1, 1,2,3,1, 3,4,5,5, 3,4,5,5,
						   5,6,5,4, 3,1,5,6, 5,4,3,1, 1,5,1,1,
						   1,5,1,1, 0};		//基础乐谱
	int music2_time[38] = {2,
						   1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1,
						   0,0,0,0, 1,1,0,0, 0,0,1,1, 1,1,1,2,
						   1,1,1,2, 2};		//乐谱节拍
	int music2_unit_HL[38] =
						  {1,
					       1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1,
						   1,1,1,1, 1,1,1,1, 1,1,1,1, 1,0,1,1,
						   1,0,1,1, 1}; 		//乐谱�??   中音
	music[2].num = 38;											//乐谱总数


	//第四首音�?? 青花瓷片�??
	cnt++;
	strcpy(music[3].name, "青花瓷片选"); 					// 使用strcpy复制字符�?? 给音乐命�??
	int music3_unit[100] = {0,0,0,0, 0,5,5,3, 2,3,6,2, 3,5,3,2, 2,5,5,3,
						    2,3,5,2, 3,5,2,1, 1,1,2,3, 5,6,5,4, 5,3,3,2,
						    2,2,1,2, 1,1,2,1, 2,3,5,3, 3,3,5,5, 3,2,3,6,
						    2,3,5,3, 2,2,5,5, 3,2,3,5, 2,3,5,2, 1,1,1,2,
						    3,5,6,5, 4,5,3,3, 2,2,5,3, 2,2,2,1, 1,0,0,0};		//基础乐谱

	int music3_time[100] = {0,0,0,0, 0,0,0,0, 0,0,1,0, 0,0,0,2, 0,0,0,0,
							0,0,1,0, 0,0,0,2, 0,0,0,0, 0,0,0,0, 0,0,0,0,
							2,0,0,0, 0,0,0,0, 0,1,0,0, 2,0,0,0, 0,0,0,1,
							0,0,0,0, 2,0,0,0, 0,0,0,1, 0,0,0,0, 2,0,0,0,
							0,0,0,0, 0,0,0,0, 0,2,0,1, 0,0,0,1, 2,1,1,1};		//乐谱节拍

	for(int i =0;i<100;i++)
		music3_time[i] = music3_time[i]+1;

	int music3_unit_HL[100] =
						  { 1,1,1,1, 1,1,1,1, 1,1,0,1, 1,1,1,1, 1,1,1,1,
							1,1,0,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1,
							1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,0,
							1,1,1,1, 1,1,1,1, 1,1,1,0, 1,1,1,1, 1,1,1,1,
							1,1,1,1, 1,1,1,1, 1,1,0,1, 1,1,1,1, 1,1,1,1}; 		//乐谱�??   中音
	music[3].num = 100;											//乐谱总数




	for (int i = 0; i < MAX_unit_num; i++) {
		//将乐谱保存进结构�??
		if(i<music[0].num){//确保数据正确
			music[0].unit[i] =music0_unit[i];
			music[0].unit_HL[i] =music0_unit_HL[i];
			music[0].time[i] =music0_time[i];
		}


		//将乐谱保存进结构�??
		if(i<music[1].num){//确保数据正确
			music[1].unit[i] =music1_unit[i];
			music[1].unit_HL[i] =music1_unit_HL[i];
			music[1].time[i] =music1_time[i];
		}

		//将乐谱保存进结构�??
		if(i<music[2].num){//确保数据正确
			music[2].unit[i] =music2_unit[i];
			music[2].unit_HL[i] =music2_unit_HL[i];
			music[2].time[i] =music2_time[i];
		}


		//将乐谱保存进结构�??
		if(i<music[3].num){//确保数据正确
			music[3].unit[i] =music3_unit[i];
			music[3].unit_HL[i] =music3_unit_HL[i];
			music[3].time[i] =music3_time[i];
		}
	}


	return cnt;
}





//播放�?? N首音�?? 音量�?? X 0 - 100
void play_music(int n, int x){
	static int ni = 0; 		//用于判断 是否换了音乐
	static int cnt = 0;		//记录播放到哪�??�?? 音节
	if(ni != n ){//如果音乐换了
		ni = n;
		cnt = 0;
		__HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_1,0);//设置音量
		HAL_Delay(1000);//
	}

	//
	int value = tone[music[n].unit_HL[cnt]][music[n].unit[cnt]];	//获取频率
	if(flag == 1){	//接受到一个音节结�??
		flag = 0;	//复位
		Beat_num = music[n].time[cnt]; 				//这个音需要多少个半拍
		LED_BEEP(music[n].unit[cnt]);				//LED随音节变动�?�变�??

		if(music[n].time[cnt] == 0){//如果�?? 1/4�??
			Beat_speed_n = Beat_speed /2;
		}
		else{//如果没有1/4�??
			Beat_speed_n = Beat_speed;
		}

		//if(value != 0)//如果有频率�?�执行,没有者只更新 时间�??
		__HAL_TIM_SET_AUTORELOAD(&htim4,value);		//自动加载频率�??

		cnt ++; 	//可进行下�??次音�??
		if(cnt >= music[n].num){ //如果�??个音节播放完�??
			cnt = 0;//重新播放
			//__HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_1,0);//设置音量
			//HAL_Delay(500);//
		}
	}
	//__HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_1,x * (value/100));//设置音量
	__HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_1,(value/10)*x);//设置音量
}


/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  if(IS_ENGINEERING_BOOT_MODE())
  {
    /* Configure the system clock */
    SystemClock_Config();
  }

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_TIM4_Init();
  MX_TIM2_Init();
  /* USER CODE BEGIN 2 */
  tone_init(); //初始化音量频�??
  list_max = music_init();//更新乐谱
  HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_1);	//�??启PWM
  HAL_TIM_Base_Start_IT(&htim2);		  	//�??启定时器�??
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */

	  if(EN == 1)//�??启音�??
		  play_music(list,Low_volume);
	  else
		  __HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_1,0);//设置音量


	  //EN = 1;
	  //play_music(0,5);
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_LSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = 16;
  RCC_OscInitStruct.HSIDivValue = RCC_HSI_DIV1;
  RCC_OscInitStruct.LSIState = RCC_LSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  RCC_OscInitStruct.PLL2.PLLState = RCC_PLL_NONE;
  RCC_OscInitStruct.PLL3.PLLState = RCC_PLL_NONE;
  RCC_OscInitStruct.PLL4.PLLState = RCC_PLL_NONE;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** RCC Clock Config
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_ACLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
                              |RCC_CLOCKTYPE_PCLK3|RCC_CLOCKTYPE_PCLK4
                              |RCC_CLOCKTYPE_PCLK5;
  RCC_ClkInitStruct.AXISSInit.AXI_Clock = RCC_AXISSOURCE_HSI;
  RCC_ClkInitStruct.AXISSInit.AXI_Div = RCC_AXI_DIV1;
  RCC_ClkInitStruct.MCUInit.MCU_Clock = RCC_MCUSSOURCE_HSI;
  RCC_ClkInitStruct.MCUInit.MCU_Div = RCC_MCU_DIV1;
  RCC_ClkInitStruct.APB4_Div = RCC_APB4_DIV1;
  RCC_ClkInitStruct.APB5_Div = RCC_APB5_DIV1;
  RCC_ClkInitStruct.APB1_Div = RCC_APB1_DIV1;
  RCC_ClkInitStruct.APB2_Div = RCC_APB2_DIV1;
  RCC_ClkInitStruct.APB3_Div = RCC_APB3_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief TIM2 Initialization Function
  * @param None
  * @retval None
  */
static void MX_TIM2_Init(void)
{

  /* USER CODE BEGIN TIM2_Init 0 */

  /* USER CODE END TIM2_Init 0 */

  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};

  /* USER CODE BEGIN TIM2_Init 1 */

  /* USER CODE END TIM2_Init 1 */
  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 6400-1;
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 1000-1;
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM2_Init 2 */

  /* USER CODE END TIM2_Init 2 */

}

/**
  * @brief TIM4 Initialization Function
  * @param None
  * @retval None
  */
static void MX_TIM4_Init(void)
{

  /* USER CODE BEGIN TIM4_Init 0 */

  /* USER CODE END TIM4_Init 0 */

  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_OC_InitTypeDef sConfigOC = {0};

  /* USER CODE BEGIN TIM4_Init 1 */

  /* USER CODE END TIM4_Init 1 */
  htim4.Instance = TIM4;
  htim4.Init.Prescaler = 640-1;
  htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim4.Init.Period = 100-1;
  htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim4) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_PWM_Init(&htim4) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = 0;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  if (HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM4_Init 2 */

  /* USER CODE END TIM4_Init 2 */
  HAL_TIM_MspPostInit(&htim4);

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOF_CLK_ENABLE();
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOI_CLK_ENABLE();
  __HAL_RCC_GPIOG_CLK_ENABLE();
  __HAL_RCC_GPIOE_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOF, GPIO_PIN_1|GPIO_PIN_6, GPIO_PIN_RESET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_7, GPIO_PIN_RESET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOI, GPIO_PIN_11|GPIO_PIN_10, GPIO_PIN_RESET);

  /*Configure GPIO pins : PF1 PF6 */
  GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_6;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);

  /*Configure GPIO pin : PC7 */
  GPIO_InitStruct.Pin = GPIO_PIN_7;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  /*Configure GPIO pins : PI11 PI10 */
  GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_10;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOI, &GPIO_InitStruct);

  /*Configure GPIO pins : PG2 PG0 PG1 */
  GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_0|GPIO_PIN_1;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);

  /*Configure GPIO pin : PE9 */
  GPIO_InitStruct.Pin = GPIO_PIN_9;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

  /* EXTI interrupt init*/
  HAL_NVIC_SetPriority(EXTI0_IRQn, 2, 0);
  HAL_NVIC_EnableIRQ(EXTI0_IRQn);

  HAL_NVIC_SetPriority(EXTI1_IRQn, 2, 0);
  HAL_NVIC_EnableIRQ(EXTI1_IRQn);

  HAL_NVIC_SetPriority(EXTI2_IRQn, 2, 0);
  HAL_NVIC_EnableIRQ(EXTI2_IRQn);

  HAL_NVIC_SetPriority(EXTI9_IRQn, 1, 0);
  HAL_NVIC_EnableIRQ(EXTI9_IRQn);

}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

stm32mp1xx_it.c

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    stm32mp1xx_it.c
  * @brief   Interrupt Service Routines.
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32mp1xx_it.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN TD */

/* USER CODE END TD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
int time_100ms_cnt = 0; //0.1s计数�??
int Beat_speed = 5;		//节拍速度,代表半个节拍需要多少个0.1s
int Beat_speed_n = 0;	//实际执行的节�??

int Beat_num = 2;		//这个�?? �??要多少就 半拍
int flag = 0; 			//当其等于 1 时,表示�??个音结束
int EN = 0;				//使能信号,用于开启整个音�??
int list = 0;			//音乐列表
int list_max = 0;		//音乐总数
int Low_volume = 5;		//音量大小
#define Low_volume_cnt  3	//音量大小�??10增加

/* USER CODE END 0 */

/* External variables --------------------------------------------------------*/
extern TIM_HandleTypeDef htim2;
/* USER CODE BEGIN EV */

/* USER CODE END EV */

/******************************************************************************/
/*           Cortex-M4 Processor Interruption and Exception Handlers          */
/******************************************************************************/
/**
  * @brief This function handles Non maskable interrupt.
  */
void NMI_Handler(void)
{
  /* USER CODE BEGIN NonMaskableInt_IRQn 0 */

  /* USER CODE END NonMaskableInt_IRQn 0 */
  /* USER CODE BEGIN NonMaskableInt_IRQn 1 */
  while (1)
  {
  }
  /* USER CODE END NonMaskableInt_IRQn 1 */
}

/**
  * @brief This function handles Hard fault interrupt.
  */
void HardFault_Handler(void)
{
  /* USER CODE BEGIN HardFault_IRQn 0 */

  /* USER CODE END HardFault_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_HardFault_IRQn 0 */
    /* USER CODE END W1_HardFault_IRQn 0 */
  }
}

/**
  * @brief This function handles Memory management fault.
  */
void MemManage_Handler(void)
{
  /* USER CODE BEGIN MemoryManagement_IRQn 0 */

  /* USER CODE END MemoryManagement_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_MemoryManagement_IRQn 0 */
    /* USER CODE END W1_MemoryManagement_IRQn 0 */
  }
}

/**
  * @brief This function handles Pre-fetch fault, memory access fault.
  */
void BusFault_Handler(void)
{
  /* USER CODE BEGIN BusFault_IRQn 0 */

  /* USER CODE END BusFault_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_BusFault_IRQn 0 */
    /* USER CODE END W1_BusFault_IRQn 0 */
  }
}

/**
  * @brief This function handles Undefined instruction or illegal state.
  */
void UsageFault_Handler(void)
{
  /* USER CODE BEGIN UsageFault_IRQn 0 */

  /* USER CODE END UsageFault_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_UsageFault_IRQn 0 */
    /* USER CODE END W1_UsageFault_IRQn 0 */
  }
}

/**
  * @brief This function handles System service call via SWI instruction.
  */
void SVC_Handler(void)
{
  /* USER CODE BEGIN SVCall_IRQn 0 */

  /* USER CODE END SVCall_IRQn 0 */
  /* USER CODE BEGIN SVCall_IRQn 1 */

  /* USER CODE END SVCall_IRQn 1 */
}

/**
  * @brief This function handles Debug monitor.
  */
void DebugMon_Handler(void)
{
  /* USER CODE BEGIN DebugMonitor_IRQn 0 */

  /* USER CODE END DebugMonitor_IRQn 0 */
  /* USER CODE BEGIN DebugMonitor_IRQn 1 */

  /* USER CODE END DebugMonitor_IRQn 1 */
}

/**
  * @brief This function handles Pendable request for system service.
  */
void PendSV_Handler(void)
{
  /* USER CODE BEGIN PendSV_IRQn 0 */

  /* USER CODE END PendSV_IRQn 0 */
  /* USER CODE BEGIN PendSV_IRQn 1 */

  /* USER CODE END PendSV_IRQn 1 */
}

/**
  * @brief This function handles System tick timer.
  */
void SysTick_Handler(void)
{
  /* USER CODE BEGIN SysTick_IRQn 0 */

  /* USER CODE END SysTick_IRQn 0 */
  HAL_IncTick();
  /* USER CODE BEGIN SysTick_IRQn 1 */

  /* USER CODE END SysTick_IRQn 1 */
}

/******************************************************************************/
/* STM32MP1xx Peripheral Interrupt Handlers                                    */
/* Add here the Interrupt Handlers for the used peripherals.                  */
/* For the available peripheral interrupt handler names,                      */
/* please refer to the startup file (startup_stm32mp1xx.s).                    */
/******************************************************************************/

/**
  * @brief This function handles EXTI line0 interrupt.
  */
void EXTI0_IRQHandler(void)
{
  /* USER CODE BEGIN EXTI0_IRQn 0 */
	if(HAL_GPIO_ReadPin(GPIOG, GPIO_PIN_0) == 0)//确保数据稳定
	{
		//每次按下解决 音量�?? Low_volume_cnt
		Low_volume = Low_volume + Low_volume_cnt;
		if(Low_volume >= 10)
			Low_volume = 0;
	}
  /* USER CODE END EXTI0_IRQn 0 */
  HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
  /* USER CODE BEGIN EXTI0_IRQn 1 */

  /* USER CODE END EXTI0_IRQn 1 */
}

/**
  * @brief This function handles EXTI line1 interrupt.
  */
void EXTI1_IRQHandler(void)
{
  /* USER CODE BEGIN EXTI1_IRQn 0 */
	if(HAL_GPIO_ReadPin(GPIOG, GPIO_PIN_1) == 0)//确保数据稳定
	{
		//控制音乐播放的�?�度
		static int i = 0;
		i++;
		//倍数计算公式 1 + (1 - (新的节拍速度 / 原来的节拍�?�度))
		switch(i){
		case 0:{
			Beat_speed = 5;	//0.5s半个节拍,正常+�?�度
			break;
		}
		case 1:{
			Beat_speed = 4;	//1.2倍数
			break;
		}
		case 2:{
			Beat_speed = 3;	//约等�?? 1.5倍数
			break;
		}
		case 3:{
			Beat_speed = 1;	//约等�?? 2 倍数
			break;
		}
		case 4:{
			Beat_speed = 6;	//约等�?? 0.8 倍数
			break;
		}
		case 5:{
			Beat_speed = 7;	//约等�?? 0.6 倍数
			break;
		}

		default:{
			Beat_speed = 5;	//0.5s半个节拍,正常�?�度
			i=0;
			break;
		}
		}
	}
  /* USER CODE END EXTI1_IRQn 0 */
  HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);
  /* USER CODE BEGIN EXTI1_IRQn 1 */

  /* USER CODE END EXTI1_IRQn 1 */
}

/**
  * @brief This function handles EXTI line2 interrupt.
  */
void EXTI2_IRQHandler(void)
{
  /* USER CODE BEGIN EXTI2_IRQn 0 */
	//按下�??次音乐进入下�??�??
	if(HAL_GPIO_ReadPin(GPIOG, GPIO_PIN_2) == 0)//确保数据稳定
	{
		list++;
		if(list > list_max){
			list = 0;
		}
	}
  /* USER CODE END EXTI2_IRQn 0 */
  HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2);
  /* USER CODE BEGIN EXTI2_IRQn 1 */

  /* USER CODE END EXTI2_IRQn 1 */
}

/**
  * @brief This function handles TIM2 global interrupt.
  */
void TIM2_IRQHandler(void)
{
  /* USER CODE BEGIN TIM2_IRQn 0 */
	if(EN == 1)
		time_100ms_cnt++;
	else
		time_100ms_cnt = time_100ms_cnt;	//其余状�?�不计数

	if(time_100ms_cnt >= Beat_speed_n * Beat_num){	//这个音节结束
		time_100ms_cnt = 0;
		flag = 1;	//发�?�音节结束信�??
	}
  /* USER CODE END TIM2_IRQn 0 */
  HAL_TIM_IRQHandler(&htim2);
  /* USER CODE BEGIN TIM2_IRQn 1 */

  /* USER CODE END TIM2_IRQn 1 */
}

/**
  * @brief This function handles EXTI line9 interrupt.
  */
void EXTI9_IRQHandler(void)
{
  /* USER CODE BEGIN EXTI9_IRQn 0 */
	if(HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_9) == 0)//确保数据稳定
		EN = !EN;
  /* USER CODE END EXTI9_IRQn 0 */
  HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_9);
  /* USER CODE BEGIN EXTI9_IRQn 1 */

  /* USER CODE END EXTI9_IRQn 1 */
}

/**
  * @brief This function handles RCC wake-up interrupt.
  */
void RCC_WAKEUP_IRQHandler(void)
{
  /* USER CODE BEGIN RCC_WAKEUP_IRQn 0 */

  /* USER CODE END RCC_WAKEUP_IRQn 0 */
  HAL_RCC_WAKEUP_IRQHandler();
  /* USER CODE BEGIN RCC_WAKEUP_IRQn 1 */

  /* USER CODE END RCC_WAKEUP_IRQn 1 */
}

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

六、总结

这是一个基于STM32MP157A单片机的音乐盒设计较为全面和详细。主要包括以下几个方面:

  1. 硬件设计:

    • 使用STM32MP157A单片机作为核心控制器
    • 采用TIM2定时器产生0.1s中断控制节拍
    • 使用TIM4输出PWM信号驱动蜂鸣器播放音乐
    • 使用GPIO输入检测按键操作
  2. 软件设计:

    • 初始化音乐频率数据表,包括低音、中音和高音
    • 定义乐谱结构体,保存不同音乐的音符和节拍信息
    • 编写播放音乐的函数,根据乐谱信息控制蜂鸣器输出
    • 编写LED灯随音乐闪烁的函数
    • 实现通过按键控制音乐播放、速度、音量的功能
  3. 中断处理:

    • 使用外部中断处理按键输入,切换音乐、调节速度和音量
    • 使用定时器中断处理节拍控制,根据乐谱信息播放音乐
  4. 整体架构:

    • 将变量和函数划分到main.c和stm32mp1xx_it.c两个文件中,方便管理
    • 通过外部变量在两个文件中交换信息
    • 整体设计思路清晰,功能模块化,可扩展性强

总的来说,这个音乐盒的设计充分==利用了STM32MP157A单片机的各种外设资源,实现了一个功能丰富的音乐播放器。对于初学者来说,这个对于了解定时器和PWM有一定帮助。

帮助:关于电脑蓝屏解决方法(ST-LINK/ J-Link)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何为 kNN 搜索选择最佳 k 和 num_candidates

作者&#xff1a;Madhusudhan Konda 如何选择最好的 k 和 num_candidates&#xff1f; 向量搜索在当前的生成式人工智能/机器学习领域中已经成为一个改变游戏规则的技术。它允许我们基于语义含义而不仅仅是精确的关键词匹配来找到相似的项目。 Elasticsearch的 k-近邻&#x…

使用 Flask 实现异步请求处理

文章目录 为什么需要异步请求处理&#xff1f;在 Flask 中实现异步请求处理使用 Flask-Cors 扩展 总结 在开发 Web 应用程序时&#xff0c;异步请求处理是提高性能和并发能力的重要方法之一。Flask 是一个轻量级的 Web 框架&#xff0c;它提供了易于使用的工具来实现异步请求处…

STM32高级控制定时器(STM32F103):检测输入PWM周期和占空比

目录 概述 1 PWM 输入模式 1.1 原理介绍 1.2 应用实例 1.3 示例时序图 2 使用STM32Cube配置工程 2.1 软件环境 2.2 配置参数 2.3 生成项目文件 3 功能实现 3.1 PWM占空比函数 3.2 输入捕捉回调函数 4 功能测试 4.1 测试软件框架结构 4.2 实验实现 4.2.1 测试实…

邀请媒体参会,媒体邀约的正确打开方式

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 邀请媒体参会是一项重要的公关活动&#xff0c;需要细致的规划和执行。以下是一些步骤和建议&#xff0c;可以帮助你更有效地进行媒体邀约&#xff1a; 1. 拟定邀约媒体名单&#xff1a;…

python数据分析——分组操作1

参考资料&#xff1a;活用pandas库 1、简介 借助“分割-应用-组合”&#xff08;split-apply-combine&#xff09;模式&#xff0c;分组操作可以有效地聚合、转换和过滤数据。 分割&#xff1a;基于键&#xff0c;把要处理的数据分割为小片段。 应用&#xff1a;分别处理每个数…

【CUDA】Nsight profile驱动的CUDA优化

前置准备 安装NVIDIA Nsight Compute。 安装好后选择使用管理员权限启动下载官方 Demo 代码官方博客Shuffle warp 1. 任务介绍及CPU版本 1.1 任务介绍 任务理解&#xff1a; 有一个 L x M 的矩阵 M 1 M_1 M1​ 对其每行取平均值 得到 V 1 ∈ R L 1 V_1 \in \mathbb{R}^{…

Java | Leetcode Java题解之第117题填充每个节点的下一个右侧节点指针II

题目&#xff1a; 题解&#xff1a; class Solution {Node last null, nextStart null;public Node connect(Node root) {if (root null) {return null;}Node start root;while (start ! null) {last null;nextStart null;for (Node p start; p ! null; p p.next) {if…

学习笔记——数据通信基础——数据通信网络(拓扑结构)

网络拓扑 网络拓扑(Network Topology)是指用传输介质(例如双绞线、光纤等)互连各种设备(例如计算机终端、路由器、交换机等)所呈现的结构化布局。 1、网络拓扑形态 星型网络∶所有节点通过一个中心节点连接在一起。 优点∶容易在网络中增加新的节点。通信数据必须经过中心节点…

【2】:向量与矩阵

向量 既有大小又有方向的量叫做向量 向量的模 向量的长度 单位向量 (只表示方向不表示长度) 向量的加减运算 向量求和 行向量与列向量的置换 图形学中竖着写 向量的长度计算 点乘&#xff08;计算向量间夹角&#xff09; 点乘满足的运算规律 交换律、结合律、分配…

新型高性能数据记录仪ETHOS 2

| 具有强大CPU性能的数据记录仪 IPETRONIK推出了一款新型高性能数据记录仪——ETHOS 2&#xff0c;作为ETHOS的第二代&#xff0c;它借助新型英特尔i7-9850HE处理器&#xff0c;实现了11,572的性能指数&#xff0c;从而能够快速有效应对CAN FD、LIN和以太网总线测量方面的日益…

【校园网网络维修】当前用户使用的IP与设备重定向地址中IP不一致,请重新认证

出现的网络问题&#xff1a;当前用户使用的IP与设备重定向地址中IP不一致&#xff0c;请重新认证 可能的原因&#xff1a; 把之前登录的网页收藏到浏览器&#xff0c;然后直接通过这个链接进行登录认证。可能是收藏网址导致的ip地址请求参数不一致。 解决方法&#xff1a; 方法…

循环buffer“一写多读“

1.往期回顾 一个简单实用的循环buffer&#xff0c;用于缓冲数据&#xff01;测试500M数据&#xff0c;耗时1.3秒。 C语言版本的循环buffer比C版本的速度更快&#xff01;测试500M数据0.5秒&#xff0c;达9.25Gbps左右&#xff01; C 语言免拷贝版本循环 buffer 比拷贝版本快了…

apexcharts数据可视化之饼图

apexcharts数据可视化之饼图 有完整配套的Python后端代码。 本教程主要会介绍如下图形绘制方式&#xff1a; 基础饼图单色饼图图片饼图 基础饼图 import ApexChart from react-apexcharts;export function SimplePie() {// 数据序列const series [44, 55, 13, 43, 22]// …

2024年下半年自考报名信息汇总

2024年下半年自考报名信息汇总&#xff0c;报名详细流程如下图所示&#xff1a;

抖店起店玩法,2024年最新保姆级抖音小店开店教程

课程下载&#xff1a;https://download.csdn.net/download/m0_66047725/89360739 更多资源下载&#xff1a;关注我。 课程内容&#xff1a; 1-抖音如何精细化选品 2-达人合作的谈判技巧 3-达人合作细节注意事项 4-短视频达人筛选方法与数据维度 5-短视频带货达人分析工具…

C++青少年简明教程:for循环语句

C青少年简明教程&#xff1a;for循环语句 C的for循环语句是一种迭代控制语句&#xff0c;用于重复执行一段代码。 语法格式&#xff1a; for(表达式1&#xff1b;表达式2&#xff1b;表达式3) 循环体 for循环语句执行流程图&#xff1a; 不太好理解&#xff0c;请看下图&am…

SpringJDBC

1.前言 Spring JDBC可以帮助开发者节省大量开发工作 自动去处理一些低级细节 比如&#xff1a;异常处理、打开和关闭资源(Connection、PreparedStatement、Statement、ResultSet) 需要下载的jar包&#xff1a; spring-jdbc(普通jar包、源码jar包)由于没有依赖其他的jar包 所以只…

探寻数据处理的高效之道:从Python内置方法到NumPy的飞跃

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、引言&#xff1a;为什么要学习NumPy&#xff1f; 二、案例展示&#xff1a;创建整数序列…

Apifox 更新|编排模式、Markdown 编辑器升级、自动申请 SSL 证书、用户反馈问题优化

Apifox 新版本上线啦&#xff01; 看看本次版本更新主要涵盖的重点内容&#xff0c;有没有你所关注的功能特性&#xff1a; 自动化测试新增「编排模式」Markdown 编辑器全新升级返回响应直接预览 PDF 及视频自动申请 SSL 证书支持配置自定义域名的子目录流式接口支持筛选和清…

Vue 菜单组件开发教程

在 Vue 项目中&#xff0c;我们常常需要构建各种菜单结构。下面就来详细介绍如何基于给定的代码来开发一个菜单组件。 组件部分 一、模板部分 <template> <template v-for"item in menuTree" :key"item._id"> <el-sub-menu v-if"i…