Search code examples
arduinoprotocolstiming

Arduino timing programming for NEC signal output on wire


For a project containing a led-strip controller I want to replace the IR remote with an Arduino nano. Now after doing some research (http://blog.allgaiershops.com/2012/05/10/reversing-an-rgb-led-remote/) and measurements I found that the led-strip controller uses the NEC protocol.

Now I have tried to create a program that will emulate one of the button press events as measured on the IR_receiver. However, when the output on the digital pin 2 (which is PWM supported) is measured, the timing isn't anywhere near desired.

However, if I use the for loop as written in the functions (high/low output) in the main loop it is possible to create a high output for 500us.

I was wondering if you could give me some support in order to get the code functioning, or could you give me some tips as how to handle timing on the Arduino in an efficient way.

/*
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);
}

For this this code, I used a timing library made by Gabriel Staples.

[update] After following the tips of @Gmodjackass the following code got the NEC signal on the output of pin 2. note: code can be optimized by adding support for direct pin port manipulation.

#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;
}

Solution

  • Some of your timing discrepancies may be caused by the very slow nature of arduino's digitalWrite, maybe look into using direct port manipulation. Also consider using this delay function instead since it may cause some more of your timing issues.

    (edited for spelling errors)