CETut – Part 5 : UART

Trong các chuẩn giao tiếp, Universal Asynchronous Receiver/Transmitter (UART), thường được gọi tắt là UART, là một mạch cứng (module) được sử dụng để truyền dữ liệu. UART có thể được bán rời dưới dạng mạch tích hợp độc lập (IC) hoặc là một mô-đun bên trong các vi điều khiển. Trong bài hướng dẫn này, chúng ta sẽ tập trung vào module UART bên trong các vi điều khiển STM32.
Share

Trong các chuẩn giao tiếp, Universal Asynchronous Receiver/Transmitter (UART), thường được gọi tắt là UART, là một mạch cứng (module) được sử dụng để truyền dữ liệu. UART có thể được bán rời dưới dạng mạch tích hợp độc lập (IC) hoặc là một mô-đun bên trong các vi điều khiển. Trong bài hướng dẫn này, chúng ta sẽ tập trung vào module UART bên trong các vi điều khiển STM32.

1. Giới thiệu về UART và USART

Thực tế có hai loại phần cứng UART:

UART (Universal Asynchronous Receiver/Transmitter): Kiểu truyền này không đồng bộ. Bộ thu phát tự tạo ra xung clock cho dữ liệu. Không có tín hiệu clock nối tiếp đến, vì vậy để đạt được giao tiếp chính xác giữa hai đầu, cả hai đều phải sử dụng cùng một tốc độ truyền baud (baud rate).

USART (Universal Synchronous/Asynchronous Receiver/Transmitter): Kiểu truyền này có thể đồng bộ hoặc không đồng bộ. Bộ thu phát đồng bộ tạo ra tín hiệu clock dữ liệu và gửi nó đến bộ thu, hoạt động theo cách được đồng bộ hóa.

Như vậy, USART linh hoạt hơn UART vì nó hỗ trợ cả hai kiểu truyền đồng bộ và không đồng bộ. Tuy nhiên, trong hầu hết các trường hợp truyền thông nối tiếp với các thiết bị bên ngoài, UART là lựa chọn phù hợp vì nó đơn giản hơn và dễ sử dụng hơn.

2. Các tính năng chính của UART trong STM32

UART trong STM32 hỗ trợ nhiều tính năng cho giao tiếp nối tiếp, bao gồm:

  • Full duplex, không đồng bộ: Cho phép truyền và nhận dữ liệu đồng thời trên hai đường truyền riêng biệt.
  • Bộ tạo tốc độ truyền baud phân số: Có thể lập trình tốc độ truyền cả phía gửi và nhận lên đến 4.5 MBits/s.
  • Kiểm tra chẵn lẻ (Parity control): Kiểm tra tính toàn vẹn của dữ liệu bằng cách thêm một bit chẵn lẻ vào mỗi khung truyền.
  • Chiều dài dữ liệu lập trình (8 hoặc 9 bits): Cho phép lựa chọn kích thước của mỗi gói dữ liệu truyền đi.
  • Bit dừng cấu hình (1 hoặc 2 bit dừng): Thêm các bit dừng vào cuối mỗi khung truyền để báo hiệu kết thúc.
  • Khả năng gửi ngắt đồng bộ LIN Master và khả năng phát hiện ngắt LIN slave: Hỗ trợ giao thức truyền thông LIN bus.
  • Đầu ra xung clock của bộ phát cho truyền đồng bộ: Cung cấp xung clock tham chiếu cho truyền dữ liệu đồng bộ.
  • Mã hóa-Giải mã IrDA SIR: Cho phép truyền nhận dữ liệu theo chuẩn IrDA hồng ngoại.
  • Khả năng giả lập thẻ thông minh: Hỗ trợ giao tiếp với các thẻ thông minh.
  • Half duplex và Single wire: Cho phép truyền nhận dữ liệu trên một đường truyền duy nhất, luân phiên giữa gửi và nhận.
  • Giao tiếp đa vùng đệm có thể cấu hình bằng DMA (truy cập bộ nhớ trực tiếp): Sử dụng DMA để đệm dữ liệu nhận/gửi trong vùng nhớ SRAM được dành riêng.
  • Bit bật tắt riêng cho bộ phát và bộ thu: Cho phép bật/tắt độc lập bộ thu và bộ phát.
  • Cờ phát hiện truyền (Bộ đệm nhận đầy – Bộ đệm truyền trống – Cờ kết thúc truyền): Cung cấp các cờ thông báo trạng thái truyền.
  • 4 cờ phát hiện lỗi (Lỗi tràn – Lỗi nhiễu – Lỗi khung – Lỗi chẵn lẻ): Giúp phát hiện các lỗi xảy ra trong quá trình truyền.
  • 10 nguồn ngắt với cờ: Cho phép sử dụng ngắt để xử lý các sự kiện liên quan đến UART.

