STM32f103 定时器之编码器接口模式
背景
买了个Arduino的旋转编码器模块,配合STM32定时器的编码器模式实现了旋转角度以及圈数的计数。这种旋转编码器我能想到的实际应用场景暂时只有实体音量旋钮,鼠标的滚轮等,所以只实现了计数。阅读Arduino关于该编码器的介绍,该编码器还可以实现旋转的速度、加速度的计算。应该算是算法层级的吧,还没做到实际应用,暂时不深究,本篇仅仅对旋转编码器的原理以及STM32编码器接口模式的配置使用方法做个简介。
正文
编码器分类:
按工作原理:光电式、磁电式和触点电刷式;
按码盘的刻孔方式:增量式和绝对式两类;
这是从网上看到一个简介,只接触过Arduino的编码器,其他暂未使用过。
Arduino的编码器属于增量式。它一共有5根线。分别为“CLK”、“DT”、“SW”、“+”、“GND”。
- “+”、“GND”:勿用多说,VCC与GND,接至板子的VCC与GND即可。
- “SW”:Arduino介绍说,当旋钮旋转完一圈时,该脚会放出一个电平跳变信号,相当于旋转编码器常说的“Z”信号,实际上我买的这个只是一个开关,即旋钮部分可以按下去(类似于汽车上的音量调节按钮),该接口会产生一个下降沿。然后由MCU去做相关处理。
- “CLK”、“DT”:在该模块上显示的丝印名称为这两个,不明白为什么是这个丝印,应该实际对应于编码器常用的“A”、“B”信号吧,这两个信号的发生方式如下:
- 正旋:如上图当旋钮开始正向旋转时,“A”从低电平变为高电平,“B”保持不变;当旋钮旋转到预定位置时,“A”维持为高电平,“B”然后跟着从低电平跳变到高电平。也就是说,正旋时,“A”总是先与“B”开始电平变化。
- 反旋:与正旋相反,“B”总是先与“A”开始电平变化。
所以在此处,丝印将该两个接线印成“CLK”、“DT”就让我有点困惑。也未找到相关资料,先暂时放放,下次有实际应用,就知道为什么了。
根据如上正旋反旋规律,就已经可以根据编码器输出的信息判断出编码器的旋转方向以及计算出其旋转角度了,具体做法如下:
将“CLk”、“DT”分别连接至MCU的任意具有外部中断的IO口,处理方式为:
- 将该两个IO口配置为双边沿外部中断。
- 当其中某个IO口检测到上升沿或者下降沿时,在中断函数内检测另一个IO口的电平状态。以正旋为例,正旋时,“A”先上升沿引起中断,得到的“A”、“B”的电平状态为“10”,紧接着,“B”上升沿,检测到“A”、“B”电平状态为“11”。
- 若一直正转,则“A”、“B”的电平状态为“10 - 11 - 01 - 00 - 10 - ...”。
- 若一直反转,则“A”、“B”的电平状态为“01 - 11 - 10 - 00 - 01 - ...”
以此,即可判断出该编码器的旋转方向,同时在“A”、“B”同时跳变完成后,即可根据编码器的旋转方向对编码器的旋转计数进行增减。
以上为使用外部中断方式处理旋转编码器的输出信息,当然,本篇要用到STM32定时器的接口模式,所以也就不会用以上的方法进行判断。那么定时器的接口模式是如何对旋转编码器进行计数的呢?
其实原理一样,将旋转编码器的“CLK(A)”、“DT(B)”脚接入到TIMx的通道,将对应通道引脚配置为编码器接口模式,使能计数,然后STM32的值就会在硬件上按照上述规对计数器的值进行加减。
本实验接到的是STM32F103的“PB6(TIM4_CH1)”、“PB7(TIM4_CH2)”,具体配置如下:
- 配置IO口:
// GPIO
// 使能对应的GPIO口时钟
RCC_APB2PeriphClockCmd(Enc_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = Enc_CLK_GPIO_PIN | Enc_DAT_GPIO_PIN | Enc_SW_GPIO_PIN;
// 该编码器模块已经做了外部上拉处理,配制成浮空输入即可
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(Enc_GPIO_PORT, &GPIO_InitStructure);
- 配置定时器基本单元:
// TIM4
// PB6 ch1 A,PB7 ch2
// TIMxCLK = 36MHZ
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
TIM_DeInit(TIM4);
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Period = 0xFF;
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision =TIM_CKD_DIV1 ;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
- 配置对应寄存器为编码器接口模式以及配置相关的输入捕获配置:
TIM_EncoderInterfaceConfig(TIM4, TIM_EncoderMode_TI12, TIM_ICPolarity_Falling, TIM_ICPolarity_Falling);
TIM_ICStructInit(&TIM_ICInitStructure);
TIM_ICInitStructure.TIM_ICFilter = 6;//ICx_FILTER;
TIM_ICInit(TIM2, &TIM_ICInitStructure);
- 清除相关中断,以及清除对应的计数器,并启动定时器:
// Clear all pending interrupts
TIM_ClearFlag(TIM4, TIM_FLAG_Update);
// 其实中断可以不用开,因为硬件自行对计数器进行加减。
TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
//Reset counter
TIM4->CNT = 0;
TIM_Cmd(TIM4, ENABLE); //启动TIM4定时器
- 如若开了中断,中断处理函数为:
void TIM4_IRQHandler(void)
{
if(TIM4->SR&0x0001)//溢出中断
{
LED_Toggle(1);
}
TIM4->SR&=~(1<<0);//清除中断标志位
}
- 主函数读取相应计数器值,并将其打印至串口:
int main(void)
{
// 读取计数器信息
Enc0Pos = TIM_GetCounter(TIM4);
// 取模2的原因是,两个引脚接到同一个定时器,每旋转一次会计数两次
Enc0Pos /= 2;
if(Enc0Pos != Enc_PinDATLast
{
Enc_PinDATLast = Enc0Pos;
printf("Position = %d\n\r", Enc0Pos);
}
}
参考文献:
"Reading Rotary Encoders Contents".
"Get Native 32Bit resolution for your encoder on STM32F4".
"STM32定时器---正交编码器模式详解".
至此,记录完毕
记录时间:2017-1-4
记录地点:深圳WZ
STM32f103 定时器之编码器接口模式的更多相关文章
- STM32伺服编码器接口
在STM32的高级定时器和一般定时器中有Encoder interface mode(编码器接口),TI1和TI2分别对应TIM_CH1 和TIM_CH2 通道. 一.计数规则如下: 表55的是编码器 ...
- STM32 HAL库学习系列第5篇 定时器TIM---编码器接口模式配置
cube基本配置,外设开启编码器,串口2 可能大家在设置的时候有这个错误 错误:error: #20: identifier "TIM_ICPOLARITY_BOTHEDGE" ...
- Dell poweredge r210进BIOS改动磁盘控制器(SATA Controller)接口模式
Dell poweredge r210进BIOS改动磁盘控制器(SATA Controller)接口模式 开机后按F2键进入BIOS设置,例如以下图: BIOS设置主界面: 使用上下键移动光标到&qu ...
- C#使用windows服务定时调用api接口
使用VS创建windows服务项目: 创建好项目 会出现一个设计界面 右键弹出对话框 选择添加安装程序 名字什么的自己可以改: 项目目录: 打开项目中的ProjectInstaller.Design ...
- 重复造轮子系列——基于Ocelot实现类似支付宝接口模式的网关
重复造轮子系列——基于Ocelot实现类似支付宝接口模式的网关 引言 重复造轮子系列是自己平时的一些总结.有的轮子依赖社区提供的轮子为基础,这里把使用过程的一些觉得有意思的做个分享.有些思路或者方法在 ...
- cocos2d-x 真正的定时器之schedule
转载请注明,原文地址:http://blog.csdn.net/musicvs/article/details/8551066 正文: 1. 不调用update函数,调用自己的函数 其实原理是一样的, ...
- 《Java设计模式》之接口模式
-----------模式是思想的体现,而非详细的实现. 抽象的讲,类的接口是类同意其它类对象訪问的方法与字段集.接口通常代表一种承诺,即方法须要实现接口方法名表示的操作,遵循代码凝视和其它文档说明. ...
- 外观模式(Facade)-子系统的协作与整合-接口模式
对子系统进行整合,对外提供更强大或更便捷的接口. 在一个模块和几个子系统进行通信时考虑. 什么是外观模式? 外观模式(Facade),为子系统中的一组接口提供一个一致的界面,定义一个高层接口,这个接口 ...
- Z从壹开始前后端分离【 .NET Core2.2/3.0 +Vue2.0 】框架之六 || API项目整体搭建 6.1 仓储+服务+抽象接口模式
本文梯子 本文3.0版本文章 前言 零.完成图中的粉色部分 2019-08-30:关于仓储的相关话题 一.创建实体Model数据层 二.设计仓储接口与其实现类 三.设计服务接口与其实现类 四.创建 C ...
随机推荐
- Wiki安装(PHP +Sqlite+Cache)
前期准备 PHP http://windows.php.net/download WinCache Extension for PHP URL:http://sourceforge.net/pro ...
- POJ 2255. Tree Recovery
Tree Recovery Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 11939 Accepted: 7493 De ...
- Java程序设计之算出一年第多少天
可以直接拷贝运行. package year; import java.util.Scanner; public class year { public static void main(String ...
- Maven :No goals have been specified for this build
Maven报错 报错信息如下:No goals have been specified for this build 解决办法:在<build></build>标签中增加 & ...
- [LeetCode] Unique Substrings in Wraparound String 封装字符串中的独特子字符串
Consider the string s to be the infinite wraparound string of "abcdefghijklmnopqrstuvwxyz" ...
- Lind.DDD.RedisClient~对StackExchange.Redis调用者的封装及多路复用技术
回到目录 两雄争霸 使用StackExchange.Redis的原因是因为它开源,免费,而对于商业化的ServiceStack.Redis,它将一步步被前者取代,开源将是一种趋势,商业化也值得被我们尊 ...
- 用vue.js学习es6(二):let和const使用
一.运行及关闭运行: 在上一节中我们用shift+右击在C:\vue\es6文件夹中打开命令行使用:npm run dev,打开了我们的vue界面. 如果要关闭则在命令行中按住ctrl+C则可以关闭. ...
- [转]Excel导入异常Cannot get a text value from a numeric cell解决
原文地址:http://blog.csdn.net/ysughw/article/details/9288307 POI操作Excel时偶尔会出现Cannot get a text value fro ...
- angularjs $emit $on $broadcast 父子 兄弟之间传值
父子之间 <div ng-controller="ParentCtrl"> <div ng-controller="ChildCtrl"> ...
- 【原创】自己动手写工具----XSmartNote [Beta 2.0]
一.前面的话 在上一篇自己动手写工具----XSmartNote中,我简单介绍了这个小玩意儿的大致界面和要实现的功能,看了一下园子里的评论,评价褒贬不一,有人说“现在那么多云笔记的工具”,“极简版ev ...