Bài 4: Lập trình UART với STM32

Bài này giới thiệu với các bạn 1 chuẩn truyền thông giúp STM32 có thể giao tiếp với các thiết bị như máy tính, module ngoại vi như màn hình, moudle sim, cảm b,.. Độ phổ biến của nó ở hầu hết các dòng vi điều khiển. Trong bài này mình sẽ hướng dẫn và giúp các bạn giao tiếp UART với máy tính 1 cách đơn giản và nhanh nhất.

1. Giới thiệu về UART

UART (Universal synchronous asynchronous receiver transmitter ) là một ngoại vi cơ bản của STM32 sử dụng 2 chân Rx và Tx để nhận và truyền dữ liệu.

UART truyền dữ liệu không đồng bộ, có nghĩa là không có tín hiệu để đồng bộ hóa đầu ra của các bit từ UART truyền đến việc lấy mẫu các bit bởi UART nhận. Thay vì tín hiệu đồng bộ, UART truyền thêm các bit start và stop vào gói dữ liệu được chuyển. Các bit này xác định điểm bắt đầu và điểm kết thúc của gói dữ liệu để UART nhận biết khi nào bắt đầu đọc các bit.

Các thông số cơ bản trong truyền nhận UART :

– Baund rate (tốc độ baund): Khoảng thời gian dành cho 1 bit được truyền. Phải được cài đặt giống nhau ở gửi và nhận. Một số Baud Rate thông dụng: 9600, 38400, 115200, 230400,…

– Frame (khung truyền): Khung truyền quy định về số bit trong mỗi lần truyền.

– Start bit: là bit đầu tiên được truyền trong 1 Frame. Báo hiệu cho thiết bị nhận có một gói dữ liệu sắp đc truyền đến. Bit bắt buộc.

– Data: dữ liệu cần truyền. Bit có trọng số nhỏ nhất LSB được truyền trước sau đó đến bit MSB.

– Parity bit: kiểm tra dữ liệu truyền có đúng không.

– Stop bit: là 1 hoặc các bit báo cho thiết bị rằng các bit đã được gửi xong. Thiết bị nhận sẽ tiến hành kiểm tra khung truyền nhằm đảm bảo tính đúng đắn của dữ liệu. Bit bắt buộc.

Cách kết nối STM32 với máy tính thông qua chip cổng com

2. Cấu hình UART trong STM32 CubeIDE

Phần tạo project các bạn tham khảo lại bài 2 phần GPIO (cách tạo project, cấu hình mạch nạp, xung clock) tại đây

Trong STM32F1 có 3 kênhUSART:

USART1:

PA9———————- USART1_TX

PA10——————— USART1_RX

USART2:

PA2———————- USART2_TX

PA3———————– USART2_RX

USART3:

PB10———————- USART3_TX

PB11———————- USART3_RX

Bây giờ mình tiến hành cấu hình USART1

(1) Click vào USART1

(2) Mode: Chọn chế độ Asynchronous

(3) Tích vào USART1 global interrupt. Mình sẽ chọn dùng ngắt nhận USART

(4) Khi đó thì 2 chân PA9 và PA10 sẽ lần lượt là USART1_TX và USART1_RX

Ở phần parameters settings: Chương trình sẽ để sẵn mặc định như bên dưới.

Baud Rate:115200 bits/s

Word Length: 8 bit

Parity: None

Stop Bits:1

3. Lập trình

Khai báo rx_data để nhận dữ liệu. Khai báo tx_data[20] để chứa dữ liệu truyền.

uint8_t rx_data;
uint8_t tx_data[20]="STM32 Hello Man!!\r\n";

Truyền dữ liệu và cho phép ngắt ở lần đầu tiên khởi động:

  HAL_UART_Transmit(&huart1,tx_data, sizeof(tx_data), 100);
  HAL_UART_Receive_IT(&huart1, &rx_data, 1);

Code nhận được 1 byte từ cổng UART sau đó gửi ngược lại nằm trong chương trình ngắt:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){

	if(huart->Instance == USART1){
		HAL_UART_Transmit(&huart1,&rx_data,sizeof(rx_data), 100);
		HAL_UART_Receive_IT(&huart1, &rx_data, 1);
	}
}

Để sử dụng được hàm printf mình sẽ thêm các bước bên dưới:

Include thư viện stdio.h ở đầu chương trình

Khai báo prototypes:

/* Private function prototypes -----------------------------------------------*/
#ifdef __GNUC__
 /* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
 set to 'Yes') calls __io_putchar() */
 #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
 #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */

Nội dung hàm:

/**
 * @brief Retargets the C library printf function to the USART.
 * @param None
 * @retval None
 */
PUTCHAR_PROTOTYPE 
{
 /* Place your implementation of fputc here */
 /* e.g. write a character to the USART */
 HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 100);


 return ch;
}

Sau đó chúng ta sẽ dùng printf bình thường. VD:

printf("STM32 hello from printf\r\n");

Code chương trình main:

/* USER CODE BEGIN 0 */
uint8_t rx_data;
uint8_t tx_data[20]="STM32 Hello Man!!\r\n";

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){

	if(huart->Instance == USART1){
		HAL_UART_Transmit(&huart1,&rx_data,sizeof(rx_data), 100);
		HAL_UART_Receive_IT(&huart1, &rx_data, 1);
	}
}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  HAL_UART_Transmit(&huart1,tx_data, sizeof(tx_data), 100);
  printf("STM32 hello from printf\r\n");
  HAL_UART_Receive_IT(&huart1, &rx_data, 1);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

Một số hàm liên quan đến UART:

HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart): Chương trình ngắt

HAL_UART_Transmit(&huart1,tx_data, sizeof(tx_data), 100): hàm truyền data với các thông số:

&huart1: cổng UART

tx_data: Con trỏ dữ liệu truyền

sizeof(tx_data) :size truyền nhận

100 : thời gian timeout

HAL_UART_Receive_IT(&huart1, &rx_data, 1): Cho phép ngắt nhận

&huart1: cổng UART

&rx_data: địa chỉ biến nhận dữ liệu rx_data

1: Size dữ liệu

Để đọc serial trên mỗi hệ điều hành bạn có thể sử dụng những phần mềm sau:

  • Windows: Hercules, Serial Monitor (Arduino IDE)
  • Linux: Minicom, Serial Monitor (Arduino IDE)

Dưới đây là kết quả sau khi nạp code, trong ví dụ này mình sửa dụng Serial Monitor của Arduino IDE:

Sau đó mình truyền 1 ký tự a từ máy tính xuống, stm32 nhận được ký tự a và gửi ngược lên

Source code đính kèm: Github

Icons made by Freepik from www.flaticon.com