Lập trình STM32 GPIO là một bài nhập môn cơ bản mà ai cũng phải học khi làm quen với STM32. Nó giống như các chương trình Hello World khi học ngôn ngữ mới vậy.
Trong bài này chúng ta sẽ tìm hiểu GPIO là gì, cách hoạt động và lập trình cho GPIO trên STM32 nhé.
Bài 3 trong Serie Học lập trình STM32 từ A tới Z
GPIO là gì?
GPIO (General Purpose Input/Output) chính là các chân đầu ra hoặc đầu vào dùng chung.
Tại sao lại là dùng chung?
Bởi vì mỗi chân GPIO ngoài chức năng Input/Output còn có thêm các chức năng khác ứng với các ngoại vi khác. Ví dụ: I2C, SPI, UART, ADC… Tất cả các chân này đều có thể sử dụng như 1 cổng I/O để đọc hoặc xuất dữ liệu theo bit.
STM32 GPIO bao gồm nhiều Port, mỗi Port có tối đa 16 chân.
STM32 GPIO hoạt động như thế nào
STM32 GPIO được cấu tạo như sau:

STM32 GPIO bao gồm 2 khối cơ bản:
- Input Driver: bao gồm thanh ghi Input Data (IDR) và 1 trigger. Tín hiệu Input ngoài việc được ghi vào IDR còn theo đường Analog để vào bộ ADC, hoặc theo đường Alternate function input vào các ngoại vi khác
- Output Driver: bao gồm thanh ghi Output Data (ODR) và một khối output control để chọn tín hiệu ra lấy từ ODR hay từ các ngoại vi khác. Khối này điều khiển 2 MOSFET để tạo điện áp ra ở chân I/O
Chức năng của STM32 GPIO bao gồm:
Input:
- Input pull up: đầu vào có trở kéo lên (điện áp mặc định trên chân là Vcc)
- Input pull down: đầu vào có trở kéo xuống (điện áp mặc định trên chân là 0V)
- Input floating: đầu vào thả nổi, điện áp không cố định, dao động từ 0V tới Vcc
- Analog: đầu vào tương tự, dùng để đo ADC
Output:
- Output Push Pull: đầu ra dạng đẩy kéo, tín hiệu chỉ có Vcc hoặc 0V tương ứng với bit 1 và 0 ghi vào chân đó
- Output Open Drain: đầu ra dạng cực máng hở. Chỉ có thể kéo về 0V bằng cách ghi bit 0, khi ghi bit 1 chân IO sẽ có điện áp tương ứng với nguồn nối vào IO đó
- Alternate function Push Pull: đầu ra kiểu đẩy kéo sử dụng trong các ngoại vi
- Alternate function Open Drain: đầu ra dạng cực máng hở, sử dụng trong các ngoại vi (thường gặp trong I2C)
Cấu hình STM32 GPIO
Sau khi đã chuẩn bị đầy đủ, chúng ta bắt đầu với bài học đầu tiên khi làm quen với bất kì con chip nào, đó là nháy led.
Đầu tiên chúng ta mở phần mềm CubeMX lên, click vào Access to MCU Selector. Các bạn cũng có thể chọn Access to Board Selector nếu sử dụng các KIT của hãng.

Sau đó gõ STM32F103C8 vào ô tìm kiếm, chọn chip và nhấn Start Project.

Trong mục SYS các bạn chọn Serial Wire debug để dùng 2 chân PA13 và PA14 làm chân nạp. Nếu không chọn, mỗi khi cần nạp bạn phải giữ nút reset rồi mới nạp được.
Còn lại để mặc định.

Trong RCC chọn HSE nếu chạy bằng thạch anh ngoài, không chọn gì sẽ chạy bằng thạch anh nội.
Chọn PC13 là Output, mở tab GPIO sửa User Label là LED.

Chọn PA0 là GPIO_Input, trong GPIO chọn Pull-up, sửa label là Button.

Phần Clock Config để mặc định, đó là thạch anh nội 8MHz. Phần Project Manager gõ tên project, nơi lưu trữ, chọn Toolchain là MDK-ARM V5, sau đó nhấn Generate Code.

Lập trình GPIO STM32 chớp tắt led
Bài đầu chúng ta bắt đầu code cho led nhấp nháy với chu kì là 500ms.
Trong phần while(1) ta viết hàm:

