🌱 Triển Khai Driver Ngoại vi MPU trên STM32F4 (Phần 2)
Ở Bài trước mình đã giới thiệu với các bạn về lập trình cấu hình MPU sử dụng MPU Driver và cấu trúc Driver MPU. Bài viết này mình sẽ tiếp tục giới thiệu về các hàm còn lại MPU Driver.
Mục lục
- Phần cứng sử dụng
- Phần mềm sử dụng
- Hướng dẫn lập trình MPU Register
- Kế hoạch xây dựng MPU Driver
- Ví dụ hoàn thiện MPU Driver
- Video thực hành
👉 Phần cứng sử dụng
STM32F401RE - 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
👉 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!
- 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ố:- Enable/Disable bit Default Memory Map.
- Enable/Disable MPU in Exception.
- Enable/Disable MemManage Handler.
- Data Config bao gồm: Region Number, Start Address, Region Size, Memory Type, Access Right, ...
- Viết hàm MPU_Init ⇒ Sử dụng để khởi tạo MPU với bộ Data Config.
- Viết hàm MPU_DeInit ⇒ Reset tất cả cấu hình MPU về trạng thái mặc định.
- 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.
- Viết hàm MPU_GetErrorDetails ⇒ Get lỗi khi chương trình nhảy vào Fault.
👉 Ví dụ hoàn thiện MPU Driver
Dưới đây là code hoàn thiện MPU Driver, tập trung vào các hàm còn lại từ phần 1 (`MPU_SetRegionConfig`, `MPU_GetErrorDetails`), dựa trên STM32F401RE:
/* 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
// Fault Status (MMFSR bits)
#define MPU_FAULT_IACCVIOL (1 << 0) // Instruction Access Violation
#define MPU_FAULT_DACCVIOL (1 << 1) // Data Access Violation
#define MPU_FAULT_MUNSTKERR (1 << 3) // Unstacking Error
#define MPU_FAULT_MSTKERR (1 << 4) // Stacking Error
#define MPU_FAULT_MLSPERR (1 << 5) // Lazy State Preservation Error
#define MPU_FAULT_MMARVALID (1 << 7) // MMFAR Valid
// 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) {
MPU->CTRL &= ~MPU_CTRL_ENABLE_Msk;
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);
}
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;
if (config->EnableMemManage) NVIC_EnableIRQ(MemoryManagement_IRQn);
__DSB();
__ISB();
}
void MPU_DeInit(void) {
MPU->CTRL = 0;
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) {
// Kiểm tra Region Number hợp lệ (0-7)
if (region->RegionNumber > 7) return;
// Disable MPU trước khi cấu hình để tránh xung đột
uint32_t ctrl = MPU->CTRL;
MPU->CTRL &= ~MPU_CTRL_ENABLE_Msk;
// Cấu hình 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);
// Khôi phục trạng thái MPU
MPU->CTRL = ctrl;
__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)
// Xóa cờ lỗi sau khi đọc
SCB->CFSR |= SCB->CFSR & 0xFF;
}
/* 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);
if (faultStatus & MPU_FAULT_DACCVIOL) {
// Xử lý lỗi Data Access Violation
}
while (1); // Debug lỗi
}
int main(void) {
// Cấu hình 2 Region ban đầu
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 lỗi: Ghi vào Region 1 (Read Only)
volatile uint32_t* test_addr = (uint32_t*)0x40000000;
*test_addr = 0xDEADBEEF; // Gây MemFault
// Thay đổi Region 1 thành Full Access sau khi Init
MPU_RegionConfig_t newRegion = {1, 0x40000000, 9, MPU_MEM_NORMAL_NONCACHE, MPU_ACCESS_FULL, 1};
MPU_SetRegionConfig(&newRegion);
// Ghi lại, lần này không gây lỗi
*test_addr = 0x12345678;
while (1);
}
Giải thích:
- MPU_SetRegionConfig: Cập nhật một Region sau khi Init, kiểm tra Region Number và disable MPU tạm thời để tránh xung đột.
- MPU_GetErrorDetails: Lấy địa chỉ lỗi (MMFAR) và trạng thái lỗi (MMFSR), hỗ trợ phân tích nguyên nhân (Data Access Violation, Instruction Violation, ...).
- Ví dụ: Test lỗi với Region 1 (Read Only), sau đó thay đổi thành Full Access để kiểm tra tính linh hoạt của driver.
👉 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 2: Thực hành hoàn thiện 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 😊