使用STM32控制無源蜂鳴器發聲播放音樂(STM32_07)

一、無源蜂鳴器和有源蜂鳴器

有源蜂鳴器內含振盪源,只要一通電就發聲,但發生頻率固定,音色單一;無源蜂鳴器內部不含振盪源,內部結構相當於電磁場揚聲器,可以通過給他輸出一定頻率的信號才能發聲。

人耳能聽到的頻率範圍在20Hz--20kHz之間,通過STM32的GPIO引腳快速切換高低電平輸出就能實現無源蜂鳴器的發聲,切換的頻率不同,發出的音調就不一樣。

二、音樂播放的實現

一段音樂就是不同頻率的聲音按一定的時間節拍轉換髮出。所以音樂包含音調和節拍信息。

C調各音符頻率如下:

音符

頻率 Hz

音符

頻率 Hz

低1 Do

262

中1 Do

523

低2 Re

294

中2 Re

587

低3 Mi

330

中3 Mi

659

低4 Fa

349

中4 Fa

698

低5 So

392

中5 So

784

低6 La

440

中6 La

880

低7 Si

523

中7 Si

988

如果要實現歌曲“紅塵情歌”,要準備相應的數據。

歌譜如下:

程序中首先準備音頻數據表:

//         低Si Do Re  Mi  Fa So  La  Si ¸高Do¸高Re¸高Mi¸高Fa¸高So 無

uc16 tone[] ={247,262,294,330,349,392,440,294,523,  587,  659,  698,  784,  1000};

u8 music[]={  5,5,6,8,7,6,5,6,13,13,……};//音調

u8 time[] = {  2,4,2,2,2,2,2,8,4, 4, ……}; //節拍時間

依次從音調數組中取music[i],然後根據music[i]的值在tone數組中得到該音的發聲頻率(tone[music[i]]),調用sound函數控制蜂鳴器發聲,聲音的發聲時間有time數組控制。

三、項目創建與配置

1、創建項目文件夾(設爲pMusic)

2、通過Keil5創建新項目,保存在所創建的文件夾中(設項目名爲pMusic),選擇MCU芯片爲"STM32F103ZE"(本程序使用的硬件爲:STM32-PZ6806L開發板)

3、在pMusic項目文件夾中新建"CMSIS"、"Device"、"Public"、"Startup"、"User"和"Lib"文件夾。

①  在"CMSIS"文件夾中複製"core_cm3.h"和"core_cm3.c"文件;

②  在" Device "文件夾中複製"stm32f10x.h"、"system_stm32f10x.h"和"system_stm32f10x.c"文件;

③  在" Startup "文件夾中複製"startup_stm32f10x_hd.s"文件;

④在"Lib"文件夾中新建"inc"和"src"兩個子文件夾,在"inc"文件夾中複製"misc.h"、"stm32f10x_gpio.h"和"stm32f10x_rcc.h"文件;在"src"文件夾中複製"misc.c"、"stm32f10x_gpio.c"和"stm32f10x_rcc.c"文件;

4、爲項目添加"CMSIS"、"Device"、"Public"、"Startup"、"User"和"Lib"組,並將上述C程序文件和"startup_stm32f10x_hd.s"啓動文件加入到相應組中。展開項目樹如下:

5、打開“項目配置”對話框,在"Output"選項卡中選擇"Create HEX File",在"C/C++"選項卡中的"Include Paths"中添加如下包含路徑:".\CMSIS;", ".\Device;", ".\Lib\inc;",".\Public;"。

