Flashing an LED with Attiny13: Timer, Watchdog timer and Sleep

Counting

led_flasherI have written two articles on timers in one article I discussed timers in general and in the 2nd article I went more into a couple of practical examples.
As I recently started to use the Attiny13 a lot, I thought it would be good to write something specifically on  how to use timers in the Attiny13.
As usual a Led flasher is a good example and we will start using the Timer OverFlow register to achive our task but also give an example using the Watchdog Timer.

Before I go on, if you are using the well known Smeezekitty core for the Attiny13 you are in trouble. In order to correct the timing, John Smeezekitty uses the Timer0 Overflow vector in his core for the Attiny13. You may want to use this core. If you continue to use that core you may want to  make this modification as well.
Also, if you would want to adopt this for another Attiny chip, apart from changing some of the register names (like TIMSK rather than TIMSK0), you may find that the Timer0 OverFlow Vector is  used in a variety of other cores. For the Attiny85 for instance, you may encounter a ‘Vector_5’  error, which is the Attiny85’s version of the Attiny13’s ‘Vector_3’error. Vector5 and Vector3 are the Timer0 Overflow vectors for resp the Attiny85 and the Attiny13
The ATtiny13 datasheet lists the timer interrupts (and other interrupts) in §9.1.
In Table 9.1 we find the Timer/Counter Overflow interrupt vector.
It is listed as TIM0_OVF. We then know the Interrupt Service Routine (ISR) we need to call in our program is “TIM0_OVF_vect”

The next chapter with information on the behaviour of the timer is in §11.7.1, it says that in “normal mode” (WGM02:0 = 0), the counter counts up, and once it gets to the “top” (0xFF as it is an 8 bit timer) it resets back to zero and the TOV0 (Timer/Counter Overflow Flag) bit is set.
How we enable the Timer/Counter0 Overflow Interrupt is found in §11.9.6: it states that if you enable the Timer/Counter0 Overflow Interrupt by setting the TOIE0 bit to 1 in the TIMSK0 register and interrupts are enabled, whenever the TOV0 bit is set, the interrupt will occur.

The timer that we are using to generate an interrupt in the Attiny13 is an 8-bit register. Unless ‘prescaled’ (see later) it increments once per clock cycle.  As it is an 8-bit register its maximum value is 255, therefore once every 256 clock cycles the timer reaches its maximum value and resets back to zero.  When this occurs the Timer/Counter0 Overflow Interrupt will be generated, and we can can catch this in and Interrupt Service Routine (ISR) in our program, to respond to the generated interrupt, and flash a LED.

The Attiny13, knows 3 major frequencies: 9.6 MHz, 4.8 Mhz and 1.2 MHz.
The frequency of 1.2 MHz is obtained by dividing  the internal 9.6 MHz clockspeed by 8 by setting some fuses. Often, when sold, those fuses are set and your brand new Attiny13  is set to the 1.2 Mhz clockspeed.
So, how often does that Timer Interrupt Overflow occur then?
Well that is easy: at 1.2 MHz, the length of a clock cycle is 0.00083333333333333 ms. As the interrupt is set when the timer register reaches 255, that means there are 256 Clock cycles (remember, we are counting from 0). Thus the interrupt occurs every 0.213333333 milisecs which is 1000/0.213333333=4687.5 per second. (I used a bit of an extensive calculation here to  make it clear. Normally you would just divide the frequency by the register (1.2Mhz/256=4687.5)).  That is a bit fast to  see the led flashing.

The standard practice then is to add a counter in the ISR and toggle the LED once the counter reaches a certain number of interrupt overflows.  Earlier we calculated that the interrupt will be triggered approx 4688 (1.2 MHz *1/256) times per second. With an 8 bit counter variable we cannot even count to 4688: we can count a max of 256 interrupts. as the interrupts occur every 0.2133333 msecs that would be 54.613333248 msec, which is about 18 times a second. Still a bit fast for a flashing LED.
If we use a 16-bit variable instead: 65536/4688 gives us a maximum flash time of ~13 seconds. So that is one way of doing it, but there is another (better?) way…

