Được 👍 Chúng ta tiếp tục Bài 12 trong lộ trình học C để lập trình STM32.
🟦 BÀI 12 – struct + typedef + enum trong chương trình STM32
Ở các bài trước, bạn đã đi qua:
- Kiểu dữ liệu C
- Biến và bộ nhớ
- Toán tử
- Mảng
- Con trỏ
struct- Hàm
typedefenum
Bây giờ chúng ta ghép chúng lại. Đây là bước rất quan trọng vì từ đây bạn sẽ bắt đầu đọc được kiểu code giống firmware STM32 thực tế.
1. Tại sao STM32 cần struct?
Giả sử chúng ta có một động cơ:
int speed;
int direction;
int enabled;
Ba biến này liên quan với nhau nhưng đang nằm riêng rẽ.
Ta có thể gom chúng thành:
struct Motor
{
int speed;
int direction;
int enabled;
};
Sau đó:
struct Motor motor1;
Truy cập:
motor1.speed = 100;
motor1.direction = 1;
motor1.enabled = 1;
Ta có một đối tượng Motor chứa toàn bộ thông tin của động cơ.
2. typedef làm code dễ đọc hơn
Cách trên hơi dài:
struct Motor motor1;
Ta có thể viết:
typedef struct
{
int speed;
int direction;
int enabled;
} Motor;
Bây giờ:
Motor motor1;
Dễ đọc hơn rất nhiều.
3. enum dùng để biểu diễn trạng thái
Ví dụ động cơ có 3 trạng thái:
STOP
FORWARD
REVERSE
Không nên tùy tiện dùng:
int state;
vì người lập trình có thể ghi:
state = 123;
Trong khi 123 chẳng có ý nghĩa gì.
Ta dùng:
typedef enum
{
MOTOR_STOP,
MOTOR_FORWARD,
MOTOR_REVERSE
} MotorState;
Mặc định:
MOTOR_STOP = 0
MOTOR_FORWARD = 1
MOTOR_REVERSE = 2
4. Ghép struct + enum
Đây mới là phần quan trọng.
typedef enum
{
MOTOR_STOP,
MOTOR_FORWARD,
MOTOR_REVERSE
} MotorState;
typedef struct
{
int speed;
MotorState state;
int enabled;
} Motor;
Ta tạo động cơ:
Motor motor1;
Thiết lập:
motor1.speed = 100;
motor1.state = MOTOR_FORWARD;
motor1.enabled = 1;
5. Đọc dữ liệu
if (motor1.state == MOTOR_FORWARD)
{
// động cơ quay thuận
}
Hoặc:
if (motor1.enabled == 1)
{
// cho phép động cơ hoạt động
}
6. Đây chính là kiểu tư duy của firmware
Ví dụ STM32 có một LED:
typedef enum
{
LED_OFF,
LED_ON
} LedState;
typedef struct
{
int pin;
LedState state;
} Led;
Tạo LED:
Led led1;
Cấu hình:
led1.pin = 13;
led1.state = LED_OFF;
Bật:
led1.state = LED_ON;
Ta đã biến một nhóm biến rời rạc thành một cấu trúc quản lý thiết bị.
7. Nhưng STM32 thực tế còn mạnh hơn
Ví dụ ta có:
typedef struct
{
int pin;
int speed;
int direction;
int enabled;
} Motor;
Ta có thể tạo nhiều motor:
Motor motor1;
Motor motor2;
Motor motor3;
Hoặc:
Motor motors[3];
Khi đó:
motors[0].speed = 100;
motors[1].speed = 200;
motors[2].speed = 150;
Đây là sự kết hợp:
struct
↓
typedef
↓
array
↓
function
↓
pointer
Đây là một trong những nền tảng quan trọng nhất của lập trình embedded C.
8. struct chứa cả enum
Một ví dụ rất gần với STM32:
typedef enum
{
STATE_IDLE,
STATE_RUNNING,
STATE_ERROR
} SystemState;
typedef struct
{
SystemState state;
int temperature;
int voltage;
int current;
} System;
Tạo:
System system1;
Gán:
system1.state = STATE_RUNNING;
system1.temperature = 35;
system1.voltage = 3300;
system1.current = 120;
Ta có thể kiểm tra:
if (system1.state == STATE_ERROR)
{
// xử lý lỗi
}
9. Vì sao điều này cực kỳ quan trọng với STM32?
Trong firmware thực tế, bạn sẽ gặp rất nhiều kiểu:
typedef struct
{
...
} xxx;
Ví dụ trong HAL của STM32, bạn sẽ gặp những cấu trúc kiểu:
UART_HandleTypeDef huart1;
hoặc:
GPIO_InitTypeDef GPIO_InitStruct;
Đừng nhìn những dòng này và nghĩ:
“Cái này phức tạp quá.”
Thực chất phía dưới vẫn là những thứ bạn đang học:
typedef
+
struct
+
biến
Ví dụ về tư duy:
GPIO_InitTypeDef GPIO_InitStruct;
có thể hình dung đơn giản là:
typedef struct
{
...
} GPIO_InitTypeDef;
rồi:
GPIO_InitTypeDef GPIO_InitStruct;
tức là tạo một biến có kiểu GPIO_InitTypeDef.
10. Một điểm rất quan trọng: . và ->
Bạn đã học con trỏ ở Bài 5.
Nếu có:
Motor motor1;
thì:
motor1.speed = 100;
dùng dấu:
.
Nhưng nếu:
Motor *pMotor;
và:
pMotor = &motor1;
thì:
pMotor->speed = 100;
dùng:
->
Hai cách này tương đương về mục đích:
motor1.speed = 100;
và:
pMotor->speed = 100;
với:
pMotor = &motor1;
Đây là thứ bạn sẽ gặp rất nhiều trong STM32 HAL.
11. Một ví dụ hoàn chỉnh
Hãy đọc thật chậm đoạn này:
typedef enum
{
MOTOR_STOP,
MOTOR_FORWARD,
MOTOR_REVERSE
} MotorState;
typedef struct
{
int speed;
MotorState state;
int enabled;
} Motor;
void Motor_Start(Motor *motor)
{
motor->enabled = 1;
motor->state = MOTOR_FORWARD;
}
void Motor_Stop(Motor *motor)
{
motor->enabled = 0;
motor->state = MOTOR_STOP;
}
int main(void)
{
Motor motor1;
motor1.speed = 100;
motor1.state = MOTOR_STOP;
motor1.enabled = 0;
Motor_Start(&motor1);
return 0;
}
Hãy phân tích:
Bước 1
typedef enum
Tạo kiểu trạng thái.
Bước 2
typedef struct
Tạo kiểu Motor.
Bước 3
Motor motor1;
Tạo một motor.
Bước 4
Motor_Start(&motor1);
Truyền địa chỉ của motor1 vào hàm.
Bước 5
Trong hàm:
motor->enabled = 1;
Dùng con trỏ để thay đổi trực tiếp motor1.
12. Mô hình tư duy bạn cần nhớ
Từ bây giờ hãy hình dung:
enum
↓
mô tả các trạng thái
struct
↓
gom nhiều thông tin
typedef
↓
đặt tên kiểu dữ liệu cho dễ dùng
pointer
↓
cho phép hàm thao tác trực tiếp lên struct
function
↓
thực hiện hành động trên struct
Ghép lại:
┌──────────────┐
│ enum │
│ trạng thái │
└──────┬───────┘
↓
┌──────────────┐
│ struct │
│ thông tin │
└──────┬───────┘
↓
┌──────────────┐
│ typedef │
│ đặt tên kiểu │
└──────┬───────┘
↓
┌──────────────┐
│ pointer │
│ thao tác trực│
│ tiếp dữ liệu │
└──────┬───────┘
↓
┌──────────────┐
│ function │
│ điều khiển │
└──────────────┘
🎯 Bài tập Bài 12
Bạn hãy tự viết một chương trình C quản lý một LED STM32 với:
LED_OFF
LED_ON
LED_BLINK
Yêu cầu:
- Dùng
enumtạo 3 trạng thái trên. - Dùng
structchứa:pinstatebrightness
- Dùng
typedef. - Tạo biến:
Led led1;
- Tạo hàm:
Led_On()
Led_Off()
Led_Blink()
- Các hàm phải nhận:
Led *led
- Trong
main()hãy:- tạo
led1 - đặt pin = 13
- bật LED
- sau đó chuyển sang BLINK.
- tạo
Nếu bạn làm được bài này thì Bài 13 chúng ta sẽ bắt đầu một phần cực kỳ quan trọng: const, static, volatile — đặc biệt là volatile, thứ bạn bắt buộc phải hiểu khi lập trình thanh ghi, interrupt và ngoại vi STM32.