๐Ÿ‡ฎ๐Ÿ‡น IT ๐Ÿ‡ฌ๐Ÿ‡ง EN

๐Ÿ“˜ Make a G90 360 Servo Turn
Step-by-Step AVR Assembly Tutorial

Starting from this code that continuously rotates a G90 360 servo, we learn every single instruction

๐ŸŽฏ The program you uploaded

.org 0 rjmp init .org 0x68 init: sbi 4,5 ; DDRB bit5 = 1 (pin D13 as output) sbi 5,5 ; PORTB bit5 = 1 (LED D13 ON) rcall wait1 ; call delay cbi 5,5 ; PORTB bit5 = 0 (LED D13 OFF) rcall wait1 ; call delay rjmp init ; start over wait1: ldi r17,32 ; load 32 into register r17 wait: dec r16 ; decrement r16 (starts at 0 โ†’ 255, then 254...) brne wait ; if r16 โ‰  0, continue decrementing dec r17 ; decrement r17 brne wait ; if r17 โ‰  0, restart loop with r16 ret

๐Ÿ“Œ This is our starting point. Let's analyze it line by line.

step 1

๐Ÿ—บ๏ธ Where to put the code: .org and rjmp

.org 0
rjmp init

๐Ÿง  What happens here?

.org 0 tells the assembler: "put the next code at address 0 of the microcontroller's memory".

At address 0 is the reset vector: when Arduino powers up or resets, it starts here.

rjmp init is a relative jump to the init label. It's used to skip the interrupt vector table that occupies the first addresses.

๐Ÿšช Wardrobe metaphor

Imagine memory is a long hallway with many doors. Door 0 is the main entrance. .org 0 says: "let's put a sign at the first door". rjmp init is like saying: "don't enter here, go directly to door number 0x68, where the party is".

step 2

๐Ÿ”ง DDRB: telling pins how to behave

sbi 4,5

โšก SBI = Set Bit in I/O register

sbi 4,5 means: go to the register at address 4 (which we call DDRB) and set bit number 5 to 1.

Bit 5 of DDRB corresponds to physical pin PB5, which on Arduino is pin D13 (the built-in LED).

Setting a bit to 1 in DDRB configures that pin as OUTPUT. It can then send current (turn on LED) or be off.

๐Ÿ“Š DDRB (address 0x04) after sbi 4,5

7
6
5 โฌ…๏ธ
4
3
2
1
0
PB7
PB6
PB5 (D13)
PB4
PB3
PB2
PB1
PB0

โœ… Only bit 5 is 1 โ†’ only pin D13 is in OUTPUT mode. Other pins are in INPUT (high impedance).

step 3

๐Ÿ’ก PORTB: turning the LED on and off

sbi 5,5
cbi 5,5

๐Ÿ”› SBI and CBI on PORTB

sbi 5,5 = register 0x05 (PORTB), bit 5 = 1 โ†’ pin D13 becomes HIGH (5V), LED turns on.

cbi 5,5 = same register, bit 5 = 0 โ†’ pin D13 becomes LOW (0V), LED turns off.

Note: if we hadn't set DDRB first, these instructions would have no effect (the pin would be in INPUT).

๐Ÿ“Š PORTB (address 0x05) after sbi 5,5

7
6
5 โฌ…๏ธ
4
3
2
1
0
PB7
PB6
PB5 (D13) = 5V
PB4
PB3
PB2
PB1
PB0

โšก Pin D13 now has voltage! The onboard LED turns on.

step 4

โฑ๏ธ wait1: how to count time

rcall wait1

rcall is a subroutine call. It saves the return address on the stack and jumps to the wait1 label. When it encounters ret, it returns.

Analysis of the wait1 subroutine

wait1: ldi r17,32 ; r17 = 32 (outer counter) wait: dec r16 ; r16 = r16 - 1 (starts at 0 โ†’ becomes 255, then 254...) brne wait ; if r16 โ‰  0, go back to 'wait' dec r17 ; r17 = r17 - 1 brne wait ; if r17 โ‰  0, go back to 'wait' (r16 restarts at 0) ret

๐Ÿงฎ How does the counting work?

ldi r17,32 loads value 32 into register r17. This is the outer loop.

Then we enter wait:. Note that r16 is not initialized! The first time the program executes dec r16, r16 is 0 (registers start at 0 on power-up).

dec r16 decrements: 0 - 1 = 255 (because 8 bits go from 0 to 255, below 0 wraps to 255). Then it continues 254, 253... until 0.

When r16 reaches 0, brne wait doesn't jump (because zero = false condition) and we go to dec r17.

r17 is decremented: if not zero, we go back to wait and r16 restarts at 0 (because we never changed it except with dec). So the inner loop repeats 32 times.

step 5

๐Ÿ”„ init: the infinite loop

init: sbi 4,5 ; configure (only needed once) sbi 5,5 ; LED ON rcall wait1 ; wait cbi 5,5 ; LED OFF rcall wait1 ; wait rjmp init ; go back to init

๐Ÿ”„ Loop analysis

After init: we have DDRB configuration (sbi 4,5). Then on, wait, off, wait, and rjmp init.

Note: rjmp init jumps to the beginning, so it reconfigures DDRB every cycle. It's not necessary, but doesn't hurt: setting a bit that's already 1 changes nothing. It's just a bit redundant.

Result: the LED blinks continuously, with a delay between ON and OFF and between OFF and ON.

step 6

โš ๏ธ The small bug: r16 not initialized

In the original code, r16 is never initialized. It works because the first time it starts at 0, but it's a bad practice: in more complex programs it could contain any value.

โœ… Corrected version

.org 0 rjmp init .org 0x68 init: sbi 4,5 ; DDRB bit5 = 1 loop: sbi 5,5 ; LED ON rcall wait1 cbi 5,5 ; LED OFF rcall wait1 rjmp loop wait1: ldi r17, 32 ; โฌ‡๏ธ NOW WE INITIALIZE R16 ldi r16, 0 wait: dec r16 brne wait dec r17 brne wait ret

Now the behavior is deterministic and the code is more robust.

next

๐ŸŽฏ Where to go now?

Now that you understand the basics, you can explore other projects:

โš™๏ธ Stepper motor

Bit sequences to drive a 28BYJ-48 motor with ULN2003.

Go to tutorial โ†’

โ˜ ๏ธ Crash ASM

Ethical lessons from stack errors that caused real disasters.

Explore โ†’

๐Ÿ“ Test what you learned

Question: What happens if I omit sbi 4,5 and only do sbi 5,5?

Show answer

The LED won't turn on. Because sbi 4,5 configures the pin as OUTPUT. If the pin is in INPUT, sbi 5,5 enables the internal pull-up resistor, but doesn't send enough current to light the LED (and the pin isn't in output mode anyway).

๐Ÿ“š Instruction summary

Instruction Meaning
.org N Put next code at address N
rjmp label Relative jump to label
sbi address, bit Set Bit in I/O register
cbi address, bit Clear Bit in I/O register
rcall label Call subroutine (saves return address)
ret Return from subroutine
ldi reg, value Load immediate value into register (r16-r31)
dec reg Decrement register
brne label Branch if Not Equal (jump if last operation didn't result in zero)