🌱 Triển Khai Driver Ngoại vi MPU trên STM32F4 (Phần 1)

🌱 Triển Khai Driver Ngoại vi MPU trên STM32F4 (Phần 1)

    Bài trước mình đã giới thiệu với các bạn về MPU và lập trình cấu hình MPU trực tiếp bằng thanh ghi. Bài viết này mình sẽ giới thiệu về MPU Driver, và xây dựng MPU Driver để người dùng sử dụng, thay vì phải nhớ các thanh ghi như lập trình thanh ghi.

ARM Cortex M4 Memory Protection Unit

Mục lục


👉 Phần cứng sử dụng

    STM32F401RE - NUCLEO Board.

STM32F401 NUCLEO Board

👉 Phần mềm sử dụng

    STM32CubeIDE ⇒ Xem hướng dẫn sử dụng.

👉 Hướng dẫn lập trình MPU Register

Xem tại đây!

👉 Kế hoạch xây dựng MPU Driver

    Chúng ta sẽ cùng nhau xây dựng các hàm chính sử dụng cho MPU!

  1. Define các Macro cần thiết
    Tổ chức Data cấu hình cho MPU, bao gồm các yếu tố:
    1. Enable/Disable bit Default Memory Map.
    2. Enable/Disable MPU in Exception.
    3. Enable/Disable MemManage Handler.
    4. Data Config bao gồm: Region Number, Start Address, Region Size, Memory Type, Access Right, ...
  2. Viết hàm MPU_Init ⇒ Sử dụng để khởi tạo MPU với bộ Data Config.
  3. Viết hàm MPU_DeInit ⇒ Reset tất cả cấu hình MPU về trạng thái mặc định.
  4. Viết hàm MPU_SetRegionConfig ⇒ Cấu hình cho một Region với các thuộc tính khác sau khi Init.
  5. Viết hàm MPU_GetErrorDetails ⇒ Get lỗi khi chương trình nhảy vào Fault.

👉 Ví dụ triển khai MPU Driver

    Dưới đây là code minh họa triển khai MPU Driver trên STM32F401RE, dựa trên kế hoạch trên:

/* mpu_driver.h */
#ifndef MPU_DRIVER_H
#define MPU_DRIVER_H

#include "stm32f4xx.h"

// Macro cho cấu hình MPU
#define MPU_ENABLE_DEFAULT_MEM      1
#define MPU_DISABLE_DEFAULT_MEM     0
#define MPU_ENABLE_IN_EXCEPTION     1
#define MPU_DISABLE_IN_EXCEPTION    0

// Memory Types
#define MPU_MEM_STRONG_ORDER        0x00
#define MPU_MEM_NORMAL_CACHEABLE    0x01
#define MPU_MEM_NORMAL_NONCACHE     0x02

// Access Rights
#define MPU_ACCESS_NO_ACCESS        0x00
#define MPU_ACCESS_FULL             0x03
#define MPU_ACCESS_READ_ONLY        0x05

// Cấu trúc dữ liệu cho MPU Region
typedef struct {
    uint8_t  RegionNumber;      // 0-7
    uint32_t StartAddress;      // Địa chỉ bắt đầu (aligned)
    uint8_t  Size;              // SIZE field (log2(size) - 1)
    uint8_t  MemoryType;        // Strong Order, Normal Cacheable, NonCacheable
    uint8_t  AccessRight;       // Quyền truy cập
    uint8_t  Enable;            // 1: Enable, 0: Disable
} MPU_RegionConfig_t;

// Cấu trúc dữ liệu cho MPU tổng quát
typedef struct {
    uint8_t EnableDefaultMem;   // MPU_CTRL.PRIVDEFENA
    uint8_t EnableInException;  // MPU_CTRL.HFNMIENA
    uint8_t EnableMemManage;    // Enable MemManage Handler
    MPU_RegionConfig_t* Regions;// Mảng các Region
    uint8_t RegionCount;        // Số lượng Region
} MPU_Config_t;

// Hàm Driver
void MPU_Init(MPU_Config_t* config);
void MPU_DeInit(void);
void MPU_SetRegionConfig(MPU_RegionConfig_t* region);
void MPU_GetErrorDetails(uint32_t* faultAddr, uint8_t* faultStatus);

#endif /* MPU_DRIVER_H */

/* mpu_driver.c */
#include "mpu_driver.h"

