Lecture 2 - (cont.) GPIO, Reset and Clock Control (RCC)
(for slides, see obsidian://open?vault=MySchoolNotes&file=CPE%20316%20Intro%20GPIO%20RCC%20STM32.pptx)
Today is very much a exercise-heavy day. We'll be primarily flashing a lot of software to verify how it work.
GPIO Revisited
The microcontroller we use is called the STM32_L476RGT6:
- STM32: Product Family
- L: Low Power
- 4: ARM Cortex M4
- 76: Line = feature set (ex: number of timers, serial ports, etc)
- R: number of pins (R === 64)
- G: Flash size (our case, 1024KB)
- T: Package (T = LQFP, the alternative is BGA or Ball Grid Array)
- 6: Temperature Range (6 = -40 to 85 C)
Recall the GPIO Registers (see [[rm0351-stm32l47xxx-stm32l48xxx-stm32l49xxx-and-stm32l4axxx-advanced-armbased-32bit-mcus-stmicroelectronics.pdf#page=303]], up to pp. 311).
- MODER: sets the mode (input, output, alt function, analog)
- OTYPER: (can really ignore for now)
- OSPEEDR: sets the speed of pin transitions (low/medium/high/v.high)
- PUPDR: Pull up-down register (see PUPD.excalidraw and Lecture 1 - Intro to STM32).
- IDR: Input Data register (read only)
- ODR: Output Data register (r/w)
- BSRR: set/reset register (write only), sets OR clears a bit
- BRR: reset register (write only), clears a bit ONLY
- AFRL, AFRH: Alt function, choose 1 of 16 functions
- ASC, LCKR: misc (see the manual [[rm0351-stm32l47xxx-stm32l48xxx-stm32l49xxx-and-stm32l4axxx-advanced-armbased-32bit-mcus-stmicroelectronics.pdf]]).
So how do we turn a pin (say 15/14) into an input? We do:
// Pin A in GPIO struct ptr -> MODER (see above) (need to clear bits 15/14 via pg. 304 of the reference manual)
GPIOA->MODER &= ~(0x3 << 14); //equivalent to 0b0b1111111111111111001111111111111111
// A port is set to an input
GPIOA->PUPDR = (0x01) << 14; // set A to the same value
Notice here that &=
maintains the previous values, while the strict =
just clears all the data and writes what we want. We could rewrite line 2 as:
GPIOA->PUPDR &= ~(0x3 << 14); // clear the two bits in 14/15 (no overwrite)
GPIOA->PUPDR |= ~(0x1 << 15); // then set the values in bits 14/15 to our wants
Note that a lot of the #include
macros should do this logic for you. We'll see more of this type of implementation in a bit.
Next thing we'd want is to try to probably read the input data register (IDR, see [[rm0351-stm32l47xxx-stm32l48xxx-stm32l49xxx-and-stm32l4axxx-advanced-armbased-32bit-mcus-stmicroelectronics.pdf#page=306]]):
if (GPIOA->IDR & (1 << 7)) // if the input is high (like a button)
{
// pressed
}
else
{
// not pressed
}
You could image we just poll our button presses and respond when that happens:
while(1)
{
if(buttonPressed())
{
doPressedEvent();
}
else
{
doNonPressedEvent();
}
}
See the example below:
#include "main.h"
int main(void)
{
// turns on clock to GPIO bankks A and C
RCC->AHB2ENR |= (RCC_AHB2ENR_GPIOAEN | RCC_AHB2ENR_GPIOCEN);
// bank A as GPIO mode
GPIOA->MODER &= ~(GPIO_MODER_MODE5);
GPIOA->MODER |= (GPIO_MODER_MODE5_0);
// bank C as GPIO mode
GPIOC->MODER &= ~(GPIO_MODER_MODE13);
GPIOC->PUPDR &= ~(GPIO_PUPDR_PUPD13);
while(1) // poll
{
if (GPIOC->IDR & GPIO_PIN_13) // if pin 13 of port C != 0
{
GPIOA->ODR &= ~GPIO_PIN_5; // set pin 5 of PORT A to 0
}
else
{
GPIOA->ODR |= GPIO_PIN_5; // set pin 5 of PORT A to 1
}
}
}
Worksheet / Board Examples 1
- Set pin
PA5
as an output - Make
PA5
go high - Wait some amount of time (
HAL_Delay(1000);
)
#include "main.h"
int main(void)
{
// 1) set pin PA5 as an output
GPIOA->MODER &= ~(0x03 << 10); // clear just our PA5_MODER data
GPIOA->MODER |= 0x01 << 10; // output mode is 0x01, so input that
// 2) Make PA4 go high
GPIOA->ODR &= ~(0x1 << 5); // clear just our PA5_ODR data (is optional)
GPIOA->ODR |= 0x1 << 5; // input our data.
// we could, alternatively, do the following:
GPIOA->BSRR = (0x1 << 5);
// 3) Wait some time
HAL_Delay(1000); // arguments of ms
}
Reset and Clock Control (RCC)
Often in embedded systems, we really want to have our power usage be lower, since:
- We may be powered by a single battery
- May be powered by a solar panel
- ...
Thus, we may actually want to lower the clock frequency when possible, as
The STM32L476 has lots of different clocks. In the chip of the microcontroller there's:
- HSI 16 internal 16MHz clock oscillator
- MSI RC oscillator (4 Mhz)
- HSE oscillator clock (4 - 48 MHz)
- PLL Clocks x 3 (modulate a lower frequency to a higher one)
- Others: 32kHz low speed internal RC, 32kHz external Xtal, RC 48 MHz for USB
One to be familiar with is the PCLK (peripheral clock). This goes to all GPIO elements (hence the name). Default state is MSI = 4MHz for your SYSCLK, and all peripherals (GPIO, SPI, ...) have their clocks turned off.
One thing we've already seen is the ability to turn on the clock to GPIOA
and GPIOC
:
// turns on clock to GPIO Banks A and C
RCC->AHB2ENR |= (RCC_AHB2ENR_GPIOAEN | RCC_AHB2ENR_GPIOCEN);