1

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!

CrowStudio
  • 75
  • 1
  • 9
  • The code shown in link to TinyServo is not directly illustrating what it is you are trying that is not working. You should consider showing what you have tried, even if, as you say, it does not make any sense. – ryyker Jun 10 '15 at 13:00
  • 1
    You could use the lower 8bit of your integer to trigger the ISR, in the ISR you cound how many times it happend incrementing another 8bit value. If that value matches your upper 8 bit, then you did the trick. – Nidhoegger Jun 10 '15 at 13:05
  • @ryyker I posted the code now ;-) – CrowStudio Jun 10 '15 at 13:11
  • @Nidhoegger - can you illustrate what you just said in an answer? – ryyker Jun 10 '15 at 13:35
  • @Nidhoegger Attached a picture that illustrates the code above. was that how you meant? – CrowStudio Jun 10 '15 at 17:14
  • .... sorry I thought that it was you @Nidhoegger that wrote the question about the illustration, hope it helps anyway! ;-) – CrowStudio Jun 10 '15 at 17:15
  • the function `servoPosSet()` should have two arguments, one the array and the second the number of elements in the array. sizeof() provides the number of bytes in a variable so your loop is going to be off in `servoPosSet()` and in the other places where you are trying to use `sizeof()` to determine the number of array elements. – Richard Chambers Jun 10 '15 at 17:17
  • @RichardChambers Aha, I thought that sizeof(servo), sizeof(hPulse) or sizeof(pos) returned the number of the array elements, ergo 5? – CrowStudio Jun 10 '15 at 17:29
  • For an array I would typically use something like `sizeof(ray)/sizeof(ray[0])` which will take the total number of bytes in all of the elements of the array and then divide by the size of a single array element. Also when you call a function and pass it the array like you are doing, the function will not know the number of elements of the array. The array basically gets turned into a constant pointer. Many people call this array decay into pointer such as in here http://stackoverflow.com/questions/1461432/what-is-array-decaying – Richard Chambers Jun 10 '15 at 17:36
  • @RichardChambers Check! I read the post http://stackoverflow.com/questions/37538/how-do-i-determine-the-size-of-my-array-in-c a few moments before I saw your answer, so I got it now (i think)! Thanks! :-) – CrowStudio Jun 10 '15 at 17:49
  • See if correcting array element count makes a difference and let us know. I took a look at some documentation, this is not something I know anything about, and it appears the compare match ISR involves loading a value into a register to let the hardware do the calculation of when to stop moving. This does not seem workable with your approach. I expect you will need to run through your array of servos and move each one a little bit as you cycle through the list until each of the servos is at the desired position. You seem to be doing this but your array end detection is wrong using sizeof(). – Richard Chambers Jun 10 '15 at 17:58
  • I think @Nidhoegger is thinking of treating the 16 bit value as a series of 8 bit values that are loaded into the ISR. If `ui16Val` is your 16 bit value and `ui8Val` is the 8 bit value to load into the ISR you would do something like: `ui8Val = (ui16Val & 0x00ff; while (ui16Val) { SendISR (ui8Val); ui16Val &= 0xff00; ui16Val >>= 1; if (ui16Val) ui8Val = 0xff; } ` The idea is to treat the most significant byte of the 16 bit value as a counter for the number of times to send down 0xff as the ISR counter value once the initial lower 8 bits are sent. But some kind of ISR synchronization need. – Richard Chambers Jun 10 '15 at 18:17

1 Answers1

1

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();
    }
}
Edgar Bonet
  • 3,116
  • 12
  • 18
  • This is close to something I looked at some time ago! It felt right but I didn't understand it at the moment, and also it was not as on-the-spot as this example! Now all I have to do is manage to free some time and turn on the light up there! ;-) – CrowStudio Jun 16 '15 at 18:17