1 输出比较简介
OC(Output Compare)输出比较
输出比较可以通过比较CNT与CCR寄存器值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PWM波形
每个高级定时器和通用定时器都拥有4个输出比较通道
高级定时器的前3个通道额外拥有死区生成和互补输出的功能
2 PWM简介
PWM(Pulse Width Modulation)脉冲宽度调制
在具有惯性的系统中,可以通过对一系列脉冲的宽度进行调制,来等效地获得所需要的模拟参量,常应用于电机控速等领域
PWM参数:
频率 = 1 / TS 占空比 = TON / TS 分辨率 = 占空比变化步距
3 输出比较模式(输出模式控制器)
4 输出比较通道(通用)
左边为CNT计数器和CCR1第一路的捕获/比较寄存器进行比较,当CNT>CCR1或CNT=CCR1时,给输出模式控制器传一个信号后会改变输出oc1ref的高低电平(ref就是指信号的高低电平reference参考信号),上面的ETRF输入为定时器的小功能。ref信号可以前往主模式控制器映射到主模式的TRGO输出。下面一路进行极性选择,寄存器写0信号往上走即电平不翻转,写1信号往下走进行非门取反,然后输出使能电路选择是否输出,最后到OC1引脚即CH1通道引脚。
输出模式控制器:输入CNT和CCR的大小关系,输出ref高低电平
5 输出比较通道(高级)
左边和上方部分与通用定时器电路一样。右边OC1和OC1N外面通常接推挽电路(上管导通下管断开输出高电平,下管导通上管断开输出低电平,上下管都导通短路不允许,上下管都断开高阻态),OC1和OC1N为两个互补的输出端口控制上下管导通和关闭,在控制上下管状态时如果上管关闭瞬间下管立刻打开(由于硬件误差导致时间差),为了避免误差就有死区生成电路(上/下管关闭时延迟一段时间再导通下/上管,避免上下管同时导通的现象)
6 PWM基本结构
左边为时基单元和运行控制部分,再左边为时钟源选择(省略),配置好时基单元,CNT就可以自增运行。下面为输出比较单元(共4路),首先为CCR捕获/比较器,不断进行比较,后面的为输出模式控制器(右上角坐标图蓝色为CNT,黄色为ARR,红色为CCR,绿色为输出),有效电平为高电平,无效电平为低电平(输出占空比由CCR控制,CCR值高占空比大,CCR值低占空比小)。REF为频率可调,占空比也可调的PWM波形,最终经过极性选择,输出使能,最后通过GPIO口。
7 参数计算
8 PWM模块
PWM输出比较对应库函数
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);
//四个为配置输出比较模块,OC为Output Compare 输出比较,参数为(TIMx选择定时器,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);
//配置强制输出模式
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);
//配置CCR预装功能,影子寄存器
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);
//配置快速使能
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);
//外部事件清除REF信号
void TIM_OC1PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
void TIM_OC1NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);
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);
//单独设置输出比较极性,NPolarityConfig高级定时器里互补通道的配置
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);//选择输出比较模式
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);
//单独更改CCR寄存器值的函数
void TIM_CtrlPWMOutputs(TIM_TypeDef* TIMx, FunctionalState NewState);
//使用高级定时器输出PWM需要调用函数使能主输出
PWM初始化
(1)RCC开启时钟,打开要用的TIM外设和GPIO外设
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
(2)配置时基单元,包括时钟源选择
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period = 100 - 1; //ARR
TIM_TimeBaseInitStruct.TIM_Prescaler = 720 - 1; //PSC
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
(3)配置输出比较单元(CCR值、输出比较模式、极性选择、输出使能)
TIM_OCInitTypeDef TIM_OCInitStruct;
TIM_OCStructInit(&TIM_OCInitStruct);//用此函数先全部赋初始值再改要用的
TIM_OCInitStruct.TIM_OCMode = ;
TIM_OCMode_Timing 冻结模式
TIM_OCMode_Active相等置有效电平
TIM_OCMode_Inactive相等置无效电平
TIM_OCMode_Toggle相等电平翻转
TIM_OCMode_PWM1 模式1,TIM_OCMode_PWM2模式2
TIM_ForcedAction_Active,TIM_ForcedAction_InActive两种强制输出
TIM_OCInitStruct.TIM_OCPolarity = ;
TIM_OCPolarity_High 高极性,REF有效时输出高电平
TIM_OCPolarity_Low 低极性,有效电平为低电平
TIM_OCInitStruct.TIM_OutputState = ;
TIM_OutputState_Disable 失能,TIM_OutputState_Enable 使能
TIM_OCInitStruct.TIM_Pulse = 50;//设置CCR寄存器值
TIM_OC1Init(TIM2, &TIM_OCInitStruct);
/*参数计算(频率1KHz,占空比50%)
PWM频率: Freq = CK_PSC / (PSC + 1) / (ARR + 1) =72M/720/100=1000
PWM占空比: Duty = CCR / (ARR + 1) =50/100=50%
PWM分辨率: Reso = 1 / (ARR + 1) =1/100=1%
*/
(4)配置GPIO,初始化为复用推挽输出
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//若想使用AFIO引脚重映射
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
解除调试端口复用
GPIO_Remap_SWJ_NoJTRST 解除JTRST调试端口(PB4引脚)
GPIO_Remap_SWJ_JTAGDisable 解除JTAG调试端口(PA15、PB3、PB4)
GPIO_Remap_SWJ_Disable 解除全部SWD和JTAG端口(PA13-15、PB3-4)
(5)运行控制,启动计数器
TIM_Cmd(TIM2, ENABLE);
1 舵机简介
舵机是一种根据输入PWM信号占空比来控制输出角度的装置
输入PWM信号要求:周期为20ms,高电平宽度为0.5ms~2.5ms
2 硬件电路
黑色电源负极GND,红色电源正极+5V,黄色PWM信号线
1 直流电机及驱动简介
直流电机是一种将电能转换为机械能的装置,有两个电极,当电极正接时,电机正转,当电极反接时,电机反转
直流电机属于大功率器件,GPIO口无法直接驱动,需要配合电机驱动电路来操作
TB6612是一款双路H桥型的直流电机驱动芯片,可以驱动两个直流电机并且控制其转速和方向
2 硬件电路
左下角VM为电机电源正极(接大电流电源,和舵机电源保持一致)。VCC逻辑电平输入端(和控制器电源保持一致,如STM32为3.3V)。AO1、AO2、BO1、BO2为两路电机输出,分别接两个电机。PWMA、AIN2、AIN1(控制AO1、AO2)直接接到GPIO口,PWMA要接PWM信号输出端。STBY待机控制引脚,接GND芯片处于待机状态不工作,接逻辑电源VCC芯片正常工作(需要待机模式可接到GPIO口给高低电平控制)。
程序源码
PWM.c
#include "stm32f10x.h" // Device header
/**
* 函 数:PWM初始化
* 参 数:无
* 返 回 值:无
*/
void PWM_Init(void)
{
/*开启时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启TIM2的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
/*GPIO初始化*/
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); //将PA1引脚初始化为复用推挽输出
//受外设控制的引脚,均需要配置为复用模式
/*配置时钟源*/
TIM_InternalClockConfig(TIM2); //选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟
/*时基单元初始化*/
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); //将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元
/*输出比较初始化*/
TIM_OCInitTypeDef TIM_OCInitStructure; //定义结构体变量
TIM_OCStructInit(&TIM_OCInitStructure); //结构体初始化,若结构体没有完整赋值
//则最好执行此函数,给结构体所有成员都赋一个默认值
//避免结构体初值不确定的问题
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //输出比较模式,选择PWM模式1
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性,选择为高,若选择极性为低,则输出高低电平取反
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //输出使能
TIM_OCInitStructure.TIM_Pulse = 0; //初始的CCR值
TIM_OC2Init(TIM2, &TIM_OCInitStructure); //将结构体变量交给TIM_OC2Init,配置TIM2的输出比较通道2
/*TIM使能*/
TIM_Cmd(TIM2, ENABLE); //使能TIM2,定时器开始运行
}
/**
* 函 数:PWM设置CCR
* 参 数:Compare 要写入的CCR的值,范围:0~100
* 返 回 值:无
* 注意事项:CCR和ARR共同决定占空比,此函数仅设置CCR的值,并不直接是占空比
* 占空比Duty = CCR / (ARR + 1)
*/
void PWM_SetCompare2(uint16_t Compare)
{
TIM_SetCompare2(TIM2, Compare); //设置CCR2的值
}
Servo.c
#include "stm32f10x.h" // Device header
#include "PWM.h"
/**
* 函 数:舵机初始化
* 参 数:无
* 返 回 值:无
*/
void Servo_Init(void)
{
PWM_Init(); //初始化舵机的底层PWM
}
/**
* 函 数:舵机设置角度
* 参 数:Angle 要设置的舵机角度,范围:0~180
* 返 回 值:无
*/
void Servo_SetAngle(float Angle)
{
PWM_SetCompare2(Angle / 180 * 2000 + 500); //设置占空比
//将角度线性变换,对应到舵机要求的占空比范围上
}
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Servo.h"
#include "Key.h"
uint8_t KeyNum; //定义用于接收键码的变量
float Angle; //定义角度变量
int main(void)
{
/*模块初始化*/
OLED_Init(); //OLED初始化
Servo_Init(); //舵机初始化
Key_Init(); //按键初始化
/*显示静态字符串*/
OLED_ShowString(1, 1, "Angle:"); //1行1列显示字符串Angle:
while (1)
{
KeyNum = Key_GetNum(); //获取按键键码
if (KeyNum == 1) //按键1按下
{
Angle += 30; //角度变量自增30
if (Angle > 180) //角度变量超过180后
{
Angle = 0; //角度变量归零
}
}
Servo_SetAngle(Angle); //设置舵机的角度为角度变量
OLED_ShowNum(1, 7, Angle, 3); //OLED显示角度变量
}
}
标签:TypeDef,05,void,STM32,TIM,uint16,GPIO,TIMx
From: https://blog.csdn.net/2302_81517810/article/details/141299581