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 27 – GPIO STM32: THANH GHI OUTPUT DATA (ODR) VÀ SET/CLEAR BIT

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

BÀI 27 – GPIO STM32: THANH GHI OUTPUT DATA (ODR) VÀ SET/CLEAR BIT

Ở Bài 26, ta đã đi đến việc điều khiển GPIO bằng cách tác động trực tiếp vào thanh ghi. Bài 27 sẽ đi sâu vào một thanh ghi cực kỳ quan trọng:

GPIOx_ODR – Output Data Register

Đây chính là nơi STM32 lưu trạng thái logic mà bạn muốn xuất ra chân GPIO.


1. Nhắc lại: GPIO thực chất hoạt động như thế nào?

Ví dụ ta dùng LED trên chân:

PC13

Khi muốn LED thay đổi trạng thái, CPU không “ra lệnh cho LED” trực tiếp.

CPU thực hiện:

CPU
 ↓
Thanh ghi GPIOC
 ↓
Mạch GPIO
 ↓
PC13
 ↓
LED

Vì vậy, lập trình GPIO thực chất là:

Đọc/ghi các bit trong những thanh ghi GPIO.


2. GPIOC_ODR là gì?

ODR viết tắt của:

Output Data Register

Có thể hiểu đơn giản:

ODR chứa giá trị logic đầu ra của các chân GPIO.

Ví dụ:

GPIOC_ODR

là thanh ghi dữ liệu đầu ra của GPIO port C.

STM32F103 có 16 chân trong mỗi GPIO port:

PC0
PC1
PC2
...
PC13
PC14
PC15

Vì vậy ODR có các bit:

Bit:  15 14 13 12 ... 2 1 0
      |  |  |  |      | | |
      PC15 PC14 PC13   PC2 PC1 PC0

Ví dụ:

GPIOC->ODR

có thể chứa:

0000 0010 0000 0000

Bit 9 = 1.

Điều đó nghĩa là:

PC9 = HIGH

3. PC13 nằm ở bit nào?

Đây là điều bạn phải nhớ.

PC13

có số chân là:

13

nên tương ứng với:

bit 13

Muốn đưa PC13 lên HIGH:

GPIOC->ODR |= (1 << 13);

Muốn đưa PC13 xuống LOW:

GPIOC->ODR &= ~(1 << 13);

Đây chính là kỹ thuật set bit và clear bit.


4. Phân tích câu lệnh SET

Ta có:

GPIOC->ODR |= (1 << 13);

Hãy tách ra.

Bước 1

1

Trong nhị phân:

0000 0000 0000 0001

Bước 2

Dịch trái 13 bit:

1 << 13

kết quả:

0010 0000 0000 0000

hay:

0x2000

Bước 3

Thực hiện OR:

GPIOC->ODR |= 0x2000;

Ví dụ ODR hiện tại:

0000 0000 0100 0010

mask:

0010 0000 0000 0000

OR:

-----------------
0010 0000 0100 0010

Bit 13 chắc chắn trở thành:

1

Các bit khác không bị thay đổi.

Đây là ưu điểm cực kỳ quan trọng của:

|=

5. CLEAR BIT

Muốn PC13 = LOW:

GPIOC->ODR &= ~(1 << 13);

Ta có:

1 << 13

=

0010 0000 0000 0000

Đảo bit:

~(1 << 13)

=

1101 1111 1111 1111

Sau đó AND:

ODR
AND
mask

Bit 13 chắc chắn trở thành:

0

Trong khi các bit khác giữ nguyên.


6. SET và CLEAR phải thuộc lòng

Bạn nên nhớ hai mẫu này:

SET bit

REG |= (1 << n);

CLEAR bit

REG &= ~(1 << n);

Ví dụ:

GPIOC->ODR |=  (1 << 13);   // PC13 = 1
GPIOC->ODR &= ~(1 << 13);   // PC13 = 0

Đây là hai mẫu câu lệnh bạn sẽ gặp rất nhiều trong lập trình STM32.


7. Tại sao không viết thế này?

Bạn có thể nghĩ:

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

Nó cũng đưa PC13 lên 1.

Nhưng có một vấn đề rất lớn.

Giả sử trước đó:

ODR:

PC15 PC14 PC13 PC12 PC11 ... PC1 PC0
 1    0    0    1    1       0   1

Nếu viết:

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

thì toàn bộ ODR sẽ trở thành:

0010 0000 0000 0000

Các bit khác bị xóa.

Trong khi:

GPIOC->ODR |= (1 << 13);

chỉ tác động vào bit 13.

Đây là sự khác biệt rất quan trọng.


8. Một ví dụ thực tế

Giả sử:

PC13 → LED
PC14 → LED2
PC15 → LED3

ODR ban đầu:

0000 0000 0000 0000

Cho PC13 HIGH:

GPIOC->ODR |= (1 << 13);

ODR:

0010 0000 0000 0000

Tiếp tục PC14 HIGH:

GPIOC->ODR |= (1 << 14);

ODR:

0110 0000 0000 0000

Bây giờ nếu clear PC13:

GPIOC->ODR &= ~(1 << 13);

