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 8 – HÀM TRONG C: CHIA CHƯƠNG TRÌNH THÀNH NHỮNG “KHỐI NÃO” 🧠

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

BÀI 8 – HÀM TRONG C: CHIA CHƯƠNG TRÌNH THÀNH NHỮNG “KHỐI NÃO” 🧠

Sau Bài 7 – struct, chúng ta chuyển sang một kiến thức cực kỳ quan trọng nếu mục tiêu của bạn là lập trình STM32 bằng C:

Hàm (function).

Trong chương trình STM32 thực tế, bạn sẽ không viết tất cả mọi thứ trong main(). Bạn sẽ chia chương trình thành rất nhiều hàm:

void LED_On(void);
void LED_Off(void);
void Motor_SetSpeed(uint8_t speed);
uint16_t ADC_Read(void);
void LCD_ShowNumber(uint16_t value);

Có thể hiểu đơn giản:

CHƯƠNG TRÌNH
│
├── khởi tạo()
├── đọc_ADC()
├── xử_lý_nút_nhấn()
├── điều_khiển_motor()
├── cập_nhật_LCD()
└── main()

1. Hàm là gì?

Ví dụ bạn muốn bật LED.

Thay vì viết:

GPIOA->BSRR = GPIO_PIN_8;

ở 20 chỗ khác nhau, bạn tạo một hàm:

void LED_On(void)
{
    GPIOA->BSRR = GPIO_PIN_8;
}

Sau đó chỉ cần:

LED_On();

Đây chính là tư duy rất quan trọng trong lập trình nhúng:

Một chức năng → một hàm.


2. Cấu trúc cơ bản của một hàm

Một hàm C có dạng:

kiểu_trả_về tên_hàm(tham_số)
{
    // câu lệnh
}

Ví dụ:

int Add(int a, int b)
{
    return a + b;
}

Ở đây:

int        → kiểu dữ liệu trả về
Add        → tên hàm
int a      → tham số 1
int b      → tham số 2
return     → trả kết quả

Sử dụng:

int result;

result = Add(10, 20);

Sau đó:

result = 30

3. Hàm không trả về giá trị – void

Đây là loại hàm bạn sẽ sử dụng rất nhiều trên STM32.

Ví dụ:

void LED_On(void)
{
    GPIOA->BSRR = GPIO_PIN_8;
}

void trước tên hàm có nghĩa:

Hàm này không trả về kết quả.

Gọi:

LED_On();

Một ví dụ đơn giản hơn:

void SayHello(void)
{
    printf("Hello STM32\n");
}

Gọi:

SayHello();

4. Hàm có tham số

Đây là phần rất quan trọng.

Ví dụ:

void LED_Set(int state)
{
    if(state == 1)
    {
        LED_On();
    }
    else
    {
        LED_Off();
    }
}

Ta có thể gọi:

LED_Set(1);

hoặc:

LED_Set(0);

Ở đây:

int state

là tham số.

Còn:

1

và:

0

là đối số truyền vào hàm.


5. Nhiều tham số

Ví dụ:

int Add(int a, int b)
{
    return a + b;
}

Gọi:

int x;

x = Add(5, 7);

Ta có:

a = 5
b = 7

5 + 7 = 12

x = 12

Một ví dụ rất giống những gì bạn sẽ gặp trong điều khiển motor:

void Motor_Set(uint8_t direction, uint8_t speed)
{
    // xử lý direction
    // xử lý speed
}

Gọi:

Motor_Set(1, 80);

Có thể hiểu:

direction = 1
speed     = 80

6. Hàm trả về giá trị

Ví dụ:

int Square(int x)
{
    return x * x;
}

Gọi:

int result;

result = Square(5);

Kết quả:

result = 25

Một ví dụ rất gần với STM32:

uint16_t ADC_GetValue(void)
{
    uint16_t value;

    // đọc ADC

    return value;
}

Sau đó:

uint16_t adc;

adc = ADC_GetValue();

