Lập trình STM32 thanh ghi cấu hình clock STM32F103RCT6

Trong quá trình cấu hình clock STM32F103, việc nắm vững cách làm việc với các thanh ghi là yếu tố then chốt để làm chủ vi điều khiển ở mức bare-metal. Bài viết này sẽ hướng dẫn anh em kỹ sư cách cấu hình bộ điều khiển clock và reset (RCC), Flash để đưa STM32F103 chạy ở 72MHz, đồng thời giải thích bản chất ‘clock tree’ và cách kiểm tra thanh ghi trực tiếp trong Keil.

1. Giới thiệu ngoại vi & Tại sao cần cấu hình?

Trong quá trình lập trình STM32 thanh ghi, việc cấu hình clock STM32F103 là bước nền tảng và bắt buộc nếu bạn muốn làm chủ vi điều khiển ở mức bare-metal (không HAL, không CubeMX). Bài viết này hướng dẫn chi tiết cách cấu hình RCC, HSE, PLL để đưa STM32F103 chạy ở 72MHz, đồng thời giải thích cách tính toán xung clock và kiểm tra thanh ghi trực tiếp trong Keil.

Mục tiêu bài lập trình STM32 thanh ghi cấu hình clock

  • Hiểu rõ clock tree STM32F103
  • Tự cấu hình HSE 8MHz + PLL x9 = 72MHz bằng thanh ghi
  • Cấu hình đúng AHB, APB1, APB2 theo RM0008
  • Biết cách kiểm tra RCC register trong Keil uVision

Kiến Trúc Tổng Quát Của STM32F103 Cần Nắm Vững

Trước khi đi sâu vào code, hãy nắm rõ cấu trúc của STM32F103 – dòng MCU phổ biến với lõi ARM Cortex-M3, flash 64-512KB và tốc độ clock lên đến 72MHz.

  • Lõi xử lý ARM Cortex-M3: Chịu trách nhiệm thực thi lệnh, hỗ trợ interrupt qua NVIC (Nested Vectored Interrupt Controller) và SysTick timer cho delay chính xác.
  • Hệ thống bus: Bao gồm AHB (Advanced High-performance Bus) cho tốc độ cao và APB1/APB2 (Advanced Peripheral Bus) cho ngoại vi chậm hơn. Điều này ảnh hưởng đến việc cấu hình clock qua RCC (Reset and Clock Control).

Các ngoại vi chính

  • GPIO: Điều khiển input/output, hỗ trợ Push-Pull, Open-Drain.
  • Timer: General-purpose timer cho PWM, capture, delay.
  • USART/UART: Giao tiếp serial cho truyền nhận dữ liệu.
  • ADC: Chuyển đổi analog-to-digital cho cảm biến.
  • FLASH: Lưu trữ chương trình và dữ liệu non-volatile.
  • Các ngoại vi khác: SPI, I2C, CAN (tùy model).

Hiểu kiến trúc này giúp bạn tránh lỗi phổ biến như quên bật clock cho ngoại vi, dẫn đến chương trình không chạy.

2. Define Struct thanh ghi kiểu Bit-field

Để truy cập các bit cụ thể trong thanh ghi một cách an toàn và tường minh, chúng ta sẽ định nghĩa các cấu trúc kiểu Bit-field. Chuẩn này giúp code dễ đọc, dễ bảo trì và giảm thiểu lỗi.

Toàn bộ định nghĩa struct dưới đây được trích xuất và kiểm chứng từ tài liệu RM0008: STM32F103xx Reference Manual.

Thanh ghi RCC (Reset and Clock Control)

Thanh ghi điều khiển clock (RCC_CR).

Chú thích: Sơ đồ chức năng thanh ghi RCC_CR, trích xuất từ RM0008.

Define struct RCC_CR cho lập trình stm32 thanh ghi Cấu hình Clock stm32f103rct6

Chú thích: Định nghĩa cấu trúc RCC_CR trong lập trình thanh ghi STM32F103.

// Trích xuất từ tài liệu RM0008, Section 7.3.1, Trang 99
// Thanh ghi Điều khiển Clock (RCC_CR)
typedef union {
    struct { // Structure for bit fields
        __IOM uint32_t HSION : 1; // Bit 0: Internal high-speed clock enable
        __IM  uint32_t HSIRDY : 1; // Bit 1: Internal high-speed clock ready
        uint32_t reserved1 : 1; // Bit 2: Reserved
        __IOM uint32_t HSITRIM : 5; // Bit 3-7: HSI trimming
        __IM  uint32_t HSICAL : 8; // Bit 8-15: HSI calibration value
        __IOM uint32_t HSEON : 1; // Bit 16: External high-speed clock enable
        __IM  uint32_t HSERDY : 1; // Bit 17: External high-speed clock ready
        __IOM uint32_t HSEBYP : 1; // Bit 18: HSE bypass
        __IOM uint32_t CSSON : 1; // Bit 19: Clock security system enable
        uint32_t reserved2 : 4; // Bit 20-23: Reserved
        __IOM uint32_t PLLON : 1; // Bit 24: PLL enable
        __IM  uint32_t PLLRDY : 1; // Bit 25: PLL ready
        uint32_t reserved3 : 6; // Bit 26-31: Reserved
    };
    __IOM uint32_t reg; // Access entire register
} RCC_CR_Type;

