STM32F411 – Tìm hiểu cấu trúc và lập trình nhập xuất GPIO cơ bản

Hiểu được cấu trúc của khối GPIO và lập trình nhập xuất tín hiệu số trên các chân của một vi điều khiển với các bài tập như: điều khiển LED nhấp nháy hay đọc trạng thái nút nhấn luôn là bước đầu tiên đối với những bạn học lập trình vi điều khiển. Bài viết này được nhóm TAPIT thực hiện nhằm giúp các bạn dễ dàng hơn trong việc tiếp cận với một board mạch vi điều khiển phổ biến STM32F411DISCOVERY. 

[HỌC ONLINE: LẬP TRÌNH VI ĐIỀU KHIỂN STM32, VI XỬ LÝ ARM CORTEX – M]

Khối GPIO có thể được sử dụng để cấu hình các chân (pin) vi điều khiển thực hiện một trong các chức năng như Input, Ouput, Analog hoặc kết nối với các ngoại vi khác thông qua việc lập trình cài đặt giá trị các bit tương ứng với pin đấy của các thanh ghi trong khối GPIO. Mỗi khối GPIO có thể điều khiển được tối đa 16 I/O Pin, thường được gọi là một PORT. Vi điều khiển STM32F411VE gồm 81 I/O Pin được chia làm 6 PORT, trong đó PORTA, PORTC, PORTD, PORTE mỗi PORT có 16 I/O Pin , PORTB có 15 I/O Pin và PORTH có 2 I/O Pin.
Mặc định, nếu không cấu hình thì sau khi khởi động, các I/O Pins được cấu hình hoạt động ở chế độ Input floating.


I. Tính năng Input
Khi một chân được cấu hình để hoạt động với tính năng Input thì:
– Khối OUTPUT sẽ bị vô hiệu hóa.
– Khối TTL Schmitt trigger sẽ được sử dụng
– Các điện trở kéo lên (pull up), kéo xuống (pull down) sẽ được sử dụng tùy vào người dùng cấu hình bằng cách lập trình điều khiển giá trị bit tương ứng trên thanh ghi GPIOx_PUPDR
– Giá trị mức logic  đang có trên chân vi điều khiển sẽ được thể tại BIT tương ứng trên thanh ghi Input Data (IDR).
– Thực hiện thao tác đọc giá trị BIT của thanh ghi IDR để biết được trạng thái logic trên chân vi điều khiển.
– Lưu ý: Tránh không để chân ở trạng thái input floating, vì lúc này giá trị logic trên thanh ghi IDR sẽ thay đổi ngẫu nhiên 1 hoặc 0. Đồng thời cũng làm gia tăng năng lượng tiêu thụ của vi điều khiển. 


II. Tính năng OUPUT
Khi một I/O Pin được cấu hình hoạt động với chức năng OUTPUT thì:

Khối điều khiển OUPUT được sử dụng với các chế độ: Open drain mode hoặc Push-pull mode.
Với chế độ Open drain: Một giá trị bit bằng “0″ ở thanh ghi ODR sẽ làm N-MOS dẫn, P-MOS ngưng dẫn, lúc này chân vi điều khiển có mức logic 0 (được nối với GND); Một giá trị bit bằng “1” ở thanh ghi ORD sẽ làm ngưng dẫn cả N-MOS và P-MOS, chân tương ứng sẽ ở trạng thái Hi-Z (trở kháng cao). 
Với chế độ Push-pull:  Một giá trị bit bằng 0 ở thanh ghi ODR sẽ làm N-MOS dẫn và P-MOS ngưng dẫn, lúc này chân vi điều khiển có mức logic 0 (được nối với GND); Một giá trị bit bằng “1” ở thanh ghi ODR sẽ làm N-MOS ngưng dẫn và P-MOS dẫn. Lúc này chân vi điều khiển có mức logic 1 (được nối với VDD).
Như vậy, để  điều khiển giá trị logic của một pin được cấu hình hoạt động với chức năng OUPUT thì chúng ta cần ghi giá trị logic vào thanh ghi Ouput Data (GPIOx_ODR). Bit tương ứng của thanh ghi sẽ điều khiển pin ở vị trí tương ứng. Ví dụ: BIT thứ 0 của thanh ghi GPIOA_ODR sẽ điều khiển pin tương ứng là PA0.


III. Các tính năng ngoại vi khác
Các tính năng ngoại vi khác như Timer, I2C, SPI, UART… có thể sử dụng các chân I/O cho chức năng của ngoại vi đó. Khi cấu hình một chân hoạt động với tính năng ngoại vi khác thì:
– Khối điều khiển OUTPUT được cấu hình sử dụng với chế độ Open-drain hoặc Push-pull
– Khối điều khiển OUTPUT nhận tín hiệu từ các ngoại vi khác để điều khiển logic trên 1 chân (chứ không phải từ thanh ghi ODR)
– Khối đầu vào TTL Schmitt trigger được kích hoạt
– Trở kéo có thể được sử dụng hoặc không tùy ngoại vi
– Giá trị mức logic có đang có trên chân vi điều khiển sẽ được thể tại BIT tương ứng trên thanh ghi Input Data (IDR).
– Thực hiện tao tác đọc giá trị BIT của thanh ghi IDR để biết được trạng thái logic trên chân vi điều khiển. 


