GPIO (General Purpose Input/Output) là ngoại vi cơ bản và quan trọng nhất khi bắt đầu với GPIO trong STM32 – dùng để đọc nút nhấn, điều khiển LED, tạo tín hiệu hay làm chân cho các chuẩn giao tiếp. Bài viết này hướng dẫn cấu hình GPIO bằng thư viện SPL (Standard Peripherals Firmware Library): các chế độ hoạt động, cách cấp clock RCC, struct GPIO_InitTypeDef, các hàm cơ bản và ví dụ thực hành nháy LED trên STM32F1.
Giới thiệu GPIO trong STM32 (thư viện SPL)
Trong bài này, ta cấu hình GPIO trên bộ thư viện SPL. Các hàm phục vụ việc cấu hình GPIO và cấp xung ngoại vi được định nghĩa trong stm32f10x_rcc.h và stm32f10x_gpio.h. Các cấu hình được tổ chức thành các trường và định nghĩa bằng struct và enum.
Các thanh ghi phục vụ chức năng GPIO được tổ chức trong struct GPIO_TypeDef:
typedef struct
{
__IO uint32_t CRL; /* Config Register Low (chân 0-7) */
__IO uint32_t CRH; /* Config Register High (chân 8-15) */
__IO uint32_t IDR; /* Input Data Register */
__IO uint32_t ODR; /* Output Data Register */
__IO uint32_t BSRR; /* Bit Set/Reset Register */
__IO uint32_t BRR; /* Bit Reset Register */
__IO uint32_t LCKR; /* Lock Register */
} GPIO_TypeDef;
Các thuộc tính của một chân GPIO được tổ chức trong struct GPIO_InitTypeDef, gồm ba trường: GPIO_Pin, GPIO_Mode và GPIO_Speed:
typedef struct
{
uint16_t GPIO_Pin; /* Chọn chân cần cấu hình */
GPIOSpeed_TypeDef GPIO_Speed; /* Tốc độ đáp ứng của chân */
GPIOMode_TypeDef GPIO_Mode; /* Chế độ hoạt động của chân */
} GPIO_InitTypeDef;
GPIO_Mode – các chế độ hoạt động
GPIO_Mode xác định chế độ hoạt động của chân. Dưới đây là 8 giá trị và ý nghĩa từng chế độ:
| Giá trị GPIO_Mode | Mô tả | Giải thích |
|---|---|---|
GPIO_Mode_AIN |
Analog Input | Đầu vào analog, thường dùng cho ADC. |
GPIO_Mode_IN_FLOATING |
Floating Input | Đầu vào thả nổi, không pull-up/pull-down. |
GPIO_Mode_IPD |
Input Pull-down | Đầu vào có điện trở kéo xuống; khi không có tín hiệu, chân về mức thấp (GND). |
GPIO_Mode_IPU |
Input Pull-up | Đầu vào có điện trở kéo lên; khi không có tín hiệu, chân về mức cao (VDD). |
GPIO_Mode_Out_OD |
Open-drain Output | Đầu ra open-drain; chỉ kéo xuống mức thấp, cần điện trở kéo lên ngoài để đạt mức cao. |
GPIO_Mode_Out_PP |
Push-pull Output | Đầu ra push-pull; đạt cả mức cao và thấp, không cần phần cứng bổ sung. |
GPIO_Mode_AF_OD |
Alternate Function Open-drain | Chức năng thay thế (USART, I2C…) dùng chế độ open-drain. |
GPIO_Mode_AF_PP |
Alternate Function Push-pull | Chức năng thay thế dùng chế độ push-pull. |
GPIO_Pin – chọn chân
GPIO_Pin xác định chân trong GPIOx, có dạng GPIO_Pin_x với x từ 0–15. Mỗi chân ứng với một bit trong thanh ghi 16-bit:
| Macro | Giá trị | Macro | Giá trị |
|---|---|---|---|
GPIO_Pin_0 |
0x0001 | GPIO_Pin_8 |
0x0100 |
GPIO_Pin_1 |
0x0002 | GPIO_Pin_9 |
0x0200 |
GPIO_Pin_2 |
0x0004 | GPIO_Pin_10 |
0x0400 |
GPIO_Pin_3 |
0x0008 | GPIO_Pin_11 |
0x0800 |
GPIO_Pin_4 |
0x0010 | GPIO_Pin_12 |
0x1000 |
GPIO_Pin_5 |
0x0020 | GPIO_Pin_13 |
0x2000 |
GPIO_Pin_6 |
0x0040 | GPIO_Pin_14 |
0x4000 |
GPIO_Pin_7 |
0x0080 | GPIO_Pin_15 |
0x8000 |
GPIO_Pin_All |
0xFFFF | ||
GPIO_Speed – tốc độ đáp ứng
GPIO_Speed xác định tốc độ đáp ứng của chân, thường cấu hình kèm chế độ Output:
typedef enum
{
GPIO_Speed_10MHz = 1,
GPIO_Speed_2MHz,
GPIO_Speed_50MHz
} GPIOSpeed_TypeDef;
Cấu hình RCC cấp clock cho ngoại vi
Xung clock cho ngoại vi được cấu hình bằng các hàm trong module RCC (định nghĩa trong stm32f10x_rcc.h). Hai hàm thường dùng là RCC_APB1PeriphClockCmd() và RCC_APB2PeriphClockCmd() – nhận tham số thứ nhất là macro ngoại vi, tham số thứ hai là ENABLE hoặc DISABLE:
| Hàm | Bus | Một số ngoại vi tiêu biểu |
|---|---|---|
RCC_APB1PeriphClockCmd |
APB1 (Low Speed) | TIM2–TIM7, SPI2/SPI3, USART2–5, I2C1/I2C2, USB, CAN1, PWR, DAC… |
RCC_APB2PeriphClockCmd |
APB2 (High Speed) | AFIO, GPIOA–GPIOG, ADC1/ADC2, TIM1, SPI1, USART1… |
Các cổng GPIO nằm trên bus APB2. Ví dụ để cấp clock cho GPIOC:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
Các bước khởi tạo GPIO
Hàm GPIO_Init() khởi tạo GPIOx với các tham số đã thiết lập trong GPIO_InitStruct. Quy trình khởi tạo một chân GPIO gồm ba bước: (1) cấp clock, (2) tạo struct GPIO_InitTypeDef và gán tham số, (3) gọi GPIO_Init():
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOx, ENABLE); /* 1. Cấp clock */
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_x; /* 2. Gán tham số */
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_xx;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_xx;
GPIO_Init(GPIOx, &GPIO_InitStruct); /* 3. Khởi tạo */
Các hàm cơ bản trên GPIO
Thư viện SPL hỗ trợ sẵn các hàm thao tác trên GPIO:
| Hàm | Chức năng |
|---|---|
GPIO_SetBits(GPIOx, GPIO_Pin) |
Đặt một/nhiều chân lên mức cao (logic 1). |
GPIO_ResetBits(GPIOx, GPIO_Pin) |
Đặt một/nhiều chân về mức thấp (logic 0). |
GPIO_ReadInputDataBit(GPIOx, GPIO_Pin) |
Đọc trạng thái một chân cấu hình Input. Trả về Bit_SET/Bit_RESET. |
GPIO_ReadOutputDataBit(GPIOx, GPIO_Pin) |
Đọc trạng thái một chân cấu hình Output. |
GPIO_WriteBit(GPIOx, GPIO_Pin, BitVal) |
Đặt trạng thái một chân theo BitVal (Bit_SET/Bit_RESET). |
GPIO_ReadInputData(GPIOx) |
Đọc 16-bit trạng thái toàn bộ chân Input của cổng. |
GPIO_ReadOutputData(GPIOx) |
Đọc 16-bit trạng thái toàn bộ chân Output của cổng. |
GPIO_Write(GPIOx, PortVal) |
Ghi giá trị 16-bit cho toàn bộ cổng GPIO. |
GPIO_PinLockConfig(GPIOx, GPIO_Pin) |
Khóa cấu hình chân đến khi reset hệ thống. |
GPIO_AFIODeInit() |
Đặt lại toàn bộ thanh ghi AFIO về mặc định. |
Ví dụ thực hành
Ví dụ 1: Nháy LED (Blink LED PC13)
Bật/tắt LED trên chân PC13 sau một khoảng delay, dùng thư viện SPL thay vì thao tác trực tiếp thanh ghi. Đầu tiên cấp clock cho GPIOC và cấu hình chân 13 ở chế độ Output Push-Pull:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
GPIO_InitTypeDef GPIOInitStruct;
GPIOInitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIOInitStruct.GPIO_Pin = GPIO_Pin_13;
GPIOInitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIOInitStruct);
while(1)
{
GPIO_WriteBit(GPIOC, GPIO_Pin_13, 0);
delay(2000);
GPIO_WriteBit(GPIOC, GPIO_Pin_13, 1);
delay(2000);
}
Ví dụ 2: Nháy đuổi LED
Hàm GPIO_Init() cho phép cấu hình nhiều chân cùng lúc chỉ với một lần gọi – đưa các chân vào trường GPIO_Pin bằng phép OR. Ở đây cấu hình 4 chân PB5–PB8 làm Output Push-Pull:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIOInitStruct;
GPIOInitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIOInitStruct.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8;
GPIOInitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIOInitStruct);
Hàm GPIO_Write() ghi giá trị ra cả cổng cùng lúc; bằng cách dịch bit giá trị ghi ra, ta tạo hiệu ứng nháy đuổi:
void Chasing(void)
{
uint16_t Ledval;
for(int k = 0; k < 7; k++)
{
if(!fl){ GPIO_Write(GPIOB, 0x0000); return; }
Ledval = 0x0010;
for(int i = 0; i < 5; i++)
{
Ledval = Ledval << 1;
GPIO_Write(GPIOB, Ledval);
Delay_Ms(50);
}
}
}
Các hàm GPIO còn được dùng để tạo xung, ghi dữ liệu ra chân trong các chuẩn giao tiếp, hay đọc tín hiệu điện áp trong ứng dụng ADC.
Ví dụ 3: 3 nút nhấn điều khiển 3 trạng thái LED
Dùng 3 nút nhấn (PA0, PA1, PA2) để điều khiển ba trạng thái: nháy, nháy đuổi và tắt toàn bộ. Các hàm đọc phím dùng GPIO_ReadInputDataBit():
uint8_t bt1(void){ return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0); }
uint8_t bt2(void){ return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1); }
uint8_t bt3(void){ return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_2); }
Vấn đề: trong lúc delay hoặc đang chạy hàm nháy/nháy đuổi thì không thể đọc nút. Để khắc phục, dùng thêm thư viện setjmp.h với setjmp()/longjmp() để thay đổi luồng thực thi, kèm hàm delay_And_Check() vừa delay vừa kiểm tra phím:
#include "setjmp.h"
#define BLINKLED_MODE 1
#define CHASELED_MODE 2
#define OFF_LED_MODE 3
jmp_buf buff;
void delay_And_Check(void){
for(int i = 0; i < 50000; i++){
if(bt1() == 0){ while(bt1() == 0); longjmp(buff, BLINKLED_MODE); }
if(bt2() == 0){ while(bt2() == 0); longjmp(buff, CHASELED_MODE); }
if(bt3() == 0){ while(bt3() == 0); longjmp(buff, OFF_LED_MODE); }
delay(100);
}
}
Khi nhấn PA0 sẽ nháy LED, PA1 nháy đuổi, PA2 tắt các LED. setjmp() trả về 0 khi gọi lần đầu; longjmp(buff, para) nhảy về điểm setjmp với giá trị para để chuyển trạng thái ngay cả khi đang trong vòng delay.
Kết luận
Qua bài viết, bạn đã nắm được cách làm việc với GPIO trong STM32 bằng thư viện SPL: struct GPIO_TypeDef và GPIO_InitTypeDef, tám chế độ GPIO_Mode (Input/Output, Push-Pull/Open-Drain, Analog, Alternate Function), cách cấp clock RCC (GPIO nằm trên APB2) và quy trình ba bước để khởi tạo một chân.
Cùng với các hàm cơ bản (GPIO_SetBits, GPIO_ResetBits, GPIO_WriteBit, GPIO_Write, GPIO_ReadInputDataBit…) và ba ví dụ nháy LED, bạn đã có nền tảng để điều khiển ngõ ra và đọc ngõ vào – bước khởi đầu cho mọi ứng dụng nhúng, từ đọc cảm biến đến các chuẩn giao tiếp trong automotive embedded.

