|
|
|
@ -377,19 +377,253 @@ HAL_TIM_PeriodElapsedCallback 是我们在主函数中定义的用户逻辑函
|
|
|
|
|
|
|
|
|
|
# 4. PWM输出功能
|
|
|
|
|
|
|
|
|
|
[什么是PWM](https://www.bilibili.com/video/BV1HD4y1k74L/?spm_id_from=333.337.search-card.all.click&vd_source=3c8e333d6657680a469ddf0238f01d6a)
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
上述视频是以电压来讲的,我们试图用能量的方式给大家理解:
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
上图是占空比 100% 的波形,显然就是直流。那么我们看做功的关系应该怎么计算?
|
|
|
|
|
|
|
|
|
|
功率:U<sup>2</sup>/R
|
|
|
|
|
|
|
|
|
|
做功:TxU<sup>2</sup>/R
|
|
|
|
|
|
|
|
|
|
显然,这里负责的R应该是一个常量。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
同样,占空比为70%的波形做功应该是:
|
|
|
|
|
|
|
|
|
|
做功:DxTxU<sup>2</sup>/R
|
|
|
|
|
|
|
|
|
|
这里的D表示占空比。
|
|
|
|
|
|
|
|
|
|
如果负载是一个LED,做功多,就亮;做功少就暗,这就达到了调光的效果;同样,也可以调整电机的快慢。
|
|
|
|
|
|
|
|
|
|
> 注意,PWM并不适合所有的功率调节,例如LED灯的调光如果使用简单的PWM,其实是LED不断的闪烁(亮的时间和灭的时间比例)。如果闪烁频率低,会造成人眼的疲劳,导致眼睛的疾病。目前的LED调光大多是使用调整电压(电流)来进行的。对电压或者电流的调整基础一般还是PWM,不过后级需要更多的滤波电路,以及电压(电流)检测电路来形成反馈回路,使得输出更为稳定。
|
|
|
|
|
|
|
|
|
|
## 4.1. 捕获/比较通道
|
|
|
|
|
|
|
|
|
|
## 4.2. PWM实现原理
|
|
|
|
|
[动画讲解轻松学会STM32的PWM](https://www.bilibili.com/video/BV1Yx4y1x7xY/?spm_id_from=333.337.search-card.all.click&vd_source=3c8e333d6657680a469ddf0238f01d6a)
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
1. 输入捕获单元:用于信号测量;
|
|
|
|
|
2. 捕获/比较寄存器(TIMx_CCR):在输入模式下,用于存放存放事件的计数(边缘检测时,计数是多少,可以测量波长和占空比);输出模式下,存放一个比较值,用于和计数器寄存器(CNT)进行比较,实现信号的反转,从而实现PWM的占空比调整(后面讲)。
|
|
|
|
|
3. 输出比较单元:根据 CCR/ARR/CNT来产生波形(PWM)。
|
|
|
|
|
|
|
|
|
|
> 每个定时器都有1~4个上述的电路(通道)。因此,一个时钟可以同时进行1-4个不同的PWM或者输入信号测量。后续的代码演示中,我们一般只使用一个确定定时器的一个通道。
|
|
|
|
|
|
|
|
|
|
## 4.2. PWM实现原理(p233)
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
1. 缺省情况下,输出为高电平;
|
|
|
|
|
2. TIMx_CLK经过PSC分频后,为定时器提供计数脉冲;
|
|
|
|
|
3. 计数脉冲使得CNT的计数从0进行累加;
|
|
|
|
|
4. 当CNT和CCR的值相等后,输出极性反转,从高电平变为低电平;
|
|
|
|
|
5. CNT持续计数;
|
|
|
|
|
6. 当CNT与ARR值相等的时候,极性再次反转,从低电平变成高电平;一个周期结束,CNT被重新设置成0;
|
|
|
|
|
|
|
|
|
|
因此:
|
|
|
|
|
|
|
|
|
|
1. CK_PSC(TIMx_CLK)、PSC、ARR 共同决定了PWM波形输出的频率,这一点和作为定时器是一致的;
|
|
|
|
|
2. CCR和ARR+1的比值决定了占空比:
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
> 公式8-7与公式8-4是一致的,其基本原理也是一致的。
|
|
|
|
|
|
|
|
|
|
课堂练习:
|
|
|
|
|
|
|
|
|
|
如图所示:
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
需要一个频率1000Hz,占空比 30 % 的PWM 应该如何进行设置?
|
|
|
|
|
|
|
|
|
|
如果选用TIM1作为PWM的时钟,PSC、ARR、CCR的值应该如何选取?
|
|
|
|
|
|
|
|
|
|
如果选用TIM4作为PWM的时钟,PSC、ARR、CCR的值应该如何选取?
|
|
|
|
|
|
|
|
|
|
如果选用TIM2作为PWM的时钟,PSC、ARR、CCR的值应该如何选取?
|
|
|
|
|
|
|
|
|
|
## 4.3. PWM输出功能的数据类型(p234)
|
|
|
|
|
|
|
|
|
|
```c
|
|
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
uint32_t OCMode; /*!< Specifies the TIM mode.
|
|
|
|
|
This parameter can be a value of @ref TIM_Output_Compare_and_PWM_modes */
|
|
|
|
|
|
|
|
|
|
uint32_t Pulse; /*!< Specifies the pulse value to be loaded into the Capture Compare Register.
|
|
|
|
|
This parameter can be a number between Min_Data = 0x0000 and Max_Data = 0xFFFF */
|
|
|
|
|
|
|
|
|
|
uint32_t OCPolarity; /*!< Specifies the output polarity.
|
|
|
|
|
This parameter can be a value of @ref TIM_Output_Compare_Polarity */
|
|
|
|
|
|
|
|
|
|
uint32_t OCNPolarity; /*!< Specifies the complementary output polarity.
|
|
|
|
|
This parameter can be a value of @ref TIM_Output_Compare_N_Polarity
|
|
|
|
|
@note This parameter is valid only for timer instances supporting break feature. */
|
|
|
|
|
|
|
|
|
|
uint32_t OCFastMode; /*!< Specifies the Fast mode state.
|
|
|
|
|
This parameter can be a value of @ref TIM_Output_Fast_State
|
|
|
|
|
@note This parameter is valid only in PWM1 and PWM2 mode. */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint32_t OCIdleState; /*!< Specifies the TIM Output Compare pin state during Idle state.
|
|
|
|
|
This parameter can be a value of @ref TIM_Output_Compare_Idle_State
|
|
|
|
|
@note This parameter is valid only for timer instances supporting break feature. */
|
|
|
|
|
|
|
|
|
|
uint32_t OCNIdleState; /*!< Specifies the TIM Output Compare pin state during Idle state.
|
|
|
|
|
This parameter can be a value of @ref TIM_Output_Compare_N_Idle_State
|
|
|
|
|
@note This parameter is valid only for timer instances supporting break feature. */
|
|
|
|
|
} TIM_OC_InitTypeDef;
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 4.3.1. OCMode
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
### 4.3.2. Pulse(CCR)
|
|
|
|
|
|
|
|
|
|
整形,这个值与ARR共同确定占空比。
|
|
|
|
|
|
|
|
|
|
### 4.3.3. OCPolarity
|
|
|
|
|
|
|
|
|
|
输出极性,如果是低电平有效,相当于占空反向;
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
### 4.3.4. OCFastMode
|
|
|
|
|
|
|
|
|
|
PWM快速模式使能
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
### 4.3.5. PWM1/PWM2
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
其实就是极性发转。注意,通常使用PWM1方式,如果是PWM2方式,占空比是100%-(CCR/(ARR+1)x100%)。
|
|
|
|
|
|
|
|
|
|
## 4.4. PWM输出功能的接口函数、
|
|
|
|
|
|
|
|
|
|
### 4.4.1. PWM 轮询方式启动函数(HAL_TIM_PWM_Start)
|
|
|
|
|
|
|
|
|
|
该函数用于在轮询方式下启动PWM信号的输出,具体描述如表8-18所示。
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
### 4.4.2. 捕获/比较寄存器设置函数(__HAL_TIM_SET_COMPARE)
|
|
|
|
|
|
|
|
|
|
该函数用于设置捕获/比较寄存器TIMxCCRn的内容,采用带参数的宏实现(宏函数),具体描述如表 8-19 所示。该函数可以在任何时间改变CCR的值,用于动态改变占空比。
|
|
|
|
|
|
|
|
|
|
## 4.3. PWM输出功能的数据类型
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
## 4.4. PWM输出功能的接口函数
|
|
|
|
|
## 4.5. 基础任务:输出PWM信号(注意与书上不一致)
|
|
|
|
|
|
|
|
|
|
使用定时器,产生一个频率为1000Hz,占空比为50%的方波,用于驱动蜂鸣器。
|
|
|
|
|
|
|
|
|
|
### 4.5.1. 实现过程
|
|
|
|
|
|
|
|
|
|
1. 建立一个项目 Buzzer
|
|
|
|
|
2. 蜂鸣器是在PB9上,设置PB9的功能是 **TIM4_CH4**
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
3. 配置TIM4:
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
注意:这里缺省配置的APB1上的定时器时钟是8M,要实现1000Hz的频率,PSC=79;ARR=99。注意在设置的时候,ARR+1最好是100或者是1000等10的整数倍数。因为Pulse和ARR+1的比值决定了占空比。设置Pulse(CCR)为50,这样就有了1000Hz,50%的占空比。
|
|
|
|
|
|
|
|
|
|
4. 在main.c 的大约92行加入代码:
|
|
|
|
|
|
|
|
|
|
```c
|
|
|
|
|
/* USER CODE BEGIN 2 */
|
|
|
|
|
HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_4);
|
|
|
|
|
/* USER CODE END 2 */
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
5. 编译下载后,可以听到蜂鸣器的声音。
|
|
|
|
|
|
|
|
|
|
### 4.5.2. 代码分析
|
|
|
|
|
|
|
|
|
|
main.c 大约147行左右是定时器的初始化:
|
|
|
|
|
|
|
|
|
|
```c
|
|
|
|
|
static void MX_TIM4_Init(void) {
|
|
|
|
|
|
|
|
|
|
/* USER CODE BEGIN TIM4_Init 0 */
|
|
|
|
|
|
|
|
|
|
/* USER CODE END TIM4_Init 0 */
|
|
|
|
|
|
|
|
|
|
TIM_ClockConfigTypeDef sClockSourceConfig = { 0 };
|
|
|
|
|
TIM_MasterConfigTypeDef sMasterConfig = { 0 };
|
|
|
|
|
TIM_OC_InitTypeDef sConfigOC = { 0 };
|
|
|
|
|
|
|
|
|
|
/* USER CODE BEGIN TIM4_Init 1 */
|
|
|
|
|
|
|
|
|
|
/* USER CODE END TIM4_Init 1 */
|
|
|
|
|
htim4.Instance = TIM4;
|
|
|
|
|
htim4.Init.Prescaler = 79;
|
|
|
|
|
htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
|
|
|
|
|
htim4.Init.Period = 99;
|
|
|
|
|
htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
|
|
|
|
|
htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
|
|
|
|
|
if (HAL_TIM_Base_Init(&htim4) != HAL_OK) {
|
|
|
|
|
Error_Handler();
|
|
|
|
|
}
|
|
|
|
|
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
|
|
|
|
|
if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK) {
|
|
|
|
|
Error_Handler();
|
|
|
|
|
}
|
|
|
|
|
if (HAL_TIM_PWM_Init(&htim4) != HAL_OK) {
|
|
|
|
|
Error_Handler();
|
|
|
|
|
}
|
|
|
|
|
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
|
|
|
|
|
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
|
|
|
|
|
if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig)
|
|
|
|
|
!= HAL_OK) {
|
|
|
|
|
Error_Handler();
|
|
|
|
|
}
|
|
|
|
|
sConfigOC.OCMode = TIM_OCMODE_PWM1;
|
|
|
|
|
sConfigOC.Pulse = 50;
|
|
|
|
|
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
|
|
|
|
|
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
|
|
|
|
|
if (HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_4)
|
|
|
|
|
!= HAL_OK) {
|
|
|
|
|
Error_Handler();
|
|
|
|
|
}
|
|
|
|
|
/* USER CODE BEGIN TIM4_Init 2 */
|
|
|
|
|
|
|
|
|
|
/* USER CODE END TIM4_Init 2 */
|
|
|
|
|
HAL_TIM_MspPostInit(&htim4);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
main.c 的92行,用户代码:
|
|
|
|
|
|
|
|
|
|
```c
|
|
|
|
|
/* USER CODE BEGIN 2 */
|
|
|
|
|
HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_4);
|
|
|
|
|
/* USER CODE END 2 */
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 4.5. 基础任务:输出PWM信号
|
|
|
|
|
上述代码是以查询方式启动TIM4的PWM(因为没有使用中断)。htim4是TIM4的结构体,该结构书上没有说明,和定时中断中的结构体是一致的,可以理解成C语言的对象,包括数据与很多函数指针。
|
|
|
|
|
|
|
|
|
|
## 4.6. 进阶任务:实现呼吸灯
|
|
|
|
|
## 4.6. 进阶任务:实现呼吸灯(略)
|
|
|
|
|
|
|
|
|
|
# 5. 输入捕获功能
|
|
|
|
|
|
|
|
|
|