【實時Linux實戰系列】V4L2 采集零拷貝:DMA-BUF 在低延遲視頻中的應用

在實時視頻處理系統中,視頻幀的高效傳輸和處理是確保系統低延遲和高吞吐量的關鍵。傳統的視頻采集和處理流程中,數據拷貝是一個常見的性能瓶頸,它不僅增加了處理延遲,還可能導致幀間抖動。為了克服這些問題,Linux 提供了 V4L2(Video for Linux 2)和 DMA-BUF(DMA Buffer Sharing)等技術,用于實現零拷貝的數據傳輸,從而降低幀傳輸延遲并穩定幀間抖動。

項目背景與重要性

在實時視頻處理系統中,如視頻監控、實時視頻會議、自動駕駛等領域,低延遲和穩定的幀傳輸是確保系統性能的關鍵。V4L2 是 Linux 內核中用于視頻設備的標準接口,而 DMA-BUF 是一種用于共享內存緩沖區的機制,它們的結合可以實現高效的零拷貝數據傳輸。

掌握此技能的重要性

  1. 降低延遲:通過零拷貝技術,可以減少數據在內存中的拷貝次數,從而降低處理延遲。

  2. 減少抖動:零拷貝技術可以減少數據傳輸過程中的不確定性,從而穩定幀間抖動。

  3. 提高性能:減少數據拷貝可以釋放 CPU 資源,提高系統的整體性能。

  4. 簡化代碼:使用 V4L2 和 DMA-BUF 可以簡化視頻采集和處理的代碼,提高代碼的可讀性和可維護性。

核心概念

在深入實踐之前,我們需要了解一些與主題相關的基本概念和術語。

V4L2(Video for Linux 2)

V4L2 是 Linux 內核中用于視頻設備的標準接口,它提供了一組 API,用于控制視頻設備的采集、處理和輸出。V4L2 支持多種視頻格式和采集模式,適用于各種視頻設備。

DMA-BUF(DMA Buffer Sharing)

DMA-BUF 是一種用于共享內存緩沖區的機制,它允許內核中的不同組件共享內存緩沖區,而無需進行數據拷貝。DMA-BUF 可以顯著減少數據在內存中的拷貝次數,提高數據傳輸的效率。

DMABUF-HEAPS

DMABUF-HEAPS 是一個用戶空間庫,用于管理 DMA-BUF 緩沖區。它提供了一組 API,用于分配、釋放和共享 DMA-BUF 緩沖區。

零拷貝

零拷貝是指在數據傳輸過程中,數據不需要在內存中進行多次拷貝,從而減少處理延遲和提高性能。在視頻處理中,零拷貝技術可以顯著減少幀傳輸延遲和幀間抖動。

環境準備

在開始實踐之前,我們需要準備以下軟硬件環境。

操作系統

  • Linux:建議使用 Ubuntu 20.04 或更高版本,因為這些版本提供了最新的內核和開發工具。

開發工具

  • GCC:用于編譯 C 程序。可以通過以下命令安裝:

  • sudo apt-get update
    sudo apt-get install build-essential
  • GDB:用于調試程序。可以通過以下命令安裝:

    sudo apt-get install gdb
  • libv4l2:用于操作 V4L2 設備。可以通過以下命令安裝:

    sudo apt-get install libv4l2-dev
  • DMABUF-HEAPS:用于管理 DMA-BUF 緩沖區。可以通過以下命令安裝:

  • sudo apt-get install libdmabuf-heaps-dev

硬件環境

  • 開發板:建議使用樹莓派或 BeagleBone 等開發板,這些開發板提供了豐富的視頻接口。

  • 視頻設備:準備一個支持 V4L2 的視頻設備,如 USB 攝像頭或 HDMI 捕獲卡。

環境配置

確保你的系統已經安裝了上述工具,并且可以通過命令行訪問它們。可以通過以下命令檢查 GCC 和 GDB 是否安裝成功:

gcc --version
gdb --version

實際案例與步驟

接下來,我們將通過一個具體的案例來展示如何使用 V4L2 和 DMA-BUF 實現零拷貝的視頻采集。我們將創建一個簡單的程序,該程序從 V4L2 設備采集視頻幀,并通過 DMA-BUF 將幀共享給其他進程。

步驟 1:初始化 V4L2 設備

首先,我們需要初始化 V4L2 設備,并設置視頻采集參數。

示例代碼
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>#define DEVICE "/dev/video0"int main() {int fd;struct v4l2_format fmt;struct v4l2_requestbuffers req;struct v4l2_buffer buf;void *buffers[4];// 打開 V4L2 設備fd = open(DEVICE, O_RDWR);if (fd < 0) {perror("open");exit(EXIT_FAILURE);}// 設置視頻格式memset(&fmt, 0, sizeof(fmt));fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;fmt.fmt.pix.width = 640;fmt.fmt.pix.height = 480;fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;if (ioctl(fd, VIDIOC_S_FMT, &fmt) < 0) {perror("ioctl VIDIOC_S_FMT");close(fd);exit(EXIT_FAILURE);}// 請求緩沖區memset(&req, 0, sizeof(req));req.count = 4;req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;req.memory = V4L2_MEMORY_MMAP;if (ioctl(fd, VIDIOC_REQBUFS, &req) < 0) {perror("ioctl VIDIOC_REQBUFS");close(fd);exit(EXIT_FAILURE);}// 映射緩沖區for (int i = 0; i < req.count; i++) {memset(&buf, 0, sizeof(buf));buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;buf.index = i;if (ioctl(fd, VIDIOC_QUERYBUF, &buf) < 0) {perror("ioctl VIDIOC_QUERYBUF");close(fd);exit(EXIT_FAILURE);}buffers[i] = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);if (buffers[i] == MAP_FAILED) {perror("mmap");close(fd);exit(EXIT_FAILURE);}}// 將緩沖區放入隊列for (int i = 0; i < req.count; i++) {memset(&buf, 0, sizeof(buf));buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;buf.index = i;if (ioctl(fd, VIDIOC_QBUF, &buf) < 0) {perror("ioctl VIDIOC_QBUF");close(fd);exit(EXIT_FAILURE);}}// 開始采集if (ioctl(fd, VIDIOC_STREAMON, &buf.type) < 0) {perror("ioctl VIDIOC_STREAMON");close(fd);exit(EXIT_FAILURE);}printf("V4L2 device initialized\n");// 保持程序運行一段時間sleep(10);// 停止采集if (ioctl(fd, VIDIOC_STREAMOFF, &buf.type) < 0) {perror("ioctl VIDIOC_STREAMOFF");}// 取消映射緩沖區for (int i = 0; i < req.count; i++) {munmap(buffers[i], buf.length);}close(fd);return 0;
}
編譯與運行

將上述代碼保存為 v4l2_capture.c,然后使用以下命令編譯和運行程序:

gcc -o v4l2_capture v4l2_capture.c
./v4l2_capture

步驟 2:使用 DMA-BUF 共享緩沖區

接下來,我們將使用 DMA-BUF 將采集到的視頻幀共享給其他進程。

示例代碼
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include <linux/dma-buf.h>#define DEVICE "/dev/video0"
#define DMA_BUF_DEVICE "/

?

// 打開 V4L2 設備
fd = open(DEVICE, O_RDWR);
if (fd < 0) {perror("open");exit(EXIT_FAILURE);
}// 設置視頻格式
memset(&fmt, 0, sizeof(fmt));
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = 640;
fmt.fmt.pix.height = 480;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
if (ioctl(fd, VIDIOC_S_FMT, &fmt) < 0) {perror("ioctl VIDIOC_S_FMT");close(fd);exit(EXIT_FAILURE);
}// 請求緩沖區
memset(&req, 0, sizeof(req));
req.count = 4;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
if (ioctl(fd, VIDIOC_REQBUFS, &req) < 0) {perror("ioctl VIDIOC_REQBUFS");close(fd);exit(EXIT_FAILURE);
}// 映射緩沖區
for (int i = 0; i < req.count; i++) {memset(&buf, 0, sizeof(buf));buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;buf.index = i;if (ioctl(fd, VIDIOC_QUERYBUF, &buf) < 0) {perror("ioctl VIDIOC_QUERYBUF");close(fd);exit(EXIT_FAILURE);}buffers[i] = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);if (buffers[i] == MAP_FAILED) {perror("mmap");close(fd);exit(EXIT_FAILURE);}
}// 將緩沖區放入隊列
for (int i = 0; i < req.count; i++) {memset(&buf, 0, sizeof(buf));buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;buf.index = i;if (ioctl(fd, VIDIOC_QBUF, &buf) < 0) {perror("ioctl VIDIOC_QBUF");close(fd);exit(EXIT_FAILURE);}
}// 開始采集
if (ioctl(fd, VIDIOC_STREAMON, &buf.type) < 0) {perror("ioctl VIDIOC_STREAMON");close(fd);exit(EXIT_FAILURE);
}// 導出 DMA-BUF 文件描述符
memset(&exp, 0, sizeof(exp));
exp.flags = O_CLOEXEC;
if (ioctl(fd, DMA_BUF_IOCTL_EXPORT, &exp) < 0) {perror("ioctl DMA_BUF_IOCTL_EXPORT");close(fd);exit(EXIT_FAILURE);
}
dma_buf_fd = exp.fd;printf("DMA-BUF file descriptor: %d\n", dma_buf_fd);// 保持程序運行一段時間
sleep(10);// 停止采集
if (ioctl(fd, VIDIOC_STREAMOFF, &buf.type) < 0) {perror("ioctl VIDIOC_STREAMOFF");
}// 取消映射緩沖區
for (int i = 0; i < req.count; i++) {munmap(buffers[i], buf.length);
}close(fd);
close(dma_buf_fd);
return 0;

}


#### 編譯與運行將上述代碼保存為 `v4l2_dma_buf.c`,然后使用以下命令編譯和運行程序:```bash
gcc -o v4l2_dma_buf v4l2_dma_buf.c
./v4l2_dma_buf

步驟 3:在其他進程訪問 DMA-BUF 緩沖區

接下來,我們將在另一個進程中訪問 DMA-BUF 緩沖區,以實現零拷貝的視頻幀共享。

示例代碼
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <linux/dma-buf.h>#define DMA_BUF_DEVICE "/dev/dma_buf"int main() {int dma_buf_fd;struct dma_buf_importer imp;void *buffer;// 打開 DMA-BUF 設備dma_buf_fd = open(DMA_BUF_DEVICE, O_RDWR);if (dma_buf_fd < 0) {perror("open");exit(EXIT_FAILURE);}// 導入 DMA-BUF 緩沖區memset(&imp, 0, sizeof(imp));imp.fd = dma_buf_fd;if (ioctl(dma_buf_fd, DMA_BUF_IOCTL_IMPORT, &imp) < 0) {perror("ioctl DMA_BUF_IOCTL_IMPORT");close(dma_buf_fd);exit(EXIT_FAILURE);}// 映射緩沖區buffer = mmap(NULL, imp.size, PROT_READ | PROT_WRITE, MAP_SHARED, dma_buf_fd, 0);if (buffer == MAP_FAILED) {perror("mmap");close(dma_buf_fd);exit(EXIT_FAILURE);}printf("DMA-BUF buffer mapped at: %p\n", buffer);// 保持程序運行一段時間sleep(10);// 取消映射緩沖區munmap(buffer, imp.size);close(dma_buf_fd);return 0;
}
編譯與運行

將上述代碼保存為 dma_buf_importer.c,然后使用以下命令編譯和運行程序:

