[solved]delay time is wrong for TCA CMP2

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

I used CMP0 CMP1 & CMP2 of TCA in ATTINY814.

 CMP2 : I used it to check timeout.

 CMP1 : change the port status after many ticks

 CMP0 : flash the LED.

 

Init code is simple,set the normal mode, and enable the timer :

void TCA_init()
{
	/* set Normal mode */
	TCA0.SINGLE.CTRLB = TCA_SINGLE_WGMODE_NORMAL_gc;
	/* disable event counting */
	TCA0.SINGLE.EVCTRL &= ~(TCA_SINGLE_CNTEI_bm);

	//TCA0.SINGLE.CTRLB = //0 << TCA_SINGLE_ALUPD_bp /* Auto Lock Update: disabled */
	//		  1 << TCA_SINGLE_CMP0EN_bp /* Compare 0 Enable: disabled */
	//		 | 1 << TCA_SINGLE_CMP1EN_bp /* Compare 1 Enable: disabled */
	//		 | 1 << TCA_SINGLE_CMP2EN_bp; /* Compare 2 Enable: disabled */
	//		 | TCA_SINGLE_WGMODE_NORMAL_gc; /*  */

	//TCA0.SINGLE.DBGCTRL = 1 << TCA_SINGLE_DBGRUN_bp; /* Debug Run: disabled */

	TCA0.SINGLE.INTCTRL |= 1 << TCA_SINGLE_OVF_bp; /* Compare 0 Interrupt: disabled */
	//		 | 1 << TCA_SINGLE_CMP1_bp; /* Compare 1 Interrupt: disabled */
	//		 | 0 << TCA_SINGLE_CMP2_bp /* Compare 2 Interrupt: disabled */
	//		 | 0 << TCA_SINGLE_OVF_bp; /* Overflow Interrupt: disabled */

	//TCA0.SINGLE.CTRLESET = 0x2; //LUPD=1 取消自动更新BUFF内容到CNT和CMP

	TCA0.SINGLE.CTRLA |= TCA_SINGLE_CLKSEL_DIV8_gc /* System Clock */
			            | 1 << TCA_SINGLE_ENABLE_bp; /* Module Enable: disabled */
}

start & stop CMP2 :

void beginCMP2(uint16_t ticksToWait){
    TCA0.SINGLE.INTCTRL &= ~TCA_SINGLE_CMP2_bm; //disable CMP2
    uint16_t t = TCA0.SINGLE.CNT;
    uint16_t t2 = t + ticksToWait;//*MULTIPLIER_US_TO_TICK;
    //forceUpdateCMP2(t2);
    TCA0.SINGLE.CMP2 = t2;

    ticksBeforeEnableCmp2 = 0;
    uint16_t t3 = TCA0.SINGLE.CMP2;
    if(t3-5>t){
        TCA0.SINGLE.INTCTRL |= TCA_SINGLE_CMP2_bm;
    }else{
        needStartCMP2 = true; // OV, need to start CMP in next scope
    ticksBeforeEnableCmp2 = t2 - TCA0_SINGLE_CMP2;
    }
}

void stopCMP2(void){
    TCA0.SINGLE.INTCTRL &= ~TCA_SINGLE_CMP2_bm;
}

ISR(TCA0_CMP2_vect){
	owStatus = ST_OW_IDLE; //timeout ,set to default status: waiting for reset
	TCA0.SINGLE.INTCTRL &= ~TCA_SINGLE_CMP2_bm; //disable int
	TCA0.SINGLE.INTFLAGS = TCA_SINGLE_CMP2_bm;  //clr int flag

	PORTA_OUTSET = PIN4_bm; PORTA_DIRSET = PIN4_bm; //for debug
}

#define OW_CHECK_TIMEOUT(ticks) { PORTA_OUTCLR = PIN4_bm; PORTA_DIRSET = PIN4_bm; beginCMP2(ticks); }
#define OW_CANCEL_CHECK_TIMEOUT { stopCMP2(); }

If I want to check timeout for 300us,just call: 

       OW_CHECK_TIMEOUT(300);

 

As upper picture shows,the pulse in yellow line should be longer than the blue one, but the result is much few.

I followed the code, input ticks is right,CMP2 is set correctlly. But the time from current CNT to CMP2 is wrong.

The pulse width is nearly same while I try to change the ticks filled in.

The clock & prescaler are checked, no problem.

 

The reason should be simple and hard to notice, I failed to find till now, anyone have advice? 

 

Last Edited: Fri. Nov 22, 2019 - 06:16 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Not sure what you are doing, but you are not clearing the cmp2 flag before its enabled. If the yellow line is cmp2 debug, then that could explain- you enable cmp2 irq with cmp2 flag already set, so irq fires as soon as you enable it and your debug pin is set right away.

 

Seems like too much code for what you want.

 

void setTimeoutCMP2(uint16_t ticks){
    //PORTA_OUTCLR = PIN4_bm; PORTA_DIRSET = PIN4_bm;
    TCA0.SINGLE.CMP2 = TCA0.SINGLE.CNT + ticks;
    TCA0.SINGLE.INTFLAGS = TCA_SINGLE_CMP2_bm;
    TCA0.SINGLE.INTCTRL |= TCA_SINGLE_CMP2_bm;
}

