STM32标准库——(9)TIM编码器接口

目录

1.编码器接口简介

2.正交编码器

3.编码器基本结构

4.工作模式

5.实例

5.1 均不反相

5.2 TI1反相

6.相关API

7.编码器接口测速

7.1 实现思路

7.2 接线图

7.3 相关代码

Encoder.c

Encoder.h

main.c


1.编码器接口简介

  • Encoder Interface 编码器接口
  • 编码器接口可接收增量(正交)编码器的信号,根据编码器旋转产生的正交信号脉冲,自动控制CNT自增或自减,从而指示编码器的位置、旋转方向和旋转速度
  • 每个高级定时器和通用定时器都拥有1个编码器接口
  • 两个输入引脚借用了输入捕获的通道1和通道2

2.正交编码器

  1. 编码器接口 相当于一个带有方向控制的外部时钟 它同时控制着CNT的计数时钟和计数方向
  2. 旋转速度:当编码器的旋转轴转起来时 A相和B相就会输出这样的方波信号 转得越快 这个方波的频率就越高 所以方波的频率就代表了速度
  3. 旋转方向:当正转时A相提前B相90度 反转时A相滞后B相90度 当然这个正转是A相提前还是A相滞后 并不是绝对的 这只是一个极性问题 毕竟正转和反转的定义也是相对的 总之就是朝一个方向转 是A相提前 另一个方向是A相滞后

3.编码器基本结构

  1. TI1FP1和TI2FP2分别对应定时器的CH1、CH2这两个引脚
  2. 输入捕获的前两个通道 通过GPIO口接入编码器的A、B相 然后通过滤波器和边沿检测极性选择  产生TI1FP1和TI2FP2 通向编码器接口 编码器接口通过预分频器控制CNT计数器的时钟 同时 编码器接口还根据编码器的旋转方向 控制CNT的计数方向 编码器正转时 CNT自增 编码器反转时 CNT自减

  3. 这里ARR是有效的 一般我们会设置ARR为65535(最大量程) 这样的话 利用补码的特性 很容易得到负数 比如CNT初始为0 正转 CNT自增,0、 1、2、3、4、5、6、7等等  反转时CNT自减 0下一个数就是65535 接着是65534 65533等等 这里负数不应该是-1、-2吗  所以直接把这个16位的无符号数转换为16位的有符号数 根据补码的定义 这个65535就对应-1 65534就对应-2(有符号编码时负数按补码计算,2^16 的补码= -1)等等 这样就可以直接得到负数 非常方便 这就是我们读出数据得到负数的一个小技巧

4.工作模式

5.实例

5.1 均不反相

  • 红框内就是正交编码器抗噪声原理 由图可知 TI2没有发生变化 但TI1却跳变好几次 这不符合正交编码器的信号规律 然后通过上表逻辑 成功将这种毛刺信号滤掉 TI1为上升沿 TI2为低电平 查表可知此时为向上计数 即自增 接着看下一个状态 TI1为下降沿 TI2为低电平 查表可知此时为向下计数 即自减 所以这里一个引脚不变 另一个引脚来回跳动 计数器会来回加、减、加、减 尽管发生多次变化 但其值与原先值还是一样

5.2 TI1反相

  • TI1反相的计数频率与5.1中的类似 只不过TI1真正的信号是与上图相反 如红色划线部分 后续也是如此 将其电位翻转后 通过查询表可知其计数方式

6.相关API

TIM_EncoderInterfaceConfig

void TIM_EncoderInterfaceConfig(TIM_TypeDef* TIMx, uint16_t TIM_EncoderMode,
                                uint16_t TIM_IC1Polarity, uint16_t TIM_IC2Polarity);
功能:
	定时器编码器接口配置
参数:
    TIMx:其中x可以为1、2、3、4、5或8,选择TIM外设  
    TIM_EncoderMode: TIMx编码器模式
    TIM_IC1Polarity: IC1极性
    TIM_IC2Polarity: IC2极性
//参数3、4可以配置成TIM_ICPolarity_Falling: IC下降沿(反相)、TIM_ICPolarity_Rising: IC上升沿(不反相)
返回值:
	无    

7.编码器接口测速

7.1 实现思路

  1. 第一步,RCC开启时钟,开启GPIO和定时器的时钟
  2. 第二步,配置GPIO,这里需要把PA6和PA7配置成输入模式
  3. 第三步,配置时基单元,这里预分频器我们一般选择不分频,自动重装一般给最大65535,只需要个CNT执行计数就行了
  4. 第四步,配置输入捕获单元。不过这里输入捕获单元只有滤波器和极性这两个参数有用,后面的参数没有用到,与编码器无关
  5. 第五步,配置编码器接口模式。这个直接调用一个库函数就可以了

7.2 接线图

7.3 相关代码

Encoder.c
#include "stm32f10x.h"                  // Device header