gcc -o dma_buf_importer dma_buf_importer.c
./dma_buf_importer

代碼說明

  • open:打開 V4L2 設備和 DMA-BUF 設備。

  • ioctl:設置視頻格式、請求緩沖區、查詢緩沖區、導出和導入 DMA-BUF 緩沖區。

  • mmap:映射 DMA-BUF 緩沖區到用戶空間。

  • munmap:取消映射 DMA-BUF 緩沖區。

  • close:關閉文件描述符。

常見問題與解答

問題 1:如何確定 V4L2 設備的設備文件?

解答:可以通過查看 /dev 目錄下的設備文件來確定 V4L2 設備的設備文件。例如:

ls /dev/video*

問題 2:如何確定 DMA-BUF 設備的設備文件?

解答:DMA-BUF 設備的設備文件通常為 /dev/dma_buf。如果系統中沒有該設備文件,可能需要加載相應的內核模塊。例如:

sudo modprobe dma_buf

問題 3:如何檢查 V4L2 設備支持的格式?

解答:可以通過 v4l2-ctl 工具來檢查 V4L2 設備支持的格式。例如:

v4l2-ctl --list-formats --device /dev/video0

問題 4:如何檢查 DMA-BUF 緩沖區的大小?

解答:可以通過 ioctl 調用 DMA_BUF_IOCTL_INFO 來檢查 DMA-BUF 緩沖區的大小。例如:

struct dma_buf_info info;
memset(&info, 0, sizeof(info));
if (ioctl(dma_buf_fd, DMA_BUF_IOCTL_INFO, &info) < 0) {perror("ioctl DMA_BUF_IOCTL_INFO");
}
printf("DMA-BUF size: %zu\n", info.size);

實踐建議與最佳實踐

調試技巧

  • 使用 dmesg:通過 dmesg 查看內核日志,以便跟蹤硬件接口的初始化和錯誤信息。

  • 使用 strace:通過 strace 跟蹤系統調用,檢查程序是否正確與硬件接口通信。

性能優化

  • 減少拷貝:通過使用 DMA-BUF 實現零拷貝的數據傳輸,減少數據在內存中的拷貝次數。

  • 優化緩沖區大小:根據實際需求調整緩沖區大小,以減少內存占用和提高性能。

常見錯誤解決方案

  • 設備文件不存在:檢查設備文件是否正確安裝和配置。

  • 權限不足:確保程序具有訪問硬件接口的權限。

  • 緩沖區映射失敗:檢查緩沖區大小和權限是否正確。

總結與應用場景

通過本文的介紹,我們學習了如何使用 V4L2 和 DMA-BUF 實現零拷貝的視頻采集和共享。通過減少數據在內存中的拷貝次數,可以降低幀傳輸延遲并穩定幀間抖動。在實際應用中,這種技術可以應用于視頻監控、實時視頻會議、自動駕駛等領域,幫助開發者構建高性能的實時視頻處理系統。

希望讀者能夠將所學知識應用到真實項目中,通過實踐不斷提升自己的編程能力和技術水平。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/923485.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/923485.shtml
英文地址,請注明出處:http://en.pswp.cn/news/923485.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

STM32精準控制水流

如何用STM32精準控制水的流量&#xff1f;一、系統組成框圖------------- ------------ ----------- -------------| | | | | | | || 流量傳感器 -----> STM32 ----->| 驅動電路 ----->…

吃透 Vue 樣式穿透:從 scoped 原理到組件庫樣式修改實戰

在 Vue 項目開發中&#xff0c;我們經常會引入 Element Plus、Vant、Ant Design等成熟組件庫來提升開發效率。但即便組件庫提供了基礎樣式配置&#xff0c;實際業務中仍需根據設計需求調整組件內部細節樣式——這時候&#xff0c;「樣式穿透」就成了必須掌握的技能。而要理解樣…

記一次維修網橋經歷

