本人小白一個,在暑假期間利用休息時間自學32,在此篇中,若有不對指出,還請各位大佬多多指點,小弟感激不盡。
我想要實作的是通過stm32f103mini闆,播放制定的音樂。蜂鳴器的發聲原理如果有不了解的,請自行百度。要想播放音樂,就要掌握兩個關鍵,一個是節拍,就是類似于鋼琴,吉他之類的節拍。另一個就是音調,音調是通過PWM輸出引腳輸出PWM的高低來控制。而播放音樂就是在每一個固定的節拍中播放對應的音調,當很多個音調連接配接起來,就成了一串優美的音樂了。
話不多說,先上幹貨。
beep.h
#ifndef __BEEP_H
#define __BEEP_H
#include "sys.h"
//
#define RCC_BEEP_CLK RCC_APB2Periph_GPIOB
#define BEEP_Pin GPIO_Pin_1
#define BEEP_GPIO GPIOB//這些定義都是為了簡化調用而已。
//
#define BEEP PBout(1) // BEEP 接口定義
// 定義低音音名,機關HZ
#define L1 262
#define L2 294
#define L3 330
#define L4 349
#define L5 392
#define L6 440
#define L7 494
// 定義中音音名
#define M1 523
#define M2 587
#define M3 659
#define M4 698
#define M5 784
#define M6 880
#define M7 988
//定義高音音名
#define H1 1047
#define H2 1175
#define H3 1319
#define H4 1397
#define H5 1568
#define H6 1760
#define H7 1976
//
#define TT 2000
typedef struct
{
short mName; //元素1,音名,取值是上邊的三種音名,低中高(1~7),0表是休止符
short mTime; //元素2,時值,取值範圍是全音符,半分音符等等
}tNote;//定義一個結構體,裡邊包含兩個元素。
extern u8 beep_switch;//
//extern u16 beep_time;
void BEEP_PWM_Init(u16 arr,u16 psc);
void buzzerSound(unsigned short usFreq) ;
void musicPlay(void) ;
void buzzerQuiet(void);
#endif
beep.c
#include "beep.h"
#include "delay.h"
//
u8 beep_switch=0;
//u16 beep_time=0;
//
//PWM初始化
void BEEP_PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_BEEP_CLK | RCC_APB2Periph_AFIO, ENABLE); //ʹÄÜGPIOÍâÉèºÍAFIO¸´Óù¦ÄÜÄ£¿éʱÖÓʹÄÜ
//GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //TIM3部分重映射TIM3_CH4->PB0
GPIO_InitStructure.GPIO_Pin = BEEP_Pin; //TIM3_CH4
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //複用輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(BEEP_GPIO, &GPIO_InitStructure);
TIM_TimeBaseStructure.TIM_Period = arr;
TIM_TimeBaseStructure.TIM_Prescaler =psc; //
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //Ñ¡Ôñ¶¨Ê±Æ÷ģʽ:TIMÂö³å¿í¶Èµ÷ÖÆÄ£Ê½2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //±È½ÏÊä³öʹÄÜ
TIM_OCInitStructure.TIM_Pulse = 0; //ÉèÖôý×°Èë²¶»ñ±È½Ï¼Ä´æÆ÷µÄÂö³åÖµ
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;//TIM_OCPolarity_High;//TIM_OCPolarity_Low; //Êä³ö¼«ÐÔ:TIMÊä³ö±È½Ï¼«ÐÔµÍ
TIM_OC4Init(TIM3, &TIM_OCInitStructure); //¸ù¾ÝTIM_OCInitStructÖÐÖ¸¶¨µÄ²ÎÊý³õʼ»¯ÍâÉèTIMx
TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable); //ʹÄÜTIMxÔÚCCR2ÉϵÄÔ¤×°ÔØ¼Ä´æÆ÷
TIM_CCxCmd(TIM3,TIM_Channel_4 ,ENABLE);//Êä³ö¿ªÆô
TIM_ARRPreloadConfig(TIM3, ENABLE); //ʹÄÜTIMxÔÚARRÉϵÄÔ¤×°ÔØ¼Ä´æÆ÷
TIM_Cmd(TIM3, ENABLE); //ʹÄÜTIMxÍâÉè
}
//蜂鳴器停止發聲
void buzzerQuiet(void)
{
TIM_SetCompare4(TIM3,0); //À®°È
TIM_CCxCmd(TIM3,TIM_Channel_4 ,DISABLE);//
}
/
//·äÃùÆ÷·¢³öÖ¸¶¨ÆµÂʵÄÉùÒô
//usFreqÊÇ·¢ÉùƵÂÊ£¬È¡Öµ (ϵͳʱÖÓ/65536)+1 ¡« 20000£¬µ¥Î»£ºHz
void buzzerSound(unsigned short usFreq)
{
unsigned long ulVal;
if((usFreq<=8000000/65536UL)||(usFreq>20000))//65536µÄÖµÊǶ¨Ê±Æ÷ÖØ×°ÔØ¼Ä´æÆ÷µÄ×î´óÖµ
{
buzzerQuiet();//
}
else
{
ulVal=8000000/usFreq;//
TIM3->ARR =ulVal ; //定時器重裝載寄存器周期的值
TIM_SetCompare4(TIM3,ulVal /2);//音調高低
TIM_CCxCmd(TIM3,TIM_Channel_4 ,ENABLE);//開啟時鐘
}
}
//
void musicPlay(void)
{
static u8 i=0;
if(MyScore[i].mTime != 0)
{
buzzerSound(MyScore[i].mName);
delay_ms(MyScore[i].mTime);
i++;
buzzerQuiet(); // 防止在中間發出雜音,以及留出音樂的停頓
delay_ms(10);// 10 ms
}
}
總的來說,感覺就PWM輸出的一個小引用,難度不大,掌握蜂鳴器播放音樂的原理,那麼會事半功倍,不論是那種闆子,都能實作蜂鳴器播放音樂。花一些時間去思考,效率會提高不少。
我曾許下十年,隻為最美的遇見。
我是西伯利亞螂,有緣再見。