We will first look at how to program some of the more physical aspects of the microcontroller, like values for pin’s and using interrupts. After that we will look at the logic aspects of our program.

Here I will go through the following 4 type of tasks:

Set a pin as output

Set value on selected pin to ‘1’ or ‘0’

Make the program wait

Creating a loop where we keep running the program

Setting a pin as output

Setting the pin as output is controlled by the Data Direction Register, in this case for Port B (DDRB). In order for us to control this, we need to:

Find the address of the Data Direction Register for Port B

Find what bit in this register that controls if the pin is input or output

In the datasheet we find the following information for register DDRB:

Register Address

Name

Further details

0x17

DDRB

Page 56

The address os the DDRB register is 0x17 and we can find further details on page 56 – please see below:

Port B Data Direction Register

Bit

7

6

5

4

3

2

1

0

Pin name

-

-

DDB5

DDB4

DDB3

DDB2

DDB1

DDB0

Initial value

0

0

0

0

0

0

0

0

Physical pin

1

3

2

7

6

5

We can see what bits control each of the pins, and we can also see what the initial value of the bits are.

So if we want to make pin 3 output, then we need set this to ‘1’, and since we are writing an entire byte, the byte we need to write here is: ‘0000 1000’.

So our task for the program is:

Write ‘0000 1000’ to DDRB

The assembler instruction for this is: OUT DDRB, register X (register with value we want to load)

We only need to set this once, as we need this to be enabled for output for the entire time.

Set the value of the pin high ‘1’ and low ‘0’

The is controlled from the Port x Data Register, as we are working with Port B, it is the PORTB register – the address is 0x18 (you can check this in the datasheet – LINK TO DATASHEET) and we can find further details on PORTB on page 56 again.

From the datasheet we see that bit 4 is controlling PRTB4, so to set this high ‘1’, we need to write the byte ‘0001 0000’ to this address, when we want the LED to light up, and ‘0000 0000’ when we want to turn the LED off.

So here the programming task is:

First write ‘0001 0000’ to PORTB

after wait

Write ‘0000 0000’ to PORTB

The assembler instruction for this is: OUT PORTB, (register with – 0b00010000) for high, and

OUT PORTB, (register with – 0b00000000) for low.

In between these two commands, we have the wait statement.

Let the program wait

If we look at the possible instructions available for us in the Attiny13A, there is no direct wait x seconds or cycles instruction.

In order to perform a wait for a microcontroller there is basically 2 options;

Make the microcontroller perform a number of instructions that really do nothing but wasting time. Since we know how many clock cycles an instruction takes and we know how many clock cycles the microcontroler runs at, we can make some calculations of how many instructions would we need to run in order to ‘waste’ 1 second

The other option is to have something external that would indicate that a certain amount of time has passed – interrupts.

After reading up on both approaches I decided to try to use the interrupt approach. So we need to look at a few things on interrupts first.

Interrupts

Interrupts are external to the CPU and can be external to the microcontroller or we can use internal interrupts. For our purpose we will use the timer within the microcontroller to create the interrupt.

The timer in our microcontroller is just a counter, that counts (it will count 1 for every clock cycle), and as we are working with an 8-bit, we can only count from 0 – 255, before starting from zero again.

If we are running the microcontroller at 1 MHz (that is 1 million instruction, or counts, per second), then this will only takes us 256 nano seconds, to count from 0 to 255 – we would need to do this 1,000,000 / 256 = 3906 times to give us approxamately one 1 second.

Prescaling

To make this easier we are going to use an option for the AVR microcontroller’s to make the timer run slower than the CPU – this feature is called prescaling. So while the microcontroller still runs at 1 MHz, we can force the timer to run at a fraction of this speed.

If we look at the datasheet for the ATtiny13A (table 11-9) we see the following prescaling options:

Bit 2

Bit 1

Bit 0

Description

0

0

1

clock IO

0

1

0

clock IO / 8

0

1

1

clock IO / 64

1

0

0

clock IO / 256

1

0

1

clock IO / 1024

From the above we see that we have 4 options for making the timer run slower – the 4 options are:

1/8 of the clock speed → 8 clock cycles will make the timer count 1 time

1/64 of the clock speed → 64 clock cycles will make the timer count 1 time

1/256 of the clock speed → 256 clock cycles will make the timer count 1 time

1/1024 of the clock speed → 1024 clock cycles will make the timer count 1 time

If we use the 1/1024 option, that would mean that the counter only count 1 up, for every 1024 clock cycles.

So using this math:

1 Mhz processor = 1 million clock cycles per second

Timer only count 1 for every 1024 clock cycles

For the timer to count from 0 to 0 again (255 + 1 count) that would now take:

256 counts * 1024 clock cycles = 262,144 clock cycles

262,144 clock cyles approxamately ¼ of a second

From the above that gives us that we would use up approximately ¼ of a second to count from 0 – 255. If we do this 4 times, that would give us close to the 1 second delay we are looking for.

The register where we configure this is TCCR0B at address 0x33:

Bit

2

1

0

Name

CS02

CS01

CS00

Initial value

0

0

0

Bit 3 to 7 are not relevant for our purpose.

The assembler code: LDI, TCCR0B, 0b0000 0101

That gives us the next task – how do we keep track of how many times the counter have counted from 0 to 255?

Overflow flag and interrupts

When the timer hit’s the 255, it will throw an overflow flag – so if we count how many times we get this overthrow flag, we know that after 4 times, we have 1 seconds. One option would be to always poll for this overflow flag, but the other option, the one used here, us to have the overflow flag create an interrupt request – and this is when our program will add one to how many times we have seen the overflow flag.

When the CPU receives our interrupt request, the running program will finish it’s current instruction and then perform a jump to the address where the code associated with the interrupt exists.

So our task is to create the configuration for the interrupt triggered by the timer. This has 2 steps:

Make the timer send an interrupt request

Let the microcontroller know what to do when the interrupt happens

Enabling the timer to send the overflow flag is done by setting bit 1 (‘0000 0010’) in the TIMSKO (Timer/Counter Interrupt Mask Register), see in the datasheet in section 11.9.6.

The assembler code for this: LDI TIMSKO, 0b00000010.

Know when the timer overflows (that is at the point where the counter goes from 255 to 0), the AVR will look in the table of interrupt vectors (table 9-1) if any address is present – if yes jump to this address.

So we need to place a jump to our code that reacts to the overflow in address 0x0003 (binary 0000 0011′) – IN THE PROGRAM CODE. One interesting feature is that if we let the overflow flag trigger this interrupt, it will automatically be reset. As a result we do NOT need to write any code that reset this overflow indicator.

Please note, that here we are asked to set a value in the programming address space, not an register. That creates the necessity to avoid writing any of our program code in this specific address.

The assembler code: LDI 0b00000011, xxxx.

In summary we have so far:

Found the register where we need to set the pin we plan to use (pin 3) as an output pin.

Found the register where we can control the value of this pin (high/low)

Found the register where we can make the timer run at a slower speed than the CPU

Found the register where we can enable the timer

Found the address where we can insert the address of the program to run when an interrupt happens

Now it is time to look at some of the logic we want to create in the program.

Since we know that in order for us to measure 1 second, we need to track that the overflow flag from the timer has done this 4 times, we need logic to keep track of the number of interrupts.

The program will have to do something similar to this:

set a variable to 4

subtract one for each interrupt

if variable is 0, then change the output pin to other state (if pin is ‘1’ then change to ‘0’ and visa versa)

If not, then go back to sleep

Then go back to step 1

Set a variable
Setting a variable in assembler can be done with the LDI command, and we will use register 16 (R16) to hold this variable: