Generating servo pulses using TCD peripheral

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

Hi,

 

I'm trying to generate servo pulses using the TCD peripheral on the Attiny3217.

 

As far as I can tell, this should work.

 

The timer has two outputs (WOA and WOB) which can be independently set up with comparators to turn on the pulse, then it turns off again the next cycle.

 

This is "upside down" from what I want, so I just invert the bit on output - I can use the invert flag for this, but in this particular case, I'm using the CCL to duplicate those outputs, inverted, on different pins - this is mostly because these are more convenient pins for my application. (NB: In my project, I'm doing a lot of other things, generating other PWMs using TCA etc, which is all working well)

 

I've written a test program, here:

 

https://github.com/MarkR42/robot...

 

What this does is:

 

* Initialise the TCD to output the waveforms. (That works as expected)

* Set up CCL to invert those outputs and output on its own pins (that works as expected)

* Loop around, changing the pulse lengths

 

What I expect to happen is, approxiamtely every 10ms it should reset the TCD and generate a new pair of pulses (on different pins), with one of them changing length gradually between 1-2ms.

 

What actually happens, is it starts off ok, but then it doesn't change length smoothly, and the pulses don't happen very often - it seems semi-random, the pulses often only appear about once every 100ms, which is far less often than I'd expect.

 

If I change mainloop() into a NOP, then it generates static width pulses exactly as I'd expect (1ms and 2ms width , period about 12ms)

 

What I think's happening, is that I'm somehow not letting the TCD get started, and it's going wonky. I see the example on this forum, here: https://www.avrfreaks.net/forum/... which sends the command flag SYNCEOC in CTRLE with the comment "Sync the registers", I'm not sure whether that's a good idea.

 

Anyone have any ideas what's happening?

This topic has a solution.
Last Edited: Tue. Mar 10, 2020 - 10:26 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

markxr wrote:
approxiamtely every 10ms it should reset the TCD
10ms cycle? Servos are normally driven off a 20ms (50Hz)  cycle with a signal transition between 1ms and 2ms

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

I can't actually generate a 20ms period signal using TCD, because it is only 12 bits and the clock rate is (at least) system clock/32 (one tick every 3.2us in my setup), so it would need 6250 ticks but only has 12 bits (=4096)

 

But maybe that doesn't matter as I can reprogram it in software every tick.

 

Also, I think servos still work if the pulses are closer (or further) - it is only the pulse width that really matters.

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

I didn't read your code, but a couple quick thoughts.

 

When you made the MainLoop a NOP it worked.

So that begs the question are your ISR's starving the MainLoop (in your real program) of enough clock cycles to do any meaningful work?

 

I've not used the 3217, but most AVRs will run one instruction from the primary program between processing stacked interrupts.

So your primary program can appear to run very slowly based upon just how many clock cycles it is getting in a highly ISR driven program.

 