Hàm Toggle có nhiệm vụ đảo trạng thái của chân LED, HAL_Delay(500) sẽ cho CPU chờ 500ms, cứ thế lặp đi lặp lại liên tục trong while.
Lưu ý: Các bạn phải viết code trong khoảng USER CODE BEGIN và USER CODE END nhé. Vì nếu bạn thay đổi cấu hình trong CubeMX và Gen code lại, các code nằm ngoài khoảng này đều bị xóa hết.
Sau đó các bạn click vào Flash -> Configure Flash Tools -> Debug. Trong tab Debug chọn Settings -> Flash Download, tick vào Reset and Run.


Bước này sẽ giúp MCU tự động Reset và chạy chương trình sau khi nạp nhé.
Để nạp chương trình, các bạn cắm mạch nạp vào đúng các dây 3.3V, GND, SWDIO, SWCLK, sau đó cắm vào máy tính và nhấn nút Load hoặc F8 là xong.
Lưu ý: Các bạn phải nhấn F7 hoặc biểu tượng Build để IDE biên dịch (compile) chương trình trước, không có lỗi thì mới nạp được nhé.

Hiện như thế này là nạp thành công rồi nhé.

Lập trình STM32 GPIO đọc giá trị nút nhấn và ghi lên Led
Tạo một biến u8_Status lưu giá trị nút nhấn.

Trong while chúng ta đọc giá trị nút nhấn bằng lệnh Read và ghi lên led bằng lệnh Write.

Build và nạp lên KIT, nối nút nhấn giữa chân PA0 và GND như sơ đồ dưới đây rồi nhấn thử nhé.
Vì PA0 đã bật trở kéo lên (Pull-up) nên khi chưa nhấn, chân đọc được mức 1, khi nhấn nút chân bị kéo xuống GND và đọc được mức 0. Led PC13 trên Blue Pill lại sáng khi xuất mức 0, nên kết quả là nhấn nút thì led sáng, nhả nút thì led tắt.
Xem video lập trình STM32 GPIO tại đây:
Kết
Vậy là xong bài đầu tiên về STM32 GPIO. Ở các bài tiếp theo mình sẽ hướng dẫn nhanh hơn, các thao tác cơ bản sẽ bỏ qua, nên các bạn nhớ đọc kĩ bài này để các bài sau không làm sai bước nào nhé.



mình mới học lập trình STM cho mình hỏi tại sao cần khởi tạo u8_Status để lưu giá trị nút ấn vậy
Vì bản chất hàm HAL_GPIO_ReadPin sẽ trả về 1 trong hai giá trị là GPIO_PIN_SET(nếu nút được nhấn) hoặc GPIO_PIN_RESET(nếu nút không được nhấn). u8_Status lưu giá trị này sau đó truyền xuống hàm HAL_GPIO_WritePin để bật Led tương ứng khi nhấn nút
bạn ko cần tạo biến mà ghi trực tiếp cũng đc, nó là 1 cái biến chuyển tiếp dữ liệu cho code nhìn đẹp hơn thôi.
Nếu ghi trực tiếp có thể dùng: hal_gpio_write(gpio_port_a, gpio_pin_1, hal_gpio_read(gpio_port_a, gpio_pin_2));
Bn ơi cho mình hỏi nếu muốn khi mình nhấn nút nhấn mà một hàm mình đã viết ở trên được chạy thì sẽ dùng lệnh gì nhỉ ??
Bạn gọi hàm đó trong sau phần kiểm tra nút nhấn if(button == 0)
{
function ();
}
AD giúp mình bài tập này với:
+ Viết chương trình cho 8 led được nối với port A từ A0 -> A7. Dùng 8 button ở port B từ B0, B1, B2, B7, B8, B9, B10, B11. Mỗi button điều khiển bật tắt 1 led .
+ Đếm số lần bấm : Nếu số lần bấm là chẵn thì 8 led sáng theo hiệu ứng cánh bướm. Nếu số lần bấm lẻ thì sáng dần các led từ 1 tới 8. (lưu ý delay(50ms)).
Nếu tổng số lần bấm vượt quá 15 sẽ được reset biến đếm lại 0
anh ơi em ấn go to define mà nó ghi là ‘….’ is undefined thì phải làm sao ạ
Anh ơi các hàm mình viết á, có tài liệu không anh. VD như hàm này: HAL_GPIO_ReadPin() làm sao mình biết có hàm đó để sử dụng, em có tìm mà k có, mong anh giúp cho. Thanks anh!
Em nhấn chuột phải, chọn find to define để tìm nơi lưu hàm đó nhé
anh cho em hỏi có hàm nào trong thư viện HAL mình điều khiển 1 port ko ạ
stm32 này buộc phải gọi tất cả chân ra trước rồi mới lập trình đc hay sao vậy ạ!
Tại khi gọi thêm chân ở cubemx thì code sẽ reset hết lại từ đầu.