Regarding a glitch pulse from TCB in singleshot mode

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


Hi all,

I am using ATmega1608 and IAR for AVR IDE. I am trying to program Timer B in single-shot mode and this single-shot will give a pulse width of 1us-15us depending on the CCMP value I set . My Timer configuration works fine and I can see it working correctly i.e giving me a pulse width on output pin. I am facing a glitch when it comes to changing the compare value in the timer single shot function. what is this glitch? as soon as I change the CCMP register value, I can see a weird pulse of random duration mainly between 0.3us-2us and it can happen at the begging of normal pulse or at the end or it can combine with the normal pulse width.  I tried few things here to get rid of this glitch pulse, I am polling STATUS bit in TCB, so that when counter is not running at that time only I will change the CCMP value, but it didn't work.  I am attaching my timer initialization and the timer value change function, please let me know if anyone can help here.

 

void timer_init()

{

  TCB1.CTRLB= TCB_CCMPEN_bm | TCB_CNTMODE_SINGLE_gc | TCB_ASYNC_bm;

 

  TCB1.EVCTRL= TCB_FILTER_bm | TCB_CAPTEI_bm | ~TCB_EDGE_bm;  

 

  TCB1.CNT=65535;

 

  TCB1.CTRLA= TCB_CLKSEL_CLKDIV1_gc;

 

  TCB1.CTRLA|= TCB_ENABLE_bm;

 

}

 

void timer_singleshot(char pulse)

{    

  while((TCB1.STATUS & TCB_RUN_bm));

 

  TCB1.CNT=0;

 

  TCB1.CCMP=pulse;

}

 

scope shot of what my glitch pulse, looks like. The second continuous pulse(fourth from the left) is glitch pulse here.

 

This topic has a solution.
Last Edited: Mon. Sep 19, 2022 - 09:36 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I haven't tried this yet, but what if you fix it like this?

 

void timer_singleshot(char pulse){
    while((TCB1.STATUS & TCB_RUN_bm));
    TCB1.CNT = 0xFFFF;
    TCB1.CCMP = pulse;
    TCB1.CNT = 0;
}

 

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

Your function argument is a signed value (char) but CCMP is an unsigned value (uint16_t), so the promotion from char to uint16_t will result in bad values when pulse is >=128

https://godbolt.org/z/5GMrrczbb

notice the way the upper half of ccmp is set- it is either 0 or 0xFF depending on pulse bit7.

 

Although harmless in this case, the following is not what you want to do-

TCB1.EVCTRL= TCB_FILTER_bm | TCB_CAPTEI_bm | ~TCB_EDGE_bm; 

the ~TCB_EDGE_bm value is 0xEF, so instead of turning off a single bit you are setting all the other bits. Since you are using '=', you can just skip dealing with any bits you want to be 0.

 

I think you can make this a single function, so init/set are the same function-

 

void timer_singleshot(uint16_t pulse){

    while( TCB1.STATUS & TCB_RUN_bm ); //wait if already running
    TCB1.CTRLA = 0; //now turn off, so any event that happens before ccmp is set below are ignored

    TCB1.CTRLB = TCB_CCMPEN_bm | TCB_CNTMODE_SINGLE_gc | TCB_ASYNC_bm;
    TCB1.EVCTRL = TCB_FILTER_bm | TCB_CAPTEI_bm;    
    TCB1.CNT = TCB1.CCMP = pulse; //CNT=CCMP when tcb started so no output set (match = output low)

    TCB1.CTRLA = TCB_CLKSEL_CLKDIV1_gc | TCB_ENABLE_bm; //tcb on
}

 

The datasheet doesn't make it real clear in the single-shot description, but TOP is the highest value in the count sequence so CCMP is TOP.

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


curtvm wrote:

Your function argument is a signed value (char) but CCMP is an unsigned value (uint16_t), so the promotion from char to uint16_t will result in bad values when pulse is >=128

https://godbolt.org/z/5GMrrczbb

notice the way the upper half of ccmp is set- it is either 0 or 0xFF depending on pulse bit7.

 

Although harmless in this case, the following is not what you want to do-

TCB1.EVCTRL= TCB_FILTER_bm | TCB_CAPTEI_bm | ~TCB_EDGE_bm; 

the ~TCB_EDGE_bm value is 0xEF, so instead of turning off a single bit you are setting all the other bits. Since you are using '=', you can just skip dealing with any bits you want to be 0.

 

I think you can make this a single function, so init/set are the same function-

 