Thanh ghi Clock Configuration Register (RCC_CFGR)

Sơ đồ thanh ghi cấu hình Clock Configuration Register (RCC_CFGR) STM32F103

Chú thích: Sơ đồ thanh ghi cấu hình Clock Configuration Register (RCC_CFGR) từ RM0008.

// Trích xuất từ tài liệu RM0008, Section 7.3.2, Trang 101
// Thanh ghi Cấu hình Clock (RCC_CFGR)
typedef union {
    struct { // Structure for bit fields
        __IOM uint32_t SW : 2; // Bit 0-1: System clock switch
        __IM  uint32_t SWS : 2; // Bit 2-3: System clock switch status
        __IOM uint32_t HPRE : 4; // Bit 4-7: AHB prescaler
        __IOM uint32_t PPRE1 : 3; // Bit 8-10: APB1 prescaler
        __IOM uint32_t PPRE2 : 3; // Bit 11-13: APB2 prescaler
        __IOM uint32_t ADCPRE : 2; // Bit 14-15: ADC prescaler
        __IOM uint32_t PLLSRC : 1; // Bit 16: PLL clock source
        __IOM uint32_t PLLXTPRE : 1; // Bit 17: HSE divider for PLL entry
        __IOM uint32_t PLLMUL : 4; // Bit 18-21: PLL multiplication factor
        __IOM uint32_t USBPRE : 1; // Bit 22: USB prescaler
        uint32_t reserved1 : 1; // Bit 23: Reserved
        __IOM uint32_t MCO : 3; // Bit 24-26: Microcontroller clock output
        uint32_t reserved2 : 5; // Bit 27-31: Reserved
    };
    __IOM uint32_t reg; // Access entire register
} RCC_CFGR_Type;

Thanh ghi Bật Clock Ngoại vi AHB (RCC_AHBENR)

// Trích xuất từ tài liệu RM0008, Section 7.3.6, Trang 111
// Thanh ghi Bật Clock Ngoại vi AHB (RCC_AHBENR)
typedef union {
    struct {
        __IOM uint32_t DMA1EN : 1; // Bit 0: DMA1 clock enable
        __IOM uint32_t DMA2EN : 1; // Bit 1: DMA2 clock enable
        __IOM uint32_t SRAMEN : 1; // Bit 2: SRAM interface clock enable
        uint32_t reserved1 : 1;    // Bit 3: Reserved
        __IOM uint32_t FLITFEN : 1; // Bit 4: FLITF clock enable
        uint32_t reserved2 : 1;    // Bit 5: Reserved
        __IOM uint32_t CRCEN : 1;  // Bit 6: CRC clock enable
        uint32_t reserved3 : 7;    // Bit 7: Reserved
        __IOM uint32_t FSMCEN : 1; // Bit 8: FSMC clock enable
        uint32_t reserved4 : 4;    // Bit 9: Reserved
        __IOM uint32_t SDIOEN : 1; // Bit 10: SDIO clock enable
        uint32_t reserved5 : 21;   // Bit 11-31: Reserved
    };
    __IOM uint32_t reg; // Access entire register
} RCC_AHBENR_Type;

Thanh ghi Bật Clock Ngoại vi APB2 (RCC_APB2ENR)

// Trích xuất từ tài liệu RM0008, Section 7.3.7, Trang 112
// Thanh ghi Bật Clock Ngoại vi APB2 (RCC_APB2ENR)
typedef union {
    struct {
        __IOM uint32_t AFIOEN : 1; // Bit 0: Alternate function I/O clock enable
        uint32_t reserved1 : 1;    // Bit 1: Reserved
        __IOM uint32_t IOPAEN : 1; // Bit 2: I/O port A clock enable
        __IOM uint32_t IOPBEN : 1; // Bit 3: I/O port B clock enable
        __IOM uint32_t IOPCEN : 1; // Bit 4: I/O port C clock enable
        __IOM uint32_t IOPDEN : 1; // Bit 5: I/O port D clock enable
        __IOM uint32_t IOPEEN : 1; // Bit 6: I/O port E clock enable
        __IOM uint32_t IOPFEN : 1; // Bit 7: I/O port F clock enable
        __IOM uint32_t IOPGEN : 1; // Bit 8: I/O port G clock enable
        __IOM uint32_t ADC1EN : 1; // Bit 9: ADC1 interface clock enable
        __IOM uint32_t ADC2EN : 1; // Bit 10: ADC2 interface clock enable
        __IOM uint32_t TIM1EN : 1; // Bit 11: TIM1 timer clock enable
        __IOM uint32_t SPI1EN : 1; // Bit 12: SPI1 clock enable
        __IOM uint32_t TIM8EN : 1; // Bit 13: TIM8 timer clock enable
        __IOM uint32_t USART1EN : 1; // Bit 14: USART1 clock enable
        __IOM uint32_t ADC3EN : 1; // Bit 15: ADC3 interface clock enable
        uint32_t reserved2 : 2;    // Bit 16-18: Reserved
        __IOM uint32_t TIM9EN : 1; // Bit 19: TIM9 timer clock enable
        __IOM uint32_t TIM10EN : 1; // Bit 20: TIM10 timer clock enable
        __IOM uint32_t TIM11EN : 1; // Bit 21: TIM11 timer clock enable
        uint32_t reserved3 : 10;   // Bit 22-31: Reserved
    };
    __IOM uint32_t reg; // Access entire register
} RCC_APB2ENR_Type;

Thanh ghi Bật Clock Ngoại vi APB1 (RCC_APB1ENR)

// Trích xuất từ tài liệu RM0008, Section 7.3.8, Trang 115
// Thanh ghi Bật Clock Ngoại vi APB1 (RCC_APB1ENR)
typedef union {
    struct {
        __IOM uint32_t TIM2EN : 1; // Bit 0: TIM2 timer clock enable
        __IOM uint32_t TIM3EN : 1; // Bit 1: TIM3 timer clock enable
        __IOM uint32_t TIM4EN : 1; // Bit 2: TIM4 timer clock enable
        __IOM uint32_t TIM5EN : 1; // Bit 3: TIM5 timer clock enable
        __IOM uint32_t TIM6EN : 1; // Bit 4: TIM6 timer clock enable
        __IOM uint32_t TIM7EN : 1; // Bit 5: TIM7 timer clock enable
        __IOM uint32_t TIM12EN : 1; // Bit 6: TIM12 timer clock enable
        __IOM uint32_t TIM13EN : 1; // Bit 7: TIM13 timer clock enable
        __IOM uint32_t TIM14EN : 1; // Bit 8: TIM14 timer clock enable
        uint32_t reserved1 : 2;    // Bit 9-10: Reserved
        __IOM uint32_t WWDGEN : 1; // Bit 11: Window watchdog clock enable
        uint32_t reserved2 : 2;    // Bit 12-13: Reserved
        __IOM uint32_t SPI2EN : 1; // Bit 14: SPI2 clock enable
        __IOM uint32_t SPI3EN : 1; // Bit 15: SPI3 clock enable
        uint32_t reserved3 : 1;    // Bit 16: Reserved
        __IOM uint32_t USART2EN : 1; // Bit 17: USART2 clock enable
        __IOM uint32_t USART3EN : 1; // Bit 18: USART3 clock enable
        __IOM uint32_t UART4EN : 1; // Bit 19: UART4 clock enable
        __IOM uint32_t UART5EN : 1; // Bit 20: UART5 clock enable
        __IOM uint32_t I2C1EN : 1; // Bit 21: I2C1 clock enable
        __IOM uint32_t I2C2EN : 1; // Bit 22: I2C2 clock enable
        __IOM uint32_t USBEN : 1;  // Bit 23: USB clock enable
        uint32_t reserved4 : 1;    // Bit 24: Reserved
        __IOM uint32_t CANEN : 1;  // Bit 25: CAN clock enable
        uint32_t reserved5 : 1;    // Bit 26: Reserved
        __IOM uint32_t BKPEN : 1;  // Bit 27: Backup interface clock enable
        __IOM uint32_t PWREN : 1;  // Bit 28: Power interface clock enable
        __IOM uint32_t DACEN : 1;  // Bit 29: DAC interface clock enable
        uint32_t reserved6 : 2;    // Bit 30-31: Reserved
    };
    __IOM uint32_t reg; // Access entire register
} RCC_APB1ENR_Type;

Thanh ghi Reset Ngoại vi APB2 (RCC_APB2RSTR)

