使用Arduino读取RC接收机PWM信号的三种方式

使用Arduino读取RC接收机PWM信号的三种方式

注:最近在玩模型的静改动,需要使用2.4Ghz的RC控制器来控制arduino。
在网上搜到一片使用Arduino处理PWM信号的文章,觉得不错,翻译过来,希望能帮到同好。
(翻译、总结自 https://www.benripley.com/diy/arduino/three-ways-to-read-a-pwm-signal-with-arduino/)

PWM信号通过控制脉冲的宽度实现信息的传递,常用于RC模型的舵机操控等。(注:PWM是数字信号,只有低电平和高电平,高电平持续的时间即为脉冲的宽度)
使用Arduino来处理来自RC接收机的PWM信号大致有三种方式:

  1. pulseIn() 函数
  2. 外部中断 External Interrupts
  3. 针脚电平变化中断 Pin Change Interrupts

使用pulseIn()函数

先来说说最简单的 pulseIn()函数,该函数会等待输入信号变为高电平(在value设置为HIGH的情况下,反之LOW等待低电平),并启动定时器,当输入信号再次转为低电平时结束定时器,返回高电平的持续时间,单位为毫秒(ms)。
需要补充的是,arduino可处理的脉冲时长的范围为10微秒到3分钟。
pulseIn(pin, value, timeout) 可以手动设置timeout,超过该值返回0。

byte PWM_PIN = 3; //将PWM的信号线输入到3号引脚
 
int pwm_value;
 
void setup() {
  pinMode(PWM_PIN, INPUT);//将该引脚设置为输入模式
  Serial.begin(115200);
}
 
void loop() {
  pwm_value = pulseIn(PWM_PIN, HIGH);//检测高电平
  Serial.println(pwm_value);//串口输出值
}

使用pulseIn()函数是最简单的一种方式,但缺点是在定时器等待的同时无法使用CPU, 造成执行效率低下。

使用外部中断

使用外部中断可以解决CPU无法使用的效率问题,大部分的arduino有两个外部中断,分别在digital 2和digital 3引脚。中断可由电平的改变触发。

attachInterrupt(0, risingCallback, RISING);
//0号外部中断,在外部出现电平升高时执行risingCallback()
volatile int pwm_value = 0; 
//Arduino docs 强调在有并发执行的线程(如中断)情况下,变量声明必许使用volatile
volatile int prev_time = 0;
 
void setup() {
  Serial.begin(115200);
  // 当在外部出现电平升高时执行risingCallback()
  attachInterrupt(0, risingCallback, RISING); //启用上升中断
}
 
void loop() { }
 
void risingCallback() {
  attachInterrupt(0, fallingCallback FALLING);//启用下降中断
  prev_time = micros(); //开始计时
}
 
void fallingCallback() {
  attachInterrupt(0, risingCallback, RISING);
  pwm_value = micros()-prev_time;//低电平持续时间
  Serial.println(pwm_value);
}

使用外部中断可以提升执行效率,但由于arduino的外部中断只能使用digital 2 和digital 3 针脚 限制了一些场景下的开发。

关于arduino的中断机制可参阅
关于中断(Interrupt)的详细介绍以及IIC、软串口、PinChangeInt库

使用引脚电平变化中断 Pin Change Interrupts

PCI 可以在任意arduino针脚上使用,PCI的手动实现较为复杂,可以使用 PinChangeInt 库

#include <PinChangeInt.h>
#define MY_PIN 5 // 可以使用任意针脚
 
volatile int pwm_value = 0;
volatile int prev_time = 0;
uint8_t latest_interrupted_pin;
 
void rising()
{
  latest_interrupted_pin=PCintPort::arduinoPin;
  PCintPort::attachInterrupt(latest_interrupted_pin, &falling, FALLING);
  prev_time = micros();
}
 
void falling() {
  latest_interrupted_pin=PCintPort::arduinoPin;
  PCintPort::attachInterrupt(latest_interrupted_pin, &rising, RISING);
  pwm_value = micros()-prev_time;
  Serial.println(state);
}
 
void setup() {
  pinMode(MY_PIN, INPUT); digitalWrite(MY_PIN, HIGH);
  Serial.begin(115200);
  PCintPort::attachInterrupt(MY_PIN, &rising, RISING);
}
 
void loop() { }

版权声明:本文为chen666250原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。