🌱 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.
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ụ triển khai 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ụ 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 😊