用于线上 NEC 信号输出的 Arduino 时序编程
Arduino timing programming for NEC signal output on wire
对于包含 led-strip 控制器的项目,我想用 Arduino nano 替换红外遥控器。现在在做了一些研究(http://blog.allgaiershops.com/2012/05/10/reversing-an-rgb-led-remote/)和测量后,我发现 led-strip 控制器使用 NEC 协议。
现在我尝试创建一个程序来模拟在 IR_receiver 上测量的按钮按下事件之一。但是,当测量数字引脚 2 上的输出(支持 PWM)时,时序并不接近所需。
但是,如果我在主循环的函数(high/low 输出)中使用 for 循环,则可以创建 500us 的高输出。
我想知道您是否可以给我一些支持以使代码正常运行,或者您能否给我一些关于如何有效处理 Arduino 时序的提示。
/*
Timer2_Counter_display_time_elapsed.ino
Timer2 Counter Basic Example - Demonstrates use of my Timer2_Counter, which is a timer function with 0.5us precision,
rather than 4us precision like the built-in Arduino micros() function has.
By Gabriel Staples
Visit my blog at http://electricrcaircraftguy.blogspot.com/
-My contact info is available by clicking the "Contact Me" tab at the top of my blog.
-Please support my work & contributions by buying something here: https://sites.google.com/site/ercaguystore1/
My original post containing this code can be found here: http://electricrcaircraftguy.blogspot.com/2014/02/Timer2Counter-more-precise-Arduino-micros-function.html
Written: 17 May 2014
Updated: 30 May 2014
*/
//CODE DESCRIPTION:
//This code demonstrates the use of my Timer2, which provides a more precise timer than micros().
//micros() has a precision of only 4us. However, Timer2 keeps track of time to a precision of 0.5us.
//This is especially important in my code which reads an RC receiver PWM signal, which varies from 900~2100us.
//Though this code demonstrates the use of the Timer_2 functions I have written, it does not adequately demonstrate the
//real utility of the code, so I will state the following:
//By using my Timer2 timer to measure the PWM high time interval on an RC receiver, in place of using micros(), I can get repeatable
//pulse width reads with a fluctuation of ~1us, rather than having a read-in range fluctuating by as much as +/- 4~8 us when I use micros().
//This is an increase in precision of ~8x.
//include the library
#include <eRCaGuy_Timer2_Counter.h>
#define pin_output 2
#include <time.h>
//Note: an object of this class was already pre-instantiated in the .cpp file of this library, so you can simply access its methods (functions)
// directly now through the object name "timer2"
//eRCaGuy_Timer2_Counter timer2; //this is what the pre-instantiation line from the .cpp file looks like
boolean on=false;
int values[24][32]={0};
int one_output =1680;
int zero_output= 560;
void low_output(int );
void high_output(int );
void start_protocol(int);
void end_protocol(int);
void setup() {
//configure Timer2
timer2.setup(); //this MUST be done before the other Timer2_Counter functions work; Note: since this messes up PWM outputs on pins 3 & 11, as well as
//interferes with the tone() library (http://arduino.cc/en/reference/tone), you can always revert Timer2 back to normal by calling
//timer2.unsetup()
//values[0]={8,4,1,5,8,6};
//prepare serial
Serial.begin(115200);
pinMode(pin_output, OUTPUT);
digitalWrite(pin_output, HIGH);
//Output a header of info:
/*Serial.println(F("Notes:"));
Serial.println(F("micros() has a precision of 4us"));
Serial.println(F("get_count() with unsigned long final data type has a final precision of 1us, and is fast"));
Serial.println(F("get_count() with float final data type has a final precision of 0.5us, and is not quite as fast"));
Serial.println(F("get_micros() has a precision of 0.5us, and is slower than the above 2 methods, so one of the above 2 methods is preferred"));
Serial.println(F("=============================================="));*/
}
void loop() {
//declare local variables
delay(2000);
start_protocol();
low_output(8);
high_output(4);
low_output(1);
high_output(5);
low_output(8);
high_output(6);
end_protocol();
static unsigned long t_start = timer2.get_count();
// unsigned long t_micros = micros();
unsigned long t_T2_count = timer2.get_count();
//float t_T2_micros = timer2.get_micros();
if ((t_T2_count - t_start)/2 >= 2000003)
{
digitalWrite(pin_output, HIGH);
}
} //end of loop()
void low_output(int xtimes)
{
for (int i =0 ; i<xtimes ; i++){
static unsigned long t_start = timer2.get_count(); //units of 0.5us; the count accumulated by Timer2_Counter
//acquire time stamps
unsigned long t_T2_count = timer2.get_count(); //units of 0.5us; the count accumulated by Timer2_Counter
//See if 1.000003 seconds has elapsed. If so, print out the time stamps. Note: I am using this elapsed time because I want it to NOT be divisible by 4, so that
//you can hopefully see the extra precision provided by the Timer2_Counter library, which the default Arduino micros() function does not have
if ((t_T2_count - t_start)/2 >= zero_output)//1000003) //if 1.000003 seconds has elapsed
{
t_start = t_T2_count; //update start time
if(on==false) {
digitalWrite(pin_output, LOW);
on=true;
}else {
digitalWrite(pin_output, HIGH);
on=false;
}
}
}
return;
}
void high_output(int xtimes)
{
for (int i =0 ; i<xtimes ; i++){
static unsigned long t_start = timer2.get_count();
//acquire time stamps
unsigned long t_T2_count = timer2.get_count();
if ((t_T2_count - t_start)/2 >= one_output)
{
t_start = t_T2_count; //update start time
if(on==false) {
digitalWrite(pin_output, LOW);
on=true;
}else {
digitalWrite(pin_output, HIGH);
on=false;
}
}
}
return;
}
void start_protocol(){
static unsigned long t_start = timer2.get_count();
unsigned long t_T2_count = timer2.get_count();
digitalWrite(pin_output, LOW);
t_start = timer2.get_count();
t_T2_count = timer2.get_count();
if ((t_T2_count - t_start)/2 >= 9000)
{
digitalWrite(pin_output, HIGH);
}
}
void end_protocol(){
static unsigned long t_start = timer2.get_count();
unsigned long t_T2_count = timer2.get_count();
if ((t_T2_count - t_start)/2 >= zero_output)
{
digitalWrite(pin_output, LOW);
}
t_start = timer2.get_count();
t_T2_count = timer2.get_count();
if ((t_T2_count - t_start)/2 >= 40000)
digitalWrite(pin_output, HIGH);
t_start = timer2.get_count();
t_T2_count = timer2.get_count();
if ((t_T2_count - t_start)/2 >= 9000)
{
digitalWrite(pin_output, LOW);
}
t_start = timer2.get_count();
t_T2_count = timer2.get_count();
if ((t_T2_count - t_start)/2 >= 2100)
{
digitalWrite(pin_output, HIGH);
}
t_start = timer2.get_count();
t_T2_count = timer2.get_count();
if ((t_T2_count - t_start)/2 >= zero_output)
{
digitalWrite(pin_output, LOW);
}
digitalWrite(pin_output, HIGH);
}
对于这段代码,我使用了 Gabriel Staples 制作的时序库。
[更新]
在遵循@Gmodjackass 的提示后,以下代码在引脚 2 的输出端获得了 NEC 信号。注意:可以通过添加对直接引脚端口操作的支持来优化代码。
#define pin_output 2
#include <time.h>
boolean on=false;
int values[24][32]={0};
int one_output =1680;
int zero_output= 560;
int sb=-1;
void low_output(int );
void high_output(int );
void start_protocol();
void end_protocol();
void selection_protocol(String);
void setup() {
Serial.begin(115200);
pinMode(pin_output, OUTPUT);
digitalWrite(pin_output, HIGH);
Serial.print("user_inpu: on(1) / off(0)");
delayMicroseconds(400);
}
void loop() {
//declare local variables
if (Serial.available())
{
char sb = Serial.read();
Serial.print(sb);
switch (sb){
case '0':
selection_protocol("off");
break;
case '1':
selection_protocol("on");
break;
}
sb=-1;
}
} //end of loop()
void low_output(int xtimes)
{
for (int i =0 ; i<xtimes*2 ; i++){
if(on==false) {
digitalWrite(pin_output, LOW);
on=true;
}else {
digitalWrite(pin_output, HIGH);
on=false;
}
delayMicroseconds(zero_output);
//}
}
return;
}
void high_output(int xtimes)
{
for (int i =0 ; i<xtimes*2 ; i++){
if(on==false) {
digitalWrite(pin_output, LOW);
on=true;
delayMicroseconds(zero_output);
}else {
digitalWrite(pin_output, HIGH);
on=false;
delayMicroseconds(one_output);
}
//}
}
return;
}
void start_protocol(){
digitalWrite(pin_output, LOW);
delayMicroseconds(9000);
digitalWrite(pin_output, HIGH);
delayMicroseconds(4400);
return;
}
void end_protocol(){
digitalWrite(pin_output, LOW);
delayMicroseconds(zero_output);
digitalWrite(pin_output, HIGH);
delay(40);
digitalWrite(pin_output, LOW);
delayMicroseconds(9000);
digitalWrite(pin_output, HIGH);
delayMicroseconds(2100);
digitalWrite(pin_output, LOW);
delayMicroseconds(zero_output);
digitalWrite(pin_output, HIGH);
return;
}
void selection_protocol(String user_input){
if(user_input == "on"){
Serial.print("on selected");
start_protocol();
low_output(8);
high_output(4);
low_output(1);
high_output(5);
low_output(8);
high_output(6);
end_protocol();
}
if(user_input == "off"){
Serial.print("off selected");
start_protocol();
low_output(8);
high_output(4);
low_output(1);
high_output(3);
low_output(1);
high_output(1);
low_output(6);
high_output(1);
low_output(1);
high_output(6);
end_protocol();
}
return;
}
您的一些时间差异可能是由于 arduino 的 digitalWrite 的速度非常慢造成的,也许考虑使用 direct port manipulation. Also consider using this delay function,因为它可能会导致更多的时间问题。
(已编辑拼写错误)
对于包含 led-strip 控制器的项目,我想用 Arduino nano 替换红外遥控器。现在在做了一些研究(http://blog.allgaiershops.com/2012/05/10/reversing-an-rgb-led-remote/)和测量后,我发现 led-strip 控制器使用 NEC 协议。
现在我尝试创建一个程序来模拟在 IR_receiver 上测量的按钮按下事件之一。但是,当测量数字引脚 2 上的输出(支持 PWM)时,时序并不接近所需。
但是,如果我在主循环的函数(high/low 输出)中使用 for 循环,则可以创建 500us 的高输出。
我想知道您是否可以给我一些支持以使代码正常运行,或者您能否给我一些关于如何有效处理 Arduino 时序的提示。
/*
Timer2_Counter_display_time_elapsed.ino
Timer2 Counter Basic Example - Demonstrates use of my Timer2_Counter, which is a timer function with 0.5us precision,
rather than 4us precision like the built-in Arduino micros() function has.
By Gabriel Staples
Visit my blog at http://electricrcaircraftguy.blogspot.com/
-My contact info is available by clicking the "Contact Me" tab at the top of my blog.
-Please support my work & contributions by buying something here: https://sites.google.com/site/ercaguystore1/
My original post containing this code can be found here: http://electricrcaircraftguy.blogspot.com/2014/02/Timer2Counter-more-precise-Arduino-micros-function.html
Written: 17 May 2014
Updated: 30 May 2014
*/
//CODE DESCRIPTION:
//This code demonstrates the use of my Timer2, which provides a more precise timer than micros().
//micros() has a precision of only 4us. However, Timer2 keeps track of time to a precision of 0.5us.
//This is especially important in my code which reads an RC receiver PWM signal, which varies from 900~2100us.
//Though this code demonstrates the use of the Timer_2 functions I have written, it does not adequately demonstrate the
//real utility of the code, so I will state the following:
//By using my Timer2 timer to measure the PWM high time interval on an RC receiver, in place of using micros(), I can get repeatable
//pulse width reads with a fluctuation of ~1us, rather than having a read-in range fluctuating by as much as +/- 4~8 us when I use micros().
//This is an increase in precision of ~8x.
//include the library
#include <eRCaGuy_Timer2_Counter.h>
#define pin_output 2
#include <time.h>
//Note: an object of this class was already pre-instantiated in the .cpp file of this library, so you can simply access its methods (functions)
// directly now through the object name "timer2"
//eRCaGuy_Timer2_Counter timer2; //this is what the pre-instantiation line from the .cpp file looks like
boolean on=false;
int values[24][32]={0};
int one_output =1680;
int zero_output= 560;
void low_output(int );
void high_output(int );
void start_protocol(int);
void end_protocol(int);
void setup() {
//configure Timer2
timer2.setup(); //this MUST be done before the other Timer2_Counter functions work; Note: since this messes up PWM outputs on pins 3 & 11, as well as
//interferes with the tone() library (http://arduino.cc/en/reference/tone), you can always revert Timer2 back to normal by calling
//timer2.unsetup()
//values[0]={8,4,1,5,8,6};
//prepare serial
Serial.begin(115200);
pinMode(pin_output, OUTPUT);
digitalWrite(pin_output, HIGH);
//Output a header of info:
/*Serial.println(F("Notes:"));
Serial.println(F("micros() has a precision of 4us"));
Serial.println(F("get_count() with unsigned long final data type has a final precision of 1us, and is fast"));
Serial.println(F("get_count() with float final data type has a final precision of 0.5us, and is not quite as fast"));
Serial.println(F("get_micros() has a precision of 0.5us, and is slower than the above 2 methods, so one of the above 2 methods is preferred"));
Serial.println(F("=============================================="));*/
}
void loop() {
//declare local variables
delay(2000);
start_protocol();
low_output(8);
high_output(4);
low_output(1);
high_output(5);
low_output(8);
high_output(6);
end_protocol();
static unsigned long t_start = timer2.get_count();
// unsigned long t_micros = micros();
unsigned long t_T2_count = timer2.get_count();
//float t_T2_micros = timer2.get_micros();
if ((t_T2_count - t_start)/2 >= 2000003)
{
digitalWrite(pin_output, HIGH);
}
} //end of loop()
void low_output(int xtimes)
{
for (int i =0 ; i<xtimes ; i++){
static unsigned long t_start = timer2.get_count(); //units of 0.5us; the count accumulated by Timer2_Counter
//acquire time stamps
unsigned long t_T2_count = timer2.get_count(); //units of 0.5us; the count accumulated by Timer2_Counter
//See if 1.000003 seconds has elapsed. If so, print out the time stamps. Note: I am using this elapsed time because I want it to NOT be divisible by 4, so that
//you can hopefully see the extra precision provided by the Timer2_Counter library, which the default Arduino micros() function does not have
if ((t_T2_count - t_start)/2 >= zero_output)//1000003) //if 1.000003 seconds has elapsed
{
t_start = t_T2_count; //update start time
if(on==false) {
digitalWrite(pin_output, LOW);
on=true;
}else {
digitalWrite(pin_output, HIGH);
on=false;
}
}
}
return;
}
void high_output(int xtimes)
{
for (int i =0 ; i<xtimes ; i++){
static unsigned long t_start = timer2.get_count();
//acquire time stamps
unsigned long t_T2_count = timer2.get_count();
if ((t_T2_count - t_start)/2 >= one_output)
{
t_start = t_T2_count; //update start time
if(on==false) {
digitalWrite(pin_output, LOW);
on=true;
}else {
digitalWrite(pin_output, HIGH);
on=false;
}
}
}
return;
}
void start_protocol(){
static unsigned long t_start = timer2.get_count();
unsigned long t_T2_count = timer2.get_count();
digitalWrite(pin_output, LOW);
t_start = timer2.get_count();
t_T2_count = timer2.get_count();
if ((t_T2_count - t_start)/2 >= 9000)
{
digitalWrite(pin_output, HIGH);
}
}
void end_protocol(){
static unsigned long t_start = timer2.get_count();
unsigned long t_T2_count = timer2.get_count();
if ((t_T2_count - t_start)/2 >= zero_output)
{
digitalWrite(pin_output, LOW);
}
t_start = timer2.get_count();
t_T2_count = timer2.get_count();
if ((t_T2_count - t_start)/2 >= 40000)
digitalWrite(pin_output, HIGH);
t_start = timer2.get_count();
t_T2_count = timer2.get_count();
if ((t_T2_count - t_start)/2 >= 9000)
{
digitalWrite(pin_output, LOW);
}
t_start = timer2.get_count();
t_T2_count = timer2.get_count();
if ((t_T2_count - t_start)/2 >= 2100)
{
digitalWrite(pin_output, HIGH);
}
t_start = timer2.get_count();
t_T2_count = timer2.get_count();
if ((t_T2_count - t_start)/2 >= zero_output)
{
digitalWrite(pin_output, LOW);
}
digitalWrite(pin_output, HIGH);
}
对于这段代码,我使用了 Gabriel Staples 制作的时序库。
[更新] 在遵循@Gmodjackass 的提示后,以下代码在引脚 2 的输出端获得了 NEC 信号。注意:可以通过添加对直接引脚端口操作的支持来优化代码。
#define pin_output 2
#include <time.h>
boolean on=false;
int values[24][32]={0};
int one_output =1680;
int zero_output= 560;
int sb=-1;
void low_output(int );
void high_output(int );
void start_protocol();
void end_protocol();
void selection_protocol(String);
void setup() {
Serial.begin(115200);
pinMode(pin_output, OUTPUT);
digitalWrite(pin_output, HIGH);
Serial.print("user_inpu: on(1) / off(0)");
delayMicroseconds(400);
}
void loop() {
//declare local variables
if (Serial.available())
{
char sb = Serial.read();
Serial.print(sb);
switch (sb){
case '0':
selection_protocol("off");
break;
case '1':
selection_protocol("on");
break;
}
sb=-1;
}
} //end of loop()
void low_output(int xtimes)
{
for (int i =0 ; i<xtimes*2 ; i++){
if(on==false) {
digitalWrite(pin_output, LOW);
on=true;
}else {
digitalWrite(pin_output, HIGH);
on=false;
}
delayMicroseconds(zero_output);
//}
}
return;
}
void high_output(int xtimes)
{
for (int i =0 ; i<xtimes*2 ; i++){
if(on==false) {
digitalWrite(pin_output, LOW);
on=true;
delayMicroseconds(zero_output);
}else {
digitalWrite(pin_output, HIGH);
on=false;
delayMicroseconds(one_output);
}
//}
}
return;
}
void start_protocol(){
digitalWrite(pin_output, LOW);
delayMicroseconds(9000);
digitalWrite(pin_output, HIGH);
delayMicroseconds(4400);
return;
}
void end_protocol(){
digitalWrite(pin_output, LOW);
delayMicroseconds(zero_output);
digitalWrite(pin_output, HIGH);
delay(40);
digitalWrite(pin_output, LOW);
delayMicroseconds(9000);
digitalWrite(pin_output, HIGH);
delayMicroseconds(2100);
digitalWrite(pin_output, LOW);
delayMicroseconds(zero_output);
digitalWrite(pin_output, HIGH);
return;
}
void selection_protocol(String user_input){
if(user_input == "on"){
Serial.print("on selected");
start_protocol();
low_output(8);
high_output(4);
low_output(1);
high_output(5);
low_output(8);
high_output(6);
end_protocol();
}
if(user_input == "off"){
Serial.print("off selected");
start_protocol();
low_output(8);
high_output(4);
low_output(1);
high_output(3);
low_output(1);
high_output(1);
low_output(6);
high_output(1);
low_output(1);
high_output(6);
end_protocol();
}
return;
}
您的一些时间差异可能是由于 arduino 的 digitalWrite 的速度非常慢造成的,也许考虑使用 direct port manipulation. Also consider using this delay function,因为它可能会导致更多的时间问题。
(已编辑拼写错误)