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