void timer_singleshot(uint16_t pulse){

    while( TCB1.STATUS & TCB_RUN_bm ); //wait if already running

    TCB1.CTRLA = 0; //now turn off, so any event that happens before ccmp is set below are ignored

    TCB1.CTRLB = TCB_CCMPEN_bm | TCB_CNTMODE_SINGLE_gc | TCB_ASYNC_bm;

    TCB1.EVCTRL = TCB_FILTER_bm | TCB_CAPTEI_bm;    

    TCB1.CNT = TCB1.CCMP = pulse; //CNT=CCMP when tcb started so no output set (match = output low)

    TCB1.CTRLA = TCB_CLKSEL_CLKDIV1_gc | TCB_ENABLE_bm; //tcb on

}

 

The datasheet doesn't make it real clear in the single-shot description, but TOP is the highest value in the count sequence so CCMP is TOP.

Yes, I agree with your point. Previously, I had a single function only. It still gave me a glitch pulse. One important thing I found in the data sheet is that, Timer B does not have a compare buffer. Timer A has it. In timer A, it is mentioned that, due to this compare buffer. The value of CCMP is updated at the right time, when the counter is at top or bottom so we don't get a glitch pulse. Timer B does not have such kind of buffer. This snippet is from Timer A part of datasheet.

 

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

kabasan wrote:

I haven't tried this yet, but what if you fix it like this?

 

void timer_singleshot(char pulse){
    while((TCB1.STATUS & TCB_RUN_bm));
    TCB1.CNT = 0xFFFF;
    TCB1.CCMP = pulse;
    TCB1.CNT = 0;
}

 

Hi,

I tried the function you told me and it still gives me the glitch pulse.

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

I have a tiny3217 curiosity board where I setup a pwm output to the tcb1 event, then continually changed the pulse length (calling the below function) with small delays between changes. so they are being changed out of sync. The code I posted previously worked, but there were still occasional small glitches (very small). I then added the highlighted code below to disable WO while setting the timer up, which seems to work (no glitches seen).

 

void timer_singleshot(uint16_t pulse){

    while( TCB1.STATUS & TCB_RUN_bm ); //wait if already running

    PORTA.DIRCLR = 1<<5; //turn off WO0 (PORTA.OUT now controls, normally 0 so is low)
    TCB1.CTRLA = 0; //now turn off, so any event that happens before ccmp is set below are ignored

    TCB1.CTRLB = TCB_CCMPEN_bm | TCB_CNTMODE_SINGLE_gc | TCB_ASYNC_bm;
    TCB1.EVCTRL = TCB_FILTER_bm | TCB_CAPTEI_bm;    
    TCB1.CNT = TCB1.CCMP = pulse; //CNT=CCMP when tcb started so no output set (match = output low)

    TCB1.CTRLA = TCB_CLKSEL_CLKDIV1_gc | TCB_ENABLE_bm; //tcb on

    PORTA.DIRSET = 1<<5; //WO0 back on
}

 

 

If you do not want to miss any events (there is a chance in the above code, since you do not know when the event will come in), then you would probably have to create a tcb interrupt. The pulse value would be a global var, so you set the pulse value and let the interrupt set ccmp, but would have to make sure the interrupt does its work before the next event comes in.

 

edit- I tried the interrupt idea above, but gave up as it ended up worse (glitches) than the example code above (which seems to always work without glitch).

 

Last Edited: Thu. Sep 15, 2022 - 09:09 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

curtvm wrote:

I have a tiny3217 curiosity board where I setup a pwm output to the tcb1 event, then continually changed the pulse length (calling the below function) with small delays between changes. so they are being changed out of sync. The code I posted previously worked, but there were still occasional small glitches (very small). I then added the highlighted code below to disable WO while setting the timer up, which seems to work (no glitches seen).

 

void timer_singleshot(uint16_t pulse){

    while( TCB1.STATUS & TCB_RUN_bm ); //wait if already running

    PORTA.DIRCLR = 1<<5; //turn off WO0 (PORTA.OUT now controls, normally 0 so is low)
    TCB1.CTRLA = 0; //now turn off, so any event that happens before ccmp is set below are ignored

    TCB1.CTRLB = TCB_CCMPEN_bm | TCB_CNTMODE_SINGLE_gc | TCB_ASYNC_bm;
    TCB1.EVCTRL = TCB_FILTER_bm | TCB_CAPTEI_bm;    
    TCB1.CNT = TCB1.CCMP = pulse; //CNT=CCMP when tcb started so no output set (match = output low)

    TCB1.CTRLA = TCB_CLKSEL_CLKDIV1_gc | TCB_ENABLE_bm; //tcb on

    PORTA.DIRSET = 1<<5; //WO0 back on
}

 

