> 技术文档 > STM32使用PWM驱动无源蜂鸣器实现8bit音乐播放_无源蜂鸣器播放音乐

STM32使用PWM驱动无源蜂鸣器实现8bit音乐播放_无源蜂鸣器播放音乐


目录

      • 基础知识
      • 音频处理
        • 使用Audacity软件处理音频
        • 使用winhex软件获取数组
      • STM32部分

“力大砖飞”式播放音乐法,无需拿着乐谱转换音符,只需要一个音乐文件+你最亲爱的STM32开发板和他的无源蜂鸣器朋友,就能得到上世纪“最伟大的产物”----8bit音乐。不过,要保证你的STM32开发板内部的flash足够大呦~

基础知识

注意!!!参考文章点这里

  • 采样速率:指1s中采样多少个数据点,比如1s种采集16000个点,那么采样率就是16KHz。采样速率越高,越能抓到频率较高的声音,比如CD的采样率就是44.1KHz,确保人耳能听到的声音都会被抓到

  • 采样位数:指音频幅度最大值与最小值分为了多少阶,比如满幅度是3.3V,如果是8Bit位数,那么每一阶就是3.3V/256 = 12.89mv,采样位数越高,声音细节越好。所以采样速率和位数越高,声音还原越逼真,但存储的数据量也越大,一首三四分钟的歌曲,如果不采用编码按原始波形数据存储,数据量有好几十兆大小,这涉及到音频编码的问题

  • 如何播放

    1. 把采样(ADC)的数据按原样输出(DAC)

    2. 有些芯片本身不带有DAC,所以只能用PWM代替DAC,这里只需要把DAC的幅度值转换成PWM的占空比即可,例如16KHz 8Bit的声音转换成16Khz 256阶占空比的PWM。但有一个问题,如果用16KHz的PWM播放语音,声音是可以播放,但有一个16Khz的谐波存在,这个声音会被人耳听到,所以需要更高频率的PWM,数据还是按照16Khz更新

    3. 这里使用32KHz的PWM,用16KHz 无符号8Bit PCM格式的音频数据,8Bit的数据对应1个Byte,16KHz采样,1秒种占用存储空间就是16K Byte,换算可存储的时间为:Flash存储空间(单位转换成KB) / 16K。例如,2M Byte的Flash存储空间,理论上可以存储2048K/16K = 128秒的音频。

    4. 32K的TIM1播放,16K的TIM2中断更新

      在这里插入图片描述

音频处理

使用Audacity软件处理音频

在这里插入图片描述

打开要转换的音频文件

在这里插入图片描述

导出音频

在这里插入图片描述

配置导出设置

  • 单声道
  • 16000Hz采样
  • RAW格式
  • Unsigned 8-bit PCM 一定是无符号的这个

在这里插入图片描述

使用winhex软件获取数组

在这里插入图片描述

打开刚刚转换好的RAW文件

在这里插入图片描述

在数字区域“Ctrl+A”全选,依次点击:“编辑”—“复制选块”—“C源码”

在这里插入图片描述

新建一个H文件,“Ctrl+V”,粘贴到H文件中

在这里插入图片描述

添加预处理部分,注意要将数组名里的空格替换成下划线

在这里插入图片描述

在这里插入图片描述

STM32部分

beep_con.c

/** * @file beep_con.c * @author * @Version V1.0 * @date * @brief *//* Includes ------------------------------------------------------------------*/#include \"beep_con.h\"/*刚刚处理的H文件*/#include \"cangcang8bit.h\"/* Private macro -------------------------------------------------------------*//* Private function prototypes -----------------------------------------------*//* Private typedef -----------------------------------------------------------*//* Private variables ---------------------------------------------------------*/uint32_t AudioDataAddr = 0;uint32_t AudioDataLen = 0;Beep_sta con_sta = STOP;/* Exported variables --------------------------------------------------------*//* Private functions ---------------------------------------------------------*/void Beep_Init(void){AudioDataLen = ARRAY_LENGTH(AnsiChar_data);//在定时器开启前初始化获得数据长度}void Beep_Con(Beep_sta sta){con_sta = sta;}void HAL_TIM2_Callback(void){if(con_sta != STOP){if(con_sta == RES)AudioDataAddr = 0;//重置但不开始if(AudioDataAddr++ >= AudioDataLen){AudioDataAddr = 0;}//__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1,AnsiChar_data[AudioDataAddr]);//采用32K载波,ARR为256-1__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, (AnsiChar_data[AudioDataAddr] * 1800) >> 8);//采用40K载波,ARR为1800-1,需要从256映射到1800}}

beep_con.h

#ifndef BEEP_CON_H#define BEEP_CON_H/* Includes ------------------------------------------------------------------*/#include \"tim.h\"/* Exported types ------------------------------------------------------------*/typedef enum{STOP=0,RUN=1,RES=2,}Beep_sta;extern void Beep_Init(void);extern void Beep_Con(Beep_sta sta);extern void HAL_TIM2_Callback(void);#endif

main.c

/* USER CODE BEGIN 2 */Beep_Init();HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1); /*开启CH1的PWM输出*/HAL_TIM_Base_Start_IT(&htim2);/* USER CODE END 2 */

stm32f1xx_it.c

void TIM2_IRQHandler(void){ /* USER CODE BEGIN TIM2_IRQn 0 */ /* USER CODE END TIM2_IRQn 0 */ HAL_TIM_IRQHandler(&htim2); /* USER CODE BEGIN TIM2_IRQn 1 */HAL_TIM2_Callback();//16K中断调用 /* USER CODE END TIM2_IRQn 1 */}