Trong quá trình cấu hình FLASH STM32F103, đặc biệt với các hệ thống chạy ở tần số cao như 72 MHz, việc thiết lập đúng các thông số Flash là cực kỳ quan trọng. Bài viết này sẽ hướng dẫn anh em chi tiết cách điều chỉnh thanh ghi FLASH_ACR để tối ưu hiệu suất truy cập bộ nhớ, từ đó tránh được các lỗi đọc Flash tiềm ẩn và đảm bảo hệ thống hoạt động ổn định.
1. Giới thiệu ngoại vi & Tại sao cần cấu hình?
Giới thiệu khối FLASH trong STM32F103
Bộ nhớ Flash trên STM32F103RCT6 dùng để lưu trữ:
- Code chương trình (firmware)
- Hằng số (const data)
- Và có thể lưu dữ liệu cấu hình (EEPROM giả lập)
CPU lấy lệnh trực tiếp từ Flash, do đó khi tần số hệ thống cao (ví dụ 72 MHz), Flash cần được cấu hình phù hợp để tránh lỗi khi đọc lệnh.
Việc truy cập Flash có thể yêu cầu thời gian chờ (wait state), phụ thuộc vào tần số xung nhịp hệ thống (SYSCLK), nhằm đảm bảo dữ liệu được đọc chính xác. Đây là phần rất quan trọng trong lập trình STM32 bằng thanh ghi.
Thanh ghi FLASH_ACR (Flash Access Control Register)
Thanh ghi FLASH_ACR (Flash Access Control Register) trong vi điều khiển STM32F103RCT6 được sử dụng để cấu hình các tham số liên quan đến truy cập bộ nhớ Flash, chẳng hạn như:
- Thời gian chờ (latency)
- Bật/tắt bộ đệm prefetch
- Các tính năng liên quan đến hiệu suất truy cập Flash

Thanh ghi FLASH_ACR nằm trong khối FLASH Interface của STM32F103, cho phép cấu hình các tham số:
- LATENCY: Số chu kỳ chờ (wait states) khi CPU truy cập Flash
- PRFTBE: Bật/tắt bộ đệm prefetch (Prefetch Buffer)
- PRFTBS: Trạng thái bộ đệm prefetch (chỉ đọc)
- Một số bit khác liên quan đến chế độ Half-cycle access (không được sử dụng trong tất cả các dòng STM32F103)
2. Define Struct thanh ghi kiểu Bit-field
Để truy cập các bit cụ thể trong thanh ghi Flash, chúng ta sẽ định nghĩa cấu trúc bit-field dựa trên tài liệu RM0008.
Flash access control register
Dựa trên RM0008, Section 3.3.3, Trang 60:

