BÀI 10 — enum, typedef và cách STM32 viết mã như kỹ sư chuyên nghiệp
Ở 9 bài trước, bạn đã học những nền tảng quan trọng nhất của C:
Bài 1 → Kiểu dữ liệu, RAM
Bài 2 → Con trỏ
Bài 3 → if / else
Bài 4 → Vòng lặp
Bài 5 → Pointer nâng cao
Bài 6 → Mảng và con trỏ
Bài 7 → Struct
Bài 8 → Hàm
Bài 9 → volatile
Hôm nay chúng ta học một thứ xuất hiện khắp nơi trong thư viện STM32 HAL:
enumtypedef- kết hợp
enum + typedef - vì sao mã STM32 rất hay có dạng
GPIO_PinState,HAL_StatusTypeDef
1. Vấn đề đầu tiên: Đừng dùng số bí ẩn
Giả sử bạn lập trình LED.
Bạn viết:
C
int led = 1;
if(led == 1)
{
// bật LED
}
Sau 3 tháng nhìn lại…
1 nghĩa là gì?
- Bật?
- Tắt?
- Lỗi?
- Đang nhấp nháy?
Đây gọi là Magic Number.
Các kỹ sư gần như luôn tránh kiểu này.
2. enum là gì?
enum là kiểu dữ liệu liệt kê.
Nó dùng để đặt tên cho các giá trị số.
Ví dụ:
C
enum LED_STATE
{
LED_OFF,
LED_ON
};
Compiler tự hiểu:
|
Tên
|
Giá trị
|
| — | — |
|
LED_OFF
|
0
|
|
LED_ON
|
1
|
Bây giờ viết:
C
enum LED_STATE led;
led = LED_ON;
Dễ đọc hơn rất nhiều.
3. Hình dung enum trong RAM
Nhiều người mới nghĩ LED_ON là chữ.
Không phải.
Trong RAM vẫn chỉ là số.
Tên chỉ giúp con người đọc.
CPU cuối cùng vẫn xử lý số.
4. enum có thể có nhiều trạng thái
Ví dụ một động cơ.
C
enum MOTOR_STATE
{
MOTOR_STOP,
MOTOR_RUN,
MOTOR_ERROR
};
Compiler sẽ gán:
|
Tên
|
Giá trị
|
| — | — |
|
STOP
|
0
|
|
RUN
|
1
|
|
ERROR
|
2
|
Ta dùng:
C
enum MOTOR_STATE motor;
motor = MOTOR_RUN;
Sau đó:
C
if(motor == MOTOR_RUN)
{
// quay động cơ
}
Đọc cực kỳ rõ.
5. Có thể tự gán giá trị
Không bắt buộc phải bắt đầu từ 0.
C
enum STATUS
{
OK = 10,
WARNING = 20,
ERROR = 99
};
Kết quả:
|
Tên
|
Số
|
| — | — |
|
OK
|
10
|
|
WARNING
|
20
|
|
ERROR
|
99
|
Ví dụ:
C
enum STATUS st;
st = ERROR;
Trong RAM:
99
6. typedef là gì?
Đây là phần rất quan trọng.
typedef nghĩa là:
Đặt bí danh cho một kiểu dữ liệu.
Ví dụ bình thường:
C
unsigned char
rất dài.
Ta viết:
C
typedef unsigned char uint8;
Từ nay:
C
uint8 age = 25;
Thay vì:
C
unsigned char age = 25;
Hai cái hoàn toàn giống nhau.
7. Ví dụ dễ hiểu
Không dùng typedef
C
unsigned int counter;
unsigned int timer;
unsigned int speed;
Dùng typedef
C
typedef unsigned int uint32;
uint32 counter;
uint32 timer;
uint32 speed;
Đây chính là lý do trong STM32 bạn luôn gặp:
C
uint8_t
uint16_t
uint32_t
int32_t
Đó đều là các tên kiểu dữ liệu chuẩn.
8. enum + typedef
Đây là kiểu xuất hiện rất nhiều trong STM32.
Thông thường phải viết:
C
enum LED_STATE
{
LED_OFF,
LED_ON
};
enum LED_STATE led;
Hơi dài.
Ta kết hợp:
C
typedef enum
{
LED_OFF,
LED_ON
} LED_State;
Bây giờ chỉ cần:
C
LED_State led;
Rất đẹp.
9. Đây chính là phong cách HAL
Bạn từng nhìn thấy:
C
HAL_StatusTypeDef
Nó thực chất gần giống thế này.
C
typedef enum
{
HAL_OK,
HAL_ERROR,
HAL_BUSY,
HAL_TIMEOUT
} HAL_StatusTypeDef;
Khi một hàm trả về:
C
HAL_StatusTypeDef status;
Thì status chỉ có các trạng thái:
|
Giá trị
|
Ý nghĩa
|
| — | — |
|
HAL_OK
|
Thành công
|
|
HAL_ERROR
|
Lỗi
|
|
HAL_BUSY
|
Đang bận
|
|
HAL_TIMEOUT
|
Hết thời gian
|
Ví dụ:
C
status = HAL_OK;
if(status == HAL_OK)
{
// tiếp tục
}
Đây là phong cách lập trình công nghiệp.
10. Ví dụ STM32 thực tế
Giả sử Blue Pill điều khiển LED PC13.
Ta định nghĩa trạng thái LED.
C
typedef enum
{
LED_OFF,
LED_ON
} LED_State;
Tạo biến:
C
LED_State led;
Khởi tạo:
C
led = LED_OFF;
Khi nhấn nút:
C
if(button == 1)
{
led = LED_ON;
}
else
{
led = LED_OFF;
}
Sau đó xuất ra chân GPIO.
C
if(led == LED_ON)
{
HAL_GPIO_WritePin(GPIOC,
GPIO_PIN_13,
GPIO_PIN_RESET);
}
else
{
HAL_GPIO_WritePin(GPIOC,
GPIO_PIN_13,
GPIO_PIN_SET);
}
Lưu ý:
Blue Pill thường dùng LED Active LOW.
Nghĩa là:
|
Trạng thái mong muốn
|
Mức chân PC13
|
| — | — |
|
LED sáng
|
RESET
|
|
LED tắt
|
SET
|
Đây là phần cứng, không phải do enum.
11. Một ví dụ lớn hơn: Máy giặt
Bạn từng hỏi về công việc lập trình máy giặt bằng C/C++.
Đây chính là cách họ viết.
C
typedef enum
{
IDLE,
FILL_WATER,
WASH,
RINSE,
SPIN,
FINISH,
ERROR
} WashState;
Tạo biến:
C
WashState machine;
Ban đầu:
C
machine = IDLE;
Sau đó chương trình chạy.
Khi cảm biến báo đủ nước:
C
machine = WASH;
Khi giặt xong:
C
machine = RINSE;
Khi mất tín hiệu cảm biến:
C
machine = ERROR;
Bạn thấy ngay lợi ích của enum:
Không còn nhớ số 0,1,2,3,4...
Chỉ đọc tên trạng thái.
12. Kết hợp với switch
enum thường đi cùng switch.
Ví dụ:
C
switch(machine)
{
case IDLE:
break;
case FILL_WATER:
break;
case WASH:
break;
case RINSE:
break;
case SPIN:
break;
case FINISH:
break;
case ERROR:
break;
}
Đây chính là nền tảng của State Machine.
Trong máy giặt, điều hòa, tủ lạnh, robot…
đều dùng tư duy này.
13. Chương trình hoàn chỉnh trên máy tính
Ví dụ đơn giản.
C
#include <stdio.h>
typedef enum
{
LED_OFF,
LED_ON
} LED_State;
int main()
{
LED_State led;
led = LED_ON;
if(led == LED_ON)
{
printf(“LED BAT\n”);
}
else
{
printf(“LED TAT\n”);
}
return 0;
}
Kết quả:
LED BAT
Không có gì đặc biệt.
Nhưng cách viết rất chuyên nghiệp.
14. So sánh int và enum
|
Dùng int
|
Dùng enum
|
| — | — |
|
state=0
|
state=IDLE
|
|
state=1
|
state=WASH
|
|
state=2
|
state=ERROR
|
|
Khó đọc
|
Rất dễ đọc
|
|
Dễ nhầm
|
Ít lỗi hơn
|
15. Điều cực kỳ quan trọng: enum không phải struct
Nhiều bạn nhầm.
Struct
Dùng để gom nhiều dữ liệu.
C
struct Student
{
char name[20];
int age;
};
Một sinh viên có:
- tên
- tuổi
Enum
Chỉ chọn một trạng thái.
C
typedef enum
{
STOP,
RUN,
ERROR
} MotorState;
Động cơ chỉ ở một trạng thái tại một thời điểm.
16. Kết hợp struct + enum
Đây mới là phong cách STM32 thật sự.
C
typedef enum
{
MOTOR_STOP,
MOTOR_RUN
} MotorState;
typedef struct
{
int speed;
int current;
MotorState state;
} Motor;
Ta có:
C
Motor M1;
M1.speed = 1500;
M1.current = 3;
M1.state = MOTOR_RUN;
Trong RAM sẽ giống như:
Đây là tư duy hướng dữ liệu.
17. Bài thực hành STM32 Blue Pill
Chúng ta chưa cần CubeIDE.
Chỉ luyện tư duy C.
Đề bài
Có một động cơ với 4 trạng thái:
STOP
FORWARD
REVERSE
ERROR
Hãy viết enum.
Đáp án:
C
typedef enum
{
STOP,
FORWARD,
REVERSE,
ERROR
} MotorState;
Tạo biến:
C
MotorState motor;
Gán:
C
motor = FORWARD;
Kiểm tra:
C
if(motor == FORWARD)
{
printf(“Dong co quay thuan”);
}
18. Một bài tập gần với thực tế hơn
Giả sử bạn đang làm STM32 điều khiển máy bơm.
Máy bơm có 5 trạng thái.
Bạn hãy tự nghĩ xem enum sẽ viết thế nào.
Đáp án chuẩn:
C
typedef enum
{
PUMP_IDLE,
PUMP_PRIMING,
PUMP_RUNNING,
PUMP_STOPPING,
PUMP_FAULT
} PumpState;
Sau này trong firmware:
C
PumpState pump;
Mỗi chu kỳ chương trình chỉ cần kiểm tra:
C
switch(pump)
{
case PUMP_IDLE:
break;
case PUMP_PRIMING:
break;
case PUMP_RUNNING:
break;
case PUMP_STOPPING:
break;
case PUMP_FAULT:
break;
}
Đó chính là nền móng của firmware công nghiệp.
19. Kiến thức cần nhớ thuộc lòng
enum
Dùng để tạo các trạng thái có tên thay vì dùng số.
typedef
Dùng để đặt tên ngắn gọn cho kiểu dữ liệu.
typedef enum {...} TenKieu;
Là cách viết phổ biến nhất trong STM32 HAL.
Enum không lưu chữ
CPU vẫn lưu giá trị số trong RAM.
Enum + switch
Là nền tảng xây dựng State Machine cho máy giặt, điều hòa, robot và LINAC.
20. Bài kiểm tra nhỏ
Câu 1: enum dùng để làm gì?
Tạo mảng
Đặt tên cho các trạng thái số
Tạo con trỏ
Câu 2: typedef dùng để làm gì?
Đổi tên kiểu dữ liệu
Xóa biến
Tạo vòng lặp
Câu 3: Kiểu nào giống STM32 HAL nhất?
int state;
typedef enum {OFF,ON} LED_State;
char led[10];
Bài tập về nhà
Viết chương trình định nghĩa enum cho thang máy 5 trạng thái:
IDLEGO_UPGO_DOWNDOOR_OPENERROR
Sau đó tạo biến:
C
ElevatorState elevator;
Gán trạng thái:
C
elevator = GO_UP;
Và dùng switch để xử lý từng trạng thái.
Bài 11 sắp tới
Chúng ta sẽ học một chủ đề cực kỳ quan trọng trong firmware STM32:
State Machine hoàn chỉnh — biến một chương trình C thành bộ điều khiển thực sự cho máy giặt, điều hòa, robot và các thiết bị nhúng bằng tư duy trạng thái hữu hạn. Đây là bước chuyển từ “biết viết C” sang “biết thiết kế firmware”.