HAL (Hardware Abstraction Layer) Functions Reference Guide

Table of contents
  1. HAL (Hardware Abstraction Layer) Functions Reference Guide
    1. What is HAL?
    2. Initialization Functions
      1. HAL_Init()
      2. SystemClock_Config()
      3. MX_GPIO_Init()
      4. MX_USART2_UART_Init()
    3. GPIO (General Purpose Input/Output) Functions
      1. HAL_GPIO_WritePin()
      2. HAL_GPIO_ReadPin()
      3. HAL_GPIO_TogglePin()
    4. Timing Functions
      1. HAL_Delay()
      2. HAL_GetTick()
    5. UART (Serial Communication) Functions
      1. HAL_UART_Transmit()
    6. Common HAL Status Values
    7. HAL in Your Project: Usage Summary
    8. Important HAL Patterns
      1. 1. Always Initialize First
      2. 2. Use Handles for Peripherals
      3. 3. Check Return Values
    9. Related: The LCD Driver Functions
    10. Troubleshooting HAL Issues
    11. Quick Reference Table

This guide explains the Hardware Abstraction Layer (HAL) functions used in the Unit 3 LCD Test project. HAL functions provide easy access to the microcontroller’s hardware without needing to know low-level register details.

What is HAL?

The Hardware Abstraction Layer is a library provided by STMicroelectronics that abstracts away the complexity of the STM32 microcontroller. Instead of writing direct register access code, you call simple functions like HAL_GPIO_WritePin().

Your Code                HAL Library              Microcontroller Registers
┌──────────────────┐    ┌───────────────────┐    ┌──────────────────────┐
│ HAL_GPIO_Toggle  │───>│ Translation to    │───>│ GPIOA->BSRR = ...    │
│ HAL_Delay        │    │ register commands │    │ TIM2->CR1 |= TIM_... │
│ HAL_UART_Transmit│    │ (abstraction)     │    │ SPI2->DR = ...       │
└──────────────────┘    └───────────────────┘    └──────────────────────┘

Initialization Functions

HAL_Init()

Purpose: Initialize the HAL library. Must be called first in your program.

Syntax:

void HAL_Init(void);

What it does:

  • Sets up the Systick timer (for HAL_GetTick() and HAL_Delay())
  • Initializes priority grouping which handles interupts
  • Prepares the HAL for general use

Example:

int main(void) {
    HAL_Init();  // Must be called first!
    
    SystemClock_Config();
    MX_GPIO_Init();
    // ... rest of initialization
}

Important: Call this before any other HAL functions.


SystemClock_Config()

Purpose: Configure the system clock frequency for optimal performance.

Syntax:

void SystemClock_Config(void);

What it does:

  • Enables the PLL (Phase-Locked Loop) oscillator
  • Sets the system clock to 80 MHz (the maximum for STM32L476)
  • Configures the AHB and APB buses

In your project: The system runs at 80 MHz, which means:

  • SPI2 (for LCD) gets 80 MHz
  • UART2 (for serial) gets 80 MHz
  • All timers and peripherals get the full clock speed

You don’t need to understand the details - it’s auto-generated by CubeMX and just needs to be called once at startup.


MX_GPIO_Init()

Purpose: Initialize all GPIO pins as configured in CubeMX.

Syntax:

void MX_GPIO_Init(void);

What it does:

  • Configures each GPIO pin as input or output
  • Sets pull-up/pull-down resistors
  • Sets the speed and alternate functions
  • Enables the GPIO port clocks

In your project: This sets up:

  • LED pin (PA5) as output
  • Button pin as input
  • SPI pins for LCD communication

Auto-generated: CubeMX creates this function based on your .ioc file configuration.


MX_USART2_UART_Init()

Purpose: Initialize UART2 (serial communication) for the serial console.

Syntax:

void MX_USART2_UART_Init(void);

What it does:

  • Configures UART2 with 115200 baud rate
  • Sets up TX (transmit) and RX (receive) pins
  • Enables the UART peripheral clock

Result: This enables printf() to work by redirecting serial output.