ISR(TCA0_CMP2_vect){
    owStatus = ST_OW_IDLE; //timeout ,set to default status: waiting for reset
    TCA0.SINGLE.INTCTRL &= ~TCA_SINGLE_CMP2_bm; //disable int
    TCA0.SINGLE.INTFLAGS = TCA_SINGLE_CMP2_bm;  //clr int flag    
    //PORTA_OUTSET = PIN4_bm; PORTA_DIRSET = PIN4_bm; //for debug
}

 

the setting cmp2 from cnt while cnt is running has a flaw at the extreme values of ticks, but there is div8 clock in use so that will help, most of the time.

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

The code was simplified:

void TCA_init()
{
    /* set Normal mode */
    TCA0.SINGLE.CTRLB = TCA_SINGLE_WGMODE_NORMAL_gc;
    /* disable event counting */
    TCA0.SINGLE.EVCTRL &= ~(TCA_SINGLE_CNTEI_bm);
 
    TCA0.SINGLE.CTRLA |= TCA_SINGLE_CLKSEL_DIV8_gc /* System Clock */
                        | 1 << TCA_SINGLE_ENABLE_bp; /* Module Enable: disabled */
}

void beginCMP2(uint16_t ticksToWait){
	TCA0.SINGLE.INTCTRL &= ~TCA_SINGLE_CMP2_bm; //disable CMP2

	uint16_t t = TCA0.SINGLE.CNT;
	TCA0.SINGLE.CMP2 = t + ticksToWait;
	
	TCA0.SINGLE.INTCTRL |= TCA_SINGLE_CMP2_bm;
    if(TCA0.SINGLE.CMP2<t)
		ticksBeforeEnableCmp2 = 0xFFFF - t;
	else
		ticksBeforeEnableCmp2 = 0;
		
}

void stopCMP2(void){
	TCA0.SINGLE.INTCTRL &= ~TCA_SINGLE_CMP2_bm;
}

//CMP2 used to check timeout
ISR(TCA0_CMP2_vect){
	owStatus = ST_OW_IDLE; //timeout ,set to default status: waiting for reset
	TCA0.SINGLE.INTCTRL &= ~TCA_SINGLE_CMP2_bm; //disable int
	TCA0.SINGLE.INTFLAGS = TCA_SINGLE_CMP2_bm;  //clr int flag
	
	PORTA_OUTSET = PIN4_bm; PORTA_DIRSET = PIN4_bm; //for debug
}

#define OW_CHECK_TIMEOUT(ticks) { PORTA_OUTCLR = PIN4_bm; PORTA_DIRSET = PIN4_bm;/*for debug*/ beginCMP2(ticks); }

I have cleared the INTFLAGS in ISR, it should not happen again in the following time.

However,in my case, it happened every time.

 

If it is the reason, what triggered that action(set INTFLAGS)?

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

And there is a problem I can't explain.

The CPU clock is 20M/2=10M

The final freq of TCA is : 20M/2/8 = 1.25M

 

The width of debug pulse is about 7us or more, that means 7*10 CPU cycles, or 70 instructions.

The whole instructions of executing ISR  is much less than 70.

(Assume that triggering ISR soon after enable CMP2)

Last Edited: Fri. Nov 22, 2019 - 01:38 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

>I have cleared the INTFLAGS in ISR, it should not happen again in the following time.

 

If the timer is running, a compare match sets the flag. It doesn't matter whether the cmp irq is enabled or not. You enable the timer one time, and it keeps running regardless of what else you are doing, so whenever there is a compare match the compare flag is set (once every time through the period, assuming the compare value is within the period). Since you do not want to clear that flag before you enable the irq, you also do not know if it is already set.

 

>The whole instructions of executing ISR  is much less than 70.

 

Maybe you are not getting the clock you think you are getting. If you are not setting the main clock div correctly (which is protected), you could be still at the default /6. Or maybe you have it set to /4 instead of /2. You may want to make sure you are getting what you want. I assume you have a debugger, so can check if the clock prescale is set correctly. The debugger can also be used to troubleshoot your other problem- stop before you enable the cmp2 irq, look at the cmp2 irq flag- if its already set, you will then single step right into the isr.

 

 

Not really sure what this is for-

ticksBeforeEnableCmp2

You can set the compare register value in that same function-

CNT2 is currently 0x8000

you want a compare match in 0xF000 ticks

0x8000 + 0xF000 = 0x7000

set CMP2 = 0x7000 (not the buffer cmp2 register)

clear cmp2 flag

enable cmp2 irq

0x8000, 0x8001, ... 0xFFFF, 0x0000, 0x0001, ... , 0x6FFFF, 0x7000 <- cmp2 match, irq fires, isr runs

 

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

curtvm:

This is the key: "If the timer is running, a compare match sets the flag."

Report after tried.

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

Xiao wrote:

curtvm:

This is the key: "If the timer is running, a compare match sets the flag."

Yes,it is true.

 

Thank you,curtvm.