Assembler blink example

This code comes as an example for a first blinking program. It does work on my board, however I don't understand why. I don't understand why sbi PINB, 5 ; Toggle PINB should toggle the pin instead of just turning it on. I am a total newb, so please be gentle.

#define __SFR_OFFSET 0

#include "avr/io.h"

.global main

main:
  sbi   DDRB, 5     ; Set PB5 as output

blink:
  sbi   PINB, 5     ; Toggle PINB
  ldi   r25, hi8(500)
  ldi   r24, lo8(500)
  call  delay_ms
  jmp   blink

delay_ms:
  ; Delay about (r25:r24)*ms. Clobbers r30, and r31.
  ; One millisecond is about 16000 cycles at 16MHz.
  ; The inner loop takes 4 cycles, so we repeat it 3000 times
  ldi   r31, hi8(4000)
  ldi   r30, lo8(4000)
inner:
  sbiw    r30, 1
  brne    inner
  sbiw    r24, 1
  brne    delay_ms
  ret

A pin can have two values. Generally, a toggle command 'flips' the value, so if it's a 1 it becomes a zero, but if it's a zero, it becomes a 1.

It's the functional equivalent of, in C:

if(digitalRead(pin) == 1) 
    bit = 0;
else
   bit = 1;

or, if you understand the ternary operator:
digitalRead(pin) ? 0 : 1;

I am aware of what toggle is. My question is why the line

sbi PINB, 5

should toggle the pin. As far as I understands this line should set it to ON, no matter what

But isn't sbi just setting the bit? I think that that is where OP's confusion comes from. I did not look at the hardware implementation so it might toggle in hardware.

3 Likes

you can add the same instruction but use CBI instead of SBI to clear the bit in PINB

the pin is directly connected to the embedded led in the Arduino uno board

Yes, I think it is.

@vladosm when programming in assembler, it is essential to check the hardware details - here, the chip datasheet.

You haven't said what chip you're using, but I'd guess ATmega328P...?

2 Likes

because that's the way that register works at hardware level ➜ writing a 1 in a bit flips that bit on the output

PORTB maps to Arduino digital pins 8 to 13 The two high bits (6 & 7) map to the crystal pins and are not usable

so if you run on a UNO

void setup() {
  pinMode(13, OUTPUT);
}

void loop() {
  PINB = 0b100000; // flip pin 13
  delay(500);
  PINB = 0b100000; // flip pin 13
  delay(500);
}

you'll see the builtin LED blink at 1Hz

1 Like

Yes. Thanks a lot!
Now, another pussle. When I change the code inside the blink as follows:

blink:
  cbi   PINB, 5     ; Toggle PINB
  ldi   r25, hi8(500)
  ldi   r24, lo8(500)
  call  delay_ms
  sbi   PINB, 5     ; Toggle PINB
  ldi   r25, hi8(500)
  ldi   r24, lo8(500)
  call  delay_ms
  jmp   blink

, the led starts to bling twice as slow, and I think it should blink with the same frequency.

Where did you find the example? Please give a link.

As noted above, that's how the particular AVR hardware works.
As an example, the author really should have explained that - it's a bit of a "quirk" of these AVRs.

It cam as an example from PlatformIO extension in VS code, not from a link. Please look at post#10.

You need to go back to the datasheet.

Clearing a bit in the PIN register does not affect the output - you need the PORT register for that...

2 Likes

When programming in assembler, you directly affect the internal HW of the MCU. To understand what happens, you have to understand how this HW works. The main documentation for this is the datasheet of the MCU. But this is not easy reading.

I think these are not the best prerequisites to dive deep into the MCU hardware already :wink: :grin:

3 Likes

to to toggle the pin, you want to write a 1 in the register, as explained.

here you have cbi (clear bit)

here you have sbi (set bit)

➜ you only want to set the bit.

Anas Kuzechie has a thorough explanation of blink using asm.

Video:

The text:

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.