STM32F1与STM32CubeIDE编程实例-旋转编码器驱动

旋转编码器驱动

1、旋转编码器介绍

旋转编码器,也称为轴编码器,是一种机电设备,可将轴或轴的角位置或运动转换为模拟或数字输出信号。 旋转编码器主要有两种类型:绝对式和增量式。 绝对编码器的输出指示当前轴位置,使其成为角度传感器。 增量编码器的输出提供有关轴运动的信息,这些信息通常在其他地方处理成位置、速度和距离等信息。

在这里插入图片描述

旋转编码器广泛用于需要对机械系统进行监控或控制或两者兼有的应用,包括工业控制、机器人、摄影镜头、计算机输入设备(如光机械鼠标和轨迹球)、受控应力流变仪和旋转雷达平台 。

编码器技术类型如下:

  • 导电:蚀刻在 PCB 上的一系列圆周铜迹线用于对信息进行编码。 接触刷感应导电区域。 除了数字万用表中的用户输入外,这种形式的编码器现在很少见。
  • 光学:这使用通过金属或玻璃盘中的狭缝照射到光电二极管上的光。 反射版本也存在。 这是最常见的技术之一。 光学编码器对灰尘非常敏感。
  • 同轴磁:该技术通常使用附在电机轴上的特殊磁化 2 极钕磁铁。 因为它可以固定在轴的末端,所以它可以与只有一个轴伸出电机主体的电机一起使用。 准确度可以从几度变化到不到 1 度。 分辨率可以低至 1 度或高达 0.09 度(4000 CPR,每转计数)。 设计不佳的内部插值会导致输出抖动,但这可以通过内部样本平均来克服。
  • 离轴磁:该技术通常使用连接到金属轮毂的橡胶粘合铁氧体磁铁。 这为定制应用提供了设计灵活性和低成本。 由于许多离轴编码器芯片具有灵活性,它们可以被编程以接受任意数量的磁极宽度,因此可以将芯片放置在应用所需的任何位置。 磁性编码器在光学编码器无法工作的恶劣环境中运行。

旋转编码器的类型

**1).绝对编码器:**当编码器断电时,绝对编码器会保持位置信息。 编码器的位置在通电后立即可用。 编码器值与被控机械物理位置的关系在装配时设定; 系统无需返回校准点即可保持位置精度。

绝对编码器具有多个具有各种二进制权重的编码环,这些编码环提供一个数据字,表示编码器在一圈内的绝对位置。 这种类型的编码器通常被称为并行绝对编码器。

多圈绝对旋转编码器包括额外的码盘和齿轮。 高分辨率轮测量分数旋转,而低分辨率齿轮码轮记录轴的整转数。

在这里插入图片描述

2).增量编码器:增量编码器将立即报告位置变化,这在某些应用中是必不可少的功能。 但是,它不报告或跟踪绝对位置。 因此,由增量编码器监控的机械系统可能必须移动到固定参考点以初始化位置测量。

在这里插入图片描述

2、旋转编码工作原理

编码器有一个带有均匀间隔接触区的圆盘,这些接触区连接到公共引脚 C 和另外两个单独的触点引脚 A 和 B,如下图所示:

在这里插入图片描述

当圆盘开始逐步转动时,A、B 脚开始与公共端接触,相应地产生两个方波输出信号。

如果我们只计算信号的脉冲数,则两个输出中的任何一个都可用于确定旋转位置。 但是,如果我们也想确定旋转方向,我们需要同时考虑这两个信号。

在这里插入图片描述

我们可以注意到,两个输出信号的相位相差 90 度。 如果编码器顺时针旋转,输出 A 将领先于输出 B。

因此,如果我们计算每次信号变化时的步数,从高到低或从低到高,我们可以注意到此时两个输出信号具有相反的值。 反之亦然,如果编码器逆时针旋转,则输出信号具有相等的值。 基于这一点,我们可以轻松地对控制器进行编程以读取编码器位置和旋转方向。

3、旋转编码器配置

开发环境搭建、系统时钟配置、调试配置及串口配置,请参考:

STM32的定时器模式支持Encoder模式。因此,可以使用定时器的Encoder模式来驱动旋转编码器。STM32的定时器的Encoder模式配置请参考前面文章:

本次配置如下:

在这里插入图片描述

保存配置并生成代码。

4、旋转编码器驱动实现

1)驱动基本定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/\*
\* rotary\_encoder.h
\*
\* Created on: May 7, 2022
\* Author: jenson
\*/

#ifndef \_\_ENCODER\_ROTARY\_ENCODER\_H\_\_
#define \_\_ENCODER\_ROTARY\_ENCODER\_H\_\_

#include <stm32f1xx\_hal.h>
#include <stdbool.h>

typedef struct {
uint16\_t id;
TIM_HandleTypeDef \*TIMx;
GPIO_TypeDef \*SW_GPIOx;
uint16\_t SW_GPIO_Pin;
uint32\_t last_value;
} rotary\_encoder\_t;

// 初始化旋转编码设备
void rotary\_encoder\_init(rotary\_encoder\_t \*dev);
// 重置旋转编码设备
void rotary\_encoder\_reset(rotary\_encoder\_t \*dev);
// 读取当前值
uint32\_t rotary\_encoder\_read(rotary\_encoder\_t \*dev);
// 判断编码值是否改变
bool rotary\_encoder\_value\_changed(rotary\_encoder\_t\* dev);

#endif /\* \_\_ENCODER\_ROTARY\_ENCODER\_H\_\_ \*/


2)驱动定义实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/\*
\* rotary\_encoder.c
\*
\* Created on: May 7, 2022
\* Author: jenson
\*/
#include "rotary\_encoder.h"

void rotary\_encoder\_init(rotary\_encoder\_t \*dev) {
HAL\_TIM\_Encoder\_Start(dev->TIMx, TIM_CHANNEL_ALL);
dev->last_value = 0;
}

uint32\_t rotary\_encoder\_read(rotary\_encoder\_t \*dev) {
uint32\_t value = (dev->TIMx->Instance->CNT) >> 2;
dev->last_value = value;
return value;
}

bool rotary\_encoder\_value\_changed(rotary\_encoder\_t\* dev){
uint32\_t value = (dev->TIMx->Instance->CNT) >> 2;
if(dev->last_value == value){
return false;
}else{
dev->last_value = value;
return true;
}
}

void rotary\_encoder\_reset(rotary\_encoder\_t \*dev){
dev->last_value = 0;
HAL\_TIM\_Encoder\_Stop(dev->TIMx, TIM_CHANNEL_ALL);
HAL\_Delay(10);
HAL\_TIM\_Encoder\_Start(dev->TIMx, TIM_CHANNEL_ALL);
}

5、旋转编码器驱动测试

main.c文件中添加如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
/\* Private includes ----------------------------------------------------------\*/
/\* USER CODE BEGIN Includes \*/
#include <stdio.h>
#include "rotary\_encoder/rotary\_encoder.h"
/\* USER CODE END Includes \*/


/\* USER CODE BEGIN PV \*/
rotary\_encoder\_t rotary_encoder;
/\* USER CODE END PV \*/

int main(void) {
/\* USER CODE BEGIN 1 \*/

/\* 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\_USART1\_UART\_Init();
MX\_TIM2\_Init();
/\* USER CODE BEGIN 2 \*/
// HAL\_TIM\_Encoder\_Start(&htim2, TIM\_CHANNEL\_ALL);
/\* USER CODE END 2 \*/
rotary_encoder.SW_GPIOx = SW_GPIO_Port;
rotary_encoder.SW_GPIO_Pin = SW_Pin;
rotary_encoder.TIMx = &htim2;
rotary_encoder.id = 1;

rotary\_encoder\_init(&rotary_encoder);
/\* Infinite loop \*/
/\* USER CODE BEGIN WHILE \*/
while (1) {
/\* USER CODE END WHILE \*/

/\* USER CODE BEGIN 3 \*/
if (rotary\_encoder\_value\_changed(&rotary_encoder)) {
printf("rotary current value:%ld\r\n", rotary_encoder.last_value);
}
}
/\* USER CODE END 3 \*/
}

文章来源: https://iotsmart.blog.csdn.net/article/details/125660279