Đây là kiểu thiết kế cực kỳ phổ biến:

ADC_GetValue()
       ↓
   trả về số
       ↓
      adc

7. return cực kỳ quan trọng

Ví dụ:

int Max(int a, int b)
{
    if(a > b)
    {
        return a;
    }
    else
    {
        return b;
    }
}

Gọi:

int x;

x = Max(10, 20);

Kết quả:

x = 20

Khi chương trình chạy đến:

return 20;

hàm kết thúc và trả 20 về nơi gọi hàm.


8. Hàm main() cũng chính là một hàm

Bạn đã nhìn thấy:

int main(void)
{
    
}

Thực ra:

int
 ↓
kiểu trả về

main
 ↓
tên hàm

(void)
 ↓
không có tham số

{ }
 ↓
thân hàm

Vì vậy main() không phải là thứ gì “bí mật”.

Nó cũng là một hàm đặc biệt.


9. Prototype – khai báo hàm trước

Đây là phần rất quan trọng khi viết chương trình STM32 lớn.

Ví dụ:

#include <stdio.h>

int Add(int a, int b);

int main(void)
{
    int result;

    result = Add(10, 20);

    printf("%d\n", result);

    return 0;
}

int Add(int a, int b)
{
    return a + b;
}

Dòng:

int Add(int a, int b);

gọi là function prototype.

Nó nói cho compiler biết:

“Phía dưới sẽ có một hàm tên Add, nhận hai int và trả về một int.”


10. Tại sao STM32 rất cần prototype?

Khi chương trình nhỏ:

main.c

thì có thể chưa thấy vấn đề.

Nhưng chương trình STM32 thực tế có thể có:

main.c
gpio.c
gpio.h
adc.c
adc.h
motor.c
motor.h
lcd.c
lcd.h
uart.c
uart.h

Ví dụ:

motor.h

#ifndef MOTOR_H
#define MOTOR_H

void Motor_Init(void);
void Motor_Start(void);
void Motor_Stop(void);
void Motor_SetSpeed(uint8_t speed);

#endif

motor.c

#include "motor.h"

void Motor_Init(void)
{
    // khởi tạo motor
}

void Motor_Start(void)
{
    // chạy motor
}

void Motor_Stop(void)
{
    // dừng motor
}

void Motor_SetSpeed(uint8_t speed)
{
    // điều chỉnh tốc độ
}

main.c

#include "motor.h"

int main(void)
{
    Motor_Init();

    Motor_SetSpeed(50);

    Motor_Start();

    while(1)
    {
    }
}

Đây chính là cách chương trình STM32 được tổ chức.


11. Hàm + con trỏ

Đây là nơi kiến thức Bài 5 – con trỏ bắt đầu kết nối với Bài 8.

Ví dụ:

void SetValue(int *p)
{
    *p = 100;
}

Trong main():

int x = 10;

SetValue(&x);

Sau đó:

x = 100

Tại sao?

Ta truyền:

&x

tức là địa chỉ của x.

Hàm nhận:

int *p

Sau đó:

*p = 100;

thay đổi giá trị nằm tại địa chỉ đó.


12. Đây là kiến thức cực kỳ quan trọng đối với STM32

Ví dụ bạn có:

uint16_t adc;

Một hàm có thể trả về ADC:

uint16_t ADC_Read(void)
{
    return adc_value;
}

Nhưng đôi khi bạn muốn một hàm ghi kết quả vào biến bên ngoài:

void ADC_Read(uint16_t *value)
{
    *value = 1234;
}

Sử dụng:

uint16_t adc;

ADC_Read(&adc);

Sau đó:

adc = 1234

Đây chính là sự kết hợp:

HÀM
 +
CON TRỎ
 =
truyền dữ liệu giữa các module

13. Hàm nhận struct

Bài 7 vừa học struct, nên bây giờ ta kết hợp hai kiến thức.

Ví dụ:

struct Motor
{
    uint8_t direction;
    uint8_t speed;
};

