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 3 – CON TRỎ, ĐỊA CHỈ VÀ CÁCH STM32 “ĐỤNG” VÀO PHẦN CỨNG

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

Được bạn. Chúng ta tiếp tục Bài 3 trong lộ trình học C cho STM32 nhé.

🟦 BÀI 3 – CON TRỎ, ĐỊA CHỈ VÀ CÁCH STM32 “ĐỤNG” VÀO PHẦN CỨNG

Ở Bài 1, chúng ta học kiểu dữ liệu và RAM.

Ở Bài 2, chúng ta học biến, toán tử, if, for, while, hàm…

Bây giờ đến một thứ cực kỳ quan trọng:

Con trỏ (pointer) và địa chỉ bộ nhớ.

Nếu bạn muốn chuyển từ kiểu lập trình Arduino sang lập trình STM32/C nhúng thực sự, đây là một trong những bài quan trọng nhất.


1. Trước hết: biến thực sự nằm ở đâu?

Ví dụ:

uint8_t led = 1;

Bạn có thể hình dung RAM như một dãy hộp:

RAM STM32

Địa chỉ       Giá trị
--------------------------------
0x20000000    ...
0x20000001    ...
0x20000002    1    ← led
0x20000003    ...

Biến led không phải chỉ là con số 1.

Nó gồm:

Tên biến       : led
Kiểu dữ liệu   : uint8_t
Giá trị        : 1
Địa chỉ        : một địa chỉ nào đó trong RAM

Ví dụ tưởng tượng:

led = 1
địa chỉ của led = 0x20000010

2. Toán tử & – lấy địa chỉ

Trong C:

&led

có nghĩa:

Lấy địa chỉ của biến led.

Ví dụ:

uint8_t led = 1;

sau đó:

printf("%p", &led);

có thể cho ra đại loại:

0x20000010

Không cần quan tâm địa chỉ cụ thể lúc này.

Điều quan trọng là:

led
 ↓
giá trị

&led
 ↓
địa chỉ

3. Con trỏ là gì?

Con trỏ là một biến dùng để chứa địa chỉ của một biến khác.

Ví dụ:

uint8_t led = 1;

uint8_t *p = &led;

Ta có:

led
┌──────────────┐
│      1       │
└──────────────┘
      ↑
      │
      │ &led
      │
p ────┘

p không chứa số 1.

p chứa địa chỉ của led.


4. Ý nghĩa của dấu *

Đây là chỗ người mới học C thường bị rối.

uint8_t *p;

Ở đây * có nghĩa:

p là một con trỏ tới uint8_t.

Nhưng:

*p

lại có nghĩa:

Đi tới địa chỉ mà p đang chứa và lấy giá trị ở đó.

Ví dụ:

uint8_t led = 1;

uint8_t *p = &led;

thì:

p

là:

địa chỉ của led

còn:

*p

là:

giá trị của led

5. Đây là công thức bạn nên nhớ

& biến
   ↓
lấy địa chỉ

* con_trỏ
   ↓
lấy giá trị tại địa chỉ

Ví dụ:

uint8_t x = 10;

uint8_t *p = &x;

Ta có:

x   = 10
&x  = địa chỉ của x
p   = địa chỉ của x
*p  = 10

6. Điều cực kỳ thú vị

Ta có thể thay đổi x thông qua p.

uint8_t x = 10;

uint8_t *p = &x;

*p = 20;

Sau đó:

x

sẽ bằng:

20

Tại sao?

Vì:

p
│
│ chứa địa chỉ của x
▼
┌─────────────┐
│ x = 10      │
└─────────────┘

Khi viết:

*p = 20;

ta nói với CPU:

“Hãy tới địa chỉ mà p đang giữ và ghi số 20 vào đó.”

Kết quả:

┌─────────────┐
│ x = 20      │
└─────────────┘

7. Đây chính là nền tảng của lập trình STM32

Bây giờ mới đến phần rất quan trọng.

Trong STM32, rất nhiều phần cứng được điều khiển bằng cách đọc/ghi vào các địa chỉ bộ nhớ đặc biệt.

Ví dụ khái niệm:

CPU
 │
 │ ghi dữ liệu
 ▼
địa chỉ đặc biệt
 │
 ▼
GPIO
 │
 ▼
chân PA0

Vì vậy lập trình STM32 thường xuyên gặp:

*address = value;

hoặc:

value = *address;

8. Ví dụ giả lập thanh ghi GPIO

Giả sử một thanh ghi nằm tại:

0x4001080C

Ta có thể hình dung:

volatile uint32_t *GPIO_REG =
    (volatile uint32_t *)0x4001080C;

Sau đó:

*GPIO_REG = 0x01;

CPU sẽ ghi:

0x00000001

vào địa chỉ:

0x4001080C

Đây chính là kiểu tư duy Register-level programming.


9. volatile là gì?

Bạn sẽ gặp từ này cực kỳ nhiều trong STM32:

volatile

Ví dụ:

volatile uint32_t *GPIO_REG;

Hiểu đơn giản trước:

volatile nói với compiler rằng giá trị này có thể thay đổi bởi một nguồn bên ngoài luồng code bình thường, nên compiler không được tự ý tối ưu việc đọc/ghi nó như một biến thông thường.