3. Chức năng của UART trong STM32

Hình 3. 1 Giao tiếp UART

Mọi giao tiếp hai chiều sử dụng UART đều yêu cầu tối thiểu hai chân: Chân nhận dữ liệu vào (RX)Chân truyền dữ liệu ra (TX).

  • RX (Receive Data In): Chân đầu vào này nhận dữ liệu nối tiếp. UART sử dụng kỹ thuật lấy mẫu quá mức (oversampling) để phân biệt giữa dữ liệu hợp lệ và nhiễu, giúp phục hồi dữ liệu chính xác.
  • TX (Transmit Data Out): Chân đầu ra này truyền dữ liệu nối tiếp đi. Khi bộ phát (transmitter) không hoạt động, chân TX sẽ trở về trạng thái mặc định của cổng IO. Khi bộ phát hoạt động nhưng không có gì để truyền, chân TX sẽ ở mức cao (high level).

Lưu ý trong các chế độ đặc biệt:

Trong chế độ một dây (single-wire) và chế độ thẻ thông minh (smartcard), chân TX/RX được sử dụng để truyền và nhận dữ liệu đồng thời (ở mức độ UART, dữ liệu được nhận trên chân SW_RX).

3.1. Giao tiếp UART hai chiều

Giao tiếp hai chiều tiêu chuẩn trên UART sử dụng hai chân này để truyền và nhận dữ liệu theo dạng khung (frame). Mỗi khung (frame) bao gồm:

  • Đường nhàn rỗi (Idle Line): Xuất hiện trước khi truyền hoặc nhận dữ liệu.
  • Bit bắt đầu (Start bit): Báo hiệu sự bắt đầu của một khung truyền.
  • Từ dữ liệu (Data word): hay còn gọi cách khác là ký tự (character) gồm 8 hoặc 9 bits, bit LSB được truyền trước.
  • Bit dừng (Stop bits): Gồm 0.5, 1, 1.5, hoặc 2 bit dừng, báo hiệu kết thúc của khung truyền.
  • Bộ tạo tốc độ truyền phân số: Sử dụng bộ tạo 12-bit mantissa và 4-bit fraction để thiết lập tốc độ truyền baud rate.

Các thanh ghi điều khiển:

  • Thanh ghi trạng thái (USART_SR): Chứa các cờ trạng thái của USART.
  • Thanh ghi dữ liệu (USART_DR): Dùng để truyền hoặc nhận dữ liệu.
  • Thanh ghi tốc độ truyền (USART_BRR): Thiết lập tốc độ truyền baud rate với định dạng 12-bit mantissa và 4-bit fraction.
  • Thanh ghi thời gian bảo vệ (USART_GTPR) – Chỉ có trong chế độ Thẻ thông minh (Smartcard): Dùng để thiết lập thời gian bảo vệ trong giao tiếp thẻ thông minh.

3.2. Cấu trúc gói tin UART

Độ dài từ dữ liệu (word length) trong giao tiếp UART có thể được lựa chọn là 8 hoặc 9 bits. Quá trình truyền và nhận dữ liệu được điều khiển bởi cùng một bộ tạo tốc độ baud (baud rate generator).

Hình 3. 2. Biểu diễn cấu trúc gói tin 8 bits và 9 bits qua giao tiếp UART

Hình 3. 3. Cấu hình Độ dài từ dữ liệu cho UART trong giao diện STM32CubeMX

3.3. Bộ truyền dữ liệu

Bộ phát có thể gửi các từ dữ liệu gồm 8 hoặc 9 bits.

Quá trình truyền ký tự:

  • Trong quá trình truyền UART, dữ liệu được dịch chuyển ra ngoài theo thứ tự bit ít quan trọng (LSB) trước trên chân TX.
  • Mỗi ký tự được bắt đầu bằng một bit bắt đầu mức thấp trong một khoảng thời gian bằng một chu kỳ bit.
  • Ký tự được kết thúc bằng một số bit dừng có thể cấu hình. UART hỗ trợ các bit dừng sau: 0.5, 1, 1.5 và 2 bit dừng.