ODR trở thành:

0100 0000 0000 0000

PC14 vẫn HIGH.

Đó chính là lý do phải sử dụng mask + bitwise operator.


9. Nhưng STM32 còn có thanh ghi tốt hơn ODR

Đây là phần rất quan trọng của Bài 27.

STM32F103 còn có:

GPIOx_BSRR – Bit Set/Reset Register

Ví dụ:

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

→ SET PC13.

Còn RESET PC13:

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

hay:

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

Vì:

BSRR bit 0  → SET PC0
BSRR bit 1  → SET PC1
...
BSRR bit 15 → SET PC15

BSRR bit 16 → RESET PC0
BSRR bit 17 → RESET PC1
...
BSRR bit 31 → RESET PC15

Sơ đồ:

GPIOx_BSRR

31                         16 15                         0
+----------------------------+----------------------------+
|       RESET bits            |        SET bits            |
+----------------------------+----------------------------+
        PC15 ... PC0                 PC15 ... PC0

10. ODR và BSRR khác nhau thế nào?

Thanh ghi Chức năng
ODR Dữ liệu output
BSRR Set/Reset từng bit
IDR Đọc input
CRL Cấu hình PC0–PC7
CRH Cấu hình PC8–PC15

Bạn có thể hình dung:

             GPIOC
               │
      ┌────────┼────────┐
      │        │        │
     IDR      ODR      BSRR
      │        │        │
   INPUT     OUTPUT   SET/RESET

11. Tại sao BSRR rất hay?

Ví dụ muốn bật PC13:

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

Muốn tắt:

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

Không cần:

|=

hoặc:

&=~

BSRR được thiết kế riêng cho việc set/reset GPIO.

Đặc biệt, thao tác BSRR có tính atomic ở cấp độ phần cứng, rất hữu ích khi chương trình có interrupt hoặc nhiều luồng xử lý phần cứng liên quan đến GPIO.


12. Một điểm rất quan trọng về Blue Pill PC13

Với Blue Pill thông thường, LED onboard trên:

PC13

thường là active LOW.

Có nghĩa là:

PC13 = 0 → LED sáng
PC13 = 1 → LED tắt

Do đó:

GPIOC->ODR |= (1 << 13);

không nhất thiết làm LED sáng.

Nó làm:

PC13 = HIGH

và LED onboard thường sẽ tắt.

Muốn LED sáng:

GPIOC->ODR &= ~(1 << 13);

hoặc dùng BSRR:

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

13. Chương trình thực hành Bài 27

Sau khi PC13 đã được cấu hình output, chúng ta có thể thử:

#include "stm32f1xx.h"

int main(void)
{
    // Bật clock GPIOC
    RCC->APB2ENR |= (1 << 4);

    // PC13 output push-pull, 2 MHz
    GPIOC->CRH &= ~(0xF << 20);
    GPIOC->CRH |=  (0x2 << 20);

    while (1)
    {
        // LED ON
        GPIOC->BSRR = (1 << 29);

        for (volatile int i = 0; i < 1000000; i++);

        // LED OFF
        GPIOC->BSRR = (1 << 13);

        for (volatile int i = 0; i < 1000000; i++);
    }
}

Ở đây có một điều rất đáng chú ý:

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

là:

Reset PC13

→ PC13 LOW

→ LED onboard sáng.

Còn:

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

là:

Set PC13

→ PC13 HIGH

→ LED tắt.


14. Bài tập cho bạn

Không cần HAL, hãy tự viết chương trình:

Bài tập 1

Cho:

PC13

nhấp nháy bằng:

GPIOC->ODR

Không được dùng HAL_GPIO_WritePin().


Bài tập 2

Viết lại chương trình trên nhưng dùng:

GPIOC->BSRR

Bài tập 3 – rất quan trọng

Nếu:

GPIOC->ODR = 0xA000;

hãy xác định:

PC15 = ?
PC14 = ?
PC13 = ?
PC12 = ?

Gợi ý:

0xA000
=
1010 0000 0000 0000

15. Kiến thức cần thuộc sau Bài 27

Bạn không cần học thuộc lòng hàng trăm thanh ghi, nhưng 4 mẫu sau phải thật chắc:

// SET bit
REG |= (1 << n);

// CLEAR bit
REG &= ~(1 << n);

// TEST bit
if (REG & (1 << n))
{
}

// TOGGLE bit
REG ^= (1 << n);

Đặc biệt:

|
&
^
~
<<

chính là nền tảng để bạn điều khiển register của STM32.


Lộ trình tiếp theo

Bài 28 chúng ta nên đi vào một thứ cực kỳ quan trọng:

GPIOx_BSRR – tại sao STM32 thiết kế riêng thanh ghi SET/RESET, thế nào là thao tác atomic, và tại sao BSRR thường an toàn hơn việc sửa ODR bằng |= / &=~.

Sau Bài 28, chúng ta có thể bắt đầu ghép GPIO Input + GPIO Output để làm một bài rất thực tế: nhấn nút → điều khiển LED, hoàn toàn bằng thanh ghi STM32F103, chưa cần HAL.

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