Auto-generated: CubeMX creates this based on your configuration.


GPIO (General Purpose Input/Output) Functions

GPIO functions control digital input and output pins.

HAL_GPIO_WritePin()

Purpose: Set a GPIO pin to HIGH (3.3V) or LOW (0V).

Syntax:

void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState);

Parameters:

  • GPIOx - Which port (GPIOA, GPIOB, etc.)
  • GPIO_Pin - Which pin (GPIO_PIN_0, GPIO_PIN_1, etc.)
  • PinState - GPIO_PIN_SET (HIGH/3.3V) or GPIO_PIN_RESET (LOW/0V)

Example: Turn on LED

HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);  // LED on (3.3V)
HAL_Delay(500);  // Wait 500ms
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);  // LED off (0V)

HAL_GPIO_ReadPin()

Purpose: Read the current state of a GPIO input pin.

Syntax:

GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);

Returns:

  • GPIO_PIN_SET if the pin is HIGH (3.3V)
  • GPIO_PIN_RESET if the pin is LOW (0V)

Example: Read button state

GPIO_PinState button_state = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13);

if (button_state == GPIO_PIN_SET) {
    printf("Button is pressed\n");
} else {
    printf("Button is not pressed\n");
}

In your project: Used to read the status of the blue button (B1) during the Magic 8Ball lab.


HAL_GPIO_TogglePin()

Purpose: Toggle a GPIO pin (switch it to the opposite state).

Syntax:

void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);

What it does:

  • If pin is HIGH, set it LOW
  • If pin is LOW, set it HIGH

Example: Blinking LED

while(1) {
    HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);  // Toggle LED
    HAL_Delay(500);  // Wait 500ms
}
// Result: LED blinks every 500ms

In your project: Used to flash the green LED (LD2) during the welcome message.


Timing Functions

HAL_Delay()

Purpose: Pause execution for a specified number of milliseconds.

Syntax:

void HAL_Delay(uint32_t Delay);

Parameters:

  • Delay - Number of milliseconds to wait (0-4,294,967,295)

Example:

printf("Starting countdown...\n");
HAL_Delay(1000);  // Wait 1 second
printf("Go!\n");

In your project: Used extensively for delays between LCD refreshes and animations:

LCD_Fill_Buffer(0);
LCD_printString("Welcome", 20, 10, 1, 5);
LCD_Refresh(&cfg0);
HAL_Delay(200);  // Wait 200ms before next text

Performance note: Blocking delay - the CPU does nothing else while waiting. For a 500ms delay, the CPU sits idle for 500ms.


HAL_GetTick()

Purpose: Get the current system tick count (milliseconds since startup).

Syntax:

uint32_t HAL_GetTick(void);

Returns: Number of milliseconds since HAL_Init() was called.

Example: Measure elapsed time

uint32_t start_time = HAL_GetTick();

// Do some work...

uint32_t elapsed = HAL_GetTick() - start_time;
printf("Operation took %lu ms\n", elapsed);

Use case: Game timing

uint32_t last_frame_time = 0;

while(1) {
    uint32_t current_time = HAL_GetTick();
    
    if (current_time - last_frame_time >= 50) {  // 50ms = 20 FPS
        // Update game frame
        last_frame_time = current_time;
    }
}

UART (Serial Communication) Functions

HAL_UART_Transmit()

Purpose: Send data over the serial port (UART).

Syntax:

HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, 
                                     uint16_t Size, uint32_t Timeout);

Parameters:

  • huart - UART handle (in your case, &huart2)
  • pData - Pointer to data to send
  • Size - Number of bytes to send
  • Timeout - Maximum time to wait (in ms, use HAL_MAX_DELAY for infinite)

Example: Send a string

char message[] = "Hello World\r\n";
HAL_UART_Transmit(&huart2, (uint8_t*)message, sizeof(message)-1, HAL_MAX_DELAY);

In your project: Used indirectly by printf(). The _write() function (in main.c) redirects printf output to this function:

int _write(int file, char *ptr, int len) {
    HAL_UART_Transmit(&huart2, (uint8_t*)ptr, len, HAL_MAX_DELAY);
    return len;
}

This is why printf() works and prints to the serial monitor!

Blocking: This function waits for transmission to complete before returning.


Common HAL Status Values

Many HAL functions return a status to indicate success or failure:

HAL_StatusTypeDef status = HAL_SPI_Transmit(&hspi2, data, length, timeout);

if (status == HAL_OK) {
    // Success!
} else if (status == HAL_ERROR) {
    // Error occurred
} else if (status == HAL_BUSY) {
    // Peripheral is busy
} else if (status == HAL_TIMEOUT) {
    // Timeout waiting for operation
}

HAL in Your Project: Usage Summary

Here’s how the HAL functions work together in your LCD test:

int main(void) {
    // 1. Initialize HAL
    HAL_Init();
    
    // 2. Configure system clock for 80 MHz
    SystemClock_Config();
    
    // 3. Initialize GPIO pins
    MX_GPIO_Init();
    
    // 4. Initialize serial port
    MX_USART2_UART_Init();
    
    // 5. Now you can use HAL functions
    while(1) {
        // LED control
        HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
        
        // Timing
        HAL_Delay(500);
        
        // Serial output (via printf)
        printf("Elapsed: %lu ms\n", HAL_GetTick());
    }
}

Important HAL Patterns

1. Always Initialize First

// Wrong - HAL functions won't work yet
HAL_Delay(1000);
HAL_Init();

// Correct - Initialize before using
HAL_Init();
SystemClock_Config();
HAL_Delay(1000);  // Now this works

2. Use Handles for Peripherals

Most HAL functions take a “handle” - a structure containing peripheral configuration:

// UART handle (created by CubeMX)
extern UART_HandleTypeDef huart2;

// Use the handle to access the peripheral
HAL_UART_Transmit(&huart2, data, length, timeout);

3. Check Return Values

For critical operations, check if they succeeded:

HAL_StatusTypeDef status = HAL_UART_Transmit(&huart2, data, 100, 1000);

if (status != HAL_OK) {
    printf("Serial transmission failed!\n");
    while(1);  // Hang if critical error
}

Note that the LCD functions (like LCD_Fill_Buffer() and LCD_Refresh()) are not HAL functions - they’re part of the ST7789V2 driver library. However, they internally use HAL functions like:

// Inside LCD driver
void LCD_Draw_Circle(...) {
    // Uses HAL_Delay() for timing
    // Uses HAL_SPI functions for communication
    // Uses HAL_GPIO functions for control signals
}

Troubleshooting HAL Issues

Problem Likely Cause Solution
HAL_Delay() doesn’t work Forgot HAL_Init() Call HAL_Init() first
Serial output doesn’t appear UART not initialized Call MX_USART2_UART_Init()
LED doesn’t toggle Wrong pin name Check GPIO pin definitions in main.h
Code hangs on HAL_Delay() Interrupt conflict Check if Systick interrupt is enabled
GPIO pin stuck on Hardware issue Check connections and pin configuration

Quick Reference Table

Function Purpose Example
HAL_Init() Initialize HAL HAL_Init();
SystemClock_Config() Set system clock to 80 MHz SystemClock_Config();
MX_GPIO_Init() Initialize GPIO pins MX_GPIO_Init();
MX_USART2_UART_Init() Initialize serial port MX_USART2_UART_Init();
HAL_GPIO_WritePin() Set pin HIGH or LOW HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
HAL_GPIO_ReadPin() Read pin state if (HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13) == GPIO_PIN_SET) { ... }
HAL_GPIO_TogglePin() Toggle pin state HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
HAL_Delay() Wait in milliseconds HAL_Delay(500);
HAL_GetTick() Get elapsed milliseconds uint32_t ms = HAL_GetTick();
HAL_UART_Transmit() Send serial data HAL_UART_Transmit(&huart2, data, len, HAL_MAX_DELAY);

This site uses Just the Docs, a documentation theme for Jekyll.