🌱 STM32 - 13. Hướng Dẫn Cấu Hình Baudrate UART Trong STM32
Ở những post trước mình đã giới thiệu về giao thức USART và các thanh ghi được sử dụng với ngoại vi USART trong vi điều khiển STM32F401. Trong khi tìm hiểu về ngoại vi này, có hai vấn đề nổi lên mà các bạn thường thắc mắc: Thứ nhất là vấn đề tính Baudrate như thế nào? Và thứ hai là vấn đề về Parity hoạt động như thế nào? Vậy ở post này mình sẽ chia sẻ những hiểu biết của mình về vấn đề thứ nhất: USART Baudrate.
Hình 1: Board STM32F401 NUCLEO với các chân GPIO
👉 USART Baudrate trong STM32
Về cơ bản thì tốc độ Baudrate của ngoại vi USART được cấu hình bằng thanh ghi USART_BRR (cần cấu hình trước khi Enable ngoại vi bằng các bit TE/RE trong USART_CR1):
- Địa chỉ offset: 0x08
- Giá trị reset: 0x0000
- Các bit chính:
- Bits[15:4] - DIV_Mantissa[11:0]: Thành phần nguyên của USARTDIV (phần trước dấu phẩy).
- Bits[3:0] - DIV_Fraction[3:0]: Thành phần thập phân của USARTDIV (phần sau dấu phẩy).
Công việc cần làm là tính toán giá trị nạp vào các bit này, bằng cách tính USARTDIV.
👉 Hai cách chọn Baudrate
Khi thực hành với ngoại vi USART, các bạn có 2 cách để chọn Baudrate:
-
Cách thứ nhất là tra bảng: Vi điều khiển STM32 cung cấp các bảng tính toán sẵn Baudrate trong Reference Manual (với STM32F401 là chương 19, bảng từ 74 trở đi). Các bảng này liệt kê các giá trị Baudrate thường dùng, kèm tần số Clock thông dụng và sai số.
Ví dụ về một bảng tính toán trong STM32F401 Reference Manual:
Có thể thấy, tốc độ Baudrate sẽ phụ thuộc vào tần số Clock (PCLK1 hoặc PCLK2) và bit OVER8 của thanh ghi USART_CR1.
-
Cách thứ hai là tính theo công thức: Reference Manual cung cấp công thức để tự tính Baudrate:
Baudrate = f_CK / (8 * (2 - OVER8) * USARTDIV)
Trong đó:
- f_CK: Tần số clock nguồn (PCLK1 hoặc PCLK2).
- OVER8: Bit trong USART_CR1 (0 = oversampling 16, 1 = oversampling 8).
- USARTDIV: Giá trị cần tính để nạp vào USART_BRR.
👉 Một số lưu ý khi tính Baudrate
-
Bit[15] - OVER8 trong USART_CR1:
- OVER8 = 1: Hệ số chia là 8.
- OVER8 = 0: Hệ số chia là 16 (mặc định). -
Tần số xung clock của mỗi ngoại vi là khác nhau, trong STM32F401 có 6 ngoại vi USART:
- USART1/6: Kết nối với bus APB2 (PCLK2).
- USART2/3/4/5: Kết nối với bus APB1 (PCLK1).
❓Vậy làm cách nào từ USARTDIV có thể tính ra giá trị nạp vào thanh ghi USART_BRR, cụ thể là 2 phần DIV_Mantissa và DIV_Fraction?
-
DIV_Mantissa: Là phần nguyên của USARTDIV.
DIV_Mantissa = [USARTDIV]
💬 Ví dụ: Nếu USARTDIV = 52,0625 thì DIV_Mantissa = 52 = 0x34. -
DIV_Fraction: Là phần thập phân của USARTDIV, phụ thuộc vào OVER8:
DIV_Fraction = {USARTDIV} * 8 * (2 - OVER8)
- Nếu OVER8 = 0: Nhân phần thập phân với 16.
- Nếu OVER8 = 1: Nhân phần thập phân với 8.
💬 Ví dụ: Nếu USARTDIV = 52,0625 và OVER8 = 0 thì DIV_Fraction = 0,0625 * 16 = 1 = 0x1.
👉 Ví dụ tính toán UART Baudrate là 9600
Ví dụ mình muốn cấu hình Baudrate là 9600, với sai số 0%. Mình sẽ tính giá trị nạp vào thanh ghi USART_BRR theo các bước sau:
-
Tra bảng (bảng 74 trong Reference Manual): Với PCLK = 12MHz và OVER8 = 0, Baudrate 9600 có sai số 0%, nên mình chọn PCLK = 12MHz.
➤ Xem thêm: Cấu hình tần số 12MHz cấp cho USART - Chọn OVER8 = 0 (hệ số chia là 16) từ bảng 74.
-
Theo bảng 74, USARTDIV = 78,125. Có thể tính tay bằng công thức:
USARTDIV = f_CK / (16 * Baudrate) = 12,000,000 / (16 * 9600) = 78,125
-
Tính 2 thành phần của thanh ghi USART_BRR:
- DIV_Mantissa = [USARTDIV] = 78 = 0x04E (12 bit).
- DIV_Fraction = {USARTDIV} * 16 = 0,125 * 16 = 2 = 0x2. - Giá trị nạp vào USART_BRR = 0x04E2.
Code hoàn chỉnh cấu hình Baudrate = 9600
- // Define base addresses for peripherals
- #define RCC_BASE 0x40023800
- #define USART2_BASE 0x40004400
- // Define RCC registers
- #define RCC_CR (*(volatile uint32_t *)(RCC_BASE + 0x00))
- #define RCC_APB1ENR (*(volatile uint32_t *)(RCC_BASE + 0x40))
- // Define USART2 registers
- #define USART2_SR (*(volatile uint32_t *)(USART2_BASE + 0x00))
- #define USART2_DR (*(volatile uint32_t *)(USART2_BASE + 0x04))
- #define USART2_BRR (*(volatile uint32_t *)(USART2_BASE + 0x08))
- // Define RCC bits
- #define RCC_APB1ENR_USART2EN (1 << 17)
- // Initialize USART2 with Baudrate = 9600
- void USART2_Init(void) {
- // Configure Clock for UART = 12MHz
- // Enable clock for USART2
- RCC_APB1ENR |= RCC_APB1ENR_USART2EN;
- // Configure Baudrate = 9600
- // PCLK1 = 12 MHz, OVER8 = 0 (oversampling 16)
- // USARTDIV = f_CK / (16 * Baudrate) = 12,000,000 / (16 * 9600) = 78.125
- // DIV_Mantissa = 78 (0x4E), DIV_Fraction = 2 (0x2)
- USART2_BRR = (78 << 4) | 2; // Load value: 0x04E2
- }
>>>>>> 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 😊