// Trích xuất từ tài liệu RM0008, Section 7.3.4, Trang 106
// Thanh ghi Reset Ngoại vi APB2 (RCC_APB2RSTR)
typedef union {
    struct {
        __IOM uint32_t AFIORST : 1; // Bit 0: Alternate function I/O reset
        uint32_t reserved1 : 1;    // Bit 1: Reserved
        __IOM uint32_t IOPARST : 1; // Bit 2: I/O port A reset
        __IOM uint32_t IOPBRST : 1; // Bit 3: I/O port B reset
        __IOM uint32_t IOPCRST : 1; // Bit 4: I/O port C reset
        __IOM uint32_t IOPDRST : 1; // Bit 5: I/O port D reset
        __IOM uint32_t IOPERST : 1; // Bit 6: I/O port E reset
        __IOM uint32_t IOPFRST : 1; // Bit 7: I/O port F reset
        __IOM uint32_t IOPGRST : 1; // Bit 8: I/O port G reset
        __IOM uint32_t ADC1RST : 1; // Bit 9: ADC1 interface reset
        __IOM uint32_t ADC2RST : 1; // Bit 10: ADC2 interface reset
        __IOM uint32_t TIM1RST : 1; // Bit 11: TIM1 timer reset
        __IOM uint32_t SPI1RST : 1; // Bit 12: SPI1 reset
        __IOM uint32_t TIM8RST : 1; // Bit 13: TIM8 timer reset
        __IOM uint32_t USART1RST : 1; // Bit 14: USART1 reset
        __IOM uint32_t ADC3RST : 1; // Bit 15: ADC3 interface reset
        uint32_t reserved2 : 3;    // Bit 16-18: Reserved
        __IOM uint32_t TIM9RST : 1; // Bit 19: TIM9 timer reset
        __IOM uint32_t TIM10RST : 1; // Bit 20: TIM10 timer reset
        __IOM uint32_t TIM11RST : 1; // Bit 21: TIM11 timer reset
        uint32_t reserved4 : 10;   // Bit 22-31: Reserved
    };
    __IOM uint32_t reg; // Access entire register
} RCC_APB2RSTR_Type;

Thanh ghi Reset Ngoại vi APB1 (RCC_APB1RSTR)

// Trích xuất từ tài liệu RM0008, Section 7.3.5, Trang 109
// Thanh ghi Reset Ngoại vi APB1 (RCC_APB1RSTR)
typedef union {
    struct {
        __IOM uint32_t TIM2RST : 1; // Bit 0: TIM2 timer reset
        __IOM uint32_t TIM3RST : 1; // Bit 1: TIM3 timer reset
        __IOM uint32_t TIM4RST : 1; // Bit 2: TIM4 timer reset
        __IOM uint32_t TIM5RST : 1; // Bit 3: TIM5 timer reset
        __IOM uint32_t TIM6RST : 1; // Bit 4: TIM6 timer reset
        __IOM uint32_t TIM7RST : 1; // Bit 5: TIM7 timer reset
        __IOM uint32_t TIM12RST : 1; // Bit 6: TIM12 timer reset
        __IOM uint32_t TIM13RST : 1; // Bit 7: TIM13 timer reset
        __IOM uint32_t TIM14RST : 1; // Bit 8: TIM14 timer reset
        uint32_t reserved1 : 2;    // Bit 9-10: Reserved
        __IOM uint32_t WWDGRST : 1; // Bit 11: Window watchdog reset
        uint32_t reserved2 : 2;    // Bit 12-13: Reserved
        __IOM uint32_t SPI2RST : 1; // Bit 14: SPI2 reset
        __IOM uint32_t SPI3RST : 1; // Bit 15: SPI3 reset
        uint32_t reserved3 : 1;    // Bit 16: Reserved
        __IOM uint32_t USART2RST : 1; // Bit 17: USART2 reset
        __IOM uint32_t USART3RST : 1; // Bit 18: USART3 reset
        __IOM uint32_t UART4RST : 1; // Bit 19: UART4 reset
        __IOM uint32_t UART5RST : 1; // Bit 20: UART5 reset
        __IOM uint32_t I2C1RST : 1; // Bit 21: I2C1 reset
        __IOM uint32_t I2C2RST : 1; // Bit 22: I2C2 reset
        __IOM uint32_t USBRST : 1;  // Bit 23: USB reset
        uint32_t reserved4 : 1;    // Bit 24: Reserved
        __IOM uint32_t CANRST : 1;  // Bit 25: CAN reset
        uint32_t reserved5 : 1;    // Bit 26: Reserved
        __IOM uint32_t BKPRST : 1;  // Bit 27: Backup interface reset
        __IOM uint32_t PWRRST : 1;  // Bit 28: Power interface reset
        __IOM uint32_t DACRST : 1;  // Bit 29: DAC interface reset
        uint32_t reserved6 : 2;    // Bit 30-31: Reserved
    };
    __IOM uint32_t reg; // Access entire register
} RCC_APB1RSTR_Type;

Thanh ghi Điều khiển Miền Dự phòng (RCC_BDCR)

