Skip to content

Công nghệ 9 giây

“Website hướng dẫn, thử nghiệm và giải thích công nghệ theo cách dễ hiểu cho mọi người.”

Menu
  • Home
  • Công nghệ
  • STM32
  • ESP32
  • Altium Desinger
  • Kiến thức điện máy
Menu

BÀI 26 – GPIO STM32: ĐIỀU KHIỂN NHIỀU OUTPUT & TẠO MẪU DRIVER GPIO

Posted on August 20, 2026August 23, 2026 by congnghe9s

BÀI 26 – GPIO STM32: ĐIỀU KHIỂN NHIỀU OUTPUT & TẠO MẪU DRIVER GPIO

Ở Bài 25, chúng ta đã đi từ việc hiểu GPIO Output đến điều khiển một chân GPIO. Bây giờ ta nâng lên một bước rất quan trọng:

Không viết chương trình kiểu “điều khiển từng chân một cách tùy tiện”, mà bắt đầu tổ chức GPIO thành một hệ thống có cấu trúc.

Đây là bước rất quan trọng để sau này bạn có thể viết driver cho LED, relay, motor, LCD, sensor…


1. Ôn lại GPIO Output

Ví dụ STM32F103:

GPIOA->ODR |=  (1 << 0);   // PA0 = HIGH
GPIOA->ODR &= ~(1 << 0);   // PA0 = LOW
GPIOA->ODR ^=  (1 << 0);   // PA0 đảo trạng thái

Ta có thể viết ngắn gọn:

#define LED_PIN    0

GPIOA->ODR |=  (1 << LED_PIN);
GPIOA->ODR &= ~(1 << LED_PIN);
GPIOA->ODR ^=  (1 << LED_PIN);

Nhưng nếu có 5 LED:

PA0 → LED1
PA1 → LED2
PA2 → LED3
PA3 → LED4
PA4 → LED5

thì chương trình sẽ bắt đầu trở nên khó quản lý.

Đây chính là lúc chúng ta cần hàm điều khiển GPIO.


2. Tạo hàm điều khiển LED

Thay vì viết:

GPIOA->ODR |= (1 << 0);

ta tạo:

void LED_On(void)
{
    GPIOA->ODR |= (1 << 0);
}

và:

void LED_Off(void)
{
    GPIOA->ODR &= ~(1 << 0);
}

Sau đó trong main():

LED_On();

hoặc:

LED_Off();

Điều này có vẻ đơn giản, nhưng tư duy đã thay đổi:

main()
   ↓
LED_On()
   ↓
GPIO Register
   ↓
Phần cứng

Thay vì:

main()
   ↓
đụng trực tiếp vào register

3. Tốt hơn nữa: truyền PIN vào hàm

Nếu có nhiều LED thì không nên tạo:

LED1_On();
LED2_On();
LED3_On();
LED4_On();

Ta có thể tạo:

void GPIO_Set(GPIO_TypeDef *GPIOx, uint8_t pin)
{
    GPIOx->ODR |= (1 << pin);
}

và:

void GPIO_Clear(GPIO_TypeDef *GPIOx, uint8_t pin)
{
    GPIOx->ODR &= ~(1 << pin);
}

Bây giờ:

GPIO_Set(GPIOA, 0);

nghĩa là:

PA0 = HIGH

Còn:

GPIO_Clear(GPIOA, 0);

nghĩa là:

PA0 = LOW

4. Vì sao phải truyền GPIOx?

STM32F103 có rất nhiều GPIO:

GPIOA
GPIOB
GPIOC
GPIOD
...

Ví dụ:

GPIO_Set(GPIOA, 0);

→ PA0 HIGH

GPIO_Set(GPIOA, 5);

→ PA5 HIGH

GPIO_Set(GPIOB, 3);

→ PB3 HIGH

GPIO_Set(GPIOC, 13);

→ PC13 HIGH

Như vậy một hàm có thể điều khiển rất nhiều chân GPIO.

Đây chính là tư duy của driver.


5. Tạo hàm Toggle

Ta đã học toán tử XOR ^ ở Bài 16.

GPIOx->ODR ^= (1 << pin);

Có thể tạo:

void GPIO_Toggle(GPIO_TypeDef *GPIOx, uint8_t pin)
{
    GPIOx->ODR ^= (1 << pin);
}

Sau đó:

GPIO_Toggle(GPIOC, 13);

Mỗi lần gọi:

PC13 = 0

sẽ thành:

PC13 = 1

và ngược lại.


6. Nhưng có một vấn đề

Đoạn này:

GPIOx->ODR |= (1 << pin);

có một đặc điểm:

CPU phải đọc ODR → thay đổi bit → ghi lại ODR.

STM32F1 có cách tốt hơn:

BSRR

Thanh ghi:

GPIOx->BSRR

cho phép SET hoặc RESET GPIO.

Ví dụ:

GPIOA->BSRR = (1 << 0);

→ PA0 HIGH.

Reset:

GPIOA->BSRR = (1 << (0 + 16));

→ PA0 LOW.

Tức là:

BSRR bit 0      → SET PA0
BSRR bit 16     → RESET PA0

7. Tại sao BSRR rất quan trọng?

Giả sử:

PA0 = LED
PA1 = RELAY
PA2 = MOTOR

Nếu sử dụng ODR:

GPIOA->ODR |=  (1 << 0);
GPIOA->ODR &= ~(1 << 1);

CPU thực hiện các thao tác đọc/sửa/ghi.

Trong nhiều tình huống, điều này có thể gây vấn đề khi nhiều phần mềm cùng tác động tới GPIO.

BSRR cho phép thao tác trực tiếp:

GPIOA->BSRR = (1 << 0);

và:

GPIOA->BSRR = (1 << (1 + 16));

