[Closed] Timer1 interrupts not in desired interval

Go To Last Post
25 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi, guys, I really nned help, this problem has blocked me for months.

 

In my project, I need to send out IR info through infrared red light.

I used timer2 to create frame wave , and wan to use timer1 to control interval.

But, the timer1 did not trigger the interrupt at desired interval , always far bigger.

I have tried normal mode and CTC mode, all failed.

 

As the attached files showed, timer2 works stable, but never the timer1 did.

 

I don't know if I missed some configuration or something.

Thanks for any advice.

 

Attachment(s): 

This topic has a solution.
Last Edited: Thu. Sep 7, 2017 - 02:04 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I used the Arduino nano,that is atmel 328P.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well it's nice looking code but I'm not seeing the main() that pulls it all together? 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
You can call it like this:
computeMax4Send(0x1234);

sendbyTimer1();

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Xiao wrote:
You can call it like this: computeMax4Send(0x1234); sendbyTimer1();

No. Please supply a minimal but complete program that builds and runs and demonstrates thte problem.

 

Xiao wrote:
But, the timer1 did not trigger the interrupt at desired interval , always far bigger. I have tried normal mode and CTC mode, all failed.   As the attached files showed, timer2 works stable, but never the timer1 did.

For that minimal program, tell us what interval you are expecting on timer1, and what interval you are actually observing.

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK, see attached zip file.

But I did not test it on hardware,for I finish it in the office :).

 

What I want :

    send out 2 bytes per time.

    in 40KHZ infrared red light.

    base interval is 600us , '1' : 2*600us,'0' : 600us , pre-head : 4*600us ,  the space followed each digit is 600us.

    [output like this:  2400us PWM , 600us space, 1200us PWM,600us space, 600us PWM, 600us space, ... ]

                                  head                                     1                                     0                               ...

At the beginning, everything is OK, but someday, it didnot work ,till now.(after I added the IIC function and other codes).

The interval becomes far longer, bigger than 2 times of the right data.

This really confused me, I think timer interrupt should be precise, especially after I disable the interrupt in ISR inner.

In normal timer mode, you can find the difference between '1' & '0' , In CTC mode, you even can't find the difference , all is same!

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Xiao wrote:
But I did not test it on hardware
 

What did you test it on, then?

 

Xiao wrote:
At the beginning, everything is OK, but someday, it didnot work ,till now.

Does this imply that it does work now?

 

Xiao wrote:
The interval becomes far longer, bigger than 2 times of the right data.

PLEASE no relative values. Tell expected frequency in Hz. Tell observed frequency in Hz. Tell us how this was observed (oscilloscope, logic analyzer...?)

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I found some useful thing at least.

In clocks.h of AVR ASF,there are some definition for arduino:

 

#elif BOARD == ARDUINO
# define USE_PLL
# define OSC     0
# define PLL_MUL 8
#endif

#if OSC == 0
# define FOSC FOSC0 /* 12 MHz */
#else
# define FOSC FOSC1 /* 11.2896 MHz */
#endif

#ifdef USE_PLL
# define FMCK_HZ ((FOSC * (PLL_MUL + 1)) / 2)
#else
# define FMCK_HZ FOSC
#endif

#define FCPU_HZ FMCK_HZ
#define FHSB_HZ FCPU_HZ
#define FPBB_HZ FMCK_HZ
#define FPBA_HZ FMCK_HZ

 

According to those code, we should use FCPU_HZ instead of 16M(definition in arduino.cc), that is 12M.

But, I'm not sure, for there is osc (16M) on the board.

I will test tonight.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What a pity, still failed.

 

The value of speed is 16M indeed.

So, not find the reason.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Xiao wrote:
At the beginning, everything is OK, but someday, it didnot work ,till now.(after I added the IIC function and other codes).

Looks like you messing up your timer interrupt with other interrupt?
.
If working with timing precision I will avoid arduino.
.
MG

I don't know why I'm still doing this hobby

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For I want to develop a diy application, it is the normal choice to select ARDUINO as the platform.

 

As you said, the timer interrupt infected by other interrupt : the most likely one is TWI service.

I used TWI as the communication way between master & client, and I register the OnReceive interrupt, the workload is a little heavy in that function.

 

