第六节、TIM定时中断
6.1 TIM简介
TIM(Timer)定时器
定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断(定时触发中断)。
16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时。
不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能。
根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型。
定时器的类型如下:
STM32F103ZET6定时器资源:TIM1-8均有。
主模式触发DAC:在使用DAC时,可以将定时器的更新事件映射到触发输出TRGO上,TRGO直接接到DAC的触发转换引脚上,定时器的更新就不需要通过中断来触发DAC转换。实现了硬件的自动化。
6.2 定时器结构图分析
首先来看基本定时器:
它们的功能如下:
预分频器:对72MHZ的计数时钟进行预分频,例如如果预分频器写1,则为2分频,输出频率=输入频率/2=36MHZ。(即实际分配系数=预分频器的值+1)
计数器:对预分频后的技术时钟进行计数。
自动重装寄存器:16位的寄存器,存入写入的计数目标。运行过程中,计数值不断自增,当计数值等于自动重装值,计时时间到,产生中断信号(更新中断,更新中断之后通往NVIC,配置好NVIC的定时器中断后,定时器的更新中断就能得到CPU的响应),并且清零计数器。
接下来来看通用定时器:
通用定时器与高级定时器支持三种计数模式,即向上计数、向下计数、中央对齐计数。
通用定时器的电路总共可以分为四个部分:
1、时基单元:与基本定时器的时基单元相同。
2、内外时钟源选择和主从触发模式:通用定时器不仅可以选择内部时钟作为时钟源,也可以选贼外部时钟作为时钟源。
3、输入捕获电路:可以用于测量方波频率等。
4、输出比较电路:总共有四个通道,分别对应CH1到CH4的引脚,适用于PWM产生波形。
下面再来看一下高级定时器:
相较于通用定时器,高级定时器多了个重复计数器,使计数时间大大增加。并且高级计数器还拥有输出比较模块,驱动三相无刷电机。以及刹车输入功能。
6.3 定时中断的基本结构
通过上图,定时器初始化可以分为以下几个步骤:
1、RCC开启时钟。
2、选择时基单元的时钟源。
3、配置时基单元。
4、配置输出中断控制。运行更新中断输出到NVIC
5、配置NVIC,在NVIC中打开定时器中断的通道,并分配一个优先级。
6、运行控制。使能计数器。
通过时序图,我们能研究时基单元运行时的细节问题。
1、预分频器时序
计数器计数频率:CK_CNT=CK_PSC/(PSC+1)
预分频器的工作流程:首先,CK_PSC(预分频器输入时钟)不断运行;CNT_EN(计数器使能)为高电平时计数器正常运行,低电平时计数器停止;CK_CNT(计数器时钟)运行时,根据分频系数,前半段预分频器参数为1,计数器时钟等于预分频器前的时钟,后半段预分频器系数变为2,计数器时钟变为预分频器前时钟的一半。在计数器时钟的驱动下,计数器寄存器也跟随时钟的上升沿不断自增,在FC后,计数值变为0,下面产生更新事件。
2、计数器时序
计数器溢出频率:CK_CNT_OV=CK_CNT/(ARR+1)=CK_PSC/(PSC+1)/(ARR+1)
3、RCC时钟树
6.4 代码实现定时器功能
首先来看一下定时器的库函数:
void TIM_DeInit(TIM_TypeDef* TIMx);//恢复缺省配置
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);//时基单元初始化
/*配置输出比较模块*/
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_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
/*初始化输入捕获单元*/
void TIM_PWMIConfig(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
void TIM_BDTRConfig(TIM_TypeDef* TIMx, TIM_BDTRInitTypeDef *TIM_BDTRInitStruct);
void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);//给结构体变量赋默认值
void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct);//给结构体变量赋参数值
void TIM_ICStructInit(TIM_ICInitTypeDef* TIM_ICInitStruct);//给输入捕获结构体赋一个初值
void TIM_BDTRStructInit(TIM_BDTRInitTypeDef* TIM_BDTRInitStruct);
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);//使能计数器
void TIM_CtrlPWMOutputs(TIM_TypeDef* TIMx, FunctionalState NewState);
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);//使能外设中断输出
void TIM_GenerateEvent(TIM_TypeDef* TIMx, uint16_t TIM_EventSource);
void TIM_DMAConfig(TIM_TypeDef* TIMx, uint16_t TIM_DMABase, uint16_t TIM_DMABurstLength);
void TIM_DMACmd(TIM_TypeDef* TIMx, uint16_t TIM_DMASource, FunctionalState NewState);
/*时基单元时钟选择*/
void TIM_InternalClockConfig(TIM_TypeDef* TIMx);//选择内部时钟
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,
uint16_t TIM_ICPolarity, uint16_t ICFilter);
void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,uint16_t ExtTRGFilter);
void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler,
uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
uint16_t ExtTRGFilter);
void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);//可用于单独写预分频值
void TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode);//改变计数器的计数模式
void TIM_SelectInputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);//选择输入触发源TRGI
void TIM_EncoderInterfaceConfig(TIM_TypeDef* TIMx, uint16_t TIM_EncoderMode,
uint16_t TIM_IC1Polarity, uint16_t TIM_IC2Polarity);//定时器编码器接口配置
/*配置强制输出模式*/
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_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);//自动重装器预装功能配置
void TIM_SelectCOM(TIM_TypeDef* TIMx, FunctionalState NewState);
void TIM_SelectCCDMA(TIM_TypeDef* TIMx, FunctionalState NewState);
void TIM_CCPreloadControl(TIM_TypeDef* TIMx, FunctionalState NewState);
/*配置OC预装功能*/
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);
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);
void TIM_UpdateDisableConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
void TIM_UpdateRequestConfig(TIM_TypeDef* TIMx, uint16_t TIM_UpdateSource);
void TIM_SelectHallSensor(TIM_TypeDef* TIMx, FunctionalState NewState);
void TIM_SelectOnePulseMode(TIM_TypeDef* TIMx, uint16_t TIM_OPMode);
void TIM_SelectOutputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_TRGOSource);//选择输出触发源TRGO
void TIM_SelectSlaveMode(TIM_TypeDef* TIMx, uint16_t TIM_SlaveMode);//选择从模式
void TIM_SelectMasterSlaveMode(TIM_TypeDef* TIMx, uint16_t TIM_MasterSlaveMode);
void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);//给计数器写入一个计数值
void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload);//给自动重装器写入一个值
/*单独更改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);
/*分别单独配置通道1,2,3,4的分频器*/
void TIM_SetIC1Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetIC2Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetIC3Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetIC4Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetClockDivision(TIM_TypeDef* TIMx, uint16_t TIM_CKD);
/*分别读取四个通道的CCR*/
uint16_t TIM_GetCapture1(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture2(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture3(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture4(TIM_TypeDef* TIMx);
uint16_t TIM_GetCounter(TIM_TypeDef* TIMx);//获取当前计数器的值
uint16_t TIM_GetPrescaler(TIM_TypeDef* TIMx);//获取当前的预分频器的值
FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);
内部时钟实现定时器定时:
Timer.c:
#include "stm32f10x.h" // Device header
void Timer_Init(void)
{
/*RCC开启时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
/*选择时基单元的时钟*/
TIM_InternalClockConfig(TIM2);
/*配置时基单元*/
TIM_TimeBaseInitTypeDef Tim_InitStructure;
Tim_InitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//选择时钟分频
Tim_InitStructure.TIM_CounterMode=TIM_CounterMode_Up;//选择计数模式-向上计数
Tim_InitStructure.TIM_Period=10000-1;//选择周期-ARR值
Tim_InitStructure.TIM_Prescaler=7200-1;//预分频器
Tim_InitStructure.TIM_RepetitionCounter=0;//重复计数器-高级定时器专用
TIM_TimeBaseInit(TIM2,&Tim_InitStructure);
/*配置输出中断控制,使能更新中断*/
TIM_ClearFlag(TIM2,TIM_FLAG_Update);
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
/*配置NVIC*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStructure);
/*启动定时器*/
TIM_Cmd(TIM2,ENABLE);
}
/*
中断函数
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2,TIM_IT_Update) ==SET )
{
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
}*/
Timer.h:
#ifndef __TIMER_H
#define __TIMER_H
void Timer_Init(void);
#endif
main函数:
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "Delay.h"
#include "Timer.h"
uint16_t Num;
int main(void)
{
OLED_Init();
Timer_Init();
OLED_ShowString(1,1,"Num:");
while(1)
{
OLED_ShowNum(1,5,Num,5);
}
}
/*中断函数*/
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2,TIM_IT_Update) ==SET )
{
Num++;
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
}
定时器外部时钟:
Timer.c:
#include "stm32f10x.h" // Device header
void Timer_Init(void)
{
/*RCC开启时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/*配置GPIO*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
/*选择时基单元的时钟*/
TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0111);//通过ETR引脚的外部时钟模式2配置
/*配置时基单元*/
TIM_TimeBaseInitTypeDef Tim_InitStructure;
Tim_InitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//选择时钟分频
Tim_InitStructure.TIM_CounterMode=TIM_CounterMode_Up;//选择计数模式-向上计数
Tim_InitStructure.TIM_Period=10-1;//选择周期-ARR值
Tim_InitStructure.TIM_Prescaler=1-1;//预分频器
Tim_InitStructure.TIM_RepetitionCounter=0;//重复计数器-高级定时器专用
TIM_TimeBaseInit(TIM2,&Tim_InitStructure);
/*配置输出中断控制,使能更新中断*/
TIM_ClearFlag(TIM2,TIM_FLAG_Update);
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
/*配置NVIC*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStructure);
/*启动定时器*/
TIM_Cmd(TIM2,ENABLE);
}
/*计数器值封装*/
uint16_t Timer_GetCount(void)
{
return TIM_GetCounter(TIM2);
}
/*
中断函数
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2,TIM_IT_Update) ==SET )
{
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
}*/
Timer.h:
#ifndef __TIMER_H
#define __TIMER_H
void Timer_Init(void);
uint16_t Timer_GetCount(void);
#endif
main函数:
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "Delay.h"
#include "Timer.h"
uint16_t Num;
int main(void)
{
OLED_Init();
Timer_Init();
OLED_ShowString(1,1,"Num:");
OLED_ShowString(2,1,"CNT:");
while(1)
{
OLED_ShowNum(1,5,Num,5);
OLED_ShowNum(2,5,Timer_GetCount(),5);
}
}
/*中断函数*/
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2,TIM_IT_Update) ==SET )
{
Num++;
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
}
6.5 TIM输出比较
6.5.1 输出比较简介
OC(Output Compare)输出比较
输出比较可以通过比较CNT(时基单元计数器)与CCR寄存器(捕获/比较寄存器)值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PWM波形
每个高级定时器和通用定时器都拥有4个输出比较通道
高级定时器的前3个通道额外拥有死区生成和互补输出的功能
6.5.2 PWM简介
PWM(Pulse Width Modulation)脉冲宽度调制
在具有惯性的系统中,可以通过对一系列脉冲的宽度进行调制,来等效地获得所需要的模拟参量,常应用于电机控速等领域。使用PWM波形等效实现模拟信号的输出。
PWM参数: 频率 = 1 / TS 占空比 = TON / TS 分辨率 = 占空比变化步距
通用定时器的输出比较电路:
输出比较模式:
PWM的基本结构:
通过上图,PWM进行初始化可分为以下几步:
1、RCC开启时钟。
2、配置时基单元。
3、配置输出比较单元。
4、配置GPIO。
5、运行控制,启动计数器。
PWM的参数计算:
上图中,蓝色:CNT,黄色:ARR,红色:CCR
PWM的频率=计数器的更新频率
PWM的占空比=30/100=30%
6.5.3 舵机简介
舵机是一种根据输入PWM信号占空比来控制输出角度的装置
输入PWM信号要求:周期为20ms,高电平宽度为0.5ms~2.5ms
6.5.4 直流电机及驱动介绍
直流电机是一种将电能转换为机械能的装置,有两个电极,当电极正接时,电机正转,当电极反接时,电机反转
直流电机属于大功率器件,GPIO口无法直接驱动,需要配合电机驱动电路来操作
TB6612是一款双路H桥型的直流电机驱动芯片,可以驱动两个直流电机并且控制其转速和方向
电机驱动电路的电路图如下:
通过控制IN1,IN2两个引脚的电平,能够控制电机是正转还是反转。
6.6 代码实现TIM功能
PWM驱动LED呼吸灯:
PWM.c:
#include "stm32f10x.h" // Device header
void PWM_Init(void)
{
/*RCC开启时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/*配置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);
/*选择时基单元的时钟*/
TIM_InternalClockConfig(TIM2);
/*配置时基单元*/
TIM_TimeBaseInitTypeDef Tim_InitStructure;
Tim_InitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//选择时钟分频
Tim_InitStructure.TIM_CounterMode=TIM_CounterMode_Up;//选择计数模式-向上计数
Tim_InitStructure.TIM_Period=100-1;//选择周期-ARR值
Tim_InitStructure.TIM_Prescaler=720-1;//预分频器-PSC值
Tim_InitStructure.TIM_RepetitionCounter=0;//重复计数器-高级定时器专用
TIM_TimeBaseInit(TIM2,&Tim_InitStructure);
/*配置输出比较单元*/
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_OC2Init(TIM2,&TIM_OCInitStructure);
/*启动定时器*/
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
main函数:
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "Delay.h"
#include "PWM.h"
uint8_t i;
int main(void)
{
OLED_Init();
PWM_Init();
while(1)
{
for(i=0;i<=100;i++)
{
PWM_SetCompare2(i);
Delay_ms(10);
}
for(i=0;i<=100;i++)
{
PWM_SetCompare2(100-i);
Delay_ms(10);
}
}
}
按键控制舵机:
Servo.c:
#include "stm32f10x.h" // Device header
#include "PWM.h"
void Servo_Init(void)
{
PWM_Init();
}
/*舵机的设定角度函数
0 500
180 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
main函数:
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "Delay.h"
#include "Servo.h"
#include "Key.h"
uint8_t KeyNum;
float Angle;
int main(void)
{
OLED_Init();
Servo_Init();
Key_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);
}
}
按键控制直流电机:
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();
}
void Motor_SetSpeed(int8_t Speed)
{
if(Speed>=0)
{
GPIO_SetBits(GPIOA,GPIO_Pin_4);
GPIO_ResetBits(GPIOA,GPIO_Pin_5);
PWM_SetCompare2(Speed);
}
else
{
GPIO_SetBits(GPIOA,GPIO_Pin_5);
GPIO_ResetBits(GPIOA,GPIO_Pin_4);
PWM_SetCompare2(-Speed);
}
}
motor.h:
#ifndef __MOTOR_H
#define __MOTOR_H
void Motor_Init(void);
void Motor_SetSpeed(int8_t Speed);
#endif
main函数:
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "Delay.h"
#include "MOTOR.h"
#include "Key.h"
uint8_t KeyNum;
int8_t Speed;
int main(void)
{
OLED_Init();
Motor_Init();
Key_Init();
OLED_ShowString(1,1,"Speed:");
while(1)
{
KeyNum=Key_GetNum();
if(KeyNum == 1)
{
Speed+=20;
if(Speed>100)
{
Speed=-100;
}
}
Motor_SetSpeed(Speed);
OLED_ShowSignedNum(1,7,Speed,3);
}
}
6.7 TIM输入捕获
IC(Input Capture)输入捕获
输入捕获模式下,当通道输入引脚出现指定电平跳变时,当前CNT的值将被锁存到CCR中(将当前CNT的值读出并写入到CCR中),可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数
每个高级定时器和通用定时器都拥有4个输入捕获通道
可配置为PWMI模式,同时测量频率和占空比
可配合主从触发模式,实现硬件全自动测量
频率测量
测频法:在闸门时间T内,对上升沿计次,得到N,则频率
f_x=N / T
测周法:两个上升沿内,以标准频率fc计次,得到N ,则频率
f_x=f_c / N
中界频率:测频法与测周法误差相等的频率点
f_m=√(f_c / T)
输入捕获的基本结构:
其中的CNT,就是测周法中用来计数计时的参数。
根据上图,配置输入捕获的步骤为:
1、RCC开启时钟,将GPIO和TIM的时钟都打开。
2、GPIO初始化,将GPIO配置为输入模式。
3、配置时基单元,让CNT计数器在内部时钟的驱动下自增运行。
4、配置输入捕获单元。
5、选择从模式的触发源。
6、选择触发后执行的操作。
7、调用TIM_Cmd函数,开启定时器。
输入捕获模式测频率代码:
PWM.c:
#include "stm32f10x.h" // Device header
void PWM_Init(void)
{
/*RCC开启时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/*配置GPIO*/
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);
/*选择时基单元的时钟*/
TIM_InternalClockConfig(TIM2);
/*配置时基单元*/
TIM_TimeBaseInitTypeDef Tim_InitStructure;
Tim_InitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//选择时钟分频
Tim_InitStructure.TIM_CounterMode=TIM_CounterMode_Up;//选择计数模式-向上计数
Tim_InitStructure.TIM_Period=100-1;//选择周期-ARR值
Tim_InitStructure.TIM_Prescaler=720-1;//预分频器-PSC值
Tim_InitStructure.TIM_RepetitionCounter=0;//重复计数器-高级定时器专用
TIM_TimeBaseInit(TIM2,&Tim_InitStructure);
/*配置输出比较单元*/
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);
/*启动定时器*/
TIM_Cmd(TIM2,ENABLE);
}
/*封装设定CCR值函数*/
void PWM_SetCompare1(uint16_t Compare)
{
TIM_SetCompare1(TIM2,Compare);
}
/*封装设定PSC值函数*/
void PWM_SetPrescaler(uint16_t Prescaler)
{
TIM_PrescalerConfig(TIM2,Prescaler,TIM_PSCReloadMode_Update);
}
IC.c:
#include "stm32f10x.h" // Device header
void IC_Init(void)
{
/*RCC开启时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
/*配置GPIO*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;//只有选择复用推挽输出,引脚的控制权才能转交给片上外设
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
/*选择时基单元的时钟*/
TIM_InternalClockConfig(TIM4);
/*配置时基单元*/
TIM_TimeBaseInitTypeDef Tim_InitStructure;
Tim_InitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//选择时钟分频
Tim_InitStructure.TIM_CounterMode=TIM_CounterMode_Up;//选择计数模式-向上计数
Tim_InitStructure.TIM_Period=65536-1;//选择周期-ARR值
Tim_InitStructure.TIM_Prescaler=72-1;//预分频器-PSC值
Tim_InitStructure.TIM_RepetitionCounter=0;//重复计数器-高级定时器专用
TIM_TimeBaseInit(TIM4,&Tim_InitStructure);
/*初始化输入捕获单元*/
TIM_ICInitTypeDef TIM_InitStructure;
TIM_InitStructure.TIM_Channel=TIM_Channel_1;//选择通道
TIM_InitStructure.TIM_ICFilter=1111;//配置输入捕获滤波器
TIM_InitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;//配置极性
TIM_InitStructure.TIM_ICPrescaler=TIM_ICPSC_DIV1;//配置分频器
TIM_InitStructure.TIM_ICSelection=TIM_ICSelection_DirectTI;//选择触发信号从哪个引脚输入
TIM_ICInit(TIM4,&TIM_InitStructure);
/*配置TRGI的触发源为TI1FP1*/
TIM_SelectInputTrigger(TIM4,TIM_TS_TI1FP1);
/*配置从模式为Reset*/
TIM_SelectSlaveMode(TIM4,TIM_SlaveMode_Reset);
/*调用TIM_Cmd函数*/
TIM_Cmd(TIM4,ENABLE);
}
/*读取频率函数*/
uint32_t IC_GetFreq(void)
{
return 1000000/(TIM_GetCapture1(TIM4)+1);
}
main函数:
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "Delay.h"
#include "PWM.h"
#include "IC.h"
int main(void)
{
OLED_Init();
PWM_Init();
IC_Init();
OLED_ShowString(1,1,"Freq:00000Hz");
PWM_SetPrescaler(720-1); //Freq = 72M / (PSC+1) / (ARR+1)
PWM_SetCompare1(50); //Duty = CCR /100
while(1)
{
OLED_ShowNum(1,6,IC_GetFreq(),5);
}
}
PWMI的基本结构:
与上图相比,多了一个分频器以及另一个捕获/比较器。具体的工作原理是:当出现上升沿电平时,CCR1开始计数,同时CNT清零。而加入了CCR2后,当电平由高转低时,CCR2捕获CNT的值,此时,CCR2的值就是高电平时CNT的值,而当到达下一个周期时,CCR1捕获CNT的值,那么CCR1的值就是一个周期内的值,那么CCR2/CCR1就是占空比。
根据上图,配置PWMI的步骤为:
1、RCC开启时钟,将GPIO和TIM的时钟都打开。
2、GPIO初始化,将GPIO配置为输入模式。
3、配置时基单元,让CNT计数器在内部时钟的驱动下自增运行。
4、配置输入捕获初始化为两个通道同时捕获同一引脚模式
PWMI模式测频率占空比:
IC.c:
#include "stm32f10x.h" // Device header
void IC_Init(void)
{
/*RCC开启时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
/*配置GPIO*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;//只有选择复用推挽输出,引脚的控制权才能转交给片上外设
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
/*选择时基单元的时钟*/
TIM_InternalClockConfig(TIM4);
/*配置时基单元*/
TIM_TimeBaseInitTypeDef Tim_InitStructure;
Tim_InitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//选择时钟分频
Tim_InitStructure.TIM_CounterMode=TIM_CounterMode_Up;//选择计数模式-向上计数
Tim_InitStructure.TIM_Period=65536-1;//选择周期-ARR值
Tim_InitStructure.TIM_Prescaler=72-1;//预分频器-PSC值
Tim_InitStructure.TIM_RepetitionCounter=0;//重复计数器-高级定时器专用
TIM_TimeBaseInit(TIM4,&Tim_InitStructure);
/*初始化输入捕获单元*/
TIM_ICInitTypeDef TIM_InitStructure;
TIM_InitStructure.TIM_Channel=TIM_Channel_1;//选择通道
TIM_InitStructure.TIM_ICFilter=1111;//配置输入捕获滤波器
TIM_InitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;//配置极性
TIM_InitStructure.TIM_ICPrescaler=TIM_ICPSC_DIV1;//配置分频器
TIM_InitStructure.TIM_ICSelection=TIM_ICSelection_DirectTI;//选择触发信号从哪个引脚输入
TIM_PWMIConfig(TIM4,&TIM_InitStructure);//自动将剩下的通道初始化为相反的配置
/*配置TRGI的触发源为TI1FP1*/
TIM_SelectInputTrigger(TIM4,TIM_TS_TI1FP1);
/*配置从模式为Reset*/
TIM_SelectSlaveMode(TIM4,TIM_SlaveMode_Reset);
/*调用TIM_Cmd函数*/
TIM_Cmd(TIM4,ENABLE);
}
/*读取频率函数*/
uint32_t IC_GetFreq(void)
{
return 1000000/(TIM_GetCapture1(TIM4)+1);
}
/*获取占空比函数*/
uint32_t IC_GetDuty(void)
{
return (TIM_GetCapture2(TIM4)+1) * 100 / (TIM_GetCapture1(TIM4)+1);
}
PWM.c:
#include "stm32f10x.h" // Device header
void PWM_Init(void)
{
/*RCC开启时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/*配置GPIO*/
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);
/*选择时基单元的时钟*/
TIM_InternalClockConfig(TIM2);
/*配置时基单元*/
TIM_TimeBaseInitTypeDef Tim_InitStructure;
Tim_InitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//选择时钟分频
Tim_InitStructure.TIM_CounterMode=TIM_CounterMode_Up;//选择计数模式-向上计数
Tim_InitStructure.TIM_Period=100-1;//选择周期-ARR值
Tim_InitStructure.TIM_Prescaler=720-1;//预分频器-PSC值
Tim_InitStructure.TIM_RepetitionCounter=0;//重复计数器-高级定时器专用
TIM_TimeBaseInit(TIM2,&Tim_InitStructure);
/*配置输出比较单元*/
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);
/*启动定时器*/
TIM_Cmd(TIM2,ENABLE);
}
/*封装设定CCR值函数*/
void PWM_SetCompare1(uint16_t Compare)
{
TIM_SetCompare1(TIM2,Compare);
}
/*封装设定PSC值函数*/
void PWM_SetPrescaler(uint16_t Prescaler)
{
TIM_PrescalerConfig(TIM2,Prescaler,TIM_PSCReloadMode_Update);
}
main函数:
#include "stm32f10x.h" // Device header
void PWM_Init(void)
{
/*RCC开启时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/*配置GPIO*/
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);
/*选择时基单元的时钟*/
TIM_InternalClockConfig(TIM2);
/*配置时基单元*/
TIM_TimeBaseInitTypeDef Tim_InitStructure;
Tim_InitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//选择时钟分频
Tim_InitStructure.TIM_CounterMode=TIM_CounterMode_Up;//选择计数模式-向上计数
Tim_InitStructure.TIM_Period=100-1;//选择周期-ARR值
Tim_InitStructure.TIM_Prescaler=720-1;//预分频器-PSC值
Tim_InitStructure.TIM_RepetitionCounter=0;//重复计数器-高级定时器专用
TIM_TimeBaseInit(TIM2,&Tim_InitStructure);
/*配置输出比较单元*/
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);
/*启动定时器*/
TIM_Cmd(TIM2,ENABLE);
}
/*封装设定CCR值函数*/
void PWM_SetCompare1(uint16_t Compare)
{
TIM_SetCompare1(TIM2,Compare);
}
/*封装设定PSC值函数*/
void PWM_SetPrescaler(uint16_t Prescaler)
{
TIM_PrescalerConfig(TIM2,Prescaler,TIM_PSCReloadMode_Update);
}
6.8 TIM编码器接口
Encoder Interface 编码器接口
编码器接口可接收增量(正交)编码器的信号,根据编码器旋转产生的正交信号脉冲,自动控制CNT自增或自减,从而指示编码器的位置、旋转方向和旋转速度
每个高级定时器和通用定时器都拥有1个编码器接口
两个输入引脚借用了输入捕获的通道1和通道2
编码器接口的基本结构:
由上图,编码器接口初始化可分为以下几步:
1、RCC开启时钟。
2、配置GPIO。
3.配置时基单元。
4、配置输入捕获单元。
5、配置编码器接口模式。
6.调用TIM_Cmd函数启动定时器。
编码器接口的工作模式: