HƯỚNG DẪN STM CƠ BẢN - B4

GIAO THỨC UART VÀ I2C VỚI STM32

Author: Pham Cong Huy

GIỚI THIỆU CHUNG

Với hầu hết các project, chúng ta sẽ cần sử dụng nhiều loại thiết bị ngoại vi. Thậm chí với các project lớn, một vi điều khiển không thể xử lý hết tất cả công việc mà cần phải nhiều hơn. Chính vì thế việc để vi điều khiển giao tiếp với các ngoại vi hoặc các vi điều khiển khác, chúng ta cần phải tìm hiểu và sử dụng các chuẩn giao thức truyển tải có dây và không dây. Trong bài viết này chúng ta sẽ cùng tìm hiểu hai giao thức có dây là UART và I2C.

GIAO THỨC UART

GIỚI THIỆU

UART (Universal Asynchronous Reciver/Transmister) là giao thức truyền thông bất đồng bộ. UART hoạt động theo mô hình điểm – điểm, tức là sẽ có hai thiết bị giao tiếp với nhau đóng vai trò là transmister và receiver.

UART là ngoại vi mà gần như tất cả vi điều khiển đều có. Bên cạnh đó, có khá nhiều module, sensor dùng UART để truyền nhận dữ liệu với vi điều khiển.

Tốc độ truyền dữ liệu của UART khá chậm, trong quá trình truyển tải dễ phát sinh lỗi.

HOẠT ĐỘNG

Vì là giao thức truyền thông bất đồng bộ nên chỉ cần có cấu hình cài đặt giống nhau thì các thiết bị có thể hiểu được nhau mà không cần xung clock.

Đối với việc cặt đặt thì thông số Baund rate là quan trọng nhất với UART. Thông số này cho ta biết được số lượng bit dữ liệu được truyền tải trong một giây và cần phải cài đặt giống nhau cho hai thiết bị tham gia giao tiếp.

Một gói tin truyền đi sẽ bao gồm Start Bit để báo hiệu việc truyền dữ liệu, khung dữ liệu gửi đi có thể được điều chỉnh từ 5 đến 8 bit (nếu không sử dụng Parity Bit thì khung dữ liệu tối đa sẽ là 9bit), 1bit Parity có nhiệm vụ kiểm tra lỗi và Stop Bit để báo hiệu kết thúc truyền.

Picture1

Để giao tiếp UART chúng ta cần kết nối đúng hai chân TX (chân truyền dữ liệu), RX (chân nhận dữ liệu). Chân TX của thiết bị này sẽ được kết nối với chân RX của thiết bị còn lại và ngược lại, bên cạnh đó hai thiết bị cần được nối chung GND (nếu không chúng sẽ không hiểu được mức logic của nhau). UART có khả năng hoạt động song công – tức là có thể truyền và nhận tín hiệu đồng thời.

CẤU HÌNH

Các bước cấu hình UART rất đơn giản. Đầu tiên hãy tạo một project mới, chọn chip STM32F103C8T6. Tại mục Connectivity chọn UARTx mà bạn muốn sử dụng.

Tại Mode chọn Asynchronous. Trong phần Configuration/Parameter Settings hãy chỉnh sửa các thông số Baud Rate, Word Length (độ dài khung dữ liệu truyển đi), Parity,

Picture2

Trong tab NVIC Settings hãy Enable cho interrupt UART. Chúng ta nên sử dụng ngắt để truyển dữ liệu vì nếu không sử dụng ngắt CPU sẽ luôn bị chiếm để gửi và nhận tín hiệu rất lãng phí. Đối với UART sử dụng ngắt thì CPU có thể làm các tác vụ khác cho đến khi có một tín hiệu ngắt (bắt đầu nhận được dữ liệu ở chân Rx, nhận tín hiệu từ một nút nhấn khi muốn gửi…) trong quá trình gửi và nhận CPU lúc này sẽ được trống.

Picture3

Sau đó hãy cấu hình các phần cơ bản khác và generate code.

UART FUNCTIONS

HAL_UART_Transmit (UART_HandleTypeDef* huart, uint8_t * pTxData, uint16_t Size, uint32_t Timeout): Đây là hàm truyền dữ liệu với 4 tham số lần lượt là kênh uart, con trỏ đến dữ liệu truyển, số lượng bytes (kí tự) truyền đi, khoảng thời gian phải hoàn thành việc gửi nếu không sẽ kết thúc.

HAL_UART_Receive (UART_HandleTypeDef * huart, uint8_t * pRxData, uint16_t Size, uint32_t Timeout): Đây là hàm nhận dữ liệu, trong đó pRxData sẽ trỏ đến nơi lưu dữ liệu được nhận.

HAL_UART_TransmitReceive (UART_HandleTypeDef * huart, uint8_t * pTxData, uint8_t * pRxData, uint16_t Size, uint32_t Timeout): Đây là hàm thực hiện song công giữa truyền và nhận.

HAL_UART_Transmit_IT(UART_HandleTypeDef* huart, uint8_t* pTxData, uint16_t Size): Hàm truyền dữ liệu ở chế độ ngắt và sẽ gọi đến hàm TxCplCallback khi hoàn thành. Trong quá trình gửi dữ liệu CPU sẽ được trống để thực hiện công việc khác.

HAL_UART_Receive_IT(UART_HandleTypeDef* huart, uint8_t* pRxData, uint16_t Size): Hàm nhận dữ liệu ở chế độ ngắt và sẽ gọi đến hàm RxCplCallBack khi hoàn thành. Trong quá trình nhận dữ liệu CPU sẽ được trống để thực hiện công việc khác.

HAL_UART_TransmitReceive_IT (UART_HandleTypeDef* huart, uint8_t* pTxData, uint8_t * pRxData, uint16_t Size): Hàm hoạt động ở chế độ song công với ngắt.

void HAL_UART_TxCpltCallback (USART_HandleTypeDef * huart): Hàm thực thi sau khi hoàn thành việc gửi dữ liệu ở hàm Transmit_IT, các công việc thực thi sẽ do người lập trình viết.

void HAL_UART_RxCpltCallback (USART_HandleTypeDef * huart): Hàm thực thi sau khi hoàn thành việc nhận dữ liệu ở hàm Receive_IT.

void HAL_UART_TxRxCpltCallback (USART_HandleTypeDef * huart): Hàm thực thi sau khi nhận hoặc gửi ở hàm TransmitReceive_IT.

VÍ DỤ

Chúng ta hãy thử một ví dụ về việc truyền dữ liệu khi nhấn nút, trong quá trình gửi thì hãy cho LED PC13 nhấp nháy còn nếu không gửi nữa thì tắt LED PC13.

Đầu tiên hãy cấu hình UART, LED PC13 và chân PA0 làm nút nhấn với ngắt ngoài.

Khai báo biến Transmid_Stt để thông báo trạng thái gửi dữ liệu. Trong hàm ngắt ngoài khi nhấn nút ta sẽ gửi dữ liệu. Sau khi gửi xong hàm Transmit_IT sẽ gọi đến hàm TxCplCallBack để thực hiện việc reset lại biến Transmit_Stt.

Picture4

Trong while(1) chúng ta sẽ kiểm tra biến Transmit_Stt để thực hiện nhấp nháy LED.

Picture5

Nạp code và kiểm tra để thấy được rằng với hàm Transmit_IT sẽ hoạt động song song với chương trình thực hiện trong vòng while(1).

Lưu ý: Để kiểm tra kết quả trên laptop, bạn cần sử dụng USB UART và phần mềm Hercules hoặc các phần mềm có terminal tương tự khác.

 

GIAO THỨC I2C

GIỚI THIỆU

I2C (Inter- Intergrated Circuit) là chuẩn truyền thông nối tiếp gồm 2 dây Clock (SCL) và Data (SDA) . Giao thức này khá phổ biến trên nhiều ngoại vi, thiết bị. Tốc độ của giao thức này không quá cao với hai chế độ 100 KHz đến 400 KHz. Có thể giao tiếp giữa nhiều thiết bị.

HOẠT ĐỘNG

Có ba kiểu kết nối chính đối với các ngoại vi trong I2C: một master – một slave, một master – nhiều slave, nhiều master – nhiều slave.

Chúng ta sẽ tìm hiểu về chế độ một master – nhiều slave với sơ đồ nối chân:

Picture6

Để tránh việc sảy ra ngắn mạch, SDA và SCL sẽ hoạt động ở chế độ cực máng hở nên các thiết bị chỉ có thể đưa hai dây này về mức điện áp 0, chứ không thể kéo lên 1.

