BÀI 25 – GPIO OUTPUT: ĐIỀU KHIỂN CHÂN GPIO BẰNG THANH GHI
Ở Bài 24, chúng ta đã học cách đọc GPIO Input. Bây giờ chuyển sang phần rất quan trọng:
Làm thế nào STM32 thực sự điều khiển một chân GPIO từ bên trong thanh ghi?
Đây là bài nối trực tiếp từ Memory Map → Register → GPIO mà bạn vừa học.
1. Mục tiêu Bài 25
Sau bài này bạn cần hiểu được:
- GPIO Output là gì.
- STM32 bật/tắt chân GPIO bằng thanh ghi nào.
ODRlà gì.BSRRlà gì.- Vì sao
BSRRthường tốt hơn cáchODR. - Cách bật/tắt LED PC13 bằng register-level C.
- Cách thực hiện:
GPIOC->ODR |= ...
GPIOC->ODR &= ~...
và:
GPIOC->BSRR = ...
2. GPIO Output thực chất là gì?
Ví dụ chúng ta có:
STM32
│
│ PC13
│
├────────── LED
│
└────────── ...
Khi PC13 là Output, STM32 có thể đặt chân này thành:
HIGH ≈ 3.3V
hoặc:
LOW ≈ 0V
Về bản chất:
CPU
│
▼
GPIO Register
│
▼
Mạch GPIO
│
▼
PC13
│
▼
LED
3. Có một điều cực kỳ quan trọng
GPIO không phải chỉ có một thanh ghi.
Trong STM32F103, mỗi GPIO port có nhiều register.
Ví dụ GPIOC:
GPIOC
│
├── CRL
├── CRH
├── IDR
├── ODR
├── BSRR
├── BRR
└── LCKR
Trong đó hôm nay chúng ta đặc biệt quan tâm:
ODR
BSRR
BRR
4. ODR là gì?
ODR:
Output Data Register
Có thể hiểu đơn giản:
Thanh ghi chứa giá trị Output của các chân GPIO.
GPIOC có:
PC0
PC1
PC2
...
PC13
...
PC15
ODR có 16 bit:
bit15 bit14 bit13 ... bit2 bit1 bit0
Mỗi bit tương ứng với một chân.
Ví dụ:
GPIOC->ODR
Nếu:
bit13 = 1
thì PC13 xuất mức HIGH.
Nếu:
bit13 = 0
thì PC13 xuất mức LOW.
5. Ví dụ với PC13
Giả sử:
GPIOC->ODR
đang có:
0000 0000 0000 0000
Tức:
PC13 = 0
Nếu chúng ta thực hiện:
GPIOC->ODR |= (1 << 13);
thì:
0000 0000 0000 0000
OR
0010 0000 0000 0000
-------------------
0010 0000 0000 0000
Bit 13 trở thành:
1
Do đó:
PC13 = HIGH
6. Tại sao phải dùng |=?
Không nên tùy tiện viết:
GPIOC->ODR = (1 << 13);
vì câu lệnh này thay thế toàn bộ ODR.
Ví dụ trước đó:
ODR = 0000 0000 0000 0011
Nếu:
GPIOC->ODR = (1 << 13);
kết quả:
0010 0000 0000 0000
Hai bit trước đó bị mất.
Trong khi:
GPIOC->ODR |= (1 << 13);
có nghĩa:
Giữ nguyên các bit khác, chỉ SET bit 13.
7. Tắt PC13
Muốn đưa PC13 xuống LOW:
GPIOC->ODR &= ~(1 << 13);
Phân tích:
1 << 13
tạo:
0010 0000 0000 0000
Sau đó:
~(1 << 13)
tạo:
1101 1111 1111 1111
Rồi:
ODR &= mask;
sẽ xóa bit 13.
8. Đây chính là kiến thức Bit Manipulation
Bạn vừa học ở Bài 16:
|
&
~
<<
Bây giờ nó được dùng trực tiếp với phần cứng.
SET bit
REG |= (1 << n);
CLEAR bit
REG &= ~(1 << n);
TOGGLE bit
REG ^= (1 << n);
Đây là ba mẫu câu lệnh cực kỳ quan trọng trong lập trình STM32.
9. Toggle GPIO
Muốn đảo trạng thái PC13:
GPIOC->ODR ^= (1 << 13);
Nếu:
PC13 = 0
thì thành:
PC13 = 1
Nếu:
PC13 = 1
thì thành:
PC13 = 0
Do đó:
GPIOC->ODR ^= (1 << 13);
có thể dùng để làm LED nhấp nháy.
10. Nhưng STM32F103 còn có BSRR
Đây mới là phần rất đáng chú ý.
STM32F103 có:
BSRR
Tên đầy đủ:
Bit Set/Reset Register
Nó cho phép chúng ta:
SET GPIO
hoặc:
RESET GPIO
mà không cần thao tác Read-Modify-Write giống ODR.
11. Cấu trúc BSRR
BSRR có 32 bit:
31 16 15 0
+--------------------------+--------------------------+
| RESET bits | SET bits |
+--------------------------+--------------------------+
Cụ thể:
bit 0 → set pin 0
bit 1 → set pin 1
...
bit 15 → set pin 15
và:
bit 16 → reset pin 0
bit 17 → reset pin 1
...
bit 31 → reset pin 15
12. SET PC13 bằng BSRR
PC13:
pin = 13
Muốn SET:
GPIOC->BSRR = (1 << 13);
Tức:
bit13 = 1
Kết quả:
PC13 = HIGH
13. RESET PC13 bằng BSRR
Muốn RESET PC13:
GPIOC->BSRR = (1 << (13 + 16));
hay:
GPIOC->BSRR = (1 << 29);
Bởi vì:
13 + 16 = 29
Do đó:
BSRR bit29 = 1
sẽ RESET PC13.
14. Tại sao BSRR rất hay?
So sánh:
Dùng ODR
GPIOC->ODR |= (1 << 13);
CPU phải thực hiện về bản chất:
READ ODR
↓
OR
↓
WRITE ODR
Trong khi BSRR:
GPIOC->BSRR = (1 << 13);
chỉ cần ghi lệnh SET.
Đây là một trong những lý do BSRR rất hữu ích khi điều khiển GPIO.
15. BRR là gì?
STM32F103 còn có:
BRR
Bit Reset Register.
Muốn RESET PC13:
GPIOC->BRR = (1 << 13);
Đây là cách rất dễ hiểu:
BRR bit13 = 1
↓
PC13 = LOW
Vì vậy chúng ta có thể nhớ:
BSRR lower 16 bits → SET
BSRR upper 16 bits → RESET
BRR → RESET
16. Ví dụ thực tế với LED PC13
Giả sử PC13 đã được cấu hình Output.
Code:
GPIOC->BSRR = (1 << 13);
→ PC13 HIGH.
Sau đó:
GPIOC->BRR = (1 << 13);
→ PC13 LOW.
Nếu LED được mắc theo kiểu active-low trên Blue Pill, bạn có thể thấy:
PC13 LOW → LED sáng
PC13 HIGH → LED tắt
Đây là lý do đôi khi bạn thấy code LED Blue Pill có vẻ “ngược”.
17. Một chương trình đơn giản
Sau khi GPIOC PC13 đã được cấu hình Output:
while (1)
{
GPIOC->BSRR = (1 << 13);
for (volatile int i = 0; i < 1000000; i++);
GPIOC->BRR = (1 << 13);
for (volatile int i = 0; i < 1000000; i++);
}
LED sẽ thay đổi trạng thái liên tục.
18. Nhưng có một vấn đề
Đoạn:
for (volatile int i = 0; i < 1000000; i++);
không phải delay chính xác.
Nó phụ thuộc vào:
Clock
Compiler
Optimization
CPU
Sau này chúng ta sẽ học:
SysTick
Timer
Interrupt
để tạo delay chính xác hơn.
19. Điều cực kỳ quan trọng: GPIO Output chưa đủ
Có một điều bạn phải nhớ:
Không thể chỉ ghi ODR/BSRR rồi mong chân GPIO hoạt động nếu GPIO chưa được cấu hình Output.
Quy trình phải là:
1. Bật clock GPIO
↓
2. Cấu hình MODE/CNF
↓
3. GPIO trở thành Output
↓
4. Ghi ODR/BSRR/BRR
↓
5. Chân GPIO thay đổi mức điện áp
Ví dụ:
RCC
↓
GPIOC clock
↓
GPIOC CRH
↓
PC13 Output
↓
BSRR / BRR
↓
PC13
20. Đây chính là cầu nối từ Register đến phần cứng
Bạn hãy nhìn toàn bộ chuỗi:
C code
│
│ GPIOC->BSRR = ...
▼
Register
│
│
▼
GPIO peripheral
│
│
▼
Output driver
│
│
▼
PC13 pin
│
│
▼
LED
Đây chính là cách STM32 hoạt động.
Không có phép màu nào cả.
CPU chỉ đang:
ghi các bit vào những địa chỉ thanh ghi đặc biệt của peripheral.
21. Tư duy cực kỳ quan trọng
Bạn đã đi qua:
Bài 16
BIT
BYTE
MASK
&
|
^
~
<<
↓
Bài 17
MEMORY MAP
↓
Bài 18–24
GPIO
Input
Output
Register
↓
Bài 25
ODR
BSRR
BRR
Bạn đang bắt đầu đi từ:
học C
sang:
lập trình vi điều khiển thực sự.
22. Bài tập Bài 25
Hãy thử tự viết 3 dòng sau mà không nhìn tài liệu:
Bài tập 1
SET PC13:
____________________________
Bài tập 2
RESET PC13 bằng BRR:
____________________________
Bài tập 3
TOGGLE PC13 bằng ODR:
____________________________
Đáp án:
GPIOC->BSRR = (1 << 13);
GPIOC->BRR = (1 << 13);
GPIOC->ODR ^= (1 << 13);
23. Bạn cần thuộc 5 mẫu này
Nếu mục tiêu của bạn là lập trình STM32 bằng C thật chắc, hãy thuộc:
REG |= (1 << n); // SET bit
REG &= ~(1 << n); // CLEAR bit
REG ^= (1 << n); // TOGGLE bit
REG & (1 << n); // READ bit
REG = value; // GHI toàn bộ register
Đây là những mẫu xuất hiện liên tục trong firmware STM32.
24. Tóm tắt Bài 25
ODR
│
├── bit n = 1 → Output HIGH
└── bit n = 0 → Output LOW
BSRR
│
├── bit 0..15 → SET
└── bit 16..31 → RESET
BRR
│
└── RESET GPIO
Và ba thao tác cơ bản:
GPIOC->ODR |= (1 << 13); // SET
GPIOC->ODR &= ~(1 << 13); // CLEAR
GPIOC->ODR ^= (1 << 13); // TOGGLE
Còn cách điều khiển GPIO rất hay dùng:
GPIOC->BSRR = (1 << 13); // SET PC13
GPIOC->BRR = (1 << 13); // RESET PC13
Bài tiếp theo
Bài 26 chúng ta sẽ đi sâu vào một vấn đề rất quan trọng:
GPIO Input + Output + Pull-up/Pull-down: bên trong STM32 thực sự có những transistor/mạch điện gì và tại sao
INPUT_PULLUPlại hoạt động?
Đây sẽ là bước nối từ register → GPIO → mạch điện bên trong chân STM32.