STM32输入捕获详细配置及通过编码器读电机转速
STM32输入捕获工作过程
通过检测TIMx_CHx上的边沿信号,在边沿信号发生跳变(比如上升沿/下降沿)的时候,将当前定时器的值(TIMx_CNT)存放到对应的捕获/比较寄存器(TIMx_CCRx)里面,完成一次捕获。
STM32输入捕获设置
步骤1:设置输入捕获滤波器
首先,FDTS的频率由TIMx_CR1的CKD[1:0]的设置决定,为00时,FDTS=FCK_INT。
其次,TIMx_CCMR1的ICF[3:0]这几个为用来设置输入采用频率和数字滤波器的长度,
上面N是滤波长度,例如,当N=8时,并设置IC1映射到通道1上,且为上升沿触发,那么捕获到上升沿的时候,再以Fck_int的频率,连续采样到8次通道1的电平,如果都是高电平,则说明是一个有效的触发,就会触发输入捕获中断,这样就可以滤除那些高电平脉宽低于8给采用周期的脉冲信号。不做滤波处理的话,将其设置为0000.
步骤2:设置输入捕获极性
设置上升沿捕获还是下降沿捕获。
步骤3:设置输入捕获映射通道
步骤4:设置输入捕获分频器
步骤5:捕获到有效信号可以开启中断‘
寄存器
一:捕获比较模式寄存器CCMR寄存器
二:捕获比较使能寄存器
三:事件产生寄存器(TIMx_EGR)
四:中断使能寄存器
输入捕获(高电平脉宽)函数详解
void TIM5_CH1_Cap_Init(u32 arr,u16 psc)
{
RCC->APB1ENR|=1<<3; //TIM5时钟使能
RCC->AHB1ENR|=1<<0; //使能PORTA时钟
//端口复用
GPIO_Set(GPIOA,PIN0,GPIO_MODE_AF,GPIO_OTYPE_PP,GPIO_SPEED_100M,GPIO_PUPD_PD);
GPIO_AF_Set(GPIOA,0,2); //PA0,AF2
//设置自动重装载值与预分频系数
TIM5->ARR=arr;
TIM5->PSC=psc;
TIM5->CCMR1|=1<<0; //CC1S=01 设置CC1通道为输入,IC1映射到T1上
TIM5->CCMR1|=0<<4; //IC1F=0000 无滤波
TIM5->CCMR1|=0<<10; //IC1PS=00 不分频
TIM5->CCER|=0<<1; //CC1P=0 上升沿捕获
TIM5->CCER|=1<<0; //CC1E=1 允许捕获计数器的值到捕获寄存器中
//软件控制产生更新时间,使写入预分频器的值立即生效,否则要等到定时器溢出才生效
TIM5->EGR=1<<0;
TIM5->DIER|=1<<1; //允许捕获1中断
TIM5->DIER|=1<<0; //允许更新中断
TIM5->CR1|=0x01; //使能定时器2
MY_NVIC_Init(2,0,TIM5_IRQn,2);//设置抢占优先级2,子优先级0,组2
}
设计一个寄存器,
第7位为捕获完成标志位
第6位为捕获到低电平标志位,
因为要检测高电平脉冲所持续的时间,所以需要判断目前捕获的电平是高电平还是低电平,当高电平来时记录计数器的当前值,将该标志位设置为0,然后将捕获中断改为下降沿捕获,当下降沿来临时再次记录计数器的值,将该标志位置1,通过两次计数器值的差值就可以记录高电平持续时间
第5-0位记录定时器溢出的次数,
因为在如果高电平的持续时间很长,计数器的值会溢出,再用两次当前值相减时需要加上计数器溢出的那几个周期。
u8 TIM5CH1_CAPTURE_STA=0; //输入捕获状态
u32 TIM5CH1_CAPTURE_VAL; //输入捕获值
//中断服务程序
void TIM5_IRQHandler(void)
{
u16 tsr;
tsr=TIM5->SR;
//成功捕获后便不会进入这段程序,只有当主函数把计数值读出来,然后把该寄存器清0后,
//才会再次进入该程序,每次的执行过程是一样的,首先是上升沿捕获,然后将第6位标志位置1,
//计数器清0,将触发模式改为下降沿捕获,然后进入下降沿捕获,记录成功捕获,
//将触发模式改为上升沿,记录计数器的值。
//然后便不会进入这段程序,等待主程序将计数器的值读出来,寄存器清0后,再次执行该程序。
if((TIM5CH1_CAPTURE_STA&0X80)==0)//没有成功捕获高电平
{
if(tsr&0X01)//定时器溢出中断
{
if(TIM5CH1_CAPTURE_STA&0X40)//已经捕获到高电平
{
if((TIM5CH1_CAPTURE_STA&0X3F)==0X3F)//高电平太长,计数器溢出
{
TIM5CH1_CAPTURE_STA|=0X80; //直接标记成功捕获了一次
TIM5CH1_CAPTURE_VAL=0XFFFFFFFF;
}else TIM5CH1_CAPTURE_STA++;//每溢出一次加1
}
}
if(tsr&0x02)//定时捕获中断发生
{
if(TIM5CH1_CAPTURE_STA&0X40) //上一次捕获的是上升沿,这一次又发生捕获中断了,表明下降沿到了
{
TIM5CH1_CAPTURE_STA|=0X80; //标记成功捕获了一次高电平脉宽
TIM5CH1_CAPTURE_VAL=TIM5->CCR1; //获取当前计数器的值
TIM5->CCER&=~(1<<1); //CC1P=0 设置为上升沿捕获
}else //还未开始,第一次捕获上升沿
{
TIM5CH1_CAPTURE_STA=0; //清空
TIM5CH1_CAPTURE_VAL=0;
TIM5CH1_CAPTURE_STA|=0X40; //标记捕获到上升沿
TIM5->CR1&=~(1<<0) ; //使能定时器2
TIM5->CNT=0; //计数器清空
TIM5->CCER|=1<<1; //CC1P=1 设置为下降沿捕获
TIM5->CR1|=0x01; //使能定时器2
}
}
}
TIM5->SR=0;//清楚中断标志位
}
if(TIM5CH1_CAPTURE_STA&0X80) //成功捕获到一次高电平
{
temp=TIM5CH1_CAPTURE_STA&0X3F;
temp*=0XFFFFFFFF; //溢出时间总和
temp+=TIM5CH1_CAPTURE_VAL; //得到总的高电平时间
printf("HIGH:%lld us\r\n",temp);//打印输出
TIM5CH1_CAPTURE_STA=0; //寄存器清空,等待下一次捕获
}
编码器接口模式:
官方解释
选择编码器接口模式的方法是:如果计数器只在TI2的边沿计数,则置TIMx_SMCR寄存器中的SMS=001;如果只在TI1边沿计数,则置SMS=010;如果计数器同时在TI1和TI2边沿计数,则置SMS=011。
通过设置TIMx_CCER寄存器中的CC1P和CC2P位,可以选择TI1和TI2极性;如果需要,还可以对输入滤波器编程。两个输入TI1和TI2被用来作为增量编码器的接口。假定计数器已经启动(TIMx_CR1寄存器中的CEN=’1’),计数器由每次在TI1FP1或TI2FP2上的有效跳变驱动。TI1FP1和TI2FP2是TI1和TI2在通过输入滤波器和极性控制后的信号;如果没有滤波和变相,则TI1FP1=TI1,TI2FP2=TI2。根据两个输入信号的跳变顺序,产生了计数脉冲和方向信号。依据两个输入信号的跳变顺序,计数器向上或向下计数,同时硬件对TIMx_CR1寄存器的DIR位进行相应的设置。不管计数器是依靠TI1计数、依靠TI2计数或者同时依靠TI1和TI2计数。在任一输入端(TI1或者TI2)的跳变都会重新计算DIR位。
编码器接口模式基本上相当于使用了一个带有方向选择的外部时钟。这意味着计数器只在0到TIMx_ARR寄存器的自动装载值之间连续计数(根据方向,或是0到ARR计数,或是ARR到0计 数)。所以在开始计数之前必须配置TIMx_ARR;同样,捕获器、比较器、预分频器、触发输出特性等仍工作如常。在这个模式下,计数器依照增量编码器的速度和方向被自动的修改,因此计数器的内容始终指示着编码器的位置。计数方向与相连的传感器旋转的方向对应。下表列出了所有可能的组合,假设TI1和TI2不同时变换。
一个外部的增量编码器可以直接与MCU连接而不需要外部接口逻辑。但是,一般会使用比较器将编码器的差动输出转换到数字信号,这大大增加了抗噪声干扰能力。编码器输出的第三个信号表示机械零点,可以把它连接到一个外部中断输入并触发一个计数器复位。
下图是一个计数器操作的实例,显示了计数信号的产生和方向控制。它还显示了当选择了双边沿时,输入抖动是如何被抑制的;抖动可能会在传感器的位置靠近一个转换点时产生。在这个例子中,我们假定配置如下:
● CC1S=’01’ (TIMx_CCMR1寄存器,IC1FP1映射到TI1)
● CC2S=’01’ (TIMx_CCMR2寄存器,IC2FP2映射到TI2)
● CC1P=’0’ (TIMx_CCER寄存器,IC1FP1不反相,IC1FP1=TI1)
● CC2P=’0’ (TIMx_CCER寄存器,IC2FP2不反相,IC2FP2=TI2)
● SMS=’011’ (TIMx_SMCR寄存器,所有的输入均在上升沿和下降沿有效).
● CEN=’1’ (TIMx_CR1寄存器,计数器使能)
当定时器配置成编码器接口模式时,提供传感器当前位置的信息。使用第二个配置在捕获模式的定时器,可以测量两个编码器事件的间隔,获得动态的信息(速度,加速度,减速度)。指示机械零点的编码器输出可被用做此目的。根据两个事件间的间隔,可以按照固定的时间读出计数器。如果可能的话,你可以把计数器的值锁存到第三个输入捕获寄存器(捕获信号必须是周期的并且可以由另一个定时器产生);也可以通过一个由实时时钟产生的DMA请求来读取它的值。
相关寄存器
1:SMCR寄存器
其中的SMS位用来设置编码器模式,主要影响的是 上面表格《计数方向与编码器信号的关系》 内的计数器的方向。
设置为011时:可理解为一共有两个定时器通道来采集编码器A、B相的脉冲信号,编码器模式3时,会根据另一个通道的电平信号,以及当前通道电平的变化(上升、下降)来决定计数器的计数方向(增加/减少),例如当A相通道(TI1)电平信号为低电平时,B相通道(TI2)的电平跳变成高电平了,此时根据表格,计数器的值下降,编码器模式就是靠这个表格来计数的,这个数对应对应的就是编码器的位置
2:CR1寄存器
编码器模式需要设置位6:5为边沿对齐模式,因为SMCR设置为011,TI1和TI2同时计数情况下,计数器的方向要根据计数器的DIR方向位进行改变,故其设置为00.
void Encoder_Init_TIM2(void)
{
RCC->APB2ENR|=1<<0; //开启辅助时钟
AFIO->MAPR|=1<<8; //01部分重映射
RCC->APB1ENR|=1<<0; //TIM2时钟使能
RCC->APB2ENR|=1<<2; //PORTA时钟使能
RCC->APB2ENR|=1<<3; //PORTB时钟使能
GPIOA->CRH&=0X0FFFFFFF;//PA15
GPIOA->CRH|=0X40000000;//浮空输入
GPIOB->CRL&=0XFFFF0FFF;//PB3
GPIOB->CRL|=0X00004000;//浮空输入
TIM2->DIER|=1<<0; //允许更新中断
TIM2->DIER|=1<<6; //允许触发中断
MY_NVIC_Init(1,3,TIM2_IRQn,1);
TIM2->PSC = 0x0;//不分频
TIM2->ARR = ENCODER_TIM_PERIOD;//设置计数器自动重装载值
TIM2->CR1 &=~(3<<8);// 时钟分频,不分频
TIM2->CR1 &=~(3<<5);//选择计数模式,边沿对齐模式
TIM2->CCMR1 |= 1<<0; //CC1S='01' IC1FP1映射到TI1
TIM2->CCMR1 |= 1<<8; //CC2S='01' IC2FP2映射到TI2
TIM2->CCER &= ~(1<<1); //CC1P='0' IC1FP1不反相,ICIFP1=TI1
TIM2->CCER &= ~(1<<5); //CC2P='0' IC2FP2不反相,IC2FP2=TI2
// TIM2->CCMR1 |= 3<<4; // 输入捕获滤波器
TIM2 -> CNT=0;
TIM2->SMCR |= 3<<0; //计数器同时在TI1和TI2边沿计数
TIM2->CR1 |= 0x01; //使能定时器
}
单位时间读取编码器的计数值,可以转化为速度
入口参数:定时器
返回值:速度值
int Read_Encoder(u8 TIMX)
{
int Encoder_TIM;
switch(TIMX)
{
case 2: Encoder_TIM= (short)TIM2 -> CNT; TIM2 -> CNT=0; break;
case 3: Encoder_TIM= (short)TIM3 -> CNT; TIM3 -> CNT=0; break;
case 4: Encoder_TIM= (short)TIM4 -> CNT; TIM4 -> CNT=0; break;
default: Encoder_TIM=0;
}
return Encoder_TIM;
}
参考:《STM32中文参考手册_V10》
《STM32不完全手册_寄存器版本_V3.1》
《STM32F7中文参考手册》
正点原子官方教程