Earlier I touched upon ‘prescaling’ the timer.  This slows down the increment of the timer counter by using 1 out of every so many clock cycles to increment the timer counter (see §12.1).   These prescale factors are found in the datasheet in §11.9.2, table 11-9.  The largest slow down or ‘prescale’ factor is 1024 (by setting CS02 and CS00 bits to 1 in the TCCR0B register).

If prescaling is set to1024, the timer register gets incremented 1172 times a second (1.2MHz/1024), and thus overflows 4.6 times per second. With an 8-bit variable to count the overflows we can count to about 56 seconds.

This gives the final code:

volatile byte counter = 0;// interrupt needs volatile variable

ISR(TIMER0_OVF_vect) {
   if (++counter > 5) {// interrupt occurs 4.6 times per second
      // Toggle Port B pin 3 output state
      PORTB ^= 1<<PB3;// Toggle Port PB3
      counter = 0; //reset the counter
   }
}

void setup() {
   // Clear interrupts, just to make sure
   cli();
   // Set up Port B pin 3 mode to output by setting the proper bit
   // in the DataDirectionRegisterB
    DDRB = 1<<DDB3;

   // set prescale timer to 1/1024th 
   TCCR0B |= (1<<CS02) | (1<<CS00);// set CS02 and CS00 bit in TCCR0B

   // enable timer overflow interrupt
   TIMSK0 |=1<<TOIE0;// left shift 1 to TOIE0 and OR with TIMSK
                     //  = set TOIE0 bit
   sei(); //start timer
}
  void loop() {
      // ISR handles the flashing so no code here
   }

Sleeping.. with an alarm clock

With the LED flashing as in the program above, the Attiny13 doesnt do much more than waiting for the right amount of interrupts to pass by and then toggle the LED.
All that time it is using full energy. Now if only we could put it to sleep, only to wake up when the LED needs to be toggled,  theat would save energy consumption and thus extend  a possible battery life..
There are 3 sleep modes of which ‘Power down’ gives the deepest sleep and thus the least energy consumption. In order to wake from sleep the sleeping chip either needs an external interrupt, or it needs to be woken by the watchdog timer. The watchdog timer is described in chaper 8 of the Attiny13 datasheet. Its operation is quite simple: it needs bits set in the Watchdog Timer Control Register (WDTCR) to determine the time it will run and then  it needs to be enabled to start it. When the watchdog timer sends an interrupt, the WDT_vect ISR is called and executed and the program goes back to where it was put asleep.
As we only need to toggle an LED, that can be done in the WDT_vect ISR


ISR(WDT_vect) {
// Toggle PB3 output
PORTB ^= 1<<PB3;
}

void setup(){
// Set PB3 to output
DDRB = 1<<DDB3;
//set timer to 1 sec
WDTCR |= (0<<WDP3) | (1<<WDP2) | (1<<WDP1) | (0<<WDP0);
// set timer to 0.5s
//WDTCR |= (0<<WDP3) | (1<<WDP2) | (0<<WDP1) | (1<<WDP0);

// Set watchdog timer in interrupt mode
WDTCR |= (1<<WDTIE);
WDTCR |= (0<<WDE);

sei(); // Enable global interrupts
}
void loop() {
// Everything is already handled in the ISR
}

This program will  let a led flash every second, or should you desire, flash within a second.
Once we have done that we can put the the  Attiny to sleep, only to be woken up by the Watchdog timer.
To handle the sleep mode we make use of the sleep.h header file that is part of the avr library. In the setup we set the sleepmode to Power Down. with that we in fact set two bits in the MCUCR register, SM[1:0] to 1 resp 0.
Then in the loop we do a call to sleep_mode()
The command sleep_mode() in fact makes a call to three routines:

sleep_enable(); \
sleep_cpu(); \
sleep_disable(); \

sleep_enable() sets the Sleep Enable bit in the MCUCR register
sleep_cpu() issues the SLEEP command
sleep_disable() clears the SE bit.
So what happens is that at the end of the setup routine the watchdog is set and the sleepmode is set. Upon entry in the loop the is put to sleep and kept in sleepmode. When a Watchdog interrupt occurs, the Attiny13 wakes up, the interrupt routine is carried out and the  program jumps back to where it was  put asleep. there it finds the sleepdisable macro that resets the Sleep Enable bit. This is in case  you want to perform more tasks. But as there aren’t any more tasks, the Attiny is brought right back to sleep again, till the next Watchdog interrupt

