Trong các dự án sử dụng vi điều khiển STM32F0, đặc biệt khi triển khai tính năng nâng cấp firmware tại chỗ (IAP) bao gồm cả bootloader và ứng dụng người dùng riêng biệt, việc tái định vị bảng vector ngắt cho ứng dụng là một bước cần thiết. Tuy nhiên, phương pháp thực hiện trên các chip Cortex-M0 như STM32F0 có sự khác biệt so với các dòng STM32F10x dựa trên Cortex-M3/M4.
Điểm khác biệt chính là bộ xử lý Cortex-M0 không tích hợp thanh ghi SCB->VTOR (Vector Table Offset Register) cho phép cấu hình trực tiếp địa chỉ bảng vector ngắt. Vì lý do này, các thư viện firmware của ST cho STM32F0 không cung cấp một hàm tiện ích như NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset) thường thấy trên các dòng Cortex-M3/M4.
Để giải quyết hạn chế này, một cơ chế tái lập bản đồ vật lý bằng phần mềm là bắt buộc. Theo tài liệu tham khảo của STM32F0 (ví dụ, Reference Manual RM0091), quy trình tái định vị bảng vector ngắt cho ứng dụng người dùng, khi ứng dụng không bắt đầu từ địa chỉ Flash mặc định 0x0800 0000, bao gồm các bước sau:
- Sao chép bảng vector: Bảng vector ngắt của ứng dụng người dùng, vốn được đặt trong bộ nhớ Flash (tại địa chỉ bắt đầu của ứng dụng), phải được sao chép vào địa chỉ gốc của bộ nhớ SRAM (thường là 0x2000 0000).
- Tái lập bản đồ SRAM: Sau đó, SRAM cần được tái lập bản đồ về địa chỉ 0x0000 0000. Thao tác này được thực hiện thông qua việc lập trình các bit MEM_MODE trong thanh ghi cấu hình hệ thống SYSCFG_CFGR1.
- Xử lý ngắt: Khi một ngắt xảy ra, bộ xử lý Cortex-M0 sẽ tìm kiếm địa chỉ của bộ xử lý ngắt từ bảng vector đã được đặt trong SRAM (tại địa chỉ 0x0000 0000 sau khi remap), và sau đó nhảy đến thực thi bộ xử lý ngắt đó, vốn vẫn nằm trong bộ nhớ Flash.
Quy trình này nên được thực hiện trong giai đoạn khởi tạo ban đầu của ứng dụng người dùng.
Đoạn mã C sau đây minh họa cách thực hiện các bước trên. Giả định ứng dụng của bạn được tải vào địa chỉ 0x08004000 và kích thước bảng vector ngắt được xác định là KICH_THUOC_BANG_VECTOR byte:
#include <string.h> // Cần thiết cho hàm memcpy
#include "stm32f0xx.h" // Thư viện cấu hình STM32F0
// Định nghĩa các địa chỉ bộ nhớ quan trọng
#define DIA_CHI_SRAM_CO_SO ((uint32_t)0x20000000)
#define DIA_CHI_FLASH_APP ((uint32_t)0x08004000)
// Kích thước bảng vector ngắt (ví dụ: 48 vector * 4 byte/vector = 192 byte)
// Giá trị này phải được tính toán chính xác từ tệp startup.s của dự án.
#define KICH_THUOC_BANG_VECTOR ((uint32_t)0xC0) // Ví dụ cho 48 mục vector
/**
* @brief Cấu hình lại bảng vector ngắt cho ứng dụng trên STM32F0.
* Sao chép bảng vector từ Flash sang SRAM và tái lập bản đồ SRAM về 0x0.
*/
void KhoiTaoVectorNgatUngDung(void) {
// 1. Sao chép bảng vector ngắt của ứng dụng từ Flash vào SRAM.
// Nguồn: DIA_CHI_FLASH_APP, Đích: DIA_CHI_SRAM_CO_SO, Kích thước: KICH_THUOC_BANG_VECTOR
memcpy((void *)DIA_CHI_SRAM_CO_SO, (const void *)DIA_CHI_FLASH_APP, KICH_THUOC_BANG_VECTOR);
// 2. Tái lập bản đồ SRAM về địa chỉ 0x0000 0000.
// Sử dụng hàm thư viện để cấu hình SYSCFG_CFGR1.
// Hoặc truy cập trực tiếp thanh ghi: SYSCFG->CFGR1 |= SYSCFG_CFGR1_MEM_MODE_SRAM;
SYSCFG_MemoryRemapConfig(SYSCFG_MemoryRemap_SRAM);
}
Trong ví dụ trên:
DIA_CHI_SRAM_CO_SO(0x2000 0000) là địa chỉ bắt đầu của bộ nhớ SRAM, một địa chỉ cố định trên STM32F0.DIA_CHI_FLASH_APP(0x08004000) là địa chỉ khởi đầu của ứng dụng người dùng trong bộ nhớ Flash. Bảng vector ngắt của ứng dụng được đặt tại địa chỉ này. Địa chỉ này cần được điều chỉnh theo file linker script hoặc cấu hình dự án của bạn.KICH_THUOC_BANG_VECTORlà tổng kích thước tính bằng byte của bảng vector ngắt.
Xác định Kích thước Bảng Vector Ngắt
Để xác định giá trị chính xác cho KICH_THUOC_BANG_VECTOR, bạn cần tham khảo tệp tin khởi động (startup file) của dự án, thường có tên dạng startup_stm32f0xx.s. Trong tệp này, bảng vector ngắt được định nghĩa bằng các lệnh DCD (Define Constant Data).
Ví dụ một phần của tệp startup_stm32f030.s:
; Bảng Vector được ánh xạ tới địa chỉ 0 khi Reset
AREA RESET, DATA, READONLY
EXPORT __Vectors
EXPORT __Vectors_End
EXPORT __Vectors_Size
__Vectors DCD __initial_sp ; Con trỏ Stack ban đầu
DCD Reset_Handler ; Bộ xử lý Reset
DCD NMI_Handler ; Bộ xử lý NMI
DCD HardFault_Handler ; Bộ xử lý Hard Fault
DCD 0 ; Dành riêng
; ... (các mục DCD khác cho ngắt hệ thống)
DCD SVC_Handler ; Bộ xử lý SVCall
; ... (các mục DCD khác cho ngắt hệ thống)
DCD PendSV_Handler ; Bộ xử lý PendSV
DCD SysTick_Handler ; Bộ xử lý SysTick
; Các ngắt ngoại vi
DCD WWDG_IRQHandler ; Watchdog cửa sổ
; ... (các mục DCD khác cho các ngắt ngoại vi)
DCD USART2_IRQHandler ; UART2
__Vectors_End
__Vectors_Size EQU __Vectors_End - __Vectors
Trong đoạn mã assembly trên, mỗi lệnh DCD định nghĩa một từ 32-bit (4 byte) trong bảng vector, thường là địa chỉ của một bộ xử lý ngắt (ISR) hoặc con trỏ ngăn xếp ban đầu. Để tính KICH_THUOC_BANG_VECTOR, bạn chỉ cần đếm tổng số mục DCD giữa nhãn __Vectors và __Vectors_End, sau đó nhân số lượng này với 4 (vì mỗi mục là 4 byte).
Ví dụ, nếu bảng vector chứa 48 mục DCD, thì KICH_THUOC_BANG_VECTOR sẽ là 48 * 4 = 192 byte (tương đương 0xC0 hex).
Đảm bảo an toàn cho vùng nhớ SRAM
Sau khi bảng vector ngắt được sao chép vào SRAM và SRAM được tái lập bản đồ về địa chỉ 0x0, vùng nhớ SRAM từ 0x2000 0000 đến 0x2000 0000 + KICH_THUOC_BANG_VECTOR trở nên cực kỳ quan trọng và không được phép bị ghi đè bởi các dữ liệu khác. Để đảm bảo tính toàn vẹn của vùng nhớ này, bạn có thể áp dụng các phương pháp sau:
- Cấu hình môi trường phát triển (IDE): Điều chỉnh cài đặt vùng nhớ SRAM trong IDE của bạn (ví dụ, Keil MDK, STM32CubeIDE) để dành riêng một phân đoạn của SRAM cho bảng vector, ngăn chặn trình liên kết (linker) sử dụng vùng này cho heap, stack, hoặc các biến dữ liệu khác.
- Sử dụng tệp linker script (Scatter File): Nếu dự án của bạn sử dụng tệp linker script tùy chỉnh, hãy sửa đổi nó để định nghĩa rõ ràng một vùng nhớ SRAM cụ thể cho bảng vector, đảm bảo rằng vùng này được bảo vệ và không bị các phân đoạn bộ nhớ khác chồng lấn.
Thực hiện đúng các bước trên sẽ giúp bạn thành công tái lập bản đồ bảng vector ngắt trên vi điều khiển STM32F0, đảm bảo hệ thống xử lý các ngắt một cách chính xác ngay cả khi ứng dụng được đặt tại một địa chỉ Flash không phải là mặc định.