The microcontroller family
In Year 5 the microcontroller was a concept. Here it becomes a concrete chip with numbered pins, named registers and a four-hundred-page datasheet. Learning to read that datasheet is, to a large extent, what this year is about.
01Where we are coming from
In microprocessor and microcontroller architecture we established what is inside: a CPU with an ALU and registers, memories, buses and peripherals, all in one chip. Now the point is to use one, and that means answering three very concrete questions:
The pinout and the multiplexing of functions: almost every pin serves several purposes and you have to choose.
Everything the chip does is decided by writing bits into special function registers. Programming a peripheral is, literally, putting ones and zeros in the right place.
Flash for the program, SRAM for the variables, EEPROM for what must survive a power cut. Choosing wrong is expensive.
| Family | Architecture | Instructions | Speed | Where you see it |
|---|---|---|---|---|
| Mid-range PIC 16F84A, 16F628A, 16F877A | Harvard, RISC | 35 | 20 MHz → 5 MIPS | Argentine industry, equipment in service, plenty of documentation in Spanish |
| High-end PIC 18F, dsPIC | Harvard, RISC | 75+ | 40–70 MHz | When more peripherals or more computing power are needed |
| AVR ATmega328P, ATtiny85 | Harvard, RISC | 131 | 20 MHz → 20 MIPS | Arduino. One cycle per instruction and free tools |
| ARM Cortex-M STM32, RP2040 | 32-bit | Thumb-2 | 48–480 MHz | The current industry standard |
| ESP32 | 32-bit + radio | — | 240 MHz | Anything that needs WiFi or Bluetooth |
Because in a mid-range PIC you can see everything: the memory map fits on one page, the registers are eight bits wide, there is no operating system or layers of abstraction, and every instruction takes a known time. What you learn there —reading the datasheet, configuring a peripheral bit by bit, measuring timing— carries over unchanged to a 200 MHz ARM. It doesn't work the other way around: whoever starts with libraries never sees what is underneath.
02The pinout
It is the first useful page of any datasheet. It shows the package with its numbered pins and, for each one, all the functions it can perform, separated by slashes.
| Group | Typical pins | What to get right |
|---|---|---|
| Power | VDD / VCC y VSS / GND | A 100 nF capacitor right next to each pair, and if there are several pairs, all of them connected. Without that capacitor the micro resets by itself. |
| Reset | MCLR̄ / RESET̄ | Active low: it goes with a 10 kΩ resistor to the positive supply. Leaving it floating is the number one reason a micro “won’t start.” |
| Oscillator | OSC1/OSC2, XTAL1/XTAL2 | Crystal with its two 22 pF capacitors, as close as possible. If the internal oscillator is used, those pins are free to use as I/O. |
| Programming | ICSP: MCLR, PGD, PGC · ISP: MOSI, MISO, SCK, RESET | It pays to bring them out to a connector even if the project is final: reprogramming without desoldering is worth its weight in gold. |
| Analog | AN0…ANn, VREF | At power-up, many pins start out configured as analog. If you want them digital, you have to say so explicitly. |
| Peripherals | TX/RX, SDA/SCL, SDO/SDI/SCK, CCP/PWM, INT | They are fixed to certain pins: the printed circuit board has to respect them, or you have to use pin remapping if the chip offers it. |
On many PICs, after reset the ANSEL register (or ADCON1) leaves
several pins as analog inputs. A program that reads them as digital always gets zero,
with nothing to indicate the error. The first lines of any serious program configure those
registers. On AVR something similar happens with the comparator and with
DIDR.
03Special function registers
The CPU has no “instructions for turning on an LED.” What it has is access to a set of named memory locations —the SFRs— where each bit governs something in the hardware. All peripheral handling comes down to reading and writing those bits.
// Three ways to write the same thing (AVR): set bit 5 of port B to 1
PORTB = PORTB | 0b00100000; // explicit, hard to read
PORTB |= (1 << PB5); // the idiomatic form
PORTB |= _BV(PB5); // with the library macro
// And to clear it, the inverted mask
PORTB &= ~(1 << PB5);
// Test an input bit
if (PIND & (1 << PD2)) { /* pin 2 is high */ }
These few lines are 90% of the work with registers. They come straight from the bitwise operators covered in the C language.
Each register appears with its eight bits, the name of each one, whether it is read, write or both, and its value after reset. That last item is the one most often overlooked: if a bit starts at 1, the peripheral is already doing something before the program says anything.
The other important convention: an overbar over the name, or a trailing
n (MCLRn, CSn), means active low.
PORTB |= (1<<PB5) doesn’t just write: it reads the port, changes one bit and
writes it back. If another pin on the same port is loaded —an LED that pulls the voltage down, a
pin that is still switching— what is read is not what had been written, and that pin changes
state by itself. It is a famous PIC bug. The fix is the LAT register, which returns the
last value written instead of the actual state of the pin: LATB |= (1<<5).
04Input and output ports
Each digital pin is handled with two or three registers, and it is worth getting the mechanism clear because it is identical in all families, even though the names change.
| PIC | AVR | Function | |
|---|---|---|---|
| Direction | TRISx | DDRx | Input or output |
| Write | PORTx / LATx | PORTx | Output level |
| Read | PORTx | PINx | Actual state of the pin |
| Pull-up resistor | WPUx / OPTION | PORTx with DDR set to input | Prevents floating inputs |
On PIC, TRIS set to 1 is an input (the mnemonic: 1 looks like an I for input, 0 like an O for output). On AVR, DDR set to 1 is an output. It is exactly the opposite, and it is the most common confusion for anyone moving from one family to the other. A pin configured as an output by mistake, connected to a pushbutton that pulls it to ground, gets destroyed.
- AVR: 40 mA absolute maximum per pin, 20 mA recommended, and 200 mA in total for the whole chip.
- Mid-range PIC: 25 mA per pin, and 200 mA summing the whole port.
- A red LED with a 2 V drop supplied from 5 V through 330 Ω draws (5 − 2)/330 = 9.1 mA: no problem at all.
- Eight LEDs on at once on the same port draw 73 mA: still within limits, but at 20 mA each it would be 160 mA and the chip heats up and the voltages drift.
- A 12 V, 30 mA relay, a motor or a lamp never connect directly: they need a transistor (the transistor as a switch) with its flyback diode.
05The three memories and the configuration bits
| Memory | PIC16F877A | ATmega328P | What it holds |
|---|---|---|---|
| Flash | 8 K words of 14 bits | 32 KB | The program. It is loaded with the programmer or with a bootloader. |
| SRAM | 368 B | 2 KB | Variables and stack. It is erased when power is removed. |
| EEPROM | 256 B | 1 KB | Configuration, calibration, counters: whatever must survive a power cut. |
A 40-character text string eats 11% of the RAM of a 16F877A. An array of 100
16-bit integers doesn’t fit. The compiler doesn’t always warn you: the program compiles, gets
programmed and fails in strange ways when the stack collides with the variables. That is why on small
microcontrollers you use right-sized types (uint8_t instead of int),
avoid strings and keep constants in Flash.
They are called configuration bits on PIC and fuses on AVR. They are not data memory: they are programmed along with the program and define how the chip starts up.
- Clock source: crystal, resonator, internal oscillator, with or without the divide-by-8. It is the fuse that most often leaves a chip “dead”: if an external crystal is selected and there is no crystal, the micro doesn’t start and can’t be reprogrammed the normal way either.
- Watchdog: enabled or not, and with what timeout.
- Brown-out: at what voltage the chip resets if the supply drops.
- MCLR as reset or as an input pin.
- Code protection: prevents reading the Flash. It is used in a finished product; in the workshop it is best left off.
06The instruction set
A mid-range PIC has 35 instructions. You don’t need to memorize them, but you do need to understand which classes exist and how long each thing takes, because the real timing of the program comes from there.
| Class | Examples | What they do |
|---|---|---|
| Data transfer | MOVLW, MOVWF, MOVF | Move data between the accumulator (W) and memory. |
| Arithmetic and logic | ADDWF, SUBWF, ANDWF, XORWF | Operate, leaving the result in W or in the register. |
| Bit-oriented | BSF, BCF, BTFSS, BTFSC | Set, clear and test a bit. They are what make handling peripherals so convenient. |
| Control | GOTO, CALL, RETURN, RETFIE | Jumps, subroutines and return from interrupt. |
| Special | CLRWDT, SLEEP, NOP | Refresh the watchdog, sleep, waste a cycle on purpose. |
A PIC with a 4 MHz crystal. The clock is divided by 4, so one instruction cycle lasts 1 µs.
- Almost all instructions take one cycle: 1 µs.
- The ones that jump —GOTO, CALL, and the test instructions when they actually skip— take two.
- A delay loop of three instructions repeated 250 times takes about 750 µs; by nesting two such loops you reach the millisecond with microsecond precision.
That calculation is the reason a micro can guarantee a timing, something that is impossible on a computer with an operating system. It is also why loop delays get replaced by timers as soon as the program does more than one thing (peripherals).
Today almost everything is written in C, and that is fine: it is faster to write and to maintain. But it pays to know how to read the assembly the compiler generates, because it is the only way to answer questions like “how many cycles does this interrupt service routine take?” or “why isn’t this variable updating?” In current environments you see it with one click (disassembly window).
07Special features
An independent counter that resets the chip if the program doesn’t refresh it in time. It is the safety net against a hang: an infinite loop caused by noise or unexpected data doesn’t leave the equipment dead, it restarts it.
It is refreshed with CLRWDT (or wdt_reset()) in the
main loop, never inside an interrupt: if the main program hangs and the
interrupt keeps running, the watchdog would never act.
It monitors the supply and holds the chip in reset while the voltage is below a threshold. Without it, when the voltage drops slowly the micro keeps executing with corrupted data: it writes garbage to the EEPROM and activates outputs at random. In equipment that handles power, enabling it is not optional.
SLEEP stops the CPU clock and drops consumption from milliamperes to
microamperes. The chip wakes up on an interrupt, on the watchdog or on a change on a
pin. It is what lets a battery-powered sensor last for years.
The chip waits for the supply to stabilize and only then executes the first instruction, which is always at address 0. That delay is configurable and should be left enabled: without it, the micro starts before the rest of the circuit.
08In the lab
Build on a breadboard the bare minimum for the micro to live: power supply with a 100 nF capacitor, MCLR with its 10 kΩ resistor, crystal with its two capacitors and a programming connector. Program something that just turns on an LED. Then remove the decoupling capacitor and observe that the micro resets or runs erratically. It is the quickest demonstration of why it is there.
Without using any library, write the direction and level configuration of a whole port and build an 8-LED binary counter. Then read the datasheet and add the configuration of the analog pins so the ones that were missing work too. Write down which register solved each problem.
Write a loop that toggles a pin as fast as possible and measure the frequency with the oscilloscope. From that measurement and the number of instructions in the loop, calculate the instruction time and compare it with the crystal. Repeat after changing the fuses to the internal oscillator and compare the stability.
Enable the watchdog and deliberately cause an infinite loop: the equipment must restart. Then
measure the micro’s current while running and with SLEEP, waking it with a pushbutton. The
difference —from tens of milliamperes to microamperes— explains by itself the design of any
battery-powered device.
Store a power-up counter in EEPROM and display it at startup. Cut and restore power several times. Discuss how many times per day it would be written if the counter were in the main loop, and how long it would take to exhaust the 100,000 cycles of that cell.
09Common mistakes
| Symptom | Usual cause |
|---|---|
| The micro won’t start and can’t be programmed either | Fuses set for an external oscillator and no crystal on the circuit board. It is recovered with a programmer that injects a clock, or with high voltage. |
| It resets by itself, especially when a relay is activated | The 100 nF capacitor is missing, or the flyback diode, or the ground is badly distributed. |
| A digital input always reads 0 | The pin was left configured as analog by default. |
| A pushbutton reads random values | Floating input: the pull-up (internal or external) or the debouncing is missing. |
| When writing one bit of a port, another pin changes by itself | Read-modify-write on PORT with a load. Use LAT. |
| The program compiles but behaves erratically | SRAM has run out: the stack overwrites the variables. Check the memory map the compiler reports. |
| It works powered by the programmer but not from its own power supply | Insufficient or poorly filtered supply, or not all the VDD/VSS pairs are connected. |
| The EEPROM filled up with corrupted data | Writes during a voltage dip. Enable brown-out. |
| The equipment hangs once a week | No watchdog. With a watchdog it restarts by itself and there is also a record that it happened. |
10Self-assessment
What is the difference between TRIS on a PIC and DDR on an AVR?
The convention is opposite: on a PIC, TRIS set to 1 configures the pin as an input; on an AVR, DDR set to 1 configures it as an output. Mixing them up can destroy a pin that ends up as an output against a pushbutton to ground.
Why is it better to use LAT instead of PORT for writing?
Because writing to PORT involves first reading the actual state of the pins. If a pin is loaded, what is read doesn’t match what was written and that pin changes state. LAT returns the last value written, without looking at the pin.
A PIC with a 4 MHz crystal: how long does an instruction cycle last?
The clock is divided by four: 4 MHz/4 = 1 MHz, that is, 1 µs per instruction. Jump instructions take 2 µs.
What are fuses or configuration bits and why are they dangerous?
They define how the chip starts up —clock, watchdog, brown-out, MCLR function, code protection— and are programmed along with the program. If you select a clock that the circuit board doesn’t have, the micro doesn’t start and can’t be reprogrammed the usual way.
How much current can a pin deliver and what do you do if you need more?
On the order of 20 to 25 mA, with a total limit per chip of about 200 mA. For more, use a transistor or a MOSFET as a switch, with its base resistor and the flyback diode if the load is inductive.
What is the watchdog for, and where must it NOT be refreshed?
It resets the chip if the program stops refreshing it, getting it out of a hang. It must not be refreshed inside an interrupt: if the main program hangs and the interrupt keeps running, the watchdog would never act.
What does the brown-out reset protect against?
It prevents the micro from executing with the supply below its minimum, a situation in which it can corrupt the EEPROM and activate outputs at random. It holds the micro in reset until the voltage recovers.
A 16F877A has 368 bytes of RAM. Does an array of 100 16-bit integers fit?
No: 100 × 2 = 200 bytes for that array alone, plus the other variables and the stack. Even though numerically it looks like it fits, in practice it leaves the program with no margin and it ends up failing.
Why can a digital input always read zero even though the circuit is fine?
Because the pin is configured as an analog input, which is the default state of many pins after reset. You have to configure the corresponding registers (ANSEL, ADCON1) to switch them to digital.
What concrete advantage is there in bringing the programming connector out on the final circuit board?
It lets you reprogram the equipment without desoldering the micro, update the program at the installation site and, during development, load dozens of versions per day. It costs one five-pin connector.