目录
一、输出比较简介
二、PWM简介
三、输出比较通道(通用)
四、输出比较通道(高级)
五、输出比较模式
六、PWM基本结构
七、PWM参数计算
八、外设介绍
8.1 舵机
8.2 直流电机及驱动
九、开发步骤
十、输出比较库函数
十一、实验
9.1 PWM驱动LED呼吸灯
9.2 PWM驱动舵机
9.3 PWM驱动直流电机
>OC(Output Compare)输出比较
>输出比较可以通过比较CNT与CCR(捕获/比较寄存器)值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PWM波形
>每个高级定时器和通用定时器都拥有4个输出比较通道
>高级定时器的前3个通道额外拥有死区生成和互补输出的功能
>PWM(Pulse Width Modulation)脉冲宽度调制
>在具有惯性的系统中,可以通过对一系列脉冲的宽度进行调制,来等效地获得所需要的模拟参量,常应用于电机控速等领域
>PWM参数:
频率 = 1 / Ts 占空比 = Ton / Ts 分辨率 = 占空比变化步距
①RCC打开时钟,TIM和GPIO外设的时钟打开
②配置时基单元,包括前面的时钟源选择
③结构体配置输出比较单元(CCR,输出比较模式,极性选择,输出使能)
④配置GPIO,PWM对应GPIO初始化为复用推挽输出
⑤运行控制,启动计数器
注:标@的为重要,需掌握
@===================输出比较4模块配置函数==============================
void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC2Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC3Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC4Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);参数:①定时器;②结构体
=====================================================================
void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct);
//输出比较结构体赋初值
=====================配置强制输出模式(了解即可)========================
void TIM_ForcedOC1Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC2Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC3Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC4Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);=============配置CCR寄存器的预装功能(影子寄存器)(了解即可)=============
void TIM_OC1PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_OC2PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_OC3PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
void TIM_OC4PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);=======================配置快速使能(了解即可)==========================
void TIM_OC1FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
void TIM_OC2FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
void TIM_OC3FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
void TIM_OC4FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);=======================外部事件清除REF信号(了解即可)===================
void TIM_ClearOC1Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
void TIM_ClearOC2Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
void TIM_ClearOC3Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
void TIM_ClearOC4Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);=======================单独设置输出比较极性=============================
void TIM_OC1PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_OC1NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);//N高级定时器互补通道的配置
void TIM_OC2PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_OC2NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);
void TIM_OC3PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_OC3NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);
void TIM_OC4PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);=======================单独修改输出使能参数=============================
void TIM_CCxCmd(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_CCx);
void TIM_CCxNCmd(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_CCxN);=======================单独更改输出比较模式=============================
void TIM_SelectOCxM(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_OCMode);
@=====================单独更改CCR寄存器值=============================
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);
void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);
void TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3);
void TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4);==========================补充========================================
void TIM_CtrlPWMOutputs(TIM_TypeDef* TIMx, FunctionalState NewState);
//仅高级定时器使用,在使用高级定时器输出PWM时,需要调用这个函数,使能主输出,否则PWM将不能正常输出
PWM.c
#include "stm32f10x.h" // Device header /*PWM初始化*/ void PWM_Init(void) { /*一、RCC开启时钟,TIM与GPIO时钟打开*/ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); // /*使用重映射(*_*)*/ // RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); // GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE);//PA0->PA15,部分重映射S // GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//解除JATG调试 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;//(*_*)GPIO_Pin_15 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA,&GPIO_InitStructure); /*二、配置时基单元(时钟源选择和时基单元配置)*/ TIM_InternalClockConfig(TIM2);//选择内部时钟 TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; //ARR自动重装器值 TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1; //PSC预分频器值 TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重复计数器值 TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); /*三、配置输出比较单元(CCR的值(捕获/比较器),输出比较模式,极性选择,输出使能)*/ TIM_OCInitTypeDef TIM_OCInitStructure; TIM_OCStructInit(&TIM_OCInitStructure);//给结构体所有成员赋初始值(有些用不到但是必须赋值) TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//输出比较模式 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//输出比较的极性 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//输出使能 TIM_OCInitStructure.TIM_Pulse = 0;//设置CCR TIM_OC1Init(TIM2,&TIM_OCInitStructure); /*四、配置GPIO,复用推挽输出*/ //一处已操作 /*五、运行控制,启动计数器CNT*/ TIM_Cmd(TIM2,ENABLE); } /*封装函数->更改CCR值来改变占空比*/ void PWM_SetCompare1(uint16_t Compare) { TIM_SetCompare1(TIM2,Compare); }
PWM.h
#ifndef __PWM_H #define __PWM_H void PWM_Init(void); void PWM_SetCompare1(uint16_t Compare); #endif
main.c
#include "stm32f10x.h" // Device header #include "Delay.h" #include "OLED.h" #include "PWM.h" uint8_t i; int main(void) { OLED_Init(); PWM_Init(); while (1) { //CCR增大,逐渐变亮 for (i = 0; i <= 100; i++) { PWM_SetCompare1(i); Delay_ms(10); } //CCR减小,逐渐变暗 for (i = 0; i <= 100; i++) { PWM_SetCompare1(100 - i); Delay_ms(10); } } }
实验现象:按下按键,舵机转动固定角度
PWM.c
#include "stm32f10x.h" // Device header /*PWM初始化*/ void PWM_Init(void) { /*一、RCC开启时钟,TIM与GPIO时钟打开*/ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA,&GPIO_InitStructure); /*二、配置时基单元(时钟源选择和时基单元配置)*/ TIM_InternalClockConfig(TIM2);//选择内部时钟 TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInitStructure.TIM_Period = 20000 - 1; //ARR自动重装器值 TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; //PSC预分频器值 TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重复计数器值 TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); /*三、配置输出比较单元(CCR的值(捕获/比较器),输出比较模式,极性选择,输出使能)*/ TIM_OCInitTypeDef TIM_OCInitStructure; TIM_OCStructInit(&TIM_OCInitStructure);//给结构体所有成员赋初始值(有些用不到但是必须赋值) TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//输出比较模式 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//输出比较的极性 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//输出使能 TIM_OCInitStructure.TIM_Pulse = 0;//设置CCR(500~2500) TIM_OC2Init(TIM2,&TIM_OCInitStructure); /*四、配置GPIO,复用推挽输出*/ //一处已操作 /*五、运行控制,启动计数器CNT*/ TIM_Cmd(TIM2,ENABLE); } /*封装函数->更改CCR值来改变占空比*/ void PWM_SetCompare2(uint16_t Compare) { TIM_SetCompare2(TIM2,Compare); }
PWM.h
#ifndef __PWM_H #define __PWM_H void PWM_Init(void); void PWM_SetCompare2(uint16_t Compare); #endif
Servo.c
#include "stm32f10x.h" // Device header #include "PWM.h" //舵机初始化(PWM初始化) void Servo_Init(void) { PWM_Init(); } /* 舵机设置角度 角度:0-180 CCR:500-2500 */ void Servo_SetAngle(float Angle) { PWM_SetCompare2(Angle / 180 * 2000 + 500); }
Servo.h
#ifndef __SERVO_H #define __SERVO_H void Servo_Init(void); void Servo_SetAngle(float Angle); #endif
Key.c
#include "stm32f10x.h" // Device header #include "Delay.h" //===按键初始化===// void Key_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB,&GPIO_InitStructure); } //===获取按键返回码===// uint8_t Key_GetNum(void) { uint8_t KeyNum = 0; if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) == 0) { Delay_ms(20);//消抖 while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) == 0);//如果不松手则卡着 Delay_ms(20); KeyNum = 1; } if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11) == 0) { Delay_ms(20);//消抖 while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11) == 0); Delay_ms(20); KeyNum = 2; } return KeyNum; }
key.h
#ifndef __KEY_H #define __KEY_H void Key_Init(void); uint8_t Key_GetNum(void); #endif
main.c
#include "stm32f10x.h" // Device header #include "Delay.h" #include "OLED.h" #include "key.h" #include "Servo.h" uint8_t KeyNum; float Angle; int main(void) { OLED_Init(); Key_Init(); Servo_Init(); OLED_ShowString(1,1,"Angle:"); while (1) { KeyNum = Key_GetNum(); if(KeyNum == 1) { Angle += 30; if(Angle >180) { Angle = 0; } } Servo_SetAngle(Angle); OLED_ShowNum(1,7,Angle,3); } }
*OLED显示屏代码请参考本专栏文章STM32-OLED显示屏
实验现象: 按键按下,电机加速,达到最大转速时,再按一下,最大速度反转,按下按键,电机减速
PWM.c
#include "stm32f10x.h" // Device header /*PWM初始化*/ void PWM_Init(void) { /*一、RCC开启时钟,TIM与GPIO时钟打开*/ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA,&GPIO_InitStructure); /*二、配置时基单元(时钟源选择和时基单元配置)*/ TIM_InternalClockConfig(TIM2);//选择内部时钟 TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; //ARR自动重装器值 TIM_TimeBaseInitStructure.TIM_Prescaler = 36 - 1; //PSC预分频器值 TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重复计数器值 TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); /*三、配置输出比较单元(CCR的值(捕获/比较器),输出比较模式,极性选择,输出使能)*/ TIM_OCInitTypeDef TIM_OCInitStructure; TIM_OCStructInit(&TIM_OCInitStructure);//给结构体所有成员赋初始值(有些用不到但是必须赋值) TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//输出比较模式 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//输出比较的极性 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//输出使能 TIM_OCInitStructure.TIM_Pulse = 0;//设置CCR TIM_OC3Init(TIM2,&TIM_OCInitStructure); /*四、配置GPIO,复用推挽输出*/ //一处已操作 /*五、运行控制,启动计数器CNT*/ TIM_Cmd(TIM2,ENABLE); } /*封装函数->更改CCR值来改变占空比*/ void PWM_SetCompare3(uint16_t Compare) { TIM_SetCompare3(TIM2,Compare); }
PWM.h
#ifndef __PWM_H #define __PWM_H void PWM_Init(void); void PWM_SetCompare3(uint16_t Compare); #endif
Motor.c
#include "stm32f10x.h" // Device header #include "PWM.h" void Motor_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA,&GPIO_InitStructure); PWM_Init(); } /*设置电机速度(-100~100)*/ void Motor_SetSpeed(int8_t Speed) { if(Speed >= 0) { GPIO_SetBits(GPIOA,GPIO_Pin_4); GPIO_ResetBits(GPIOA,GPIO_Pin_5); PWM_SetCompare3(Speed); } else { GPIO_SetBits(GPIOA,GPIO_Pin_5); GPIO_ResetBits(GPIOA,GPIO_Pin_4); PWM_SetCompare3(-Speed); } }
Motor.h
#ifndef __PWM_H #define __PWM_H void Motor_Init(void); void Motor_SetSpeed(int8_t Speed); #endif
*按键与OLED显示屏的代码参考之前实验代码