​​​​​​​目录

编码器简介

硬件接线图

编码器接口模式介绍

编写代码

寄存器简介

初始化代码

仿真调试

添加实时观察变量

观察变量值变化

编码器用法总结

查询方式读取

中断方式读取



编码器简介

这里使用的编码器是点位编码器,点位编码器在各种仪器上使用的比较多。它的具体参数这里就不说了。

它的外形如下:

引脚功能如下:

这里使用的是带开关功能的,所以有5个引脚,如果不带开关的话,就只有3个引脚。编码器左右两个比较大的引脚只是启固定作用的,是不带任何功能的。

上面一排的3个引脚就是编码器的信号输出引脚,中间的引脚接地线,左右两个分别是信号输出引脚。底下两个引脚主要是按键引脚,按键未按下时;两个引脚不通,当按键按下时,两个引脚导通。 这里要注意一点,上面一排引脚中的GND和下面一排的GND在编码器内部是不通的,使用时在电路板上可以将这两个引脚连通接地。

硬件接线图

硬件接线图如下:

A端子和B端子接上拉电阻到电源,这样默认情况下A、B输出的就是高电平。同时再接一个电容到地,这样可以有效滤除编码器在旋转过程中产生的抖动信号。

A端子接单片机TIM1_CH1端口,也就是PC6口。B端子接单片机TIM1_CH2端口,也就是PC7口。

编码器接口模式介绍

下面看STM8单片机中对于编码器的相关介绍

STM8对于编码器的检测使用的是定时器1的T1和T2功能,也就是使用TIM1_CH1 和 TIM1_CH2 两个引脚接入编码器的 A、B信号输入,然后在定时器内部对输入的A、B信号边沿进行捕获计数,同时可以判断出方向。

编码器的模式有3种,这个可以从TIM1_SMCR寄存器中的SMS位看出。

编码器的模式1的意思就是,根据TIM1_CH1通道输入电平信号,然后对TIM1_CH2通道的电平跳变情况计数。

编码器的模式2的意思就是,根据TIM1_CH2通道输入电平信号,然后对TIM1_CH1通道的电平跳变情况计数。

编码器的模式3的意思就是,根据TIM1_CH1通道输入电平信号,然后对TIM1_CH2通道的电平跳变情况计数,然后根据TIM1_CH2通道输入电平信号,然后对TIM1_CH1通道的电平跳变情况计数。

也就是说模式1和模式2只对其中一个信号进行计数,模式3会对两个信号都计数。模式3的计数值是,模式1和模式2的2倍。

这样理解起来比较抽象,直接通过波形图来说明。

黄色是TIM1_CH1,也就是编码器的A引脚。绿色是TIM1_CH2,也就是编码器的B引脚。

这个是顺时针旋转编码器时TIM1_CH1和TIM1_CH2的波形图,

这个是逆时针旋转编码器时TIM1_CH1和TIM1_CH2的波形图

由于编码器器外部电路中接有电容,所以波形有一个上升沿和下降沿的过程,不是标准的方波,这里为了看起来方便,所以编码器只是轻轻的拧了一下,所以只有一个脉冲。

通过波形可以看出,编码器默认情况下时高电平,当编码器旋转时,两个引脚就会输出低电平。

当顺时针旋转时,A引脚输出低电平,然后B引脚才输出低电平。A引脚的低电平持续时间比较长。

当逆时针旋转时,B引脚输出低电平,然后A引脚才会输出高电平。B引脚输出的低电平持续时间比较长。

在模式1下,当A引脚输出低电平时,计数器会对B引脚的信号变化进行计数。

在模式2下,当B引脚输出低电平时,计数器会对A引脚的信号变化进行计数。

在模式3下时,A引脚输出低电平,对B引脚信号计数。B引脚输出低电平时,对A引脚信号进行计数。

编写代码

接下来根据文档中的步骤开始编写代码

通道上面的步骤可以看到,要将定时器1设置为编码器模式时,需要设置 TIM1_CCMR1、TIM1_CCMR2、TIM1_CCER1、TIM1_SMCR、TIM1_CR1这几个寄存器。

寄存器简介

首先看TIM1_CCMR1寄存器

将CC1S 设置为 01 :CC1通道被配置为输入,IC1映射在TI1FP1上。

