BÀI 29 – GPIO STM32F103: BRR – THANH GHI RESET VÀ MỐI QUAN HỆ VỚI BSRR
Ở Bài 28, chúng ta đã học:
GPIOx->BSRR
và biết rằng:
BSRR bit 0..15 → SET GPIO
BSRR bit 16..31 → RESET GPIO
Hôm nay chúng ta học thêm:
GPIOx->BRR
Đây là thanh ghi chuyên dùng để RESET GPIO về LOW.
1. BRR là gì?
BRR viết tắt của:
Bit Reset Register
Trong STM32F103, mỗi GPIO có thanh ghi:
GPIOA->BRR
GPIOB->BRR
GPIOC->BRR
...
Nó dùng để đưa các chân GPIO Output xuống:
LOW
2. Cách hoạt động của BRR
BRR có 16 bit điều khiển tương ứng với 16 chân GPIO:
BRR
bit 15 ................. bit 0
| |
PC15 ... PA0
Nếu:
GPIOA->BRR = (1 << 5);
thì:
PA5 = LOW
Nếu:
GPIOA->BRR = (1 << 0);
thì:
PA0 = LOW
3. So sánh BSRR và BRR
Đây là phần quan trọng nhất của bài.
| Thanh ghi | Tác dụng |
|---|---|
ODR |
Đọc/ghi trạng thái Output |
BSRR |
SET hoặc RESET GPIO |
BRR |
RESET GPIO |
Ví dụ với PA5:
SET PA5
GPIOA->BSRR = (1 << 5);
→ PA5 HIGH.
RESET PA5 bằng BSRR
GPIOA->BSRR = (1 << 21);
→ PA5 LOW.
RESET PA5 bằng BRR
GPIOA->BRR = (1 << 5);
→ PA5 LOW.
Ba thao tác trên cho kết quả tương ứng:
BSRR bit 5
↓
PA5 HIGH
BSRR bit 21
↓
PA5 LOW
BRR bit 5
↓
PA5 LOW
4. Vậy tại sao cần BRR nếu BSRR đã RESET được?
Câu hỏi rất hay.
Bạn có thể hỏi:
“Nếu
BSRRđã có phần RESET ở bit 16–31, tại sao STM32F103 còn cầnBRR?”
Về chức năng:
BSRR RESET
và:
BRR
đều có thể đưa GPIO xuống LOW.
Ví dụ:
GPIOA->BSRR = (1 << (5 + 16));
và:
GPIOA->BRR = (1 << 5);
đều làm:
PA5 = LOW
Nhưng cách biểu diễn khác nhau.
5. BRR dễ hiểu hơn khi chỉ RESET
Nếu muốn RESET PA5:
Với BSRR:
GPIOA->BSRR = (1 << (5 + 16));
Bạn phải nhớ:
RESET = pin + 16
Với BRR:
GPIOA->BRR = (1 << 5);
Rất trực tiếp:
bit 5
↓
RESET PA5
Vì vậy BRR rất dễ đọc khi bạn chỉ cần RESET GPIO.
6. Ví dụ với PC13 trên Blue Pill
Giả sử PC13 đang được cấu hình Output.
Muốn:
PC13 = LOW
ta có thể dùng:
Cách 1
GPIOC->BRR = (1 << 13);
Cách 2
GPIOC->BSRR = (1 << (13 + 16));
Hai cách đều:
PC13 = LOW
7. Nhưng nhớ LED Blue Pill là Active LOW
Đây là phần chúng ta đã nói ở bài trước.
Thông thường:
PC13 LOW → LED sáng
PC13 HIGH → LED tắt
Vì vậy:
GPIOC->BRR = (1 << 13);
không có nghĩa là:
LED OFF
mà trong trường hợp LED onboard Blue Pill thường là:
PC13 LOW
↓
LED ON
Đây là ví dụ rất tốt để phân biệt:
GPIO state
với:
Device state
8. BRR cũng có thể RESET nhiều chân
Ví dụ:
PA0
PA1
PA2
đều muốn LOW.
Ta viết:
GPIOA->BRR =
(1 << 0) |
(1 << 1) |
(1 << 2);
Hoặc:
GPIOA->BRR = 0x0007;
Kết quả:
PA0 = LOW
PA1 = LOW
PA2 = LOW
9. So sánh với BSRR
Với BSRR:
GPIOA->BSRR =
(1 << 16) |
(1 << 17) |
(1 << 18);
Với BRR:
GPIOA->BRR =
(1 << 0) |
(1 << 1) |
(1 << 2);
Cả hai đều:
PA0 = LOW
PA1 = LOW
PA2 = LOW
Nhưng BRR ngắn gọn hơn.
10. Một điểm rất quan trọng: BRR chỉ RESET
BRR không dùng để SET GPIO.
Ví dụ:
GPIOA->BRR = (1 << 5);
→ PA5 LOW.
Không có:
GPIOA->BRR = ...
nào để đưa PA5 HIGH.
Muốn HIGH, dùng:
GPIOA->BSRR = (1 << 5);
Vì vậy:
GPIO
│
┌──────┴──────┐
↓ ↓
SET RESET
│ │
BSRR BSRR
bit 0..15 bit 16..31
│
hoặc
↓
BRR
11. Viết hàm GPIO bằng BRR
Ở Bài 28, chúng ta đã viết:
void GPIO_Set(GPIO_TypeDef *GPIOx, uint8_t pin)
{
GPIOx->BSRR = (1 << pin);
}
Và:
void GPIO_Reset(GPIO_TypeDef *GPIOx, uint8_t pin)
{
GPIOx->BSRR = (1 << (pin + 16));
}
Bây giờ có thể viết GPIO_Reset() bằng BRR:
void GPIO_Reset(GPIO_TypeDef *GPIOx, uint8_t pin)
{
GPIOx->BRR = (1 << pin);
}
Dễ đọc hơn rất nhiều.
12. Vậy nên dùng BRR hay BSRR?
Đây là câu hỏi thực tế.
Nếu chỉ RESET:
GPIOx->BRR = (1 << pin);
rất rõ ràng.
Nếu muốn sử dụng một thanh ghi duy nhất để điều khiển cả:
SET
RESET
thì:
GPIOx->BSRR
rất tiện.
Ví dụ:
GPIOx->BSRR = (1 << pin); // SET
GPIOx->BSRR = (1 << (pin + 16)); // RESET
13. Tại sao BSRR rất được ưa chuộng?
Hãy tưởng tượng bạn muốn:
PA0 HIGH
PA1 LOW
Với BSRR:
GPIOA->BSRR =
(1 << 0) |
(1 << (1 + 16));
Có thể thực hiện cả hai thao tác thông qua một lần ghi BSRR.
Đây là một đặc điểm rất hữu ích khi điều khiển nhiều GPIO.
14. ODR thì sao?
Ta có thể làm:
GPIOA->ODR |= (1 << 0);
để SET PA0.
Và:
GPIOA->ODR &= ~(1 << 1);
để RESET PA1.
Nhưng đây là dạng:
READ
↓
MODIFY
↓
WRITE
Trong khi BSRR:
GPIOA->BSRR = ...
là ghi trực tiếp lệnh SET/RESET.
Đây chính là lý do BSRR rất quan trọng trong lập trình firmware.
15. Một ví dụ cực kỳ thực tế
Giả sử:
PA0 → LED đỏ
PA1 → LED xanh
Muốn:
PA0 HIGH
PA1 LOW
ta có thể viết:
GPIOA->BSRR =
(1 << 0) |
(1 << (1 + 16));
Nhưng nếu dùng BRR:
GPIOA->BSRR = (1 << 0);
GPIOA->BRR = (1 << 1);
Hai cách đều có thể tạo ra trạng thái cuối:
PA0 = HIGH
PA1 = LOW
Tuy nhiên cách BSRR đầu tiên có ưu điểm là hai thay đổi được yêu cầu trong cùng một lần ghi thanh ghi.
16. Một khái niệm mới: Atomic GPIO
Đến đây bạn nên hiểu rõ hơn từ atomic mà chúng ta bắt đầu nói ở Bài 28.
Giả sử:
GPIOA->ODR |= (1 << 0);
CPU phải:
Đọc ODR
↓
Thay đổi bit
↓
Ghi ODR
Trong khi:
GPIOA->BSRR = (1 << 0);
chỉ cần:
Ghi BSRR
↓
Phần cứng SET PA0
Điều này làm thao tác GPIO phù hợp hơn với các chương trình có:
Interrupt
Timer
DMA
RTOS
17. Một lưu ý về STM32F103
Khi học STM32, bạn sẽ gặp nhiều dòng STM32 khác nhau.
Không nên mặc định:
“Tất cả STM32 đều có thanh ghi giống hệt nhau.”
Ví dụ STM32F1 có cách tổ chức GPIO đặc trưng:
CRL
CRH
IDR
ODR
BSRR
BRR
LCKR
Trong các dòng STM32 mới hơn, cấu trúc GPIO có thể khác.
Đây là lý do khi lập trình thanh ghi, bạn phải luôn đọc Reference Manual của đúng MCU.
Với Blue Pill STM32F103C8T6, chúng ta đang học đúng cấu trúc GPIO của STM32F1.
18. Bây giờ hãy nhìn toàn bộ GPIO
Sau Bài 29, bạn đã biết:
GPIOA
│
├── CRL → cấu hình PA0...PA7
│
├── CRH → cấu hình PA8...PA15
│
├── IDR → đọc trạng thái chân
│
├── ODR → trạng thái Output
│
├── BSRR → SET + RESET
│
├── BRR → RESET
│
└── LCKR → khóa cấu hình GPIO
Đây chính là bộ khung quan trọng của GPIO STM32F103.
19. Bài tập Bài 29
Bài tập 1
Viết:
void GPIO_Reset(GPIO_TypeDef *GPIOx, uint8_t pin)
bằng BRR.
Bài tập 2
Cho:
#define LED_PIN 13
Viết lệnh làm:
PC13 LOW
bằng:
BRR
Bài tập 3
Viết lệnh làm:
PA0 = HIGH
PA1 = LOW
PA2 = HIGH
bằng một lần ghi BSRR.
Gợi ý:
SET PA0
SET PA2
RESET PA1
Bạn sẽ cần:
GPIOA->BSRR = ?
20. Kiến thức cần thuộc sau Bài 29
Bạn nên thuộc bảng này:
| Mục đích | Lệnh |
|---|---|
| SET PA5 | GPIOA->BSRR = (1 << 5); |
| RESET PA5 bằng BSRR | GPIOA->BSRR = (1 << 21); |
| RESET PA5 bằng BRR | GPIOA->BRR = (1 << 5); |
| Đọc PA5 | GPIOA->IDR & (1 << 5) |
| Đọc trạng thái Output | GPIOA->ODR & (1 << 5) |
Đặc biệt nhớ:
BSRR:
0..15 = SET
16..31 = RESET
BRR:
0..15 = RESET
🎯 Bước tiếp theo – Bài 30
Đến Bài 30, chúng ta nên ghép tất cả những gì đã học:
BÀI 30 – TỰ VIẾT GPIO DRIVER CHO STM32F103:
GPIO_Init(),GPIO_Set(),GPIO_Reset(),GPIO_Toggle()BẰNG THANH GHI, KHÔNG DÙNG HAL
Đây sẽ là bài rất quan trọng, vì từ đây bạn bắt đầu chuyển từ “học từng thanh ghi” sang “tự xây dựng thư viện GPIO của riêng mình”.