文章目录

  • 前言
  • 一、AB相编码器计数原理
    • 四倍频
  • 二、要用到的一些参数
    • 编码器参数
    • 电机参数
    • 轮子参数
    • 计算
  • 三、代码如下(更契合原理的代码)
    • 四倍频的代码(更新)

前言

详解编码器测速原理及实现
参考csdn


一、AB相编码器计数原理

适用于霍尔编码器和带光栅的光电编码器
编码器样子

让遮挡与透过形成高低电平的脉冲

此图为B 提前 A 90° 为反向旋转,相位差相差90°的波形就叫正交波形
当只有一个方波 只能测位置、速度,不能知道方向
而两个方波,就能一个测速,一个测方向
例:
A下降沿触发 并且B是低电平 判断反转
B下降沿触发 并且A是低电平 判断正转

四倍频

还有一种用法 叫四倍频,它是数 A相 和 B相 的上升和下降脉冲 4个信号来测速可以用来提高精度,因为一般只需要考虑A相一个上升或下降沿计数就行,这里数4个,所以叫四倍频。
stm32的编码器接口应该默认是四倍频的:
这里的大佬,说
手动转一圈的脉冲数 n:1560 个;
且用的是13线编码器 和 1:120转速比的电机,那么计算一圈的脉冲数则是
13 *(120)*4 = 1560;其中4应该就是一个周期数4个脉冲沿。这里计算后面会讲。

二、要用到的一些参数

编码器参数

编码器线数:线数就是编码器的分辨率,即转一圈发出的脉冲数
例:光电编码器:500ppr (500线)
霍尔编码器: 13ppr (13线)

电机参数

减速比 :输入转速/输出转速

一般减速比的表示方法是以1为分母,用“:”连接的输入转速和输出转速的比值,如输入转速为1500r/min,输出转速为25r/min,那么其减速比则为:i=60:1。

轮子参数

用直尺 或商家给的参数 得到
轮子直径 :65mm = 0.065m

计算

例 :500线的编码器 和30:1减速比的电机
也就是说 电机实际转30圈 但现实轮子只转一圈
那么轮子转一圈 编码器得到的脉冲数为 500*30 = 15000 (个)

速度计算思路

设 定时器设定时间为t,D为直径,则


e: 由程序得到
PI : 3.1415926
D : 0.065m
n :15000
另定时500ms
t = 0.5s
所以 speed = e * 0.00002722713 (m/s)
为了数据好显示 speed =1000* e * 0.00002722713 (mm/s)

三、代码如下(更契合原理的代码)

没有使用32的编码器接口

Enconder.c:

#include "stm32f10x.h"                  // Device header
//-32768 ~ +32767 编码器范围 定时器是16位
//这里没用stm32自带的编码器接口(TIM_EncoderInterfaceConfig) , 而是用原理写出int16_t Encoder_Count_Left;//带符号
int16_t Encoder_Count_Right;//带符号
// B接PB1  A接PB0//A下降沿触发 并且B是低电平 判断反转
//B下降沿触发 并且A是低电平 判断正转//左编码 E1B E1A
//       PB1 PB0
void Encoder_Init_Left(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);//AFIO中断引脚选择GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);//第0个线路拨到GPIOB上GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);//EXTI_InitTypeDef EXTI_InitStructure;EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1;EXTI_InitStructure.EXTI_LineCmd = ENABLE;EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //中断模式  也有事件模式EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;EXTI_Init(&EXTI_InitStructure);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;NVIC_Init(&NVIC_InitStructure);NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;NVIC_Init(&NVIC_InitStructure);
}//单位时间内读取编码器计数  变化的值
int16_t Encoder_Get_Left(void)
{int16_t Temp;Temp = Encoder_Count_Left;Encoder_Count_Left = 0;return Temp;
}//如果是9-10 中断函数  只需要将两个if并列的放在一个函数里就行了//A下降沿触发 并且B是低电平 判断反转
void EXTI0_IRQHandler(void)
{if (EXTI_GetITStatus(EXTI_Line0) == SET){if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0){Encoder_Count_Left --;}EXTI_ClearITPendingBit(EXTI_Line0);}
}
//B下降沿 A低电平 判断正转
void EXTI1_IRQHandler(void)
{if (EXTI_GetITStatus(EXTI_Line1) == SET){if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0){Encoder_Count_Left ++;}EXTI_ClearITPendingBit(EXTI_Line1);//清楚标志位}
}//右编码 E2B  E2A
//       PB11 PB10void Encoder_Init_Right(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);//AFIO中断引脚选择GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource10);//第0个线路拨到GPIOB上GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource11);//EXTI_InitTypeDef EXTI_InitStructure;EXTI_InitStructure.EXTI_Line = EXTI_Line10 | EXTI_Line11;EXTI_InitStructure.EXTI_LineCmd = ENABLE;EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //中断模式  也有事件模式EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;EXTI_Init(&EXTI_InitStructure);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;NVIC_Init(&NVIC_InitStructure);NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;NVIC_Init(&NVIC_InitStructure);}//变化的值
int16_t Encoder_Get_Right(void)
{int16_t Temp;Temp = Encoder_Count_Right;Encoder_Count_Right = 0;return Temp;
}//如果是10-15 中断函数  只需要将两个if并列的放在一个函数里就行了//因为轮子是对称转 所以镜像 相反//右编码 E2B  E2A
//       PB11 PB10//A下降沿触发 并且B是低电平 判断正转
void EXTI15_10_IRQHandler(void)
{if (EXTI_GetITStatus(EXTI_Line10) == SET){if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0){Encoder_Count_Right ++;}EXTI_ClearITPendingBit(EXTI_Line10);}B下降沿 A低电平 判断反转if (EXTI_GetITStatus(EXTI_Line11) == SET){if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_10) == 0){Encoder_Count_Right --;}EXTI_ClearITPendingBit(EXTI_Line11);//清楚标志位}
}void Get_Motor_Speed(int *LSpeed,int *RSpeed)
{static int LWheelEncoderNow   = 0;static int RWheelEncoderNow   = 0;static int LWheelEncoderLast  = 0;static int RWheelEncoderLast  = 0;   //记录本次左右编码器数据LWheelEncoderNow += Encoder_Get_Left();RWheelEncoderNow += Encoder_Get_Right();//500ms测速    (   *1000 )单位改为mm/s  变化显示更清楚*LSpeed  = (LWheelEncoderNow - LWheelEncoderLast)* 1000*0.00002722713;  *RSpeed  = (RWheelEncoderNow - RWheelEncoderLast)* 1000*0.00002722713;//记录上次编码器数据LWheelEncoderLast = LWheelEncoderNow;                    RWheelEncoderLast = RWheelEncoderNow;  }/*
static int 不管在函数内还是函数外,都作为一个全局变量可以保存它被修改以后的值。而 int 则没有这一功能,只有作为全局变量时能保存修改。放在函数内部时,每次调用都用的是一个新的数。
*/

Encoder.h

#ifndef __ENCODER_H
#define __ENCODER_H//左编码 E1B E1A
//       PB1 PB0
void Encoder_Init_Left(void);
int16_t Encoder_Get_Left(void);//右编码 E2B E1A
//       PB11 PB10
void Encoder_Init_Right(void);
int16_t Encoder_Get_Right(void);void Get_Motor_Speed(int *LSpeed,int *RSpeed);#endif

Timer.c

#include "stm32f10x.h"                  // Device header//extern uint16_t Num; //引用其他文件(主函数)的Num变量void TIM4_Timer_Init(u16 arr,u16 psc)
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);//TIM2 APB1总线上的外设TIM_InternalClockConfig(TIM4); //选择内部时钟//CK_CNT_OV = CK_PSC/(PSC+1)/(ARR+1)//预分频给少点 自动重装给多点 以一个比较高的频率计比较多的数//预分频给多点 自动重装给少点 以一个低的频率计比较少的数TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //配置时基单元TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //指定时钟分频 /1分频 影响不大TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数TIM_TimeBaseInitStructure.TIM_Period = arr; //周期 ARR自动重装器的值  10000 - 1TIM_TimeBaseInitStructure.TIM_Prescaler = psc;  //PSC预分频器的值 10k的计数频率 计10000的数  7200 - 1TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器的值   //高级定时器才有 这里通用计时器 不用TIM_TimeBaseInit(TIM4, &TIM_TimeBaseInitStructure);TIM_ClearFlag(TIM4, TIM_FLAG_Update);//手动把更新中断标志位清除一下 防止 reset直接到1TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE); //使能中断 update 更新中断NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); //优先级分组NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //3通道 //f10x.h 255行NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;NVIC_Init(&NVIC_InitStructure);TIM_Cmd(TIM4, ENABLE); //启动定时器
}/*
void TIM2_IRQHandler(void) //定时器2 的中断函数
{if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)//看更新中断标志位{Num++;TIM_ClearITPendingBit(TIM2, TIM_IT_Update);}
}
*/