1.前言 前倆天突然下大雨了&#xff0c;大雨過后我也迎來斷網時刻&#xff0c;經過簡單排查發現是網絡的網橋這條線路無法連通。 猜測1 可能是網線損壞&#xff0c;2 網橋損壞 2.拆解 經過測試網線設備后發現是網橋的問題&#xff0c;嘗試reset發現無反應&#xff08;正常情況重…

OceanBase001-入門--里面有的概念不確定文章作為了解使用

目錄資料來源特點支持和不支持的點名詞概念租戶資源池租戶使用資源數據庫表分區示例資料來源 B站視頻 點擊跳轉 特點 分兩個版本 企業版支持Oracle 和MySql 社區版本支持 MySql 這里視頻這么講解的。后續有沒有社區版本什么樣子不知道&#xff0c;請不要噴我 單節點部署 兼…

KITTI數據集

KITTI數據集是由德國卡爾斯魯厄理工學院 Karlsruhe Institute of Technology (KIT) 和美國芝加哥豐田技術研究院 Toyota Technological Institute at Chicago (TTI-C) 于2012年聯合創辦&#xff0c;是目前國際上最為常用的自動駕駛場景下的計算機視覺算法評測數據集之一。該數據…

rk3568移植WebRTC AudioProcessing

前言&#xff1a; 大家好&#xff0c;我是飛一樣的成長&#xff0c;今天這篇文章主要想分享音頻3A的內容。在之前有網友找我怎么移植原生的webrtc到rk3568/rk3588上&#xff0c;當時我自己也沒有移植過&#xff0c;后面折騰了一個禮拜才搞定&#xff0c;當時遇到的最大問題&…

介紹一下 RetNet

RetNet&#xff08;Retention Network&#xff09;是微軟亞洲研究院于 2023 年提出的一種新型序列建模架構&#xff0c;旨在解決 Transformer 架構在長序列處理中存在的計算復雜度高、內存占用大、推理速度慢等核心問題。它通過創新的 “循環注意力機制”&#xff0c;實現了 “…

CANopen - PDO映射

CiA402為什么不放到一個PDO中。而是分成幾個PDO? 簡短答案&#xff1a;裝不下 解耦時序。 PDO負載上限&#xff1a;經典CAN的每個PDO只有8字節。TargetPosition(607A:0032bit) ProfileVelocity(60FF:0032bit) ModesOfOperation(6060:008bit) 共9字節&#xff0c;單個PDO放不…

北理工提出僅依賴機載傳感器針對IAP的控制與狀態估計框架

近日&#xff0c;度量用戶、北京理工大學俞玉樹老師團隊在IEEE RAL&#xff0c;IEEE TRO和IEEE TASE期刊上分別發表論文&#xff0c;研究著力于解決多飛行器集聯平臺&#xff08;Integrated Aerial Platforms, IAPs&#xff09;的相對位姿和全局定位問題&#xff0c;提出IAP的控…

13年測試老鳥,性能測試-618與雙11大促銷壓測(二)

目錄&#xff1a;導讀 前言一、Python編程入門到精通二、接口自動化項目實戰三、Web自動化項目實戰四、App自動化項目實戰五、一線大廠簡歷六、測試開發DevOps體系七、常用自動化測試工具八、JMeter性能測試九、總結&#xff08;尾部小驚喜&#xff09; 前言 1、準備工作 準備…

StreamCap(直播錄制) v1.0.2 綠色版

StreamCap 是一個基于FFmpeg和StreamGet的多平臺直播流錄制客戶端&#xff0c;覆蓋 40 國內外主流直播平臺&#xff0c;支持批量錄制、循環監控、定時監控和自動轉碼等功能。軟件特色 多端支持&#xff1a;支持Windows/MacOS/Web運行。循環監控&#xff1a;實時監控直播間狀態&…

OpenCV:圖像拼接(SIFT 特征匹配 + 透視變換)