Cơ chế truyển của I2C ở chế độ Master được minh họa như sau:

Picture7

  • Đầu tiên bộ I2C sẽ tạo ra tín hiệu start (S) bằng việc master kéo điện áp của SDA xuống mức thấp
  • Sau đó gửi địa chỉ và hướng truyền
  • Đợi bit ACK (A) từ Slave truyền cho Master
  • Ở EV5: Master gửi lại địa chỉ của Slave nhận để Slave này xác định việc nhận dữ liệu. Ở EV8_1: Master xác nhận buffer truyền vẫn còn trống. 
  • Ở EV8: Ngay khi có dữ liệu trên buffer truyền, Data sẽ ngay lập tức được gửi đến slave theo đúng địa chỉ truyền.
  • Nếu như Slave đã nhận được một frame dữ liệu, nó sẽ gửi về master một bit ACK để thông báo xác nhận. Hoạt động truyền và phản hồi ACK sẽ được lặp lại cho đến khi buffer truyền trống.
  • EV8_2 sẽ kiểm tra và xác nhận việc truyền Data đã hoàn thành.
  • Bộ I2C tạo ra tín hiệu kết thúc.

CẤU HÌNH

Tạo một project mới, và bật tính năng I2C trong mục Connectivity. Phần Master Features chúng ta có thể lựa chọn Speed Mode có hai chế độ Standard (Clock Speed 10KHz) và Fast Mode (Clock Speed 40KHz).

Picture8

Tiếp theo trong tab NVIC Settings hãy Enable cho hai tùy chọn ngắt: I2C envent interrupt – ngắt khi có sự kiện I2C, I2C error interrupt – ngắt khi có lỗi đường truyền.

Picture9

Tiếp theo có thể generate code và tiến hành lập trình. Các module và IC được trang bị chuẩn I2C như các IC thời gian thực (DS1307, DS3231), LCD dùng IC PCF8574, các cảm biến gia tôc, cảm biến góc nghiêng, các IC chuyển đổi tín hiệu, IC nhớ.

I2C FUNCTIONS

HAL_I2C_Master_Receive (I2C_HandleTypeDef * hi2c, uint16_t DevAddress, uint8_t * pData, uint16_t Size, uint32_t Timeout): Hàm gửi ở chế độ master khá tương tự UART nhưng trong đó DevAddress sẽ là địa chỉ của slave sẽ gửi dữ liệu.

HAL_I2C_Master_Receive (I2C_HandleTypeDef * hi2c, uint16_t DevAddress, uint8_t * pData, uint16_t Size, uint32_t Timeout): Hàm nhận dữ liệu ở chế độ master

HAL_I2C_Slave_Transmit (I2C_HandleTypeDef * hi2c, uint8_t * pData, uint16_t Size, uint32_t Timeout): Hàm gửi dữ liệu về master ở chế độ slave.

HAL_I2C_Slave_Receive (I2C_HandleTypeDef * hi2c, uint8_t * pData, uint16_t Size, uint32_t Timeout): Hàm nhận dữ liệu ở chế độ slave

Tương tự như UART, I2C sẽ có các hàm truyền nhận ngắt, các bạn hãy tìm hiểu thêm trong Description.

void HAL_I2C_MasterTxCpltCallback (I2C_HandleTypeDef * hi2c): Hàm xử lý khi có tín hiệu ngắt trả về từ hàm truyền ngắt của master.

void HAL_I2C_MasterRxCpltCallback (I2C_HandleTypeDef * hi2c): Hàm xử lý khi có tín hiệu ngắt trả về từ hàm nhận ngắt của master.

Tương tự cũng sẽ có những hàm xử lý ngắt truyền nhận cho slave.

HAL_I2C_IsDeviceReady (I2C_HandleTypeDef * hi2c, uint16_t DevAddress, uint32_t Trials, uint32_t Timeout): Hàm kiểm tra buffer thiết bị đã sẵn sàng cho việc truyền nhận.

HAL_I2C_Mem_Read (I2C_HandleTypeDef * hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t * pData, uint16_t Size, uint32_t Timeout): Đọc dữ liệu từ một địa chỉ bộ nhớ.

HAL_I2C_Mem_Write (I2C_HandleTypeDef * hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t * pData, uint16_t Size, uint32_t Timeout): Ghi dữ liệu vào một địa chỉ bộ nhớ.