下面看TIM1_CCMR2寄存器

将CC2S 设置为 01 :CC2通道被配置为输入,IC2映射在TI2FP2上。

映射关系相关配置如下所示:

输入部分对相应的TIx输入信号采样,并产生一个滤波后的信号TIxF。然后,一个带极

性选择的边缘监测器产生一个信号(TIxFPx),它可以作为触发模式控制器的输入触发或者作为捕获控制。该信号通过预分频进入捕获寄存器(ICxPS)。

接下来设置TIM1_CCER1寄存器

这个寄存器的CC1P位和CC2P 位,不设置,直接使用默认值0.

接下来设置TIM1_SMCR寄存器

SMS位设置定时器的工作模式为 编码器模式3,也就是将SMS设置为 “011” 。

最后还需要设置TIM1_CR1寄存器,用来开启定时器。

将CEN位设置为1,使能计数器。

初始化代码

下面就通过代码来设置这几个寄存器

void encoder_init(void){TIM1_SMCR |= 0x03;        //编码器模式3TIM1_CCMR1 |= 0x01;       //通道位输入 CC1通道映射在 TI1FP1 上TIM2_CCMR2 |= 0x01;       //通道位输入 CC2通道映射在 TI2FP2 上TIM1_ARRH = 0xFF;        TIM1_ARRL = 0xFF;TIM1_IER |= 0x01;         //开中断TIM1_CR1 |= 0x01;         //启动计数
}

可以看到,初始化代码中多了一个对ARR寄存器的设置,这是因为在编码器模式下,计数器计数值的范围是 0 到 ARR ,如果不设置ARR寄存器,那么它的值就是默认的0。当旋转编码器的时候,计时器的值就会从0 到 0变化,这样就看不到编码器的旋转过程了。所以这里直接将ARR的值设置为最大。

文档中相关介绍如下:

仿真调试

接下来直接通过软件仿真,观察在编码器旋转的时候,计数器变化情况。

添加实时观察变量

首先启动IAR软件,进入单步调试模式。

然后选中 TIM1_CR1寄存器,单击鼠标右键,选择Add to Live Watch:‘TIM1_CR1’

将TIM1_CR1寄存器添加到实时监控的窗口中,因为TIM1_CR1寄存器中的DIR 位会显示编码器旋转的方向。这样在编码器旋转的过程中,就可以实时看到这个寄存器中值的变化情况了。

添加完成后,就会在右边Live Watch窗口中看到 TIM1_CR1寄存器了。

在TIM1_CR1寄存器上单击鼠标右键选择Binary Format格式,使用二进制格式查看TIM1_CR1寄存器的值。

接下来还需要将TIM1的计数器添加到变量观察窗口,这样在旋转编码器的时候,就可以看到计时器值变化情况了。

可是这个计数器并没有在代码中使用,要如何添加到观察窗口呢?

直接单击观察窗口里面 <click to add> 的位置

然后直接输入要观察的寄存器就行

将高8位计数器低8位计数器都输入进去,然后在寄存器上单击鼠标右键,选择Decimal Format模式,也就是通过10进制数观察。

观察变量值变化

接下来全速运行程序,然后旋转编码器。观察这几个寄存器值的变化情况。

可以看到编码器旋转一下,计数器里面的值就增加4

编码器向相反方向旋转,计数器里面的值就减4,同时TIM1_CR1寄存器第5位值变成了1.

通过芯片手册可以看到

当DIR值为0时,计数器是向上计数,当DIR值为1时,计数器是向下计数。

这里为什么编码器拧一下时,计数器值会跳变4呢?

因为这里设置的是模式3,在模式3下,计数器会对A端子和B端子的波形全部计数。

当编码器拧一次时,A和B都会出现一个低电平,总共有4次电平跳变,所以计数器就会计数4次。

所以在使用模式3时,统计编码器旋转次数时,需要用计数器的值除以4,才是真正编码器旋转的次数。

下面将编码器模式改为1,看看拧一次编码器时,计数器值变化是多少?

将编码器模式改为1之后,每次旋转编码器,计数器的值变化为2,DIR方向位的变化和模3一样。