I copied and pasted your exact same function. But I can still see the glitch. One change I made is

TCB1.CNT=0;

TCB1.CCMP=pulse;

 

Here is my function

void timer_singleshot(uint16_t pulse)
{

    while( TCB1.STATUS & TCB_RUN_bm ); //wait if already running

    PORTA.DIRCLR = 1<<3; //turn off WO0 (PORTA.OUT now controls, normally 0 so is low)
    TCB1.CTRLA = 0; //now turn off, so any event that happens before ccmp is set below are ignored

    TCB1.CTRLB = TCB_CCMPEN_bm | TCB_CNTMODE_SINGLE_gc | TCB_ASYNC_bm;
    TCB1.EVCTRL = TCB_FILTER_bm | TCB_CAPTEI_bm;    
    TCB1.CNT = 0;
    TCB1.CCMP = pulse;

    TCB1.CTRLA = TCB_CLKSEL_CLKDIV1_gc | TCB_ENABLE_bm; //tcb on

    PORTA.DIRSET = 1<<3; //WO0 back on
}

 

I am still seeing the glitch pulse.

 

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

I copied and pasted your exact same function. But I can still see the glitch. One change I made is

Yeah, I still see a glitch pulse also with your change, so why did you change it and not try it as-is.  With the code I posted, I see no glitches as I change the pulse value every 1ms on a pwm event source of 33khz.

 

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

 

 

curtvm wrote:

I copied and pasted your exact same function. But I can still see the glitch. One change I made is

Yeah, I still see a glitch pulse also with your change, so why did you change it and not try it as-is.  With the code I posted, I see no glitches as I change the pulse value every 1ms on a pwm event source of 33khz.

 

Because it didn’t work.I couldn’t see any pulse on output.

Last Edited: Fri. Sep 16, 2022 - 02:25 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have a tiny3217 curiosity board

I am using ATmega1608

I forgot about the differences, and I also used tcb0 on my board for the single-pulse so I could hook up a probe as the led was on tcb1 WO (tcb1 WO was the event source, doing pwm at 33khz).

 

Your mega1608 TCB1 WO is on pin PA3 (if using default, or PF5 is using alt pin), so the two red highlighted lines that disable/enable WO pin output need to change accordingly to use PA3 instead of PA5. You may have already changed those lines and tried that, I don't know.

 

I just know there are no glitches on my tiny3217 with the code I posted, and although you can sometimes see a missed event (because of turning off tcb while the ccmp change takes place), the single pulse is always correct with no extra little pulses seen anywhere. I don't know why these glitches show up, but it seems there is something in the tcb peripheral that is sensitive to enabling single-pulse mode. I also tried other ideas, but it seemed the only success I had was to both disable tcb and disable the pin output before changing ccmp.

 

 

 

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

curtvm wrote:

I have a tiny3217 curiosity board

I am using ATmega1608

I forgot about the differences, and I also used tcb0 on my board for the single-pulse so I could hook up a probe as the led was on tcb1 WO (tcb1 WO was the event source, doing pwm at 33khz).

 

Your mega1608 TCB1 WO is on pin PA3 (if using default, or PF5 is using alt pin), so the two red highlighted lines that disable/enable WO pin output need to change accordingly to use PA3 instead of PA5. You may have already changed those lines and tried that, I don't know.

 

I just know there are no glitches on my tiny3217 with the code I posted, and although you can sometimes see a missed event (because of turning off tcb while the ccmp change takes place), the single pulse is always correct with no extra little pulses seen anywhere. I don't know why these glitches show up, but it seems there is something in the tcb peripheral that is sensitive to enabling single-pulse mode. I also tried other ideas, but it seemed the only success I had was to both disable tcb and disable the pin output before changing ccmp.

 

 

 

the function I posted above was for PA3 pin as output. 

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

the function I posted above was for PA3 pin as output. 

I can see that in your function, but I really don't know what you used with the example code given- whether you copied/pasted as-is or made the pin changes.

 

In any case, I doubt you want to ever set CNT to 0 as you are doing. When the example code turns on tcb, the datasheet says the counter will run (and presumably then also set the WO output) unless you set CNT to TOP (CCMP).  With CNT=TOP/CCMP, a match is seen when enabled so the counter  will be stopped when enabled along with the WO output off.

 

