🌱 STM32 - 16. Lập trình Thanh ghi STM32 - UART Interrupt

🌱 STM32 - 16. Lập trình Thanh ghi STM32 - UART Interrupt

    Ở bài viết trước mình đã giới thiệu với các bạn về một ví dụ giao tiếp UART với PC, chế độ Polling, ở bài viết này mình sẽ sử dụng Interrupt để giao tiếp. Một ưu điểm dễ thấy của Interrupt đó là khi có những sự kiện quan trọng xảy ra chúng ta có thể phát hiện và xử lý ngay lập tức.

    Xem bài: Các kỹ thuật thiết kế luồng xử lý trong chương trình nhúng

👉 Phần cứng sử dụng:

  • STM32F401RE - NUCLEO Board.
  • CP2102 USB-to-UART.

👉 Phần mềm sử dụng:

    Việc dùng ngắt giúp công việc này dễ dàng hơn rất nhiều. Chẳng hạn, khi muốn cập nhật Firmware, chúng ta có thể truyền UART đến Vi điều khiển và STM32 sẽ không để lỡ sự kiện này. Khi chúng ta setup ngắt UART ở mức ưu tiên cao để tránh những sự kiện khác xen vào.

    Khác với chế độ Polling, UART Interrupt có một số đặc điểm khác biệt sau:

  • Ở hàm Init, chúng ta cần Enable ngắt Truyền/Nhận UART.
  • Viết Hàm IRQ Handler.

👉 Ví dụ lập trình UART Interrupt

    Dưới đây là đoạn code bare-metal cho USART2 (PA2-TX, PA3-RX) sử dụng ngắt để 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
#define NVIC_BASE       0xE000E100

// Đị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 thanh ghi NVIC
#define NVIC_ISER1      (*(volatile uint32_t *)(NVIC_BASE + 0x004)) // NVIC Interrupt Set-Enable Register 1

// Đị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)
#define USART_CR1_RXNEIE (1 << 5) // Ngắt khi nhận dữ liệu (RXNE Interrupt Enable)
#define USART_CR1_TXEIE  (1 << 7) // Ngắt khi truyền xong (TXE Interrupt Enable)

// Đị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()?;
#define USART_SR_TC     (1 << 6)
#define USART_SR_RXNE   (1 << 5)

// Định nghĩa bit trong NVIC
#define NVIC_USART2_IRQn 6 // USART2 nằm ở vị trí 38 trong vector table, thuộc ISER1 (bit 6)

// Biến toàn cục để lưu dữ liệu nhận được
volatile uint8_t rxData = 0;
volatile uint8_t txReady = 0;

// 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 với ngắt
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, bật ngắt RXNE
    USART2_CR1 = 0;
    USART2_CR1 |= USART_CR1_TE | USART_CR1_RE | USART_CR1_RXNEIE | USART_CR1_UE;

    USART2_CR2 = 0;
    USART2_CR2 |= USART_CR2_STOP_1BIT;

    USART2_CR3 = 0; // Không dùng hardware flow control

    // Bật ngắt USART2 trong NVIC
    NVIC_ISER1 |= (1 << NVIC_USART2_IRQn); // Enable IRQ38 (USART2)
}

// 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 xử lý ngắt USART2
void USART2_IRQHandler(void) {
    // Kiểm tra ngắt nhận dữ liệu (RXNE)
    if (USART2_SR & USART_SR_RXNE) {
        rxData = USART2_DR; // Đọc dữ liệu nhận được
        UART_SendChar(rxData); // Echo lại ngay lập tức
    }
}

int main(void) {
    SystemClock_Config();
    GPIO_Init();
    UART_Init();

    // Gửi chuỗi chào
    UART_SendString("Hello from STM32 with Interrupt!\r\n");

    while (1) {
        // Vòng lặp chính trống - xử lý trong ngắt
    }
}
    

👉 Mình để lại Video sau để các bạn tham khảo nhé!

Video: Hướng dẫn lập trình UART Interrupt 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 😊

Nguyễn Văn Nghĩa

Mình là một người thích học hỏi và chia sẻ các kiến thức về Nhúng IOT.

Đăng nhận xét

Mới hơn Cũ hơn
//