目錄 一、核心技術原理與對應 API 解析 1.1 SIFT 特征檢測與描述&#xff08;尺度不變特征提取&#xff09; 1.1.1 灰度圖轉換&#xff1a;cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) 1.1.2 SIFT 檢測器初始化&#xff1a;cv2.SIFT_create() 1.1.3 特征點檢測與描述符計算&…

日語學習-日語知識點小記-進階-JLPT-N1階段藍寶書,共120語法(10):91-100語法+考え方13

日語學習-日語知識點小記-進階-JLPT-N1階段藍寶書&#xff0c;共120語法&#xff08;10&#xff09;&#xff1a;91-100語法1、前言&#xff08;1&#xff09;情況說明&#xff08;2&#xff09;工程師的信仰&#xff08;3&#xff09;高級語法N1語法和難點2、知識點-語法&…

繼承與組合:C++面向對象的核心

C 繼承&#xff1a;從基礎到實戰&#xff0c;徹底搞懂面向對象的 “代碼復用術” 在面向對象編程&#xff08;OOP&#xff09;的世界里&#xff0c;“繼承” 是實現代碼復用的核心機制 —— 就像現實中孩子會繼承父母的特征&#xff0c;C 的子類也能 “繼承” 父類的成員&#…

Matplotlib定制:精解顏色、字體、線型與標記

Matplotlib定制&#xff1a;精解顏色、字體、線型與標記導語 Matplotlib 是 Python 數據可視化領域的基石。雖然它的默認樣式足以滿足快速分析的需求&#xff0c;但要創作出具有專業水準、信息清晰、視覺美觀的圖表&#xff0c;就必須掌握其強大的定制功能。本文將深入探討 Mat…

Qt開發經驗 --- Qt監聽文件/文件夾改變(17)

文章目錄[toc]1 概述2 演示效果3 簡單使用示例4 帶界面的使用示例5 源代碼地址更多精彩內容&#x1f449;內容導航 &#x1f448;&#x1f449;Qt開發經驗 &#x1f448;1 概述 QT實現實時監控文件的創建、修改、刪除操作 跟蹤文件夾內容的增刪改變化 可用于文件發生變化時自…

數據分析:合并一

&#x1f537; DA37&#xff1a;統計運動會項目報名人數&#xff08;僅輸出有人報名的項目&#xff09;? 題目描述給定兩個 CSV 文件&#xff1a;items.csv&#xff1a;包含項目信息&#xff08;item_id, item_name, location&#xff09;signup.csv&#xff1a;包含員工報名信…

WWW‘25一通讀 |圖Anomaly/OOD檢測相關文章(1)

寫在前面&#xff1a;進入新一輪學習階段&#xff0c;從閱讀開始。 本文分享的是WWW2025收錄的與作者研究相近的graph-based xx相關paper的閱讀筆記&#xff0c;含個人理解&#xff0c;僅供參考&#x1f604; 0x01 HEI&#xff1a;利用不變性原理實現異配圖結構分布偏移學習 J…

static_cast:C++類型系統的“正經翻譯官”

1. 背景與核心概念 1.1 C的“類型安全”哲學 想象一下&#xff0c;你所在的世界突然失去了所有規則&#xff1a;文字可以隨意變成數字&#xff0c;人可以瞬間變成椅子&#xff0c;汽車能飛上天變成飛機… 這聽起來像是瘋狂的夢境&#xff0c;但對于早期C語言來說&#xff0c;這…

【嵌入式原理系列-第八篇】USART從原理到配置全解析

目錄 一.通信領域基礎知識介紹 1.1 串行和并行通信 1.2 同步和異步傳輸 1.3 串口和COM口 1.4 通信協議標準以及物理層定義 1.5 物理層協議之TTL / RS-232 / RS-485 二.USART介紹 2.1 USART特點介紹 2.2 UART和TTL / RS-232 / RS-485 2.3 USART硬線流控介紹 2.4 USAR…