This tutorial has the purpose of helping a beginner to make the first steps in embedded programming: blinking a led and using a switch. What the reader should understand is that the tutorials can be helpful but it is strongly recommended to read carefully the datasheet of the microcontroller used. In this article only basic terms definitions are given + 2 simple examples. I consider adding an USB JTAG (for Atmel) project when the time will allow me to do it (I hope in the near future).
MCU: is the Microcontroller Unit = uC (micro – Controller). We’ll use either microcontroller or MCU in this article.
BIT: is the atomic unit when measuring the memory. It corresponds to a “latch” = “flip-flop” = the basic circuit with only 2 states (bistabil): logic “0″ and “1″.
BYTE: is the most known bit multiple. One byte has 8 bits. The maximum value of one byte is 255 – because this is the maximum value that can be represented on 8 bits.
- 24 (decimal) = 0×18 (hexa) = 00011000 (binary) . To be more readable 0×18 =  , where 0001 = 1 (binary) and 1000 (binary) = 8 (hexa)
- 148 (decimal) = 0×94 (hexa) = 1001 0100 (binary)
- 255 (decimal) = 0xFF (hexa) = 1111 1111 (binary)
LOGIC (1) & (0): in digital electronics the signal level has only 2 values: either logic “1″ (known also as high level) or logic “0″ (low level). Logic “1″ usually represents the same voltage as Vdd (MCU power supply voltage) whereas logic “0″ represents the GND, or no voltage. The MCU will see either logic “0″ or logic “1″ regardless of the voltage placed across its pins.
PIN: represents one MCU wire. PINs have usually multiple functions selectable from MCU internal logic. For example, one MCU pin can act as a GPIO (general purpose input output) or TX line (transmission line) for UART peripheral but also as clock line for SPI for example (serial peripheral interface). Of course it cannot act in the same time for all these functions. User must select the desired function before starting using it (write correct values in the configuration registers for that port) . MCUs have also special pins which have only one function.
- reset pin: this is the general MCU reset pin. When this pin is tied to GND the complete logic inside the MCU is reset. For this reason the user should make sure this line is kept tied to Vdd for the whole program run time (through a resistor).
- Xtall pins: crystal oscillator has 2 lines. The MCU needs clock all the time so these lines are used only for this purpose.
- Vdd: main voltage pin. The user has to power up the MCU through this pin. A MCU might have several Vdd pins. All of them have to be linked together on the schematic.
- GND: ground pin. As in the Vdd case the GND might have several pins. All of them have to be linked together.
MCU pins are grouped in ports. A port has usually a number of pins equal to a power of 2 (4, 8, 16, 32). They are usually named as Port A, B, C and so on.
PULL UP / DOWN: is a resistor placed between MCU pin and either Vdd or GND. It helps strengthen the signal level across that pin. In other words you have to know that a MCU can deliver a limited amount of power through its pins. We can, for example light up a LED, because a LED requires a couple of mA (mili amps) to light up, but we cannot drive an electric motor (which requires more current). The pull up/down resistor needs to be in a certain range otherwise it may damage the pin logic.
MEMORY: this is where we place the code and/or data. The memory can be a separate chip or embedded in a MCU. You have to know that a MCU might have several types of memory embedded, but some of them might also work with external memories. A MCU has usually at least one flash memory and one RAM. The MCU needs the flash because it can store information after power is removed. Here the code will be placed. The RAM is used at run time to play with data.
MCU CORE: or ALU – Arithmetic – Logic – Unit or whatever name you want. This is the “brain” of a MCU. It drives the whole activity inside the chipset.
MICROCONTROLLER: represents the chipset containing a MCU core, some buses, peripherals, memories. The MCU needs a flash where the program is stored and at least one RAM . There are different types of MCUs, with different architectures. You can also imagine a MCU without RAM or flash inside. It may have dedicated ports for address and data so that the memories used can be external chipsets. Or even a MCU with internal RAM but also buses for external flash and external RAM.
PERIPHERAL: additional logic in a MCU dedicated for a specific usage. The MCU itself is made out of core, buses and peripherals so the complexity of a MCU is given also by the number of peripherals. I will not enter now into details regarding the peripheral usage, but I will give some examples only:
- UART: Universal Asynchronous Receiver Transmitter: used for transmitting/receiving data on a serial bus with 2 lines TX & RX.
- USART: Universal Synchronous Asynchronous Receiver Transmitter: same as UART but has the capability to transmit also synchronous data.
- SPI: Serial Peripheral Interface: a synchronous data transmitter/receiver peripheral. It has 4 lines: MISO (master in slave out), MOSI (master out slave in), SCK (spi clock) and CS (chip select). A transfer starts when the master CPU puts the CS line into low state.
- Timer: represents a counter. It counts the clock cycles in some internal registers. It can also count external events (number of 1/0 toggling on a dedicated MCU pin.) or control an output pin according to some internal algorithms. I will give a practical example in one of the following tutorials.
- I2C: another peripheral for exchanging data serially. It has 2 lines: SCL – signal clock, SDA – signal data.
- GPIO: general purpose input output: this is the place where the user can decide whether a pin is to be configured as input or output, set it to low or high. This is also the place where the user can decide what function should have the pins: GPIO or alternate functions (dedicated to peripherals). A pin can be set as input or output or set to high/low only when it is configured in GPIO mode. Otherwise the peripheral takes over the full pin functionality.
- ADC: analog to digital converter: this is one of the peripherals in a MCU which has the ability to play with analog data. It measures the voltage level from GND up to Vdd. It can also measure negative range voltages. More details will be given in a future article.
INSTRUCTION: all MCUs have a set of instructions. Depending on MCU architecture, one instruction can be executed in 1, 2 or even more clock cycles. The instruction is an operation(opcode) which has a specific meaning for the MCU. Instructions can move data, do arithmetic operations, clear/set bits and so on. So most of the instructions can be seen as: “opcode operand1 operand2 … operand n” where operand1..n can be MCU registers or memory addresses. Instructions statements(asm code lines) are called mnemonics.
PROGRAM: is the complete flow of instructions which has to be executed by the MCU to accomplish the user target. The program can be written in ASM, C or both languages (one part in ASM, another one in C). The C code is translated into ASM code at compilation time. Then all ASM instructions converted in machine code. So you can see C language as an easier way to tell the MCU what has to perform.
MCU REGISTERS: there are 3 types of registers in a MCU:
- registers used by the MCU core to execute the program. There are some specific registers and some general purpose registers. Specific registers are PC-program counter = keeps the address of the current executed instruction in the program, SP-stack pointer = keeps the current stack address being used by the program. General purpose registers are used during instruction execution to hold data, results of arithmetic and logical operations and so on.
- control and status registers of the MCU core. It can hold result of arithmetic operation like “carry/sign number/negative flag” but also can control some internal MCU settings like alignment, general interrupt status etc.
- peripheral registers: each peripheral has a set of register to control the peripheral’s hardware.
For the time being it’s enough to know there are 3 types of registers, details about them cannot be given now due to complexity reasons.
What a programmer must understand is that an embedded program has 2 main parts:
1. Initialization: code/data relocation, stack setup, clocks setup, interrupt vectors setup and so on. This initialization contains mandatory settings to be performed for the MCU to run properly. For beginners, the best option is to use compilers/IDEs, which will add automatically this initialization part to each program. In this case the user has to write only what he wants to accomplish with the program. The more advanced embedded developers can make or customize this initialization depending on their needs.
2. Main program: contains the code sequence to accomplish the tasks desired by the user. Below I will give 2 program examples and the schematic needed. The MCU is an Atmega16, where we connect a switch and a LED. Along with these 2 parts the MCU should have a Xtal, which gives the clock, a reset switch and additionally a programming/debugging port.
- MCU: Atmega16
- Resistors: 1 x 1K, 2 x 10K
- Capacitors: 2 x 22pF, 1 x 100nF
- Switches: 2 – any type
- LED: 1
- Connectors: 2×5 pins header for JTAG.
In this schematic we have the user switch (Switch1) connected to pin PB2, the LED connected to pin PA7, reset switch connected to pin #4, Xtal to pins #7 & #8 and a capacitor to 5V line. The capacitor has the purpose of suppressing the spikes on the line making the voltage across the MCU more stable. Another capacitor, polarized of around 10-100uF might be added in parallel with C3. It is useful as the MCU might not behave correctly if the voltage is fluctuating or has spikes. I added in the schematic the JTAG port, this is how I’m usually flashing/debugging the programs.
The code examples will show you how you can blink the LED (example 1) and how you can blink the LED only when the Switch1 is pressed (example 2).
Please note that the schematic is the same for both examples, but in the first example we ignore the switch.
Short description of example 1. There are 2 functions: a delay loop implemented with “for” statements. If we consider that each code line is expanded in more ASM code lines and each ASM instructions need 1 or more clock cycles to be executed, we can build a delay by keeping the MCU busy in some “useless” instructions – if I can call them like this. In other words, this functions doesn’t do anything else but keeping the MCU busy. To make an estimation of desired delay, we have to know the MCU clock frequency, how many ASM instructions are expanded from the C code, how many loops we have and how many clock cycles it takes for those ASM instructions to be executed. Or we can tune this delay by changing the number of “for” loops until our required delay is hit. In my example the argument passed to “delay” function, “count”, gives the number of loops. At a clock cycle of 6MHz and count=10, the LED flash period is around 1 second. I didn’t do any estimation here, I just guessed. You have to know that different compilers can generate different amount of asm code for the same C program. That’s why for such simple example you just give a try and adjust the timing if the flash speed is too high/low.
In the “main” functions you can see the user code. Please note that the code for the initialization was automatically added in my example by the compiler. This means in the “main” function I have to write only the desired code for my task.
In this example I want to blink the LED connected to port A, pin PA7. For this to accomplish, I have to do the following:
- configure the PORTA, line PA7 as output. The port structure is PORTx = P7P6P5P4P3P2P1P0, where P = pin number.
- switch the output level on this line(toggle) between logic “0″ and logic “1″. The delay in between will give the flashing period.
As you can see DDRA register is used to configure the port lines as input or output and PORTA register is used to toggle between logic “0″ and logic “1″, by writing either “0″, or “1″ on the corresponding bit number, in our case bit number 7. Please note that counting starts from “0″, so 8 bits will be represented as bit #0 to bit #7. These registers have a fixed address in the memory, so a writing into one register can be seen as the following equivalent:
DDRA = 0×80 <=> *(volatile unsigned int*)DDRA_address = 0×80.
In other words this can be translated into: we write 0×80 value at the address DDRA_address.
The second example adds a switch into picture, from which the user can control when the LED should blink.
I’ll explain the additional functionality in this second example.
We have to configure also the PB2 pin as input so that we can read the switch status. This is performed by writing a “0″ to the corresponding DDRB line. In this example we set all the DDRB port lines as input, but this doesn’t affect our functionality. The corresponding code change consists in checking the PB2 input level in all loop iterations ( if(!(PINB & 0×04)) ). The pin read is performed using an “and” operation with “0×04″. This will clear all result’s bits except for bit #2. So this bit will give the actual result if different from “0″. Whenever the result is “0″ it means PB2 = “0″ =>the switch is pressed so we execute the PORTA PA7 toggle and delay, otherwise we place logic “1″ on the port to shut down the LED.
The flexibility here is that we can move, for example, the LED to PA6 and have it also working. The only change in the code would be to toggle the 6th bit instead of 7th (0×40 instead of 0×80). The same with the switch1. We can place it on port PB3. The additional change would be to check PIN3 instead of PIN2 (if(!(PINB & 0×08))).
Another detail regardless of the program refers to MCU general settings like clock source. If you use the AVR studio for programming/debugging, you can find these settings in the MCU fuses tab (after connection to programmer & MCU is established). The clock source has to be selected as “Ext. Crystal/Resonator High Freq”. Pay attention not to uncheck first 3 fuses which allow the programming/debugging.
The 2 program examples can be downloaded from here: example1, example2.
If you have any question or comments, please use the “Contact” form.