STM32-编码器接口测速(十七)
2026/6/8 3:17:36 网站建设 项目流程

编码器接口测速

这个接线和之前外部中断那一节的类似啊。我们把旋转编码器插在左边, VCC 和 GND 接上电源正负极。下面的 A 相输出我们接到 PA6 引脚, B 相输出接到 PA7 引脚。这里 PA6 和 PA7 两个引脚可以交换一下啊, A 相接 PA6, B 相接 PA7,或者 A 相接 PA7, B 相接 PA6,都是可以的啊,就是正转和反转的极性不一样而已。

但是 PA6 和 PA7 这两个引脚不能随便更换。我们看一下引脚定义。 PA6 和 PA7 是 TIM3 的通道一和通道二,我们计划用 TIM3 接编码器,所以需要接在 PA6 和 PA7 这两个引脚。其他定时器的话也都需要参考这个表哈,接在对应 TIM 的 CH1 和 CH2 这两个引脚的位置。

所以这里编码器的 A、 B 项我们接的是 PA6 和 PA7,这就是接线图。

先注释点代码:

第一步, RCC 开启时钟,开启 GPIO 和定时器的时钟。

第二步,配置 GPIO, 这里需要把 PA6 和 PA7 配置成输入模式啊。

第三步,配置时基单元,这里预分频器我们一般选择不分频,自动重装一般给最大 65535,只需要个 CNT 执行计数就行了。

第四步,配置输入捕获单元,不过这里输入捕获单元只有滤波器和极性这两个参数有用,后面的参数没有用到啊,与编码器无关。

第五步,配置编码器接口模式,这个直接调用个库函数。就可以了。

最后调用 TIMCMOD 启动定时器就完事了。

电路初始化完成之后, CNT 就会随着编码器旋转而自增自减。如果想要测量编码器的位置,那直接读出 CNT 的值就行了。如果想测量编码器的速度和方向,那就需要每隔一段固定的闸门时间,取出读一次 CNT, 然后再把 CNT 清 0,这样就是测频法测量速度了。

那流程看完,我们来看一下库函数。打开 TIM 点 h 的文件,在这里找一下库函数啊。本小节我们需要新学习的库函数比较少,只有这一个。 TIM_ Encoder_ Interface_ Config, 定时器编码器接口配置。

第一个参数选择定时器,第二个参数选择编码器模式,然后后面两个参数分别选择通道一和通道二的电平极性,这是配置编码器接口的函数,其他函数啊我们就不需要再了解了。

那回到这里。我们开始写代码哈。首先前面几步和之前的代码基本一样,我们还是到之前的代码复制一下哈。我们打开这个输补过的代码。

然后从这里到这里复制下来,在这个 Encoder_ Init 里面粘贴,然后逐行看一下。

然后主函数看一下,第一步开启时钟, TIM3, GPIOA 没问题。第二步 GPIO 初始化,我们使用的是 PA6 和 PA7,所以 GPIO pin 这里我们加个或 GPIO pin 7,下面初始化 GPIOA,这样就是把 PA6 和 PA7 配置成上拉输入模式。

另外这里这个 GPIO 模式可以选择上拉、下拉或者浮空。上拉和下拉如何选择呢?

我们一般可以看一下接在这个引脚的外部模块输出的默认电平,如果外部模块空闲默认输出高电平,我们就选择上拉输入,默认输入高电平。如果外部模块默认输出低电平,我们配置下拉输入,默认输入低电平。和外部模块保持默认状态一致哈,防止默认电平打架,这是上拉和下拉的选择原则啊。不过一般来说默认高电平是一个习的状态,所以一般上拉输入用的比较多。

然后如果你不确定外部模块输出的默认状态,或者外部信号输出功率非常小,这时就尽量选择浮空输入哈。浮空输入没有上拉电路和下拉电路去影响外部信号。但是缺点就是当引脚悬空时,没有默认的电平了,输入就会受噪声干扰,来回不断的跳变。这就是三种输入模式的选择原则哈,

然后继续往下看,定时器内部时钟配置,这一行就不需要了,因为编码器接口会托管时钟,编码器接口就是一个带方向控制的外部时钟啊,所以这个内部时钟就没有用了。

接下来是时区单元配置,计数器模式这个参数目前也是没有作用的哈,因为计数方向也是被编码器接口托管的。自动重装值目前还是给 65536-1,也就是满量程计数啊,这样计数的范围是最大的,而且方便换算为负数。预分频器这里改成 1-1,预分频给 0 就是不分频啊,编码器的时钟直接驱动计数器,最后初始化 TIM3,这就是死区单元部分的配置。