Cấu hình bit dừng:

Hình 3. 4. Biễu diễn các dạng cấu hình bit dừng trong khung truyền

Hình 3. 5. Cấu hình Bit dừng cho UART trong giao diện STM32CubeMX

  • 1 stop bit: Đây là giá trị mặc định.
  • 2 stop bits: Được hỗ trợ bởi các chế độ USART thông thường, một dây và modem.
  • 0.5 stop bit: Được sử dụng khi nhận dữ liệu trong chế độ Thẻ thông minh (Smartcard).
  • 1.5 stop bits: Được sử dụng khi truyền và nhận dữ liệu trong chế độ Thẻ thông minh (Smartcard).

Truyền khung nhàn rỗi và ngắt:

  • Việc truyền khung nhàn rỗi sẽ bao gồm cả các bit dừng.
  • Truyền ngắt sẽ là 10/11 bit ở mức thấp tiếp theo là số bit dừng. Không thể truyền các ngắt dài (ngắt có độ dài lớn hơn 10/11 bit ở mức thấp).

3.4. Bộ nhận dữ liệu

UART có thể nhận các từ dữ liệu gồm 8 hoặc 9 bit. Trong UART, bit bắt đầu được phát hiện khi nhận ra một chuỗi mẫu nhất định.

Phát hiện Bit Bắt Đầu

Hình 3. 6. Quá trình phát hiện bit bắt đầu

Xung của bộ nhận nhanh hơn xung bộ phát x16 lần và cùng được tạo ra bởi cùng một bộ tạo tốc độ truyền. Điều này đảm bảo có nhiều mẫu hơn (thường là 16) trên một khoảng thời gian bit. Sơ đồ bên trên cho bạn thấy quá trình phát hiện bit bắt đầu và các điều kiện chính xác cần được đáp ứng để xác nhận một bit bắt đầu thực sự.

Nhận Dữ liệu

Trong quá trình nhận UART, dữ liệu dịch chuyển vào bit ít quan trọng nhất trước thông qua chân RX. Khi một ký tự được nhận, dữ liệu đó sẽ được dịch chuyển vào thanh ghi dịch chuyển. Nói cách khác, dữ liệu này đã được nhận và hoàn toàn có thể đọc dữ liệu.

3.5. Chức năng bộ tạo tốc độ truyền (BRG)

Fractional Fractional Baud Rate Generator (BRG) chịu trách nhiệm thiết lập tốc độ truyền (baud rate) cho cả thu (RX) và phát (TX) của UART. Tốc độ truyền này được xác định bởi giá trị Mantissa và Fraction được lập trình trong thanh ghi USARTDIV.

USARTDIV: Là một số dấu phẩy cố định không dấu được mã hóa trong thanh ghi USART_BRR.

FCK: Là xung nhịp đầu vào cho ngoại vi USART.

Mỗi khi ghi giá trị mới vào thanh ghi USART_BRR, bộ đếm tốc độ truyền sẽ được cập nhật với giá trị mới này. Do đó, không nên thay đổi giá trị của thanh ghi tốc độ truyền trong quá trình truyền thông.

Lưu ý:

  • Xung nhịp CPU càng thấp thì độ chính xác của tốc độ truyền cụ thể càng giảm. Giới hạn trên của tốc độ truyền có thể đạt được có thể được cố định bằng dữ liệu này.
  • Chỉ USART1 được clock bởi PCLK2 (tối đa 72 MHz). Các USART khác được clock bởi PCLK1 (tối đa 36 MHz) với STM32F1

Hình 3. 7. Cấu hình Tốc độ truyền dữ liệu cho UART trong giao diện STM32CubeMX

3.6. Kiểm tra kiểm soát chẵn lẻ UART (UART parity)

Kiểm soát chẵn lẻ (gồm tạo bit chẵn lẻ khi truyền và kiểm tra chẵn lẻ khi nhận) có thể được kích hoạt bằng cách thiết lập bit PCE trong thanh ghi USART_CR1. Tùy thuộc vào độ dài khung được xác định bởi bit M, các định dạng khung USART khả dụng được liệt kê trong bảng bên dưới.

Hình 3. 8. Bảng định dạng khung dữ liệu có bit và không có bit kiểm tra chẵn lẻ

