Bare Metal Programming
Yes, let’s take a byte out of this apple
(I am sorry I am dad and grandfather and I live for these puns.
This presentation is partially based on the
podcasts by Mitch Davis - Bare-Metal MCU
Bare Metal Programming
Why? Speed and Efficiency
Bare Metal Programming
When we mentioned Arduino
Bootloader earlier in the
class, we spoke about memory.
Specific memory blocks are
called registers
Bare Metal Programming
Registers are memory blocks
containing essential data for
controlling or setting up
microcontrollers.
Bare Metal Programming
Each port is controlled by
three registers, which are
also defined variables in the
arduino language. The DDR
register, determines whether
the pin is an INPUT or OUTPUT.
The PORT register controls
whether the pin is HIGH or
LOW, and the PIN register
reads the state of INPUT pins
set to input with pinMode().
Registers are memory blocks containing essential
data for controlling or setting up microcontrollers.
Bare Metal Programming
Each port is controlled by
three registers, which are
also defined variables in the
arduino language. The DDR
register, determines whether
the pin is an INPUT or OUTPUT.
The PORT register controls
whether the pin is HIGH or
LOW, and the PIN register
reads the state of INPUT pins
set to input with pinMode().
Bare Metal Programming
Each port is controlled by
three registers, which are
also defined variables in the
arduino language. The DDR
register, determines whether
the pin is an INPUT or OUTPUT.
The PORT register controls
whether the pin is HIGH or
LOW, and the PIN register
reads the state of INPUT pins
set to input with pinMode().
Ok, but what is a register exactly?
Bare Metal Programming
Each port is controlled by
three registers, which are
also defined variables in the
arduino language. The DDR
register, determines whether
the pin is an INPUT or OUTPUT.
The PORT register controls
whether the pin is HIGH or
LOW, and the PIN register
reads the state of INPUT pins
set to input with pinMode().
Registers are memory blocks containing essential
data for controlling or setting up microcontrollers.
Bare Metal Programming
void setup() {
Each port is controlled by
// initialize the LED pin as an output: three registers, which are
pinMode(ledPin, OUTPUT); DDR Responsibility
// initialize the pushbutton pin as an input: also defined variables in the
pinMode(buttonPin,INPUT_PULLUP); DDR Responsibility
} arduino language. The DDR
void loop() { register, determines whether
// read the state of the pushbutton value:
buttonState = digitalRead(buttonPin); the pin is an INPUT or OUTPUT.
// check if the pushbutton is pressed. If it is, the
The PORT register controls
buttonState is HIGH:
if (buttonState == HIGH) {
whether the pin is HIGH or
// turn LED on: LOW, and the PIN register
digitalWrite(ledPin, HIGH);
} else { reads the state of INPUT pins
// turn LED off:
digitalWrite(ledPin, LOW); set to input with pinMode().
}
}
Look at our Arduino code to use a momentary switch
with a LED.
Bare Metal Programming
void setup() {
Each port is controlled by
// initialize the LED pin as an output: three registers, which are
pinMode(ledPin, OUTPUT);
// initialize the pushbutton pin as an input: also defined variables in the
pinMode(buttonPin,INPUT_PULLUP);
} arduino language. The DDR
void loop() { register, determines whether
// read the state of the pushbutton value:
buttonState = digitalRead(buttonPin); the pin is an INPUT or OUTPUT.
// check if the pushbutton is pressed. If it is, the
The PORT register controls
buttonState is HIGH:
if (buttonState == HIGH) {
whether the pin is HIGH or
// turn LED on: LOW, and the PIN register
digitalWrite(ledPin, HIGH); Port Responsibility
} else { reads the state of INPUT pins
// turn LED off:
digitalWrite(ledPin, LOW); Port Responsibility set to input with pinMode().
}
} Look at our Arduino code to use a momentary switch
with a LED.
Bare Metal Programming
void setup() {
Each port is controlled by
// initialize the LED pin as an output: three registers, which are
pinMode(ledPin, OUTPUT);
// initialize the pushbutton pin as an input: also defined variables in the
pinMode(buttonPin,INPUT_PULLUP);
} arduino language. The DDR
void loop() { register, determines whether
// read the state of the pushbutton value:
buttonState = digitalRead(buttonPin); Pin Responsibility the pin is an INPUT or OUTPUT.
// check if the pushbutton is pressed. If it is, the
The PORT register controls
buttonState is HIGH:
if (buttonState == HIGH) Pin Responsibility
whether the pin is HIGH or
{ LOW, and the PIN register
// turn LED on: digitalWrite(ledPin, HIGH);
} else { reads the state of INPUT pins
// turn LED off:
digitalWrite(ledPin, LOW); set to input with pinMode().
}
}
Look at our Arduino code to use a momentary switch
with a LED.
Bare metal Programming
We need a quick review of bits and bytes.
Bare metal Programming
We need a quick review of bits and bytes.
Bare metal Programming
We need a quick review of bits and bytes.
Bare metal Programming
Please note: The Arduino Port Registers are 8 bit.
0 0 1 1 0 0 1 0
0 0 32 16 0 0 2 0
50 = 0+0+32+16+0+02+0
The decimal equivalent of 00110010 is 50
We need a quick review of bits and bytes.
Bare metal Programming
Please note: The Arduino Port Registers are 8 bit.
Convert 00100010 to decimal
0 0 1 0 0 0 1 0
What is the decimal equivalent?
We need a quick review of bits and bytes.
Bare metal Programming
Please note: The Arduino Port Registers are 8 bit.
Convert 00100010 to decimal
0 0 1 0 0 0 1 0
0 0 32 0 0 0 2 0
What is the decimal equivalent? 34 = 32 +2
We need a quick review of bits and bytes.
Bare Metal Programming
Port Register
How to:
Bare Metal Programming
Let’s return back to the
Arduino. PLease review
how we would program the
BuiltIn LED pin (13) to
blink using Arduino code.
Bare Metal Programming
// C++ code
void setup()
pinMode(LED_BUILTIN, OUTPUT);
void loop()
digitalWrite(LED_BUILTIN, HIGH);
Want to see it work? Please access
Circuit in TinkerCad. Select an delay(1000); // Wait for 1000 millisecond(s)
Arduino and review how to program digitalWrite(LED_BUILTIN, LOW);
Pin 13 or the BuiltIn Pin See link
delay(1000); // Wait for 1000 millisecond(s)
}
Bare Metal Programming
Port B has an 8 Bit Register
Port Bin Dec
PortB0 Digital Pin 8
PortB1 Digital Pin 9
B0 0 0
PortB2 Digital Pin 10
B1 0 0
PortB3 Digital Pin 11
B2 0 0
PortB4 Digital Pin 12
PortB5 Digital Pin 13 (LED_BUILTIN);
B3 0 0
We cannot access PortB6 or PORTB B4 0 0
We want to turn on Pin13 B5 1 32
Therefore all the ports are set to Zero except for PortB5 B6 0 0
00100000 binary
B7 0 0
00320000 = 0+0+32+0+0+0+0
Bare Metal Programming
Now use the decimal
equivalent to turn on BUILTIN
LED
HINT 32
Click here for solution
Bare Metal Programming
Now use turn on digital pin 9
using the PORT variable to
turn on a LED.
HINT: You will need a
breadboard, resistor and LED
Click here for solution
Bare Metal Programming
DDR Register
How to:
Bare Metal Programming
void setup()
//pinMode(LED_BUILTIN, OUTPUT);
DDRB = 32;// B010000 sets PB5 as OUTPUT PB5 is 0010000
or 32
void loop()
PORTB=32; //0010000;
Click here for solution
delay(2000); // Wait for 1000 millisecond(s)
PORTB=0;
delay(1000); // Wait for 1000 millisecond(s)
}
Bare Metal Programming
Now go ahead and change
the DDR register
variable to suit Pin 9
Bare Metal Programming - memory & Pointers
Bare Metal Programming - memory & Pointers
Bare Metal Programming - memory & Pointers
So how do we
write 32 to Ox25
(PORTB)?
Bare Metal Programming - memory & Pointers
So how do we
write 32 to Ox25
(PORTB)?
We use pointers!
Bare Metal Programming - memory & Pointers
So how do we
write 32 to Ox25
(PORTB)?
We use pointers!
A Pointer in the C
language is a variable
that points to the
memory location of
another variable
memory address.
Bare Metal Programming - memory & Pointers
We use pointers!
A Pointer in the C language is a variable that points to the memory
location of another variable memory address.
Simple Example
int classicVariable = 16; // create a “classic integer variable
int* pointerToClassicVariable = &classicVariable; //create a pointer to “point” to classicVariable or reference the variable
* pointerToClassicVariable = 32; //dereference pointerToClassicVariable and assign 32 to classicVariable
Bare Metal Programming - memory & Pointers
We use pointers!
A Pointer in the C language is a variable that points to the memory
location of another variable memory address.
So how do we write 32 to Ox25 (PORTB)?
volatile byte* pointerToRegisterB = 0x25; // create a pointer variable called pointerToRegisterB which is pointed to Ox25;
* pointerToRegisterB = 32; //dereference pointerToRegisterB and assign 32 to memory location to Ox25
Bare Metal Programming - memory & Pointers
So how do we write 32 to Ox25 (PORTB)?
void setup()
{
DDRB = 32;// B010000 sets PB5 as OUTPUT PB5 is 0010000 or 32 );
}
void loop()
{
volatile byte* pointerToRegisterB = 0x25;
*pointerToRegisterB = 32; //PORTB=32;// PORTB is from pinout 32 decimal
delay(2000); // Wait for 1000 millisecond(s)
*pointerToRegisterB = 0; //PORTB=0; //PORTB 0 decimal
delay(1000); // Wait for 1000 millisecond(s)
}
Bare Metal Programming - memory & Pointers
So how do we write 32 to Ox25 (PORTB)?
volatile byte* pointerToRegisterB = 0x25; // create a pointer variable called pointerToRegisterB which is pointed to Ox25;
* pointerToRegisterB = 32; //dereference pointerToRegisterB and assign 32 to memory location to Ox25
Wow, can we do this all on one line? I don’t type well
Bare Metal Programming - memory & Pointers
So how do we write 32 to Ox25 (PORTB)?
volatile byte* pointerToRegisterB = 0x25; // create a pointer variable called pointerToRegisterB which is pointed to Ox25;
* pointerToRegisterB = 32; //dereference pointerToRegisterB and assign 32 to memory location to Ox25
Wow, can we do this all on one line? I don’t type well
Well, yes we can
* ( (volatile byte*) 0x25) = 32;
Ok, I guess if you say so.
Bare Metal Programming - memory & Pointers
So how do we write 32 to Ox25 (PORTB)?
volatile byte* pointerToRegisterB = 0x25; // create a pointer variable called pointerToRegisterB which is pointed to Ox25;
* pointerToRegisterB = 32; //dereference pointerToRegisterB and assign 32 to memory location to Ox25
We can so let me show you.
* ( (volatile byte*) 0x25) = 32;
We assign the memory address 0x25 to a pointer
Bare Metal Programming - memory & Pointers
So how do we write 32 to Ox25 (PORTB)?
volatile byte* pointerToRegisterB = 0x25; // create a pointer variable called pointerToRegisterB which is pointed to Ox25;
* pointerToRegisterB = 32; //dereference pointerToRegisterB and assign 32 to memory location to Ox25
We can say so. Let me show you.
* ( (volatile byte*) 0x25) = 32;
We dereference it with * and assign the 0x25 to 32
Bare Metal Programming - memory & Pointers
So how do we write 32 to Ox25 (PORTB)?
volatile byte* pointerToRegisterB = 0x25; // create a pointer variable called pointerToRegisterB which is pointed to Ox25;
* pointerToRegisterB = 32; //dereference pointerToRegisterB and assign 32 to memory location to Ox25
We can say so. Let me show you.
* ( (volatile byte*) 0x25) = 32;
We dereference it with * and assign the 0x25 to 32
Bare Metal Programming - memory & Pointers
So how do we write 32 to Ox25 (PORTB)?
void loop()
{
/* volatile byte* memoryPointer = (volatile byte*) 0x25;// we are assigning 0x25 memory to memoryPointer
*memoryPointer=32; */
* ( (volatile byte*) 0x25) = 32; //replaces two lines of the above code
delay(2000); // Wait for 1000 millisecond(s)
* ( (volatile byte*) 0x25) = 0;
delay(1000); // Wait for 1000 millisecond(s)
}
Bare Metal Programming - memory & Pointers
So how do we write 32 to Ox25 (PORTB)?
void loop()
{
* ( (volatile byte*) 0x25) = 32; //replaces two lines of the above code
delay(2000); // Wait for 1000 millisecond(s)
* ( (volatile byte*) 0x25) = 0;
delay(1000); // Wait for 1000 millisecond(s)
}
This is good. We can make it better by using the #define
Bare Metal Programming - memory & Pointers
#define blink13 * ( (volatile byte*) 0x25)
void setup()
{
DDRB = 32;
}
void loop()
{
blink13 = 32; // replaces * ( (volatile byte*) 0x25) = 32;
delay(2000); // Wait for 1000 millisecond(s)
blink13 = 0; // ( (volatile byte*) 0x25) = 0;
delay(1000); // Wait for 1000 millisecond(s)
}
#define allows us to create blink13 - sorta like a variable
Bare Metal Programming - memory & Pointers
#define blink13 * ( (volatile byte*) 0x25)
#define blink13Set * ( (volatile byte*) 0x24)
void setup()
{
blink13Set = 32; // replaces DDRB = 32;
}
void loop()
{
blink13 = 32; // replaces * ( (volatile byte*) 0x25) = 32;
delay(2000); // Wait for 1000 millisecond(s)
blink13 = 0; // ( (volatile byte*) 0x25) = 0;
delay(1000); // Wait for 1000 millisecond(s)
}
We replace DDRB with blink13Set assigning 0x24 DDRB memory location
Bare Metal Programming - BIT Masking
We have been using axee, time for a
scalpel
0 0 1 0 0 0 0 0
Ok, Pin 13 is turned on, but the other pins are turned off!
Bare Metal Programming - Quick register reminder
Port B has an 8 Bit Register
Port Bin Dec
PortB0 Digital Pin 8
PortB1 Digital Pin 9
B0 0 0
PortB2 Digital Pin 10
B1 0 0
PortB3 Digital Pin 11
B2 0 0
PortB4 Digital Pin 12
PortB5 Digital Pin 13 (LED_BUILTIN);
B3 0 0
We cannot access PortB6 or PORTB B4 0 0
We want to turn on Pin13 B5 1 32
Therefore all the ports are set to Zero except for PortB5 B6 0 0
00100000 binary
B7 0 0
00320000 = 0+0+32+0+0+0+0
Bare Metal Programming - BIT Masking
We have been using axe, time for a scalpel
0 0 1 0 0 0 0 0
Ok, Pin 13 is turned on, but the other pins are turned off!
This is not very efficient. We just to turn on PB5 or Pin13
Bare Metal Programming - BIT Masking
First we need to get to the truth. The above is a Logic Truth Table and Not Truth Table
OR, AND, XOR - TRUTH TABLES NOT ~ TRUTH TABLE
A B OR | AND & XOR ^ A B
False False False False False
0 1
False True True False True
1 0
True False True False True
True True True True False
We have been using axe, time for a scalpel
Bare Metal Programming - BIT Masking
We have been using axe, time for a scalpel
OR OPERATION - TURN ON A PIN
0 0 0 0 1 0 1 0
OR
0 0 1 0 0 0 0 0
0 0 1 0 1 0 1 0
PORTB = PORTB|32 OR PORTB|=32 - turns on Pin 13 while leaving on Pin 9 and Pin 11
Bare Metal Programming - BIT Masking
We have been using axe, time for a scalpel
AND OPERATION - TURN OFF A PIN
0 0 1 0 1 0 1 0
AND
1 1 0 1 1 1 1 1
0 0 0 0 1 0 1 0
PORTB = PORTB&223 OR PORTB&=233 - turns off Pin 13 while leaving on Pin 9 and Pin 11
Bare Metal Programming - BIT Masking
We have been using axe, time for a scalpel
AND OPERATION - TURN OFF A PIN
PORTB = PORTB&223 OR PORTB&=233 - turns off Pin 13 while leaving on Pin 9 and Pin 11
233 Really? How am I am going to remember that?
Bare Metal Programming - BIT Masking
We have been using axe, time for a scalpel
AND OPERATION - TURN OFF A PIN
PORTB = PORTB&223 OR PORTB&=233 - turns off Pin 13 while leaving on Pin 9 and Pin 11
233 Really? How am I am going to remember that?
Bit Shifting to the rescue!
Bare Metal Programming - BIT Masking
Bit Shifting
0 0 1 1 1 0 1 0 PORTB
0 1 1 1 0 1 0 0 PORTB <<1
1 1 1 0 1 0 0 0 PORTB <<2
1 1 0 1 0 0 0 0 PORTB <<3
Bare Metal Programming - BIT Masking
Bit Shifting
0 0 0 0 0 0 0 1 PORTB
0 0 0 0 0 0 1 0 PORTB <<1
0 0 0 0 0 1 0 0 PORTB <<2
0 0 0 0 1 0 0 0 PORTB <<3
Bare Metal Programming - BIT Masking
We have been using axe, time for a scalpel
Bit Banging - one bit on
0 0 0 0 1 0 1 0
OR
0 0 1 0 0 0 0 0 1<<5
0 0 1 0 1 0 1 0
PORTB | = 32
PORTB |=(1<<5)
Bare Metal Programming - BIT Masking
Bit Shifting
0 0 0 0 0 0 1 0 1<<2
1 1 1 1 1 1 0 1 ~(1<<2)
Bare Metal Programming - BIT Masking
Bit Shifting
1 0 1 0 1 0 1 0
AND
1 1 0 1 1 1 1 0 ~(1<<5)
1 0 0 0 1 0 1 0
PORTB &= 223
PORTB &=~(1<<5)
Bare Metal Programming - BIT Masking
We have been using axe, time for a scalpel
Bit Banging - one bit on
1 0 1 0 1 0 1 0 1 0 0 0 1 0 1 0
1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 0
1 0 0 0 1 0 1 0 1 0 1 0 1 0 1 0
XORR XORR
PORTB ^ (1<<5) or PORTB ^= (1<<5)
XOR one or other but not both
Bare Metal Programming - BIT Masking
Bare Metal Programming - Putting It All Together
l
#define blink13 * ( (volatile byte*) 0x25)
#define blink13Set * ( (volatile byte*) 0x24)
void setup()
{
blink13Set |=(1<<5) // replaces 32 or B0010000
}
void loop()
{
blink13 |= (1<<5); // replaces 32 or B001000032; // replaces 32
delay(2000); // Wait for 2000 millisecond(s)
blink13 &= ~(1<<5); //
delay(500); // Wait for 500 millisecond(s)
}