So, I will try to cancel the TWI ONReceive interrupt, use polling in the loop() instead.

Check tonight.

 

If I disable any interrupt while entering ISR, the delay() function infected too, 

I need to cancel any other interrupt to get rid of disturb?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Xiao wrote:
For I want to develop a diy application, it is the normal choice to select ARDUINO as the platform.

CVAVR evaluation do better as a starter setup and testing. Then port it to Studio for bigger project size. But maybe that's just for me? Everybody do Arduino now?
.
Xiao wrote:
If I disable any interrupt while entering ISR, the delay() function infected too, 
I need to cancel any other interrupt to get rid of disturb?

I don't really understand, if you do timing critical task you should avoid "delay" thing?
I'm not sure how that delay works in arduino but as I said I will avoid arduino for timing critical tasks.
As Johan suggestion maybe it's time for you to show minimal but complete code? So maybe arduino users can help you.
.
MG

I don't know why I'm still doing this hobby

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The main reason is hardware : ARDUINO exist for so many years, and it has many boards to select with a free IDE.

And it is so cheap.

 

I'm trying to develop a laser war DIY system, use infrared red light as the "bullet".

IR communication is the key function , but there are many other thing to do, so,delay function maybe used.

 

We can set the IR function the most high priority,

now, I don't find the right way , for after I canceled other interrupt, the output interval is still not right .

I'm sure the problem is not complex, it just hides somewhere ;)

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Xiao wrote:
I'm trying to develop

I take it as no board was created for the project which means the design still in workbench.

 

I'll suggest using RX/TX 2C chip which commonly used at RC toys. It can deliver 4ch decode/encode for RF or IR - RX/TX. So you won't be bothered by IR timing stuff.

The interface is just a high or low on the chip button input. I don't know how fast it can handle for the button press speed but I think it will be fast enough to handle the needed transmission since you need is IR.

You can create your datastream with your arduino and send it to and receive from the chip (if you need 2 ways). Just google RX2C and TX2C for datasheet.

 

Working with tight timing interrupt (i.e: 1 us) is difficult if you don't know how to calculate it clearly for your main loop task. You said your delay function was affected. That's because your ISR is tight so your MCU was too busy serving the ISR. 

 

Xiao wrote:
now, I don't find the right way , for after I canceled other interrupt, the output interval is still not right . I'm sure the problem is not complex, it just hides somewhere ;)

Well that depends on what your code doing. That's why usually the suggestion is to do the task step by step to make sure every added task will not mess up the previous "working" task.

It can be complex if you don't know how to integrate all the task.

I'll suggest you to search the tutorial section for working with multiple task for that.

 

Now it depends on you what way you choose.

 

MG

I don't know why I'm still doing this hobby

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you very much to response :)

 

This afternoon, I found one library : timeone for ARDUINO. I studied it and got some advices.

After changed the code ,the bug solved, though there is still some other bugs.

And the reason is very simple : count register for timer1 is 2 bytes, it need 2 steps to apply value to it: high byte and low byte.

So, atomic operation needed :

Wrong:

   TCNT1 = xxxxxx;

Right:

   oldSREG = SREG;

   cli();

   TCNT1 = xxxxxx;

   SREG = oldSREG;

 

of course, some other codes optimised too.

 

Last Edited: Thu. Aug 31, 2017 - 12:34 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just google RX2C and TX2C for datasheet.

 

I'l study it later.

Thank you very much.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

MicroGyro wrote:
usually the suggestion is to do the task step by step to make sure every added task will not mess up the previous "working" task.

Absolutely.

 

And always make sure that you keep copies of the previous "working" task - so that you can go back when you break something.

 

The proper was to do this is with a Revision Control aka Version Control tool - eg, Git, SVN - but simply taking a copy can be sufficient for a simple, 1-person project.

 

#StepByStep

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You are right.

Actually, I make copy after each modification (each day).

When this bug raised, I thought about the design carefully, and decide to change the mode to timer interrupt.

For the interval is really big : 600us at least.

328P can do much work within this time period. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As the picture attached before, the first wave is too long.

 

I monitored the data last sunday , the result is unbelievable :)

 