Another option to try using the example code is to disable the event user and generator after you set your WO pin to input, then after the WO pin is set back to output enable the event user and generator. I added that to the example code on my tiny3217 and this also seems to work. I guess you just have to try various things until something works.

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

curtvm wrote:

the function I posted above was for PA3 pin as output. 

I can see that in your function, but I really don't know what you used with the example code given- whether you copied/pasted as-is or made the pin changes.

 

In any case, I doubt you want to ever set CNT to 0 as you are doing. When the example code turns on tcb, the datasheet says the counter will run (and presumably then also set the WO output) unless you set CNT to TOP (CCMP).  With CNT=TOP/CCMP, a match is seen when enabled so the counter  will be stopped when enabled along with the WO output off.

 

Another option to try using the example code is to disable the event user and generator after you set your WO pin to input, then after the WO pin is set back to output enable the event user and generator. I added that to the example code on my tiny3217 and this also seems to work. I guess you just have to try various things until something works.

With CNT=CCMP, we are never to see the pin go high! or am i missing something here?

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

curtvm wrote:

the function I posted above was for PA3 pin as output.

In any case, I doubt you want to ever set CNT to 0 as you are doing.

 

The reason I was setting CNT=0 is because, when you look at my original function I just update the compare value and do not re-enable the timer(timer_singleshot()). The function you wrote we don't need CNT=0 as it stops and starts timer from zero only.

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

The reason I was setting CNT=0

If CNT does not match CCMP(TOP), the counter will count, when the counter counts the WO output is on, so setting CNT to 0 probably never makes any sense unless you wanted to 'manually' force a pulse on WO.

 

The datasheet doesn't seem to explain the process real well- the event seems to 'just' reset the counter to 0, the counter runs anytime the CNT != CCMP, the WO output is on anytime the counter is running (although if using async, WO could be set a few cycles before). So the process of changing CCMP means at some point CNT != CCMP, and the remedy seems to be to disable the WO output from occurring in some way why those values get changed.

 

Another idea is to disable/enable ccmpen while changing ccmp (notice CNT also needs to match to prevent counter from running). This also seems to work ok on my board.

void timer_init(){

    /* setup timer, leave CNT/CCMP both at 0

    * so no counter start and WO output until

    * timer_singleshot() is used */

}

 

void timer_singleshot(uint16_t pulse){
    while( TCB1.STATUS & TCB_RUN_bm );
    TCB1.CTRLB &= ~TCB_CCMPEN_bm; //turn off ccmpen
    TCB1.CNT = TCB_PULSE.CCMP = pulse;
    TCB1.CTRLB |= TCB_CCMPEN_bm; //ccmpen back on
}

 

 

 

Another which works for me, and will sync the change at the end of a generated pulse. If the generated pulse width does not get close to the incoming event period, an incoming event will not be missed. The defines are for my particular board/test, change to match your setup.

 

#define TCB_PULSE       TCB0
#define TCB_WO0_PORT    PORTA
#define TCB_WO0_PINbm   PIN5_bm;
#define TCB_EVENT_SET() EVSYS.ASYNCCH0 = EVSYS_ASYNCCH0_PORTA_PIN3_gc; \
                        EVSYS.ASYNCUSER0 = EVSYS_ASYNCUSER0_ASYNCCH0_gc;

void timer_init(){
    TCB_PULSE.CTRLB = TCB_CCMPEN_bm | TCB_CNTMODE_SINGLE_gc | TCB_ASYNC_bm;
    TCB_PULSE.EVCTRL = TCB_FILTER_bm | TCB_CAPTEI_bm;
    TCB_WO0_PORT.DIRSET = TCB_WO0_PINbm; //WO0 on
    TCB_EVENT_SET();
}
void timer_singleshot(uint16_t pulse){
    if( ! TCB_PULSE.CTRLA ){
        timer_init(); //init on first use
    } else{ //already running
        while( ! (TCB_PULSE.STATUS & TCB_RUN_bm) ){} //wait for count to start
        while( TCB_PULSE.STATUS & TCB_RUN_bm ){} //wait for count to stop
        //now at a trailing end of a generated pulse
    }
    TCB_PULSE.CTRLA = 0; //tcb off
    TCB_PULSE.CNT = TCB_PULSE.CCMP = pulse; //set new value
    TCB_PULSE.CTRLA = TCB_CLKSEL_CLKDIV1_gc | TCB_ENABLE_bm; //tcb on
}

 

 

Last Edited: Mon. Sep 19, 2022 - 05:21 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

