深入理解嵌入式UART驅動程式設計:從函數指標到環形緩衝區

在嵌入式系統開發中,串列通訊(特別是UART/USART)是與外部設備連接的常用方式。本文將探討如何設計高效的UART驅動程式,重點關注兩個核心技術:函數指標和環形緩衝區。 函數指標:實現靈活的回調機制 什麼是函數指標? 函數指標是C語言中一個強大特性,允許我們在運行時動態選擇執行的函數。在UART驅動中,它特別適合實現回調(callback)機制。 // 定義函數指標類型 typedef void (*uart_rx_callback_t)(char data); // 全域函數指標變數 static uart_rx_callback_t g_rx_callback[PORT_NUM] = {NULL}; // 註冊回調函數 void uart_register_callback(uint8_t port, uart_rx_callback_t callback) { if (port < PORT_NUM) { g_rx_callback[port] = callback; } } 為什麼使用函數指標? 在UART驅動中,函數指標提供了以下優勢: 解耦合:硬體驅動層與應用層之間實現松耦合 事件驅動:實現中斷驅動的事件處理模型 多通道支持:每個UART埠可配置不同的處理邏輯 靈活性:應用程式可以在運行時改變處理函數 環形緩衝區:高效的數據管理 什麼是環形緩衝區? 環形緩衝區(Circular Buffer或Ring Buffer)是一種固定大小的緩衝區,當達到尾部時會自動環繞回開始位置,形成一個邏輯上的環。 typedef struct { uint8_t data[UART_BUFFER_SIZE]; // 緩衝區數據 uint16_t read_index; // 讀指針 uint16_t write_index; // 寫指針 bool is_full; // 滿標誌 bool is_empty; // 空標誌 } uart_ring_buffer_t; 高效的緩衝區操作 在環形緩衝區實現中,有一個關鍵的優化技巧:使用位元運算代替模運算。 // 寫入一個字節到緩衝區 bool uart_buffer_write(uart_ring_buffer_t *buffer, uint8_t data) { if (buffer->is_full) { return false; // 緩衝區已滿 } // 存儲數據 buffer->data[buffer->write_index] = data; // 更新寫指針,關鍵優化在這裡! buffer->write_index = (buffer->write_index + 1) & (UART_BUFFER_SIZE - 1); // 更新緩衝區狀態 if (buffer->write_index == buffer->read_index) { buffer->is_full = true; } buffer->is_empty = false; return true; } 為什麼UART_BUFFER_SIZE必須是2的冪次方? 注意上面代碼中的這一行: ...

February 27, 2025 · 2 分鐘 · 

C 語言標準函式庫完整指南

一開始在學C 語言的時候,常遇到有些功能可以使用,有些不行,後來才知道,標準函式庫的存在。標準函式庫是每個 C 程式設計師必須掌握的基礎工具。本文將詳細介紹各個重要的標準函式庫、引入方式及其常用函數。 Include 指令說明 在 C 語言中,我們使用 #include 預處理指令來引入標準函式庫。有兩種主要的引入方式: 1. 使用尖括號 < > #include <stdio.h> #include <stdlib.h> 這種方式用於引入標準函式庫 編譯器會在系統的標準函式庫目錄中查找頭文件 建議用於引入所有標準函式庫 2. 使用雙引號 “ “ #include "myheader.h" 這種方式主要用於引入自定義的頭文件 編譯器會先在當前目錄查找,如果找不到才會到系統目錄查找 適用於引入專案中自己創建的頭文件 常見引入示例 // 基本程序常用引入 #include <stdio.h> // 輸入輸出相關 #include <stdlib.h> // 工具函數相關 // 字符串處理相關 #include <string.h> // 字符串函數 #include <ctype.h> // 字符類型判斷 // 數學計算相關 #include <math.h> // 數學函數 #include <float.h> // 浮點數限制 #include <limits.h> // 整數限制 // 時間相關 #include <time.h> // 時間函數 // 錯誤處理相關 #include <errno.h> // 錯誤碼 #include <assert.h> // 斷言 // 國際化支持 #include <locale.h> // 本地化設置 #include <wchar.h> // 寬字符支持 最佳實踐 1. 避免重複引入 #ifndef MYHEADER_H #define MYHEADER_H // 頭文件內容 #endif 2. 引入順序建議 — 標準 C 函式庫 — 系統相關標準庫 — 第三方函式庫 — 專案自定義頭文件 ...

February 13, 2025 · 3 分鐘 · 

Understanding Memory Management in C: malloc, calloc, and Common Mistakes

Memory management in C can be tricky, especially for beginners. In this article, we’ll explore the differences between malloc and calloc, and look at some common mistakes developers make when using these functions. The Basics: malloc vs calloc Let’s start with the basic differences between these two memory allocation functions: malloc (Memory Allocation) Allocates memory but doesn’t initialize it Takes one parameter: the total number of bytes needed Faster execution as it doesn’t initialize memory Memory contains garbage values after allocation int *arr = malloc(100 * sizeof(int)); // Allocates memory for 100 integers calloc (Contiguous Allocation) Allocates memory and initializes all bytes to zero Takes two parameters: number of elements and size of each element Slower than malloc because it initializes memory Memory is guaranteed to be zero after allocation int *arr = calloc(100, sizeof(int)); // Allocates and zeros memory for 100 integers Why Use malloc + memset in Firmware Development? In firmware development, developers often prefer using malloc followed by memset instead of calloc. Here’s why: ...

January 28, 2025 · 3 分鐘 · 

My First Day Learning C: Understanding Characters and ASCII

As a beginner tackling HackerRank’s C programming challenges, my first day was both challenging and enlightening. After completing three exercises, I discovered some fascinating aspects of C programming, particularly regarding character and string handling. The Surprising Difference Between ‘A’ and “A” One of the most interesting discoveries was understanding the fundamental difference between single quotes (‘A’) and double quotes (“A”) in C. This distinction is crucial for any C programmer to understand: ...

January 9, 2025 · 2 分鐘 ·