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 20 – GPIO STM32F103: TỪ REGISTER ĐẾN CHƯƠNG TRÌNH C THỰC TẾ

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

BÀI 20 – GPIO STM32F103: TỪ REGISTER ĐẾN CHƯƠNG TRÌNH C THỰC TẾ

Ở Bài 18, chúng ta đã bắt đầu đi vào GPIO.
Ở Bài 19, ta tiếp tục hiểu cách CPU tương tác với GPIO thông qua thanh ghi.

Bây giờ Bài 20 sẽ là bài rất quan trọng:

Tự cấu hình GPIO STM32F103 bằng cách thao tác trực tiếp vào REGISTER — không dùng HAL, không dùng CubeMX.

Đây chính là bước chuyển từ “biết C” → “lập trình được STM32 ở mức thanh ghi”.


1. Trước tiên: GPIO thực chất là gì?

Ví dụ ta muốn điều khiển LED trên chân:

PC13

Ta có:

CPU
 │
 │ ghi dữ liệu
 ▼
GPIOC REGISTER
 │
 ▼
PC13
 │
 ▼
LED

CPU không biết khái niệm:

digitalWrite(PC13, HIGH);

Đó là cách Arduino che giấu phần cứng.

STM32 thực sự làm việc kiểu:

GPIOC->ODR |= ...

hoặc thậm chí thấp hơn:

*(volatile uint32_t *)0x4001100C |= ...;

2. GPIOC nằm ở địa chỉ nào?

Từ Memory Map ở Bài 17, chúng ta biết vùng Peripheral bắt đầu từ:

0x40000000

GPIO của STM32F103 nằm trong vùng APB2.

Các GPIO có địa chỉ cơ sở:

GPIOA = 0x40010800
GPIOB = 0x40010C00
GPIOC = 0x40011000
GPIOD = 0x40011400
GPIOE = 0x40011800

Điều rất đáng chú ý:

GPIOA
0x40010800

GPIOB
0x40010C00

GPIOC
0x40011000

Mỗi GPIO cách nhau:

0x400

tức:

1024 bytes

3. Bên trong GPIOC có những REGISTER nào?

Ví dụ GPIOC:

GPIOC = 0x40011000

Các thanh ghi quan trọng:

Offset Register Chức năng
+0x00 CRL cấu hình PC0–PC7
+0x04 CRH cấu hình PC8–PC15
+0x08 IDR đọc trạng thái chân
+0x0C ODR xuất dữ liệu
+0x10 BSRR Set/Reset GPIO
+0x14 BRR Reset GPIO
+0x18 LCKR khóa cấu hình

Do đó:

GPIOC base
0x40011000

suy ra:

CRL = 0x40011000
CRH = 0x40011004

IDR = 0x40011008
ODR = 0x4001100C

BSRR = 0x40011010
BRR  = 0x40011014

4. PC13 dùng CRH chứ không dùng CRL

Đây là kiến thức cực kỳ quan trọng.

GPIO được chia thành:

CRL → Pin 0 → Pin 7

CRH → Pin 8 → Pin 15

Vì:

PC13

nằm trong:

PC8 → PC15

nên PC13 phải cấu hình bằng:

GPIOC->CRH

Không phải:

GPIOC->CRL

5. Mỗi GPIO chiếm 4 bit cấu hình

Đây là chỗ rất nhiều người mới học STM32 bị rối.

Trong CRH:

PC8  → 4 bit
PC9  → 4 bit
PC10 → 4 bit
PC11 → 4 bit
PC12 → 4 bit
PC13 → 4 bit
PC14 → 4 bit
PC15 → 4 bit

Tổng cộng:

8 × 4 = 32 bit

Do đó:

CRH

31                  0
┌────┬────┬────┬────┬────┬────┬────┬────┐
│15  │14  │13  │12  │11  │10  │9   │8   │
└────┴────┴────┴────┴────┴────┴────┴────┘

Mỗi pin có:

CNF[1:0]
MODE[1:0]

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

PC13 là pin số 13.

Trong CRH:

pin 8  → bit 0–3
pin 9  → bit 4–7
pin 10 → bit 8–11
pin 11 → bit 12–15
pin 12 → bit 16–19
pin 13 → bit 20–23
pin 14 → bit 24–27
pin 15 → bit 28–31

Vậy:

PC13 → bit 20–23

Trong 4 bit này:

bit 20–21 = MODE13
bit 22–23 = CNF13

7. MODE13 nghĩa là gì?

Hai bit MODE:

MODE1 MODE0

quy định tốc độ/output.

Các giá trị:

00 = Input mode

01 = Output 10 MHz