Kiểm tra chẵn lẻ giúp phát hiện lỗi đơn trong quá trình truyền dữ liệu.

  • Kiểm tra chẵn lẻ Even: Bit chẵn lẻ được tính toán để có được một số chẵn các bit “1” bên trong khung được tạo bởi 7 hoặc 8 bit và bit chẵn lẻ.
  • Kiểm tra chẵn lẻ Odd: Bit chẵn lẻ được tính toán để có được một số lẻ các bit “1” bên trong khung được tạo bởi 7 hoặc 8 bit LSB và bit chẵn lẻ.

Lưu ý: Kiểm tra chẵn lẻ chỉ phát hiện được lỗi đơn và không thể sửa lỗi.

3.7. Cấu hình chế độ UART trong STM32

Hình 3. 9. Cấu hình chế độ UART trong giao diện STM32CubeMX

Để biết mô-đun UART nào trong MCU của bạn hỗ trợ các chế độ cấu hình nào, bạn cần tham khảo tài liệu kỹ thuật (datasheet) của MCU đó. Trong tài liệu này, bạn thường sẽ tìm thấy một bảng tóm tắt các chế độ được hỗ trợ bởi từng mô-đun USART, tương tự như bảng được hiển thị bên dưới.

Lưu ý: STM32F103C8T6 chỉ có 3 USART

Bảng này sẽ giúp bạn nhanh chóng xác định xem mô-đun UART cụ thể có hỗ trợ các tính năng mong muốn của bạn.

Hình 3. 10. Bảng cấu hình chế độ của mô-đun UART trong STM32F1

3.8. Mô hình giao tiếp của UART

3.8.1. UART với Polling

Polling là phương thức đơn giản nhất để giao tiếp UART, sử dụng CPU để kiểm tra liên tục trạng thái của thanh ghi cờ (flag register) của UART để xác định xem dữ liệu đã sẵn sàng để truyền hoặc nhận hay chưa.

Ưu điểm:

  • Dễ dàng triển khai.
  • Không yêu cầu cấu hình phức tạp.

Nhược điểm:

  • Gây lãng phí thời gian và tài nguyên CPU: CPU bị chặn trong khi chờ dữ liệu, không thể thực hiện các tác vụ khác.
  • Hiệu quả thấp: Không phù hợp cho các ứng dụng đòi hỏi tốc độ truyền dữ liệu cao hoặc thời gian đáp ứng nhanh.

Sử dụng: Polling phù hợp cho các ứng dụng đơn giản, không yêu cầu hiệu suất cao, ví dụ như đọc cảm biến, điều khiển LED đơn giản.

Hàm thư viện HAL cho UART với Polling:

  • Hàm truyền dữ liệu UART với Polling:
HAL_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size, uint32_t Timeout);
  • Hàm nhận dữ liệu UART với Polling:
HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);

3.8.2. UART với Ngắt luồng

Ngắt luồng sử dụng một cơ chế thông báo để CPU biết khi nào dữ liệu UART đã sẵn sàng để truyền hoặc nhận. Khi có dữ liệu, một tín hiệu ngắt được gửi đến CPU, cho phép CPU tạm dừng tác vụ hiện tại và xử lý dữ liệu UART. Sau khi xử lý xong, CPU tiếp tục thực thi tác vụ hiện tại.

Ưu điểm:

  • Hiệu quả hơn Polling: CPU chỉ bị chặn trong thời gian ngắn để xử lý dữ liệu UART, cho phép thực hiện các tác vụ khác song song.
  • Phù hợp cho các ứng dụng đòi hỏi tốc độ truyền dữ liệu cao: CPU có thể xử lý dữ liệu UART nhanh chóng và kịp thời.

Nhược điểm:

  • Yêu cầu cấu hình phức tạp hơn Polling: Cần thiết lập trình các trình xử lý ngắt để xử lý dữ liệu UART.
  • Có thể gây ra overhead: Việc xử lý ngắt có thể gây tốn thời gian và tài nguyên CPU, đặc biệt là khi có nhiều ngắt xảy ra thường xuyên.

Sử dụng: Ngắt phù hợp cho các ứng dụng đòi hỏi hiệu suất cao, thời gian đáp ứng nhanh và cần xử lý nhiều luồng dữ liệu UART.

Hàm thư viện HAL cho UART với Ngắt:

  • Hàm truyền dữ liệu UART với Ngắt:
HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size);
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {     
// Handle UART RX Interrupt Here!
}
  • Hàm nhận dữ liệu UART với Ngắt:
HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
// Handle UART RX Interrupt Here!
}  

3.8.3. UART với DMA

DMA là một bộ điều khiển chuyên dụng cho phép truyền dữ liệu trực tiếp giữa thiết bị ngoại vi và bộ nhớ mà không cần sự can thiệp của CPU. DMA sử dụng các bus DMA riêng biệt để truyền dữ liệu, giải phóng CPU để thực hiện các tác vụ khác.

Ưu điểm:

  • Hiệu quả cao nhất: DMA truyền dữ liệu trực tiếp giữa thiết bị ngoại vi và bộ nhớ, không cần CPU tham gia, giúp tiết kiệm thời gian và tài nguyên CPU.
  • Giảm tải cho CPU: CPU có thể thực hiện các tác vụ khác trong khi DMA đang truyền dữ liệu.
  • Hỗ trợ truyền dữ liệu khối lớn: DMA có thể truyền dữ liệu khối lớn hiệu quả hơn so với Polling và Ngắt.

Nhược điểm:

  • Cấu hình phức tạp nhất: Cần thiết lập trình DMA và cấu hình các kênh DMA để truyền dữ liệu.
  • Có thể không phù hợp cho các ứng dụng truyền dữ liệu nhỏ: DMA có overhead nhất định cho việc thiết lập và khởi động, do đó có thể không hiệu quả cho các truyền dữ liệu nhỏ.

Sử dụng: DMA phù hợp cho các ứng dụng đòi hỏi hiệu suất truyền dữ liệu cao nhất, cần truyền dữ liệu khối lớn liên tục và CPU cần được giải phóng để thực hiện các tác vụ khác.

Hàm thư viện HAL cho UART với DMA:

  • Hàm truyền dữ liệu UART với DMA:
HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size);  
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {     
// Handle UART Tx Comlete Interrupt Here!
}
  • Hàm nhận dữ liệu UART với DMA:
HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {     
// Handle UART Rx Comlete Interrupt Here!
}

3.9. Thực hành UART với STM32F1C8T6 và module Bluetooth HC-06

Ở ví dụ này chúng ta sử dụng Module Bluetooth HC-06 để kết nối Bluetooth với điện thoại và truyền dữ liệu Uart tới STM32F103C8T6 để điều khiển bật tắt LED.

Sơ đồ kết nối chân:

3.9.1. Cấu hình các chân

Cấu hình cho việc nạp code:

Cấu hình các chân GPIO:

Cấu hình UART: Ở đây mình chọn UART_1. Các bạn có thể sử dụng UART khác.

Trong phần Configuration → NVIC Setting để cấu hình UART mode Interrupt. Các bạn có thể truyền nhận bằng cách Polling bình thường vẫn được.

Kết quả:

3.9.2. Giải thuật sử dụng

Khai báo buffer để nhận dữ liệu Uart, ở đây ta chỉ nhận 1 byte dữ liệu duy nhất, nên không cần phải khai báo buffer dạng mảng

uint8_t data = 0;

Sau đó, ta viết hàm Callback cho Uart_1.

/* USER CODE BEGIN 0 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {   
if (huart->Instance == USART1)    {        
switch (data) {        
case '1':            
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_1);            
break;        
case '2':            
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_2);            
break;        
case '3':            
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_3);            
break;        
default:            
break;        
}        
HAL_UART_Receive_IT(&huart1, &data, 1);   
}
}
/* USER CODE END 0 */
 

– Giải thích về hàm này, chúng ta sẽ xem xem tín hiệu ngắt có đến từ UART1 hay không bằng

if (huart->Instance == USART1).

Nếu đúng, ta sẽ đọc dữ liệu của buffer phía trên và tiến hành Toggle Led tùy theo tín hiệu nhận được

⇒ Với kết nối với Module Bluetooth, truyền dữ liệu bằng UART và điều khiển động cơ bằng xung PWM (bài Timer sau). Ta có thể làm 1 mô hình xe điều khiển từ xa thông qua Bluetooth. Các bạn có thể mua linh kiện để thực hành thử.

3.9.3. Video Demo

Để có thể kết nối với Module HC-06 ta sử dụng phần mềm Arduino Bluetooth Controller trên CH Play và làm theo video hướng dẫn bên dưới.