#include <avr/interrupt.h>
#include <avr/sleep.h>
ISR(WDT_vect) {
// Toggle Port B pin 3 output state
PORTB ^= 1<<PB3;
}

void setup(){
// Set up Port B as Input
DDRB = 0; // usually not necessary but it will save current
// Set Port B pin 3 mode back to output
DDRB = 1<<DDB3;

//set timer to 1 sec
WDTCR |= (0<<WDP3) | (1<<WDP2) | (1<<WDP1) | (0<<WDP0);
// set timer to 0.5s
// WDTCR |= (1<<WDP2) | (1<<WDP0);
// set timer to 4 sec
// WDTCR |= (1<<WDP3);

// Set watchdog timer in interrupt mode
WDTCR |= (1<<WDTIE);
WDTCR |= (0<<WDE);
sei(); // Enable global interrupts

set_sleep_mode(SLEEP_MODE_PWR_DOWN);
}
void loop() {
sleep_mode();   // go to sleep and wait for interrupt...
}

In order to save  even more energy, there is more we can do:  disable the ADC channels and set  all I/O lines to input (but when out of sleep you have to set back the one that toggles the LED).
In the setup routine we can switch the ADC off by the command:

cbi(ADCSRA,ADEN);    // switch Analog to Digitalconverter OFF

or

ADCSRA &= ~(1<<ADEN); //

Which is the same

With the commands:

ADCSRA &= ~(1<<ADEN);     //turn off ADC
ACSR |= _BV(ACD);         // disable analog comparator

I got the following results:
sleep: 6.6uA (0.0066mA)
non sleep: 3600 uA (3.6mA)
That is a factor 600. Of course this was with the LED unplugged as I was only interested in the consumption of the chip. The LED would add another 4-5 mA.
Disabling the analog input buffers with the command:

DIDR0 |= (1<< AIN1D)|(1 << AIN0D);//disable

didn’t change the power-consumption. It should be possible to go even lower with lower chip frequency (mine was 9.6Mhz) and lower voltage (mine was 5V). On 3.3 Volts the current went to 5uA. Mind you that also the use of the WDT ‘contributes significantly to the power consumption’ according to the datasheet.
If you are using an attiny85, it may pay to also issue the   power_all_disable(); command that sets the PRR, but the attiny13 doesnt have that. The Attiny13A does, but as the core used is thinking it is an Attiny13, the <avr/iotn13a.h> file is not loaded.
One can counter that by adding the following definitions:

#define PRR _SFR_IO8(0x25)
#define PRADC 0
#define PRTIM0 1

and for the BOD CONTROL Register

#define BODCR _SFR_IO8(0x30)
#define BPDSE 0
#define BPDS 1

However, activating the PRR register or closing the BOD did not change the power consumption of my attiny13A.

Note: if you are using the MicroCore for the attiny13 you may get a vector 8 error. That is because the WDT is already used to set millis(). You then need to disable that in the core_settings.h file. If you installed the core via the boards manager you are most likely to find it here: user\xxxxxxx\AppData\Local\Arduino15\packages\MicroCore\hardware\avr\1.0.2\cores\microcore

Advertisement

Attiny Motorboard

tinystepper

For a project I needed to drive a steppermotor, a servo and possibly a small regular DC motor. Piece of cake of course with an Arduino, but I am anal retentive where it comes to underusing an Arduino and as I had come by a couple of Attiny13A’s why not use those.attiny-stepper

