如何用STM32驱动小喇叭或者蜂鸣器来演奏菊次郎的夏天

前言

上一篇文章写到STlink的小插曲,问题已经解决,于是就继续完成了目标,就是驱动一个小喇叭工作,它的原理与蜂鸣器相同,之前提到过原本我是想整个驱动电路的,毕竟功率与声音的大小有关系,后来觉得麻烦就没做,试了一下IO可以驱动起来,就这样直接做了。

准备

首先准备一个小喇叭或者蜂鸣器,这两个都是通过PWM来驱动的,理论上来说PWM的频率决定了声调,占空比决定了振幅,我还没试过输出其他声音,理论上来说都是震动,或许PWM能力有限,只能靠震动频率分辨出音调,音色或许与波形有关,但是想输出一首钢琴曲还是足够的。

使用的板子是STM32F103C8最小板,简单好用,通过STlink烧录代码,使用定时器1来做,用C1口输出PWM,也就是PA8,用CubeMX省点力气,如下图配置很简单:

这里可能要注意一下时钟的问题,我给的定时器时钟是72MHz,可以从时钟树上读出来,72分频也就是1MHz,然后Counter的值和比较值后面会重新赋值,初始化的时候分别给1000和500就可以响了。

此时我们在主函数中加两个函数:

HAL_TIM_Base_Start(&htim1);
HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);

这两句就能够驱动起来定时器和PWM了,这样连接好喇叭的GND和PA8之后烧录代码运行就会响了。

但是响不是目的,我们要输出《菊次郎的夏天》

音乐数组准备

第一步我们要先弄清楚每个音调的频率,直接搜博客上已经有前辈讲过,再次不赘述,也可以直接抄我的数组:

uint16_t cn_music_3[28] = {
	956,851,758,715,637,568,506,
	477,425,379,358,318,284,253,
	239,212,189,179,159,142,126,
	119,106,94,89,79,71,63};

说明一下这里一共28个值,分别是在刚才配置情况下的音调对应的频率。也就是说当我们的定时器频率是1MHz的时候,Counter的值是956时震动的音调接近do,这里并不是C调,主要是因为频率太低声音很小就没有了,所以我就往高频方向移了几个八度。向后Counter的数值越来越小,频率越来越高,我也没有写升调,好像用不到,读者拿计算器一按基本上就能发现规律。

第二步,找一个谱子,我虽然也钢琴入了门,但是太不熟练了就找了一个按数字写的,然后到网上找一个曲谱转蜂鸣器的软件(这里可以自己慢慢算,但是实在太慢,还是使用软件效率高一些,或者直接抄我的数组也可以)。

软件界面长这样:

在右边按对应的音调,然后转换就能出数组,我按照谱子转换了一部分,同时进行了一定的修正,如下:

uint8_t music_3[] = {
	6,3, 13,3, 16,3, 13,3, 4,3,
	11,3, 14,3, 11,3, 5,3, 12,3,
	15,3, 12,3, 11,3, 15,3, 21,3,
	15,3, 6,3, 13,3, 16,3, 13,3, 
	4,3,11,3, 14,3, 11,3, 5,3, 
	12,3,15,3, 12,3, 11,3, 15,3, 
	25,4, 31,4, 32,4, 33,4,32,3,
	31,4, 31,4, 16,3, 13,3,4,3, 
	11,3, 25,4, 31,4, 32,4,33,4, 
	32,3, 31,4, 32,5, 33,3,
	33,2, 25,4, 31,4, 32,4, 33,4,
	32,3, 31,4, 31,4, 16,3, 13,3,
	4,3, 11,3, 25,4, 31,4, 32,4, 
	33,4, 32,3,	31,4, 32,3, 35,3,
	33,2, 33,3, 34,3, 35,3, 35,4, 
	35,5, 35,3, 35,3, 33,4, 
	31,4, 14,3, 33,4, 34,4, 35,3, 
	35,4, 35,5, 35,3, 35,3, 
	33,4, 31,4,16,3, 31,4, 32,4, 
	33,3, 33,4,33,5, 33,3, 
	33,3, 36,3,33,3, 31,3, 32,2, 
	5,3, 12,3,25,4, 31,4, 32,4, 
	33,4, 32,3,31,4, 31,4, 16,3, 
	13,3, 4,3,11,3, 25,4, 31,4,
	32,4, 33,4,32,3, 31,4, 32,5, 
	33,3,33,2, 25,4, 31,4, 
	32,4, 33,4,32,3, 31,4, 31,4, 
	16,3, 13,3,4,3, 11,3, 25,4, 
	31,4, 32,4,33,4, 32,3, 31,4, 
	32,5, 35,3, 33,2, 33,3, 
	34,3, 35,3,35,4, 35,5, 
	35,3, 35,3,33,4, 31,4, 14,3, 
	33,4, 34,4,35,3, 35,4, 35,5, 
	35,3, 35,3, 33,4, 31,4, 
	16,3, 31,4,32,4, 33,3, 33,4, 
	33,5, 33,3, 33,3, 11,3, 
	33,4, 32,4,31,4, 26,4, 31,2, 
	31,2, 23,3,16,3, 13,3, 
};

转换完成后格式基本上跟上面一样,但是我作了略微修改,第一个数是音调,相当于十位是所在的八度(没有学过乐理大家将就一下我的措辞,理解就好,这里的意思是说21比11高一个八度),个位是音调取值1-7,然后第二个数是与拍相关的,数字越大拍越短,这里不会出现5,但是限于软件做不出来连音,我就自己改了一些5出来,配合下面的数组使用:

uint16_t delay_1[4] = {420,180,60,300},delay_2[4] = {60,60,60,60};

这个数组的含义就是延时时间,也就是每个音持续的时间和它响完之后静音的时间,其实静音时间都是60ms,然后将上面数组的数字减去2对应的delay_1数组中的值就是延时时间,单位ms,比如3-2=1,那就是这个音持续180ms。

然后由于我只有一个小喇叭,就只能输出双手中的高音,将低音覆盖掉。

主函数

有了上面的数组之后,加上主函数就能跑了:

int main(void)
{
  /* USER CODE BEGIN 1 */
	uint16_t i = 0;
	uint16_t len = sizeof(music_3) / sizeof(music_3[0]);	//放数组长度
	uint8_t x;
  /* 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 */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_TIM1_Init();
  /* USER CODE BEGIN 2 */
	HAL_TIM_Base_Start(&htim1);
	HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);
  /* USER CODE END 2 */

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

    /* USER CODE BEGIN 3 */
		x = (music_3[i] / 10)*7 + music_3[i] % 10 - 1;
		__HAL_TIM_SET_PRESCALER(&htim1,cn_music_3[x]);
		__HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_1,cn_music_3[x]-1);
		i++;
		i %= len;
		HAL_Delay(delay_1[music_3[i]-2]);
		__HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_1,0);
		HAL_Delay(delay_2[music_3[i]-2]);
		i++;
		i %= len;
		
  }
  /* USER CODE END 3 */
}

这里补充一下占空比,我为了图省事直接用Counter中的数值减去1作为比较值,小喇叭是可以正常工作(而且理论上已经做到了最大的响度了,或者还可以继续调定时器高频率就能等效调高占空比,但是其他数据也要跟着改变),就是不知道蜂鸣器行不行,不行的话只需要将它除2就行,就能做出来接近50占空比的方波了。

代码为什么这样写,相信读者理解了上面的内容就懂了,然后我们跑一下代码就能听到小喇叭放出音乐啦!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值