前言
有博客号快三年了,之前都是看大佬们的作品,白嫖多了开始觉得不好意思了(狗头)。现在跟大家分享一下我最近做的一个给车轮安装编码器测速计程的项目,一来跟需要这方面资料的朋友分享一下;二来把最近学习到的东西记录下来,方便以后查阅;三是希望有志同道合的朋友可以一起探讨学习,共同成长。第一次写文章,写的不好别见怪,有错误的地方欢迎指正。
1.实现目标
尽管现在市面上大部分电机都自带码盘,有其速度以及位置的反馈,但是在某些场合下,或许没有自带的码盘,或许自带码盘的精度不够高,这时就需要外加编码器来实现轮子的转速以及转过角度的测量了。本项目将介绍一种基于STM32F103C8最小系统板+欧姆龙E6A2-CW5C旋转编码器的测速计程方案。
2.硬件配置
1. STM32f103C8最小核心开发板
2. 欧姆龙旋转编码器E6A2-CW5C
3. 12v供电电池
4. 稳压模块
5. TTL-USB串口通讯模块
3.硬件连接
- 编码器AB相分别接PB.6、PB.7(对应TIM4_CH1、TIM4_CH2)
- TTL-USB串口通讯模块TxD接PA10,RxD接PA9 (USART1)
4.功能介绍
- TIM4开启编码器计数模式,计下由连着车轮的编码器发出的脉冲数
- TIM3定时,间隔固定时间进行更新计算,根据对应关系在TIM3中断服务函数算出转过角度,角速度,线速度,路 程等数据
- 串口USART1把数据定时发送给上位机
5.编码器原理
这款编码电机采用的是光电编码,在光学编码器中,黑色线条会挡住光线,透明窗口使光线穿过码盘(或者在码盘上反射)到达传感器。当传感器接收到光时,输出高电平,而当光被阻挡时,输出低电平。
LED光线通过码盘窗口,透射到另外一侧的感应器感应区域。码盘旋转,固定的感应器便可以读取到光的这种交替变化模式。
感应器所产生的波形如下图,是重复的方波,且高电平和低电平占用时间相同,因为码盘上的不透明刻线和透明窗口间隔的角度相同,一个黑线和一个透明扇区域构成一个周期,对应着波形图中的一个低电平和一个高电平,也就是一个360度电周期。
如果只有一个感应器A,那么当触发零位后,感应器A就开始计数了,每经过一个黑线和一个透明线,波形就形成一个低电平和一个高电平,也就是一个周期,但是它并不知道码盘的旋转方向,可能是逆时针旋转,也可能是顺时针旋转。
于是,为了判断码盘的旋转方向,特地引入与光电感应器A错位1/4物理周期的感应器B,如果顺时针旋转,A领先于B四分之一周期,那么逆时针旋转,A必然滞后于B四分之一周期。从而可以根据A领先于B,还是滞后于B来判断旋转方向。
参考自: 旋转编码器的原理是什么?增量式编码器和绝对式编码器有什么区别? - 罗罗日记的文章
https://zhuanlan.zhihu.com/p/163866607.
但这款欧姆龙编码器只有AB两相,没有用于零位校验的Z相,不过对于应用来说已经够用了。
6.定时器TIMx配置
以下内容参考正点原子《STM32中文参考手册》,可以去其官方论坛下载:
正点原子
对于STM32系列的单片机来说,其通用定时器都有一个增量编码器模式的功能。
也就是说,其在硬件上已经帮你搭建好了编码计数的功能:
你只需要选择好合适的定时器和IO通道,软件对定时器进行相应配置,定时器的计数器就会自动处理来自编码器的AB相脉冲了。
以下是对编码器工作模式的说明:
总的来说,判断编码器旋转方向的工作已经由内部硬件电路完成并输出于TIMx_CR1控制寄存器的DIR位了;关于软件配置一个定时器的两个输入通道为编码器模式,重点可归结为以下几点:
1. TIMx_SMCR寄存器的SMS位选择编码器模式:
这里选择SMS=011;即上下边沿同时触发,这样一个周期AB相就共有2*2=4个脉冲,达到四分频的效果(分辨率提高四倍)。
2. 输入通道 IC1、2映射到编码器接口通道 TI1、2
令TIMx_CCMR1寄存器的CC1S、CC2S位都为01。
3. 控制寄存器TIMx_CR1的CEN位置1,使能计数器
需要特别补充的是,启动定时器后,编码器模式就开始计数了,且其将计数值保存在16位无符号的计数器TIMx_CNT中:
可以通过 TIMx->CNT 直接访问读取其值,其数值范围是0 ~2^16-1 (0-65535),数值并不是很大,需要及时处理,不然很容易溢出产生错误;还有因为其数据类型是u16,没有负数,当反方向转时其数值会递减,0再减就溢出变成1111 1111 1111 1111了,故一般会将其值做强制转换处理:(short)TIMx->CNT(32位CPU的 short类型占两个字节的,包括符号位共16位)。
7.关键代码
`//选择定时器4的输入通道1、2来接编码器AB相
void encoder_TIM4_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);//使能定时器4时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//使能GPIOB时钟
/* TIM4_CH1、TIM4_CH2 对应 PB.6 PB.7 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/*配置定时器4*/
TIM_TimeBaseStructure.TIM_Period = 65535;//预装载值加满
TIM_TimeBaseStructure.TIM_Prescaler = 0;//不分频
TIM_TimeBaseStructure.TIM_ClockDivision =0 ;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12, TIM_ICPolarity_BothEdge ,TIM_ICPolarity_BothEdge); //TIMx_SMCR寄存器的SMS位选择编码器模式选择SMS=011;即上下边沿同时触发
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; //CC1S=01 输入通道 IC1、2映射到编码器接口通道 TI1、2
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_BothEdge; //双边沿触发
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //直接输入
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //不分频
TIM_ICInitStructure.TIM_ICFilter = 0x01;//配置输入滤波,不滤波
/
TIM_ICInit(TIM4, &TIM_ICInitStructure);//定时器4中断配置
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2 ;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE); //清除状态标志位
TIM_SetCounter(TIM4,0); //TIM4->CNT=0;计数器值清零
TIM_Cmd(TIM4, ENABLE); //使能TIM4
}
需要源码的朋友可以到以下链接下载:
完整源码库函数版.zip下载
8.实验结果
通过TIM3中断服务函数定时读取TIM4->CNT的值,再进行求商得出速度,积分得出路程,把数据通过串口USART1发送到上位机,转动编码器验证有以下结果:
反复正反方向转动编码器很多圈,最终所得数据圈数跟实际情况完全吻合,精度非常高。就是这款编码器有点小贵(狗头),原理都一样,小项目的话还是换一下便宜精度又够用的编码器吧(捂脸)。
参考文章
旋转编码器的原理是什么?增量式编码器和绝对式编码器有什么区别? - 罗罗日记的文章
https://zhuanlan.zhihu.com/p/163866607.