// Trích xuất từ tài liệu RM0008, Section 3.3.3, Trang 60
// Thanh ghi Điều khiển Truy cập Flash (Flash access control register - FLASH_ACR)
typedef union {
struct { // Cấu trúc cho các trường bit
__IOM uint32_t LATENCY : 3; // Bit 0-2: Độ trễ truy cập Flash
__IOM uint32_t HLFCYA : 1; // Bit 3: Bật truy cập chu kỳ bán phần của Flash
__IOM uint32_t PRFTBE : 1; // Bit 4: Bật bộ đệm Prefetch
__IM uint32_t PRFTBS : 1; // Bit 5: Trạng thái bộ đệm Prefetch
uint32_t reserved0 : 26; // Bit 6-31: Reserved, phải giữ giá trị reset
};
__IOM uint32_t reg; // Truy cập toàn bộ thanh ghi 32-bit
} FLASH_ACR_Type;
// Trích xuất từ tài liệu RM0008, Section 3.3, Trang 56
// Khối thanh ghi giao diện Flash (Flash memory interface registers)
typedef struct
{
FLASH_ACR_Type ACR; // Offset 0x00: Thanh ghi điều khiển truy cập Flash
__IO uint32_t KEYR; // Offset 0x04: Thanh ghi khóa Flash
__IO uint32_t OPTKEYR; // Offset 0x08: Thanh ghi khóa Option byte
__IO uint32_t SR; // Offset 0x0C: Thanh ghi trạng thái Flash
__IO uint32_t CR; // Offset 0x10: Thanh ghi điều khiển Flash
__IO uint32_t AR; // Offset 0x14: Thanh ghi địa chỉ Flash
uint32_t RESERVED; // Offset 0x18: Reserved (4 bytes)
__IO uint32_t OBR; // Offset 0x1C: Thanh ghi Option byte đọc
__IO uint32_t WRPR; // Offset 0x20: Thanh ghi bảo vệ ghi
} FLASH_TypeDef;
#define FLASH ((FLASH_TypeDef *)0x40022000UL)
3. Giải thích chức năng từng thanh ghi liên quan
Dựa theo dữ liệu tại RM0008, Section 3.3.3 (Trang 60) và Section 3.3 (Trang 56):
- FLASH_ACR (Flash Access Control Register): Điều khiển thời gian truy cập bộ nhớ Flash.
- LATENCY (Bits 2:0): Số chu kỳ chờ. Cực kỳ quan trọng để CPU đọc đúng dữ liệu Flash khi tần số hệ thống (SYSCLK) cao.
000: Zero wait state, nếu 0 < SYSCLK ≤ 24 MHz001: One wait state, nếu 24 MHz < SYSCLK ≤ 48 MHz010: Two wait states, nếu 48 MHz < SYSCLK ≤ 72 MHz
- HLFCYA (Bit 3): Bật/tắt truy cập chu kỳ bán phần. Thường được tắt để tối ưu hiệu suất, chỉ bật khi cần tiết kiệm năng lượng ở chế độ đọc đặc biệt.
- PRFTBE (Bit 4): Bật/tắt bộ đệm Prefetch. Khi bật, bộ đệm này sẽ đọc trước các lệnh tiếp theo, giúp CPU không phải chờ đợi Flash, tăng tốc độ thực thi chương trình.
- PRFTBS (Bit 5): Trạng thái của bộ đệm Prefetch. Cho biết bộ đệm có đang hoạt động hay không.
- LATENCY (Bits 2:0): Số chu kỳ chờ. Cực kỳ quan trọng để CPU đọc đúng dữ liệu Flash khi tần số hệ thống (SYSCLK) cao.
- FLASH_KEYR (Flash Key Register): Dùng để mở khóa bộ nhớ Flash cho các hoạt động ghi/xóa.
- FLASH_OPTKEYR (Option Byte Key Register): Dùng để mở khóa các option byte (cấu hình đặc biệt của chip).
- FLASH_SR (Flash Status Register): Chứa các cờ trạng thái hoạt động của Flash (lỗi ghi, lỗi xóa, bận, v.v.).
- FLASH_CR (Flash Control Register): Chứa các bit điều khiển cho các hoạt động ghi, xóa trang, xóa khối, và các chế độ lập trình Flash khác.
- FLASH_AR (Flash Address Register): Chứa địa chỉ đích cho các hoạt động ghi hoặc xóa.
- FLASH_OBR (Option Byte Register): Chứa các giá trị của option byte đã được cấu hình.
- FLASH_WRPR (Write Protection Register): Dùng để cấu hình bảo vệ ghi cho các vùng Flash cụ thể.
4. Quy trình cấu hình FLASH
Việc cấu hình Flash_ACR cần được thực hiện cẩn thận, đặc biệt trước khi chuyển System Clock (SYSCLK) lên tần số cao. Dựa theo RM0008, Section 3.3.3 (Trang 60) và các khuyến nghị về System Clock:
graph TD; A["Bắt đầu"] --> B["Xác định tần số SYSCLK mong muốn"]; B --> C["Tính toán LATENCY cần thiết"]; C --> D["Bật Prefetch Buffer (PRFTBE)"]; D --> E["Thiết lập LATENCY vào FLASH_ACR"]; E --> F["Hoàn thành cấu hình FLASH"]; F --> G["Tiếp tục cấu hình System Clock"];
- Xác định tần số SYSCLK: Chương trình của chúng ta sẽ đưa SYSCLK lên 72 MHz (ví dụ: HSE 8MHz + PLLx9).
- Xác định LATENCY: Với SYSCLK = 72 MHz, theo bảng trong RM0008, chúng ta cần
2wait states (LATENCY =010b).
FLASH->ACR.LATENCY = 2; // Flash 2 wait state- Bật Prefetch Buffer (PRFTBE): Luôn bật Prefetch Buffer để tối ưu hiệu suất truy cập Flash.
FLASH->ACR.PRFTBE = 1; // Enable Prefetch Buffer- Thực hiện cấu hình: Thiết lập các bit PRFTBE và LATENCY vào thanh ghi FLASH_ACR.
- Thời điểm cấu hình: Nên cấu hình FLASH_ACR trước khi chuyển SYSCLK sang PLL để đảm bảo tính ổn định ngay từ đầu.
5. Code thực tế chạy trên Keil C
Dưới đây là đoạn code mẫu tích hợp cấu hình FLASH vào hàm SystemClock_Config, đảm bảo các thiết lập được thực hiện theo đúng trình tự.
Code cấu hình FLASH và System Clock
void SystemClock_Config(void) {
// Bật HSE (High Speed External) oscillator
// Tham khảo RM0008, Section 7.3.1, Trang 99
RCC->CR.HSEON = 1;
// Đợi cho HSE ổn định và sẵn sàng
while (RCC->CR.HSERDY == 0);
// Cấu hình FLASH_ACR trước khi chuyển SYSCLK sang tần số cao
// Bật Prefetch Buffer để tăng hiệu suất
FLASH->ACR.PRFTBE = 1;
// Thiết lập 2 chu kỳ chờ (wait states) cho Flash khi SYSCLK = 72 MHz
FLASH->ACR.LATENCY = 2;
// Cấu hình PLL: Nguồn HSE, hệ số nhân x9
// Tham khảo RM0008, Section 7.3.2, Trang 101
RCC->CFGR.PLLSRC = 1; // Chọn HSE làm nguồn clock cho PLL
RCC->CFGR.PLLMUL = 7; // Hệ số nhân PLL x9 (0111b tương ứng x9)
RCC->CR.PLLON = 1; // Bật PLL
// Đợi cho PLL ổn định và sẵn sàng
while (RCC->CR.PLLRDY == 0);
// Đặt các bộ chia tần số (prescaler): HCLK=/1, PCLK1=/2, PCLK2=/1
// Tham khảo RM0008, Section 7.3.2, Trang 101-102
RCC->CFGR.HPRE = 0; // HCLK = SYSCLK (Không chia)
RCC->CFGR.PPRE1 = 4; // PCLK1 = HCLK/2 (Chia 2 cho APB1, max 36MHz)
RCC->CFGR.PPRE2 = 0; // PCLK2 = HCLK (Không chia cho APB2, max 72MHz)
// Chọn PLL làm System Clock (SYSCLK)
// Tham khảo RM0008, Section 7.3.2, Trang 101
RCC->CFGR.SW = 2; // Chọn PLL làm SYSCLK (10b)
// Đợi cho SYSCLK chuyển sang PLL
while (RCC->CFGR.SWS != 2);
}
int main(void) {
// Gọi hàm cấu hình hệ thống clock
SystemClock_Config();
// Các cấu hình ngoại vi khác và vòng lặp chính của ứng dụng
while (1) {
// Application code goes here
}
}
Link Github: Download code cấu hình FLASH STM32F103RCT6
6. Cách kiểm tra cấu hình (Debugging)
Để kiểm tra xem cấu hình Flash của bạn đã chính xác chưa, anh em có thể sử dụng tính năng debug của Keil uVision:
- Mở Keil uVision
- Vào menu Peripherals → System Viewer → FLASH

