雨打灯难灭,风吹色更明。
若飞天上去,定作月边星。——李白《咏萤火》
旋转编码器是一种可以左右旋转,同时也可以按下,也可以按下旋转的器件,通过左右旋转对应着内部不同开关的导通,同时按下也可以旋转,由此看来旋转编码器可以实现很复杂的功能,简单的通过左右旋转可以调节音量、亮暗等功能,按键可以发挥普通按键的作用,按下按键的同时左右旋转又可以区别普通旋转的按键,因此可以用一个旋转编码器同时调节音量和亮暗(举例),同时也可以通过不同的转速实现不同的功能,综上所述,它的功能很复杂,用处也很多。
旋转编码器是用来测量转速并配合
PWM
技术可以实现快速调速的装置,光电式旋转编码器通过光电转换,可将输出轴的角位移、角速度等机械量转换成相应的电脉冲以数字量输出(REP)
。
- 按信号的输出类型分为: 电压输出、集电极开路输出、推拉互补输出和长线驱动输出。
- 形式分类:
有轴型
:有轴型又可分为夹紧法兰型、同步法兰型和伺服安装型等。
轴套型
:轴套型又可分为半空型、全空型和大口径型等。
以编码器工作原理可分为: 光电式、磁电式和触点电刷式。
按码盘的刻孔方式不同分类编码器可分为增量式和绝对式两类。
增量式编码器是将位移转换成周期性的电信号,再把这个电信号转变成计数脉冲,用脉冲的个数表示位移的大小。增量式编码器也称为正交编码器,是通过两个信号线的脉冲输出来进行数据处理,一个输出脉冲就对应于一个增量位移,编码器每转动一定的位移,就会产生一个脉冲信号。通过读取单位时间脉冲信号的数量,达到测量速度的效果
(v = s / t)
,通过对脉冲信号的累加,和编码器的码盘周长 (转一圈对应的距离) 便可以达到计算距离的效果(s = n * d)
。
顺时针运动 | 逆时针运动 |
---|---|
A B | A B |
1 1 | 1 1 |
0 1 | 1 0 |
0 0 | 0 0 |
1 0 | 0 1 |
我们把当前的A、B输出值保存起来,与下一个到来的A、B输出值做比较,就可以得出角度码盘转动的方向;
如果光栅格S0等于S1时,也就是S0和S1弧度夹角相同,且S2等于S0的1/2,那么可得到此次角度码盘运动位移角度为S0弧度夹角的1/2,再除以所用的时间,就得到此次角度码盘运动位移的角速度。
S0等于S1时,且S2等于S0的1/2时,1/4个运动周期就可以得到运动方向位和位移角度,如果S0不等于S1,S2不等于S0的1/2,那么要1个运动周期才可以得到运动方向位和位移角度了。
(我们用的鼠标的滚轮也是这个原理。)
实际使用的增量式编码器输出三组方波脉冲A、B和Z(有的叫C相)相。A、B两组脉冲相位差90º,可以判断出旋转方向和旋转速度。而Z相脉冲又叫做零位脉冲(有时也叫索引脉冲),为每转一周输出一个脉冲,Z相脉冲代表零位参考位,通过零位脉冲,可获得编码器的零位参考位,专门用于基准点定位,如下图所示:
增量是编码器转轴旋转时,有相应的脉冲输出,其计数起点可以任意设定,可实现多圈无限累加和测量。编码器轴转动一圈会输出固定的脉冲数,脉冲数由编码器码盘上面的光栅的线数所决定,编码器以每旋转360度提供多少通或暗的刻线称为分辨率,也称解析分度、或称作多少线,一般在每转5~10000线,当需要提高分辩率时,可利用90度相位差的A、B两路信号进行倍频或者更换高分辩率编码器。
增量型编码器精度取决于机械和电气的因素,这些因素有:光栅分度误差、光盘偏心、轴承偏心、电子读数装置引入的误差以及光学部分的不精确性,误差存在于任何编码器中。
编码器的信号输出有正弦波(电流或电压)、方波(TTL、HTL)等多种形式。并且都可以用差分驱动方式,含有对称的A+/A-、B+/B-、Z+/Z-三相信号,由于带有对称负信号的连接,电流对于电缆贡献的电磁场为0,信号稳定衰减最小,抗干扰最佳,可传输较远的距离,例如:对于TTL的带有对称负信号输出的编码器,信号传输距离可达150米。对于HTL的带有对称负信号输出的编码器,信号传输距离可达300米。
增量式编码器轴旋转时,有相应的相位输出。其旋转方向的判别和脉冲数量的增减,需借助后部的判向电路和计数器来实现。其计数起点可任意设定,并可实现多圈的无限累加和测量。还可以把每转发出一个脉冲的Z信号,作为参考机械零位。当脉冲已固定,而需要提高分辨率时,可利用带90度相位差A,B的两路信号,对原脉冲数进行倍频。
我使用的旋转编码器有五个引脚,可能还会有其他不同类型的会有不同的引脚,
这五个引脚分别为GND, VCC(+), SW, DT, CLK
,具体的引脚已经在简介的图中标明了。
VCC
:接电源正极 3.3~5V
;GND
:接地;SW
(Switch:开关):PA7(TIM3_CH2)
DT
(DT:数据):PA6(TIM3_CH1)
CLK
(CLK:时钟):PA5
(任选一个引脚:配置为上拉输入模式)接线表:
旋转编码器的接口 | STM32的IO口 | 设置的工作模式 |
---|---|---|
VCC | VCC(3.3V) | - |
GND | GND | - |
SW(Switch:开关) | PA7 | 定时器编码模式 |
DT(DT:数据) | PA6 | 定时器编码模式 |
CLK(CLK:时钟) | PA5 | GPIO_Mode_IPU(上拉输入模式) |
- | PA9 | TX(USART1 串口1通信) |
- | PA10 | RX(USART1 串口1通信) |
具体学习可以参考:博客网站-RCC学习
输入项目名称和路径。(注:路径中不允许出现中文。)
选择应用的IDE
,开发环境MDK-ARM V5
每个外设生成独立的 ’.c/.h’ 文件
main.c
GPIO
初始化代码生成在 gpio.c
中。DeBug的模式根据不同的芯片进行选择:
在main.c
文件中,添加一下代码:
fget
和fput
函数:勾选微库(这个很重要),添加头文件
;/* USER CODE BEGIN Includes */
#include
/* USER CODE END Includes */
/**
* 函数功能: 重定向c库函数printf到DEBUG_USARTx
* 输入参数: 无
* 返 回 值: 无
* 说 明:无
*/
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
return ch;
}
/**
* 函数功能: 重定向c库函数getchar,scanf到DEBUG_USARTx
* 输入参数: 无
* 返 回 值: 无
* 说 明:无
*/
int fgetc(FILE *f)
{
uint8_t ch = 0;
HAL_UART_Receive(&huart1, &ch, 1, 0xffff);
return ch;
}
#include "encoder.h"
encoder.c
中添加代码:#include "encoder.h"
uint8_t lock; //旋钮锁死标志(1表示锁死)
uint16_t count; //计数标志
uint8_t ENCODER_READ(void)
{
uint8_t key = 0; //存放按键的值
uint8_t level; //记录按钮另一端的电平值
if(HAL_GPIO_ReadPin(encoder_port_A, encoder_right) && HAL_GPIO_ReadPin(encoder_port_A, encoder_left)) //读取此时
{
lock = 0; //判断旋钮是否锁死
}
if(!HAL_GPIO_ReadPin(encoder_port_A, encoder_right) && lock == 0) //判断是否旋转按钮,同时判断是否有按钮锁死
{
HAL_Delay(100);
level = HAL_GPIO_ReadPin(encoder_port_A,encoder_left); //把旋钮另一端电平状态记录
HAL_Delay(13); //延时
if(!HAL_GPIO_ReadPin(encoder_port_A, encoder_right)) //去抖
{
if(level == 0)
{
key = 1; //右转
}
else
{
key = 2; //左转
}
count = 0; //初始锁死判断计数器
while(!HAL_GPIO_ReadPin(encoder_port_A, encoder_right) && count < 60000) //等待放开旋钮,同时累加判断锁死
{
count++;
lock = 1;
HAL_Delay(200);
}
}
}
if(!HAL_GPIO_ReadPin(encoder_port_A,encoder_down) && lock == 0)
{
HAL_Delay(20);
if(!HAL_GPIO_ReadPin(encoder_port_A,encoder_down)) //消抖
{
key=3;
}
}
return key;
}
encoder.h
中添加代码:#ifndef _ENCODER_H_
#define _ENCODER_H_
#include "main.h"
#include "tim.h"
#define encoder_port_A GPIOA
#define encoder_left GPIO_PIN_6 //定义IO接口,DT, 旋钮左转
#define encoder_right GPIO_PIN_5 //定义IO接口,CLK,旋钮右转
#define encoder_down GPIO_PIN_7 //定义IO接口,SW, 旋钮按下
uint8_t ENCODER_READ(void); //接口读取值
#endif
main.c
中添加代码:/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
int b;
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM3_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
printf("encoder is ready:\r\n");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
b = ENCODER_READ(); //读出旋转器编码器值
HAL_Delay(1000);
if(b==1)
{
printf("编码器右转\r\n");
}
if(b==2)
{
printf("编码器左转\r\n");
}
if(b==3)
{
printf("编码器按下\r\n");
}
}
/* USER CODE END 3 */
}