10 = Output 2 MHz

11 = Output 50 MHz

Ví dụ:

MODE13 = 01

nghĩa là:

PC13 = Output 10 MHz

8. CNF13 nghĩa là gì?

CNF xác định loại input/output.

Đối với output:

CNF = 00

là:

General purpose push-pull

Tức là GPIO thông thường:

HIGH → gần VCC
LOW  → gần GND

9. Muốn PC13 là Output Push-Pull 10 MHz

Ta cần:

MODE13 = 01
CNF13  = 00

Do đó 4 bit của PC13 là:

CNF MODE

00 01

hay:

0001

Bây giờ phải đặt 0001 vào:

bit 20–23

10. Dùng SHIFT để đưa 0001 vào đúng vị trí

Ta viết:

1 << 20

Nó tạo ra:

00000000 00010000 00000000 00000000

Tức:

bit 20 = 1

và:

bit 21 = 0
bit 22 = 0
bit 23 = 0

Đúng chính xác:

MODE13 = 01
CNF13  = 00

11. Nhưng trước khi cấu hình phải xóa 4 bit cũ

Giả sử CRH hiện tại:

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Ta không được đơn giản:

GPIOC->CRH |= (1 << 20);

bởi vì 3 bit còn lại có thể đang chứa giá trị cũ.

Ta phải:

Bước 1

Xóa bit 20–23.

Mask:

0xF << 20

Sau đó:

GPIOC->CRH &= ~(0xF << 20);

Ý nghĩa:

AND
NOT
MASK

12. Bước 2: ghi cấu hình mới

Sau khi xóa:

GPIOC->CRH &= ~(0xF << 20);

ta ghi:

GPIOC->CRH |= (1 << 20);

Kết quả:

PC13 = Output Push-Pull 10 MHz

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

Bạn có thể hỏi:

GPIOC hoạt động rồi, vậy tại sao đôi khi viết register lại không có tác dụng?

Vì GPIOC cũng phải được cấp clock.

STM32F103 có hệ thống clock cho peripheral.

GPIOC thuộc:

APB2

và clock của GPIOC được điều khiển bởi:

RCC

14. RCC nằm ở đâu?

RCC base:

0x40021000

Một register quan trọng:

RCC_APB2ENR

địa chỉ:

0x40021018

Register này chứa các bit enable clock cho các peripheral APB2.


15. Bit GPIOC nằm ở đâu?

Trong RCC_APB2ENR:

IOPAEN → GPIOA
IOPBEN → GPIOB
IOPCEN → GPIOC
IOPDEN → GPIOD
IOPEEN → GPIOE

GPIOC là:

IOPCEN = bit 4

Do đó:

RCC->APB2ENR |= (1 << 4);

16. Đây chính là quy trình STM32 thực sự

Muốn sử dụng PC13:

① Enable clock GPIOC
        ↓
② Configure PC13
        ↓
③ Write output

Cụ thể:

RCC->APB2ENR |= (1 << 4);

GPIOC->CRH &= ~(0xF << 20);
GPIOC->CRH |=  (0x1 << 20);

Sau đó:

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

hoặc:

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

17. Đây là điều bạn cần hiểu thật sâu

Khi viết:

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

CPU thực chất đang làm:

CPU
 │
 │ write
 ▼
0x4001100C
 │
 │ bit 13
 ▼
GPIOC
 │
 ▼
PC13

Nó không phải phép thuật.

Nó chỉ là:

CPU ghi một bit vào một địa chỉ bộ nhớ đặc biệt.

Đây chính là lý do chúng ta học:

Memory Map
        ↓
Address
        ↓
Register
        ↓
Bit
        ↓
GPIO

18. Vậy GPIOC->ODR thực chất là gì?

Trong CMSIS, GPIOC thường được định nghĩa dựa trên địa chỉ:

#define GPIOC ((GPIO_TypeDef *)0x40011000)

Và cấu trúc:

typedef struct
{
    volatile uint32_t CRL;
    volatile uint32_t CRH;
    volatile uint32_t IDR;
    volatile uint32_t ODR;
    volatile uint32_t BSRR;
    volatile uint32_t BRR;
    volatile uint32_t LCKR;
} GPIO_TypeDef;

Ta có:

GPIOC
 ↓
0x40011000

ODR là thành viên thứ 4:

offset = 0x0C

nên:

GPIOC->ODR

tương đương:

0x40011000 + 0x0C
=
0x4001100C

19. Đây chính là lý do phải học struct

Bạn đã học:

Bài 7  → struct
Bài 8  → function
Bài 9  → struct nâng cao
Bài 10 → enum / typedef
...
Bài 16 → bit / mask / shift
Bài 17 → Memory Map
Bài 18 → GPIO
Bài 19 → Register
Bài 20 → ghép tất cả lại

Bây giờ bạn bắt đầu thấy tại sao C lại có struct.

Ví dụ:

GPIOC->CRH
GPIOC->IDR
GPIOC->ODR
GPIOC->BSRR

thực chất là cách C giúp chúng ta mô tả một khối register phần cứng.


20. Viết chương trình PC13 bằng Register

Một phiên bản đơn giản:

#include "stm32f10x.h"

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

    // PC13 = Output Push-Pull, 10 MHz
    GPIOC->CRH &= ~(0xF << 20);
    GPIOC->CRH |=  (0x1 << 20);

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

Với Blue Pill, PC13 thường nối với LED onboard theo kiểu active-low.

Vì vậy:

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

thường làm LED:

ON

còn:

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

thường:

OFF

21. Muốn LED nhấp nháy thì sao?

Ta cần delay.

Ví dụ:

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

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

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

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

Đặt trong:

while (1)

thì:

ON
 ↓
delay
 ↓
OFF
 ↓
delay
 ↓
ON
 ↓
...

22. Nhưng có cách tốt hơn ODR

STM32F1 có:

BSRR

Bit:

0–15  → SET
16–31 → RESET

Ví dụ PC13:

Set PC13

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

Reset PC13

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

hay:

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

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

Giả sử:

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

đây là kiểu:

READ
 ↓
MODIFY
 ↓
WRITE

Trong một số tình huống nhiều code/interrupt cùng tác động GPIO, kiểu read-modify-write có thể gây vấn đề.

BSRR cho phép:

ghi trực tiếp SET hoặc RESET

Ví dụ:

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

không cần đọc ODR trước.

Đây là một trong những lý do BSRR rất quan trọng trong lập trình STM32.


24. Một bài tập cực kỳ quan trọng

Bạn hãy tự tính:

Câu 1

PC13 nằm ở:

CRL hay CRH?

Câu 2

PC13 chiếm:

bit nào đến bit nào?

Câu 3

Muốn PC13:

Output Push-Pull 10 MHz

thì:

MODE = ?
CNF  = ?

Câu 4

GPIOC clock nằm ở:

RCC_APB2ENR

bit số mấy?

Câu 5

Địa chỉ:

GPIOC->ODR

là bao nhiêu nếu:

GPIOC = 0x40011000
ODR offset = 0x0C

25. Bài tập thực hành với Blue Pill của bạn

Bạn có thể làm đúng bài này trên STM32 Blue Pill mà không cần CubeMX/HAL.

Mục tiêu:

PC13
 ↓
LED onboard
 ↓
ON 1 giây
 ↓
OFF 1 giây
 ↓
lặp lại

Nhưng trước khi chạy, hãy thử tự viết 3 dòng quan trọng nhất:

// 1. Enable clock GPIOC


// 2. Configure PC13


// 3. Điều khiển PC13

Đừng nhìn đáp án ngay.


26. Kiến thức quan trọng nhất của Bài 20

Bạn hãy ghi nhớ chuỗi này:

RCC
 ↓
Enable Clock
 ↓
GPIO Register
 ↓
CRL / CRH
 ↓
MODE + CNF
 ↓
IDR / ODR / BSRR
 ↓
GPIO PIN

Hay nói theo ngôn ngữ lập trình:

Address
   ↓
Pointer
   ↓
Struct
   ↓
Register
   ↓
Bit
   ↓
Hardware

Đây chính là nền tảng để sau này bạn có thể tự đọc Reference Manual của STM32 mà không phụ thuộc hoàn toàn vào CubeMX/HAL.


🔜 BÀI 21

Bài tiếp theo tôi đề xuất đi đúng theo lộ trình của chúng ta:

BÀI 21 – GPIO INPUT: ĐỌC NÚT NHẤN BẰNG IDR, PULL-UP/PULL-DOWN, CNF, VÀ TẠI SAO NÚT NHẤN BỊ “NHIỄU”

Ở bài đó chúng ta sẽ lấy một nút nhấn thật + LED PC13 trên chính Blue Pill của bạn, rồi tự cấu hình:

Nút nhấn
   ↓
GPIO INPUT
   ↓
IDR
   ↓
if()
   ↓
GPIO OUTPUT
   ↓
LED

và sau đó tôi sẽ chỉ cho bạn vì sao phải debounce nút nhấn — đây là lúc kiến thức C + GPIO + register bắt đầu biến thành một chương trình nhúng thực sự.

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