STM32 CubeMX学习:5. 其他PWM设备
系列文章目录文章目录
0 前言
这次我们将了解更多使用PWM控制的设备-蜂鸣器,舵机,学习使用PWM功能对蜂鸣器和舵机进行控制。同时将学习有源蜂鸣器和无源蜂鸣器的大致结构与原理以及舵机的基本知识。
1 基础知识
1.1 蜂鸣器
蜂鸣器是一种能够通过电子信号控制的发声器件。在生活中,几乎所有能够发出哔哔响声的电子器件中都装有蜂鸣器。蜂鸣器能够为使用者提供直观的声音信息,是一种常见的人机交互模式。常见的蜂鸣器分为插针型和贴片型。
根据是否内置震荡电路可分为有源蜂鸣器和无源蜂鸣器。有源蜂鸣器只需要提供直流电压就可以通过内部的震荡电路产生震荡电流进而发出声音,而无源蜂鸣器需要输入特定频率的方波才能发出声音。两者比较起来,有源蜂鸣器的控制更加简单,但是只能发出单一频率的声音,而无源蜂鸣器虽然控制起来比较麻烦,但是可以通过改变输入方波的频率发出不同音调的声音,甚至可以用来演奏乐曲。
有源蜂鸣器 | 无源蜂鸣器 | |
---|---|---|
内置震荡源 | 有 | 无 |
激励方式 | 直流电压 | 特定频率方波 |
音调 | 固定 | 可变 |
探索者 STM32F4 开发板板载的蜂鸣器是电磁式有源
1.2 舵机的控制
舵机是机器人中的常见的执行部件,通常使用特定频率的PWM进行控制。
舵机的主要组成部位由一个小型的电机和传动机构(齿轮组)构成,多被用于操控飞行器上的舵面,故而得名舵机。由于控制简单,价格便宜,用于简单的动作控制,下图为市场上常见的舵机。
通常舵机的三根线按照颜色分别为:黑色-GND,红色-VCC,黄色-PWM信号。在使用舵机时,只需要使用杜邦线或者其他连接线接入单片机对应的引脚接口。
舵机使用的PWM信号一般为频率50Hz,高电平时间0.5ms-2.5ms的PWM信号,不同占空比的PWM信号对应舵机转动的角度,以180度舵机为例,对应角度图如下图所示。
2 程序学习
2.1 蜂鸣器的CubeMX配置
2.1.1 有源蜂鸣器
本章需要用到的硬件有:
1)指示灯 DS0(即LED0)
2)蜂鸣器
DS0在之前的博客已有介绍, 而蜂鸣器硬件也是直接连好了的不需要经过任何设置, 直接编写代码就可以了 。蜂鸣器的驱动信号连接在 STM32F4的 PF8上。如图所示:蜂鸣器使用的引脚为PF8。
图中我们用到一个 NPN 三极管(S8050)来驱动蜂鸣器,R61 主要用于防止蜂鸣器的误发声。当 PF.8 输出高电平的时候,蜂鸣器将发声,当 PF.8 输出低电平的时候,蜂鸣器停止发声。相应的代码大家可以参考 点亮LED 的方法配置GPIO,这里就不给相应的代码展示,大家可以自行尝试一下,可以和我多多交流哟!
2.1.2 无源蜂鸣器
但是这不是我们今天主要想讲解的蜂鸣器,我这里给大家讲解一下另一种无源蜂鸣器,由于正点原子的开发板没有板载,这里只给大家参考一下。
下图为一个设计好的无源蜂鸣器的电路
我这里展示的蜂鸣器程序,使用的引脚为PD14,为定时器4的通道3。
1、打开CubeMX,使能定时器4,预分配设置为0,重载值设置为20999,设置通道3为PWM输出,其余设置保持默认即可,此时开发板的PD14引脚变为绿色。
2、点击Generate Code,生成工程。
根据在之前博客学习的知识,可以通过查看数据手册的方式知道定时器4挂载在APB1总线上,对应的总线频率为84MHz,分频值为0,重载值为20999,并通过公式计算得到PWM波的输出频率为4000Hz。
3、程序讲解
在之前的原理介绍中,改变PWM的频率就可以改变无源蜂鸣器的音调。故而改变定时器的分频系数和重载值,改变PWM的频率,就能够控制无源蜂鸣器发出的响声频率。
在主程序中,我们声明了psc和pwm两个变量,分别控制定时器4的分频系数和重载值,每一次循环中这两个变量进行一次自加。
通过宏定义的方式,设置 pwm 的值在 MIN_BUZZER_PWM(10000)和 MAX_BUZZER_PWM(20000)之间变动,psc 的值在0和MAX_PSC(1000)之间变动。
主函数代码如下:
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
pwm++;
psc++;
if(pwm > MAX_BUZZER_PWM)
{
pwm = MIN_BUZZER_PWM;
}
if(psc > MAX_PSC)
{
psc = 0;
}
buzzer_on(psc, pwm);
HAL_Delay(1);
}
/* USER CODE END 3 */
在buzzer_on函数中,将psc和pwm两个值赋值给定时器4的预分频计数器和3号通道的比较寄存器,从而调整PWM信号的周期,进而控制蜂鸣器发出不同声调,不同响度的声音。
void buzzer_on(uint16_t psc, uint16_t pwm)
该函数的返回值为void,该函数的作用在于控制蜂鸣器定时器的分频和重载值,该函数有两个参数
(1)参数1:psc——设置定时器的分频系数
(2)参数2:pwm——设置定时器的重载值
这里我们需要自己定义这个函数,所以我选择新建一个Boards目录,
并在该目录下新建buzzer.c文件,
#include "buzzer.h"
#include "main.h"
extern TIM_HandleTypeDef htim4;
void buzzer_on(uint16_t psc, uint16_t pwm)
{
__HAL_TIM_PRESCALER(&htim4, psc);
__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_3, pwm);
}
void buzzer_off(void)
{
__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_3, 0);
}
<注意>在上面的图片中我犯了个小错误,在步骤2和3之间,我们需要点击一下那个 “单个小磁盘” 保存文件,才可以弹出保存位置的窗口。
并且新建对应的buzzer.h文件放置于对应位置
#ifndef BUZZER_H
#define BUZZER_H
#include "struct_typedef.h"
extern void buzzer_on(uint16_t psc, uint16_t pwm);
extern void buzzer_off(void);
#endif
在这里,为了更方便的定义我们需要的变量类型,我们还需要自己定义一个声明文件struct_typedef.h,和上面定义buzzer.h文件的步骤类似,放置于同一个文件夹下。下面我只是把文件的内容给大家放出来。
#ifndef STRUCT_TYPEDEF_H
#define STRUCT_TYPEDEF_H
typedef signed char int8_t;
typedef signed short int int16_t;
typedef signed int int32_t;
typedef signed long long int64_t;
/* exact-width unsigned integer types */
typedef unsigned char uint8_t;
typedef unsigned short int uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
typedef unsigned char bool_t;
typedef float fp32;
typedef double fp64;
#endif
然后我们在我们的Boards目录中添加buzzer.c文件
最后一定不要忘记在main.c文件中对应位置引用buzzer.h头文件,否则找不到我们定义的buzzer_on(uint16_t psc, uint16_t pwm);函数
这样我们就得到了完整的可执行代码。
2.2 舵机在CubeMX的配置
这里我示范输出7路pwm波,为复杂功能做准备。
1、首先开启定时器1,预分频值设置为167,重载值设置为19999。打开1-4的PWM输出通道,在PWM通道的设置中,将Pulse值设置为2000,比较寄存器的初始值就会被设成2000。
2、接着开启定时器8,将预分频值设置为167,重载值设置为19999。打开1-3的PWM输出通道,在PWM通道的设置中,将Pulse值设置为2000。
可以通过查看源代码或者数据手册的方式我们知道定时器1和8挂载在APB2总线上,对应的总线频率为168MHz,定时器分频值为167,重载值19999,并通过公式计算得到PWM波的输出频率为50Hz,对应的周期为20ms。
通过PWM博客学习的知识,计算出PWM占空比最小为500/20000即2.5%,对应高电平时间为20ms乘以2.5%等于0.5ms,最大为2000/20000即10%,对应高电平时间为20ms乘以10%等于2ms。
3、点击Generate Code,生成工程。
4、程序的讲解
mai函数中,
在初始化时,通过HAL_TIM_Base_Start函数启动定时器1和定时器8,再通过HAL_TIM_PWM_Start函数将定时器1的1,2,3,4号通道的PWM输出和定时器8的1,2,3通道的PWM输出开启。
在主循环中,通过__HAL_TIM_SetCompare来设置PWM的占空比,需要注意的是__HAL_TIM_SetCompare并非是一个函数,查看定义后会发现这其实是一个宏,最后其功能的依然是依靠将数值赋值给定时器某一通道比较寄存器来实现的。
#define __HAL_TIM_SetCompare __HAL_TIM_SET_COMPARE
#define __HAL_TIM_SET_COMPARE(__HANDLE__, __CHANNEL__, __COMPARE__) \
(((__CHANNEL__) == TIM_CHANNEL_1) ? ((__HANDLE__)->Instance->CCR1 = (__COMPARE__)) :\
((__CHANNEL__) == TIM_CHANNEL_2) ? ((__HANDLE__)->Instance->CCR2 = (__COMPARE__)) :\
((__CHANNEL__) == TIM_CHANNEL_3) ? ((__HANDLE__)->Instance->CCR3 = (__COMPARE__)) :\
((__HANDLE__)->Instance->CCR4 = (__COMPARE__)))
使用__HAL_TIM_SetCompare进行占空比设置时,依次对应的输入为:
参数1 | *htim 定时器的句柄指针,如定时器1就输入&htim1,定时器2就输入&htim2 |
---|---|
参数2 | Channel 定时器PWM输出的通道,比如通道1为TIM_CHANNEL_1 |
参数3 | 需要赋值给比较寄存器的值,PWM占空比等于比较值除以重载值 |
随着主循环中各个PWM输出的占空比的变化,舵机的转动角度也随之变化。
代码我已经放到了我的GitHub仓库,如有需要可以下载使用:
CubeMX学习