Module-1.2 - Embedded Systems Based on Microcontrollers
Module-1.2 - Embedded Systems Based on Microcontrollers
1
20240924
[module 1.2]
EMBEDDED SYSTEMS BASED
ON MICROCONTROLLERS
ESIOT ISI-LT - UNIBO Embedded systems based on microcontrollers 1
SUMMARY
• In this module we discuss the main component of a
microcontroller and basic microcontroller programming
– CPU and Memory
– GPIO
– Interruptions
– Timer
– Serial communication
• Arduino Uno and Wiring framework are used as
reference platform and programming framework
• The concepts introduced here will be see in practice in
lab
• CPU
• Memory unit
• Ports for general purpose
Input/Output (GPIO)
• Analog/digital converters
• Timers
• Bus for serial communication
• Clock
• Power circuit
• CPU
• Memory unit
• Ports for general purpose
Input/Output (GPIO)
• Analog/digital converters
• Timers
• Bus for serial communication
• Clock
• Power circuit
ATmega328P
- F_END = 32 KiB
- RAM_END = 2 KiB
- E_END = 1 KiB
00000092 <loop>:
92: 08 95 ret
void setup(){ 00000094 <initVariant>:
} 94: 08 95 ret
00000096 <main>:
96: 0e 94 a4 00 call 0x148 ; 0x148 <init>
void loop(){ 9a: 0e 94 4a 00 call 0x94 ; 0x94 <initVariant>
9e: 0e 94 48 00 call 0x90 ; 0x90 <setup>
} a2: c0 e0 ldi r28, 0x00 ; 0
a4: d0 e0 ldi r29, 0x00 ; 0
empty program in a6:
aa:
0e 94 49
20 97
00 call
sbiw
0x92
r28, 0x00
;
;
0x92 <loop>
0
Wiring ac: e1 f3 breq .-8 ; 0xa6 <main+0x10>
ae: 0e 94 00 00 call 0 ; 0x0 <__vectors>
b2: f9 cf rjmp .-14 ; 0xa6 <main+0x10>
…
Executable disassembly
(created by the tool avr-objdump, -d option)
ESIOT ISI-LT - UNIBO Embedded systems based on microcontrollers 24
CONTROL LOOP DISASSEMBLY
00000090 <setup>:
90: 08 95 ret
00000092 <loop>:
92: 08 95 ret
…
00000096 <main>:
int main(void)
{
init();
96: 0e 94 a4 00 call 0x148 ; 0x148 <init>
initVariant();
9a: 0e 94 4a 00 call 0x94 ; 0x94 <initVariant>
setup();
9e: 0e 94 48 00 call 0x90 ; 0x90 <setup>
for (;;) {
loop();
a2: c0 e0 ldi r28, 0x00 ; 0
a4: d0 e0 ldi r29, 0x00 ; 0
a6: 0e 94 49 00 call 0x92 ; 0x92 <loop>
if (serialEventRun) serialEventRun();
aa: 20 97 sbiw r28, 0x00 ; 0
ac: e1 f3 breq .-8 ; 0xa6 <main+0x10>
ae: 0e 94 00 00 call 0 ; 0x0 <__vectors>
b2: f9 cf rjmp .-14 ; 0xa6 <main+0x10>
ESIOT ISI-LT - UNIBO Embedded systems based on microcontrollers 25
THE EXAMPLE
int c; …
000000a6 <setup>:
a6: 84 e6 ldi r24, 0x64 ; 100
void setup(){ a8: 90 e0 ldi r25, 0x00 ; 0
c = 100; aa: 90 93 01 01 sts 0x0101, r25
} ae: 80 93 00 01 sts 0x0100, r24
b2: 08 95 ret
void loop(){
000000b4 <loop>:
c = c + 1;
b4: 80 91 00 01 lds r24, 0x0100
} b8: 90 91 01 01 lds r25, 0x0101
bc: 01 96 adiw r24, 0x01 ; 1
be: 90 93 01 01 sts 0x0101, r25
c2: 80 93 00 01 sts 0x0100, r24
c6: 08 95 ret
…
• CPU
• Memory unit
• Ports for general purpose
Input/Output (GPIO)
• Analog/digital converters
• Timers
• Bus for serial communication
• Clock
• Power circuit
#define LED_PIN 13
void setup() {
pinMode(LED_PIN, OUTPUT);
}
void loop() {
digitalWrite(LED_PIN, HIGH);
delay(1000);
digitalWrite(LED_PIN, LOW);
delay(1000);
}
void setup() {
// initialize the LED pin as an output:
pinMode(ledPin, OUTPUT);
// initialize the pushbutton pin as an input:
pinMode(buttonPin, INPUT);
}
void loop(){
// read the state of the pushbutton value:
buttonState = digitalRead(buttonPin);
void setup() {
// initialize serial communications at 9600 bps:
Serial.begin(9600);
}
void loop() {
// read the analog in value:
sensorValue = analogRead(analogInPin);
// map it to the range of the analog out:
outputValue = map(sensorValue, 0, 1023, 0, 255);
// change the analog out value:
analogWrite(analogOutPin, outputValue);
[*] actually it is possible to enable and manage interrupts for all pins, but
limiting to a specific kind of interrupt: PIN CHANGE (vs. EXTERNAL)
https://github.com/GreyGnome/EnableInterrupt
• Parameters:
– intNum: interrupt number (int)
– ISR: pointer to the interrupt handler
(tipo: void (*func)(void))
– mode: when the interrupt should be generated:
CHANGE/FALLING/RISING: at every change HIGH -> LOW,
LOW -> HIGH
LOW/HIGH: when the state is LOW or HIGH
void setup()
{
// 0 => detecting pin 2 changes
attachInterrupt(0, inc, CHANGE);
Serial.begin(9600);
}
void loop()
{
Serial.println(count);
}
void inc()
{
count++;
}
BEWARE - concurrency problems here
• race conditions
ESIOT ISI-LT - UNIBO Embedded systems based on microcontrollers 55
DISABLING INTERRUPTS
• Possibility to disable/enable interrupt from the user programs,
typically to avoid concurrency problems
– by setting a bit in the status register of the CPU
• Instructions
– STI = Set Interrupt (enable)
– CLI = Clear Interrupt (disable)
• In Wiring/Arduino:
– noInterrupts()
– interrupts()
void setup()
{
Serial.begin(9600);
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), inc, RISING);
}
void loop() {
noInterrupts();
int current = count;
interrupts();
if (current != prev){
Serial.println(current);
prev = current;
}
}
• CPU
• Memory unit
• Ports for general purpose
Input/Output (GPIO)
• Analog/digital converters
• Timers
• Bus for serial communication
• Clock
• Power circuit
we check if this value is less than 256 if we use timer 0 and 2, leads than
65536 if we use timer1
– if this does not hold, then we have to increase the prescaler
• Since the value is grater than 256 but less than 65536, then we can use
timer 1.
void blinkHandler(){
if (!flagState){
digitalWrite(LED_PIN, HIGH);
} else {
digitalWrite(LED_PIN, LOW);
}
flagState = !flagState;
}
void setup()
{
pinMode(LED_PIN,OUTPUT);
Timer1.initialize(1000000); // set a timer of length 10^6 micro sec = 1 sec
Timer1.attachInterrupt(blinkHandler); // runs blinkHandler on each timer interrupt
}
void loop(){}
Figure
• Bit 710 on page
– SE: Sleep25Enable
presents the different clock systems in the ATmega8, and their distribu-
gister
ATMEGA PWR MANAGEMENT
tion. The figure is helpful in selecting an appropriate sleep mode.
The SE bit must be written to logic one to make the MCU enter the sleep mode when the SLEEP
instruction
The is executed.
MCU Control To avoid
Register the control
contains MCU entering the sleep
bits for power mode unless it is the programmer’s
management.
purpose, it is recommended to set the Sleep Enable (SE) bit just before the execution of the
Bit 7 6 5 4 3 2 1 0
SLEEP instruction.
SE SM2 SM1 SM0 ISC11 ISC10 ISC01 ISC00 MCUCR
• Bits 6..4 – SM2..0: Sleep Mode Select Bits 2, 1, and 0
Read/Write R/W R/W R/W R/W R/W R/W R/W R/W
These bits select
Initial Value 0 between
0 the five
0 available
0 sleep
0 modes0 as shown
0 in Table
0 13.
•Table
Bit 713.
– SE: Sleep
Sleep Enable
Mode Select
The SESM2bit must be written
SM1 to logicSM0
one to make theMode
Sleep MCU enter the sleep mode when the SLEEP
instruction is executed. To avoid the MCU entering the sleep mode unless it is the programmer’s
purpose,0 it is recommended
0 to set0 the Sleep
IdleEnable (SE) bit just before the execution of the
SLEEP0instruction. 0 1 ADC Noise Reduction
• Bits06..4 – SM2..0:1 Sleep Mode 0Select Bits 2, 1, and 0
Power-down
These 0bits select between
1 the five available
1 sleep modes as shown in Table 13.
Power-save
ATmega8(L)
1 Sleep Mode
Table 13. 0 Select 0 Reserved
Standby1
SM2
Mode SM1 0 When the 1 SM2..0Sleep
SM0
bits are 110 and an external crystal/resonator clock option is selected, the
Reserved
Mode
SLEEP instruction makes(1)the MCU enter Standby mode. This mode is identical to Power-down
01 01 0 Standby
with the 0exceptionIdle
that the Oscillator is kept running. From Standby mode, the device wakes up
Note: 0 1. Standby mode
0 isin 6
onlyclock
1 cycles.
available with external
ADC Noise crystals or resonators
Reduction
0 1 0 Power-down
Table 14. Active Clock Domains and Wake-up Sources in the Different Sleep Modes
0 1 1 Power-save
Active Clock Domains Oscillators Wake-up Sources
1 0 0 Reserved
TWI SPM/
Sleep 1 0 1 ReservedMain Clock Timer Osc. INT1 Address Timer EEPROM Other
Mode clkCPU clkFLASH clkIO clkADC clkASY Source(1) Enabled Enabled INT0 Match 2 Ready ADC I/O
1 1 0 Standby
Idle X X X X X(2) X X X X X X
Note: 1. Standby mode is only available with external crystals or resonators 33
ADC Noise
X X X X(2) X(3) X X X X
Reduction
Power
X(3) X
Down
Power
X(2) X(2) X(3) X X(2)
Save
Standby(1) X X(3) X
Notes: 1. External Crystal or resonator selected as clock source 33
2. If AS2 bit in ASSR is set
ESIOT ISI-LT
3. Only level interrupt- INT1
UNIBO
and INT0 Embedded systems based on microcontrollers 79
ARDUINO EXAMPLE
30
5.5V
25 5.0V
4.5V
20
ICC (mA)
15
10
3.3V
5 3.0V
2.7V
0
0 2 4 6 8 10 12 14 16 18 20
Frequency (MHz)
Figure 120. Active Supply Current vs. VCC (Internal RC Oscillator, 8MHz)
18
16
-40°C
14
25°C
12 85°C
ICC (mA)
10
0
2.5 3 3.5 4 4.5 5 5.5
VCC (V)
Test examples:
https://learn.sparkfun.com/tutorials/reducing-arduino-power-consumption
• CPU
• Memory unit
• Ports for general purpose
Input/Output (GPIO)
• Analog/digital converters
• Timers
• Bus for serial communication
• Clock
• Power circuit
• The data chunk (block) represents the real data transmitted: each packet
can have a data chunk from 5 to 9 bit (typical value: 8)
• About little endian or big endian
– part of the protocol includes agreeing also on data endianness dei dati,
i.e. the order used to send the bit (from the most significative or the less
significative).
• default if not specified: less significative
• Synchronisation bits include a start bit and a stop bit, to mark the beginning
and end of the packet (there is always a single start bit, while stop bits can
be 2).
• Parity bits (optional) are used for simple low-level error checking.
ESIOT ISI-LT - UNIBO Embedded systems based on microcontrollers 87
CONCRETE EXAMPLE
• Il 9600 8N1 - 9600 baud, 8 data bits, no parity, and 1 stop bit - is one of the
most used protocol
• Transmission example
– suppose to transmit ASCII characters 'O' and ‘K'. ASCII value of O is 79,
i.e. 01001111. The value of K in binary is 01001011. Bits are sent starting
from the least significative:
– since we are transferring at 9600 bps, the time used to keep the signal
high (or low) is 1/(9600 bps), i.e. 104 µs per bit.
– for each byte there are 10 bits sent: one start bit, 8 data bits, and one
stop bit => at 9600 bps we are sending 960 (9600/10) byte per sec.
• UARTs exist also as stand-alone integrate circuits, however typically they are
embedded inside microcontrollers
– example: Arduino Uno - based on ATmega328 - has a single UART,
while Arduino Mega - based on su ATmega2560 - has 4
ESIOT ISI-LT - UNIBO Embedded systems based on microcontrollers 91
UART
A0 TXD
A1 RXD
A2 RTS
CTS Driver /
D0 etc Receiver
D1
D2 UART
...
D7
Connector
IRQ/
WE/
OE/
CE/
Clock
circuit
char data;
void setup()
{
Serial.begin(9600);
}
Using e.g. ArduinoIDE SerialMonitor, it
is possible to send characters that are
void loop()
read and re-sent to the connected PC
{
if (Serial.available()){
data = Serial.read();
Serial.print(data);
}
}
(chapter 8, [EA])
(https://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus)
ESIOT ISI-LT - UNIBO Embedded systems based on microcontrollers 112
SPI AND ARDUINO/ATmega328P
• Arduino/ATmega328P natively supports SPI
– 4 GPIO (multiplexed): pin 10, 11, 12, 13
• 10 => SS
• 11 => MOSI
• 12 => MISO
• 13 => SCK
• Methods
• opening and closing the communication session
SPI.beginTransaction(settings)
…
SPI.endTransaction();
• transferring data
receivedVal = SPI.transfer(val)
receivedVal16 = SPI.transfer16(val16)
SPI.transfer(buffer, size)
• Example:
SPI.beginTransaction(SPISettings(14000000, MSBFIRST,
SPI_MODE0));
receivedVal = SPI.transfer(val)
receivedVal16 = SPI.transfer16(val16)
SPI.transfer(buffer, size)
DATASHEET: PINOUT
of the key parts of this datasheet. First, take a look at th
the first page of the datasheet.
• the bar on top means that it is active when the signal is LOW
• 0V => selected, 5V => not selected
• SDI, SDO:
• serial data in and out pins (i.e. MOSI and MISO on the SPI)
• SCK: clock signal of SPI
• SHDN e WP
• used for shutdown and write protect functions. Not used. WP is
unconnected and SHDN is active when LOW => it must be kept at HIGH to
keep the chip active.
the next 2 bits are the first 2 data bits, which should always be 0 because the
potentiometer can only be as high as 128.
INTENSITY
This is all the information you need to wire the DigiPot correctly and to
send SPI commands to it from your Arduino. Now, you wire it up to control
the brightness of some LEDs.
• 2 potentiometers to control the intensity of 4 legs To fully flesh out your knowledge of SPI communication, you’ll use two MCP44231
DigiPot ICs, for a total of four controllable potentiometer channels. Each one
is used to control the brightness of two LEDs by varying the series resistance
The code in Listing 9-1 executes all these steps and includes a function for
passing the SS pin, register byte, and command to a given chip via SPI. The SPI
.begin() command enables you to initialize the SPI interface on the hardware
SPI pins of the Arduino, and you can use SPI.transfer() to actually send data
ESIOT ISI-LT - UNIBO
over the SPI bus. Embedded systems based on microcontrollers 121
EXAMPLE: SOURCES
#include <SPI.h>
void setup()
{
/* set pin directions for SS */
pinMode(SS1, OUTPUT);
pinMode(SS2, OUTPUT);
/* initialize SPI */
SPI.begin();
}
(chap. 9, [EA])
(chap. 9, [EA])
ESIOT ISI-LT - UNIBO Embedded systems based on microcontrollers 123
I2C AND SPI - COMPARISON
• Many devices - including accelerometers, digital potentiometers,
displays, … - are available both on I2C and SPI
• Comparison
– I2C
• only 2 lines
• slave addressing modality more agile
– SPI
• can work with higher speed
• pull-up resistors not needed