大家好,我是小政。本篇文章我将针对平衡小车电机上的编码器进行讲解。让每位小伙伴能够对编码器的硬件结构和软件编程有更加清晰的理解。

一、硬件结构

1.什么是编码器?

  编码器是一种将角位移或者角速度转换成一串电数字脉冲的旋转式传感器。编码器又分为光电编码器和霍尔编码器。

2.编码器的工作原理

  这里以霍尔编码器为主介绍(光电编码器精度比霍尔高38倍)。霍尔编码器是有霍尔马盘和霍尔元件组成。霍尔马盘是在一定直径的圆板上等分的布置有不同的磁极。霍尔马盘与电动机同轴,电动机旋转时,霍尔元件检测输出若干脉冲信号,为判断转向,一般输出两组存在一定相位差的方波信号。示意图如下:

图1 霍尔编码器

3.编码器接口引脚
  我用的电机是GM25-370直流减速电机(带霍尔编码器),其对应编码器引脚图如下:

图2 霍尔编码器接口引脚

4.编码器的测速原理
  单位时间内,根据脉冲走过的距离计算电机实际速度。

5.采集数据方式
  通常有两种方式,第一种软件技术直接采用外部中断进行采集,根据AB相位差的不同可以判断正负。第二种硬件技术直接使用定时器的编码器模式,这里采用第二种。也是大家常说的四倍频,提高测量精度的方法。其实就是把AB相的上升沿和下降沿都采集而已,所以1变4。自己使用外部中断方式实现就比较占用资源了,这里建议使用定时器的编码器模式来测量脉冲变化值。

二、软件编程

1.编码器函数——Encoder.c
1)编码器初始化函数
 入口参数:无
  ① 时钟配置
   ② GPIO配置
   ③ 定时器配置
   ④ 编码器配置
   ⑤ 输入捕获配置
   ⑥ 清除定时器溢出更新标志位
   ⑦ 定时器溢出更新
   ⑧ 定时数据清零
   ⑨ 定时器使能

这里具体讲解一下编码器配置函数

// 编码器配置函数: 定时器2,模式3,上升沿
TIM_EncoderInterfaceConfig(TIM2,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising);

① 第一个参数是定时器选择。
② 第二个参数是编码器启动模式;TIM_EncoderMode_TI12:在T1和T2的每个跳变沿均计数。
【模式】:
Tl1模式:在T1的所有边沿计数。
Tl2模式:在T2的所有边沿计数。
Tl1l2模式:在T1和T2的所有边沿计数。
简单举个例子:Tl1处于上升沿时,Tl2处在低电平,即对应Tl1FP1信号向上计数。依次类推,下图我圈起来的就是根据编码器计数操作推导出的计数模式。

图3 T1与T2极性

图4 对应计数模式

③ 第三和第四个参数是极性配置;这里分为上升沿和上升沿,为什么说设置极性变成设置上升沿和上升沿呢?通过进入stm32f10x.tim.c函数中可以查找到,设置极性实际上与TIM_CCER_CC1P这个寄存器是有关联的。我们查找STM32中文参考手册可以了解到。
  这里可以看到,我们设置为不反相,对应IC1上升沿

图5 IC极性查找

编码器函数代码