Ta có thể tạo hàm:

void Motor_Set(struct Motor *motor)
{
    printf("Direction = %d\n", motor->direction);
    printf("Speed = %d\n", motor->speed);
}

Sử dụng:

struct Motor m1;

m1.direction = 1;
m1.speed = 80;

Motor_Set(&m1);

Bạn thấy không?

Chúng ta đang ghép:

Bài 5
Con trỏ
    ↓
Bài 7
struct
    ↓
Bài 8
function

Đây chính là nền móng của C dùng trong embedded.


14. Một ví dụ STM32 thực tế

Giả sử Blue Pill của bạn có:

PA8 → LED
ADC → biến trở 50K

Ta có thể tổ chức chương trình như sau:

void LED_Set(uint8_t state)
{
    if(state)
    {
        // bật LED
    }
    else
    {
        // tắt LED
    }
}

Đọc ADC:

uint16_t ADC_Read(void)
{
    // đọc ADC
    return 0;
}

Chuyển ADC thành tốc độ:

uint8_t ADC_To_Speed(uint16_t adc)
{
    return (adc * 100) / 4095;
}

Sau đó:

int main(void)
{
    uint16_t adc;
    uint8_t speed;

    while(1)
    {
        adc = ADC_Read();

        speed = ADC_To_Speed(adc);

        if(speed > 50)
        {
            LED_Set(1);
        }
        else
        {
            LED_Set(0);
        }
    }
}

Đây đã bắt đầu giống một chương trình embedded thực tế.


15. Một lỗi rất hay gặp

Bạn viết:

int Add(int a, int b)
{
    a + b;
}

Sai.

Vì bạn chưa trả kết quả.

Phải là:

int Add(int a, int b)
{
    return a + b;
}

16. Một lỗi khác

Bạn viết:

void LED_On(void)
{
    return 10;
}

Sai vì:

void

không được trả về giá trị.

Đúng:

void LED_On(void)
{
    // bật LED
}

17. Bài tập Bài 8

Bạn hãy tự viết, chưa cần STM32.

Bài 1

Viết hàm:

int Add(int a, int b)

trả về tổng hai số.


Bài 2

Viết:

int Max(int a, int b)

trả về số lớn hơn.

Ví dụ:

Max(10, 20)

→ 20


Bài 3

Viết:

int Square(int x)

trả về bình phương của x.


Bài 4

Viết:

void LED_Set(int state)

Nếu:

state = 1 → ON
state = 0 → OFF

Chưa cần điều khiển GPIO thật, chỉ cần printf():

LED ON
LED OFF

Bài 5 – quan trọng nhất

Viết:

void ChangeValue(int *p)

Hàm này biến giá trị bên ngoài thành 500.

Ví dụ:

int x = 10;

ChangeValue(&x);

Sau đó:

x = 500

🎯 Sau Bài 8 bạn cần nắm chắc 6 thứ

1. Hàm là gì?
2. void là gì?
3. Tham số là gì?
4. return là gì?
5. Prototype là gì?
6. Truyền con trỏ vào hàm để thay đổi biến bên ngoài

Và đặc biệt hãy nhớ chuỗi kiến thức chúng ta đang đi:

Bài 1 → Kiểu dữ liệu / RAM
Bài 2 → Biến / toán tử
Bài 3 → Cấu trúc điều khiển
Bài 4 → Mảng
Bài 5 → Con trỏ
Bài 6 → Mảng + con trỏ
Bài 7 → struct
Bài 8 → HÀM
          ↓
Bài tiếp theo → kết hợp các kiến thức này
          ↓
      STM32 thực tế

Điểm quan trọng: từ Bài 8 trở đi, bạn sẽ bắt đầu thấy C của STM32 khác hẳn kiểu C “làm bài tập”. Ta sẽ tập xây dựng các module như ADC, GPIO, Motor, LCD, UART bằng chính hàm + con trỏ + struct.

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