plz, help me.. how to check the PWM input using ISR(TIMER4_CAPT_vect).

1. Type of board used in the project : Arduino Mega 2560

2. Used sensor model name : CO2 Sensor (MH-Z19B)

3. Description of the connected circuit : Connect the PWM pin to ICP4 (D49)

4. Source Code

ISR(TIMER4_CAPT_vect)
{
    if (mu08RisingEdge == TRUE)
    {
        TCNT4L = 0;
        TCNT4H = 0;
        TCCR4B &= ~(1 << ICES4);
        mu08RisingEdge = FALSE;
        digitalWrite(4, HIGH);          // I want to check if the PWM input is correct
    }
    else
    {
        TCCR4B |= (1 << ICES4);
        mu08RisingEdge = TRUE;
        mu16Low = ICR4L;
        mu16High = ICR4H;
        mu16CO2HighLevelCount = ICR4;
        digitalWrite(4, LOW);
    }
}
 

void setup()

{

    noInterrupts();
 
    pinMode(4, OUTPUT);
    pinMode(49, INPUT);
 
    // clk I/O / 1024 (prescaling, 64us) /////////////////////////
    SET_BIT(TCCR4B, CS40);
    CLEAR_BIT(TCCR4B, CS41);
    SET_BIT(TCCR4B, CS42);
    //////////////////////////////////////////////////////////////
 
    TCNT4 = 0;
    ICR4 = 0;
    mu16CO2HighLevelCount = 0;
 
    SET_BIT(TCCR4B, ICES4); // enable input capture
    SET_BIT(TIMSK4, ICIE4); // enable input capture interrupt for timer 4
 
    interrupts();
 
    Serial.begin(9600);
}
 
void loop()
{
    Serial.print(mu16CO2HighLevelCount);
}

5. Problems and Errors
The CO2 sensor sends the sensor value as PWM data.
So I tried to use Input Capture Register. Timer4 is used.

When I checked it with a logic analyzer, I checked high output for about 200ms.
I thought "mu16CO2HighLevelCount" should be about 3,125 (200,000us / 64us).
However, the value measured by the serial monitor was not 100 degrees.

I want to use ICR ..... Please check if there is a wrong part.
I can not find it even if I continue to googleing.....t.t
somebody help me....t.t

That code does not compile. Post complete code!

I thought "mu16CO2HighLevelCount" should be about 3,125 (200,000us / 64us).
However, the value measured by the serial monitor was not 100 degrees.

Please explain how 3125 and 100 degrees relate.

Thank you for your help~ :slight_smile:

Post the code.

#define TRUE  1
#define FALSE 0
#define ON    1
#define OFF   0

byte mu08RisingEdge;
int mu16Low;
int mu16High;
int mu16CO2HighLevelCount;

ISR(TIMER4_CAPT_vect)
{
    if (mu08RisingEdge == TRUE)
    {
        TCNT4L = 0;
        TCNT4H = 0;
        TCCR4B &= ~(1 << ICES4);
        mu08RisingEdge = FALSE;
        digitalWrite(4, HIGH);          // I want to check if the PWM input is correct
    }
    else
    {
        TCCR4B |= (1 << ICES4);
        mu08RisingEdge = TRUE;
        mu16Low = ICR4L;
        mu16High = ICR4H;
        mu16CO2HighLevelCount = ICR4;
        digitalWrite(4, LOW);
    }
}
 
#define SET_BIT(Reg, SetBit)      ((Reg) |= (ON << (SetBit)))
#define CLEAR_BIT(Reg, ClrBit)    ((Reg) &= ~(ON << (ClrBit)))

void setup()
{
    noInterrupts();
 
    pinMode(4, OUTPUT);
    pinMode(49, INPUT);
 
    // clk I/O / 1024 (prescaling, 64us) /////////////////////////
    SET_BIT(TCCR4B, CS40);
    CLEAR_BIT(TCCR4B, CS41);
    SET_BIT(TCCR4B, CS42);
    //////////////////////////////////////////////////////////////
 
    TCNT4 = 0;
    ICR4 = 0;
    mu16CO2HighLevelCount = 0;
 
    SET_BIT(TCCR4B, ICES4); // enable input capture
    SET_BIT(TIMSK4, ICIE4); // enable input capture interrupt for timer 4
 
    interrupts();
 
    Serial.begin(9600);
}
 
void loop()
{
    Serial.println(mu16CO2HighLevelCount);
    Serial.print(mu16High);
    Serial.print(", ");
    Serial.println(mu16Low);
    delay(1000);
}