Timer.h

#ifndef __TIMER_H
#define __TIMER_Hvoid TIM4_Timer_Init(u16 arr,u16 psc);#endif

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Motor.h"
#include "Encoder.h"
#include "Timer.h"uint8_t KeyNum;
int8_t Speed;
int16_t NumL;
int16_t NumR;
int LSpeedNow = 0;
int RSpeedNow = 0;int main(void)
{OLED_Init();Motor_Init_Left();Motor_Init_Right();Encoder_Init_Left();Encoder_Init_Right();TIM4_Timer_Init(5000-1,7200-1);//500ms 定时OLED_ShowString(1, 1, "VLeft:");OLED_ShowString(2, 1, "VRight:");OLED_ShowString(3, 1, "LSpd:");OLED_ShowString(4, 1, "RSpd:");while (1){//KeyNum = Key_GetNum();/*Delay_ms(1000);KeyNum = 1;if (KeyNum == 1){Speed += 20;if (Speed > 100){Speed = -100;}}*/Speed = 10;Motor_SetSpeed_Left(Speed);Motor_SetSpeed_Right(Speed);//NumL += Encoder_Get_Left();//调用这个函数的间隙里,旋转编码器产生的正负脉冲数OLED_ShowSignedNum(3, 5, LSpeedNow, 3);//NumR += Encoder_Get_Right();//调用这个函数的间隙里,旋转编码器产生的正负脉冲数OLED_ShowSignedNum(4, 5, RSpeedNow, 3);OLED_ShowSignedNum(1, 8, Speed, 3);OLED_ShowSignedNum(2, 8, Speed, 3);OLED_ShowSignedNum(4, 10, NumL, 2);}
}void TIM4_IRQHandler(void) //定时器4 的中断函数
{if (TIM_GetITStatus(TIM4, TIM_IT_Update) == SET)//看更新中断标志位{Get_Motor_Speed(&LSpeedNow,&RSpeedNow);TIM_ClearITPendingBit(TIM4, TIM_IT_Update);}
}

四倍频的代码(更新)

在原来基础上修改,这里写的四倍频不管向前还是向后转后会加加,理论上按照00 ,01 ,10,11 四倍频也能有方向,但觉得写的太烦,没写,下面的四倍频已实操过可以用来调pid,在本来的脉冲数上*4就行。

