🌱 STM32 - 15. Lập Trình UART Truy Xuất Thanh Ghi
Các bài viết trước về USART:
- STM 11. USART in STM32 Overview
- STM 12. STM32 USART Registers
- STM 13. USART: Cấu hình Baudrate
- STM 14. USART Workflow
Bài viết này mình sẽ cùng các bạn thực hành một ví dụ sử dụng UART trên Vi điều khiển STM32, truy xuất trực tiếp thanh ghi.
👉 Phần cứng sử dụng:
- STM32F401RE - NUCLEO Board.
- CP2102 USB-to-UART.
👉 Phần mềm sử dụng:
- STM32CubeIDE ⇒ Xem hướng dẫn sử dụng.
- Hercules.
👉 UART Workflow
🔎 Khởi tạo UART
Để làm việc với giao thức UART, chúng ta cần khởi tạo ngoại vi bằng cách cấu hình một số thông số: Baudrate, Stop Bits, Parity Bits, ...
Chúng ta có thể config theo flow sau:
- Enable Clock tương ứng với ngoại vi USART sử dụng.
- Setup Pin tương ứng với chức năng UART.
- Enable Transmit/Receive bằng bit TX/RX trên thanh ghi USART_CR1.
- Config Word Length (8bit or 9bit) bằng bit M thanh ghi USART_CR1.
- Config Parity Bit sử dụng bit PCE - Parity Control Bit và PS - Parity Selection thanh ghi USART_CR1.
- Config Stop Bits sử dụng trường STOP thanh ghi USART_CR2.
- Option: Config Hardware Flow Control sử dụng thanh ghi USART_CR3.
- Cấu hình Baudrate sử dụng thanh ghi USART_BRR.
??? Tính toán Baudrate
Bài viết: STM 13. USART: Cấu hình Baudrate
Ví dụ: Tính toán Baudrate = 9600 với F_CLK = 8MHz:
- F_CLK = 8MHz, OVER8 = 0 (oversampling 16).
- USARTDIV = 8,000,000 / (16 * 9600) = 52.083.
- Mantissa = 52 (0x34), Fraction = 0.083 * 16 = 1.33 ≈ 1 (0x1).
- USART_BRR = (52 << 4) | 1 = 0x0341.
🔎 Truyền dữ liệu UART
Truyền từng frame dữ liệu (7/8/9 bits data) bằng flow sau:
- Đợi thanh ghi truyền dữ liệu trống - empty bằng cờ TXE - Transmission Data Register Empty, thanh ghi USART_SR.
- Đặt Byte Data cần truyền vào thanh ghi USART_DR.
- Đợi việc truyền hoàn tất bằng cờ TC - Transmission Complete thanh ghi USART_SR.
🔎 Nhận dữ liệu UART
Nhận dữ liệu từ UART theo flow sau:
- Đợi thanh ghi nhận dữ liệu chứa data - not empty bằng cờ RXNE - Receive Data Register Not Empty, thanh ghi USART_SR.
- Đọc dữ liệu nhận được từ thanh ghi USART_DR.
👉 Example: STM32 giao tiếp PC USB-to-UART
Mình dùng con CP2102 USB-to-UART để test giao thức UART của STM32, giao tiếp truyền nhận dữ liệu với máy tính (Cài phần mềm Hercules).
Nối dây TX của CP2102 với RX của STM32 (PA3), RX của CP2102 với TX của STM32 (PA2).
Dưới đây là đoạn code bare-metal cho USART2 (PA2-TX, PA3-RX) giao tiếp với PC:
#include// Định nghĩa địa chỉ cơ bản #define RCC_BASE 0x40023800 #define GPIOA_BASE 0x40020000 #define USART2_BASE 0x40004400 // Đị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)) #define RCC_APB1ENR (*(volatile uint32_t *)(RCC_BASE + 0x40)) // Định nghĩa thanh ghi GPIOA #define GPIOA_MODER (*(volatile uint32_t *)(GPIOA_BASE + 0x00)) #define GPIOA_OSPEEDR (*(volatile uint32_t *)(GPIOA_BASE + 0x08)) #define GPIOA_PUPDR (*(volatile uint32_t *)(GPIOA_BASE + 0x0C)) #define GPIOA_AFRL (*(volatile uint32_t *)(GPIOA_BASE + 0x20)) // Định nghĩa thanh ghi USART2 #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 USART2_CR1 (*(volatile uint32_t *)(USART2_BASE + 0x0C)) #define USART2_CR2 (*(volatile uint32_t *)(USART2_BASE + 0x10)) #define USART2_CR3 (*(volatile uint32_t *)(USART2_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_CFGR_HPRE_DIV2 (1 << 4) // AHB = HSI/2 = 8MHz #define RCC_AHB1ENR_GPIOAEN (1 << 0) #define RCC_APB1ENR_USART2EN (1 << 17) // Định nghĩa bit trong GPIO #define GPIO_MODER_AF 2 #define GPIO_OSPEEDR_HIGH 3 #define GPIO_AF7_USART2 7 // Định nghĩa bit trong USART_CR1 #define USART_CR1_UE (1 << 13) #define USART_CR1_TE (1 << 3) #define USART_CR1_RE (1 << 2) // Định nghĩa bit trong USART_CR2 #define USART_CR2_STOP_1BIT (0 << 12) // Định nghĩa bit trong USART_SR #define USART_SR_TXE (1 << 7) #define USART_SR_TC (1 << 6) #define USART_SR_RXNE (1 << 5) // 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); RCC_CFGR |= RCC_CFGR_HPRE_DIV2; // AHB = 8MHz, APB1 = 8MHz } // Hàm cấu hình GPIO cho USART2 (PA2-TX, PA3-RX) void GPIO_Init(void) { RCC_AHB1ENR |= RCC_AHB1ENR_GPIOAEN; // PA2 (TX) và PA3 (RX) thành Alternate Function GPIOA_MODER &= ~(3 << (2 * 2)); GPIOA_MODER |= (GPIO_MODER_AF << (2 * 2)); GPIOA_MODER &= ~(3 << (3 * 2)); GPIOA_MODER |= (GPIO_MODER_AF << (3 * 2)); // Tốc độ cao GPIOA_OSPEEDR &= ~(3 << (2 * 2)); GPIOA_OSPEEDR |= (GPIO_OSPEEDR_HIGH << (2 * 2)); GPIOA_OSPEEDR &= ~(3 << (3 * 2)); GPIOA_OSPEEDR |= (GPIO_OSPEEDR_HIGH << (3 * 2)); // Không pull-up/pull-down GPIOA_PUPDR &= ~(3 << (2 * 2)); GPIOA_PUPDR &= ~(3 << (3 * 2)); // AF7 cho USART2 GPIOA_AFRL &= ~(0xF << (2 * 4)); GPIOA_AFRL |= (GPIO_AF7_USART2 << (2 * 4)); GPIOA_AFRL &= ~(0xF << (3 * 4)); GPIOA_AFRL |= (GPIO_AF7_USART2 << (3 * 4)); } // Hàm khởi tạo USART2 void UART_Init(void) { RCC_APB1ENR |= RCC_APB1ENR_USART2EN; // Baudrate = 9600, PCLK1 = 8MHz USART2_BRR = (52 << 4) | 1; // 0x0341 // Cấu hình: 8 bits, no parity, 1 stop bit USART2_CR1 = 0; USART2_CR1 |= USART_CR1_TE | USART_CR1_RE | USART_CR1_UE; USART2_CR2 = 0; USART2_CR2 |= USART_CR2_STOP_1BIT; USART2_CR3 = 0; // Không dùng hardware flow control } // Hàm truyền một ký tự void UART_SendChar(uint8_t data) { while (!(USART2_SR & USART_SR_TXE)); USART2_DR = data; while (!(USART2_SR & USART_SR_TC)); } // Hàm truyền chuỗi void UART_SendString(const char *str) { while (*str) { UART_SendChar(*str++); } } // Hàm nhận một ký tự uint8_t UART_ReceiveChar(void) { while (!(USART2_SR & USART_SR_RXNE)); return (uint8_t)USART2_DR; } int main(void) { SystemClock_Config(); GPIO_Init(); UART_Init(); // Gửi chuỗi chào UART_SendString("Hello from STM32!\r\n"); while (1) { // Echo dữ liệu nhận được uint8_t received = UART_ReceiveChar(); UART_SendChar(received); } }
👉 Mình để lại Video sau để các bạn tham khảo nhé!
Video 1: Hàm UART_Init()
Video 2: Hàm UART_SendData() và UART_ReceiveData()
Các bạn thấy Video hữu ích có thể cho mình xin 1 Like + 1 Subcribe nhé 💗
>>>>>> 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 😊