is it possible compare a 16-bit value with a 8-bit compare match ISR

246 views Asked by At

I am trying to make a servo controller that have a higher resolution than the ATtiny85 8-bit timer/counter. So far I have managed to get about 2000 positions on my servo (1µs/step) within a time frame of 21'000 µs. I have also managed to move 5 servos sequential and with different speed, but now I want to move them synchronous.

My biggest problem is that I don't get how I should make it happen! I have looked around on other servo codes including servo8bit library and tried to find a way. It seems that most of the examples uses compare match ISR to move the servos "at the same time", my problem is that I have a 16-bit integer that I want to compare.

Is there a way to do some magic so I can use the 8-bit compare match ISR with my 16-bit integer? Or does anyone of you have some other suggestions on how I can move my servos synchronous without using compare match ISR?

I hope my questions make sense!

Since I don't really have any code to show yet (only flawed attempts without compar match ISR that makes no sense) I post the link to my TinyServo code if it helps.

EDIT 1:

Here is the part of the code I mentioned and didn't post the first time:

void servoMove(void)
{   
uint16_t nextPulse = hPulse[0];

timerSetup ();      //16-bit setup for counter

for (i = 0; i < sizeof(servo)/sizeof(servo[0]); i++)
{
    if ( (oTime > nextPulse) && (channel < sizeof(servo)/sizeof(servo[0])) )        //check if HIGH pulse (pos) is done
    {
        PORTB &= ~(1 << servo[channel]);

        if (i+1 < sizeof(hPulse)/sizeof(hPulse[0]))
        {
            nextPulse += hPulse[i+1];
        }

        channel++;
    }

    else
    {
        channel = 0;

        oTime = 0;      //resets 16-bit variable
        tot_overflow = 0;       //resets tot_overflow variable  
        TIFR |= (1 << TOV1);        // clear counter1 overflow-flag
        TCNT1 = 0;      //resets Timer/Counter1 
    }

}

for (i = 0; i < sizeof(servo)/sizeof(servo[0]); i++)
{
    if ( (oTime > tPulse - nextPulse) && (channel < sizeof(servo)/sizeof(servo[0]))   )         //check if LOW pulse (period) is done 
    {   
        PORTB |= (1 << servo[channel]);
        nextPulse -= hPulse[i];
        channel++;  
    }

}

}


void servoPosSet(volatile uint16_t pos[], uint8_t size)
{
     for (i = 0; i < size; i++)
     {
        hPulse[i] = pos[i];
     }

}


int main(void)
{       
TCCR1 |= (1 << CS12);   //set Timer/Counter1 prescaler to increment every 1 µs (PCK/8)

for (channel = 0; channel < size); channel++)
{
    DDRB |= (1 << servo[channel]);  //sets PB0-PB4 as output pins
}

channel = 0;

uint16_t pos[] = {2000, 1500, 1900, 1300, 1700};
uint8_t size = 5;

while(1)        
{
        servoPosSet(pos);

        servoMove();
}

}

EDIT 2:

This is an illustration of how I think the code should work: enter image description here

...but it does not!

1

There are 1 answers

1
Edgar Bonet On

If you have nothing else to do during the pulse, you could use a busy loop instead of interrupts:

#include <avr/io.h>
#include <util/delay_basic.h>

/* Send a pulse of width = 4*count cycles. */
void pulse(uint16_t count, uint8_t channel)
{
    uint8_t mask     = 1 << channel,
            old_port = PORTB,
            high     = old_port | mask,
            low      = old_port & ~mask;

    PORTB = high;
    _delay_loop_2(count);
    PORTB = low;
}

This will give you a resolution of 4 clock cycles, or 0.5 µs with a 8 MHz clock.

Sending the pulses to the 5 servos should take at most 10 ms. Since you repeat the pulse train every 21 ms, this leaves you 11 ms to compute the next set of positions, which should be plenty. You could program a timer to wake you up every 21 ms, then your main() may look like:

int main(void)
{
    static uint16_t pos[] = {4000, 3000, 3800, 2600, 3400};
    uint8_t i;

    /* Wake up every 21 ms. */
    setup_timer();
    sleep_enable();

    for (;;) {
        /* Update the servos. */
        for (i = 0; i < 5; i++) pulse(pos[i], i);

        /* Compute the next set of positions. */
        ...

        /* Wait for timer interrupt. */
        sleep_cpu();
    }
}