I set the start value for TCNT1 in atomic operation way, after that,I print out the value of TCNT1, it is not the data I set.

e.g.

     oldSREG = SREG;

     cli();

     TCNT1 = 3333;

     SREG = oldSREG;

     Serial.println(TCNT1);

 

Actually, the value under 256, looks like operating as 8 bits timer.

And the most interesting thing  is : it counting down! and frozen at some value(2,4 as I watched)

I have set the mode as normal mode :

 

void IRsender::sendByTimer1(){
    unsigned int tcnt1;
  
    TIMSK1 &= ~_BV(TOIE1);         // AR added
    TCCR1A = 0;
    TCCR1B = 0;                    //stop counter

    data_idx = 0;

 

    oldSREG = SREG;                // AR - save status register
    noInterrupts();                // AR - Disable interrupts
    TCNT1 = array_data_2_send[data_idx];                  
    SREG = oldSREG;                // AR - Restore status register

 

    TIFR1 |= (1<<TOV1);            //clear pending interrupt flag
    TCCR1B |= (1<<CS10);           //start count,normal mode
    IR_SEND_PWM_START;             //start framewave output
    TIMSK1 |= (1<<TOIE1);          //enable overflow interrupt
    
    while (tcnt1==array_data_2_send[data_idx]){       // //if the timer has not ticked yet ?????????
        oldSREG = SREG;
        noInterrupts();
        tcnt1 = TCNT1;
        SREG = oldSREG;
    } 
    
    array_data_2_send_millis_start[data_idx] = micros(); //debug
    array_data_2_send_debug[data_idx] = 1;
}

 

what's going on?!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I setup a little test project, init in setup() and set value for TCNT1 and monitor it ,it works well.

 

So other codes affected the timer1.

But what can do this?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

From post #5:

JohanEkdahl wrote:
Please supply a minimal but complete program that builds and runs and demonstrates the problem.

You ignored what he suggested and post cut and paste code which I believe confusing everybody who trying to help you.

Xiao wrote:

I setup a little test project, init in setup() and set value for TCNT1 and monitor it ,it works well.

 

So other codes affected the timer1.

But what can do this?

Now you want everybody guess what you did in your code. 

 

I believe your code is very top secret so I will simply ignored what you're doing in your code. That's your own problem now.

 

Instead, I will suggest a more simple way rather than trying to re-inventing wheel and complicated simple thing.

 

Here's the simple schematic for IR comm using UART as communication protocol.

You can get an echo of your transmitted data using amplified IR RX (google it) to see if you transmitted data correctly.

The number of IR TX LED and the current calculation is just an example, do as you need.

 

In here I use INT0 to turn the Timer0 on/off according to UART data send. Timer 0 is set to 40khz for IR TX carrier.

So it's simply a UART to IR tranceiver.

Go ahead if you have a better idea how to turn the timer on/off.

 

Here's the INT0 ISR code skeleton:

ISR(INT0_vect)
{
  if(falling edge) 
  {
   turn on timer0;   
   set to rising edge;
  }
  else
  {
   turn off timer0;   
   set to falling edge;   
  }
}

That way you don't need to do complicated "timing" thing like what you did. Surely there's another way but this idea was popped in my mind and tested on workbench.

Again it's just a suggestion and I hope others also can get benefit from it.

 

MG

I don't know why I'm still doing this hobby

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have posted it before (#6).

 

Thanks for your advice to use UART port, new direction for me.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Xiao wrote:
the result is unbelievable :)
Xiao wrote:
what's going on?!

I've lost track in this thread.  But why "unbelievable"?

 

Xiao wrote:
oldSREG = SREG; cli(); TCNT1 = 3333; SREG = oldSREG; Serial.println(TCNT1);

"Atomic"?  Yes, the setting of TCNT1.  But then you re-enable interrupts before "reading back" TCNT1.  It appears to me that there is at least one ISR that does manipulations of TCNT1.  You set TCNT1; you enable global interrupts; the interrupt fires and changes TCNT1; then you display TCNT1 contents.  Unbelievable?  I can believe why that code fragment shows a different value.

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

These days I did some study, the codes posted before is not right, e.g.:

 

