Imagine a wardrobe with many drawers. Each drawer has 8 compartments.
In our case: address 4 (DDRB) and address 5 (PORTB) are two adjacent drawers.
Each compartment corresponds to a physical pin of the microcontroller.
Compartment number 5 (bit 5) corresponds to physical pin D13 (the one with the built-in LED on Arduino).
DDRB drawer (address 4):
Each switch decides if the pin can "talk" (OUTPUT) or only "listen" (INPUT).
If you put 1 in compartment 5 of drawer 4 โ pin D13 becomes OUTPUT.
PORTB drawer (address 5):
Each switch sends current (5V) or not (0V) to the pin.
If you put 1 in compartment 5 of drawer 5 โ pin D13 goes to 5V (LED ON).
If you put 0 โ pin D13 goes to 0V (LED OFF).
This is why it works:
It's just opening and closing switches in drawers. The exact same principle applies to any programmable peripheral: sound cards, car ECUs, other microcontrollers.
Servo G90 360 controlled in pure assembly, no IDE, no libraries, directly from the browser.
๐ The r17 value determines the pulse duration. 31 = 1.5ms = servo stopped at center.
| Servo G90 360 | Arduino |
|---|---|
| Brown (GND) | GND |
| Red (+5V) | 5V |
| Yellow (PWM signal) | D13 (pin 13) |
How to upload the code:
Go to costycnc.it/avr1/compiler.html, paste the code, click "Assemble" then "Connect".
A G90 360 servo (continuous rotation) interprets the pulse duration as command:
| r17 value | Pulse duration | Behavior |
|---|---|---|
| 25-30 | 1.0-1.4 ms | Rotates one way (e.g. clockwise) |
| 31 | 1.5 ms | STOP at center |
| 32-37 | 1.6-2.0 ms | Rotates opposite way (e.g. counterclockwise) |
The farther the value from 31, the faster it rotates.
With 16MHz clock, 31 ร 512 cycles โ 15,872 cycles โ 0.99ms. With two calls and surrounding code, you get about 1.5ms and 18.5ms.
At power-on, all general registers (including r16) are 0. First dec r16 โ 0-1 = 255, then 254... down to 0. It works, but in complex programs you should initialize with ldi r16, 0.
| Instruction | Meaning (simple explanation) |
|---|---|
| .org N | "address where code starts" (e.g. .org 0 = start from beginning, .org 0x68 = skip interrupt table) |
| rjmp label | "jump to label" (go forward or backward in code) |
| sbi a,b | "set bit b in register a" (compartment b of drawer a = 1) |
| cbi a,b | "clear bit b in register a" (compartment b of drawer a = 0) |
| rcall label | "call subroutine label" (go there, do stuff, then return) |
| ret | "return" (after rcall) |
| ldi r,val | "load value val into register r" (e.g. ldi r17,31 = r17=31) |
| dec r | "decrement register r" (r = r - 1) |
| brne label | "branch if not equal" (if last operation didn't give zero, jump to label) |
If you understand this, you understand any programmable peripheral.
Example 1: Realtek sound card
The driver writes bits to specific registers to raise/lower volume. Same thing.
Example 2: Car and OBD2
The ECU has hundreds of registers. Speed, RPM, temperature are numbers in registers.
Example 3: Other microcontrollers
STM32, ESP32, PIC: names change, but the principle is identical.
Question: What happens if you change r17 from 31 to 28?
Shorter pulse (1.3-1.4ms). The servo rotates one way. The smaller r17, the faster.