🌱 Lộ trình học lập trình Embedded C cho Hệ thống Nhúng
Bài viết trước mình đã cùng mọi người tìm hiểu về ngôn ngữ lập trình C và tại sao nó lại được sử dụng rộng rãi trong hệ thống nhúng. Học lập trình C nói riêng và bất cứ kiến thức nào khác nói chung, bạn nên có cho mình một lộ trình học tập tương đối hoàn chính, và cố gắng follow theo lộ trình đó một cách chăm chỉ nhất.
Với mỗi chủ đề, bạn nên tham khảo lộ trình ở các nguồn chính thống, hoặc hỏi chính những người có kinh nghiệm, những người mentor đang hướng dẫn mình. Ở đây, dựa trên kinh nghệm, mình sẽ giới thiệu một lộ trình đề xuất để các bạn tham khảo - mục tiêu là phục vụ cho lập trình nhúng.
➤ Các bài viết trong Series này mình cũng sẽ link trong bài viết này luôn khi hoàn thành. Vì vậy bạn hãy lưu lại link này để theo dõi tốt hơn nhé!
Mục Lục
Các kiến thức chung của bất cứ ngôn ngữ lập trình nào- Kiến thức đặc thù của C
- Các kiến thức khác
- Một số kiến thức nâng cao
① Các kiến thức chung của bất cứ ngôn ngữ lập trình nào
Dễ thấy, các trường đại học luôn để sinh viên bắt đầu với C/C++, dù sau này có dùng hay không, mục đích để cung cấp cho người học cái nhìn tổng quan về lập trình, hiểu lập trình là gì, cần những công cụ gì, máy tính chạy chương trình của ngôn ngữ lập trình như thế nào. Sau đó là học những kiến thức cụ thể mà hầu hết ngôn ngữ lập trình nào cũng có:
- Variable & Data Types: Các biến là thành phần phần lưu trữ dữ liệu, và lưu trữ như thế nào sẽ được thể hiện qua kiểu dữ liệu của chúng. Các khái niệm đi kèm như Identifiers, Keywords, ...
- Storage Class: Các biến trong C được phân bổ như thế nào, bài này sẽ học cùng phần Memory Layout ở bên dưới thì sẽ hiệu quả hơn.
- Operators: Các toán tử để thao tác với biến và số.
- Flow Control: Các lệnh điều khiển luồng logic của chương trình
- Conditional Statements - Các câu lệnh điều kiện
- Loop Statements - Các câu lệnh vòng lặp
- Function: Tổ chức các hàm để có thể sử dụng lại các đoạn code nhằm tiết kiệm thời gian phát triển chương trình và bộ nhớ chương trình.
- Khi học Embedded C, nơi quan trọng về bộ nhớ và thời gian thực thi, thì bạn cần biết thêm về cách máy tính/vi điều khiển thực hiện Function Call như thế nào, tốn bộ nhớ và thời gian hay không, cùng với một số khái niệm đặc biệt như inline function, nested function, đệ quy (recursion).
- Compound Data Types: Khi cần lưu trữ và xử lý nhiều dữ liệu hơn, các ngôn ngữ lập trình cung cấp cho ta các kiểu dữ liệu tích hợp, ví dụ trong các ngôn ngữ bậc cao có thể có list, tuple, dictionary, ...Với C, bạn cần biết đến:
- Arrays, Strings, Structures, Unions, Enumeration (enum)
Ở phần này, mình nghĩ các bạn mới học nên bắt đầu với một vài bài toán nhỏ để làm quen với ngôn ngữ lập trình, cũng như làm việc với một số thuật toán cơ bản để rèn luyện tư duy logic.
- Tính tổng và trung bình mảng:
- Nhập mảng 10 số nguyên, tính tổng và trung bình.
- Dùng mảng, vòng lặp, biến float cho trung bình.
- Kiểm tra số nguyên tố:
- Viết hàm kiểm tra một số có phải số nguyên tố không.
- Dùng vòng lặp, điều kiện, và toán tử %.
- Đảo ngược chuỗi ký tự:
- Nhập chuỗi (mảng char), in chuỗi đảo ngược mà không dùng mảng phụ.
- Dùng vòng lặp và con trỏ hoặc chỉ số mảng.
- Tìm giá trị lớn nhất/nhỏ nhất:
- Nhập mảng số thực, tìm và in giá trị lớn nhất, nhỏ nhất.
- Dùng hàm, mảng, và điều kiện so sánh.
- Đếm số lần xuất hiện:
- Nhập mảng số nguyên và một số, đếm số lần xuất hiện của số đó.
- Dùng vòng lặp và biến đếm static trong hàm.
- Sắp xếp mảng (Bubble Sort):
- Nhập mảng số nguyên, sắp xếp tăng dần bằng bubble sort.
- Dùng vòng lặp lồng và hoán đổi giá trị.
- Quản lý thông tin sinh viên:
- Dùng struct để lưu tên, ID, điểm của 5 sinh viên.
- Viết hàm in danh sách và tìm sinh viên có điểm cao nhất.
- Tính giai thừa:
- Viết hàm tính giai thừa của số n (không dùng đệ quy).
- Dùng vòng lặp và kiểm tra tràn số.
Mình sẽ bổ sung thêm một số bài toán đặc thù khi hoàn thành series này!
② Kiến thức đặc thù của C
Phần này đặc biệt quan trọng khi học ngôn ngữ lập trình C và Embedded C. Điểm đầu tiên khiến C là ngôn ngữ có thể thao tác với phần cứng, kiểm soát phần cứng tốt hơn, đặc biệt khi làm việc với Vi điều khiển, đó là Pointer - Con trỏ. Từ pointer sinh ra rất nhiều khái niệm và kiến thức phát triển từ đó:
- Pointer Basic
- Pass by Value and Pass by Reference
- Khởi tạo String sử dụng Pointer
- Struct Pointer
- Function Pointer
Sau đó là những kiến thức khác như:
- Memory Layout - Tổ chức bộ nhớ trong ngôn ngữ lập trình C
- Cấp phát bộ nhớ động - Dynamic Allocation
Phần này có lẽ là phần kiến thức khó và nặng nhất của C. Bạn nên tập trung hiểu các khái niệm và thực hành thật nhiều, có thể qua các bài tập nhỏ, hoặc các project.
Ở đây, mình đưa ra một số bài tập nhỏ/project để có thể luyện tập như sau:
- Sao chép chuỗi dùng con trỏ
- Mô tả: Viết hàm sao chép chuỗi từ nguồn sang đích bằng con trỏ, không dùng hàm thư viện (strcpy).
- Kiến thức: Pointer Basic, String Pointer, Pass by Reference.
- Hoán đổi hai số bằng con trỏ
- Mô tả: Viết hàm hoán đổi giá trị của hai biến số nguyên bằng cách truyền con trỏ.
- Kiến thức: Pointer Basic, Pass by Reference.
- Quản lý danh sách sinh viên bằng struct pointer
- Mô tả: Tạo struct Student (ID, tên, điểm), dùng con trỏ để nhập và in danh sách 5 sinh viên, cấp phát động bằng malloc.
- Kiến thức: Struct Pointer, Dynamic Allocation, Pass by Reference.
- Triển khai Singly Linked List
- Mô tả: Xây dựng Linked List để quản lý danh sách sinh viên (ID, tên). Hỗ trợ: thêm node vào cuối, in danh sách, xóa node theo ID.
- Kiến thức: Struct Pointer, Dynamic Allocation, Pointer Basic, Pass by Reference.
- Function Pointer để chọn thuật toán
- Mô tả: Viết chương trình tính tổng hoặc tích của mảng số nguyên, cho phép chọn phép toán (cộng/nhân) qua function pointer.
- Kiến thức: Function Pointer, Pointer Basic.
- Kiểm tra rò rỉ bộ nhớ
- Mô tả: Viết chương trình cấp phát mảng động bằng malloc, thực hiện phép toán (như tìm giá trị lớn nhất), sau đó giải phóng bộ nhớ. Kiểm tra lỗi null pointer.
- Kiến thức: Dynamic Allocation, Memory Layout.
- In địa chỉ bộ nhớ
- Mô tả: Khai báo biến cục bộ, toàn cục, static, và động (malloc). In địa chỉ của chúng để hiểu Memory Layout (stack, data, heap).
- Kiến thức: Memory Layout, Pointer Basic.
- Đếm ký tự trong chuỗi dùng con trỏ
- Mô tả: Viết hàm đếm số lần xuất hiện của một ký tự trong chuỗi, dùng con trỏ để duyệt.
- Kiến thức: String Pointer, Pointer Basic.
- Thử triển khai một Linked List bằng C để quản lý các thông tin không cố định.
- Mô tả: Triển khai một linked list để quản lý thông tin các device - với số lượng device không cố định
- Kiến thức: Cấp phát động, structure, linked list
👉 Chi tiết về từng bài và cách triển khai mình sẽ viết và đính kèm ở từng link nha!
③ Các kiến thức khác
File Handling
Xử lý việc đọc ghi File cũng là một yếu tố quan trọng trong C, nó cũng là một dạng kiến thức general (ngôn ngữ nào cũng có) nhưng với mỗi ngôn ngữ / công việc lại cần thao tác với các dạng file khác nhau. Cũng như với Embedded C, có nhiều công việc chúng ta cũng không cần quan tâm tới File (Ví dụ làm việc với MCU trong ứng dụng Non-OS).
Dưới đây là một số dạng file thường cần xử lý trong Embedded:
- Text File: Lưu log hệ thống, file cấu hình (ví dụ: /etc/config), hoặc dữ liệu đơn giản. Dễ đọc, chỉnh sửa, phù hợp cho debug hoặc lưu thông tin runtime.
- Binary File: Lưu dữ liệu hiệu quả (như firmware, image, hoặc dữ liệu sensor). Tiết kiệm không gian, tốc độ đọc/ghi nhanh, dùng trong lưu trữ hoặc truyền dữ liệu.
- Hex File: Lưu firmware hoặc chương trình cho vi điều khiển (Intel HEX, S-record). Chủ yếu dùng để nạp code vào chip, ít thao tác trực tiếp trong runtime.
- JSON File: Lưu cấu hình phức tạp hoặc trao đổi dữ liệu (ví dụ: với ứng dụng IoT). Phổ biến trong Embedded Linux, ít dùng trên hệ thống nhúng không có OS do cần thư viện parse.
Các kiến thức liên quan đến quá trình Build:
C là ngôn ngữ yêu cầu dev cần khá hiểu về quá trình biên dịch chương trình, để có thể sử dụng tốt các công cụ lập trình cũng như tối ưu hóa chương trình. Ở phần này, mình liệt kê một số kiến thức như:
- Build Process: Quá trình build một chương trình từ source code đến binary.
- Linkage: Quá trình linking.
- Static Library & Dynamic Library: hai loại thư viện phổ biến được sử dụng trong C.
- Preprocessor: Chỉ thị tiền xử lý - được đề cập trong build process.
- Macro & Function-like macro: Macro là một loại Preprocessor được sử dụng phổ biến nhất - nên học sớm cùng phần generic.
Error Handling - Xử lý lỗi
Không giống như các ngôn ngữ lập trình khác có xử lý lỗi tự động, trong ngôn ngữ C, xử lý lỗi được thực hiện thủ công bởi các nhà phát triển bằng các phương pháp xử lý lỗi, chiến lược gỡ lỗi và các hàm như perror(), strerror(), v.v.
- Xử lý lỗi cơ bản - Kiểm tra và xử lý các điều kiện lỗi (như mở file thất bại, cấp phát bộ nhớ không thành công) để đảm bảo chương trình chạy ổn định.
- Xử lý lỗi bằng goto - Dùng goto để nhảy đến nhãn (label) xử lý lỗi trong hàm, thay vì lồng nhiều if-else. Thường dùng để dọn dẹp tài nguyên (như đóng file, giải phóng bộ nhớ) trước khi thoát.
- Xử lý lỗi File - Kiểm tra lỗi khi làm việc với file (mở, đọc, ghi, đóng) bằng cách kiểm tra giá trị trả về của fopen, fread, fwrite, hoặc dùng errno.
④ Một số kiến thức nâng cao
Các kiến thức này khá specific và được sử dụng trong việc lập trình ứng dụng, sử dụng hệ điều hành.
- Variadic Functions - Hàm chấp nhận số lượng tham số thay đổi (như printf). Ứng dụng để tạo hàm linh hoạt như log, format chuỗi, hoặc xử lý dữ liệu không cố định.
- System Calls - Gọi trực tiếp chức năng của hệ điều hành (Linux/Unix) để thực hiện tác vụ như đọc/ghi file (open, read), quản lý tiến trình (fork).
- Signals - Xử lý sự kiện bất đồng bộ (như SIGINT khi nhấn Ctrl+C) bằng <signal.h>. Đăng ký hàm xử lý (handler) cho tín hiệu.
- Socket Programming - Lập trình mạng để giao tiếp qua TCP/UDP, dùng <sys/socket.h> với các hàm như socket, bind, connect.
- Multithreading - hạy nhiều luồng đồng thời trong một tiến trình, dùng <pthread.h> (POSIX) với pthread_create, pthread_mutex.
- Inter-Process Communication (IPC) - Cơ chế giao tiếp giữa các tiến trình, như pipes, message queues, shared memory, hoặc semaphores, dùng các API POSIX (<sys/ipc.h>, <sys/shm.h>).
- Volatile and Const Keyword - Những từ khóa phổ biến trong embedded C.
- Attributes và Compiler Extensions - Đây là những tính năng bổ trợ của compiler giúp cho việc kiểm soát chương trình tốt hơn và lập trình linh hoạt hơn.
Các kiến thức khác mình sẽ bổ sung thêm, cũng như mong muốn sự góp ý từ các bạn ở dưới phần comment!
>>>>>> Follow ngay <<<<<<<
Để nhận được những bài học miễn phí mới nhất nhé 😊
Chúc các bạn học tập tốt 😊