STM32 驱动直流无刷电机(BLDC)发声

STM32驱动直流无刷电机(BLDC)发声


  • ✨需要注意,这是驱动直流无刷电机(BLDC)发出声音,不是直接驱动无刷电机转动。
  • 📍内容移植参考:ttps://github.com/AlkaMotors/AM32-MultiRotor-ESC-firmware
  • 📍Arduino平台BLDC Music开源工程:https://github.com/owennewo/tone-player
  • 🔖测试单片机:STM32L431、STM32F446.

🔖在很多商业用的电调中,基本都带,在上电自检或设置以及输入信号检测时,电机会发出提示声音。BLDC电机在PWM调制过程中,电机的绕组在不同开关频率下工作时,产生不同分贝的噪声。

📄弄明白发声原理,驱动BLDC电机发声其实很简单,只需要给BLDC电机任意一相导通,在6步换相中,给6步其中的任意一步,在短时间内,给定一个特定频率,较低占空比(小于3%)的PWM信号即可发声。
  • 🌿PWM 频率推荐:1.6KHz - 3KHz之间
  • 🌿PWM占空比推荐:1% ,不要超过3%。
  • 🧨am32-firmware(https://github.com/am32-firmware/AM32)在线调参页面:
https://esc-configurator.com/

在这里插入图片描述
可以在线测试不同音乐曲目:
在这里插入图片描述

bluejay:b=570,o=4,d=32:4b,p,4e5,p,4b,p,4f#5,2p,4e5,2b5,8b5
  • 🌿模拟bluejay旋律音功能测试效果:

    BLDC无刷电机模拟音乐旋律

  • ✨代码解析:(个人理解)其中字母bef代表频率,字母p代表停顿的时间。
  • 🌿音符与频率对照表:https://www.gotozhuan.com/cn/tools/tool_pitch_map.php
音符	频率(hz)	波长(cm)
C0	16.35	2109.89
C#0	17.32	1991.47
D0	18.35	1879.69
D#0	19.45	1774.2
E0	20.60	1674.62
F0	21.83	1580.63
F#0	23.12	1491.91
G0	24.50	1408.18
G#0	25.96	1329.14
A0	27.50	1254.55
A#0	29.14	1184.13
B0	30.87	1117.67
C1	32.70	1054.94
C#1	34.65	995.73
D1	36.71	939.85
D#1	38.89	887.1
E1	41.20	837.31
F1	43.65	790.31
F#1	46.25	745.96
G1	49.00	704.09
G#1	51.91	664.57
A1	55.00	627.27
A#1	58.27	592.07
B1	61.74	558.84
C2	65.41	527.47
C#2	69.30	497.87
D2	73.42	469.92
D#2	77.78	443.55
E2	82.41	418.65
F2	87.31	395.16
F#2	92.50	372.98
G2	98.00	352.04
G#2	103.83	332.29
A2	110.00	313.64
A#2	116.54	296.03
B2	123.47	279.42
C3	130.81	263.74
C#3	138.59	248.93
D3	146.83	234.96
D#3	155.56	221.77
E3	164.81	209.33
F3	174.61	197.58
F#3	185.00	186.49
G3	196.00	176.02
G#3	207.65	166.14
A3	220.00	156.82
A#3	233.08	148.02
B3	246.94	139.71
C4	261.63	131.87
C#4	277.18	124.47
D4	293.66	117.48
D#4	311.13	110.89
E4	329.63	104.66
F4	349.23	98.79
F#4	369.99	93.24
G4	392.00	88.01
G#4	415.30	83.07
A4	440.00	78.41
A#4	466.16	74.01
B4	493.88	69.85
C5	523.25	65.93
C#5	554.37	62.23
D5	587.33	58.74
D#5	622.25	55.44
E5	659.25	52.33
F5	698.46	49.39
F#5	739.99	46.62
G5	783.99	44.01
G#5	830.61	41.54
A5	880.00	39.2
A#5	932.33	37
B5	987.77	34.93
C6	1046.50	32.97
C#6	1108.73	31.12
D6	1174.66	29.37
D#6	1244.51	27.72
E6	1318.51	26.17
F6	1396.91	24.7
F#6	1479.98	23.31
G6	1567.98	22
G#6	1661.22	20.77
A6	1760.00	19.6
A#6	1864.66	18.5
B6	1975.53	17.46
C7	2093.00	1
  • 📜换算成执行语句:单位:Hz
frequency(4*570),delay(1),frequency(4*659.25),delay(1),frequency(4*570),delay(1),frequency(4*739.99),delay(2),frequency(4*659.25),frequency(2*987.77),frequency(8*987.77)

📘基于AM32-MultiRotor-ESC-firmware实现发声的主要实现代码

  • 🌿sounds.c
/*
 * sounds.c
 *
 *  Created on: May 13, 2020
 *      Author: Alka
 */

#include "sounds.h"
#include "phaseouts.h"
#include "functions.h"
//#include "eeprom.h"
#include "targets.h"
#include "common.h"


uint8_t beep_volume;

//uint8_t blueJayTuneBuffer[128] = {};

void pause(uint16_t ms) {
	TIM1->CCR1 = 0; // volume of the beep, (duty cycle) don't go above 25
	TIM1->CCR2 = 0;
	TIM1->CCR3 = 0;

	delayMillis(ms);
	TIM1->CCR1 = beep_volume; // volume of the beep, (duty cycle) don't go above 25 out of 2000蜂鸣声的音量(占空比)在2000中不超过25
	TIM1->CCR2 = beep_volume;
	TIM1->CCR3 = beep_volume;
}


void setVolume(uint8_t volume) {
	if (volume > 11) {
		volume = 11;
	}
	if (volume < 0) {
		volume = 0;
	}
	beep_volume = volume * 2;           // volume variable from 0 - 11 equates to CCR value of 0-22
}

void setCaptureCompare() {
	TIM1->CCR1 = beep_volume; // volume of the beep, (duty cycle) don't go above 25 out of 2000
	TIM1->CCR2 = beep_volume;
	TIM1->CCR3 = beep_volume;
}

void playBJNote(uint16_t freq, uint16_t bduration) {       // hz and ms
	uint16_t timerOne_reload = TIM1_AUTORELOAD;

	TIM1->PSC = 10;
	timerOne_reload = CPU_FREQUENCY_MHZ * 100000 / freq;

	TIM1->ARR = timerOne_reload;
	TIM1->CCR1 = beep_volume * timerOne_reload / TIM1_AUTORELOAD ; // volume of the beep, (duty cycle) don't go above 25 out of 2000
	TIM1->CCR2 = beep_volume * timerOne_reload / TIM1_AUTORELOAD;
	TIM1->CCR3 = beep_volume * timerOne_reload / TIM1_AUTORELOAD;

	delayMillis(bduration);
}


uint16_t getBlueJayNoteFrequency(uint8_t bjarrayfreq) {
	return 10000000 / (bjarrayfreq * 247 + 4000);
}



void playStartupTune() {
	__disable_irq();
		TIM1->ARR = TIM1_AUTORELOAD;
		setCaptureCompare();
		comStep(3);       // activate a pwm channel
		TIM1->PSC = 55;        // frequency of beep
		delayMillis(200);         // duration of beep

		comStep(5);
		TIM1->PSC = 40;            // next beep is higher frequency
		delayMillis(200);

		comStep(6);
		TIM1->PSC = 25;         // higher again..
		delayMillis(200);


		allOff();                // turn all channels low again
		TIM1->PSC = 0;           // set prescaler back to 0.


	TIM1->ARR = TIMER1_MAX_ARR;
	__enable_irq();
}

void playBrushedStartupTune() {
	__disable_irq();
	TIM1->ARR = TIM1_AUTORELOAD;
	setCaptureCompare();
	comStep(1);       // activate a pwm channel
	TIM1->PSC = 40;        // frequency of beep
	delayMillis(300);         // duration of beep
	comStep(2);       // activate a pwm channel
	TIM1->PSC = 30;        // frequency of beep
	delayMillis(300);         // duration of beep
	comStep(3);       // activate a pwm channel
	TIM1->PSC = 25;        // frequency of beep
	delayMillis(300);         // duration of beep
	comStep(4);
	TIM1->PSC = 20;         // higher again..
	delayMillis(300);
	allOff();                // turn all channels low again
	TIM1->PSC = 0;           // set prescaler back to 0.
//	signaltimeout = 0;
	TIM1->ARR = TIMER1_MAX_ARR;
	__enable_irq();
}

void playDuskingTune() {
	setCaptureCompare();
	TIM1->ARR = TIM1_AUTORELOAD;
	comStep(2);       // activate a pwm channel
	TIM1->PSC = 60;        // frequency of beep
	delayMillis(200);         // duration of beep
	TIM1->PSC = 55;            // next beep is higher frequency
	delayMillis(150);
	TIM1->PSC = 50;         // higher again..
	delayMillis(150);
	TIM1->PSC = 45;        // frequency of beep
	delayMillis(100);         // duration of beep
	TIM1->PSC = 50;            // next beep is higher frequency
	delayMillis(100);
	TIM1->PSC = 55;         // higher again..
	delayMillis(100);
	TIM1->PSC = 25;         // higher again..
	delayMillis(200);
	TIM1->PSC = 55;         // higher again..
	delayMillis(150);
	allOff();                // turn all channels low again
	TIM1->PSC = 0;           // set prescaler back to 0.
	TIM1->ARR = TIMER1_MAX_ARR;
}


void playInputTune2() {
	TIM1->ARR = TIM1_AUTORELOAD;
	__disable_irq();
//	LL_IWDG_ReloadCounter(IWDG);
	TIM1->PSC = 60;
	setCaptureCompare();
	comStep(1);
	delayMillis(75);
	TIM1->PSC = 80;
	delayMillis(75);
	TIM1->PSC = 90;
//	LL_IWDG_ReloadCounter(IWDG);
	delayMillis(75);
	allOff();
	TIM1->PSC = 0;
//	signaltimeout = 0;
	TIM1->ARR = TIMER1_MAX_ARR;
	__enable_irq();
}




void playInputTune() {
	__disable_irq();
	TIM1->ARR = TIM1_AUTORELOAD;
//	LL_IWDG_ReloadCounter(IWDG);
	TIM1->PSC = 80;
	setCaptureCompare();
	comStep(3);
	delayMillis(100);
	TIM1->PSC = 70;
	delayMillis(100);
	TIM1->PSC = 40;
	delayMillis(100);
	allOff();
	TIM1->PSC = 0;
//	signaltimeout = 0;
	TIM1->ARR = TIMER1_MAX_ARR;
	__enable_irq();
}

void playDefaultTone() {
	TIM1->ARR = TIM1_AUTORELOAD;
	TIM1->PSC = 50;
	setCaptureCompare();
	comStep(2);
	delayMillis(150);
//	LL_IWDG_ReloadCounter(IWDG);
	TIM1->PSC = 30;
	delayMillis(150);
	allOff();
	TIM1->PSC = 0;
//	signaltimeout = 0;
	TIM1->ARR = TIMER1_MAX_ARR;

}

void playChangedTone() {
	TIM1->ARR = TIM1_AUTORELOAD;
	TIM1->PSC = 40;
	setCaptureCompare();
	comStep(2);
	delayMillis(150);
//	LL_IWDG_ReloadCounter(IWDG);
	TIM1->PSC = 80;
	delayMillis(150);
	allOff();
	TIM1->PSC = 0;
//	signaltimeout = 0;
	TIM1->ARR = TIMER1_MAX_ARR;

}


void playBeaconTune3() {
	TIM1->ARR = TIM1_AUTORELOAD;
	__disable_irq();
	setCaptureCompare();
	for (int i = 119 ; i > 0 ; i = i - 2) {
//		LL_IWDG_ReloadCounter(IWDG);
		comStep(i / 20);
		TIM1->PSC = 10 + (i / 2);
		delayMillis(10);
	}
	allOff();
	TIM1->PSC = 0;

	TIM1->ARR = TIMER1_MAX_ARR;
	__enable_irq();
}

  • 🌿main测试代码:
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 */

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

    /* USER CODE BEGIN SysInit */

    /* USER CODE END SysInit */

    /* Initialize all configured peripherals */
    MX_GPIO_Init();
    MX_COMP1_Init();
    MX_TIM1_Init();
    MX_TIM6_Init();
    MX_TIM16_Init();
    MX_USART2_UART_Init();
    // MX_IWDG_Init();
    MX_TIM7_Init();
    /* USER CODE BEGIN 2 */
    HAL_TIM_Base_Start(&htim6);
    HAL_TIM_Base_Start(&htim7);
    HAL_TIM_Base_Start(&htim16);

    HAL_TIM_GenerateEvent(&htim1, TIM_EVENTSOURCE_UPDATE);
    PWM_Init();


    TIMER1_MAX_ARR = map(35, 24, 48, TIM1_AUTORELOAD, TIM1_AUTORELOAD / 2);//24 - 48范围
    // TIMER1_MAX_ARR = map(20, 12, 24, TIM1_AUTORELOAD * 2, TIM1_AUTORELOAD); //12 - 24范围
    // TIMER1_MAX_ARR = map(10, 7, 16, TIM1_AUTORELOAD * 3, TIM1_AUTORELOAD / 2 * 3);//7 - 16范围

    TIM1->ARR = TIMER1_MAX_ARR;//2570装载值不同,影响声音
    setVolume(5);
    /* USER CODE END 2 */

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

        /* USER CODE BEGIN 3 */

        printf("from playInputTune Sound.\r\n");
        playInputTune();//
        LL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
        HAL_Delay(2500);
        printf("from playInputTune2 Sound.\r\n");
        playInputTune2();
        LL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
        HAL_Delay(2500);
        printf("from playInputTune3 Sound.\r\n");
        playBeaconTune3();
        LL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
        HAL_Delay(2500);
    }
    /* USER CODE END 3 */
}
📚测试工程
  • 🔖STM32L431
