Micro beginner ==> 2. LED on
Lecture 2: Switch a LED on
The first gigantic microcontroller program switches a light emitting diode LED on.
We learn here how a program looks like, how it is assembled (translated to the
controller's binary language), what comes out of the assembling process and how
this is transferred to the controller. We learn much about LEDs and I/O pins
and their manipulation.
- Components and mounting
LEDs send light, if current flows through them. So far so good. In a certain area
"the more current the more light" applies, but this area is rather
limited. There, more current does not emit more light but produces more heat.
In the area with small current of a few milli-amperes (mA) the increase in
emitted light is the highest, at high currents the increase is not visible
any more. The human eye and its sensitivity is anything else but linear so that
we cannot really see and realize "more light" and "even more
Electrically spoken a light emitting diode is a somewhat strange component because
its resistance is not constant and diminishes with increasing current (nearly down
to zero). That is the reason why LEDs have to be operated with a limited current
because they are not able to regulate the current via their resistance. This limited
current can be provided by a resistor or an electronic current regulator based on
The value of current limiting resistors can be calculated applying Ohm's law.
R = U / I says that the current I increases with the voltage
applied linearly. The voltage on the resistor UR is the difference
between the operating voltage (e.g. 5 V) and the voltage of the LED
ULED (at a given current). At 5.0 V and a LED voltage of 2.1 V
the voltage on the resistor is 5.0 - 2.1 = 2.9 V. For a current of 10 mA
the resistor should be
R = 2.9 / 0.010 = 290 Ohms.
Controller pins as in- and output
The controller ATtiny13 has 8 pins of which five can be used as port pins. Those
are named PB0 to PB4. Each of those port pins is controlled by two bits in two
internal storage places. Those storage places are named DDRB (data direction register
port B) and PORTB (data output register port B). The logical state of the port pin
can be read via PINB (input register B). Why the port names start with B and not
with A is ATMEL's secret. Why they named those storages "registers" is to
confuse you a little bit with other internals named registers.
Each of the port pins can be switched to four states (in the following for port pin
- Output driver off (DDB0 = 0), pull-up resistor off (PORTB0 = 0). In this mode
the port pin can be used as a high-resistance input pin. The logical state of
the pin can be read in PINB0.
- Output driver off (DDB0 = 0), pull-up resistor on (PORTB0 = 1). In this mode
a resistor of approximately 50 k ties the voltage on the open input pin to
the operating voltage of the controller. Externally a switch or push button can
overwrite this and pull the input voltage down to zero volts (Ground, GND), the
bit PINB0 reflects this level change.
- Output driver on (DDB0 = 1), output bit zero (PORTB0 = 0). The pin now is on
a very low voltage near the negative operating voltage. The pin can pull down
currents of up to 50 mA, with the pin voltage slightly increasing with
- Output driver on (DDB0 = 1), output bit one (PORTB0 = 1). The pin is on a high
voltage slightly below the operating voltage. The pin can drive up to about
30 mA, with slightly decreasing pin voltage.
2.1.2 Controller pins as output
The task to switch on a LED with one of the controller pins can be resolved in two
Both cases are different in that, in the first case, the output bit is high, in the
second case it is low.
- The pin is configured as output and the output bit in PORTB is set to one. In
that case the LED (with its anode) and the resistor are connected to the pin and
the cathode side goes to GND. The pin is high and provides current
- The pin is configured as output and the output bit is cleared pulling the
output pin to GND. Now the cathode of the LED points to the pin and the resistor
is connected with the operating voltage (%quot;sink".
Here both opportunities are shown with the respective voltages at 4.8 V. The
output driver transistors of the AVR show a slightly higher voltage difference
when soucing. At 10 mA and nearly 5 V operating voltage the difference is
not very significant, but increases at higher output currents and smaller operating
voltages. Please consult the electrical characteristics chapter in the device databook
in those cases.
All outputs can be short-circuited without any damage. If several pins are
simultaneously short-circuited, trhe maximum heat power of the AVR can be exceeded.
If more than approximately 30 mA have to be driven, consider an external transistor
To switch the LED on, the cathode of the LED is connected to PB0 and via a resistor of
220 Ohms to the positive operating voltage. The sink option is selected here, if
the output pin is low the LED is on.
The formula to calculate the LED current is also given. The 4.8 V are the battery
pack voltage, the 2.1 V the conducting state voltage of the LED (at approx.
10 mA current) and the 0.2 V is the AVR driver voltage in sink mode (at
approximately 10 mA and 4.8 operating voltage). Larger currents make no sense
because the increase in emitted light cannot be seen. Only if a larger LED is used a
higher current is appropriate.
The other parts of the circuit remain the same as in the previous lecture. PB0 is used
for two purposes now: for driving the LED and as MISO in the ISP programming mode.
That does not cause any conflicts (higher currents might conflict with the ISP
This is a 5 mm standard-LED. The longer pin is the anode. If mounted in reverse
mode: the diode has a Zener voltage break-through at around 16 V.
The 220 Ohm resistor
These are two different types of resistors of 220 Ohm. The upper is a carbon
film resistor with 5% accuracy, the lower a metal film type with 1% tolerance.
2.3.2 The hardware
The mounting of the hardware requires that the cathode of the LED is connected with
PB0 (pin 5) of the ATtiny13. The anode is tied to the neighboring empty comlumn.
From there the 220 Ohm resistor goes to the plus bar. That is it.
Even under power: nothing happens. The reason is that a native ATtiny13 switches
off all his port pins. To switch those ports on, program code has to be executed.
2.4.1 Program storage
To animate the ATtiny13 he has to be programmed. In this case with the two hexdecimal
words 9AB8 and 98C0. In binary language (which the controller natively speaks) this
corresponds to 1001.1010.1011.1000 and 1001.1000.1100.0000. The useful place for
those 32 bits, four bytes or two words is at the beginning of its program storage area
(flash), on addresses 0000 and 0001. If you buy a new device (or if you erase the flash
area) there is nothing useful there. As a storage cannot be empty (there is always
something there), it is filled with hexadecimal FFFF in all those cells. Factually
the controller reads the FFFFs, decodes those and executes them. But with the result
to do nothing. The same operation happens, if he reads a 0000 from its flash: doing
nothing. This operation is called "NOP" or "No operation". NOP
by the way is a mnemonic representation for the binary 0000.0000.0000.0000 or
hexadecimal 0000. All things that the controller can do (execute instructions) have
such a mnemonic, as we will see later on.
The program storage or flash memory of the ATtiny13 has 1024 bytes, into which 512
instruction words fit. That sounds not very large, but in assembler this is a large
bunch. Our most complicated program will have several tens of instruction words, and
in douzends of projects I never reached the limit of the flash. My stepper motor
application had only 141 instruction words, and this has to perform complex
timing, counting and AD conversion tasks in parallel. One does definitely not need
more if you avoid ineffective C style programming.
2.4.2 Source code
Because binary codes such as 9AB8 and 98C0 are not easy to remember understandable
representations have been defined that are easier to remember. In this language the
software engineer writes the two lines
into a textfile (commented source code file here). From
these two lines the assembler, a translation program, generates the two instruction
words 9AB8 and 98C0 for the controller.
The abbreviations sbi and cbi are not understandable either, but if you write
SBI: Set bit DDB0 in I/O port DDB0, the data direction bit of pin
PB0 (DDB0), to one.
Clear bit PORTB0 in I/O port PORTB, the data bit of pin PB0, to
That sounds more understandable. With that background knowledge the mnemonics SBI and
CBI are memorable.
In assembler, as with the windows filesystem, upper and lower letters are not discriminated.
That gives us the opportunity to signal the type of symbols by our own. In the above
formulation instructions are in lower letters, symbols in upper letters.
Each line in the source code (e.g. "sbi DDRB,DDB0") is exactly one instruction
of the controller. Only those instructions that the central processing unit (CPU) of the
controller physically masters have a representing mnemonic. This is specific for
assembler: it depends completely from the abilities of the controller. While in other
languages instructions can be generated and named by the author (such as subroutines
or functions), no such ability is given in assembler. Each line is exactly one physical
operation of the controller.
That is the reason why assembler instructions differ only minorly with different dialects.
What the CPU understands and handles is not so different. Each CPU can add two binary
numbers. In each assembler dialect the mnemonic for this might be different, but the
basic processing is virtually the same. Those that have learned AVR assembler might
well switch to PIC assembler. All that is to be learned are the slightly different
mnemonics (and specific abilities of the CPU). The principle, one mnemonic translates
to one CPU operation, remains the same.
2.4.3 To assemble
In the code line "sbi DDRB,DDB0" the type of "sbi" stands for an
instruction while "DDRB" and "DDB0" are parameters for this
instruction. The first parameter is the data direction port of port B, the second
is the bit position in that port to be set to one. The device data handbook for the
ATtiny13 says about this:
DDRB therefore translates to port number 17 (hexadecimal, that is 23 decimal). DDB0
translates to hexadecimal 0 or bit 0. Both parameters stand for numbers. We do not
need to remember those numbers if we use the symbol names DDRB and DDB0 instead.
Avoid to learn and use the numbers, because in a different device some of those
numbers might be different from those in an ATtiny13.
The ports DDRB and PORTB are listed in the device databook as follows:
R/W means that those bits can be read (R) and written (W). The "initial Value"
means that this bit will be cleared (0) or set (1) during the reset sequence of the
2.4.4 Writing source code
To write assembler source code one needs a simple text editor. No special software,
just an editor that writes ASCII chars to a file. Such as Notepad (under windows,
see picture to the left) or KWrite (under linux KDE). And name this text file
"somehow.asm", to just better remember what is written in there and for
which purpose. Please do not use a textwriter such as Word or OpenOffice to write
source code, those do not store plain text and are confusing the assembler with
Those who want it more comfortable write the source code in the editor window of
ATMEL's studio (here: older version 4). First we open a new project, select the name
of the project and the source code file and its location.
Then we select the simulation platform and the device (irrelevant in this case).
In the editor window to the right we type in the source code.
Lines with a semicolon are comments that are ignored by the assembler. They are only
useful for the human reader.
The directive .INCLUDE "tn13def.inc" reads in a file where all
symbol representations of numbers for the device type ATtiny13 are defined, in our
case the ports DDRB and PORTB and the port bits DDB0 and PORTB0. If we would skip
this line, the assembler does not know these symbols and error messages would result.
The combination of the directives ".NOLIST" and ".LIST" switches
the output in the listing off and on so that the include does not produce output in
the listfile. If you leave these two lines out you can see in the listing all symbols
that are defined for the ATtiny13.
To ease the recognition the editor of the Studio uses different colors for instructions,
parameters, directives and comments. This is called syntax-highlighting.
2.4.5 To assemble
There are many ways to assemble these source code files. Many assemblers are available,
I recommend my own command line version, gavrasm. It is available
here for download,
versions for windows and linux as well as the source code written in fpc-Pascal is
With gavrasm open a windows command line or a linux bash shell and type two commands.
The first command, "cd path to source file", is necessary to introduce
the path where the source code file is located. The second command, "[path to
gavrasm\]gavrasm.exe -seb source.asm" calls gavrasm to assemble the source
This shows the assembler at work. Finally it comes out with "No errors"
and one warning. The warning is that gavrasm knows all symbols of all AVR devices
internally and does not read the file "tn13def.inc". That makes this
assembler independant from the def.inc files of ATMEL, and it works under Linux
or MacOS or whatever operating system. Only gavrasm provides this service. So
you can finally ignore the warning.
If you have to assemble the same source code file over and over again it is useful
to place these two call into a batch file that can be started by clicking on it.
This provides such an example batch file for windows.
gavrasm produced two new files after succesful completion: a listing and a hex file.
The files are of the same name as the source but have different extensions:
".lst" and ".hex". Both are simple text files and can be viewed
with any simple text editor. The hex file can be used to program the controller's
This is the assembler listing. It shows that the sbi instruction translates to hex
9AB8 at address 000000 and cbi to 98C0 at address 000001. The list of symbols in
the lower part of the listing (switched on with the -s parameter on the command line)
says that only one symbol is defined: the ATtiny13 with a T type is defined once
(column nDef), is once used (column nUsed) and has an internal value of decimal 18.
Other types of symbols (registers, constants) etc. would occur here if defined
and used. This symbol table is only provided by gavrasm, no other assembler has that.
With the Studio the creation of the listing has to be switched actively on because
it is disabled by default.
This is the generated hex code in readable form, in Intel hex format. This provides
the naked code with addresses, bytewise coded instruction words and a check byte
on each line. We do not have to care about this file, it works with every programmer.
2.4.6 To write the hex code to the program flash storage
To transfer the hex file's content to the controller, one has to start the Studio, open
the tools section and select "Autoconnect" there. In the tab "Main"
we assure that the controller is accessible and of the correct device type. The we
go to the tab "Program". There we select the "Input Hex File" by
clicking on the small square to the right of the input field. The button
"Program" initiates the programming.
After some on and off of the LED (the programming pulses) the LED is permanently on. The
two code lines finally work and do what they are supposed to do.
©2017 by http://www.avr-asm-tutorial.net