// 编码器1初始化函数
void Encoder_TIM2_Init(void)
{TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;GPIO_InitTypeDef GPIO_InitStruct;TIM_ICInitTypeDef TIM_ICInitStruct;// 1.时钟配置RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 ,ENABLE);  // 开启定时器2时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO,ENABLE); // 开启GPIO时钟// 2.GPIO配置GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;    // 浮空输入GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;   // 编码器1:PA0/PA1GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA ,&GPIO_InitStruct);   // 3.定时器配置TIM_TimeBaseStructInit(&TIM_TimeBaseInitStruct);TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;        // 不分频TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;    // 向上计数TIM_TimeBaseInitStruct.TIM_Period = 65535;                      // 重装载值65535TIM_TimeBaseInitStruct.TIM_Prescaler =0;                        // 分频系数0(自动加1)TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);// 4.编码器配置: 定时器2,模式3,上升沿TIM_EncoderInterfaceConfig(TIM2,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising);  // 5.输入捕获配置TIM_ICStructInit(&TIM_ICInitStruct);TIM_ICInitStruct.TIM_ICFilter = 10;       // 滤波器设置为10TIM_ICInit(TIM2,&TIM_ICInitStruct);// 6.清除定时器溢出更新标志位(清除计数值)TIM_ClearFlag(TIM2,TIM_FLAG_Update);// 7.定时器2,溢出更新,使能TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);  // 8.定时数据清零(输入捕获的值从0开始计数)TIM_SetCounter(TIM2,0);                   // 9.定时器2使能TIM_Cmd(TIM2,ENABLE);
}// 编码器2初始化函数
void Encoder_TIM4_Init(void)
{TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;GPIO_InitTypeDef GPIO_InitStruct;TIM_ICInitTypeDef TIM_ICInitStruct;// 1.时钟配置RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4 ,ENABLE);  // 开启定时器4时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO,ENABLE); // 开启GPIO时钟// 2.GPIO配置GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;    // 浮空输入GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;   // 编码器2:PB6/PB7GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB ,&GPIO_InitStruct);   // 3.定时器配置TIM_TimeBaseStructInit(&TIM_TimeBaseInitStruct);TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;      // 不分频TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;  // 向上计数TIM_TimeBaseInitStruct.TIM_Period = 65535;                    // 重装载值65535TIM_TimeBaseInitStruct.TIM_Prescaler = 0;                     // 分频系数0(自动加1)TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStruct);//重装载值设置为65535代表能够装载最大的脉冲数计数值// 4.编码器配置: 定时器4,模式3,上升沿TIM_EncoderInterfaceConfig(TIM4,TIM_EncoderMode_TI12,TIM_ICPolarity_Rising,TIM_ICPolarity_Rising);  // 5.输入捕获配置TIM_ICStructInit(&TIM_ICInitStruct);TIM_ICInitStruct.TIM_ICFilter = 10;       // 滤波器设置为10TIM_ICInit(TIM4,&TIM_ICInitStruct);// 6.清除定时器溢出更新标志位(清除计数值)TIM_ClearFlag(TIM4,TIM_FLAG_Update);// 7.定时器4,溢出更新,使能TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE); // 8.定时数据清零(输入捕获的值从0开始计数)TIM_SetCounter(TIM4,0);                   // 9.定时器4使能TIM_Cmd(TIM4,ENABLE);
}

2)编码器数据读取函数
 入口参数:定时器x
  ① 选择定时器x
   ② 采集编码器的计数值并保存
   ③ 将定时器的计数值清零
   ④ 返回定时器计数值
注:因为TIM_GetCounter得到的是短整型(short int),所以这里我们要进行一个强制转换。

// 编码器速度读取函数
// 入口参数:定时器
// 编码器产生的是脉冲,计数器计脉冲数(位移)
int Read_Speed(int TIMx)
{int value_1;switch(TIMx){case 2:// 单周期位移作为速度值value_1 = (short)TIM_GetCounter(TIM2);  // 采集编码器的计数值并保存TIM_SetCounter(TIM2,0);                 // 将定时器的计数值清零break;case 4:// 单周期位移作为速度值value_1 = (short)TIM_GetCounter(TIM4);  // 采集编码器的计数值并保存TIM_SetCounter(TIM4,0);                 // 将定时器的计数值清零break; default: value_1 = 0;}return value_1;
}

3)定时器中断服务函数

// 定时器2中断服务函数
void TIM2_IRQHandler()
{if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)  // 中断标志位置1{TIM_ClearITPendingBit(TIM2,TIM_IT_Update);  // 清楚中断标志位}
}// 定时器4中断服务函数
void TIM4_IRQHandler()
{if(TIM_GetITStatus(TIM4,TIM_IT_Update)==SET)  // 中断标志位置1{TIM_ClearITPendingBit(TIM4,TIM_IT_Update);  // 清楚中断标志位}
}

2.编码器函数头文件——Encoder.h

#ifdef  _ENCODER_H
#define _ENCODER_Hvoid Encoder_TIM2_Init(void);   // 编码器1初始化函数
void Encoder_TIM4_Init(void);   // 编码器2初始化函数
int Read_Speed(int TIMx);       // 编码器速度读取函数
void TIM2_IRQHandler(void);     // 定时器2中断服务函数
void TIM4_IRQHandler(void);     // 定时器4中断服务函数
#endif

  以上就是平衡小车系列文章第三讲——编码器,包括硬件结构讲解和STM32软件编程的讲解,文章中出现错误或者小伙伴对以上内容有所疑问,欢迎大家在评论区留言,小政看到后会尽快回复大家!
【平衡小车制作】(四)陀螺仪MPU6050(超详解)https://blog.csdn.net/weixin_44270218/article/details/113656398