and i explain about the 3125 and 100.

  1. i choose 1024 prescaling. (Arduino mega2560 is 16MHz)
    so i thought one count in timer4(TCNT4) was equal to 64us.

  2. the pwm waveform was measured with a logic analyzer.
    It is approximately 200ms. (i want to attach image...but i don't know how....t.t)

  3. so i thought high count(ICR4) would be displayed on the serial monitor.
    because i initialized TCNT4 in risingEdge. and I read ICR4 in fallingEdge.
    ( 200(ms) / 64(us) = 200,000(us) / 64(us) = 3,125 = 0xC35 )
    so I expected the value displayed on the serial monitor.....
    12, 53
    3125

  4. but the count displayed on the serial monitor is strange....(t.t) it was as follows.
    0, 251
    251
    ah...100 is "ICR4L" value....and now it will continue to change.
    but it's displayed as 0 ~ 250

What did I miss or wrong??

mu08RisingEdge is not initialized.

All variables used in ISRs and loop() code must be declared volatile, otherwise the compiler might optimize some stuff away.

Can you post a picture of the scope output?

I still don't see what 100 degrees are. What kind of degrees are we talking about?

        mu16Low = ICR4L;
        mu16High = ICR4H;

To get the time of the high signal you have to read TCNT4, not ICR4. ICR4 hold the number of pulses you had in the signal.

@OP

You can make a quick operational check of your CO2 sesnor based on the following strategy:

1. The cycle time of the PWM signal is known (1004 ms).

2. The ON time varies with CO2 concentration. The ON time pulse is sharply marked by a rising edge and a falling edge.

3. Connect the PWM signal with a digital pin (say, DPin-5) and with an interrupt pin (say, INT0).

4. Interrupt your MEGA by the rising edge of the PWM signal and immediately starts TC1 as Timer-1 to count 16 MHz clocking pulses from the internal oscillator.

5. After starting TC1, keep polling the logic value of DPIN-5 for LL-state. As long as LL-state is detected at DPin-5, stop TC1 and reads its counts (say, N).

6. Now, compute CO2 level from the following formula:

Cppm=2000×(TH-2ms)/(TH+TL-4ms)
TH = High level output time during cycle in ms = 625*10-7*N

If you like the above algorithm, write Arduino codes for it; test it and post it in this thread.

oh...really~? not ICR4~?

i added "mu08RisingEdge" initialization and "volatile".
but the counter value is abnormal...t.t

and i attach output files.

#define TRUE  1
#define FALSE 0
#define ON    1
#define OFF   0

volatile byte mu08RisingEdge;
volatile int mu16Low;
volatile int mu16High;
volatile int mu16CO2HighLevelCount;

ISR(TIMER4_CAPT_vect)
{
    if (mu08RisingEdge == TRUE)
    {
        TCNT4L = 0;
        TCNT4H = 0;
        TCCR4B &= ~(1 << ICES4);
        mu08RisingEdge = FALSE;
        digitalWrite(4, HIGH);          // I want to check if the PWM input is correct
    }
    else
    {
        TCCR4B |= (1 << ICES4);
        mu08RisingEdge = TRUE;
        mu16Low = TCNT4L;
        mu16High = TCNT4H;
        mu16CO2HighLevelCount = TCNT4;
        digitalWrite(4, LOW);
    }
}
 
#define SET_BIT(Reg, SetBit)      ((Reg) |= (ON << (SetBit)))
#define CLEAR_BIT(Reg, ClrBit)    ((Reg) &= ~(ON << (ClrBit)))

void setup()
{
    noInterrupts();
 
    pinMode(4, OUTPUT);
    pinMode(49, INPUT);
 
    // clk I/O / 1024 (prescaling, 64us) /////////////////////////
    SET_BIT(TCCR4B, CS40);
    CLEAR_BIT(TCCR4B, CS41);
    SET_BIT(TCCR4B, CS42);
    //////////////////////////////////////////////////////////////
 
    TCNT4 = 0;
    ICR4 = 0;
    mu08RisingEdge = TRUE;
    mu16CO2HighLevelCount = 0;
 
    SET_BIT(TCCR4B, ICES4); // enable input capture
    SET_BIT(TIMSK4, ICIE4); // enable input capture interrupt for timer 4
 
    interrupts();
 
    Serial.begin(9600);
}
 
void loop()
{
    Serial.println(mu16CO2HighLevelCount);
    Serial.print(mu16High);
    Serial.print(", ");
    Serial.println(mu16Low);
    delay(1000);
}

Serial monitor caputre.JPG

oh...really~? not ICR4~?

No, you're right, I was wrong. Read again and I also think you should read ICR4. Sorry for that.

But according to the datasheet there must be some changes to your ISR:

ISR(TIMER4_CAPT_vect)
{
    if (mu08RisingEdge == TRUE)
    {
        TCNT4L = 0;
        TCNT4H = 0;
        TCCR4B &= ~(1 << ICES4);
        CLEAR_BIT(TIFR4, ICF4);
        mu08RisingEdge = FALSE;
        digitalWrite(4, HIGH);          // I want to check if the PWM input is correct
    }
    else
    {
        mu16Low = ICR4L;
        mu16High = ICR4H;
        mu16CO2HighLevelCount = ICR4;
        TCCR4B |= (1 << ICES4);
        CLEAR_BIT(TIFR4, ICF4);
        mu08RisingEdge = TRUE;
        digitalWrite(4, LOW);
    }
}

Please report if that changes the output.