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调制过程中,电机的绕组在不同开关频率下工作时,产生不同分贝的噪声。

  • 🧨am32-firmware(https://github.com/am32-firmware/AM32)在线调参页面:
  • https://esc-configurator.com/
    


    可以在线测试不同音乐曲目:

    📘基于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)会打印当前频率:
  • 作者:perseverance52

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32驱动直流无刷电机(BLDC)发声指南

    发表回复