// Trích xuất từ tài liệu RM0008, Section 7.3.9, Trang 118
// Thanh ghi Điều khiển Miền Dự phòng (RCC_BDCR)
typedef union {
    struct {
        __IOM uint32_t LSEON : 1;   // Bit 0: External low-speed oscillator enable
        __IM  uint32_t LSERDY : 1;  // Bit 1: External low-speed oscillator ready
        __IOM uint32_t LSEBYP : 1;  // Bit 2: External low-speed oscillator bypass
        uint32_t reserved1 : 5;    // Bit 3-7: Reserved
        __IOM uint32_t RTCSEL : 2;  // Bit 8-9: RTC clock source selection
        uint32_t reserved2 : 5;    // Bit 10-14: Reserved
        __IOM uint32_t RTCEN : 1;   // Bit 15: RTC clock enable
        __IOM uint32_t BDRST : 1;   // Bit 16: Backup domain software reset
        uint32_t reserved3 : 15;   // Bit 17-31: Reserved
    };
    __IOM uint32_t reg; // Access entire register
} RCC_BDCR_Type;

Thanh ghi Trạng thái/Điều khiển (RCC_CSR)

// Trích xuất từ tài liệu RM0008, Section 7.3.10, Trang 120
// Thanh ghi Trạng thái/Điều khiển (RCC_CSR)
typedef union {
    struct {
        __IOM uint32_t LSION : 1;   // Bit 0: Internal low-speed oscillator enable
        __IM  uint32_t LSIRDY : 1;  // Bit 1: Internal low-speed oscillator ready
        uint32_t reserved1 : 22;   // Bit 2-23: Reserved
        __IOM uint32_t RMVF : 1;    // Bit 24: Remove reset flag
        uint32_t reserved2 : 1;    // Bit 25: Reserved
        __IM  uint32_t PINRSTF : 1; // Bit 26: PIN reset flag
        __IM  uint32_t PORRSTF : 1; // Bit 27: POR/PDR reset flag
        __IM  uint32_t SFTRSTF : 1; // Bit 28: Software reset flag
        __IM  uint32_t IWDGRSTF : 1; // Bit 29: Independent watchdog reset flag
        __IM  uint32_t WWDGRSTF : 1; // Bit 30: Window watchdog reset flag
        __IM  uint32_t LPWRSTF : 1; // Bit 31: Low-power reset flag
    };
    __IOM uint32_t reg; // Access entire register
} RCC_CSR_Type;

Thanh ghi Ngắt Clock (RCC_CIR)

// Trích xuất từ tài liệu RM0008, Section 7.3.3, Trang 104
// Thanh ghi Ngắt Clock (RCC_CIR)
typedef union {
    struct {
        __IM  uint32_t LSIRDYF : 1;  // Bit 0: LSI ready interrupt flag
        __IM  uint32_t LSERDYF : 1;  // Bit 1: LSE ready interrupt flag
        __IM  uint32_t HSIRDYF : 1;  // Bit 2: HSI ready interrupt flag
        __IM  uint32_t HSERDYF : 1;  // Bit 3: HSE ready interrupt flag
        __IM  uint32_t PLLRDYF : 1;  // Bit 4: PLL ready interrupt flag
        __IM  uint32_t CSSF : 1;     // Bit 5: Clock security system interrupt flag
        uint32_t reserved1 : 2;     // Bit 6-7: Reserved
        __IOM uint32_t LSIRDYIE : 1; // Bit 8: LSI ready interrupt enable
        __IOM uint32_t LSERDYIE : 1; // Bit 10: HSI ready interrupt enable
        __IOM uint32_t HSIRDYIE : 1; // Bit 10: HSI ready interrupt enable
        __IOM uint32_t HSERDYIE : 1; // Bit 11: HSE ready interrupt enable
        __IOM uint32_t PLLRDYIE : 1; // Bit 12: PLL ready interrupt enable
        uint32_t reserved2 : 3;     // Bit 13-15: Reserved
        __IOM uint32_t LSIRDYC : 1;  // Bit 16: LSI ready interrupt clear
        __IOM uint32_t LSERDYC : 1;  // Bit 17: LSE ready interrupt clear
        __IOM uint32_t HSIRDYC : 1;  // Bit 18: HSI ready interrupt clear
        __IOM uint32_t HSERDYC : 1;  // Bit 19: HSE ready interrupt clear
        __IOM uint32_t PLLRDYC : 1;  // Bit 20: PLL ready interrupt clear
        uint32_t reserved3 : 2;     // Bit 21-22: Reserved
        __IOM uint32_t CSSC : 1;     // Bit 23: Clock security system interrupt clear
        uint32_t reserved4 : 8;     // Bit 24-31: Reserved
    };
    __IOM uint32_t reg; // Access entire register
} RCC_CIR_Type;

Bản đồ thanh ghi RCC (RCC register map)

Sơ đồ bản đồ thanh ghi RCC Boundary address STM32F103

Chú thích: Bản đồ địa chỉ thanh ghi RCC (Reset and Clock Control) trong STM32F103 từ RM0008, Section 7.3.11, Trang 122.

