<center>
<h4> EEE 158 1st Sem AY 2022-2023 </h4>
<h1> STM32 Register Level Programming and GPIO</h1>
</center>
*Notes before proceeding:*
* The lengths of the required videos are indicated inside the parentheses for your reference.
* The given sample codes are for reference only and are not meant to be run as they are unless otherwise stated.
* The definitions of SFRs here are summarized; check the datasheet for more information.
# Introduction
For this week, we will explore the functionality of STM32 microcontrollers in interfacing with simple input/output devices such as pushbuttons and light-emitting diodes (LEDs). In addition, we will start with a bare metal project to have low-level understanding of what happens in the microcontroller.
# Learning Outcomes
* Create a bare metal C project on STM32Cube.
* Initialize and modify MCU registers using bit masking.
* Implement software-based delays.
* Learn about the microcontroller's GPIO module and associated registers.
* Use the GPIO module to interface with simple I/O devices (pushbutton and LED).
# Summary of Activities
<center>
<table>
<tr>
<th width=20%>Topic</th>
<th width=30%>Activity</th>
<th width=30%>Assessment</th>
<th width=20%>Estimated Time (hours)</th>
</tr>
<tr>
<td>STM32 Bare Metal Project</td>
<td><a href="#Creating-a-Bare-Metal-Project">Setup Guide</a></td>
<td></td>
<td>0.50</td>
</tr>
<tr>
<td>Transistor Level Programming</td>
<td><a href="#Creating-a-Bare-Metal-Project">Sample Program <br>and Analysis</a></td>
<td></td>
<td>0.50</td>
</tr>
<tr>
<td rowspan=3>GPIO</td>
<td><a href="#MCU-GPIO-Peripheral">Overview Handout</a></td>
<td></td>
<td>0.25</td>
</tr>
<tr>
<td><a href="#Digital-Outputs">Video (Digital Outputs)</a></td>
<td><a href="#Exercise-1-Blinky-Blinky-Blinky">RGB LED Exercise</a></td>
<td>0.75</td>
</tr>
<tr>
<td><a href="#Digital-Inputs">Video (Digital Inputs)</a></td>
<td><a href="#Exercise-2-Pushy">Configuration Quiz</a></td>
<td>0.50</td>
</tr>
<tr>
<td> </td>
<td>Optional Synchronous Discussion/Consultation</td>
<td> </td>
<td>0.50</td>
</tr>
<tr>
<th width=20%>Total</th>
<th width=30%> </th>
<th width=30%> </th>
<th width=20%> 3.00 </th>
</tr>
</table>
</center>
---
# Creating a Bare Metal Project
A *bare metal* project has nothing but the bare minimum code necessary for a microcontroller (or any programmed device for that matter) to function. The following are the steps to take in order to create a bare metal project:
1. Make sure you have our previous project from the week 2 activities loaded into our *Project Explorer*. We will refer to this project as your **HAL project**.
2. Create a new STM32 project. 
3. Set STM32F411RET6 as the target as done previously. 
4. Name your project and, in contrast to what we have previously done, select **Empty** as the **Target Project Type** before you select finish. After this step, you should now find the project you created in the *Project Explorer*. 
5. Create a new folder (`Ctrl + N`) and name it `Drivers`. 
6. Copy the `CMSIS` folder from our *HAL Project* to the newly created `Drivers` folder. We should be able to find the CMSIS folder in a folder called `Drivers` as well. CMSIS stands for Common Microcontroller Software Interface Standards and this folder contains the code that initializes our MCU. We should be able to find two folders and a `txt` file if we were able to correctly copy the CMSIS folder. 
7. Copy the `Core -> Src -> system_stm32f4xx.c` file from our *HAL Project* to the `Src` folder of our bare metal project. 
8. The next step is to configure debugging by going to the *Project* menu and clicking *Properties* while our bare metal project is selected. In this window, set the following:
* `C/C++ Build -> Settings -> MCU Post build outputs -> Convert to Intel Hex file` to enabled.
* `C/C++ Build -> Settings -> MCU GCC Assembler -> Debugging` to `Maximum (-g3)`
* `C/C++ Build -> Settings -> MCU GCC Compiler -> Debugging` to `Maximum (-g3)`