接着继续往下看,下一步就是输入捕获单元的配置。这里看一下 PPT 啊,可以看出输入捕获单元并没有全部使用到,编码器接口只使用了通道一和通道二的滤波器和极性选择。所以我们只需要配置这两部分的参数即可。

那在代码里,就是后面这两个参数与编码器无关啊,我们可以直接删掉。

或者你留着也行,只是它们目前没有作用。那删掉之后,目前结构体的配置是不完整的,为了防止结构体中出现不确定值可能会造成的问题啊,我们最好用 `struct` 的 `init` 给结构体赋一个初始值。加个 `struct` 的 `init` 也是提提醒一下我们哈,结构体并没有配置完整。那在定义结构体变量之后,我们来一个 TIM_ IC, struct _init,结构体初始化,把这个结构体的地址传进去,负一个初始值哈。转到定义看一下,这些就是它默认给的初始值。

然后回到这里,初始值负好之后,我们再指定,通道为一,滤波器为 0XF,电平极性为上升沿。上小节我们说过,这里的上升沿并不代表上升沿有效啊,因为编码器接口始终都是上升沿下降沿都有效。这里的上升沿参数代表的是高低电平极性不反转,也就是我们 PPT 这里演示的 TI1、 TI2 是否反向,对应通道给上升沿就是不反向,给下降沿就是反向这个意思。其实这里的这个极性参数啊,等会我们配置编码器接口的时候也有,属于重复配置了。这里这个其实也可以删掉,等会对比一下之后再删啊。

那这里参数配置好了,最后传给 ICInit 的,把这些参数写到通道一。然后还有通道二,我们复制一下这下面一部分,把通道号改成Channel二,另外两个参数可以在这里指定啊,指定好之后再调用 ICED 的,把下面这些参数写入到通道二。

整理下这一块代码的逻辑啊,就是首先定义结构体变量,然后使用 STRUCT 的给结构体赋一个初始值,再部分修改我们想要的参数,调用 ICED 的配置一遍电路。这个结构体变量的配置哈,调用 ICInit 的函数之后,就写入到硬件的寄存器了,所以 Init 了之后,这个结构体我们可以换个值继续使用,不需要重新定义新的结构体了哈。那到这里两个通道的滤波器和极性就都配置好了,这一步也就完成了。

接下来下一步,配置编码器接口。这里我们只需要调用一个函数就行了。到 TIM 点 h 里,复制这个 TIM_ EncoderInterfaceConfig 放到这里。参数第一个 TIMx 给 TIM3,剩下的转到定义看一下哈。

第二个编码器模式,可以选择下面三个参数之一。第一个 TI1 就是仅在 TI1 计数,第二个 TI2 就是仅在 TI2 计数,第三个 TI12 就是 TI1 和 TI2 都计数。对应我们 PPT 这个位置的三个模式,那我们一般使用 TI1 和 TI2 都计数哈,所以复制第三个参数。

放到这里。接着继续转到定义看一下,后两个参数就是 IC1 的极性和 IC2 的极性了,参数列表一样哈,都是 Falling 和 Rising, 选择 Rising 就是这个通道不反向,选择 Falling 就是这个通道反向,这个可以根据实际情况来配置哈

我们目前可以先选择 Rising 复制。放到这里。然后最后一个参数再给一次 Resin。 这样参数就配置好了。然后大家可以注意到哈,这里后两个参数和上面这里的两个参数是一样的,实际的效果呢确实就是一样的。这两个地方的参数啊,其实都配置的是同一个寄存器,属于重复配置的哈,后配置的参数会覆盖前面的参数。

所以我们可以把这前面这个极性的参数也删掉哈,只使用后面这个函数来配置极性。不过要注意,这时一定要保证这个 Encoder 的函数位于 ICED 的函数的下面,否则的话就是 ICED 覆盖 Encoder 函数的配置了,这个注意一下。

那现在看这个 ICED 的参数啊就只剩一个滤波器还有用了哈。好到这里我们整个电路就配置完成了。最后我们再调用一个 TIM cmd TIM3 enable 开启定时器,这样初始化配置就结束了。调用下这个 Encoder enabled 函数,编码器旋转就能控制 CNT 自增自减了。

编码器接口计次

那我们下面再写个 get 函数啊, Unit 16_ t Encoder Get void 在这里面我们暂时先直接返回 CNT 的值看看,直接 return TIM Get Counter TIM3

这样来测试一下。那我们把这两个函数都放到头文件声明一下。

向右转编码器:cnt自增,往左转cnt自减

