编码器接口测速这个接线和之前外部中断那一节的类似啊。我们把旋转编码器插在左边 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。这就是我们最终程序的现象啊。好到这里我们这个编码器接口测速的代码就写完了。