void IRsender::sendByTimer1(){
    unsigned int tcnt1;
  
    TIMSK1 &= ~_BV(TOIE1);         // AR added
    TCCR1A = 0;
    TCCR1B = 0;                    //stop counter

    data_idx = 0;

 

    oldSREG = SREG;                // AR - save status register
    noInterrupts();                // AR - Disable interrupts
    TCNT1 = array_data_2_send[data_idx];                  
    SREG = oldSREG;                // AR - Restore status register

 

    TIFR1 |= (1<<TOV1);            //clear pending interrupt flag
    TCCR1B |= (1<<CS10);           //start count,normal mode
    IR_SEND_PWM_START;             //start framewave output
    TIMSK1 |= (1<<TOIE1);          //enable overflow interrupt
    
    while (tcnt1==array_data_2_send[data_idx]){       // //if the timer has not ticked yet ?????????
        oldSREG = SREG;
        noInterrupts();
        tcnt1 = TCNT1;
        SREG = oldSREG;
    } 
    
    array_data_2_send_millis_start[data_idx] = micros(); //debug
    array_data_2_send_debug[data_idx] = 1;
}

 

The right way:

 

void IRsender::sendByTimer1(){
  
    TIMSK1 &= ~_BV(TOIE1);         // AR added
    TCCR1A = 0;
    TCCR1B = 0;                    //stop counter

    TCCR1B |= (1<<CS10);           //start count,normal mode

    data_idx = 0;

 

    oldSREG = SREG;                // AR - save status register
    noInterrupts();                // AR - Disable interrupts
    TCNT1 = array_data_2_send[data_idx];                  
    SREG = oldSREG;                // AR - Restore status register

 

    TIFR1 |= (1<<TOV1);            //clear pending interrupt flag
    IR_SEND_PWM_START;             //start framewave output
    TIMSK1 |= (1<<TOIE1);          //enable overflow interrupt
    
}

 

I did not understand this operation before:

    TCCR1B |= (1<<CS10);           //start count,normal mode

When that operation executed, the TCNT1 will be set to 0 (reset).

So, if I lay it after set TCNT1, it will reset the timer1, and the value of TCNT1 will start from 0.

Do like that you will never start from your desired value (array_data_2_send[data_idx],e.g.).

 

But, the sad news is: for the affection made by other interrupts, the frame wave interval will not be achieved pricisely through timer interrupt. (us level, maybe acceptable for ms/s level)

Time to back classic way : delay with nop :)

 

As the end, I explained a little far to classic way.

 

I have write the send function in classic way before:

 

void IRsender::sendDirect(unsigned int data) {
    
    IR_SEND_PWM_START;
    delay(2);
    delayMicroseconds(400);
    IR_SEND_PWM_STOP;
    delayMicroseconds(spaceBoth);

    //send data
    int i = numBits - 1;
    while(i>=0) {
        if (bitRead(data,i)==1) {
            IR_SEND_PWM_START;
            delay(1);
            delayMicroseconds(200);
            IR_SEND_PWM_STOP;
            delayMicroseconds(spaceBoth);
        }else {
            IR_SEND_PWM_START;
            delayMicroseconds(markZero);
            IR_SEND_PWM_STOP;
            delayMicroseconds(spaceBoth);
        }
        i--;
    }

}

 

It works well at the beginning, with other function added, It can't output precisely, obviously affected by interrupts.

Of course , you think you can disable global interrupt to keep it work.

But if you try, you'll find it can't keep the precise too ,  for the delay() & delayMicroseconds() & millis() & micros() in arduino are based on timer0 , and  timer0 interrupt disabled .

 

If you want to delay with global interrupt disabled, you have to use useless code to occupy CPU.

IAR compiler support __delay_cycles(), I plan to use it, studying.

 

Thank you  all.

Last Edited: Thu. Sep 7, 2017 - 02:01 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Xiao wrote:
But if you try, you'll find it can't keep the precise too ,  for the delay() & delayMicroseconds() & millis() & micros() in arduino are based on timer0 , and  timer0 interrupt disabled .

MicroGyro wrote:
I'm not sure how that delay works in arduino but as I said I will avoid arduino for timing critical tasks.

.
MG

I don't know why I'm still doing this hobby