Digispark as multiple servo driver.

Arduino projects on the go
MaxZ
Posts: 145
Joined: 31 Jan 2019, 11:48
Location: Boskoop, Netherlands

Digispark as multiple servo driver.

Post by MaxZ » 09 Jul 2019, 13:11

I will have a go at programming the Digispark as a multiple servo driver, to be used for instance onboard a model as a sequencer for a retractable landing gear with -doors or such.

I have done similar projects before, but these were based on the 16-bit ProMini or Nano boards, so I could use the Servo library from the Arduino IDE. The Digispark is based on the ATtiny85, which has an 8-bit processor and requires a different library. I found one on the net: http://www.cunningturtle.com/attiny4585-servo-library/ , installed it and made use of it in a very simple sketch, just driving a single servo to a fixed position. I avoided the use of Delay(), to prevent any clashes in the timing. Alas, no luck, none of the pins showed any pulses when checked with a servo and Phil_G's pulse tester.

Next, I wrote a simple sketch using the standard IDE Delay() function, similar to the familiar Blinky, but with writeMicroseconds() set to 1500 high and 18500 low periods to create a PWM output. That worked, but things were not as expected:
- the output pin had been specified as 3 (later PB3), but the output appeared on PB1 instead. Note that pin #3 on the ATtiny85 is specified as PB4, not PB1. Go figure
- the pulses came out as 750, with a frame time of 10000, so only half the value specified. I programmed the Digispark with the timer set at 8 MHz. In order to double the delay periods I guess I have to set it at 4 MHz, but the only other options were 1 MHZ and 16 MHz. I tried both, but both did not produce a readable result on the pulse checker, probably because they were out of the normal PWM range.

I could continue on that route by simply multiplying the delays by 2, or try to follow the suggestions Phil made here, I will see where it gets me.

Cheers,
Max.

MaxZ
Posts: 145
Joined: 31 Jan 2019, 11:48
Location: Boskoop, Netherlands

Re: Digispark as multiple servo driver.

Post by MaxZ » 09 Jul 2019, 13:27

The Digispark, properly "defluffed" as per Phil's instructions, and extended with a strip of veroboard to connect the servo pins. The USB-tab will be hacked off later:
DSC03899.JPG
And with the USBasp programming adapter cables connected. The largish flag is to remind me that I should swap the +5V and Reset leads when I use them on the ProMini (for which they were originally intended as per Mike_K's description in his S/C Tx design documentation):
DSC03898.JPG
Max.

User avatar
Phil_G
Posts: 295
Joined: 15 Feb 2018, 23:32
Contact:

Re: Digispark as multiple servo driver.

Post by Phil_G » 09 Jul 2019, 15:15

Hi Max
The DigiSpark as supplied should have 'blink' (P1) loaded and be set to the 16Mhz PLL clock option, so by setting the IDE to 8mhz you're halving your delays. Having said that I've had a few (out of 200+) that have neither 'blink' loaded nor any changes to the ATTiny85 chips 'as supplied' 1Mhz clock.
Set the IDE to 16mhz and click Tools, Burn Bootloader. It wont burn a bootloader (you dont want one!) but it does set the clock to 16Mhz PLL. The accuracy of the intenal clock is good but if you want to get it exact, play with OSCCAL (values around 70 to 100 ish).

Output port numbering is different to physical processor-chip pin numbers on all Arduinos, thats not particular to the Tiny85, but with the correct damellis 'boards' setup the pin numbering in the IDE should match the DigiSpark P0 - P5.

The clock settings in the IDE have no effect on the ATTiny's clock, its purely to inform the compiler what speed the chip is running at, it has no way of knowing if you dont tell it.
This is so the compiler knows what factors to use for delays etc.
Programming a 16mhz chip with the IDE set to 1mhz will only result in delays being 1/16 their expected value, ie 16x too fast!

With the exception of 'burn bootloader' the IDE clock setting does nothing to reconfigure the ATTiny85's clock. So, when you 'set it faster' you're telling the compiler to use longer delays.

Say for example the chip is physically configured for 8Mhz. If you wrongly tell the compiler its running at 16mhz, the code produced will be for 16mhz. Your chip runs it at 8mhz and it so it runs slower, not faster.

'Burn bootloader' is the one exception where the IDE does actually set the clock speed within the chip.

Re your output on 1 rather than 3, thats because you defined 'servout' as 'bool'. Boolian values can only be true or false, zero or 1, it cant be 3, but it can be the least significant bit of 3 which is 1. Your program isnt misbehaving, its doing exacty what you've told it to do!

Forget chip pin numbers, just go on the Arduino port numbers Image

Also delayMicroseconds only takes a parameter up to 16383, use delay() instead ;)

The code around your 1500uS delay takes time to run, and even digitalWrite() isnt instant.
You can reduce the time wasted either side of the pulse by flipping bits directly in the port register:

Instead of "digitalWrite(3,HIGH);" you can use PORTB = 0b00001000; which is faster, and
instead of "digitalWrite(3,LOW);" use PORTB = 0b00000000;

Even easier you can halve the wasted time by running the chip at 16mhz. Both would be optimal.
In timing-critical applications I'd always say run the chip flat out, unless its slowed to sync with something slower, like some video apps etc. Despite claims to the contrary compiled HLL code is almost always bigger & slower than hand-crafted assembler, you make up for this by running at its fastest clock rate Image