链接:https://pan.baidu.com/s/1hdn-vaNXe4UvFgnPtAgJlA?pwd=ikvv 
提取码:ikvv

📒自定义声音

void DIY_on_tone(void)
{
	__disable_irq();
	TIM1->ARR = TIM1_AUTORELOAD;
	TIM1->PSC = 30;
	TIM1->CCR1 = beep_volume; // volume of the beep, (duty cycle) don't go above 25 out of 2000
	TIM1->CCR2 = beep_volume;
	TIM1->CCR3 = beep_volume;
	comStep(1);
	delayMillis(150);
	TIM1->PSC = 40;
		TIM1->CCR1 = beep_volume; // volume of the beep, (duty cycle) don't go above 25 out of 2000
	TIM1->CCR2 = beep_volume;
	TIM1->CCR3 = beep_volume;
	delayMillis(200);
	TIM1->PSC = 50;
	TIM1->CCR1 = beep_volume; // volume of the beep, (duty cycle) don't go above 25 out of 2000
	TIM1->CCR2 = beep_volume;
	TIM1->CCR3 = beep_volume;
	delayMillis(250);
	allOff();
	TIM1->PSC = 0;
	TIM1->ARR = TIMER1_MAX_ARR;
	__enable_irq();
	
}
  • 🌿comStep()函数所做的事情就是,让六步换相中,只开通一条通路。其余高阻关闭。如果只是让其发出声音,可以是6步中的任意一项。