// Trích xuất từ tài liệu RM0008, Section 7.3.11, Trang 122
// Bản đồ thanh ghi RCC (Reset and Clock Control)
typedef struct {
    volatile RCC_CR_Type CR;         // 0x00: Clock control register
    volatile RCC_CFGR_Type CFGR;     // 0x04: Clock configuration register
    volatile RCC_CIR_Type CIR;       // 0x08: Clock interrupt register
    volatile RCC_APB2RSTR_Type APB2RSTR; // 0x0C: APB2 peripheral reset register
    volatile RCC_APB1RSTR_Type APB1RSTR; // 0x10: APB1 peripheral reset register
    volatile RCC_AHBENR_Type AHBENR;  // 0x14: AHB peripheral clock enable register
    volatile RCC_APB2ENR_Type APB2ENR; // 0x18: APB2 peripheral clock enable register
    volatile RCC_APB1ENR_Type APB1ENR; // 0x1C: APB1 peripheral clock enable register
    volatile RCC_BDCR_Type BDCR;      // 0x20: Backup domain control register
    volatile RCC_CSR_Type CSR;       // 0x24: Control/status register
} RCC_TypeDef;

#define RCC ((RCC_TypeDef *)0x40021000UL)

Flash access control register

Flash access control register FLASH_ACR STM32F103

Chú thích: Thanh ghi điều khiển truy cập Flash (FLASH_ACR) từ RM0008, Section 3.3.3, Trang 54.

// Trích xuất từ tài liệu RM0008, Section 3.3.3, Trang 60
// Flash access control register
typedef union {
    struct { // Structure for bit fields
        __IOM uint32_t LATENCY : 3; // Bit 0-2: Latency
        __IOM uint32_t HLFCYA : 1; // Bit 3: Flash half cycle access enable
        __IOM uint32_t PRFTBE : 1; // Bit 4: Prefetch buffer enable
        __IM  uint32_t PRFTBS : 1; // Bit 5: Prefetch buffer status
        uint32_t reserved0 : 26; // Bit 6-31: Reserved
    };
    __IOM uint32_t reg; // Access entire register
} FLASH_ACR_Type;

typedef struct
{
  FLASH_ACR_Type ACR;
  __IO uint32_t KEYR;
  __IO uint32_t OPTKEYR;
  __IO uint32_t SR;
  __IO uint32_t CR;
  __IO uint32_t AR;
  __IO uint32_t RESERVED;
  __IO uint32_t OBR;
  __IO uint32_t WRPR;  
} 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ừ RM0008, Section 7.3 (trang 99-122)Section 3.3.3 (trang 54-60), chúng ta cùng đi sâu vào chức năng của các thanh ghi chính:

Giải thích các thanh ghi RCC

  • CR (Clock Control Register): Điều khiển các nguồn dao động chính như HSI (Internal High-speed), HSE (External High-speed) và PLL (Phase-Locked Loop). Các bit như HSION, HSEON, PLLON cho phép bật/tắt các nguồn này, trong khi các cờ HSIRDY, HSERDY, PLLRDY cho biết trạng thái sẵn sàng của chúng.
  • CFGR (Clock Configuration Register): Cấu hình nguồn clock hệ thống (SYSCLK), các hệ số chia tần cho AHB (HPRE), APB1 (PPRE1), APB2 (PPRE2), ADC (ADCPRE), cũng như nguồn đầu vào và hệ số nhân của PLL (PLLSRC, PLLXTPRE, PLLMUL).
  • CIR (Clock Interrupt Register): Quản lý các cờ ngắt và cho phép ngắt liên quan đến trạng thái của các nguồn clock.
  • APB2RSTR, APB1RSTR: Các thanh ghi reset ngoại vi trên bus APB2 và APB1. Việc set bit tương ứng sẽ reset ngoại vi đó.
  • AHBENR, APB2ENR, APB1ENR: Các thanh ghi cho phép clock cho các ngoại vi trên bus AHB, APB2 và APB1. Bắt buộc phải bật clock cho một ngoại vi trước khi sử dụng. Ví dụ: Bật clock cho GPIOA bằng RCC->APB2ENR.IOPAEN = 1;.
  • BDCR (Backup Domain Control Register): Điều khiển các chức năng trong miền dự phòng như LSE (External Low-speed Oscillator) và RTC (Real-Time Clock).
  • CSR (Control/Status Register): Chứa các cờ trạng thái reset và điều khiển một số chức năng đặc biệt.

Giải thích thanh ghi FLASH_ACR

  • ACR (Access Control Register): Điều khiển quyền truy cập vào bộ nhớ Flash. Các bit quan trọng là LATENCY (độ trễ truy cập Flash, cần cấu hình đúng khi tăng tần số hệ thống), PRFTBE (cho phép bộ đệm prefetch để tối ưu hiệu suất đọc), và PRFTBS (trạng thái của bộ đệm prefetch).