The port manipulation method gets a bit more complicated when you have more than one servo, when the required bit is "OR'ed in" and "AND'ed out". Like PORTB |= (1 << 3); to set it and PORTB &= ~(1 << 3); to reset it. The ~ is 'NOT' so "1 << 3" (b00001000) becomes (b11110111). This means it only resets the required bit (P3) and leaves the rest as they were.

The pause between pulses (18-20ms ...ish) isnt critical at all but note that delayMicroseconds only takes a parameter up to 16383 maximum. 'Overloading' it will affect the timing. As its not critical, delay(18) will do nicely Image

I like your servo headers, very neat :D

Cheers
Phil

Martin
Posts: 315
Joined: 16 Feb 2018, 14:11
Location: Warwickshire

Re: Digispark as multiple servo driver.

Post by Martin » 09 Jul 2019, 15:22

Yes, I normally set the clock option in the Arduino IDE to 16MHz internal, "burn bootloader" as Phil described, and then send a blink sketch to the Digispark to make sure it's running at the speed I expect. If you do the normal blink thing with delays of 1000 (milliseconds) for the high and low phases, then you can watch the output with an LED or meter or whatever and check that it is indeed performing a 2-second cycle.

You only have to 'burn bootloader' once - what it actually does is configure the ATtiny's internal 'fuses' to select the correct clock speed option. It won't harm the chip if you do it more than once though.

MaxZ
Posts: 145
Joined: 31 Jan 2019, 11:48
Location: Boskoop, Netherlands

Re: Digispark as multiple servo driver.

Post by MaxZ » 09 Jul 2019, 19:08

Thanks guys. I used this board:
Schermafbeelding 2019-07-09 om 19.05.34.png
Schermafbeelding 2019-07-09 om 19.05.34.png (13.46 KiB) Viewed 997 times
As I said, a choice between 1MHz, 8MHz and 16 MHz as clock speed. In my innocence I believed simply selecting a speed would set it through uploading the sketch, I now understand I should use "burn bootloader" for that.
What I don't understand is why increasing clock speeds will result in longer delays, I would have thought these would be shorter at faster clocking.

As for the output pin, here is the sketch, but the pulse appears on PB1(pin 6 on the chip)?
Schermafbeelding 2019-07-09 om 19.06.16.png
Cheers,
Max.

MaxZ
Posts: 145
Joined: 31 Jan 2019, 11:48
Location: Boskoop, Netherlands

Re: Digispark as multiple servo driver.

Post by MaxZ » 09 Jul 2019, 20:01

Ok Phil, I think I understand now :|
Anyway, I "burned the bootloader" at 8MHz, and the output is now more or less as expected. A bit disappointing is that the programmed 1500 us pulse has a spread from 1493 us to 1505 us, so more than a trim pip of 10 us, which reduces the practical use of this method to generate servo pulses.

Re. pinout: what a silly mistake, bool instead of static int. You are right, I was getting what I asked for... :roll: :D

Cheers,
Max.

MaxZ
Posts: 145
Joined: 31 Jan 2019, 11:48
Location: Boskoop, Netherlands

Re: Digispark as multiple servo driver.

Post by MaxZ » 09 Jul 2019, 22:43

It is not the inaccuracy caused by processing time that surprises me, but the fluctuation. I see quick changes between 1493 microseconds and 1507 microseconds.

Direct port manipulation...hmmm...interesting idea (and new for me). Now I have to learn bitwise manipulation :shock:
But that is why I am posting about this project in the first place, I regard it as a vehicle to learn new skills. I'll give it a try (tomorrow). Thanks for coaching me this far.

Cheers,
Max.

Martin
Posts: 315
Joined: 16 Feb 2018, 14:11
Location: Warwickshire

Re: Digispark as multiple servo driver.

Post by Martin » 09 Jul 2019, 23:31

The fluctuation or "jitter" as we usually call it, is because the microcontroller is sometimes interrupted from your task by the need to do some background job. For example, the millis() clock has to be updated every millisecond, and that is done by a hardware timer generating an interrupt. Even if the interrupt routine is short, say 32 machine instructions, that's still a delay of 32 machine cycles before it gets back to running your code. The Atmel microcontroller, I think, executes many (all?) Instructions in a single clock cycle but at 8Mhz a 32-instruction sequence is still a delay of 4 microseconds.

MaxZ
Posts: 145
Joined: 31 Jan 2019, 11:48
Location: Boskoop, Netherlands

Re: Digispark as multiple servo driver.

Post by MaxZ » 10 Jul 2019, 08:44

I simplified the code somewhat (wiping out my error :oops: ;) ) and I have clocked the Digispark now at 16MHz. As predicted by Phil, the jitter is reduced:
Schermafbeelding 2019-07-10 om 09.31.24.png
Then I tried the direct port access, which gives about the same amount of jitter, but for some reason is slightly further off the 1.5 ms mark (1487-1495 vs 1491-1497). Nothing to worry about though :
Schermafbeelding 2019-07-10 om 09.31.39.png
Cheers,
Max.

MaxZ
Posts: 145
Joined: 31 Jan 2019, 11:48
Location: Boskoop, Netherlands

Re: Digispark as multiple servo driver.

Post by MaxZ » 10 Jul 2019, 15:42

Phil_G wrote:
10 Jul 2019, 14:55
Just as an experiment, put noInterrupts(); at the start
I think your jitters will disappear ;)


Error at compiling for ATtiny25/45/85

I also experimented with replacing the delays with a counter based on Micros(), but the jittering is still there :(

Cheers,
Max.

Post Reply