Link tải phầm mềm tại đây.

Khi HC-06 kết nối thành công với điện thoại, nó sẽ nhận dữ liệu từ điện thoại gửi đi và gửi cho STM32F103C8T6 thông qua giao thức UART. STM32F103C8T6 nhận gói tin từ HC_06 gửi, nó sẽ đọc dữ liệu và tiến hành bật tắt LED dựa theo dữ liệu nhận được

  • Số 1 tương ứng với Led Xanh dương
  • Số 2 tương ứng với Led Đỏ
  • Số 3 tương ứng với Led Xanh lá

Demo cho quá trình thao tác tại đây.

3.10. Thực hành UART với STM32F1C8T6 và phần mềm Hercules

3.10.1. Chuẩn bị các linh kiện cần thiết

Trong nội dung thực hành lần này, chúng ta sẽ sử dụng USB to TLL CP2102 để truyền và nhận dữ liệu điều khiển LED thông qua giao tiếp UART với STM32F103C8T6 cùng với phần mềm Hercules trên máy tính.

STM32F103C8T6
3 DIODE LED 5MM 3 MÀU KHÁC NHAU
3 Trở 100 Ohm ¼ W
Dây cắm breadboard các loại
Breadboard  
USB 2.0 to TTL CP2102
STLINK-V2

Sơ đồ ghép nối:

3.10.2. Cài đặt và cấu hình phần mềm Hercules

  • Bước 1: Các bạn tải phần mềm Hercules tại đây.
  • Bước 2: Các bạn tải driver cho USB to TTL CP2102 tại đây.
  • Bước 3: Sau khi giải nén thư mục driver vừa được tải, các bạn cắm USB TTL CP2102 vào máy tính và mở Device Manerger, lúc này trong mục Other device sẽ hiện biểu tượng dấu chấm thang. Các bạn chỉ cần update driver cho USB này bằng cách trỏ tới thư mục driver vừa được giải nén. Sau khi thiết lập driver USB hoàn tất trong COM sẽ hiện ra thiết bị USB CP2102 và Cổng COM đang được kết nối

Lưu ý: Hãy nhớ số của Cổng COM này để có thể thiết lập trong phần mềm Hercules nhé!

  • Bước 4: Các bạn bật phần mềm Hercules, gắn USB CP2102 và cấu hình thông số Serial và cổng COM tương ứng với USB CP2102 ở bước trên nhé
  • Bước 5: Sau khi nhấn Open, USB CP2102 sẽ báo đèn led đỏ ngay chân GND là các bạn đã kết nối thành công rồi nhé

3.10.3. Cấu hình trong giao diện STM32CubeMX

  • Bước 1: Chọn vào Biểu tượng STM32CubeIDE trên Desktop, điền đường dẫn folder thích hợp cho Workspace và chọn Launch
  • Bước 2: Chọn ô Start new STM32 project để tao mới project
  • Bước 3: Sau khi hiện lên STM32 Project, các bạn hãy điền và chọn đúng vi xử lý STM32F103C8T6, chọn Next
  • Bước 4: Điền tên Project, tích vào các Option cần thiết và chọn Finish
  • Bước 5: Các bạn chờ để phần mềm tải Package cho STM32F1 nhé
  • Bước 6: Sau khi chờ loading lên giao diện STM32CubeMX, các bạn vào mục System Core để bật Debug bằng STLINK-V2
  • Bước 7:  Các bạn tiếp tục cấu hình các chân GPIO cho các 3 led 3 màu khác nhau nhé
  • Bước 8: Trong bước này các bạn sẽ cấu hình USART1 với chế độ Bất đồng bộ
  • Bước 9: Bật ngắt cho USART1 trong mục NVIC Settings
  • Bước 10: Nhấn Save và Generate Code. Bạn đã cấu hình thành công!

3.10.4. Code và giải thuật

Ý tưởng giải thuật: Chúng ta sẽ cho STM32F103C8T6 truyền dữ liệu màu led đang bật tắt đến phần mềm Hercules qua UART với Polling. Sử dụng UART với Ngắt để nhận dữ liệu chuyển màu khác từ máy tính.

  • Bước 1: Trong file main.c, chúng ta sẽ khai báo các buffer toàn cục để giao tiếp UART có thể truyền và nhận dữ liệu từ những buffer này.