void Encoder_Init(void)
{
    /*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
    /*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//将PA6引脚初始化为上拉输入
	//外部模块空闲默认输出高电平 选择上拉输入 默认输入高电平 外部模块空闲默认输出低电平 选择下拉输入 默认输入低电平 不过一般默认高电平 若不确定外部模块输出的默认状态或者外部模块输出功率非常小 这时就选择浮空输入模式
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;//这里TIM2要用来输出PWM 所以输入捕获的定时器需要换一个 暂时换到TIM3 由引脚定义表可以看到TIM3的通道1对应PA6 通道2对应PA7 通道3对应PB0 通道4对应PB1 这里选择TIM3的通道1 所以选择PA6引脚 这里需要配置上拉输入模式
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStructure);
	
    /*配置时钟源*/
//	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 = 1 - 1;//预分频给0 就是不分频
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);
	
    /*PWMI模式初始化*/
	TIM_ICInitTypeDef TIM_ICInitStructure;
	TIM_ICStructInit(&TIM_ICInitStructure);//该结构体最后两个参数在此无用处 因为这里只需配置滤波器和极性选择 但删除其他两个参数后可能显示结构体不完整 所以调用此函数给结构体赋一个初始值
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;//配置通道参数 这里选择通道1
	TIM_ICInitStructure.TIM_ICFilter = 0xF;//用来配置输入捕获的滤波器 若信号有毛刺或噪声 可以增大滤波器参数 可以有效避免干扰
//	TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//对应图里的边沿选择、极性选择部分
	//这里并不是上升沿有效 编码器接口始终都是上升沿、下降沿都有效 这里上升沿参数代表的是高低电平极性不反转
	TIM_ICInit(TIM3,&TIM_ICInitStructure);//调用此函数后 上面结构体的配置就写入寄存器中 接着再配置通道2
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;//配置通道参数 这里选择通道1
	TIM_ICInitStructure.TIM_ICFilter = 0xF;//用来配置输入捕获的滤波器 若信号有毛刺或噪声 可以增大滤波器参数 可以有效避免干扰
//	TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//对应图里的边沿选择、极性选择部分
	TIM_ICInit(TIM3,&TIM_ICInitStructure);//将第二个结构体参数配置写入寄存器中 配置通道2
	
	TIM_EncoderInterfaceConfig(TIM3,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising);//编码器接口配置 第二个参数选择TI1和TI2都计数
	//这里第三个参数和第四个参数与上面的极性选择配置重复 其功能一样 后定义的函数会覆盖前面的 所以前面的可以注释掉

	TIM_Cmd(TIM3,ENABLE);
}

/**
  * 函    数:获取编码器的增量值
  * 参    数:无
  * 返 回 值:自上此调用此函数后,编码器的增量值
  */
int16_t Encoder_Get(void)
{
	/*使用Temp变量作为中继,目的是返回CNT后将其清零*/
	int16_t Temp;
	Temp = TIM_GetCounter(TIM3);
	TIM_SetCounter(TIM3, 0);
	return Temp;
}
Encoder.h
#ifndef __ENCODER_H
#define __ENCODER_H

void Encoder_Init(void);
int16_t Encoder_Get(void);

#endif
main.c
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
#include "Encoder.h"

int16_t Speed;//定义速度变量

int main(void)
{
	OLED_Init();//OLED初始化
	Timer_Init();//定时器初始化
	Encoder_Init();//编码器初始化
	
	OLED_ShowString(1,1,"Speed:");//1行1列显示字符串Speed:
	
	while (1)
	{
		OLED_ShowSignedNum(1,7,Speed,5);//不断刷新显示编码器测得的最新速度
	}
}

/**
  * 函    数:TIM2中断函数
  * 参    数:无
  * 返 回 值:无
  * 注意事项:此函数为中断函数,无需调用,中断触发后自动执行
  *           函数名为预留的指定名称,可以从启动文件复制
  *           请确保函数名正确,不能有任何差异,否则中断函数将不能进入
  */
void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)		//判断是否是TIM2的更新事件触发的中断
	{
		Speed = Encoder_Get();								//每隔固定时间段读取一次编码器计数增量值,即为速度值
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);			//清除TIM2更新事件的中断标志位
															//中断标志位必须清除
															//否则中断将连续不断地触发,导致主程序卡死
	}
}

现象:接上电源后OLED显示屏显示Speed:00000 旋转编码器 这里以顺时针转为正向 Speed的值会发生改变 旋转的越快数值越大 停止旋转则数值为0  逆时针旋转则为反方向 旋转越快数值越大 往负方向增大


注:若想改变旋转方向 即逆时针转为正向 顺时针转为反向

方法一:可以将A、B相接口接线互换位置

方法二:在Encoder.c的编码器配置函数中 第三个参数或者第四个参数选择反相即可 即一个选择Falling 另一个选择Rising 若两个选择一样则还是同向输出 类似于乘法(正正得正 正负得负 负负得正)

  • 22
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值