This topic is to help me write a tutorial about how an Arduino turns an LED on and off, please post your suggestions and feedback here.
We all know that with Arduino, it is easy to turn on an LED with a simple line like:
digitalWrite(13, HIGH);
and the LED lights up.
But what really happens between this line of code and the LED producing light?
To make this concept easy to understand, imagine the microcontroller registers as a "cabinet full of switches". Each switch can be flipped UP (ON) or DOWN (OFF). Changing one of these switches changes the behavior of a microcontroller pin.
Of course, when you write digitalWrite(13, HIGH);, Arduino doesn't actually understand that language.The real process is more complex, but for learning purposes we can imagine that the final hardware action is equivalent to this raw instruction:
sbi 5, 5
What does sbi 5, 5 mean? Technicians call it Set Bit in I/O Register. But in our reality, it means: "Hey Arduino, flip UP switch number 5 inside cabinet number 5".
Because that is exactly what happens inside the chip: cabinet 5 is PORTB, which holds 8 switches. And switch number 5 is physically wired straight to pin D13 (the built-in LED on your board).
Now, let’s go to the webpage costycnc.it/avr1 and see if it is true.
Let's type sbi 5, 5, compile, and upload the code.
.org 0
rjmp start
.org 0x60
start:
sbi 5,5
loop:
rjmp loop
You will see that the LED turns on! It will light up faintly, but don't worry about the details right now.
Want to turn it off? Just write:
cbi 5, 5
Compile, upload, and Arduino listens to us blindly! Do you see? Arduino doesn't do magic; it just opens and closes switches.
I think this concept is extremely important: from now on, you will see Arduino in a completely different way. All those abstract names like PORTB, PORTD, or PORTC are just cabinets (numbered registers), and labels like PB5, PC1, or PD2 are just switches (BITS) that need to be opened or closed. And we could easily stop right here.
However, for those who love an adventure and want to dig deeper, a question might pop up: why is the LED light so faint?
Trust me, it is not easy to explain this with everyday words and without any previous technical knowledge on your part, but I will try. Forgive me in advance if the terms are not academic.
To set pin D13 as an output, we need to go to cabinet 4 (switch 5) and close it. This tells the chip that D13 is now a power output. Let's test it! Go back to the program, add sbi 4, 5 as well, and you will see that the LED now shines at full brightness.
For those who don't like an adventure, you can stop here. We get it: cabinet 4 handles pins D8 to D13 as either inputs or outputs. If I put a 1 (using sbi) in cabinet 4 at switch 0, pin D8 becomes an output. If I put a 0 (cbi), it becomes an input.
But for those who love an adventure and wonder why, let's move forward. You will see that it's worth it.
The difference between setting a pin as an output or an input is simply a resistor and a switch connected in parallel. When we do sbi 4, 5, we close the switch that bypasses the resistor. This way, the LED receives power directly from the 5V rail without going through that resistor. When we do cbi 4, 5, the switch opens and the 5V current is forced to pass through the resistor before reaching the LED (which is why the light was so faint!).
This is the famous internal pull-up, which most people do not understand. When we want to read the state of pin D13, we set it to pull-up: the current flows through the resistor and the pin will always stay at value 1 (HIGH). However, if we connect it to ground (0) from the outside, the value drops from 1 to 0. This keeps the pin from floating "in the air" and catching electrical noise, holding it securely to the positive side through that resistor.
I think stopping here is the right choice because for someone who knew nothing, this is already a lot to take in.
But I know some of you still love an adventure, so I will go just a tiny bit further just for you! ![]()
If you don't want to use the CostyCNC tool, you can try raw Assembly inside the classic Arduino IDE too. Just create a folder named ledon. Inside it, put two files: ledon.ino and ledon.S (it must be a capital S). When you press compile, Arduino automatically finds the .S file in the folder and includes it in the program. It's a bit more inconvenient, but totally possible.
CODE INO
// Tell Arduino that the function is external and written in C/Assembly
extern "C" {
void led_on();
}
void setup() {
// Call the Assembly function that will do all the work
led_on();
}
void loop() {
// Keep the loop empty: the chip has already done everything in the setup
}
CODE .S (ASM)
.global led_on
led_on:
sbi 0x04, 5 ; Cabinet 4, switch 5 UP: Pin D13 becomes a power output
sbi 0x05, 5 ; Cabinet 5, switch 5 UP: Light is ON at full brightness!
ret ; End of function: go back to the main program
One last thing and then I'll stop, promised! I left this for the very end so I wouldn't overload your brain.
Some might ask: why not make a tutorial about what happens inside newer boards like the Arduino Uno R4 or an ESP32? Why waste time with an old Arduino Nano using an ATmega328, which is considered slow today?
You are right, but you might not realize the internal complexity of a modern chip. The ATmega328 is perfect for learning: it has a simple architecture, immediate Assembly commands, incredible documentation, and an infinite amount of ready-made examples. Understanding the fundamentals of this MCU will allow you to understand the basics of any other microcontroller in the world. It was not a random choice at all.
And do you know what? Cabinet 5 and switch 5 actually exist, along with all the other cabinets and switches. You can see them for yourself if you want, written right inside the official datasheet from the manufacturer of the ATmega328. https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf
page 280
And I apologize to those I managed to make read all the way to this point! Until the next tutorial... what do you say? Shall we talk about Interrupts?
Goodbye!








