目录
????????PWM就是脉冲宽度调制,是一种对模拟信号电平进行数字编码的方法,PWM波就是具有一定占空比的方波信号,通过定时器的设置可以控制方波的频率和占空比,从而对模拟电压进行数字编码,理论上,只要带宽足够(PWM波的频率足够高)任何模拟值都可以使用PWM进行编码,使用定时器生成PWM波的工作原理是边缘对齐方式
????????其基本工作原理描述如下
1、设置自动重装载寄存器ARR的值,这个值决定了PWM波一个周期的长度,比如PWM一个周期是100ms
2、设置捕获/比较寄存器CRR的值,在一个ARR计数周期内,当计数器值CNT<CCR时,PWM参考信号OCxREF(x表示定时器编号)为高电平,当CNT>=CCR时,PWM参考信号OCxREF为低电平,并且产生CC(捕获/比较)事件,所以CCR的值决定了占空比,例如一个PWM周期为100ms,设置CCR的值让PWM波一个周期内高电平时间长为70ms,则占空比为70%
3、在计数器的值达到ARR时,产生UEV中断事件,CCR具有预装载功能,修改的CCR值需要在下一个UEV事件才能生成
????????与生成PWM波相关的HAL函数如下表所示,还有以DMA方式启动和停止PWM的函数,但定时器基本不使用DMA方式,后文也不会列出各种模式DMA相关函数,以下函数的用法与前面篇章的基础定时器函数用法类似,其头文件在stm32f1xx_hal_tim.h中
其中还有几个重要的函数需要我们记录一下
HAL_TIM_PWM_PulseFinishedCallback()这是PWM的中断回调函数
__HAL_TIM_SET_COMPARE() 这个函数可以改变占空比的值,实例中会用到
__HAL_TIM_GET_COMPARE() 这个函数可以读取占空比的值,实际都是对寄存器CCR操作
????????CubeMX中通道以上的配置我们都在以前介绍过了,现在先介绍通道中的选项都是什么意思
????????我们先随便选择一个定时器,设置定时器时钟源为内部时钟信号,然后选择通道打开PWM,这里我们可以选择是否打开定时器中断,如果打开后每次触发PWM中断都会跳转到定时器中断回调函数中,需要我们用户自己编写
????????到我们配置定时器PWM参数的时候了,上面这部分和基础定时器中的设置是一样的,我们修改ARR和PSC即可,这里我设置的是20KHZ,频率的计算方式就是周期的倒数,其他的默认设置即可,我们来介绍以下PWM中特有的设置?
配置CubeMX结束后就可以生成工程了,我们这里简单来写一个呼吸灯,首先不要忘记在我们生成的tim,c文件中将定时器PWM打开哦,否则使用不了,先开启定时器然后再开启PWM模式
void MX_TIM1_Init(void)
{
/* USER CODE BEGIN TIM1_Init 0 */
/* USER CODE END TIM1_Init 0 */
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};
/* USER CODE BEGIN TIM1_Init 1 */
/* USER CODE END TIM1_Init 1 */
htim1.Instance = TIM1;
htim1.Init.Prescaler = 0;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 3600-1;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_PWM_Init(&htim1) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_4) != HAL_OK)
{
Error_Handler();
}
sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
sBreakDeadTimeConfig.DeadTime = 0;
sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM1_Init 2 */
HAL_TIM_Base_Start(&htim1); /* 先开启定时器 */
HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1); /* 启动PWM输出 */
/* USER CODE END TIM1_Init 2 */
HAL_TIM_MspPostInit(&htim1);
}
接下来附带流水灯的程序
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
while (pwmVal< 500)
{
pwmVal++;
__HAL_TIM_SetCompare(&htim1, TIM_CHANNEL_1, pwmVal); //修改比较值,修改占空比
// TIM1->CCR1 = pwmVal; 与上方相同
HAL_Delay(1);
}
while (pwmVal)
{
pwmVal--;
__HAL_TIM_SetCompare(&htim1, TIM_CHANNEL_1, pwmVal); //修改比较值,修改占空比
// TIM1->CCR1 = pwmVal; 与上方相同
HAL_Delay(1);
}
HAL_Delay(200);
/* USER CODE END 3 */
}