/* USER CODE BEGIN 0 */
uint8_t rx_data[10];
uint8_t tx_data[] = "receive ok!n";
uint8_t tx_led_blue [] = "bluen";
uint8_t tx_led_green [] = "greenn";
uint8_t tx_led_red [] = "redn";
uint8_t mode = 1;
/* USER CODE END 0 */
  • Bước 2: Trong hàm main, các bạn gọi hàm nhận dữ liệu UART với ngắt trước vòng lặp while.
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart1, rx_data, sizeof(rx_data));
/* USER CODE END 2 */
  • Bước 3: Trong vòng lặp While, các bạn gọi các hàm bật tắt các chân GPIO và hàm truyền dữ liệu màu UART đến máy tính.
/* USER CODE BEGIN WHILE */  
while (1)   {        
switch (mode) {          
case 1:            
HAL_GPIO_WritePin(RED_GPIO_Port, RED_Pin, 1);            
HAL_UART_Transmit(&huart1, tx_led_red, sizeof(tx_led_red), 10);            
break;        
case 2:           
HAL_GPIO_WritePin(BLUE_GPIO_Port, BLUE_Pin, 1);           
HAL_UART_Transmit(&huart1, tx_led_blue, sizeof(tx_led_blue), 10);           
break;        
case 3:           
HAL_GPIO_WritePin(GREEN_GPIO_Port, GREEN_Pin, 1);           
HAL_UART_Transmit(&huart1, tx_led_green, sizeof(tx_led_green), 10);           
break;        
default:           
break;        
}        
HAL_Delay(1000);        
HAL_GPIO_WritePin(RED_GPIO_Port, RED_Pin, 0);        
HAL_GPIO_WritePin(BLUE_GPIO_Port, BLUE_Pin, 0);        
HAL_GPIO_WritePin(GREEN_GPIO_Port, GREEN_Pin, 0);        
HAL_Delay(1000);    
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 /
}
/ USER CODE END 3 */
  • Bước 4: Các bạn sẽ định nghĩa lại hàm Callback khi UART sẽ được nhận dữ liệu thành công.
/* USER CODE BEGIN 4 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {  
/* Prevent unused argument(s) compilation warning */  
if(huart -> Instance == huart1.Instance)   {        
HAL_UART_Transmit(huart, tx_data, sizeof(tx_data), 10);        
HAL_UART_Transmit(huart, rx_data, sizeof(rx_data), 10);        
HAL_UART_Transmit(huart, “n”, 1, 10);          
if(strcmp(rx_data, “red——-“) == 0) {              
mode = 1;        
} else if(strcmp(rx_data, “blue——“) == 0) {              
mode = 2;        
} else if(strcmp(rx_data, “green—–“) == 0) {              
mode = 3;        
}        
// Nhan chuoi ky tu moi        
// Xoa ky tu co trong chuoi        
for(uint8_t i = 0; i < sizeof(rx_data); i++) {
rx_data[i] = ”;        
}        
HAL_UART_Receive_IT(&huart1, rx_data, sizeof(rx_data));  
}  
/* NOTE: This function should not be modified, when the callback is needed,           
the HAL_UART_RxCpltCallback could be implemented in the user file    */
}
/* USER CODE END 4 */

Giải thích: Kích thước hiện tại của buffer nhận là 10 bytes, như vậy UART sẽ nhận 10 lần ký tự và sau đó UART Ngắt vào hàm Callback khi nhận dữ liệu hết đủ 10 ký tự. Nếu lần Ngắt đó là của USART1 thì ta sẽ thực hiện so sánh chuỗi tương ứng với màu khác nhau. “red——-” là chuỗi có 10 ký tự và các chuỗi khác cũng phải đảm bảo đủ 10 ký tự, nếu không sẽ xảy ra hiện tượng sai lệch chuỗi nhận và bị ghi dè lên chuối trước đó. Các hãy đảm bảo rằng chuỗi ký tự trong buffer phải được xử lý và xóa trước khi nhận một chuỗi dữ liệu mới. Trong hàm ngắt ta tiếp tục gọi thêm một hàm nhận dữ liệu UART để chờ dữ liệu được nhận một các liện tục mà không ngăn chặn CPU đang thực thi.

Các bạn xem Video Demo tại đây.

Thực hiện bài viết bởi Nguyễn Hoàng Đăng Khoa, Huỳnh Phúc Nhân