Next, on the XmegaE series, (I don't know about the 3217), many ISRs have to be manually reset by your software, within the ISR.

Otherwise the program will exit the ISR, run one instruction, and re-enter the ISR.

Just executing the ISR handler doesn't always reset the ISR flag.

Just double check on this for your chip for the specific ISRs you are using.

 

JC

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

Hi, it's a nice idea, but not the right answer,

 

my minimal test program has no ISRs at all, no interrupts are happening.

 

 

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

Not sure why cmpa and cmpb are both used, but I could be missing something going on with ccl.

 

Why not just set cmpaset to 0, and change cmpaclr as needed from 312 (1ms) to 615 (2ms). Or reverse set/clr if need inverted.

You will get 1ms to 2ms on woa pulses every 13ms and all you have to do is set the double-buffered cmpaclr register to change pulse width.

I would be surprised if a servo would care about a little faster repetition rate.

 

Set it and forget it, except to change servo position.

 

Simple example on a tiny817 which simply changes led brightness (simple polling of woa pin). For a servo, you would limit the cmpaclr values from 312-615.

 

//tiny817 Xplained
#include <avr/io.h>
#include <stdbool.h>
#include <avr/interrupt.h>
#define F_CPU 10000000ul
#include <util/delay.h>
//PC0 = led, PA4 = WOA

 

static void led(bool tf){
    VPORTC.DIR |= PIN0_bm;
    if( tf ) VPORTC.OUT |= PIN0_bm; else VPORTC.OUT &= ~PIN0_bm;
}

static void setCmpa(uint16_t v){
    while( 0 == (TCD0.STATUS & 2) );//cmd busy?
    TCD0.CMPACLR = v; //new value
    TCD0.CTRLE = 1;//SYNCEOC
}

int main(){
    CCP = 0xD8;
    CLKCTRL.MCLKCTRLB = 1; //div2, 10MHz
    TCD0.CTRLA = 0b01110000; //pbclk, /32
    CCP = 0xD8;
    TCD0.FAULTCTRL = 0b00010000; //cmpaen
    TCD0.CMPBCLR = 0xFFF; //max count
    TCD0.CTRLA |= 1; //tcd0 enable

   

    //poll woa, set led to woa value

    //inc cmpa to inc brightness

    //led brightness ramps up, repeatedly

    for(uint16_t i = 0, v = 0; ; led( VPORTA.IN & 1<<4 ), i++){
        if( i==0 ) setCmpa(v+=50);
    }
}

Last Edited: Mon. Mar 9, 2020 - 09:58 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for the reply.

 

The reason I don't do that, is that I want to drive *two* servos, and the cmpbclr sets the maximum count so I can't change that otherwise the period changes. So I inverted the polarity, and used cmpaset and cmpbset to set the pulse widths (i.e. have WOA active low) which is ok.

 

I haven't tried it with a servo yet but I strongly suspect that the 12ms between pulses will be ok.

 

I've now solved the main problem I had earlier - the SYNCEOC command was missing and that seems very important; without SYNCEOC it seldom updates.

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

The TCD has a prescaler and can generate 50Hz.

 

Above all, reality is here.
I'm using all of the TCD's from WOA to WOD and running the four servos independently.

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

Not sure why you cannot also use cmpb as you would cmpa, you just work from the end-

 

cmpaset = 0
cmpaclr = 312 to 615

 

cmpbset = 4095-(312 to 615)
cmpbclr = 4095

 

period never changes as you change cmpbset for the b channel.

 

WOA = 1-2ms pulse

WOB = 1-2ms pulse

 

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

Thanks, curt, I think working from the end is the best way to do it, if I can tolerate an extra 10ms of latency, it's really going to make more sense.

 

 

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

kabasan wrote:

The TCD has a prescaler and can generate 50Hz.

 

The prescaler maximum is 32x, but I want to run the CPU at 10mhz (faster than the default, 3.3mhz), so I can't do a period of more than ~ 13ms. But I think it's not a problem, the servos will work ok.

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

I physically realize 50Hz.
This is because TCD has two prescalers and can divide 1/256.

 

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

You do wonder what advantage there is in spitting this into 2 and 2 bits rather than just having a 4 bit "prescaler division" as found in traditional AVRs??

 

It's this kind of thing that makes these new chips look complex and intimidating when it doesn't have to be. If it were 4 bits it would simply be:

0000 /1
0001 /2
0010 /4
0011 /8

0100 /4
0101 /8
0110 /16
0111 /32

1000 /32
1001 /64
1010 /128
1011 /256
1100 Reserved
1101 Reserved
1110 Reserved
1111 Reserved

I suppose it's the reappearance of several that makes them split it?

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

@kabsan, thanks, I didn't realise the "synchronisation prescaler" would scale the counter. I shall use this if it works for me.

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

>You do wonder what advantage there is in spitting this into 2 and 2 bits rather than just having a 4 bit "prescaler division" as found in traditional AVRs??

 

Its in the datasheet-

The TCD synchronizer clock is separate from the other module clocks enabling faster synchronization between the TCD domain and the IO domain.

although I guess that may not answer the question you asked.

Last Edited: Tue. Mar 10, 2020 - 12:35 PM