- Kiểm tra giá trị của các bit:
- LATENCY (mong muốn là
010b hoặc2) - PRFTBE (mong muốn là
1)
- LATENCY (mong muốn là
Đảm bảo các giá trị trong FLASH->ACR đúng với cấu hình đã thiết lập.

7. Kết luận
Trong quá trình lập trình STM32 bằng thanh ghi, việc cấu hình FLASH là bước bắt buộc khi hệ thống hoạt động ở tần số cao, đặc biệt với STM32F103RCT6 chạy 72 MHz. Thanh ghi FLASH_ACR cho phép cấu hình số chu kỳ chờ (LATENCY) và bật Prefetch Buffer, giúp đảm bảo việc truy cập Flash diễn ra chính xác và ổn định.
Nếu LATENCY không được thiết lập đúng, CPU có thể đọc sai lệnh từ Flash, dẫn đến lỗi chương trình hoặc treo vi điều khiển. Ngược lại, việc bật Prefetch Buffer giúp cải thiện hiệu suất truy cập Flash, giảm thời gian chờ khi thực thi lệnh.
Vì vậy, khi cấu hình system clock bằng HSE + PLL, cần luôn cấu hình FLASH_ACR trước khi chuyển SYSCLK sang PLL. Đây là nguyên tắc quan trọng trong lập trình STM32F103 thanh ghi, giúp hệ thống hoạt động ổn định và đúng theo thiết kế phần cứng.