void  comStep (int newStep){
//TIM14->CNT = 0;
switch(newStep)
{

        case 1:			//A-B
        	phaseAPWM();
        	phaseBLOW();
        	phaseCFLOAT();
        	break;


        case 2:		// C-B
        	phaseAFLOAT();
        	phaseBLOW();
        	phaseCPWM();
        	break;



        case 3:	// C-A
        	phaseALOW();
        	phaseBFLOAT();
        	phaseCPWM();
        	break;


        case 4:// B-A
        	phaseALOW();
        	phaseBPWM();
        	phaseCFLOAT();
        	break;


        case 5:    // B-C
        	phaseAFLOAT();
        	phaseBPWM();
        	phaseCLOW();
        	break;


        case 6:      // A-C
        	phaseAPWM();
        	phaseBFLOAT();
        	phaseCLOW();
        	break;
	}

//stop_time = TIM14->CNT;

}

📘Arduino tone-player项目简介

  • 🔖工程基于VSCode platformio ,基于Arduino平台。直接使用VSCode打开,如果安装了STM32相关固件,会自动加载并安装相对应的库。(编译工程时,不要有中文路径名,否则会在最终生成文件时报错)
    在这里插入图片描述
  • 🛠上传支持:stlink cmsis-dap, jlink

-🌟 如果点击烧录时,OpenOCD上传报错,可以直接选择对应生成的.bin文件进行烧录。

Error: timed out while waiting for target halted
embedded:startup.tcl:1516: Error: ** Unable to reset target **
in procedure 'program' 
in procedure 'program_error' called at file "embedded:startup.tcl", line 1553
at file "embedded:startup.tcl", line 1516
*** [upload] Error 1
========================================= [FAILED] Took 194.56 seconds =========================================
  • 👉修改路径下:C:\Users\Administrator\.platformio\packages\tool-openocd\openocd\scripts\board相关型号对应的.cfg文件:
    在这里插入图片描述
  • 🔖将cfg里面的reset_config srst_only改为reset_config none.
  • 🔧工程依赖SimpleFOCSimpleFOCDrivers
  • 📄包含以下音乐内容:
  • RTTTL_CROATIA
  • RTTTL_FRANCE
  • RTTTL_WALES
  • RTTTL_USA
  • RTTTL_SIMPSONS
  • RTTTL_PINK_PANTHER
  • RTTTL_SUPER_MARIO_BROS
  • RTTTL_SUPER_MARIO_BROS_BASS
  • RTTTL_SUPER_MARIO_BROS_POLY
  • 🌿在运行时,串口2(PA2,PA3)会打印当前频率:
    在这里插入图片描述
  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值