4. Quy trình cấu hình

Quy trình cấu hình System Clock cho STM32F103 để chạy ở 72MHz tuân thủ chặt chẽ theo các bước khuyến nghị trong RM0008, Section 7.3 (trang 99-122).

graph TD
    A["Bắt đầu"] --> B["Bật HSE"]
    B --> C["Chờ HSE sẵn sàng"]
    C --> D["Cấu hình Flash Wait State & Prefetch Buffer"]
    D --> E["Chọn HSE làm nguồn PLL"]
    E --> F["Đặt hệ số nhân PLL x9"]
    F --> G["Bật PLL"]
    G --> H["Chờ PLL sẵn sàng"]
    H --> I["Đặt Prescaler AHB APB1 APB2"]
    I --> J["Chọn PLL làm SYSCLK"]
    J --> K["Chờ SYSCLK chuyển đổi"]
    K --> L["Hoàn tất cấu hình Clock"]

5. Clock Tree STM32F103 và tính toán xung clock

Cấu trúc clock tree

Clock tree (RM0008, Figure 11, trang 126):

Sơ đồ Clock tree STM32F103

Chú thích: Sơ đồ Clock tree tổng quan của STM32F103, cho thấy các đường dẫn và bộ chia tần số.

  • HSE: 8MHz (Section 7.2.1, trang 95).
  • PLL: HSE x9 = 72MHz (PLLMUL = 7, Section 7.3.2, trang 101).
  • HCLK: SYSCLK / HPRE = 72MHz / 1 = 72MHz.
  • PCLK1: HCLK / PPRE1 = 72MHz / 2 = 36MHz.
  • PCLK2: HCLK / PPRE2 = 72MHz / 1 = 72MHz.

Tính toán xung Timer

Các khối clock quan trọng sau khi cấu hình STM32F103

Chú thích: Các khối ngoại vi quan trọng cần quan tâm sau khi cấu hình clock.

Ví dụ tính toán xung clock cho Timer STM32F103

Chú thích: Ví dụ minh họa tính toán xung clock cho Timer.

– Timer clock (APB1): PPRE1 > 1 nên TIMCLK = 2 x PCLK1 = 72MHz (Section 7.2, trang 93). – Ví dụ: Timer 1ms tại 72MHz, PSC = 71, ARR = 999 (72MHz / 72 / 1000 = 1kHz).

6. Cấu hình RCC và ngoại vi STM32F103 bằng thanh ghi

Bật clock cho GPIOA

RCC->APB2ENR.IOPAEN = 1

Bật clock: RCC->APB2ENR.IOPAEN = 1 cho GPIOA (RM0008, Section 7.3.7, trang 112).

Reset ngoại vi nếu cần

RCC->APB2RSTR.IOPAEN = 1

Reset nếu cần: RCC->APB2RSTR.IOPAEN = 1 rồi clear (Section 7.3.4, trang 106).

Ví dụ cấu hình GPIO Output

GPIOA->CRL &= ~(0xF << (5 * 4));
GPIOA->CRL |= (0x1 << (5 * 4));

Cấu hình thanh ghi RCC

  • Enable HSE: Set bit HSEON to 1
Enable HSE: Set bit HSEON to 1 trong lập trình stm32f103
Enable HSE
  • Enable PLL: Set bit PLLON to 1
Enable PLL: Set bit PLLON to 1
Enable PLL
  • Configure PLL:

PLL HSE source: Set bit PLLSRC to 1

PLL HSE source: Set bit PLLSRC to 1
Select PLL HSE source: Set bit PLLSRC to 1

PLL MUL: Set PLL input clock x9, set PLLMUL to 7 (b0111)

PLL MUL: Set PLL input clock x9, set PLLMUL to 7 (b0111)
Set-PLLMUL-x9
  • Set prescalers:

AHB prescaler: HCLCK/1 = SYSCLK, set HPRE to 0

AHB prescaler: HCLCK/1 = SYSCLK, set HPRE to 0
AHB prescaler: HPRE

APB low-speed prescaler (APB1): PLCK1 = HCLK/2 = SYSCLK/2, set PPRE1 to 4 (100)

APB low-speed prescaler (APB1): PLCK1 = HCLK/2 = SYSCLK/2, set PPRE1 to 4 (100)
PPRE1: APB low-speed prescaler (APB1)

APB high-speed prescaler (APB2): PLCK2 = HCLK = SYSCLK, set PPRE2 to 0

APB high-speed prescaler (APB2): PLCK2 = HCLK = SYSCLK, set PPRE2 to 0
PPRE2: APB high-speed prescaler (APB2)

Chọn system clock: Set PLL như system clock

Chọn system clock: Set PLL như system clock
Chọn system clock: Set PLL

7. Tạo file system cho cấu hình clock STM32F103

Tạo file system.h