【平衡小车制作】(三)编码器讲解(超详解)相关推荐

  1. C语言制作个人通讯录管理系统—超详解(附源码)

    之前利用C语言完成了一个关于个人通讯录管理系统的课题,主要是关于联系人的添加.查找.删除.修改.输出以及文件的写入与读出,还有一个甜点功能-模拟通话,它的实现原理也很容易理解,文章末尾会介绍到. 主框 ...

  2. 【平衡小车制作】(一)硬件原理图讲解(超详解)

      大家好,我是小政.之后的一系列文章我将介绍我玩平衡小车的过程以及遇到的一些问题,将这些内容记录下来分享给大家,也让大家少走一些弯路.接下来我将从硬件框架选择.软件编程.PID算法.PID调参.蓝牙 ...

  3. 【平衡小车制作】(七)串级PID调参及平衡成果展示(超详解)

      大家好,我是小政.本篇文章我将针对PID调参进行详细的讲解,让每位小伙伴能够对比例.积分.微分三个参数如何调节有更加清晰的理解. 一.调参步骤 确立机械中值 直立环(内环)--Kp极性.Kp大小. ...

  4. TT马达平衡小车制作

    TT马达平衡小车制作 假期无聊本来买个淘宝的寻迹小车套件,做了个寻迹小车和遥控功能.后来看到了平衡小车就想搞一个. 去搜了方案基本都是平衡小车之家的编码器电机和车模,一搜好几百,学生党不太买得起. 还 ...

  5. 平衡小车制作系列之八——总结

    文章目录 一. 前言 二. 说在最前面 2.1 模块总结 2.2 时间分配(2 Weeks In All) 2.3 一些问题 三. 收获与总结 四. 碎碎念 一. 前言 本博客原题目叫做"我 ...

  6. Android vector标签 PathData 画图超详解

    此文章来源于https://www.cnblogs.com/yuhanghzsd/p/5466846.html点击打开链接 Android vector标签 PathData 画图超详解 SVG是一种 ...

  7. python控制手机模拟器_Appium+python自动化之连接模拟器并启动淘宝APP(超详解)...

    简介 上一篇讲解完模拟器的安装.配置好以后,就好比我们手机已经买好,并且系统已经做好了,就差我们用数据线和电脑连接开始实战了,这篇宏哥就带着小伙伴们和童鞋们趁热打铁,讲解和分享一下如何连接模拟器(电脑 ...

  8. 消息队列超详解(以RabbitMQ和Kafka为例,为何使用消息队列、优缺点、高可用性、问题解决)

    消息队列超详解(以RabbitMQ和Kafka为例) 为什么要用消息队列这个东西? 先说一下消息队列的常见使用场景吧,其实场景有很多,但是比较核心的有3个:解耦.异步.削峰. 解耦:现场画个图来说明一 ...

  9. Ubuntu16.04下制作deb包的方法详解

    CSDN GitHub Ubuntu下制作deb包的方法详解 AderXCoding/system/tools/build_deb 本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可 ...

  10. python菜单怎么做_Python 城市菜单详解(超详解)

    print("--------城市查询系统---------") print("--------按数值进行查询--------") menu={"内蒙 ...

最新文章

  1. 让手机跑SOTA模型快8倍!Facebook AI开源最强全栈视频库:PyTorchVideo!
  2. bootstrap的使用
  3. linux——shell 中的变量
  4. Silverlight实用窍门系列:54.详解Silverlight中的矩阵变换MatrixTransform,实现其余各种变换【附带实例源码】...
  5. 统计学习方法第二章作业:感知机模型原始形式与对偶形式代码实现
  6. case 日期when 范围_多个日期段如何分组 case when convert等用法
  7. matlab多变量优化,matlab - Matlab使用fminsearch优化多变量 - 堆栈内存溢出
  8. vue.js开发环境搭建
  9. Exception from HRESULT: 0x800A03EC
  10. 常用adb shell命令大全
  11. android 访问本地image url_微信图片无法通过 WXSS 获取,可使用网络图片或 base64或image...
  12. Fragment与Activity
  13. 算法精解:C语言描述(递归)
  14. 网站优化众说纷纭 往左走还是往右走?
  15. 【Coverity】Jenkins 的 Synopsys Coverity plugin参数配置详解
  16. PDF提取图片(错误纠正)
  17. 一台服务器​最大并发 tcp 连接数多少?65535?
  18. 京东话费充值,点击出现的css样式
  19. k8s节点NotReady问题定位
  20. 完全用GNU/Linux工作,摈弃Windows---你我共勉

热门文章

  1. 录制GIF动画 MAC
  2. HTML5前端开发实战03-网上花店网页制作
  3. 计算机桌面放大了,电脑桌面好像被放大了,怎么处理?
  4. 三菱FX5U,机床X轴Y轴工作台定位控制程序 使用三菱J4-A系列伺服驱动器绝对位置系统,程序大小27000多步
  5. 如何用Visio画出总线(空心的箭头)如何放置粗双向可变箭头
  6. 微服务整合J2cache并改造使用
  7. Undefined class constant ‘SERIALIZER_IGBINARY‘ 解决方法
  8. android 截图root权限,为什么 Android 截屏需要 root 权限
  9. 键盘按键响应时间越快越好吗_按键响应、按键无冲、按键调速到底是怎么一回事?...
  10. MacBook如何配置环境变量