A circuit is easily drawn up and doesn’t hold too many surprises. The chip of choice is the ULN2003 that can take 500mA per port. Driving a steppermotor for a while can make the chip unpleasantly hot and adding a heatsink is a wise precaution. My Steppermotor, the 55SI-25DAWC (1350 g/cm torque) has a 6 prong connector with the two midconnectors that receive the Motor Voltage on one side and the 4 coil connectors next to that, so that kinda dictated my design on K1. It is 36 Ohm at 12 Volt, so each coil uses 333mA.
The servo connector K2 is rather standard: Signal-Vcc-Ground. The DC motor connector K3 is fed by the open collector output of the ULN2003. The signal for that is shared with the Servo, as for now I probably wont be using both, but If I need both, I might be feeding it off of PB5, though that would mean resetting the fuses to disable the reset that is attached to pin1. It also means I need a high voltage programmer if I need to reprogram the chip.tinystepper-stripboard

A prototype is easily set up on a piece of stripboard. The interruption of the second strip between F and G is just there in case I want to add a jumper to switch between servo use and ULN2003 use on Output Q3. Rather than making K3 just a two prong connector, I decided to add an entire row that is connected to V++, so if I so wish, I can also use Q1-Q7 just for DC motors. It gives a bit more flexibility. As the Attiny13 has a max of 6 I/O pins and the ULN2003 has 7 drivers, one can always decide to use the drivers in parallel to drive bigger loads.

Programming:
The Attiny13 has only 1 kByte available, but that is enough for a simple application with a servo and a steppermotor.
As my steppermotor is a rather old one already, I doubt if too many people have it. so my program is most likely no use for anybody else, but driving steppermotors is rather easy.
With the unipolar steppermotor that I use, there are 3 ways of driving it: Halfstepping, Full stepping or wave driving
Wave driving is the easiest: you just make each pin high in the right sequence.coils.jpg
For a unipolor motor that would be: 1a-2a-1b-2b. That would spin the motor in low torque because there is always a moment in which there is no coil magnetized.
Half stepping requires alternating between energising a single coil and two coils at the same time. This increases the angular resolution, but the torque fluctuates between utilizing a single coil and both coils. In principle though it means that you keep one coil magnetized while you already switch on the second one. So that would be:
1a-1a2a-2a-2a1b-1b-1b2b-2b-2b1a,
Full stepping is when two coils are always energized at the same time. This allows the maximum torque of the motor to be used, at the expense of more current. This looks like:
1a2a-2a1b-1b2b-2b1a.
For completeness sake: the 55SI-25DAWC can also be used as bipolar stepper motor just by not using the white wires. This gives more torque and less power consumption
The below program is just a quick illustration of how to drive a stepper in Full stepping mode. The program was written on the basis of the Smeezekitty core for the Attiny13. For the MCUDude microcore a small change is necessary, indicated in the program below:

/*
                                _____
                               |     |
 Hardware:             +5V     |     |
             ________   |      |     |     
      ___   |        |  |      |     |  ___  
 +5V-|___|--|RES  VCC|--+   B3-|I4 O4|-|___|-+Q3 brown
            |        |         |     |  ___  
        B3--|PB3  PB2|---------|I5 O5|-|___|-+Q2 yellow
                     |         |     |  ___  
          --|PB4  PB1|---------|I6 O6|-|___|-+Q4 blue
            |        |         |     |  ___  
         |--|GND  PB0|---------|I7 O7|-|___|-+Q1 red
            |________|         |     |       
             ATtiny13        |-|GNDCC|------- +12Volt white
                               |_____|    |__ +12Volt white
                                ULN2003 
Funktioning: sequence: red-yellow-brown-blue
    Stepmotor drive sequence and encoding:
      Portbit      PB0   PB1   PB2   PB3   |            |
      Color         rd    bl    yl    bn   | Portbit    | Byte
      Step          O7    O6    O5    O4   | 3  2  1  0 |
      -------------------------------------+------------+------
         1          1      0    1     0    | 0  1  0  1 |  05
         2          0      0    1     1    | 1  1  0  0 |  12 / 0C
         3          0      1    0     1    | 1  0  1  0 |  10 / 0A
         4          1      1    0     0    | 0  0  1  1 |  03
If you would use D8-D11 on an Atmega328, you could use the same values 
for the Portbits, as D8-D11 is equivalent to PB0-PB3

Coils
 red    ------------+------------ brown
 yellow-------------+------------ blue
 */
 #define delayTime  200// time between steps
  byte servoPin=4;
 void setup()
 {
 DDRB=31; // set Port 0-5 as output
 }
 void loop()
{
 stepper(3); //=90 degrees. The angle is 7.5 degr,=> 4 steps=30 degr.
 }
 void stepper(byte turn)
 {
 for (byte pos=0;pos<turn;pos++)
 {
 PORTB=5;
 delay(delayTime);
 PORTB=12;
 delay(delayTime);
 PORTB=10;
 delay(delayTime);
 PORTB=3; delay(delayTime);
 }
delay(1000);
 }

