Arduino学习笔记(14)-- Arduino使用增量编码器测速
原理参考:https://www.arduino.cn/thread-42559-1-1.html
https://blog.csdn.net/qq_42795251/article/details/90057308
摘要:
利用Arduino、增量式光电编码器测量速度、方向、位移。
材料:
- Arduino mega 2560
- 分辨率256 ABZ相增量式编码器
- pc
编码器简介
编码器(encoder)是将信号(如比特流)或数据进行编制、转换为可用以通讯、传输和存储的信号形式的设备。编码器把角位移或直线位移转换成电信号,前者称为码盘,后者称为码尺。(对于电机测速来说应该需要将角位移转变为电信号,所以应该是码盘。)按照工作原理编码器可分为增量式和绝对式两类。增量式编码器是将位移转换成周期性的电信号,再把这个电信号转变成计数脉冲,用脉冲的个数表示位移的大小。(淘宝上搜“带编码器的电机”大多是这种类型的编码器)绝对式编码器的每一个位置对应一个确定的数字码,因此它的示值只与测量的起始和终止位置有关,而与测量的中间过程无关。(From 百度百科)
旋转编码器
旋转编码器是一种光电式旋转测量装置,它将被测的角位移直接转换成数字信号(高速脉冲信号)。编码器如以信号原理来分,有增量型编码器,绝对型编码器。我们通常用的是增量型编码器,不同型号的旋转编码器,其输出脉冲的相数也不同,有的旋转编码器输出A、B、Z三相脉冲,有的只有A、B相两相,最简单的只有A相。
- 如单相联接,用于单方向计数,单方向测速。
- A.B两相联接,用于正反向计数、判断正反向和测速。
- A、B、Z三相联接,用于带参考位修正的位置测量。
测速原理
脉冲数/(编码器分辨率*时间)
A脉冲为下降沿,B脉冲为高电平,方向为正;
A脉冲为下降沿,B脉冲为低电平,方向为负。
————————————————
测试代码
/*
* 测速原理
脉冲数/(编码器分辨率*时间)
A脉冲为下降沿,B脉冲为高电平,方向为正;
A脉冲为下降沿,B脉冲为低电平,方向为负。
*/
#include <FlexiTimer2.h> //定时器库头文件
#define PinA 2 //外部中断0
#define PinZ 3 //外部中断1
#define PinB 8 //编码器的OUT_B信号连接到数字端口8
//变量初始化
const double sampTime = 100; // 定时器周期100ms,采样频率10Hz
volatile long time1;
volatile int num = 0;//采样周期内圈数,由Z相中断计数
volatile double count = 0; // 脉冲数计数器,由A相计数
int tmp = 0; //暂存编码器计数脉冲数值
volatile float spdVal = 0; //采样周期内,返回的速度计算结果
volatile double distance = 0;
const float D = 0.30; //*的直径,单位m
const float pi = 3.141592654;//圆周率
//// Z相零位中断子程序
//void Set_state(){
// tmp = count; //暂存当前A相计数
// count = 0; //脉冲计数归零
// num +=1;
//}
// 编码器B相脉冲计数中断子程序
void Encode()
{
//为了不计入噪音干扰脉冲,
//当2次中断之间的时间大于0.1ms时,计一次有效计数
if ((millis() - time1) > 0.1)
{
//当编码器码盘的OUTA脉冲信号下跳沿每中断一次,
if ((digitalRead(PinA) == LOW) && (digitalRead(PinB) == HIGH))
{
count++;
}
else
{
count--;
}
}
time1 == millis();
}
// 编码器转速计算中断子程序
void Speed(){
spdVal = 60 * (count/256)/(sampTime/1000);
distance += (count/256) * pi * D;
count = 0;
Serial.print("The wheel has run : ");Serial.print(distance); Serial.println("m.");
Serial.print("The w_speed is: ");Serial.print(spdVal); Serial.println("r/min.");
}
void setup()
{
pinMode(PinA, INPUT_PULLUP);//因为编码器信号为欧姆龙E6B2-CWZ6C,为开漏输出,因此需要上拉电阻,此处采用arduino的内部上拉输入模式,置高
pinMode(PinB, INPUT_PULLUP);//同上
pinMode(PinZ, INPUT_PULLUP);//同上
attachInterrupt(0, Encode, FALLING);//触发脉冲中断函数:执行Encode函数,捕捉A相信号,并判断A、B相先后顺序
// attachInterrupt(1, Set_state , FALLING);//用于在捕捉到Z的零信号时,进行状态置零
FlexiTimer2::set(sampTime,Speed); //设置计时器参数以及终端子程序
FlexiTimer2::start();
Serial.begin (9600);
}
void loop()
{
}
测试结果
用手转动编码器码盘,转速测量结果如下,但可信度和准确度有待考量,请按需改进优化。
定时器中断
arduino-mega2560上要用FlexiTimer2.h,否则编译能通过,但不会产生定时中断,中断库函数在附件中查看。
#include <FlexiTimer2.h > void flash() { static boolean output = HIGH; digitalWrite(13, output); output = !output; } void setup() { pinMode(13, OUTPUT); FlexiTimer2::set(1000, flash); FlexiTimer2::start(); } void loop() { } |
变量命名
volatile (易变变量)
volatile关键字
volatile这个关键字是变量修饰符,常用在变量类型的前面,以告诉编译器和接下来的程序怎么对待这个变量。
声明一个volatile变量是编译器的一个指令。编译器是一个将你的C/C++代码转换成机器码的软件,机器码是arduino上的Atmega芯片能识别的真正指令。
具体来说,它指示编译器编译器从RAM而非存储寄存器中读取变量,存储寄存器是程序存储和操作变量的一个临时地方。在某些情况下,存储在寄存器中的变量值可能是不准确的。
如果一个变量所在的代码段可能会意外地导致变量值改变那此变量应声明为volatile,比如并行多线程等。在arduino中,唯一可能发生这种现象的地方就是和中断有关的代码段,成为中断服务程序。
例子
//当中断引脚改变状态时,开闭LED
int pin = 13;
volatile int state = LOW;
void setup()
{
pinMode(pin, OUTPUT);
attachInterrupt(0, blink, CHANGE);
}
void loop()
{
digitalWrite(pin, state);
}
void blink()
{
state = !state;
}