IV. Một số thanh ghi cơ bản cần quan tâm
Có nhiều cách để bạn có thể lập trình cho vi điều khiển STM32 như lập trình trực tiếp trên thanh ghi, lập trình sử dụng bộ thư viện ở cấp thấp (yêu cầu người dùng hiểu tốt về phần cứng vi điều khiển) hoặc những thư viện ở lớp cao như thư viện HAL (người dùng không cần hiểu sâu về phần cứng vi điều khiển).
Tuy nhiên, để có thể xây dựng được firmware cho STM32 một cách tốt nhất, sử dụng được chức năng debug hay can thiệp vào các hàm của thư viện hoặc kết hợp tác động trực tiếp lên các thanh ghi khi cần thiết thì các bạn cũng cần biết qua một số thanh ghi cơ bản sau của khối GPIO.
– Thanh ghi IDR: Là một thanh ghi có 32bit, tuy nhiên chỉ sử dụng 16bit thấp (vì mỗi PORT quản lý tối đa 16 Pin). Thanh ghi IDR là thanh ghi chỉ cho phép thao tác đọc (r). Giá trị các bit trong thanh ghi IDR thể hiện giá trị logic trên I/O pin tương ứng:

– Thanh ghi ODR: Là thanh ghi 32bit, tuy nhiên chỉ sử dụng 16bit thấp giống như IDR. Thanh ghi ODR cho phép người dùng có thể ghi dữ liệu (hoạt động chủ yếu) và đọc lại các giá trị đã ghi (rw). Giá trị các bit trong thanh ghi ODR sẽ điều khiển giá trị logic trên Ouput Pin tương ứng, mặc định tất cả các bit có mức logic 0.

– Thanh ghi BSRR: Là thanh ghi 32bit. Trong đó, 16 bit cao (từ BR0 – BR15) khi có mức logic 1 thì có chức năng Reset giá trị logic của bit có thứ tự tương ứng trên thanh ghi ODR (0-15), làm cho pin tương ứng có mức logic 0. 16 bit thấp (BS0 – BS15) khi có mức logic 1 thì có chức năng Set giá trị logic của bit có thứ tự tương ứng trên thanh ghi ODR (0-15), làm cho pin tương ứng có mức logic 1. Mặc định, tất cả các bit có mức logic 0. 

VI. Thư viện HAL và lập trình
Hướng dẫn thực hành này sẽ sử dụng phần cứng là Kit phát triển STM32F411DISCOVERY, với thiết kế có sẵn nút nhấn tại chân PA0, và LED tại chân PD12. Phần mềm để cấu hình và sinh code là STM32CubeMX, sau đó sử dụng thư viện HAL để code hoàn thiện và nạp code sử dụng Keil C IDE. 

– Các bước dưới đây sẽ hướng dẫn các bạn thực hành project nhấn nút, đảo trạng thái của LED. 
Bước 1: Tại ô “Part Number Search” gõ dòng chip STM32F411VE cũng là dòng chip trên Kit phát triển STM32F411DISCOVERY. Kích chọn dòng STM32F411VETx và sau đó chọn “Start Project”.

Bước 2: Chọn Serial Wire để nạp code

Bước 3: Click chuột phải vào chân PD12 sau đó tích vào ô GPIO_Output

Bước 4: Chọn chân PA0 là chân GPIO Input. 

Vì sơ đồ nguyên lý của nút nhấn  PA0 trên kit STM32F411DISCOVERY đã thiết kế sẵn trở kéo xuống nên trong CUBEMX ta chỉ cần  cấu hình No pull-up No pull-down tại chân này. 

Bước 5: Setting project và sinh code

Bước 6: Click chọn Open Project và chuyển qua tab Keil C.

Ở tab Function góc ngoài cùng bên phải chứa các hàm hỗ trợ cho việc lập trình chức năng ngoại vi trên chip STM32F411VE. Trong bài viết này ta quan tâm đến file stm32f4xx_hal_gpio.c, đây là file chứa các hàm để lập trình GPIO như:

– Hàm đọc giá trị 1 chân: HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
– Hàm ghi giá trị 1 chân: HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
– Hàm đảo giá trị 1 chân: HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)

Ý nghĩa các tham số:

GPIO_TypeDef* GPIOx: Port của chân GPIO cần sử dụng, ví dụ: GPIOA,…

– uint16_t GPIO_Pin: chân IO cần sử dụng, ví dụ: GPIO_PIN_1,..

– GPIO_PinState PinState: Giá trị logic mà bạn muốn ghi vào chân GPIO_Pin, ví dụ: GPIO_PIN_SET hoặc GPIO_PIN_RESET

Ta sẽ sử dụng hàm HAL_GPIO_ReadPin để đọc giá trị chân PA0. Vì chân PA0 đã được nối với trở kéo xuống bên ngoài nên mức logic mặc định tại chân này sẽ là mức 0 (GPIO_PIN_RESET), tuy nhiên khi người dùng nhấn nút thì chân PA0 sẽ được nối lên VCC và mức logic lúc này sẽ là 1 (GPIO_PIN_SET). Vì vậy nếu hàm HAL_GPIO_ReadPin trả về mức 1 thì nút nhấn đã được nhấn và ta thực hiện việc đảo trạng thái của Led.

 

Chúc các bạn thành công!

Nhóm TAPIT ARM R&D