Introduction to
Microcontroller Programming
using an Atmel ATmega32U4
Joachim Fenkes (@dop3j0e)
Gregor Jehle (@hdznrrd)
Note regarding parts: Don't hand out parts right away; people will natually look at them, play with them, and generally pay less attention. Rather hand out parts just before you build anything, and only the parts needed for the module, so fast people can't work too far in advance.
Today's Goals
Understand
Microcontroller programming basics
I/O and Hardware assist units
Basic external peripherals
Reading Datasheets
Implement
Hello World
LED Larson Scanner
Button Control
Who we are
Joachim "Jocki" Fenkes
created the board we're using today
Gregor "hadez" Jehle
used to program MCUs for a living
Who are you?
Please introduce yourselves with
Your name
One sentence about your prior knowledge with microcontrollers
Also ask about prior C knowledge and let people know that this is not a C workshop and we wont help you with C problems
Dedicated to
Jan-Bernd Themann
R.I.P., my friend
Feedback
Please speak up any time you have feedback.
We also gladly take feedback via mail =)
Scope:
Too much information for one day or not?
Granularity:
Too many details somewhere? Too little? Just fine?
Prerequisites:
Did we expect too much prior knowledge anywhere?
Speed:
Did we go too fast / too slow?
Simplicity:
Were we hard to follow anywhere?
What is a Microcontroller?
"A microcontroller [...] is a small computer on a single integrated circuit containing a processor core, memory, and programmable input/output peripherals. Program memory [...] is also often included on chip, as well as a typically small amount of RAM. Microcontrollers are designed for embedded applications [...]" (Source: Wikipedia, bit.ly/1tW56eP )
Applications
CC-BY-NC Gregor Jehle, Faye Yu
CC-BY-NC-SA Gregor Jehle, Samsung Tomorrow
CC-BY-NC-SA Gregor Jehle, Samsung Tomorrow
uC are everywhere. Whenever something has to be measured and/or controlled, a uC is in there somewhere.
Typical block diagram
R - RAM
F - Program Flash
P - Craploads of Peripherals
CPU, a lot of I/O, a whole bunch of peripherals, some RAM for computing & flash for storing your program. Everything connected via a bus
Limitations
A µC ain't a PC!
Optimized for low power and size
Slow compared to a PC or a smartphone
Little RAM
No floating point unit
⇒ using float
or double
will be very very very very slow!
Sometimes not even HW multiplication
For some time now there's very powerfull uC that get close to what PCs where a few years back. A uC is the one-package solution of CPU, RAM, and peripherals.
Our learning modules today
Hello, World!
Interrupts & Timers
LEDs & I/O
Button Control
Extending your I/O
The ATmega32U4 microcontroller
8-bit AVR CPU
Up to 16 MHz on 5V, up to 8 MHz on 3.3V
32 KiB of program Flash
4 KiB used for bootloader, so 28 KiB available for programs
2.5 KiB of SRAM
26 external I/O pins
Integrated USB controller
We won't be talking about the USB controller today because that's pretty advanced stuff. But you'll be using it whenever you program new firmware into the device.
The ATmega32U4 microcontroller
Attention! This is the simple abstraction of a uC, now for reality.
The ATmega32U4 microcontroller
Mention that box colors correspond to previous slide.
AVR 8-bitness
AVR is an 8-bit CPU
Each instruction takes 8-bit operands
More bits are emulated with multiple instructions
Caveat: int
defaults to 16 bits!
→ Wasted cycles if 8 bits are enough
Make it a habit to use uint8_t
#include <inttypes.h>
uintN_t
unsigned integer, N bits
intN_t
signed integer, N bits
The TinyMega Board
The MCU itself
USB jack
Lots of labeled I/O pins
Two LEDs (Green: Power, Blue: User)
Two buttons (Reset, Bootloader/User)
The ATmega32U4 datasheet
Most important piece of documentation
433 pages o.O
We will guide you to the relevant sections
General intro to datasheets will follow later
Get it here: bit.ly/1Ht3JLj (PDF)
(Note: Hover any shortened link to see where it leads)
This is where you get out your pen & paper so you can take notes about things you had to look up.
Arduino vs. bare metal coding
Arduino hides the gory details
Pro: Easier to learn, quick prototyping
Con: You don't understand how it's done → "magic"
Con: Arduino trades speed & size for simplicity
Speed trade-off example: Mapping of pin numbers to ATmega ports/pins via table, so several instructions / memory access vs. single bit set/clear instruction.
Interlude: Bit Operations & uC Patterns
Indispensable when writing µC code
So we prepared a cheat sheet for you
Get the PDF: bit.ly/8bitcheat
What is this shift?
Why would you (1 << PD6)?
To the blackboard!
Module 1
Hello, World!
in which an LED will blink!
Structure of MCU firmware
It's a program like any other!
main()
function comprised of
Initialization code
Main loop
avr-libc takes care of the really gory stuff
Caveat: Don't exit the main-loop
If anyone asks: avr-libc puts an infinite loop after main() exits.
avr-libc
Startup code, platform support
Standard C library functions
Access to µC specialties (avr/*.h
)
Home page: bit.ly/1yI2MN0
Prettier docs: bit.ly/1CduWR7
General Purpose I/O
Most I/O pins of the ATmega can be controlled directly as digital I/O
Output mode
Write a zero (GND) or one (supply voltage, VDD)
Input mode
Convert pin voltage to one or zero and return
Optional pull-up resistor selectable by software
Caveat: Some levels are undefined!
General Purpose I/O
Pins are grouped into "ports"
ATmega32U4 has ports B, C, D, E, F
Pins inside a port are numbered 0..7 eg. B1, B2, C7
Caveat: Not all ports have all pins populated!
If anyone asks: Port A used to be available in very old Atmel controllers. No longer relevant.
Port control though registers
All hardware units in AVR MCUs are controlled through memory-mapped registers
Like special RAM variables
Each port has three registers
PORTx
⇒
Write output values or enable/disable pull-up
PINx
⇒
Read input values (read "Port INput", not "Pin"!)
DDRx
⇒
Data Direction Register selects in (0) / out (1) direction per pin
Port control: Example
#include <avr/io.h>
{
PORTB = (1 << PB3);
DDRB = (1 << PB2);
if (!(PINB & (1 << PB3)))
PORTB |= (1 << PB2);
}
Register field names in avr-libc are generally shift distances , not values. This is necessary for multi-bit fields.
If anyone asks about what a pull-up is -- we'll cover that later today.
Simple delay
#define F_CPU 16000000UL
#include <util/delay.h>
{
_delay_ms(1000 );
_delay_us(10 );
}
These are busy delay loops that work because they know how long specific instructions take based on F_CPU.
Toolchain installation
We are not going to spend time setting up a full IDE. There are tools available for every platform, e.g. Eclipse, but any text editor with syntax highlighting will do today.
Template code
Create a file module1.c
:
#include <avr/io.h>
#include <util/delay.h>
int main(void )
{
while (1 ) {
}
}
Build
Compile the source
avr-gcc -mmcu=atmega32u4 -DF_CPU=16000000UL \
-Os -o module1.elf module1.c
Generate an Intel HEX file for flashing
avr-objcopy -O ihex module1.elf module1.hex
Display the firmware size
avr-size module1.elf
text
+ data
⇒ Flash usage
data
+ bss
⇒ RAM usage
text is the program code. bss is uninitialized data. data is initialized data that is copied from Flash to SRAM before main() is called. There is a way to prevent static data from being copied to RAM -- google for PROGMEM.
Program (Linux)
Erase the whole device -- bootloader always needs this
sudo dfu-programmer atmega32u4 erase
Program the HEX file we just generated
sudo dfu-programmer atmega32u4 flash module1.hex
Have the bootloader jump into the firmware
sudo dfu-programmer atmega32u4 start
Rumor has it you can save on sudo by adding your user to the uucp or serial group.
The default bootloader works in "secure" mode where the only allowed first operation after power-up is erase.
Program (Windows)
Make sure "Reset" is turned off for your first programs
No need to mention this, but if anyone asks: "Reset" would reset the device using the watchdog. If the user program doesn't disable the watchdog, the µC will repeatedly reset. Unchecking "reset" will make the bootloader jump to the user program, so the µC state may not be exactly as after a reset.
Now go forth and code!
Task: Make the user LED on the TinyMega blink with 1 Hz
(the MCU equivalent of "Hello, World!")
Info:
The LED is connected to pin E6
Set the pin to 1 to turn on the LED
Remember to set the pin to output first
Use a simple delay loop, nothing fancy
We left a trap for you to discover, so call us if you run into problems ;)
If you're done, come to the front to order lunch!
Base Clock Prescaler
Ask whether anyone accidentally made their TinyMega 3.3V. They should use clock_div_2. Also use suitable F_CPU!
Bonus task!
Make the LED morse SOS, or even arbitrary text.
Module 2
Interrupts & Timers
in which we will waste less CPU cycles!
Problems with the previous solution
Hogs CPU cycles ("busy waiting" or "polling")
Burns power: CPU constantly busy
More complex tasks will be difficult/impossible to time accurately
Solution: Hardware Timers!
ATmega32U4 has four Timer/Counter (T/C) modules
HW counter, counting up at (divided) clock frequency
"Compare registers" to trigger actions at certain values
Can also directly control I/O pins, capture event timestamps, ...
Interrupts (IRQs)
Asynchronous interruption of program flow
Can happen at any time, triggered by hardware
Many IRQ sources with individual handlers
Timer overflow, Timer compare, Pin change, ...
Can be enabled / disabled globally
sei()
- SEt Interrupts
cli()
- CLear Interrupts
Interrupt Handlers
Special function, "called" through interrupt
Also called "Interrupt Service Routine" (ISR)
Return from handler continues program
#include <avr/interrupt.h>
ISR(TIMER0_COMPA_vect)
{
}
ISR best practices
You're just interrupting someone else
Communication with main loop: shared global variables
Declare them volatile
!
Tells compiler they may change without notice
volatile prevents the compiler from optimizing out repeated reads if it thinks there was no write in between.
Setting up a Timer/Counter
Let's look at the datasheet, chapter 14
o.O So many options!
Let's pick a few that make sense for us
T/Cs are very powerful. It's well worth reading the whole chapter over a glass of whisky.
Choosing a mode
T/C1 has over 16 operational modes - which one do we pick?
We want to wait for a given time, generate an IRQ, repeat
CTC mode (14.8.2) sounds like the one for us:
"Clear Timer on Compare match"
Count to N, interrupt, start back at zero.
Choosing a prescaler
T/C1 can run at several different speeds between CLK and CLK/1024
T/C1 can count up to 65535
We want to wait for half a second
Choose slowest prescaler value of CLK/1024
Question: How many ticks per second?
15625 ticks per second at 16 MHz
Building the T/C control registers
TCCR1A - 14.10.1 (Timer/Counter Control Register A, T/C 1)
We can ignore the Compare Output Modes
Choose WGM 4: CTC with TOP = OCR1A
TCCR1B - 14.10.3 (Timer/Counter Control Register B, T/C 1)
We can ignore Input Capture
Clock Select = 0b101 (CLK / 1024)
TCCR1C - 14.10.5 (you get the idea)
First explain that there are 3 registers. For every fragment, ALT-TAB to the relevant parts of the datasheet to extract the info.
Building the T/C control registers
OCR1A - 14.10.9 (Output Compare Register A, T/C 1)
Determines IRQ period
F_CPU / 1024 * period_in_seconds
TIMSK1 - 14.10.17 (Timer Interrupt Mask, T/C 1)
Enable OCIE1A, disable all others(Output Compare A Interrupt Enable, T/C 1)
TIFR1 - 14.10.19 (Timer Interrupt Flag Register, T/C 1)
Write 0xFF once to clear all pending IRQs
Just to be safe
The setup code
#include <avr/io.h>
{
TCCR1A = 0 ;
TCCR1C = 0 ;
TCNT1 = 0 ;
OCR1A = F_CPU / 2048 ;
TIMSK1 = 1 << OCIE1A;
TIFR1 = 0xFF ;
TCCR1B = (1 << WGM12) | (5 << CS10);
}
Now go forth and code!
Task: Transform the busy loop from module 1 into a timer-driven blinking LED.
Info:
Use T/C 1 in CTC mode
Remember to enable interrupts after setup
Question:
Answer: TIMER1_COMPA_vect, NOT TIMER1_OVF_vect -- overflow only occurs at 0xFFFF, which we never reach!
Bonus tasks!
Save power by using Sleep Mode in main loop
Chapter 7 of datasheet
avr-libc sleep mode documentation: bit.ly/1yMSooS
Again, get the LED to morse text
Tips & Tricks
Toggle a port pin value by writing PINx:
PINE = 1 << PE6;
Little known (but documented) AVR feature
Module 3
LEDs & I/O
in which we will revive K.I.T.T.!
LED basics
Cathode = Minus = short leg = Base plate inside LED
(German: K athode = k urzes Bein)
An LED's brightness depends on current
Typical IF : 10..20 mA
Add "dropper resistor" in series to get the right current
Let's check the datasheet for the expected VF : bit.ly/1IbHPdP (PDF)
Draw typical LED schematic with VDD, resistor, LED, GND.
Briefly open the LED datasheet, scroll through quickly, then explain datasheets in general using the next slide.
Datasheets
Typical sections:
Summary, core features and values
Basic operation
Absolute Maximum Ratings
Device characteristics
Tables with min/typ/max values
Graphs
Detailed usage information
Example application circuits
Test circuits used
Package information, solder patterns
Order information, revisions, disclaimers
Now go through the actual LED datasheet in more detail.
Our LED
IF = 20 mA
VF = 1.85 V
⇒ R = (VDD - VF ) / IF ≈ 160 Ω
Pick next higher standard value: 180 Ω
Explain LED formula using the schematic you drew.
Recommended coding
Build upon your code from last module
Change OCR1A to go faster
Connect all LEDs to pins of the same port
One assignment to set them all
Now go forth and code!
Task: Implement a Larson Scanner
Info:
Remember correct port setup
Bonus task!
Invent other, more complex patterns
(from inside to outside, binary counter, etc...)
Add a button!
Button will usually connect A to B when pushed
"Single Pole, Single Throw" - SPST
Pole ⇒ number of "channels"
Throw ⇒ number of positions per channel(think "# ways to throw a switch")
Idea: Push to connect input pin to VDD
Question: But what happens while not pushed?
Not pressed = floating. But that's explained next slide.
High-Z / floating state
Pins that are not connected anywhere
Called "floating"
Or High-Z for "high resistance to anywhere"
A floating input will read random values!
Collects charge though static and ambient noise
Solution: Remove the High-Z condition
Draw picture of port pin as capacitor and button to VDD.
Low-Z does not mean short circuit!
Pull-up / Pull-down resistors
Provide a path from pin to defined voltage (VDD, GND)
Neither a short circuit nor High-Z
Typical resistor value: around 10kΩ
Pulls floating pin up to VDD or down to GND
Can be easily overcome by a short circuit, e.g. a button
SC creates negligible current while pushed -- 0.5 mA for 10kΩ, 5V
Like a water reservoir (think flush toilet)
Water level is pin voltage
Pull-Up is water supply with valve
Button is big flush valve
Add pull-down resistor to drawing.
Full circuit
Either pull-down with button to VDD or pull-up with button to GND
Question: Which one should we use?
Reminder: ATmega pins have optional internal pull-up!
So choose pull-up with button to GND
Switch pin to input + pull-up
If we read a zero , the button is pushed
Draw pull-up variant with button to GND. Pull-up with button to GND is preferred (button is never connected to VDD)
Level triggered vs. edge triggered
We will poll the button in our main loop
Options:
Do something whenever polling returns "pushed"
Will do something every iteration
"Level triggered"
Do something when button changes from "not pushed" to "pushed"
Will do something once per button push-release cycle
"Edge triggered"
Draw button signal in time plot so the "edge" can be seen.
Coding Advice
This task proved surprisingly hard to solve due to the levels of brainfuck involved, so here are some important hints:
Remember you will read a zero when the button is pushed
Split the complexity into parts:
Now go forth and code!
Task: Advance Larson Scanner manually instead of timer
Info:
Don't throw away the timer code, just turn off timer
Move main Larson code into own function
Poll button in main loop, edge trigger Larson code
Remember to switch pin to input + pull-up
We have left a trap for you -- if anything's weird, please speak up!
Button contact "bouncing"
Buttons have mechanical contacts that are not perfect
When pushed, will bounce ever so slightly
Causes short on-off-on-off-... sequence
Will settle into permanent "on" after a short time
Trap: If polling code is fast enough, it will see this!
Will interpret as several very fast pushes
Only a problem with edge triggering
Show bouncing on oscilloscope if there's enough time.
Debouncing techniques
Analog filter (lowpass)
Will smooth out button signal, needs hardware
Digital filtering
Keep history of last N measurements
Require all measurements to agree
Slower polling (if possible)
Bouncing will disappear in time between polls
Easiest solution
Further reading
ATmega has ways of causing interrupts when an input changes
Pin Change Interrupt
External Interrupt
Trigger on rising edge, falling edge, or low level
Check out datasheet chapter 11 for details
Both methods remove the polling aspect, but you can't debounce them by simply polling slowly -- digital filtering is needed.
Module 5
Extending your I/O
in which three pins will control 16 LEDs!
What is a shift register?
Converts serial data to parallel data or vice versa
Data is shifted in/out bit by bit
A clock signal triggers a single shift
Our device: Serial in, parallel out (SIPO)
Latched: Outputs kept static while new data shifted in
Latch signal changes all outputs at once
How do I use a SIPO shift reg?
for (i = 0 .. number of bits) {
set data input pin to next bit
(starting with outermost bit)
pulse clock pin
}
pulse latch pin
Daisy Chaining
A shift reg usually also has a serial output pin
Can be used as input for another shift reg
Clock and latch pins can be tied together
Infinite shift register! \o/
Not arbitrarily infinite. Shift propagation delay is not the problem, because all regs start shifting at the same time, so you only get that once. CLK and LATCH fan-out will become a problem after a while, though.
SN74HC595
Part of the 74xxx series of standard logic (bit.ly/SaEU1L )
Lots and lots and lots of parts available (bit.ly/SwsD89 )
From every vendor
VV 74SS NNN
VV - Vendor, e.g. SN for Texas Instruments
SS - Series, e.g. HC for High-Speed CMOS
NNN - Function, 595 is latched SIPO
There's also a 4000 series which is CMOS based
SN74HC595 datasheet
Get the datasheet (bit.ly/UdZpfR , PDF)
This is for an NXP part, but equivalent and more readable
Unusual pin names:
SHCP = Shift Clock Pulse = Serial clock
STCP = Storage Clock Pulse = LATCH
Circuit Building
Put the two 74HC595 onto the breadboard
Connect VDD and GND, tie MR to VDD, OE to GND
First DS to TinyMega, second DS to first Q7S
Both CLK to one TinyMega pin, both LATCH to another
Now place LEDs and resistors
Fresh out of the tube, the chips' pins are at an angle. Carefully bend them into right angle by putting the pins on a flat surface and applying force to the chip itself.
Circuit Cheat Sheet
Connect VDD and GND, tie MR to VDD, OE to GND
First DS to TinyMega, second DS to first Q7S
Both CLK to one TinyMega pin, both LATCH to another
Now go forth and code!
Task: Transform the Larson Scanner to use SIPO
Info:
Use a uint16_t buffer for the LED states
Shift that buffer out to the SIPO
Use a simple loop, don't try to be smart
After a while, or when the first people finish, interrupt the coding phase to show debugging techniques and wrap up.
Aside: Debugging technique
Use unused pin as debug signal
Set / reset on given events
Watch on oscilloscope to:
Measure signal timing
Find out time taken by code
Trigger on sporadic events
Demonstrate on oscilloscope if time permits.
Wrapping Up: What we touched today
Feedback
Please help us improve this workshop!
Scope:
Too much information for one day or not?
Granularity:
Too many details somewhere? Too little? Just fine?
Prerequisites:
Did we expect too much prior knowledge anywhere?
Speed:
Did we go too fast / too slow?
Simplicity:
Were we hard to follow anywhere?
We also gladly take feedback via mail =)
That is all.
Now go forth and make stuff!
Thank you for attending.
Everyone's free to leave as they finish
Links to relevant avr-libc docs
Source Attribution
Samsung Crystal Blue washing machine
CC-BY-NC-SA Gregor Jehle
Original: CC-BY-NC-SA Samsung Tomorrow, 2014-05-14 15:31, https://www.flickr.com/photos/samsungtomorrow/14180614352/
Rancilio coffee machine
CC-BY-NC Gregor Jehle
Original: CC-BY-NC Faye Yu, 2014-05-14 15:42, https://www.flickr.com/photos/fayeyu/7776398/
Samsung Stainless Steel microwave oven
CC-BY-NC-SA Gregor Jehle
Original: CC-BY-NC-SA Samsung Tomorrow, 2014-05-14 15:47, https://www.flickr.com/photos/samsungtomorrow/7792317532/
Atmel ATmega32U4 Block Diagram
Color highlighting: CC-0 Gregor Jehle
Original: © 2014 Atmel Corporation, 2014-05-28 15:00, Atmel-7766G-AVR-ATmega16U4-32U4-Datasheet_02/2014