(以上步驟可以參看:使用STM32固件庫操作控制LED燈(CMSIS)   使用STM32固件庫函數操作控制LED燈

6、在"stm32f10x.h"中添加函數參數檢查宏

(參看:使用STM32固件庫函數操作控制LED燈

#ifdef  USE_FULL_ASSERT

/**

  * @brief  這個assert_param宏用於函數參數檢查

  * @param  expr:如果expr是 false,就調用 assert_failed函數報告源文件名和

  *         失敗的行號,如果expr是 true ,就返回一個空值

  * @retval None

  */

  #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__))

/* Exported functions ------------------------------------------------------- */

  void assert_failed(uint8_t* file, uint32_t line);

#else

  #define assert_param(expr) ((void)0)

#endif /* USE_FULL_ASSERT */

7、新建一個文件(system.h),保存到"Public"文件夾中,內容爲:

#ifndef __SYSTEM__H

#define __SYSTEM__H

#include "stm32f10x.h"

//定義位帶地址宏

#define BITBAND(addr,bitnum) ((addr&0xF0000000) + 0x02000000 + ((addr&0x000FFFFF)<<5) + (bitnum<<2))

#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))

#define BIT_ADDR(addr,bitnum) MEM_ADDR(BITBAND(addr,bitnum))

//IO口地址映射

//數據輸出寄存器地址

#define GPIOA_ODR_Addr          (GPIOA_BASE + 12)

#define GPIOB_ODR_Addr (GPIOB_BASE + 12)

#define GPIOC_ODR_Addr (GPIOC_BASE + 12)

#define GPIOD_ODR_Addr          (GPIOD_BASE + 12)

#define GPIOE_ODR_Addr (GPIOE_BASE + 12)

#define GPIOF_ODR_Addr (GPIOF_BASE + 12)

#define GPIOG_ODR_Addr          (GPIOG_BASE + 12)

//數據輸入寄存器地址

#define GPIOA_IDR_Addr  (GPIOA_BASE + 12)

#define GPIOB_IDR_Addr  (GPIOB_BASE + 12)

#define GPIOC_IDR_Addr  (GPIOC_BASE + 12)

#define GPIOD_IDR_Addr  (GPIOD_BASE + 12)

#define GPIOE_IDR_Addr   (GPIOE_BASE + 12)

#define GPIOF_IDR_Addr   (GPIOF_BASE + 12)

#define GPIOG_IDR_Addr  (GPIOG_BASE + 12)

#endif

該文件定義了GPIO端口位帶操作的宏。

(位帶操作請參看:通過位帶地址操作GPIO在數碼管顯示數字)

8、新建文件"SysTick.h",保存到"Public"文件夾中,內容爲:

#ifndef __SysTick__H

#define __SysTick__H

#include "stm32f10x.h"

void SysTick_Init(u8 SYSCLK);

void delay_us(u32 nus);

void delay_ms(u16 nms);

#endif

新建文件"SysTick.c",保存到"Public"文件夾中,內容爲:

#include "SysTick.h"

#include "misc.h"

u8 fac_us = 0;

u16 fac_ms = 0;

void SysTick_Init(u8 SYSCLK)

{

         SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);

         fac_us = SYSCLK / 8;

         fac_ms = (u16)fac_us*1000;

}

 

void delay_us(u32 nus)

{

         u32 temp;

         SysTick->LOAD = nus * fac_us;

         SysTick->VAL = 0;

         SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;

         do{

                   temp = SysTick->CTRL;

         }while((temp&0x01)&&(!(temp&(1<<16))));

         SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;

         SysTick->VAL = 0;

}

void delay_ms(u16 nms)

{

         u32 temp;

         SysTick->LOAD = nms * fac_ms;

         SysTick->VAL = 0;

         SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;

         do{

                   temp = SysTick->CTRL;

         }while((temp&0x01)&&(!(temp&(1<<16))));

         SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;

         SysTick->VAL = 0;

}

這兩個文件實現了通過SysTick精準延時的函數,提供給後續的音頻頻率產生程序使用。

(關於SysTick,請參看:在STM32項目中使用SysTick實現延時)

將"SysTick.c"文件添加到項目的"Public"組中。

9、實現發聲

①開發板無源蜂鳴器的電路連接如下:

從電路連接可以看出通過MCU的PB5(GPIOB_5)控制蜂鳴器的發聲。

②在項目文件夾的"User"文件夾下新建"Beep"文件夾,在項目中新建"beep.h"文件,保存在"User/Beep"文件夾中,文件內容爲:

#ifndef __BEEP__H

#define __BEEP__H

#include "system.h"

#include "stm32f10x_gpio.h"

#include "stm32f10x_rcc.h"

//定義GPIOB的位地址變量宏

#define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n)

#define PBeep PBout(5)

#define BEEP_PORT   GPIOB

#define BEEP_PIN      GPIO_Pin_5

#define BEEP_PORT_RCC RCC_APB2Periph_GPIOB

void BEEP_Init(void);

void Sound(u16 frq);

void play(void);

#endif

③在項目中新建"beep.c"文件,保存在"User/Beep"文件夾中,文件內容爲:

#include "beep.h"

#include "systick.h"

void BEEP_Init(void)

{

         GPIO_InitTypeDef GPIO_mode;

         RCC_APB2PeriphClockCmd( BEEP_PORT_RCC, ENABLE );         //使能GPIOB時鐘

         GPIO_mode.GPIO_Pin = BEEP_PIN;

         GPIO_mode.GPIO_Speed = GPIO_Speed_50MHz;

         GPIO_mode.GPIO_Mode = GPIO_Mode_Out_PP;

         GPIO_Init(BEEP_PORT, &GPIO_mode);      //設置GPIOB_5爲推輓輸出,50MHz速度

}

 

void Sound(u16 frq)

{

         u32 n;

         if(frq != 1000) //如果頻率不爲1000則按頻率輸出,否則只延時

         {

                   n = 500000/((u32)frq);

                   PBeep = 0;

                   delay_us(n);

                   PBeep = 1;

                   delay_us(n);

         }else

                   delay_us(1000);

}

 

void play(void)

{

         //             低7  1   2   3   4   5   6   7  高1 高2 高3 高4 高5 不發音

         uc16 tone[] = {247,262,294,330,349,392,440,294,523,587,659,698,784,1000};//音頻數據表

         //紅塵情歌

         u8 music[]={       5,5,6,8,7,6,5,6,13,13,//音調

                                                                           5,5,6,8,7,6,5,3,13,13,

                                                                   2,2,3,5,3,5,6,3,2,1,

                                                                           6,6,5,6,5,3,6,5,13,13,

 

                                                                           5,5,6,8,7,6,5,6,13,13,

                                                                           5,5,6,8,7,6,5,3,13,13,

                                                                   2,2,3,5,3,5,6,3,2,1,

                                                                           6,6,5,6,5,3,6,1,   

 

                                                                           13,8,9,10,10,9,8,10,9,8,6,

                                                                           13,6,8,9,9,8,6,9,8,6,5,

                                                                           13,2,3,5,5,3,5,5,6,8,7,6,

                                                                           6,10,9,9,8,6,5,6,8

         };     

         u8 time[] = {       2,4,2,2,2,2,2,8,4, 4, //時間

                                                                           2,4,2,2,2,2,2,8,4, 4,

                                                                           2,4,2,4,2,2,4,2,2,8,

                                                                           2,4,2,2,2,2,2,8,4 ,4,

        

                                                                           2,4,2,2,2,2,2,8,4, 4,

                                                                           2,4,2,2,2,2,2,8,4, 4,

                                                                           2,4,2,4,2,2,4,2,2,8,

                                                                           2,4,2,2,2,2,2,8,

        

                                                                           4, 2,2,2, 4, 2,2,2, 2,2,8,

                                                                           4, 2,2,2,4,2,2,2,2,2,8,

                                                                           4, 2,2,2,4,2,2,5,2,6,2,4,

                                                                           2,2 ,2,4,2,4,2,2,12

         };     

         u32 yanshi;

         u16 i,e;

         yanshi = 10;

         for(i=0;i<sizeof(music)/sizeof(music[0]);i++){

                   for(e=0;e<((u16)time[i])*tone[music[i]]/yanshi;e++){

                            Sound((u32)tone[music[i]]);

                   }      

         }

}

程序中定義了端口使能和方式配置函數BEEP_Init,輸出音頻函數Sound和播放音樂函數play。

④ 將"beep.c"文件加入到項目的"User"組中;在"C/C++"選項卡中的"Include Paths"中添加包含路徑:"\User\Beep;"。

10、在項目中新建"main.c"文件,保存在"User "文件夾中,文件內容爲:

#include "beep.h"

#include "SysTick.h"

int main()

{

         SysTick_Init(72);

         BEEP_Init();

         while(1)

         {

                   play();

         }

}

"main.c"文件中包含main函數反覆調用play函數播放歌曲。

11、編譯、連接、下載程序。

代碼下載地址:下載源碼 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章