1 输出比较
定时器通过对预设的比较值(CCR寄存器,输入捕获/输出比较寄存器)与定时器计数器CNT的值做匹配比较之后,并依据相应的输出模式从而实现各类输出。如PWM输出、电平翻转、单脉冲模式、强制输出等。高级定时器和通用定时器都具有4个输出比较通道, 且高级定时器的前3个通道额外拥有死区生成和互补输出的功能。
PWM:脉冲宽度调制。对于惯性系统,可以通过对脉冲的宽度进行调制,从而实现等效的模拟参量,可以理解为一般只能输出3.3V(高电平)或0V(低电平),而通过PWM则可以获得等效的0-3.3V之间的电压,类比交流电中的有效电压。常用于OLED屏幕亮度调节、电机控速等。
- 频率:$1/T_s$
- 占空比:$T_{on}/T_s$
- 分辨率:占空比变化步长,如占空比最小的变化的细密度.
参数计算
- 频率: f = CK_PSC/(PSC+1)/(ARR+1)
- 占空比:CCR/(ARR+1)
- 分辨率:1/(ARR+1)
2 初始化
- 使能时钟
- 选择时钟源
- 配置时基单元
- 配置输出比较单元
- 配置对应的GPIO口为复用推挽输出
- 运行控制
3 PWM库函数实现
- 使能时钟
- 选择时钟源
- 配置时基单元
- 配置输出比较单元
- 函数1-5
- 函数原型
- 函数1
void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
- 函数2
void TIM_OC2Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
- 函数3
void TIM_OC3Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
- 函数4
void TIM_OC4Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
- 函数1
- 输入:
TIMx
选择定时器编号,TIM_OCInitStruct
指向定时器输出比较初始化结构体的指针,结构体定义如下:typedef struct { uint16_t TIM_OCMode; // 设置比较模式 uint16_t TIM_OutputState; // 设置输出使能 uint16_t TIM_OutputNState; // 与上边一样,高级定时器 uint16_t TIM_Pulse; // 配置CCR uint16_t TIM_OCPolarity; // 配置极性 uint16_t TIM_OCNPolarity; // 与上边一样,高级定时器 uint16_t TIM_OCIdleState; uint16_t TIM_OCNIdleState; // 与上边一样,高级定时器 } TIM_OCInitTypeDef;
- 功能:配置4个通道, 选择哪个通道就用哪个函数即可,而通道选择和GPIO口有关。
- 函数原型
- 函数6
- 函数原型:
void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct);
- 输入:
TIM_OCInitStruct
指向定时器输出比较初始化结构体的指针 - 功能:为输出比较结构体赋默认值
- 函数原型:
- 函数7-10
- 函数原型
- 函数7:
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);
- 函数7:
void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);
- 函数7:
void TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3);
- 函数7:
void TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4);
- 函数7:
- 输入:
TIMx
选择定时器编号,Comparex
为CCR寄存器的值 - 功能:修改CCR寄存器值,即配置PWM占空比
- 函数原型
- 配置GPIO为复用推挽输出
运行控制(启动定时器)
代码实现
用TIM1的通道1输出频率为1kHz、占空比为50%、分辨率为1%的PWM信号.
计算:分辨率 = 1/(ARR + 1)得ARR = 100-1.
占空比 = CCR/(ARR+1)得CCR = 50 - 1.
频率 = PCK / (ARR+1) / (PSC + 1)得 PSC = 720-1.
void PWM_Init(void){
// 开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
// 选择时钟源
TIM_InternalClockConfig(TIM2);
// 配置时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructrue;
TIM_TimeBaseInitStructrue.TIM_Prescaler = 720 - 1// PSC
TIM_TimeBaseInitStructrue.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructrue.TIM_Period = 100 - 1;// ARR
TIM_TimeBaseInitStructrue.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructrue.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructrue);
// 配置输出比较单元
TIM_OCInitTypeDef TIM_OCInitStructrue;
TIM_OCStructInit(&TIM_OCInitStructrue); // 先给默认值
TIM_OCInitStructrue.TIM_OCMode = TIM_OCMode_PWM1; // 输出比较模式
TIM_OCInitStructrue.TIM_OutputState = TIM_OutputState_Enable; // 使能
TIM_OCInitStructrue.TIM_Pulse = 50; // CCR, 分辨率为0.01
TIM_OCInitStructrue.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM2, &TIM_OCInitStructrue);
// 开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 配置GPIO口
GPIO_InitTypeDef GPIO_InitStructrue;
GPIO_InitStructrue.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructrue.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructrue.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructrue);
// 开启定时器
TIM_Cmd(TIM2, ENABLE);
}
4 引脚重映射
STM32中GPIO口引脚的复用功能都是实现规定好的,如果需要使用到同一个引脚上的不同复用功能时会产生冲突,因而需要定义引脚重映射,将引脚的复用功能重映射到其他的引脚上。引脚重映射同样也是实现设计好的,用户无法自行更改。
相关函数:
void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);
- 输入:
GPIO_Remap
选择要进行重映射的引脚,NewState
为使能或失能 - 备注:
GPIO_Remap
选择引脚的同时也进行了模式选择,即部分重映射和完全重映射两种。如定时器TIM2对应的有:GPIO_PartialRemap1_TIM2
部分重映射1、GPIO_PartialRemap2_TIM2
部分重映射2、GPIO_FullRemap_TIM2
完全重映射三种。此外,部分端口被定义为调试端口,如SW和JTAG两种,如果需要用到这些端口,首先需要通过上述函数失能调试功能使该端口变为普通GPIO口。
- 输入:
代码实现
RCC_APB2PeriphClockCmd(RCC_APB2PeriphAFIO, ENABLE); // 使能AFIO时钟,重映射是通过AFIO寄存器来实现 GPIO_PinRemapConfig(GPIO_ParitialRemap1_TIM2, ENABLE); // 对TIM2进行部分重映射1 GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); // 取消端口调试功能
注意:慎用取消调试功能,因为取消后无法再通过调试端口烧写程序,只能通过串口下载。