定时器分类:
①高级定时器(功能最全)(Stm32的TIM1、TIM8)(挂载在APB2总线上)
:通用定时器+重复计数器、死区生成、互补输出、刹车输入
②通用定时器(最常用)(Stm32的TIM2、TIM3、TIM4、TIM5)(挂载在APB1总线上)
:基本定时器+内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式
③基本定时器(Stm32的TIM6、TIM7)(挂载在APB1总线上)
:只有定时中断、DAC功能。也就是最基本的传感器所用的定时器就是这种
StmF103C8T6的定时器资源:TIM1、TIM2、TIM3、TIM4(1高3通)
Stm定时器的功能:
①定时中断(+内外部时钟源选择)
②输出比较(PWM—>电机)
③输入捕获(测输入的PWM的频率、占空比)
④编码器接口(?)
⑤主从触发模式
Stm32定时器的时基单元:
①16位的CNT计数器(CRR,产生一次中断所需要的计数值)
②16的PSC预分频器(系统时钟/预分频 = 时钟频率)
②16位的自动重装计数器(每次循环的计数值)
(主频) / (预分频) / (计数器)
2^16 = 65536 ,所以最小中断频率Small-Hz是72 000 000 / 65535 / 65535
所以最大中断时间是 T = 1 / Hz = 1 /72 000 000 / 65535 / 65535 = 59.650503125 ≈59.65s
级联:
Stm32的定时器可以级联,也就是A定时器的输出作为B定时器的输入。
时钟信息流向(自己编的名字):
RCC(复位时钟控制器)—(触发控制器)—>PSC预分频器——>CNT计数(当CNT==自动重装计数器,会产生中断信号,自动重装计数器会重置CNT)
CNT计数器:
向上计数,等于ARR时产生中断
向下计数,等于0是产生中断
中央对齐计数,等于ARR或0时产生中断
中断:
先经过中断控制(是否开启/允许中断),再去向NVIC(中断通路、优先级)
①更新中断:会通向NVIC,在NVIC配置了优先级与中断通路之后,CPU才能响应中断。
②更新事件:
可以触发其他电路的工作,映射到RTGO上,TRGO可以直接连接到某个引脚进行控制。硬件自动化。
TRGO也可以连接其他定时器,定时器被其他定时器输入的引脚是ITRx引脚,不同定时器的x值不同(下图),且不是数值刚好对应(定时器1对应ITR1)的情况。称为定时器级联。
定时器的部分说明:
①基本定时器只能选择内部时钟
②通用和高级定时器可以选择外部时钟ERT(),也就是可以让一个ETR引脚接收方波信号来模拟时钟的高低电平,但是这需要配置内部硬件的<极性选择><边沿检测><预分频><输入滤波电路>
③输入的时钟
①走ETRF进入触发控制器,由此可以配置其作为时基单元的时钟源。在STM32中被称为“外部时钟模式2”,一个实例就是用TIM来计数外部时钟的次数(按键当时钟信号)
②走TRGI走触发控制器,作为触发输入(占用了这个通道),与定时器的从模式有关。当这一路的输入被作为时钟信号时,称为“外部时钟模式1”(定时器级联)。
③两者输入都可以作为外部时钟,区别是有没有占用触发输入的通道
外部时钟的英文简称是HSE(High Speed External Clock),内部时钟的英文简称是HSI(High Speed Internal Clock)。
TI1F_ED连接的是输入捕获引脚CH1,ED是边沿的意思,此路意为输入的上下边沿均有效 。编码器接口左侧的TI1FP1和TI2FP2是连载CH1、CH2的滤波器之后的。
CH引脚用于输入捕获和测频率,ITRx用于定时器的级联,编码器接口用于读取正交编码器的波形
内部时钟优先72Mhz,外部时钟优先走ETR通路(外部时钟模式2,方便)
TRGO是定时器的主模式输出
捕获/比较的通道用于输出PWM驱动电机(舵机、直流电机)
高级计数器的部分额外功能
重复次数计数器:设定一个值x,每经过x个计数周期才产生一次中断。类似于再分频。
DTG:死区生成电路,防止互补输出由于硬件限制,驱动三项无刷电机驱动桥臂时造成短暂的直通现象,会在开关切换的瞬间生成一定时常的死区,关闭输出,防止直通
最右侧的输出的3个引脚由一个(TIMx_CHx)变成了两个(TIMx_CHx、TIMx_CHxN),可以输出互补的(极性相反)的PWM波,用于三相无刷电机,因为无刷电机一般需要3路控制,所以图中第4路没有多一条输出。
BRK:刹车输入。保证电机运行安全,外部产生BRK信号的时候或内部时钟失效,就会切断输出,防止意外发生
缓冲计数器
(影子寄存器、硬件说明图的底框加黑就说明该硬件有影子寄存器):
在某个计数周期未完成的时候,如果写入新的预分频值,则缓冲计数器会先记录写入的值,在当前的计数周期完成之后,再把数值写入预分频值,避免中途写入导致前后计数频率不一致误差问题。两个没缓冲计数器的情况:
情况1:原ARR==36,CNT<36,写入55,则导致当前计数周期加长;
情况2:原ARR==40,CNT<40但>35,写入35,写导致当前计数会从CNT—>FF—>35,而不是直接因为CNT>35而进入下一个计数周期
预分频器的工作原理:通过一个计数器在计数预定分频值之后输出一个信号。比如2分频,那么就是0 1 2 0 1 2 ,并且在2—>0过程中输出一次信号给CNT,从而CNT得到的值就是分频过后的
时钟树的部分功能说明:
左侧是时钟来源(什么频率的时钟作为输入源),右侧是时钟分配(谁需要用什么频率的时钟)。
硬件执行前都需要先初始化配置时钟,但是Stm32已经完成了这一步操作。
系统时钟:72MHz
有四个时钟震荡源:
8MHz的内部高速RC振荡器
4~16MHz的外部高速石英振荡器(晶振,比内部RC稳定)
32.768KHz的低速晶振(给RTC(实时时钟)提供时钟)
40KHz内部低速RC振荡器(给看门狗提供时钟)
两个高速晶振用于给系统提供时钟(AHP,APB1,APB2)
Stm32的时钟配置流程(SystemInit()函数的配置)
先以内部高速8Mhz时钟(RC)启动,然后启动外部时钟,将外部8Mhz时钟(晶振)进行PLL锁相环9倍频得到72Mhz的时钟,等到锁相环输出稳定后,在将锁相环输出的时钟作为系统时钟。从而把系统时钟改8Mhz——>72MHz。
但一旦外部时钟出了问题,系统就会选择内部8MHz的时钟作为系统时钟,72/8=9≈10,这时候程序运行的速度降低。举例:原先一秒的中断会变成9秒的中断
外部时钟的运行状态由CSS检测,一旦出错就会更换系统时钟源。(高级定时器的刹车输入也有CSS)
外设时钟使能就是XXX_cmd(XXX,ENABlE)
输出比较+PWM:
1.大概介绍
CCR里的是自行设定的一个值。
比较CNT和CCR(capture/compare registe捕获/比较寄存器里的值)的值,来设置是输出高/低,或是翻转。捕获比较寄存器是两个功能一起的,看配置要求来启动其中的一个功能。
PWM在惯性系物件上使用。通过调节输出的高低电平在一段时间内的占比范围(占空比==高T/周期T),来控制惯性系物件的状态。用于LED呼吸灯、直流电机调速。
工作原实现:配置CNT和CCR的值,通过比较两值大小关系来输出高低电平;
模式1:
向上:CNT<CCR(输出有效电平),CNT≥CCR(无效)
向下:CNT>CCR(无效),CNT≤CCR(有效)
模式2:
向上:CNT<CCR(无效),CNT≥CCR(有效)
向下:CNT>CCR(有效),CNT≤CCR(无效)
PWM频率:计数器频率, 即 时钟源频率 / 预分频+1 / ARR+1
PWM占空比:CCR/(ARR + 1)
PWM分辨率:1/(ARR + 1) (占空比最小变化单位,如1%,0.1%)
注意:不同定时器对应的输出引脚不同
2.函数配置:
外设使能——>GPIO初始化——>时基单元初始化——>输出比较配置——>cmd
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;//受外设控制(定时器、ADC)的都要用复用模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/*配置时钟源*/
TIM_InternalClockConfig(TIM2); //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); //将结构体变量交给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_OC1Init(TIM2, &TIM_OCInitStructure); //将结构体变量交给TIM_OC1Init,配置TIM2的输出比较通道1
/*TIM使能*/
TIM_Cmd(TIM2, ENABLE); //使能TIM2,定时器开始运行
}
TIM_SetCompare3(TIM2,Compare);//用于修改第3输出比较通道的CCR的值,总共有1、2、3个输出比较CCR寄存器
输入捕获和输出比较的输入输出引脚都是公用的,且一次只能启动一中种功能
输入捕获IC(Intput Capture):
1.输入捕获的方式有:上升沿触发、下降沿触发、边沿触发。
以及专门测量PWM的PWMI模式,可以同时测量频率和占空比。
适合主从模式实现硬件的自动测量PWM
2.以上升沿触发为例解释两种测量方法:
(1)测频法(适合测量高频。由于是一段固定时间内的值,所以结果是均值,出结果速度慢):给定一个时间T(常1s),在时间T内测量有N次上升沿,然后频率f = N / T
(2)测周法(适合测量低频。只测量一个周期,所以出结果得速度取决于输入频率,波动较大):在两个上升沿内,用标准频率计N次来计算一个周期的时间T,然后f = 1 / T。
两种理解
①先后两次测量CNT的值得X1,X2;
若X1<X2,则T = X2 - X1;
若X1>X2,则T = ARR - X1 + X2
然后得f = 1 / T(这里的时间的频率f取决于系统主频f'。)
②在第一次上升沿时value1 = 0并开始计数,第二次上升沿时value2 = x,记一个数得时间t = 1 / f', 则期间的总计时间是(value2 -value1)*t,记N = value2 - value1
则两次上升沿的频率是 f = 1 / (N * t) = f' / N。
(假设72Mhz的时钟源,那么每次计数的时间是T=1 / 72,000,000秒,计数N次就是 Tn = N / 72000000秒),再由f = 1 / T,T = Tn,得到频率f = f’ / N。)
3.以上两种测量方法都存在正负1的误差,即①测量过的上升沿次数不一定是完整的一个周期,而可能是半个周期,所以会需要取舍;②测量到第2个上升沿的时候,可能标准计数次数也不是完整的一个频率,而是0.xx,这时候也需要取舍。所以两种测量方法都有误差,但是要选择更合适的方法以减少误差。
4.由此,当(1)和(2)的N相同时,也就是误差相同,此时的输入频率f = √(f' / T)。式子来源:
将(1) (2)得N提出来,N = f * T = f' / f,则f*f = f' / T,f = √(f' / T)。
当待测频率fx<f时,测周法合适;当fx>f时,测频法合适
3.XOR:
输入引脚处的三输入异或门是为三项无刷电机服务的,当三个输入引脚有任一引脚电平翻转时,异或门输出的电平翻转,通过数据选择器TI1。如果数据选择器选择了上面的模式,那么输出的就是三个输入引脚(TIMx_CH1、CH2、CH3)的异或值,要是选择了下面的模式,那就是输出TIMx_CH1得值,三个引脚各输出各的。
三项无刷电机有3个霍尔传感器来检测转子的位置,可以根据转子的位置进行换相,前三个输入通道可以接入无刷电机的霍尔传感器,从而让定时器 作为无刷电机的接口定时器去驱动换相电路工作
输入滤波器可以对输入的信号进行滤波,减少毛刺信号;
边沿检测器可以配置是上升沿检测、下降沿检测、边沿检测
有两套滤波+极性选择;
第一套输入得到TIF1FP1然后输入给通道1
第二套经过另一套滤波+极性选择,得到TIF1FP2,输入给通道2
同理,TI1、2、3、4的输入都可以经过两套滤波+极性选择,可以得到两个输出结果进入两个通道。(1和2交叉、3和4交叉)
这可以实现交叉输入,灵活切换后续捕获电路的输入
也可以把一个引脚的捕获输入给两个通道,用于PWMI:第一个捕获通道配置上升沿触发,捕获周期,第二个通道配置下降沿触发,捕获占空比。两个通道对一个引脚进行捕获,就可以获得周期、占空比
4.输入捕获单元的内部原理图解释
TI1是外部输入信号,Fdts是测周法的参考频率
而ICF[3:0]指的是TIMx_CCMR1寄存器里的ICF位,可以控制滤波器的参数。不同x对应的ICF的称号不同,如TIM1_CCMR1的ICF的名字是IC1F。
如图说明IC1F,采样频率就是测周法的参考频率,频率约小,一段时间内采样的个数N就越大,吕滤波效果就越好。
5.数字滤波器的工作原理:
计数了N个电平,如果这N个电平都是一样的,就输出这N个电平对应的状态。比如N个都是低电平,那就输出低电平)。
如果N个中有不同的,那就维持原先的电平输出。比如原先输出高电平,后续的N个采样中有N-1个低电平,1个高电平,那就接着输出高电平,因为这N个采样中有波动,所以滤波。
不要理解成吃了N个输入信号才吐一个信号!实际情况是检测N个信号来判断是不是要改变输出信号!如果N个信号内有波动,则说明是信号不稳,就需要维持原状态输出!要是N个信号的状态是稳的,那就需要转换输出!但是在检测的过程中也是在不断维持原有信号状态进行输出的!
而且采样频率一般会很高,如果真的有外部输入信号的频率和采样频率一摸一样,才可能导致N个信号一直不稳,但这种情况是不被允许的(应该极力避免的),否则会导致滤波器的输出信号可能不发生改变。
N可以配置,采样频率可以配置。
会导致输入信号降低频率的是分频器。
滤波之后的信号再通过边沿检测器来统计上升沿、下降沿或边沿。
其中CC1P用于使能选择器的输出极性,然后输出TI1FP1,之后通过后续的捕获电路。
前文说过每个输入滤波器和边沿检测器都有两套,所以上图中少了一套产生由通道1但产生信号TI2FP2并通向通道2的滤波+极性器。
然后通过CC1S控制的数据选择器(选择是接收TI1FP1还是TI2FP2还是TRC的输入)
ICPS用于配置分频器(1、2、4、8)
CC1E用于控制输出使失能。开启使能之后,输入信号在经历系列电路之后,就可以让CCR捕获CNT的值,并且清零CNT。
自动清零CNT:TI1FP1与TIF_ED都可以通向从模式控制器,也就是TI1FP1在触发边沿的时候,还可以触发从模式控制,而从模式内就有控制CNT清零的电路。也就是CNT清零靠从模式触发。
6.主从模式(图片参考自江协)
主模式:能够将定时器内部的信号映射到TRGO(Trigger Out触发输出),而TRGO可以用来触发别的外设
从模式:可以接收自身定时器或者是其他定时器输出映射到TRGO的信号控制的外设。
触发源:TRGO的信号来源,触发源选择就是选择触发从模式的信号源
想要TI1FP1信号触发从模式电路实现清零,就需要触发源选择TI1FP1,并且将从模式的操作选怎Reset,就可以实现全自动清零。
7.配置流程(测周法)及补充:
(1)配置:
1.非PWM输入捕获:
外设时钟—>GPIO—>时基单元—>滤波器、边沿检测/极性选择(上、下、边沿)—>
① (主从模式)TI1FP1—>触发源选择(选择TIxFPx)—>从模式功能选择(Reset)
②信号输入通道(直通TI1FP1、交通TI1FP2)—>分频器—>CCR(CCR记录当前 CNT的值)
配置好 后,CCR之后从模式会自动Reset CNT的值。
2.PWM输入捕获:
外设时钟—>GPIO—>时基单元—>滤波器、边沿检测/极性选择(上、下、边沿)—>
① (主从模式)TI1FP1—>触发源选择(选择TIxFPx)—>从模式功能选择(Reset)
②TI1FP1—>输入信号预分频器配置—>CCR1(CCR1记录上升沿CNT的值)
③TI1FP2—>输入信号预分频器配置—>CCR2(CCR1记录下降沿CNT的值)
配置好后,CCR1会在上升沿记录CNT的值,并且在第二次及以后记录后会清零CNT的值,但是CCR2将在下降沿读取CNT的值,且不会清零CNT的值。由于CCR1第一次记录CNT的之后会清零CNT的值,所以CCR2记录的值时0~CNT的值。
记CCR1第二次及以后记录的CNT值为x,CCR2记录的为y,则占空比 = y / x 。
详细内容流程:
①外设时钟(TIM、GPIO)——》
②GPIO——》
③时基单元(ARR、PSC、CNT、计数模式、分频、重复计数器)——》
④IC(IC通道、滤波器的采样N、边沿检测器极性选择(上、下、边沿)、分频器、信号源(通、交))——》
⑤从模式(触发源、触发模式(信号来了干什么:Reset))
(2)配置函数(来自江协):
//1.测频率
void IC_Init(void)
{
/*开启时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //开启TIM3的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
/*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); //将PA6引脚初始化为上拉输入
/*配置时钟源*/
TIM_InternalClockConfig(TIM3); //选择TIM3为内部时钟,若不调用此函数,TIM默认也为内部时钟
/*时基单元初始化*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //定义结构体变量
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数
TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1; //计数周期,即ARR的值
TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; //预分频器,即PSC的值
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器,高级定时器才会用到
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure); //将结构体变量交给TIM_TimeBaseInit,配置TIM3的时基单元
/*输入捕获初始化*/
TIM_ICInitTypeDef TIM_ICInitStructure; //定义结构体变量
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; //选择配置定时器通道1
TIM_ICInitStructure.TIM_ICFilter = 0xF; //输入滤波器参数,可以过滤信号抖动
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //极性,选择为上升沿触发捕获
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //捕获预分频,选择不分频,每次信号都触发捕获
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //输入信号交叉,选择直通,不交叉
TIM_ICInit(TIM3, &TIM_ICInitStructure); //将结构体变量交给TIM_ICInit,配置TIM3的输入捕获通道
/*选择触发源及从模式*/
TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1); //触发源选择TI1FP1
TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset); //从模式选择复位
//即TI1产生上升沿时,会触发CNT归零
/*TIM使能*/
TIM_Cmd(TIM3, ENABLE); //使能TIM3,定时器开始运行
}
//测周法获取CNT值:其中1 000 000 = 72Mhz / 72(psc)
//f' = fc / N
uint32_t IC_GetFreq(void)
{
return 1000000 / (TIM_GetCapture1(TIM3) + 1); //测周法得到频率fx = fc / N,这里不执行+1的操作也可(不+得到的数值大概率多1)
}
//2.测频率+占空比,
//方法①在输入捕获初始化下面沾上下面的语句。
TIM_ICInitStructure.TIM_Channel = TIM_Channel_2; //选择配置定时器通道2
TIM_ICInitStructure.TIM_ICFilter = 0xF; //输入滤波器参数,可以过滤信号抖动
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling; //极性,选择为上升沿触发捕获
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //捕获预分频,选择不分频,每次信号都触发捕获
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_IndirectTI; //输入信号交叉,交叉输入
方法②将 TIM_ICInit(TIM3, &TIM_ICInitStructure);
改为 TIM_PWMIConfig(TIM3, &TIM_ICInitStructure);
这是Stm公司写好的函数,但是只允许是对通道1、2进行配置;不能对3、4进行这样的配置。
读取占空比(0~100)
uint32_t IC_GetDuty(void)
{
return (TIM_GetCapture2(TIM3) + 1) * 100 / (TIM_GetCapture1(TIM3) + 1); //占空比Duty = CCR2 / CCR1 * 100,这里不执行+1的操作也可
}
(3)补充:
①运行时,第一次记录的CNT的值是未知的,记录第一次之后就会立即Reset CNT,在第二次记录CNT的值时,该值对应的就是在系统频率下,输入信号的一个周期的时间,。因为第一次记录之后清零,第二次及以后记录的值就是从0~>CNT的最新CNT的值。
②要是外部输入信号的频率太低,就可能导致CNT的值溢出。所以把ARR的值弄到最大,极可能人为避免这种情况。
举例就是:第一次记录到高电平之后CNT被Reset了,但是在等待第二次高电平的时候,在CNT到最高计数值的期间内都等不到第二次高电平,然后CNT的值已经超过了ARR的最高上限,这时候定时器的配置会让CNT从零开始。
③从模式的信号源不是所有定时器都有,如参考自江协的图片所示,只有TI1FP1和TI2FP2的信号,没有TI3FP3和TI4FP4的信号,因此从模式自动清零CNT只能选择输入捕获通道1、2,而不能选择3、4。对于通道3、4,就只能通过捕获中断在中断内部调用函数手动清零,但易消耗软件资源。
④
直通:输入信号从哪里来就从哪输入
交通:输入信号交错输入,因为1和2、3和4有交通输入,也就是1/2的信号可以输入给2/1...
⑤采样频率与系统时钟频率有关。
⑥占空比是下降沿触发的原因:
⑦测周法
最大检测频率 = 72Mhz / ARR。但是当检测频率接近72Mhz / ARR时,误差会越来越大
最小检测频率 = 72Mhz / PSC / ARR。小于这个频率的话,CNT的值就会溢出。
误差精度 = 72Mhz / ARR / 最大检测频率
上述的PSC都是时基单元分频器的值,而非IC通道分频器的值。
对于过高的待测频率,就应该用测频法,而不是用测周法了
用PWM驱动电机,再用编码器测量电机的速度,然后用PID算法进行闭环控制,就可以控制小车的移动。电机速度一般比较快,一般用无接触的霍尔传感器or光栅测
编码器接口(Encoder Interface):
1.优于用外部中断来计次编码器转次,防止电机高速转动时,软件频繁进入中断消耗软件资源。
2.可以接收增量(正交)编码器的信号,根据编码器的旋转方向产生正交信号脉冲,自动控制控制CNT自增或自减,从加减可以测得旋转方向,从数值变化大小可以测得旋转速度。
3.正交编码器:两个相(A、B)的输入相差刚好90°。
①这样在旋转的时候,根据旋转方向的不同,就可以知道哪一个端先输入有效信号,哪一端后输入有效信号,从而判断旋转方向。
②而在一段时间内产生多少个脉冲,就可以计数多少个脉冲,然后根据固定时间内接受的脉冲数量,进行一段处理就可以得到速度。
4.每个高级、通用定时器都只有1个编码器接口;编码器的两个相的输入端分别占用IC的通道1、2,(用到了TI1FP1、TI2FP2;通道3、4不能接编码器,即CH3、CH3)。需要注意的是,作为编码器接口的定时器不能启用其他功能。TI1FP1、TI2FP2见下图编码器接口左侧。
5.输入的信号的流传:
CH1、2—>输入滤波器和边沿检测器——>触发控制器的编码器接口——>PSC——>CNT。
其中边沿检测器中的极性选择的可以选择输入信号是高电平还是低电平有效,这会导致计数方向改变。(因为极性选择是接收到对应极性才输出高电平,否则输出低电平。如果选在高极性,那就是接收高电平输出高电平,接收低电平就输出低电平;如果选择低极性有效,那就是接收低电平输出高电平,接收高电平输出低电平。个人理解)
其中ARR一般配置为最大值65535,用于计数满后清零。在这个流程下,72Mhz的内部时钟、时基单元配置的CNT计数方向(向上、向下、中央对齐)不会被该电路使用。CNT的计数快慢,计数加减由编码器控制
补充说明:在编码器接口的工作模式下,通道1、2的OC也是可以正常工作的。(前提是使能了输出控制)
6.工作模式:三种。第三种的精度最高
这个表的看法是:先选定自己的工作模式,再想所需要两个输入的信号高低,然后看右上方的TI1、TI2的上升、下降,然后再找对应工作模式下选定上升/下降列对应的计数方式,此时相对信号的电平就控制计数是+还是-。
比如我选在TI1和2上计数,想要知道TI1高、TI2低电平的计数是什么:
函数配置流程:
外设时钟——>GPIO——>时基单元——>IC——编码器。
其中时基单元的CNT计数方式、时钟源选择即使配置了也不会生效,因为CNT的的计数由编码器作为时钟源控制,而CNT的技术方向由编码器的旋转方向决定是+还是-。IC的边沿检测器的极性选择将控制旋转编码器的计数方向(因为极性选择将导致输入信号的高低电平是否翻转)。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
TIM_InternalClockConfig(TIM3);
//GPIO
GPIO_InitTypeDef GPIOAInitStructure;
GPIOAInitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIOAInitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIOAInitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIOAInitStructure);
//时基单元
TIM_TimeBaseInitTypeDef InitStructure;
InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//时钟源分频——编码器模式下用不到
InitStructure.TIM_CounterMode = TIM_CounterMode_Up;//计数模式 向上计数——编码器模式下用不到
InitStructure.TIM_Period = 65536 - 1;//ARR 自动重装值
InitStructure.TIM_Prescaler = 1 - 1;//PSC 预分频
InitStructure.TIM_RepetitionCounter = 0;//重复计数器-高级定时器:多少个计数周期算计数一次
TIM_TimeBaseInit(TIM2,&InitStructure);//结构体是初始化谁的就是谁Init
//IC
TIM_ICInitTypeDef ICInitStructure;
TIM_ICStructInit(&ICInitStructure);//不完全配置参数就需要这一步,用结构体初始化
ICInitStructure.TIM_Channel = TIM_Channel_1 ;//IC的输入通道
ICInitStructure.TIM_ICFilter = 0xF;//输入信号滤波器的值
ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_BothEdge;//输入信号有效边沿选择——编码模式下则对应是否翻转极性——后面可以统一配置,这里可以不配
// ICInitStructure.TIM_ICPrescaler = ;//后半段的输入信号分频——编码器用不到
// ICInitStructure.TIM_ICSelection = ;//后半段的信号通道选择——编码器用不到
TIM_ICInit(TIM3,&ICInitStructure);
ICInitStructure.TIM_Channel = TIM_Channel_2 ;//IC的输入通道
ICInitStructure.TIM_ICFilter = 0xF;//输入信号滤波器的值
ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_BothEdge;//输入信号有效边沿选择——编码模式下则对应是否翻转极性——后面可以统一配置,这里可以不配
// ICInitStructure.TIM_ICPrescaler = ;//后半段的输入信号分频——编码器用不到
// ICInitStructure.TIM_ICSelection = ;//后半段的信号通道选择——编码器用不到
TIM_ICInit(TIM3,&ICInitStructure);
//配置编码器接口
TIM_EncoderInterfaceConfig(TIM3,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising);//定时器通道、编码模式(单计数、双计数)、IC1的输入极性、IC2的输入极性
TIM_Cmd(TIM3,ENABLE);//TIM使能
获取编码器旋转值的函数
TIM_GetCounter(TIM3);
清零编码值的函数
TIM_SetCounter(TIM3,0);