#ifndef __SYSTEM_H
#define __SYSTEM_H


void SystemClock_Config(void);


#endif

Tạo file system.c

#include "stm32f10x.h"
#include "system.h"


void SystemClock_Config(void) {
// Enable HSE (8MHz)
RCC->CR |= RCC_CR_HSEON;
while (!(RCC->CR & RCC_CR_HSERDY));


// Configure PLL: HSE x9 = 72MHz
RCC->CFGR |= RCC_CFGR_PLLSRC;
RCC->CFGR |= RCC_CFGR_PLLMULL9;


// Enable PLL
RCC->CR |= RCC_CR_PLLON;
while (!(RCC->CR & RCC_CR_PLLRDY));


// Prescalers configuration
RCC->CFGR |= RCC_CFGR_HPRE_DIV1; // HCLK = SYSCLK
RCC->CFGR |= RCC_CFGR_PPRE1_DIV2; // PCLK1 = HCLK/2
RCC->CFGR |= RCC_CFGR_PPRE2_DIV1; // PCLK2 = HCLK


// Select PLL as system clock
RCC->CFGR |= RCC_CFGR_SW_PLL;
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
}

Chỉnh sửa startup để dùng clock tự cấu hình

Mở file startup_stm32f10x_hd.s

Hướng dẫn ghi bỏ tick read only file startup để cho phép chỉnh sửa trên Keil C
Bỏ tick read-only file startup_stm32f10x_hd.s
  • Chuột phải → Properties
  • Bỏ chọn Read-only

Ghi đè hàm SystemInit

Thay thế function SystemInit bằng SystemClock_Config cho file startup_stm32f10x_hd
Thay thế function SystemInit bằng SystemClock_Config cho file startup_stm32f10x_hd
IMPORT SystemClock_Config


SystemInit
BL SystemClock_Config
BX LR

Sửa function SystemInit thành function mà chúng ta đã định nghĩa lại (SystemClock_Config)

👉 Mục đích: thay thế clock mặc định của CMSIS bằng clock do người dùng cấu hình.

8. Code cấu hình clock STM32F103 thực tế chạy trên Keil C

void SystemClock_Config(void) {
    // Enable HSE (RM0008, Section 7.3.1, page 99)
    RCC->CR.HSEON = 1;
    while (!RCC->CR.HSERDY);  // Wait for HSE ready

    // Configure PLL: HSE source, x9 (RM0008, Section 7.3.2, page 101)
    RCC->CFGR.PLLSRC = 1;   // HSE as PLL source
    RCC->CFGR.PLLMUL = 7;   // PLL x9
    RCC->CR.PLLON = 1;      // Enable PLL
    while (!RCC->CR.PLLRDY); // Wait for PLL ready

    // Set prescalers: HCLK=/1, PCLK1=/2, PCLK2=/1
    RCC->CFGR.HPRE = 0;     // HCLK = SYSCLK
    RCC->CFGR.PPRE1 = 4;    // PCLK1 = HCLK/2
    RCC->CFGR.PPRE2 = 0;    // PCLK2 = HCLK

    // Select PLL as system clock
    RCC->CFGR.SW = 2;       // PLL as SYSCLK
    while (RCC->CFGR.SWS != 2); // Wait for switch
}
int main(void) {
    
    SystemClock_Config();  // Initialize system clock
    
    // Peripheral configurations
    while (1) {
        // Application code
    }
}

Link Github: Download code chương trình cấu hình Clock STM32F103 thanh ghi

9. Kiểm tra cấu hình clock STM32F103 trong Keil

Bật cửa sổ trạng thái thanh ghi RCC trong Keil C
Bật cửa sổ trạng thái thanh ghi RCC trong Keil C
  1. Debug → Start Debug Session
  2. Peripherals → System Viewer → RCC
  3. Kiểm tra:
    • HSEON = 1
    • PLLON = 1
    • PLLSRC = HSE
    • PLLMUL = x9
    • SW = PLL
    • HPRE = /1
    • PPRE1 = /2
    • PPRE2 = /1
Kiểm tra RCC register configuration trong cửa sổ Keil C
Kiểm tra RCC register configuration trong cửa sổ Keil C

Tham chiếu: RM0008, Section 7.2 (trang 92-98), Table 19 (trang 157).

10. Tổng kết

Bài viết này đã hướng dẫn đầy đủ cách lập trình STM32 thanh ghi cấu hình clock STM32F103, giúp bạn hiểu bản chất RCC, clock tree và cách tính toán xung. Đây là nền tảng quan trọng để tiếp tục các bài GPIO, Timer, UART theo hướng bare-metal chuyên sâu. Với những kiến thức này, anh em đã có thể tự tin cấu hình hệ thống clock và Flash cho các dự án nhúng của mình.

Viết một bình luận

This site uses Akismet to reduce spam. Learn how your comment data is processed.