通过上面的动态图片可以看出,当DIR值为0时,计数器的值向上增加,每次增加2.当DIR的值为1时,计数器的值向下减小,每次减小值为2.当减小到0时,计数器的值就会变为最大值,然后从最大值继续向下减小。如果向上增加时,增加到最大值后,就会自动变为0,然后继续从0开始增加。

这个现象和文档中描述的一样。

编码器用法总结

通过上面的观察就可以总结出编码器的用法:

当编码器模式设置为模式1和模式2时,编码器每旋转一次,计数器的值就加2。当编码器模式设置为模式3时,编码器每旋转一次,计数器的值就加4。这样通过读计数器的值就可判断出编码器旋转了多少下,同时通过TIM1_CR1寄存器中的DIR位就可以判断出,编码器的旋转方向。

按照这个思路,编写编码器读取代码:

查询方式读取

void encoder_init( void )
{// 在编码器1、2模式下,编码器每转一次,TIM1_CNTR 寄存器中的值跳变 2,也就是只对一组信号的边沿跳变计数// 在编码器3模式下,编码器每转一次,TIM1_CNTR 寄存器中的值跳变 4,也就是对两组信号的边沿跳变全部计数TIM1_SMCR |= 0x01;        //编码器模式1TIM1_CCMR1 |= 0x01;       //通道位输入 CC1通道映射在 TI1FP1 上TIM2_CCMR2 |= 0x01;       //通道位输入 CC2通道映射在 TI2FP2 上TIM1_ARRH = ( 60000 / 256 );  //最大溢出值  也就是编码器出现60000个跳变沿时才会 触发中断TIM1_ARRL = ( 60000 % 256 );TIM1_CNTRH = ( 30000 / 256 ); //计数器设置为中间值,这样旋转编码器的时候,计数器值就会在中间值的基础上增大或者减小TIM1_CNTRL = ( 30000 % 256 );TIM1_CR1 |= 0x01;         //启动计数}//dir为编码器旋转方向,cnt为编码器累计旋转次数,cnt在编码器相关处理代码完成之后清零。
unsigned int dir = 0, cnt = 0;
//读取计数器值  在main函数中循环调用此函数
void read_encoder( void ){static unsigned int  lastcnt = 30000;     //上次计数器值static unsigned int  nowcnt = 0;          //本次计数器值if( TIM1_CR1 & 0x10 )   //顺时针方向{dir = 1;}else                    //逆时针方向{dir = 2;}nowcnt = TIM1_CNTRH * 256 + TIM1_CNTRL ;if( nowcnt != lastcnt )                 //计数器值变化,说明编码器旋转了{if( nowcnt >= lastcnt )cnt += ( nowcnt - lastcnt ) / 2;     //在编码器1、2模式下,编码器每转一次,TIM1_CNTR 寄存器中的值跳变 2,除以2就是编码器旋转的次数elsecnt += ( lastcnt - nowcnt ) / 2;     //如果初始化中编码器的模式选择是3,在这里就要除4}lastcnt = nowcnt;
}

将ARR的值设置为60000,将CNTR的值设置为30000,这样编码器在旋转的过程中就会从30000开始往上加,或者往下减。避免了编码器在加减过程中产生溢出,方便对编码器旋转次数的计算。

在主程序中每 100ms 调用一次read_encoder函数,然后通过当前计数器中的值是否发生改变来判断编码器是否发生了旋转。计数器中的值默认为30000,如果发现当前编码器中的值不等于30000,那么就说明编码器发生了旋转,然后计算旋转的差值,然后除以2就是编码器转动的次数。这里除2是应为当前编码器工作在模式1下,如果工作在模式4下,那么就要除以4。

通过变量监控可以看出,当前计数器中的值为29982,而默认计数器中的值为30000,计数器相差了18,然后使用18除以2,可以计算出编码器旋转了9次。

这里cnt就表示编码器旋转的次数,在统计旋转此时的时候,要使用+=号来计算,而不能直接使用=号来计算。这是因为人手动在旋转编码器的过程中,中间有时候会有停顿。如果直接使用=来计算两次数据的差时,只会将最后编码器旋转的值计算进去,而中间抖动之前的值就会默认为上一次旋转的值。

这是一次连续的旋转过程。

这也是一次旋转过程,但是在转动的时候中间稍微有停留。

如图所示,这是一次旋转的过程,但是在中间稍微有停留。就会导致中间有一段时间计数器的值没有发生变化。如果每次值发生变化时,直接使用=去读取两次计数器的差,就会把上面这种认为是2次旋转,第一次旋转到来之后,程序还没做响应动作,第二次旋转又立马发生了,这样计算出来的cnt就只是第二次这一点数据变化,但实际上对应人的操作来说,这是一次旋转操作。

所以在计算cnt值时,要使用+=来累计统计旋转的次数,就会将一次旋转过程中所有的变化都累加起来。这样在用手拧编码器的时候,就算中间稍微有停留,程序也能正确的检测到。

中断方式读取

上面是通过查询的方式来计算编码器的旋转次数和方向的,这种方式实时性就会比较差,那么能不能通过中断的方式来检测编码器呢?这里的中断不是常规的IO口中断,通过边沿检测触发中断,进入中断后读取另一个端子的电平值,从而来判断方向和计数。这里可以直接使用定时器的溢出中断来检测。

通过上面的分析已经知道了,在模式1和模式2下编码器每转动一下,计数器的值就会增加2。那么在这里将ARR的值直接设置为2,那么计编码器每转动一下,计数器的值就加2,这时计时器溢出,从而触发中断。这样编码器值每变化2,就会触发一次中断。从而就能实时检测到编码器的旋转了。

下面编写中断相关代码

void encoder_init( void )
{// 在编码器1、2模式下,编码器每转一次,TIM1_CNTR 寄存器中的值跳变 2,也就是只对一组信号的边沿跳变计数// 在编码器3模式下,编码器每转一次,TIM1_CNTR 寄存器中的值跳变 4,也就是对两组信号的边沿跳变全部计数TIM1_SMCR |= 0x01;        //编码器模式1TIM1_CCMR1 |= 0x01;       //通道位输入 CC1通道映射在 TI1FP1 上TIM2_CCMR2 |= 0x01;       //通道位输入 CC2通道映射在 TI2FP2 上TIM1_ARRH = ( 2 / 256 );  //最大溢出值  也就是编码器出现多少个边沿跳变时触发一次中断TIM1_ARRL = ( 2 % 256 );  //在模式1和模式2下,这里的值设置为2,也就是编码器拧一次,就会进一次中断TIM1_IER |= 0x01;         //开中断TIM1_CR1 |= 0x01;         //启动计数
}//dir为编码器旋转方向,cnt为编码器累计旋转次数,cnt在编码器相关处理代码完成之后清零。
unsigned int dir = 0, cnt = 0;
#pragma vector  =  13                                   // IAR中的中断号,要在STVD中的中断号上加2
__interrupt void Timer1_Handle( void )                 // 定时中断
{TIM1_SR1 &= ~0x01;                               //清除更新中断标志if( TIM1_CR1 & 0x10 )   //顺时针方向{dir = 1;}else                    //逆时针方向{dir = 2;}cnt++;
}

单步调试结果如下

编码器每旋转一次,就会进入一次中断,然后在中断里面直接累加次数,cnt就代表编码器旋转的次数。dir代表旋转的方向。如果编码器模式使用的是模式3的话,那么初始化的时候ARR寄存器的值就要设置为4,因为模式3下编码器旋转一次,计数器的值增加4.

到这里对于编码器的使用就有了一个清晰的认识了。

完整工程下载代码连接: https://download.csdn.net/download/qq_20222919/21306163

STM8单片机定时器1编码器功能使用详解相关推荐

  1. 单片机控制两个步进电机画圆_基于单片机的步进电机转速控制设计详解(附程序)...

    步进电机是将电脉冲信号转变为角位移或线位移的开环控制元件.在非超载的情况下,电机的转速.停止的位置只取决于脉冲信号的频率和脉冲数,而不受负载变化的影响,即给电机加一个脉冲信号,电机则转过一个步距角.这 ...

  2. 单片机I/O开漏输出详解 “与逻辑” ,改变上拉电源的电压,提供TTL/CMOS电平输出,标准的开漏脚一般只有输出的能力添加其它的判断电路,才能具备双向输入、输出的能力

    单片机I/O开漏输出详解 开漏输出:输出端相当于三极管的集电极. 要得到高电平状态需要上拉电阻才行. 适合于做电流型的驱动,其吸收电流的能力相对强(一般20ma以内). 我们先来说说集电极开路输出的结 ...

  3. python画简单的图形的代码-Python实现画图软件功能方法详解

    概述 虽然Python的强项在人工智能,数据处理方面,但是对于日常简单的应用,Python也提供了非常友好的支持(如:Tkinter),本文主要一个简单的画图小软件,简述Python在GUI(图形用户 ...

  4. 17个之多!Windows Vista各版本功能区别详解

           Vista 开始全球同步上市,版本达17个之多.                Windows Vista各版本功能区别详解 : http://digi.it.sohu.com/2006 ...

  5. [系统安全] 四十五.APT系列(10)Metasploit后渗透技术信息收集、权限提权和功能模块详解

    您可能之前看到过我写的类似文章,为什么还要重复撰写呢?只是想更好地帮助初学者了解病毒逆向分析和系统安全,更加成体系且不破坏之前的系列.因此,我重新开设了这个专栏,准备系统整理和深入学习系统安全.逆向分 ...

  6. php怎么自定义设置打印区域,JavaScript_jQuery实现区域打印功能代码详解,使用CSS控制打印样式,需要设 - phpStudy...

    jQuery实现区域打印功能代码详解 使用CSS控制打印样式,需要设置样式media="print",并且将页面中不需要打印的元素的样式display属性设置为none.如DEMO ...

  7. android搜索功能xml,Android_Android ActionBar搜索功能用法详解,本文实例讲述了Android ActionBar - phpStudy...

    Android ActionBar搜索功能用法详解 本文实例讲述了Android ActionBar搜索功能用法.分享给大家供大家参考,具体如下: 使用ActionBar SearchView时的注意 ...

  8. python实现文本编辑器_Python实现文本编辑器功能实例详解

    这篇文章主要介绍了Python实现的文本编辑器功能,结合实例形式详细分析了基于wxpython实现文本编辑器所需的功能及相关实现技巧,需要的朋友可以参考下 本文实例讲述了Python实现的文本编辑器功 ...

  9. python画图代码大全-Python实现画图软件功能方法详解

    概述 虽然Python的强项在人工智能,数据处理方面,但是对于日常简单的应用,Python也提供了非常友好的支持(如:Tkinter),本文主要一个简单的画图小软件,简述Python在GUI(图形用户 ...

最新文章

  1. 郎凤娥谈定西实施煤粉锅炉改造项目
  2. 宇宙飞行器的几种飞行原理设计
  3. C++标准输出流对象
  4. 无线研究 破解分享
  5. GDCM:读取和打印DICOM的属性的测试程序
  6. .net core高性能通讯开源组件BeetleX
  7. 基因结构显示服务器,服务器固定结构 Server fixed structure
  8. CodeForces 230A
  9. 你离成为程序员的梦想仅一“证”之遥
  10. edge浏览器主页被360篡改如何修改?
  11. [BUUCTF]PWN——[BJDCTF 2nd]snake_dyn
  12. flex布局中的自动占满剩下的内容
  13. 如何在Nintendo Switch上进行游戏共享
  14. android旋转的列表,Android利用layer-list实现ProgressBar顺时针及逆时针旋转
  15. php北京平均工资,2016年北京平均工资是多少?
  16. Linux云计算之VSFTP服务器概述-安装vsftp服务器端、客户端
  17. 清北学堂第一题 游乐园 SmartOJ 1815(二分查找的精髓)
  18. 翻译《CSS权威指南》第3版第1章有感
  19. c语言编写的商品管理程序,C语言超市商品管理系统.docx
  20. 2019-2020“一带一路”国际滑雪系列赛-高山滑雪远东杯|滑雪计时设备|滑雪比赛计时

热门文章

  1. 多旋翼飞行器控制的难点
  2. C语言 strftime 格式化显示日期时间 时间戳
  3. 将mbr的分区改为gpt分区
  4. 指数温度20190804
  5. linux系统的空间满的问题
  6. java8 Stream2
  7. android 相对布局例子代码
  8. 软件工程概论第十三周学习进度
  9. 【计算机视觉】基于Shading Model(对光照变化一定不变性)的运动目标检测算法...
  10. DotNetTextBox编辑器