void MPU_Init(MPU_Config_t* config) {
    // Disable MPU trước khi cấu hình
    MPU->CTRL &= ~MPU_CTRL_ENABLE_Msk;

    // Cấu hình các Region
    for (uint8_t i = 0; i < config->RegionCount; i++) {
        MPU_RegionConfig_t* region = &config->Regions[i];
        MPU->RNR  = region->RegionNumber;
        MPU->RBAR = region->StartAddress;
        MPU->RASR = (region->Size << MPU_RASR_SIZE_Pos) |
                    (region->AccessRight << MPU_RASR_AP_Pos) |
                    (region->MemoryType == MPU_MEM_STRONG_ORDER ? (0x2 << MPU_RASR_TEX_Pos) :
                     region->MemoryType == MPU_MEM_NORMAL_CACHEABLE ? (0x1 << MPU_RASR_TEX_Pos) | MPU_RASR_C_Msk :
                     (0x1 << MPU_RASR_TEX_Pos)) |
                    (region->Enable ? MPU_RASR_ENABLE_Msk : 0);
    }

    // Cấu hình MPU Control
    uint32_t ctrl = 0;
    if (config->EnableDefaultMem) ctrl |= MPU_CTRL_PRIVDEFENA_Msk;
    if (config->EnableInException) ctrl |= MPU_CTRL_HFNMIENA_Msk;
    ctrl |= MPU_CTRL_ENABLE_Msk;
    MPU->CTRL = ctrl;

    // Kích hoạt MemManage Handler nếu cần
    if (config->EnableMemManage) {
        NVIC_EnableIRQ(MemoryManagement_IRQn);
    }

    // Đồng bộ hóa
    __DSB();
    __ISB();
}

void MPU_DeInit(void) {
    MPU->CTRL = 0; // Disable MPU và reset tất cả
    for (uint8_t i = 0; i < 8; i++) {
        MPU->RNR = i;
        MPU->RBAR = 0;
        MPU->RASR = 0;
    }
    __DSB();
    __ISB();
}

void MPU_SetRegionConfig(MPU_RegionConfig_t* region) {
    MPU->RNR  = region->RegionNumber;
    MPU->RBAR = region->StartAddress;
    MPU->RASR = (region->Size << MPU_RASR_SIZE_Pos) |
                (region->AccessRight << MPU_RASR_AP_Pos) |
                (region->MemoryType == MPU_MEM_STRONG_ORDER ? (0x2 << MPU_RASR_TEX_Pos) :
                 region->MemoryType == MPU_MEM_NORMAL_CACHEABLE ? (0x1 << MPU_RASR_TEX_Pos) | MPU_RASR_C_Msk :
                 (0x1 << MPU_RASR_TEX_Pos)) |
                (region->Enable ? MPU_RASR_ENABLE_Msk : 0);
    __DSB();
    __ISB();
}

void MPU_GetErrorDetails(uint32_t* faultAddr, uint8_t* faultStatus) {
    *faultAddr = SCB->MMFAR;   // Địa chỉ gây lỗi
    *faultStatus = SCB->CFSR & 0xFF; // Trạng thái lỗi (MMFSR)
}

/* main.c - Ví dụ sử dụng */
#include "mpu_driver.h"

void MemManage_Handler(void) {
    uint32_t faultAddr;
    uint8_t faultStatus;
    MPU_GetErrorDetails(&faultAddr, &faultStatus);
    while (1); // Debug lỗi
}

int main(void) {
    // Cấu hình 2 Region như bài trước
    MPU_RegionConfig_t regions[] = {
        {0, 0x00000000, 31, MPU_MEM_STRONG_ORDER, MPU_ACCESS_FULL, 1},      // Region 0: Background
        {1, 0x40000000, 9, MPU_MEM_NORMAL_NONCACHE, MPU_ACCESS_READ_ONLY, 1} // Region 1: Test
    };
    MPU_Config_t mpuConfig = {
        MPU_ENABLE_DEFAULT_MEM, MPU_ENABLE_IN_EXCEPTION, 1, regions, 2
    };

    // Khởi tạo MPU
    MPU_Init(&mpuConfig);

    // Test ghi vào Region 1 (Read Only)
    volatile uint32_t* test_addr = (uint32_t*)0x40000000;
    *test_addr = 0xDEADBEEF; // Gây MemFault

    while (1);
}
    

    Giải thích:

  • Macro: Định nghĩa các hằng số cho Memory Type và Access Right.
  • MPU_RegionConfig_t: Struct chứa thông tin Region.
  • MPU_Init: Khởi tạo toàn bộ MPU với nhiều Region.
  • MPU_DeInit: Reset MPU về mặc định.
  • MPU_SetRegionConfig: Cấu hình một Region cụ thể.
  • MPU_GetErrorDetails: Lấy thông tin lỗi từ SCB (MMFAR, MMFSR).
  • Ví dụ sử dụng tái hiện bài toán từ MPU Phần 3.

👉 Video thực hành

    Chi tiết các bạn xem Video thực hành bên dưới của mình:


Video 1: Thực hành triển khai MPU Driver

>>>>>> Follow ngay <<<<<<<

Để nhận được những bài học miễn phí mới nhất nhé 😊
Chúc các bạn học tập tốt 😊

Nguyễn Văn Nghĩa

Mình là một người thích học hỏi và chia sẻ các kiến thức về Nhúng IOT.

Đăng nhận xét

Mới hơn Cũ hơn
//