可以看到值变成 4 了。我们这个编码器是有这个段落感的哈,每转一格,它输出的波形其实是这样的, A 相产生一个低电平脉冲, B 相产生一个相位差 90 度的脉冲,提前还是之后取决于正转还是反转。如果连续转动啊,就和我们 PPT 的波形是一样的了。那可以看出现在编码器转动一格, A、 B 相各出现了一个下降沿和上升沿,所以计次总共加了 4 次。但如果你是电机的编码器啊,那就不会有这个段落感了。然后我们继续往右转, CNT 就继续自增。往左转呢 CNT 就自减

转到0,再往左转

这就是目前我们使用 uint16_ t 数据的现象。那如果我们想要 0 之后变为 -1,就直接把 uint16_ t 类型强制转换成 int16_ t 就行了。我们试一下,你可以直接把Show num 改成Show sign num,这样就行,也是进行了类型强转。

那我们还是在这里 Get 的时候就转换一下吧。这里函数的返回值直接换成 int16_ t,然后函数声明也别忘了改一下,

在主循环里改成秀 sign number

这样就能显示负数了。我们试一下。下载看一下,现在是 0,向右转数值自增,向左转数值自减,0 之后变成负数,继续自减哈。这就是借用补码的特性快速完成负数转换的小技巧啊。

之后我们再来研究一下极性的问题。目前向右转是增,向左转是减。如果这个方向和你想要的不一致的话,可以修改一下极性。在硬件上面我们可以这样,把 A B 相两根线换一下。

之后我们再来研究一下极性的问题。目前向这样增减方向就给反过来了,试一下,现在向右是减,向左是增。

那在软件上面呢,我们可以修改这里的两个输入通道的极性,把任意一个极性反转一下,方向就会反过来。如果两个极性都反转,那极性还是保持不变哈。所以我们目前想改变极性的话,可以把第一个这里的 rising 改成 falling。

试下看看,下载,现在极性就是又反转过来了,向右是增,向左是减,这就是极性的问题。

目前我们这个代码是编码器测量位置,如果需要测量位置的话,就这样直接 get counter 就行了。

那我们继续研究,把这个线先改回来。程序这里极性也改回来。

如果我们想用这个编码器来测速的话,就可以在固定的闸门时间读一次 cnt,然后把 cnt 清零。

编码器接口测速

我们修改一下这个 Get 函数,要求读完后清零 CNT。 所以我们要定一个临时变量, int 16_7 temp。 然后呢, temp 等于 TIM_ GetCounter)( 获取 CNT。 之后, TIM_ SetCounter (TIM3,0) 给 CNT 清零。最后 return temp。 这就是读取 CNT 然后把 CNT 清零的逻辑,因为要先读取后清零,所以需要用 temp 缓存一下哈。

我们来试一下。在主循环里每隔一段时间 get 一次,所以下面可以给一个 delay 1000 毫秒。因为我们人手转比较慢哈,所以咱们时间就给 1 秒。如果你是电机飞速旋转的话,咱们时间就可以给短点哈,这样可以提高速度刷新的频率,而且防止计数器溢出哈。

我们来试一下,下载,看一下,目前 CNT 的值就代表速度,单位是脉冲个数每秒。

我们向右慢速转,速度是正数,比较小哈,快速转,正数比较大。向左慢速转,速度负数比较小,快速转负数比较大。

好,这就是我们这个用旋钮模拟编码器测速的程序现象。当然我们目前只是用这个旋钮模拟的测速哈,如果你有编码电机的话,可以实际接电机的编码器试试看,现象都是一样的哈。不过要注意把这个闸门时间弄短点,防止计数器溢出。那程序现象到现在就完成了。

不过目前我们是直接通过 `delay` 实现的闸门时间,如果你主程序没有其他东西的话,可以这样来做。但是如果有其他东西的话,最好就不要再主循环加入过长的 delay 哈,这样会阻塞主循环的执行。

那比较好的方法就是用这里留了这么久的定时中断了。我们解除注释,上面 TIM1 也解除注释啊。这里定时中断目前是每隔 1 秒执行一次,你可以修改定时中断的时间哈,来调整咱们时间。之后我们可以在前面定义一个全局变量, INT 16 位, speed.

然后在定时中断里执行 speed 等于 encoder get 每隔一秒读取一下速度存在 speed 的变量里,然后主循环就可以快速刷新显示 speed, delay 可以删掉,这样就不会阻塞主循环就行了。

然后前面的字符串我们也改成 speed 哈,下面显示数字的位置往后挪两格,改到 7 列,这样我们整个程序就完成了

我们试一下,编译。下载看一下,向右旋转正的速度,向左旋转负的速度,停止速度为 0。这就是我们最终程序的现象啊。好到这里我们这个编码器接口测速的代码就写完了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询