//四倍频void EXTI0_IRQHandler(void)
{if (EXTI_GetITStatus(EXTI_Line0) == SET){//if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)//{Encoder_Count_Left++;//}EXTI_ClearITPendingBit(EXTI_Line0);}
}
//B下降沿 A低电平 判断正转
void EXTI1_IRQHandler(void)
{if (EXTI_GetITStatus(EXTI_Line1) == SET){//if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)//{Encoder_Count_Left++;//}EXTI_ClearITPendingBit(EXTI_Line1);//清楚标志位}
}void EXTI15_10_IRQHandler(void)
{if (EXTI_GetITStatus(EXTI_Line10) == SET){//if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0)//{Encoder_Count_Right++;//}EXTI_ClearITPendingBit(EXTI_Line10);}B下降沿 A低电平 判断反转if (EXTI_GetITStatus(EXTI_Line11) == SET){//if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_10) == 0)//{Encoder_Count_Right++;//}EXTI_ClearITPendingBit(EXTI_Line11);//清楚标志位}
}

至此就可以在oled上显示速度了。

【一文读懂】如何用编码器测速相关推荐

  1. 独家 | 一文读懂如何用深度学习实现网络安全

    作者:Guest Blog 翻译:张玲 校对:丁楠雅 本文约4700字,建议阅读10+分钟. 本文简要介绍深度学习以及它支持的一些现有信息安全应用,并提出一个基于深度学习的TOR流量检测方案. 简介 ...

  2. 一文读懂如何用LSA、PSLA、LDA和lda2vec进行主题建模

    选自 Medium,作者:Joyce X,机器之心编译. 本文是一篇关于主题建模及其相关技术的综述.文中介绍了四种最流行的技术,用于探讨主题建模,它们分别是:LSA.pLSA.LDA,以及最新的.基于 ...

  3. 一文读懂如何用python调用matlab函数(windows环境)

    第一步配置环境: 要安装引擎 API,请在操作系统提示符下执行以下命令,其中 matlabroot 是 MATLAB 文件夹的路径.您可能需要管理员权限才能执行这些命令.或者,使用在非默认位置安装用于 ...

  4. 从实验室走向大众,一文读懂Nanopore测序技术的发展及应用

    关键词/Nanopore测序技术    文/基因慧 随着基因测序技术不断突破,二代测序的发展也将基因检测成本大幅降低.理想的测序方法,是对原始DNA模板进行直接.准确的测序,消除PCR扩增带来的偏差, ...

  5. 一文读懂序列建模(deeplearning.ai)之序列模型与注意力机制

    https://www.toutiao.com/a6663809864260649485/ 作者:Pulkit Sharma,2019年1月21日 翻译:陈之炎 校对:丁楠雅 本文约11000字,建议 ...

  6. 一文读懂AI简史:当年各国烧钱许下的愿,有些至今仍未实现

    一文读懂AI简史:当年各国烧钱许下的愿,有些至今仍未实现 导读:近日,马云.马化腾.李彦宏等互联网大佬纷纷亮相2018世界人工智能大会,并登台演讲.关于人工智能的现状与未来,他们提出了各自的观点,也引 ...

  7. 一文读懂TOF深度相机技术原理--TI-Tintin-OPT8241二次开发和应用系列--Theory Level

    一文读懂TOF深度相机技术原理--TI-Tintin-OPT8241二次开发和应用系列--Theory Level 转载请附上出处,本文链接:https://www.cnblogs.com/pans0 ...

  8. gps导航原理与应用_一文读懂角速度传感器(陀螺仪)的应用场景

    前文我们大致了解陀螺仪的来历,原理和种类,那么,它与我们的日常生活有怎样的关系呢? 陀螺仪器最早是用于航海导航,但随着科学技术的发展,它在航空和航天事业中也得到广泛的应用.陀螺仪器不仅可以作为指示仪表 ...

  9. 易基因|一文读懂精准简化基因组甲基化测序(RRBS+oxRRBS)分析怎么做

    大家好,这是专注表观组学十余年,领跑多组学科研服务的易基因. 本期,我们讲讲精准简化基因组甲基化测序(RRBS+oxRRBS)怎么做,从技术原理.建库测序流程.信息分析流程等方面详细介绍. 一.精准简 ...

最新文章

  1. 图片资源 php,php图片转为资源数据
  2. Linux工具快速上手,Linux很实用命令
  3. 涨点小姿势 奥迪TFSI前面数字是什么
  4. c++primer第五版,p134页第2题自编程序
  5. SpringBoot防止重复请求,重复表单提交超级简单的注解实现
  6. 传统蒙文字体_蒙古要改回使用传统回鹘蒙文,这是种什么文字,蒙古为什么要改回...
  7. [20150304]唯一索引与阻塞.txt
  8. 前端架构最全总结——GUI 应用程序架构的十年变迁:MVC、MVP、MVVM、Unidirectional、Clean...
  9. 什么是OpenStack
  10. 利用 SQL Monitor 查看语句运行状态步骤
  11. 【软件工程】软件测试报告——软件测试说明书
  12. 信道编码:编码FEC 前向纠错码
  13. 计算机快捷键英语,计算机快捷键(国外英语资料).doc
  14. 【测试专场沙龙报名】千万级日活App的质量保证
  15. 如何用c语言添加背景图片,如何实现在单文档的窗口背景上贴上图片?
  16. 仙人掌之歌——坑,各种坑(2)
  17. 《JavaScript 正则表达式迷你书》问世了!
  18. 《PyTorch深度学习实践》 课堂笔记 Lesson7 神经网络多维特征输入的原理推导与实现
  19. Matlab和C#混合编程
  20. 小程序项目:基于微信小程序的商城系统

热门文章

  1. 不同操作系统的TTL值
  2. 文心一言 VS 讯飞星火 VS chatgpt (27)-- 算法导论5.1 2题
  3. 南京师范大学 地图学与地理信息系统(GIS) 国家重点学科 江苏省重中之重学科 地理信息科学江苏省重点实验室...
  4. 如何向ipad上传视频(使用iTunes)
  5. 五百内的蓝牙耳机哪款好?2022年五百内超好用的蓝牙耳机推荐
  6. Grammly专业版
  7. 语音广播服务器,网络语音广播主机-文字语音转换设备 - 航天信广
  8. huggingface Tokenizers 官网文档学习:tokenizer训练保存与使用
  9. 游戏怎么这么卡?!--浅谈手游开发性能优化
  10. LeetCode 第594题——最长和谐子序列