目录
一、输出比较
1.什么是输出比较
•OC(Output Compare)输出比较。输出比较只在通用定时器和高级定时器中存在。以通用定时器的框图来介绍输出比较。
看图中时基单元下方一大堆东西,也就是CNT计数器下方,是捕获/比较寄存器,这个寄存器是输出比较和输入捕获共用的,两种功能在同一时刻只能选择一种执行。先将其用作输出比较来看,输出比较最重要的一个功能就是产生PWM波。这里先有个概念。
输出比较可以通过比较CNT与CCR寄存器值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PWM波。每个高级定时器和通用定时器都拥有4个输出比较通道,高级定时器的前3个通道额外拥有死区生成和互补输出的功能。互补输出好理解,就是取反,死区生成是用来解决冒险现象的,多个信号同时发生变化时,可能因为发生变化的先后顺序导致输出信号有一段不稳定的“毛刺”,死区生成就是在这种情况下,多个输入信号发生变化的时候先“中断”一会输出与输入之间的关系,等到输入信号稳定下来,再重新“建立”输出与输入的关系。
2.PWM波的基本参数
PWM:Pulse Width Modulation,脉冲宽度调制。在51中已经学习过,不过多赘述。复习一下其基本参数:
频率 = 1/Ts 占空比 = Ton/Ts 分辨率:占空比变化步距
Ts:PWM波周期 Ton:PWM波一次周期中高电平持续时间
3.输出比较通道框图
结构比较明了,看图就行
高级定时器的输出比较通道:
通用定时器的输出比较通道:
4.输出比较模式
模式 | 描述 |
冻结 | CNT=CRR时,REF保持为原状态 |
匹配时置有效电平 | CNT=CRR时,REF置有效状态 |
匹配时置无效电平 | CNT=CRR时,REF置无效状态 |
匹配时电平翻转 | CNT=CRR时,REF电平翻转 |
强制为无效电平 | CNT与CRR无效,REF强制为无效电平 |
强制为有效电平 | CNT与CRR无效,REF强制为有效电平 |
PWM模式1 | 向上计数:CNT<CRR时,REF置有效电平;CNT≥CRR时, REF置无效电平 向下计数:CNT>CRR时,REF置无效电平;CNT≤CRR时, REF置有效电平 |
PWM模式2 | 向上计数:CNT<CRR时,REF置无效电平;CNT≥CRR时, REF置有效电平 向下计数:CNT>CRR时,REF置有效电平;CNT≤CRR时, REF置无效电平 |
具体这些模式在Keil5中一个函数即可设置。
5.基本结构/步骤
使能时钟->配置GPIO->配置好时基单元->配置好输出比较单元->打开时基单元的运行控制
6.Keil5代码
输出比较常用函数:
初始化输出比较(OC)
TIM_OC1Init
TIM_OC2Init
TIM_OC3Init
TIM_OC4Init
TIM_OCStructInit 给初始化函数中的结构体赋一个默认值
强制输出(为0/1)模式
TIM_ForcedOC1Config
TIM_ForcedOC2Config
TIM_ForcedOC3Config
TIM_ForcedOC4Config
配置CCR寄存器的预装功能(影子寄存器)
TIM_OC1PreloadConfig
TIM_OC2PreloadConfig
TIM_OC3PreloadConfig
TIM_OC4PreloadConfig
配置快速使能
TIM_OC1FastConfig
TIM_OC2FastConfig
TIM_OC3FastConfig
TIM_OC4FastConfig
外部事件时清除REF信号
TIM_ClearOC1Ref
TIM_ClearOC2Ref
TIM_ClearOC3Ref
TIM_ClearOC4Ref
配置输出极性(原码输出还是取非后输出),带N的是高级定时器中互补通道的配置函数
TIM_OC1NPolarityConfig
TIM_OC2NPolarityConfig
TIM_OC3NPolarityConfig
OC4没有互补通道
TIM_OC1PolarityConfig
TIM_OC2PolarityConfig
TIM_OC3PolarityConfig
TIM_OC4PolarityConfig
单独修改输出使能
TIM_CCxCmd
TIM_CCxNCmd
选择输出比较模式
TIM_SelectOCxM
单独更改CCR寄存器的值的函数
TIM_SetCompare1
TIM_SetCompare2
TIM_SetCompare3
TIM_SetCompare4
仅高级定时器使用,使用高级定时器输出PWM时,使用此函数使能主模式,否则PWM无法正常输出
TIM_CtrlPWMOutputs
使用输出比较产生PWM波一般初始化步骤(ARR与PSC的值根据需要调整):
void PWM_Init(void)
{
//第一步,使能时钟
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_0;
GPIO_Initstructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_Initstructure);
//第二步,选择时钟源
TIM_InternalClockConfig(TIM2);
//第三步,时基单元
TIM_TimeBaseInitTypeDef TIM_TimeInitstructure;
TIM_TimeInitstructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeInitstructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeInitstructure.TIM_Period = 100 - 1; //ARR
TIM_TimeInitstructure.TIM_Prescaler = 720 - 1; //PSC
TIM_TimeInitstructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&TIM_TimeInitstructure);
//第四步,输出比较OC
TIM_OCInitTypeDef TIM_OCInitStruct;
TIM_OCStructInit(&TIM_OCInitStruct);
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_Low;
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_Pulse = 0;
TIM_OC1Init(TIM2,&TIM_OCInitStruct);
//第五步,运行控制
TIM_Cmd(TIM2,ENABLE);
}
二、输入捕获
1.什么是输入捕获
还是通用定时器的框图,在时基单元下方,捕获/比较寄存器左边,就是输入捕获部分的框图。
输入捕获:IC(Input Capture)。输入捕获模式下,当通道输入引脚出现指定电平跳变时,当前CNT的值(计数器的值)将被锁存到CCR(捕获/比较寄存器,下同)中,可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数。每个高级定时器和通用定时器都有四个输入捕获通道。输入捕获通道可以设置为PWMI模式,可以同时测量PWM波的频率和占空比。此外,配合主从触发模式可以实现硬件电路自动测量,就省去了软件代码反复调用函数和中断所需要的时间。
2.输入捕获通道测量频率的方法
1.测频法:在一段时间T内,对上升沿次数进行计数,得到N,则频率f=N/T;适用于高频信号,因为如果信号频率比较低,在T时间内可能一次上升沿都记不到,得到的频率为0,这显然误差比较大。不过即便是对高频信号用测频法,也可能存在一次上升沿的误差,比如在信号为低电平时,时间T结束了,那时间T结束时,信号低电平之后的那一个上升沿就没有计上。
2.测周法:在两个上升沿之间,以一个自己定的,标准频率F计次,计了几次,N就是多大,那么此时频率f=F/N。这种方法适用于低频信号,信号频率低,两个上升沿之间间隔时间大,N就大,得到的结果误差相对会小一些。如果用于高频,那么试想一下极端情况,两个上升沿之间时间间隔非常短,短到N为0,那么得到频率就是0,误差非常大。同样的测周法也和测频法一样,N有一次计数上的误差。
中界频率:上述两种方法的使用场景是不同的,针对信号频率的高低,使用不同的方法,误差也是不一样的,那高频低频的界限是什么呢?这就引入了中界频率的概念,中界频率处,两种方法的误差是一样的,信号频率高于中界频率,就认为是高频,用测频法,信号频率低于中界频率,就认为是低频,用测周法。
3.输入捕获结构框图
输入捕获通道,个人认为应该看成两个通道一组,四个通道构成两组,每组两个。如框图所示。信号从通道1进入后,经过滤波器去除毛刺,然后检测边沿,可以选择捕获上升沿还是下降沿,然后选择是原码输出,还是取非再输出,到TI1FP1为止,通道1和2的流程一样。两个通道的信号都可以从通道1的输出通道输出(IC1),同样的,通道1也可以从IC2输出(框图中没画),这样。通道1和2的输出部分是交叉着的。这一点可以用来同时测频率和占空比。一个IO口输入,然后通过两个通道分别输出频率,占空比。
4.主从触发模式
将定时器内部的引脚,或者说信号,映射到TRGO引脚,再通过TRGO引脚发送给其他外设,用于触发其他外设。
从模式:接受其他/自身外设的信号,被别的信号控制去执行一些功能。
触发源选择:选择控制从模式的信号源。映射到TRGI。
5.输入捕获和PWMI模式框图
输入捕获:使能时钟->配置GPIO->配置输入捕获单元->配置时基单元->触发源,从模式->打开运行控制
基本流程不变,在输入捕获配置步骤中,不再使用初始化函数,而是直接用TIM_PWMIConfig()函数自动配置。
6.Keil5代码
常用函数:
/*
TIM_ICInit //初始化,四个输入通道公用一个函数,在输出中是四个函数分开的
TIM_PWMIConfig //可以快速配置两个输入通道
TIM_ICStructInit //给结构体赋一个默认初始值
//三兄弟
TIM_SelectInputTrigger //选择输入触发源TRGI(从模式触发源的选择)
TIM_SelectOutputTrigger //选择输出触发源TRGO(主模式输出的触发源)
TIM_SelectSlaveMode //选择从模式
TIM_SetIC1Prescaler //单独配置1/2/3/4通道的预分频器
TIM_SetIC2Prescaler
TIM_SetIC3Prescaler
TIM_SetIC4Prescaler
TIM_GetCapture1 //读取通道1/2/3/4的CCR
TIM_GetCapture2 //输出比较模式下,CCR寄存器只写,输入捕获模式下,CCR寄存器只读
TIM_GetCapture3
TIM_GetCapture4
*/
初始化函数:
void IC_Init(void)
{
//第一步,使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,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(GPIOA,&GPIO_InitStructure);
//配置时基单元
TIM_InternalClockConfig(TIM3); //使用内部时钟源
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(TIM3,&TIM_Initstructure);
//配置输入捕获单元
TIM_ICInitTypeDef TIM_ICInitStruct;
TIM_ICInitStruct.TIM_Channel = TIM_Channel_1; //选通道
TIM_ICInitStruct.TIM_ICFilter = 0xF; //滤波
TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising; //
TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI; //直接通道
TIM_PWMIConfig(TIM3,&TIM_ICInitStruct);
//从模式
TIM_SelectInputTrigger(TIM3,TIM_TS_TI1FP1);
TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset);
//使能中断控制
TIM_Cmd(TIM3,ENABLE);
}