The above program is just rough, to show how to drive a stepper and it misses some sophistication. Writing directly to PORTB is OK, but in this case it will also write a “0” to PB4 and PB5. PB5 is not much of a problem, but you may want to use PB4. In my case that is where I put my servo and that doesn’t really cause a problem as I do not use them at the same time.
If you want to avoid writing to PB4 and PB5, use a mask to only write to bit 0-3. The mask to do that is B00001111.
If you then want to set bits bits 0 and 2, go like this:
PORTB=(PORTB &~mask) | (B00000101);
For those who find this too cryptic:
it first ANDs the value of PORTB with NOT mask and OR’s the result with the value we want to write and assigns that back to PORTB.
So, suppose PORTB= 00010000 and we want to write 00000101 to it, we cannot assign that immediately because that would clear PB4.
However, if we do as described, it becomes:
PORTB=(PORTB & 11110000) | 00000101
PORTB=(00010000 & 11110000) | 00000101
PORTB=00010000 | 00000101
PORTB= 00010101
We have written our value and kept PB4
So, why cant we immediately OR PORTB with the value we want in stead of AND-ing it first?
Well because that might keep PB4 and PB5… but it also keeps PB3-PB0 unchanged if one of them already contained a ‘1’
Of course inverting the mask wouldn’t be necessary if we would define it already inverted, but it is common practice to do it as such

Writing to the Servo is quite easy:
add this function:

void pulseOut( byte pin, byte p){
digitalWrite(pin,HIGH);
//delayMicroseconds(300+p*(2500/180));// this is no longer allowed in the Microcore
delay(5);// so we use this
digitalWrite(pin,LOW);
}

and call that like this:

for (byte pos=0;pos<180;pos++)
{
pulseOut(servoPin,pos);
delay(20);
}

Full program here.  (backup)

High Torque mode uses more power than low Torque mode. In Low Torque mode each step in my case (12V, 36 Ohm coil resistance) takes 333mA. In high torque mode, each step takes 667mA (2×333.33).

In case you are wondering what project I needed it for: I need a doll to turn and nod its head.

Self build alertSmart-Electronics-28BYJ-48-5V-4-Phase-DC-Gear-Stepper-Motor-ULN2003-Driver-Board-for-Arduino

I enjoy building stuff, but it isn’t always the wisest thing to do. There is a ULN2003 based driver board, including a good steppermotor available for about 1.80 euro. That is a good deal. In my case, I just wanted the Attiny13 and ULN2003 driver to be on one PCB, I already had a spare ULN2003 and I had the steppermotor so that was a no brainer, But for anybody else wanting to drive a steppermotor, consider that cheap motor and driver.
My cost for just the driver section (chip, pins, socket), I estimate 45 ct (euro). Not sure what my stepper costed me 30 years ago, but a decent hobby stepper now will be between 1.60 euro and 5 euro, so the savings in self built (not even counting the solder and electricity use) are minimal to non existent. So only do it for fun, educational purposes or if you need something tailored like i did.

I have a 3d file for a motorflange and housing for the commercial board avaiable.

That trusty Attiny13
Finally I have to praise the Attiny13 for being extremely resilient. Due to some stupidity from my side, I connected PB3 with O4 instead of I4, So it received 12 Volt through a 36 ohm resistor and also tried to drive a 330mA motor coil. This situation lasted for at least a week in which it would sometimes be steering the stepper for hours, with me trying to find out why it wasn’t working optimally.
I only found out when I accidentally felt the chip was getting pretty hot.
I fixed the connection and the chip was still working (and the motor turned better). A week of 12 Volt on one of its pins and getting hot and still working great