实验目的
通过上一张的学习,我们知道知道STM32的通用定时器和高级定时器拥有PWM输出功能,本章我们将利用TIM3的CH3通道输出波形,数据手册请参看第14章中的相关内容。
实验简介
脉宽调制器(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。简单一点,就是对脉冲宽度的控制。
STM32的定时器除了TIM6和TIM7,其他的定时器都可以用来产生PWM输出,其中高级定时器TIM1和TIM8可以同时产生多达7路的PWM输出。而通用定时器也能同时产生多达4路的PWM输出,这样,STM32最多可以同时产生30路PWM输出。这里我们仅利用TIM3的CH3产生一路PWM输出。
PWM原理
上图就是一个简单的 PWM 原理示意图。图中,我们假定定时器工作在向上计数 PWM 模式,且当 CNT<CCRx 时,输出 0,当 CNT>=CCRx 时输出 1。那么就可以得到如上的 PWM 示意图:当 CNT 值小于 CCRx 的时候,IO 输出低电平(0),当 CNT 值大于等于 CCRx 的时候, IO 输出高电平(1),当 CNT 达到 ARR 值的时候,重新归零,然后重新向上计数,依次循环。 改变 CCRx 的值,就可以改变 PWM 输出的占空比,改变 ARR 的值,就可以改变 PWM 输出的 频率,这就是 PWM 输出的原理。
电路设计
TIM3的CH3通道映射到PB0上,外接了红色LED灯D4,PWM输出波形可以控制LED的亮度,改变占空比,可以改变LED亮度,电路图如下图。
有关寄存器
捕获/比较模式寄存器 1(TIMx_CCMR1)
这里我们主要了解 110和111这两种相反的模式
** 捕获/比较使能寄存器(TIMx_CCER) **
因为TIM3的CH3通道映射到PB0上
捕获/比较寄存器 1(TIMx_CCR1)
小、中和大容量产品的寄存器映像和位定义
寄存器代码
test.c
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "timer.h"
int main(void)
{
u16 led0pwmval = 0;
u8 dir = 1;
Stm32_Clock_Init(9);
uart_init(72,115200);
delay_init(72);
LED_Init();
TIM3_PWM_Init(899,0);
//频率= 72000/(899+1)=80khz
while(1)
{
delay_ms(10);
if(dir)
led0pwmval++;
else
led0pwmval--;
if(led0pwmval > 300)
dir = 0;
if(led0pwmval == 0)
dir = 1;
LED3_PWM_VAL = led0pwmval;
//捕捉/比较 寄存器3 使用的TIM3_CH3
}
}
timer.h
#ifndef __TIMER_H
#define __TIMER_H
#include "sys.h"
#define LED3_PWM_VAL TIM3->CCR3
void TIM3_Int_Init(u16 arr,u16 psc);
void TIM3_PWM_Init(u16 arr,u16 psc);
#endif
timer.c
#include "timer.h"
#include "led.h"
void TIM3_IRQHandler(void)
{
if(TIM3->SR & 0x0001)
//溢出中断
{
LED2 = !LED2;
}
TIM3->SR &= ~(1<<0);
//清除中断标志位
}
void TIM3_Int_Init(u16 arr,u16 psc)
{
RCC->APB1ENR |= 1<<1;
//TIM3使能
TIM3->ARR = arr;
//设置计数器自动重装值
TIM3->PSC = psc;
//预分频器
TIM3->DIER |= 1<<0;
//允许中断更新
TIM3->CR1 |= 0x01;
//使能定时器3
MY_NVIC_Init(1,3,TIM3_IRQn,2);
//抢占1 子优先级3 组 2
}
void TIM3_PWM_Init(u16 arr,u16 psc)
//PWM初始(自动重装值,时钟预分频数)
{
RCC->APB1ENR |= 1<<1;
//TIM3时钟使能
RCC->APB2ENR |= 1<<3;
//使能PORTB时钟
GPIOB->CRL &= 0xFFFFFFF0;
GPIOB->CRL |= 0x0000000B;
//复用推挽输出
RCC->APB2ENR |= 1<<0;
//辅助时钟
//在配置AFIO,相关寄存器的时候,必须开启辅助
//功能
AFIO->MAPR &= 0xFFFFF3FF;
//清除MAPR的【11:10】;
AFIO->MAPR |= 1<<11;
//部分重映射 TIM3_CH3->PB0
TIM3->ARR = arr;
//设定计数器自动重装值
TIM3->PSC = psc;
//预分频器不分频
TIM3->CCMR2 |= 7<<4;
//CH3 PWM模式
TIM3->CCMR2 |= 1<<3;
//CH3 预装载使能
TIM3->CCER |= 1<<8;
//OC3输出使能
TIM3->CR1 = 0x0080;
//ARPE使能
TIM3->CR1 |= 0x01;
//使能定时器3
}
HAL库代码
main.c
#include "MyIncludes.h"
int main()
{
u8 dir = 0;
u16 duty = 1;
System_Init();
SysTick_Init(NULL);
PWM_Init(1000,duty);
while(1)
{
delay_ms(10);
if(dir == 0)
{
if(duty < 100)
duty++;
else
dir = 1;
}
else if(dir == 1)
{
if(duty > 1)
duty -- ;
else
dir = 0;
}
PWM_Init(10000,duty);
}
}
pwm.h
#ifndef __PWM_H_
#define __PWM_H_
#include "stm32f1xx.h"
#include "stm32_types.h"
#include "stm32f1xx_hal.h"
void PWM_Init(u32 Freq, u32 Duty_Cycle);
//PWM脉冲调制初始化(PWM频率,占空比)
#endif
pwm.c
#include "pwm.h"
TIM_HandleTypeDef PWMHandle;
//TIM句柄结构变量声明
TIM_OC_InitTypeDef sConfig;
//TIM输出比较配置结构变量声明
uint32_t uwPeriodValue = 0;
//定时器ARR自动重装寄存器值
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
//在HAL_TIM_PWM_Init中调用
{
GPIO_InitTypeDef GPIO_InitStruct;
__GPIOB_CLK_ENABLE();
__TIM3_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOB,&GPIO_InitStruct);
}
void PWM_Init(u32 Freq,u32 Duty_Cycle)
//PWM(脉冲调制)初始化(PWM频率,占空比)
{
uwPeriodValue = (uint32_t)((SystemCoreClock/2/Freq) - 1);
//uwPeriodValue = (72000000/2/1000)-1
// = 3599
PWMHandle.Instance = TIM3;
//TIM3_CH3产生PWM
PWMHandle.Init.Prescaler = 0;
//设置计数频率为 0
PWMHandle.Init.Period = uwPeriodValue;
//设置自动重装值
PWMHandle.Init.ClockDivision = 0;
//时钟不分频,来自RCC
PWMHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
//递增计数
PWMHandle.Init.RepetitionCounter = 0;
//指定重复计数器值 为 0
if(HAL_TIM_PWM_Init(&PWMHandle) != HAL_OK)
{
while(1);
}
//如果初始化未完成将一直循环
sConfig.OCMode = TIM_OCMODE_PWM1;
//PWM1模式
sConfig.OCPolarity = TIM_OCPOLARITY_LOW;
//输出极性低电平
sConfig.OCFastMode = TIM_OCFAST_DISABLE;
//输出比较快速使能禁止
sConfig.OCNPolarity = TIM_OCNPOLARITY_HIGH;
// 指定互补输出极性
sConfig.OCNIdleState = TIM_OCNIDLESTATE_RESET;
//指定空闲状态期间的TIM输出比较管脚状态。
sConfig.OCIdleState = TIM_OCIDLESTATE_SET;
//指定空闲状态期间的TIM输出比较管脚状态。
sConfig.Pulse = (Duty_Cycle *(uwPeriodValue - 1))/100;
//设置占空比
//占空比是指高电平的时间占整个周期的比例。
//pulse = Duty_Cycle*(3599-1)/100 = 34.98Duty_Cycle;
if(HAL_TIM_PWM_ConfigChannel(&PWMHandle,&sConfig,TIM_CHANNEL_3) != HAL_OK )
{
while(1);
}
if(HAL_TIM_PWM_Start(&PWMHandle,TIM_CHANNEL_3) != HAL_OK)
//PWM启动
{
while(1);
}
}