Đặc biệt quan trọng đối với:

  • GPIO
  • Timer
  • UART
  • ADC
  • Interrupt
  • DMA
  • các peripheral register

10. Tại sao STM32 cần volatile?

Ví dụ CPU đang đọc một thanh ghi:

while (UART_STATUS == 0)
{
}

Compiler có thể suy luận:

“UART_STATUS chưa thay đổi trong đoạn code này.”

Nhưng thực tế phần cứng UART có thể thay đổi thanh ghi đó bất cứ lúc nào.

Vì vậy ta dùng:

volatile

để compiler không đưa ra những tối ưu sai đối với phần cứng.


11. Một ví dụ rất dễ hiểu với LED

Giả sử:

uint8_t led = 0;

Ta tạo con trỏ:

uint8_t *p = &led;

Sau đó:

*p = 1;

thì:

led = 1

Tiếp tục:

*p = 0;

thì:

led = 0

Vậy:

p
│
│
▼
led

Con trỏ giống như một địa chỉ nhà.


12. Hãy tưởng tượng RAM là một khu phố

Ví dụ:

RAM
────────────────────────────

Nhà 1000
┌───────────┐
│ x = 25    │
└───────────┘

Nhà 2000
┌───────────┐
│ p=1000    │
└───────────┘

p chứa:

1000

nên:

*p

nghĩa là:

Đến nhà số 1000 lấy giá trị.

Do đó:

*p

→ 25

Còn:

*p = 50;

→ đến nhà 1000 và đổi thành 50.


13. Con trỏ và hàm

Đây là một lý do cực kỳ quan trọng khiến con trỏ xuất hiện nhiều trong C.

Ví dụ:

void set_value(uint8_t *p)
{
    *p = 100;
}

Sau đó:

uint8_t x = 10;

set_value(&x);

Kết quả:

x = 100

Vì ta truyền:

&x

tức là địa chỉ của x vào hàm.

Hàm nhận:

uint8_t *p

và thay đổi:

*p

14. So sánh với Arduino

Trong Arduino bạn có thể viết:

digitalWrite(LED_BUILTIN, HIGH);

Bạn không cần quan tâm:

GPIO register nằm ở đâu?
bit nào điều khiển output?
địa chỉ thanh ghi là gì?

Arduino đã che giấu phần đó.

STM32 HAL cũng che giấu rất nhiều thứ:

HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);

Nhưng bên dưới:

HAL
 ↓
GPIO driver
 ↓
Register
 ↓
Memory address
 ↓
Phần cứng GPIO
 ↓
PC13

Cho nên nếu bạn hiểu con trỏ + địa chỉ + register, bạn sẽ bắt đầu hiểu được STM32 thực sự hoạt động như thế nào.


15. Một bài tập rất quan trọng

Bạn hãy tự viết đoạn C này:

#include <stdint.h>

int main(void)
{
    uint8_t a = 10;

    uint8_t *p = &a;

    *p = 50;

    return 0;
}

Hãy tự trả lời 5 câu:

Câu 1

a

có giá trị bao nhiêu?

Câu 2

&a

là gì?

Câu 3

p

chứa gì?

Câu 4

*p

bằng bao nhiêu?

Câu 5

Sau:

*p = 50;

thì:

a

bằng bao nhiêu?

Nếu bạn trả lời được 5 câu này mà không cần nhìn tài liệu, bạn đã bắt đầu thực sự hiểu pointer.


16. Bài thực hành trên STM32 Blue Pill

Với Blue Pill của bạn, chúng ta sẽ chưa vội lao ngay vào register.

Hãy đi theo 3 tầng:

TẦNG 1
C thuần
 ↓
biến
con trỏ
địa chỉ

TẦNG 2
STM32 HAL
 ↓
GPIO
ADC
Timer
UART

TẦNG 3
STM32 Register
 ↓
RCC
GPIOx_CRL/CRH
GPIOx_ODR
GPIOx_IDR
ADC
TIM
USART

Mục tiêu cuối cùng là bạn nhìn thấy:

HAL_GPIO_WritePin(...)

và hiểu được:

“À, bên dưới nó cuối cùng cũng phải thao tác với các thanh ghi phần cứng.”


🎯 Sau Bài 3 bạn cần nhớ 7 thứ

1. Biến nằm trong bộ nhớ.

2. &x = địa chỉ của x.

3. Con trỏ là biến chứa địa chỉ.

4. *p = giá trị tại địa chỉ mà p trỏ tới.

5. *p = value → ghi value vào vùng nhớ mà p trỏ tới.

6. STM32 dùng địa chỉ bộ nhớ để truy cập peripheral/register.

7. volatile rất quan trọng khi làm việc với phần cứng.

Và có một câu cực kỳ đáng nhớ:

C cho phép bạn không chỉ thao tác với dữ liệu, mà còn thao tác trực tiếp với nơi dữ liệu đang nằm.

Đó chính là một trong những lý do C trở thành ngôn ngữ quan trọng trong embedded/STM32.

Bài tiếp theo

Bài 4 – “BIT, BYTE và REGISTER: Vì sao chỉ 1 bit cũng có thể bật/tắt một chân GPIO?”

Bài 4 sẽ nối trực tiếp kiến thức hôm nay với PC13 trên Blue Pill của bạn, và chúng ta sẽ bắt đầu nhìn một GPIO STM32 dưới dạng từng bit trong thanh ghi.

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