9. In the same *Properties* window, go to `C/C++ Build -> Settings -> MCU GCC Compiler -> Include Paths`. In the window that pops up, click on `Workspace...` and add the CMSIS folders we copied to our bare metal project (i.e. `Drivers -> CMSIS -> Include` and `Drivers -> Device -> ST -> STM32F4xx -> Include`). 
10. Lastly, in the *Properties* window, go to `C/C++ Build -> Settings -> MCU GCC Compiler -> Preprocessor` and then add `STM32F411xE`to `Define symbols`. 
*Congratulations*! You have now set-up a bare metal project. Most of what we did was to include the libraries that will help us access registers without knowing their exact memory location.
# Register Level Programming
To check if everything was set-up correctly, replace the contents of `main.c` (`Src` folder) with the following code:
```c=
#include <stdint.h>
#include <stm32f4xx.h>
void GPIO_init(void);
void delay(int);
int main(void){
GPIO_init();
/* Loop forever */
while(1){
GPIOA->ODR |= (1<<5);
delay(100);
GPIOA->ODR &= ~(1<<5);
delay(100);
}
}
void GPIO_init(void){
//Enable Peripherals (GPIOA, GPIOC)
RCC->AHB1ENR |= (1<<0); //Enables GPIOA peripheral (bit 0)
//Set GPIOA, PIN 5 as Output (MODER[11:10] = 01)
GPIOA->MODER &= ~(1<<11); // clear bit 11
GPIOA->MODER |= (1<<10); // set bit 10
GPIOA->OTYPER &= ~(1<<5); //Sets GPIOA, PIN 5 as push-pull
GPIOA->ODR |= (1<<5); //Initializes GPIOA, PIN 5 as HIGH
}
void delay(int d){
int i;
for(; d>0 ;d--){
for(i =0; i<2657;i++);
}
}
```
Before we proceed with making our program from scratch using GPIO, let us first observe what the sample code above does. Build, export, and upload your project on your STM32F411 testbench on TINA and run a transient analysis from 0 to 1s. Make sure that the MCU frequency is $16 MHz$ and set `HSI_value` to `00F42400` which is $16M$ in hex. You should get the following result:
<center>
<img src="https://drive.google.com/uc?export=view&id=1ZQ2US6g2rS3tOqqdb5MntC25tHXKJdOP"/> <br />
</center>
If you happen to have the NUCLEO board, you can also run the project there and get something like this:
<center>
<img src="https://drive.google.com/uc?export=view&id=1LjKQMPlipWmyaEHxXU11pIeSiwr4Thzw"/> <br />
</center>
*So what's happening?* We see the voltage across the LED to toggle between $3.25V$ to $0V$. On the actual board, this looks like a blinking LED. Let's analyze the code per line to see how that is:
* Lines 1 and 2 are `include` commands which loads up external files that would contain variable definitions and built-in functions.
* Lines 4 and 5 are function prototypes which lets the compiler know about the existence of a function without defining it yet. Read more about that [here](https://codescracker.com/cpp/cpp-function-definition.htm#).
* Lines 7 to 17 define our main function.
* Line 8 calls the `GPIO_init()` function.
* Lines 11 to 16 define a `while` loop that runs *forever*.
* Lines 18 to 29 define the function `GPIO_init()`.
* Lines 31 to 36 define the funtion `delay()` which is just a nested `for` loop that repeats depending on the `delay` function parameter.
## Register Bit Masking
One question you may have is what do lines 12, 14 and the whole definition of `GPIO_init` mean. Each line is actually three different operations happening at the same time. Let us look at line 12 as an example:
```c
GPIOA->ODR |= (1<<5);
```
* `GPIOA->ODR` is a dereference operation. Later on, we will learn that `ODR` is a register associated to GPIO pins and preceding it with `GPIOA` means that we are accessing the `ODR` register of Port A. If you are curious and want to learn more about C pointers and dereferencing you can watch [this playlist (optional)](https://www.youtube.com/watch?v=cc16ShyiUG8&list=PLRJhV4hUhIymdWRSwo3cZUlHetbmsSuzS) by Professor Zhu.
* `|=` is a compound assignment operator which combines the regular assignment (`=`) operator with an operation. Assuming that the instruction is `VarA |= VarB`, we can rewrite this as `VarA = VarA | VarB`. In other words, `VarA` becomes both the first operand and destination variable. You can read more about compound assignment [here (optional).](https://www.tutorialspoint.com/cprogramming/c_assignment_operators.htm).
* `(1<<5)` is a bit shift operation. In particular, `1` is bit-shifted to the left by `5` positions. If we rewrite `1` as `0x00000001`, then `(1<<5)` is `0x00000020` or `32` in decimal. For those with a `~` operator (like in line `14`), we invert each of the bits (i.e. `~(1<<5) = 0xFFFFFFDF`).
So, if we 'expand' line 12 it could look like something like this:
```c
// Note that registers are 32-bit unsigned integers
uint32 target_reg = GPIOA->ODR;
uint32 mask = (1<<5); // mask = 0x00000020
target_reg = target_reg | mask;
```
Besides being a massive flex of C prowess, the single command effectively ensures that the fifth bit of `target_reg` is set to `1`. In fact, depending on which bits `mask` is `1`, we can ensure that `target_reg` will also have `1`s in those positions. This is because in an `OR` operation, as long as one operand is `1` the result is always `1`
Similarly, we can also expand line 14:
```c
uint32 target_reg = GPIOA->ODR;
uint32 mask = ~(1<<5); // mask = 0xFFFFFFDF
target_reg = target_reg & mask;
```
With an `AND` operation, as long as one operand has a `0`, the result will always be zero. Thus, in this version of the command, we ensure that the fifth bit in `target_reg` is set to `0`.
***Won't we get the same thing if we used a normal assignment opertion?*** Or, in other words, just use `GPIOA->ODR = (1<<5)`. It will be true that this operation will also ensure the the fifth bit of `GPIOA->ODR` is set to `1`. However, this also sets all the other bits to `0`. If you had multiple changes to `GPIOA->ODR` before this part of your code, these changes are disregarded completely.
You will see later on in the course that it is significantly easier to modifiy individual bits at a time instead of writing the resulting register value. While this may take more time, it is far more flexible, especially when register values are modified by different subroutines or functions.
## Software Delays
We will sometimes find ourselves overwhelmed when things just go too fast and it becomes hard to keep up. Sometimes, we just need to stop everything we are doing and breathe. On each breath, count from `0` to `2567` and then maybe do it again.
This is exactly what happens in the `delay()` function. The MCU literally does nothing but count up until the specified number. It may sound simple but sometimes doing less makes us appreciate things we have more. Try and modify the argument to the `delay()` calls in lines 13 and 15. *Why do you think this delay is necessary?* (Hint: It is more necessary if you have the physical board.)
The `delay()` function is an example of a *Software Delay* or parts of our code that deliberately do nothing in order to wait for other things to happen or to let other things catch up. There are also other possible sources of delay that the MCU can generate either knowingly and unknowingly. For our exercise this week, software delays are enough already.
---
# MCU GPIO Peripheral
In order to understand our [sample code](#Register-Level-Programming) further, we need to talk about peripherals. ***Peripherals*** are like the parts of a computer that is not the CPU (e.g. keyboard, monitor, etc...). Peripherals make our MCU capable of doing things beyond just computating and executing commands. Each peripheral has associated ***Special Function Registers*** (SFRs) that would be where you read information from, write information to, and set the configuration of the peripheral.
## STM32F411RE GPIO
*General-purpose input/output* (GPIO) pins are considered the simplest of peripherals. The *STM32F411RE* microcontroller has 4 GPIO ports. According to its [datasheet](https://uvle.upd.edu.ph/mod/resource/view.php?id=345804), the available pins per port are:
* Port A : `PA0 - PA15`
* Port B : `PB0 - PB10, PB12 - PB15`
* Port C : `PC0 - PC13`
* Port D : `PD2`
All GPIO ports (i.e. A, B, C, and D) have the several registers associated with the configuration of the port which can be found in chapter 8 of the [STM32F411 reference manual](https://uvle.upd.edu.ph/mod/resource/view.php?id=345803). Each I/O pin (i.e. `PA0`, `PB1`, `PC3`, etc...) in the development board has an associated bits in these registers. Some of the most important or frequently used registers are listed below:
<center>
<table>
<tr>
<th><code><b>GPIOx_MODER</b></code></th>
<td align="left">
<b> Port 'x' Port Mode Register </b>
<br>
The GPIOx_MODER register control bits determine whether each pin associated with the I/O port is used as an input, output, alternate function, or analog mode.
<br> <b> 00 - input (reset state)
<br> 01 - General purpose output mode </b>
<br> <b> 10 - Alternate function mode </b>
<br> <b> 11 - Analog mode </b>
</td>
</tr>
<tr>
<th><code><b>GPIOx_OTYPER</b></code></th>
<td align="left">
<b> Port 'x' Port Output Type Register </b>
<br> The GPIOx_OTYPER Register determines whether a pin being used as an output is a 'push-pull' configuration or an 'open-drain' configuration.
<br><b> 0 - push-pull (reset state) </b>
<br><b> 1 - open-drain </b>
</td>
</tr>
<tr>
<th><code><b>GPIOx_IDR</b></code></th>
<td align="left">
<b> Port 'x' Input Data Register </b>
<br>
Data on the I/O pin is accessed via a GPIOx_IDR register. a read of the GPIOx_IDR register reads the value of the I/O pin.
</td>
</tr>
<tr>
<th><code><b>GPIOx_ODR</b></code></th>
<td align="left">
<b> Port 'x' Output Data Register </b>
<br>
Data on the I/O pin can be set/reset through the GPIOx_ODR register. Writing a '1' to this register sets the corresponding pin HIGH, while writing a '0' to this register sets the corresponding pin LOW.
</td>
</tr>
<tr>
<th><code><b>GPIOx_BSRR</b></code></th>
<td align="left">
<b> Port 'x' Bit Set/Reset Register </b>
<br> The GPIOx_BSRR Register set can set or reset a bit. Writing a 1 to the lower 16 bits of this registers sets the bits.
Writing a 1 to the upper 16 bits of this register resets the bits.
</td>
</tr>
</table>
</center>
On the NUCLEO-F411RE development board, there are onboard devices connected to some of our GPIO pins which you may be familiar with already. There is one general-purpose LED, labeled **LD2**, which is connected to pin `PA5`. There is also one the pushbutton, labeled **B1**, which is connected to pin `PC13`. Shown below are the exact connections based on the [board schematic](https://uvle.upd.edu.ph/mod/resource/view.php?id=345805):
<center><img src="https://drive.google.com/uc?export=view&id=1f4lmbtGDg4y9gM5BRoRSHQMw6O8ptNZV"/><br /></center>
## Digital Outputs
A digital output can only be a `1` or a `0` but how this manifests in the real world can vary. Learn more about digital outputs with [this video (11:00)](https://youtu.be/zHHwbRdstoQ). Do note that the target MCU in the lecture is not exactly the same as ours so there are minor differences in some of the registers. Be sure to always check the [reference manual](https://uvle.upd.edu.ph/mod/resource/view.php?id=345803) in case things don't line up.
## Digital Inputs
Similarly, digital inputs can only be two values, but there are also many ways this can be done. The [next video to watch(12:32)](https://youtu.be/BNqL7t7a9W4) explores how we can get digital inputs from a joystick which is similar to the on-board pushbutton of the NUCLEO-F411RE. Similarly, we can check the [reference manual](https://uvle.upd.edu.ph/mod/resource/view.php?id=345803) when there things that don't seem to work for our case.
---
# Graded Activities
## Exercise 1: Blinky Blinky Blinky
An RGB LED is a multi-terminal LED, which, as the name suggests, are three differently colored LEDs combined in a single package. Each individual LED can be turned on to generate a red, green, or blue light. You can read more about [here (optional)](https://www.circuitbread.com/tutorials/how-rgb-leds-work-and-how-to-control-color) but for this exercise you can just treat them as three separate LEDs with one common node.
Below is a sample schematic of a common cathode RGB LED connected to the MCU:
<center><img src="https://drive.google.com/uc?export=view&id=1f2wrlKncyeMqk3HolE8MzDvAkqjodql6"/><br /></center>
You may also download the `.tsc` file [here](https://uvle.upd.edu.ph/mod/resource/view.php?id=345995).
Configure `PC6`, `PC8`, and `PC9` as digital outputs that can control the `R`, `G`, and `B` LEDs respectively. Then, program the LEDs to light up in a certain sequence determined by your student number:
<center>
<table>
<tr>
<th width=10%>Digit </th>
<th width=55%>Description</th>
<th width=35%>Example </th>
</tr>
<tr>
<td><i>Format</i></td>
<td>20XY - SNRGB</td>
<td>2019-06309</td>
</tr>
<tr>
<td>S</td>
<td>If <i>S is odd</i>: Red -> Green -> Blue -> OFF <br />If <i>S is even</i>: Blue -> Green -> Red -> OFF</td>
<td>Blue -> Green -> Red -> OFF</td>
</tr>
<tr>
<td>N</td>
<td>During OFF, all LEDs are OFF for N00 ms</td>
<td>All LEDS are OFF for 600ms</td>
</tr>
<tr>
<td>R</td>
<td>During R, the red LED must be on for R00 ms</td>
<td>Red LED is ON for 300ms</td>
</tr>
<tr>
<td>G</td>
<td>During G, the green LED must be on for G00 ms</td>
<td>Green LED is ON for 1000ms*</td>
</tr>
<tr>
<td>B</td>
<td>During B, the blue LED must be on for B00 ms</td>
<td>Blue LED is ON for 900ms</td>
</tr>
<tr>
<td colspan=3><i>*if N, R, G, or B is 0, it is considered to be equal to 10.</i></td>
</tr>
</table>
</center>
For the example student, the expected LED currents from a transient simulation should look like this:
<center><img src="https://drive.google.com/uc?export=view&id=1lQo0uYhcoaoqg0PcRS7990dZ7u_noC8n"/><br /></center>
#### Technical Notes
* An LED is considered ON if at least 8mA is measured in the transient simulations.
* Sequence durations do not need to be exact but should be reasonably close.
* A small delay is expected at the start of your sequence before configuration finishes; anything that happens during this time is not considered part of your sequence.
* You may choose to use different pins and ports if you think it is necessary.
* You must not modify the connections of the RGB LED.
* You may start simulations with shorter sequences to speed up simulations but your final submissions must show the correct timings.
#### Submission and Grading
* Submit your TINA circuit file (`.tsc`) and properly documented main C code (`.c`).
* Include your name, section, and student number as a comment in your code.
* The exercise is worth 7 points and the following milestones will be considered for partial points:
1. Successfully configuring the RGB LED pins as digital inputs (1 point each, 3 total).
2. Implementing the correct sequence of LEDs turning ON (2 points).
3. Implementing the correct timing for each sequence (0.5 points each, 2 total).
* Add as a comment in the beggining of your code which milestones you were able to accomplish if you weren't able to finish them all.
## Exercise 2: Pushy
*Unfortunately, we cannot directly simulate digital inputs on the TINA simulation platform right now. Thus, the assessment is converted into a quiz. This also means that you should not test your code on the simulator as this will not lead to the expected results.*
Consider two push buttons that we want to interface with the MCU shown below:
<center><img src="https://drive.google.com/uc?export=view&id=1ZAyodjQHnXFf_PTu26IW9reSCqn382dl"/><br /></center>
Assuming `PB6` and `PB7` are the corresponding MCU pins, answer the following questions:
1. (2 points) Which of the two input pins can be configured as `HIGH` by default and `LOW` when pressed? Describe what needs to be done and provide the necessary configuration commands (e.g. `GPIOA->ODR |= (1<<5);`).
2. (2 points) Which of the two input pins can be configured as `LOW` by default and `HIGH` when pressed? Describe what needs to be done and provide the necessary configuration commands.
If both can be used, provide the configuration commands for both. Answers may be typewritten or handwritten as long as they are legible and submitted as one PDF file. No particular format is necessary but your section handler may require one.
*Additional Note*: We consider an input pin to `HIGH` when we read a `1` from the associated `IDR` bit and `LOW` when we read a `0`.
## Submission Bin
Submit one `.zip` file containing the following:
1. The `.tsc` and `.c` files for Exercise 1.
2. The `.pdf` file containing your answers for Exercise 2.
through our [submission bin on UVLe](https://uvle.upd.edu.ph/mod/assign/view.php?id=346062).