8. Viết driver GPIO đơn giản

Ta có thể tạo:

void GPIO_WritePin(GPIO_TypeDef *GPIOx,
                   uint8_t pin,
                   uint8_t state)
{
    if (state)
    {
        GPIOx->BSRR = (1 << pin);
    }
    else
    {
        GPIOx->BSRR = (1 << (pin + 16));
    }
}

Bây giờ:

GPIO_WritePin(GPIOC, 13, 1);

→ PC13 HIGH.

GPIO_WritePin(GPIOC, 13, 0);

→ PC13 LOW.


9. Thêm Toggle

void GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint8_t pin)
{
    GPIOx->ODR ^= (1 << pin);
}

Như vậy chúng ta đã có một bộ API nhỏ:

GPIO_WritePin()
GPIO_SetPin()
GPIO_ClearPin()
GPIO_TogglePin()

Đây chính là cách tư duy để sau này xây dựng:

LED Driver
Relay Driver
Button Driver
Motor Driver
LCD Driver
UART Driver
SPI Driver
I2C Driver

10. Một khái niệm rất quan trọng: abstraction

Có hai cách viết.

Cách 1 – viết trực tiếp register

GPIOC->BSRR = (1 << 13);

Cách 2 – thông qua driver

GPIO_SetPin(GPIOC, 13);

Cách 2 có lợi thế:

Ứng dụng
   ↓
GPIO_SetPin()
   ↓
GPIO Register
   ↓
STM32 Hardware

Ứng dụng không cần biết chi tiết register bên dưới.

Đây gọi là:

Abstraction – tầng trừu tượng hóa.


11. Đây chính là tư duy lập trình STM32 chuyên nghiệp

Một chương trình lớn không nên có hàng nghìn dòng như:

GPIOA->CRL = ...
GPIOA->ODR |= ...
GPIOB->CRH = ...
GPIOC->BSRR = ...
GPIOA->ODR &= ...

rải khắp main.c.

Thay vào đó:

main.c
   │
   ├── LED
   │
   ├── BUTTON
   │
   ├── MOTOR
   │
   └── SENSOR
          │
          ↓
      GPIO Driver
          │
          ↓
      STM32 Register

Đây là nền tảng của Embedded Software Architecture.


12. Bài thực hành 26

Với Blue Pill của bạn, chúng ta có:

PC13 → LED onboard

Hãy tạo:

void GPIO_SetPin(GPIO_TypeDef *GPIOx, uint8_t pin)
{
    GPIOx->BSRR = (1 << pin);
}

void GPIO_ClearPin(GPIO_TypeDef *GPIOx, uint8_t pin)
{
    GPIOx->BSRR = (1 << (pin + 16));
}

void GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint8_t pin)
{
    GPIOx->ODR ^= (1 << pin);
}

Sau đó:

while (1)
{
    GPIO_TogglePin(GPIOC, 13);

    for (volatile uint32_t i = 0; i < 500000; i++);
}

LED PC13 sẽ nhấp nháy.


13. Điều quan trọng nhất của Bài 26

Bạn cần nắm chắc chuỗi tư duy này:

GPIO
 ↓
Port + Pin
 ↓
Register
 ↓
Bit
 ↓
Mask
 ↓
Function
 ↓
Driver
 ↓
Application

Ví dụ:

GPIOC->BSRR = (1 << 13);

không còn chỉ là một câu lệnh khó hiểu.

Bạn phải nhìn được:

GPIOC
  ↓
Port C

13
  ↓
Pin 13

1 << 13
  ↓
Mask 0x2000

BSRR
  ↓
Set bit GPIO

Kết quả
  ↓
PC13 = HIGH

🧠 Bài tập tự làm

Không xem đáp án trước, hãy tự viết 4 hàm:

GPIO_SetPin()
GPIO_ClearPin()
GPIO_TogglePin()
GPIO_ReadPin()

Trong đó hàm cuối:

GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint8_t pin)

phải đọc trạng thái chân GPIO bằng thanh ghi:

IDR

Ví dụ mục tiêu:

if (GPIO_ReadPin(GPIOA, 0))
{
    // PA0 đang HIGH
}

Bài 27 chúng ta sẽ đi tiếp vào phần rất quan trọng: GPIO Input + Pull-up/Pull-down + nút nhấn thực tế, và từ đó hiểu tại sao một chân input có thể đọc 1, 0, hoặc bị trôi (floating).

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Recent Posts

  • Bạn có thể hướng dẫn tôi quy trình sửa chữa một mainboard laptop cũng như mainboard của máy tính để bàn không?
  • Có những mức nguồn DC nào trên một bo mạch mainboard laptop?Kỹ thuật đốt dòng để kiểm tra nguồn laptop được thực hiện như thế nào?
  • bài tiếp theo hợp lý là: BÀI 63 – SENSOR FUSION THỰC CHIẾN: ENCODER + IMU MPU6050 + EKF + ODOMETRY X,Y,θ TRÊN STM32F103
  • BÀI 62 – EKF (EXTENDED KALMAN FILTER) CHO ROBOT 2 BÁNH: FUSION ENCODER + IMU + ƯỚC LƯỢNG X, Y, θ, V, ω
  • BÀI 61 – KALMAN FILTER CHO ENCODER + IMU: ƯỚC LƯỢNG TỐC ĐỘ, GÓC θ VÀ SENSOR FUSION

Recent Comments

No comments to show.

Archives

  • September 2026
  • August 2026
  • July 2026
  • June 2026
  • May 2026
  • February 2026

Categories

  • Altium Desinger
  • Công nghệ
  • ESP32
  • Khác
  • Kiến thức điện máy
  • STM32
Live 3D Globes Visitor
©2026 Công nghệ 9 giây | Design: Newspaperly WordPress Theme