curtvm wrote:

The reason I was setting CNT=0

If CNT does not match CCMP(TOP), the counter will count, when the counter counts the WO output is on, so setting CNT to 0 probably never makes any sense unless you wanted to 'manually' force a pulse on WO.

 

The datasheet doesn't seem to explain the process real well- the event seems to 'just' reset the counter to 0, the counter runs anytime the CNT != CCMP, the WO output is on anytime the counter is running (although if using async, WO could be set a few cycles before). So the process of changing CCMP means at some point CNT != CCMP, and the remedy seems to be to disable the WO output from occurring in some way why those values get changed.

 

Another idea is to disable/enable ccmpen while changing ccmp (notice CNT also needs to match to prevent counter from running). This also seems to work ok on my board.

void timer_init(){

    /* setup timer, leave CNT/CCMP both at 0

    * so no counter start and WO output until

    * timer_singleshot() is used */

}

 

void timer_singleshot(uint16_t pulse){
    while( TCB1.STATUS & TCB_RUN_bm );
    TCB1.CTRLB &= ~TCB_CCMPEN_bm; //turn off ccmpen
    TCB1.CNT = TCB_PULSE.CCMP = pulse;
    TCB1.CTRLB |= TCB_CCMPEN_bm; //ccmpen back on
}

 

 

 

Another which works for me, and will sync the change at the end of a generated pulse. If the generated pulse width does not get close to the incoming event period, an incoming event will not be missed. The defines are for my particular board/test, change to match your setup.

 

#define TCB_PULSE       TCB0
#define TCB_WO0_PORT    PORTA
#define TCB_WO0_PINbm   PIN5_bm;
#define TCB_EVENT_SET() EVSYS.ASYNCCH0 = EVSYS_ASYNCCH0_PORTA_PIN3_gc; \
                        EVSYS.ASYNCUSER0 = EVSYS_ASYNCUSER0_ASYNCCH0_gc;

void timer_init(){
    TCB_PULSE.CTRLB = TCB_CCMPEN_bm | TCB_CNTMODE_SINGLE_gc | TCB_ASYNC_bm;
    TCB_PULSE.EVCTRL = TCB_FILTER_bm | TCB_CAPTEI_bm;
    WO0_PORT.DIRSET = WO0_PINbm; //WO0 on
    TCB_EVENT_SET();
}
void timer_singleshot(uint16_t pulse){
    if( ! TCB_PULSE.CTRLA ){
        timer_init(); //init on first use
    } else{ //already running
        while( ! (TCB_PULSE.STATUS & TCB_RUN_bm) ){} //wait for count to start
        while( TCB_PULSE.STATUS & TCB_RUN_bm ){} //wait for count to stop
        //now at a trailing end of a generated pulse
    }
    TCB_PULSE.CTRLA = 0; //tcb off
    TCB_PULSE.CNT = TCB_PULSE.CCMP = pulse; //set new value
    TCB_PULSE.CTRLA = TCB_CLKSEL_CLKDIV1_gc | TCB_ENABLE_bm; //tcb on
}

 

 

Can you post a scope plot of your single-shot pulses, when you start decreasing the CCMP value. In my case The function you provided is working(correct single-shot without glitch pulse) when I increase the CCMP value, but the timer wraps around when I am putting a CCMP value smaller than previous.

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

No need to quote my full posts each time.

 

Using the second example code in #15, a 33kHz signal coming in (~30us period), calling timer_singleshot() every 500us with values from 60 to 10, its all uniform pulses that decrease in width with no glitches so nothing really to see (same results when incrementing from 10 to 60). The screenshot below is the transition from 10 to 60. No events are being missed because of the two lines of code that are syncing up with the trailing edge of the single-shot pulse. If the single-shot width gets too close to the incoming event period, then an event can be missed while changing pulse width value.

 

 

 

but the timer wraps around when I am putting a CCMP value smaller than previous.

The timer only stops in single-shot mode when CNT == CCMP, or when the timer is disabled. In both examples in #15, CNT is set to CCMP so the timer stops until it sees another event. It sounds like you are not setting CNT and only setting CCMP.  If only setting CCMP, the CNT register will (most likely) not equal CCMP so keeps running, and if CCMP is then less than CNT the counter will wrap around until CNT == CCMP. If CCMP < CNT, then would stop without wrap, but would most likely also produce a glitch.

 

TCB_PULSE.CNT = TCB_PULSE.CCMP = pulse;

 

Post your timer_init and timer_pulse functions if they are not exactly the same as the example code.