🌱 STM32 - 17. Lập trình Thanh ghi STM32 - Giao tiếp bộ nhớ Flash nội
Đối với các ứng dụng nhúng, việc quản lý tài nguyên, đặc biệt là bộ nhớ, là công việc rất quan trọng. Trong đó bộ nhớ FLASH là bộ nhớ chính của chương trình, và có rất nhiều vấn đề cần bàn luận xoay quanh nó. Ở bài viết này, mình sẽ giới thiệu với các bạn cách giao tiếp (Đọc/Ghi/Xóa) vào bộ nhớ Flash, sử dụng vi điều khiển STM32.
👉 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.
👉 Tổ chức vùng nhớ Flash STM32
Bộ nhớ Flash của STM32 có một số đặc điểm/Features chính, các bạn có thể tham khảo tài liệu Reference Manual, chapter Embedded Flash Memory Interface.
Đối với bài viết này mình sử dụng Vi điều khiển STM32F401 - Nucleo Board, vi điều khiển này có một số đặc điểm sau:
- Dung lượng 512 KBytes.
- Main Memory Block chia thành 4 Sector kích thước 16 KBytes, 1 Sector 64 KBytes, 3 Sector 128 KBytes.
- System Memory (30 KBytes) sử dụng cho Device Boots ở System Memory Boot Mode.
- 512 Bytes OTP (One-Time Programmable) + 16 Bytes mở rộng dùng để Lock vùng OTP.
- 16 Option Bytes để configure việc bảo vệ đọc/ghi, watchdog software/hardware, reset khi thiết bị ở mode Standby/Stop.
👉 Lock/Unlock Flash
Mặc định, thanh ghi Control của Flash sẽ bị Lock để tránh các truy cập bất thường, chẳng hạn một nhiễu/ghi nhầm cũng có thể làm thanh ghi này bị thay đổi không theo mong muốn của chương trình.
Đối với STM32, trạng thái Lock/Unlock của thanh ghi FLASH_CR được quy định bởi bit LOCK của thanh ghi FLASH_CR (LOCK = 1, thanh ghi FLASH_CR ở trạng thái Lock và ngược lại được Unlock khi bit LOCK = 0).
Nếu Flash ở trạng thái Lock (Bit LOCK = 1), chúng ta có thể thực hiện Unlock bằng cách sau:
- Ghi giá trị KEY1 = 0x45670123 vào thanh ghi FLASH_KEYR.
- Ghi giá trị KEY2 = 0xCDEF89AB vào thanh ghi FLASH_KEYR.
Sau khi thực hiện theo Sequence trên, bit LOCK của thanh ghi FLASH_CR sẽ tự động clear về 0.
👉 Sequence Erase - Xóa Flash
Đối với việc xóa, bộ nhớ Flash hỗ trợ xóa từng Sector, hoặc xóa toàn bộ các Sector (Mass Erase). Để thực hiện xóa một/toàn bộ các sector, chúng ta sẽ thực hiện theo sequence sau (Đảm bảo đã Unlock Flash):
- Đợi hoạt động trước đó hoàn thành, bằng cách check bit BSY của thanh ghi FLASH_SR.
- Set bit SER - Sector Erase, của thanh ghi FLASH_CR.
- Ghi giá trị Sector number vào trường SNB thanh ghi FLASH_CR.
- Set bit STRT của thanh ghi FLASH_CR.
- Đợi hành động thực hiện xong bằng bit BSY (Clear về 0), của thanh ghi FLASH_SR.
Đối với Mass Erase, sequence sẽ tương tự như trên, chỉ khác ở điểm: Thay vì set bit SER - Sector Erase ở bước 2, chúng ta sẽ set bit MER - Mass Erase của thanh ghi FLASH_CR. Và không cần chọn sector bằng trường SNB ở bước 3, bởi vì chúng ta sẽ xóa toàn bộ nội dung của tất cả các Sector.
👉 Sequence Write - Ghi vào Flash
Đối với việc ghi, Flash hỗ trợ việc ghi theo từng Word (4 bytes). Để thực hiện ghi, chúng ta sẽ tuân theo sequence sau đây (Đảm bảo đã Unlock Flash):
- Đợi hoạt động trước đó hoàn thành, bằng cách check bit BSY của thanh ghi FLASH_SR.
- Set bit PG - Program, của thanh ghi FLASH_CR.
- Thực hiện ghi dữ liệu (4 bytes) vào địa chỉ vùng nhớ muốn ghi.
- Đợi hành động thực hiện xong bằng bit BSY (Clear về 0), của thanh ghi FLASH_SR.
👉 Ví dụ lập trình Flash
Dưới đây là đoạn code bare-metal để xóa Sector 1 và ghi dữ liệu vào Flash trên STM32F401RE:
#include// Định nghĩa địa chỉ cơ bản #define RCC_BASE 0x40023800 #define FLASH_BASE 0x40023C00 // Định nghĩa thanh ghi RCC #define RCC_CR (*(volatile uint32_t *)(RCC_BASE + 0x00)) #define RCC_CFGR (*(volatile uint32_t *)(RCC_BASE + 0x08)) #define RCC_AHB1ENR (*(volatile uint32_t *)(RCC_BASE + 0x30)) // Định nghĩa thanh ghi FLASH #define FLASH_ACR (*(volatile uint32_t *)(FLASH_BASE + 0x00)) #define FLASH_KEYR (*(volatile uint32_t *)(FLASH_BASE + 0x04)) #define FLASH_OPTKEYR (*(volatile uint32_t *)(FLASH_BASE + 0x08)) #define FLASH_SR (*(volatile uint32_t *)(FLASH_BASE + 0x0C)) #define FLASH_CR (*(volatile uint32_t *)(FLASH_BASE + 0x10)) #define FLASH_OPTCR (*(volatile uint32_t *)(FLASH_BASE + 0x14)) // Định nghĩa bit trong RCC #define RCC_CR_HSION (1 << 0) #define RCC_CR_HSIRDY (1 << 1) #define RCC_CFGR_SW_HSI (0 << 0) #define RCC_AHB1ENR_FLASHEN (1 << 8) // Định nghĩa bit trong FLASH_ACR #define FLASH_ACR_LATENCY_2WS (2 << 0) // Định nghĩa bit trong FLASH_SR #define FLASH_SR_BSY (1 << 16) // Định nghĩa bit trong FLASH_CR #define FLASH_CR_LOCK (1 << 31) #define FLASH_CR_SER (1 << 1) #define FLASH_CR_MER (1 << 2) #define FLASH_CR_SNB(n) ((n) << 3) // Sector number (SNB[3:0]) #define FLASH_CR_PG (1 << 0) #define FLASH_CR_STRT (1 << 16) #define FLASH_CR_PSIZE_32BIT (2 << 8) // Program size: 32-bit // Định nghĩa khóa Unlock #define FLASH_KEY1 0x45670123 #define FLASH_KEY2 0xCDEF89AB // Định nghĩa địa chỉ Flash (Sector 1) #define FLASH_SECTOR1_BASE 0x08004000 // Sector 1 bắt đầu từ 0x08004000 // Hàm cấu hình clock void SystemClock_Config(void) { RCC_CR |= RCC_CR_HSION; while (!(RCC_CR & RCC_CR_HSIRDY)); RCC_CFGR &= ~(RCC_CFGR_SW_HSI); // HSI = 16 MHz } // Hàm Unlock Flash void FLASH_Unlock(void) { if (FLASH_CR & FLASH_CR_LOCK) { FLASH_KEYR = FLASH_KEY1; FLASH_KEYR = FLASH_KEY2; while (FLASH_CR & FLASH_CR_LOCK); // Chờ Unlock } } // Hàm Lock Flash void FLASH_Lock(void) { FLASH_CR |= FLASH_CR_LOCK; } // Hàm xóa Sector void FLASH_EraseSector(uint32_t sector) { while (FLASH_SR & FLASH_SR_BSY); // Đợi Flash sẵn sàng FLASH_CR |= FLASH_CR_SER; // Bật Sector Erase FLASH_CR &= ~(0xF << 3); // Xóa trường SNB FLASH_CR |= FLASH_CR_SNB(sector); // Chọn Sector FLASH_CR |= FLASH_CR_STRT; // Bắt đầu xóa while (FLASH_SR & FLASH_SR_BSY); // Đợi xóa xong FLASH_CR &= ~FLASH_CR_SER; // Tắt Sector Erase } // Hàm ghi Word (4 bytes) vào Flash void FLASH_WriteWord(uint32_t address, uint32_t data) { while (FLASH_SR & FLASH_SR_BSY); // Đợi Flash sẵn sàng FLASH_CR |= FLASH_CR_PG; // Bật chế độ ghi FLASH_CR |= FLASH_CR_PSIZE_32BIT; // Ghi 32-bit *(volatile uint32_t *)address = data; // Ghi dữ liệu while (FLASH_SR & FLASH_SR_BSY); // Đợi ghi xong FLASH_CR &= ~FLASH_CR_PG; // Tắt chế độ ghi } int main(void) { SystemClock_Config(); // Bật clock cho Flash interface RCC_AHB1ENR |= RCC_AHB1ENR_FLASHEN; // Cấu hình latency cho Flash (2 wait states với HSI 16 MHz) FLASH_ACR |= FLASH_ACR_LATENCY_2WS; // Unlock Flash FLASH_Unlock(); // Xóa Sector 1 FLASH_EraseSector(1); // Ghi dữ liệu thử vào Sector 1 uint32_t testData = 0xDEADBEEF; FLASH_WriteWord(FLASH_SECTOR1_BASE, testData); // Lock Flash lại FLASH_Lock(); while (1) { // Đọc dữ liệu từ Flash để kiểm tra (không cần hàm đặc biệt) uint32_t readData = *(volatile uint32_t *)FLASH_SECTOR1_BASE; (void)readData; // Biến tránh cảnh báo unused } }
👉 Mình để lại Video sau để các bạn tham khảo nhé!
Video: Hướng dẫn giao tiếp bộ nhớ Flash trên STM32
>>>>>> 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 😊