自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Openharmony南向研究—Linux驅(qū)動框架-串口

系統(tǒng) OpenHarmony
本章節(jié)主要指導(dǎo)基于Linux驅(qū)動完成串口驅(qū)動開發(fā)并調(diào)用串口與USB接口與外設(shè)完成有效通信。

??想了解更多關(guān)于開源的內(nèi)容,請訪問:??

??51CTO 開源基礎(chǔ)軟件社區(qū)??

??https://ost.51cto.com??

驅(qū)動開發(fā) -串口和串行總線

基本知識

一般情況下,設(shè)備間的通信方式可以劃分為串行通行方式和并行通信方式兩種。在Linux字符設(shè)備、塊設(shè)備、網(wǎng)絡(luò)設(shè)備分類方式下,該外設(shè)分類劃分于字符設(shè)備當(dāng)中。本章節(jié)主要指導(dǎo)基于LINUX驅(qū)動完成串口驅(qū)動開發(fā)并調(diào)用串口與USB接口與外設(shè)完成有效通信。

串行通信的分類

按照數(shù)據(jù)傳輸方向

按照數(shù)據(jù)傳輸?shù)姆较蚩梢詣澐譃?單工,半雙工和全雙工。單工通信允許數(shù)據(jù)在同一方向上進行傳輸,半雙工則允許數(shù)據(jù)雙向傳輸?shù)窃谕粫r刻僅允許一個方向的數(shù)據(jù)傳輸嗎,不需要獨立的接收端和放松端,兩者可以合并使用相同端口。全雙工通信則包含兩個方向上的同時傳輸,全雙工通信是兩個半雙工的通信方式的拼接,從而完成的獨立接收端和發(fā)送端。

#創(chuàng)作者激勵#【FFH】openharmony南向研究(6)-linux驅(qū)動框架-串口-開源基礎(chǔ)軟件社區(qū)

按照通信方式

而按照通信方式的不同,可以劃分為同步通信和異步通信兩種,同步通信是需要帶時鐘信號進行互相時鐘同步從而解析電平信號的,如SPI,IIC,而異步通信是無需時鐘同步信號的,如UART等。

在同步通訊中,收發(fā)設(shè)備的上方會使用一根信號線傳輸信號,在時鐘信號的驅(qū)動下雙方進行數(shù)據(jù)的同步,通常會在收發(fā)兩端規(guī)定在時鐘信號的上升沿和下降沿對數(shù)據(jù)線進行采樣。

在異步通訊中,不適用時鐘信號進行數(shù)據(jù)同步,直接在數(shù)據(jù)信號中穿插一些用于數(shù)據(jù)同步的信號位,或通過指定數(shù)據(jù)協(xié)議進行數(shù)據(jù)打包,以數(shù)據(jù)幀的方式傳輸數(shù)據(jù),通訊中需要約束傳輸速率波特率,常見波特率有 4800 9600 115200等。

UART連接方式

存在兩個引腳:

  • RX接收引腳
  • TX發(fā)送引腳

#創(chuàng)作者激勵#【FFH】openharmony南向研究(6)-linux驅(qū)動框架-串口-開源基礎(chǔ)軟件社區(qū)

在連接時如圖,兩個芯片的GND引腳共地。

按照電平標準

在嵌入式開發(fā)領(lǐng)域通常描述串口按照電平標準劃分由USB設(shè)備,RS485,RS-422,D-USB接口為主流的差分電平信號,雙端電平信號包括LVDS,LVPECL等。另外一類是單片機上使用為主的單端信號,其傳輸電平標準為TTL,RS-232,CMOS等。普通單端信號無法連接差分信號,如上文中描述的Tx,Rx 傳輸?shù)腡TL電平信號無法連接LVDS信號,在使用時需要使用到轉(zhuǎn)換模塊。

本文中將會以講解USB接口在Linux驅(qū)動中的使用,以及一些單端信號的使用為主。

在標準系統(tǒng)使用的開發(fā)板上包括了RS-485和USB2.0,USB3.0接口。

單端信號 UART

單端UART全稱 通用異步收發(fā)傳輸器,是一種串行異步收發(fā)協(xié)議。UART的工作原理是將數(shù)據(jù)的二進制格式數(shù)據(jù)幀一位一位進行傳輸,在UART中使用TTL電平為主,在閾值電平以上規(guī)定為高電平1,閾值電平以下規(guī)定為低電平0.

關(guān)于串口傳輸速率: bps就是比特每秒,115200bps就是每秒傳輸115200比特(115200bit),1kb=1024bit。注意,大寫的B表示字節(jié),1[Byte]=8bit?;蛘哒f1B=8b.所以115200bps=每秒112.5kb=每秒14.0625kB。

USB接口

 USB,是英文Universal Serial Bus(通用串行總線)的縮寫,是一個外部總線標準,用于規(guī)范電腦與的連接和通訊。是應(yīng)用在[PC]領(lǐng)域的接口技術(shù)。

USB的電源線是5V,為USB設(shè)備提供最大500mA的電流,它與數(shù)據(jù)線上的電平無關(guān),數(shù)據(jù)線是差分信號,通常D+和D-在+400mV~-400mV間變化,在傳統(tǒng)的單端(Single-ended)通信中,一條線路來傳輸一個比特位。高電平表示1,低電平表示0。倘若在數(shù)據(jù)傳輸過程中受到干擾,高低電平信號完全可能因此產(chǎn)生突破臨界值的大幅度擾動,一旦高電平或低電平信號超出臨界值,信號就會出錯。在差分傳輸電路中,輸出電平為正電壓時表示邏輯“1”,輸出負電壓時表示邏輯“0”,而輸出“0”電壓是沒有意義的,它既不代表“1”,也不代表“0”。而差分通信中,干擾信號會同時進入相鄰的兩條信號線中,在信號接收端,兩個相同的干擾信號分別進入差分放大器的兩個反相輸入端后,輸出電壓為0。所以說,差分信號技術(shù)對干擾信號具有很強的免疫力。對于串行傳輸來說,LVDS能夠低于外來干擾;而對于并行傳輸來說,LVDS可以不僅能夠抵御外來干擾,還能夠抵御數(shù)據(jù)傳輸線之間的串?dāng)_。因為上述原因,實際電路中只要使用低壓差分信號(Low Voltage Differential Signal,LVDS),350mV左右的振幅便能滿足近距離傳輸?shù)囊?。假定負載電阻為100Ω,采用LVDS方式傳輸數(shù)據(jù)時,如果雙絞線長度為10m,傳輸速率可達400 Mbps;當(dāng)電纜長度增加到20m時,速率降為100 Mbps;而當(dāng)電纜長度為100m時,速率只能達到10 Mbps左右。

#創(chuàng)作者激勵#【FFH】openharmony南向研究(6)-linux驅(qū)動框架-串口-開源基礎(chǔ)軟件社區(qū)

串口驅(qū)動程序開發(fā)

基本串口驅(qū)動程序?qū)崿F(xiàn)思路從底層機制大體有兩種一種是通過輪訓(xùn)機制,不斷訪問串口從而實現(xiàn)數(shù)據(jù)的收發(fā),但是會導(dǎo)致cpu占用過高,第二種是使用中斷或者DMA等技術(shù)實現(xiàn)串口的非實時讀取,但是可以保證cpu占用率低并且保證數(shù)據(jù)有效。

在上層應(yīng)用層開發(fā)過程中有串口通信協(xié)議,需要進行校驗位,數(shù)據(jù)位等需要進行規(guī)定。

總體上開發(fā)過程分為四步:

  1. 制定設(shè)備間串口協(xié)議,波特率、數(shù)據(jù)位、停止位和校驗位等。在開發(fā)驅(qū)動之前,需要確認設(shè)備和設(shè)備之間所使用的串口通信協(xié)議,以便能夠正確地配置和初始化串口。
  2. 確認串口的硬件信息,保證串口硬件相同,底層物理特性一致,如不一致需要通過CP2102等芯片進行數(shù)據(jù)轉(zhuǎn)換。同時還需要確認單臺設(shè)備串口的物理接口、I/O地址、中斷號等。
  3. 編寫串口驅(qū)動程序,根據(jù)操作系統(tǒng)根據(jù)操作系統(tǒng)的要求,編寫對應(yīng)的驅(qū)動程序。驅(qū)動程序需要包括串口的初始化、數(shù)據(jù)傳輸、中斷處理等功能。
  4. 測試和調(diào)試,完成驅(qū)動程序后完成驅(qū)動程序的編寫后,需要進行測試和調(diào)試。首先完成常規(guī)調(diào)用代碼的實現(xiàn),然后可以使用串口調(diào)試工具等工具對驅(qū)動程序進行測試,確認串口通信是否正常,數(shù)據(jù)是否正確傳輸?shù)取?/li>

#創(chuàng)作者激勵#【FFH】openharmony南向研究(6)-linux驅(qū)動框架-串口-開源基礎(chǔ)軟件社區(qū)

通常使用數(shù)據(jù)協(xié)議表格可以簡單表示如下表

數(shù)據(jù)幀內(nèi)容

長度

功能

起始位

1位

標志幀的起始

數(shù)據(jù)位

8位 (有時描述為9位)

傳輸數(shù)據(jù)

校驗位

無校驗(1位奇校驗/偶校驗)

校驗本幀數(shù)據(jù)正確性和完整性

停止位

1 (0.5 、1、 1.5、 2)

標志幀的結(jié)束

除了上述數(shù)據(jù)協(xié)議在通信雙方需要完全一致外,還需要保證數(shù)據(jù)的傳輸速率一致,即波特率一致,波特率(Baud rate)是一種衡量數(shù)字通信中數(shù)據(jù)傳輸速率的單位,通常以每秒鐘傳輸?shù)谋忍財?shù)(bit per second,bps)為單位。它指的是在數(shù)字通信中每秒鐘傳輸?shù)姆枖?shù),每個符號可以攜帶多個比特的信息。

在串行通信中,波特率是指在傳輸數(shù)據(jù)時,串行線路上數(shù)據(jù)變化的速率。例如,一個波特率為9600 bps的串行通信系統(tǒng),可以在一秒鐘內(nèi)傳輸9600個符號,每個符號可以攜帶多個比特的信息。波特率是通過調(diào)整串行通信系統(tǒng)中時鐘信號的頻率來實現(xiàn)的。因此,波特率也可以理解為時鐘頻率的一種體現(xiàn)。和時鐘周期成倒數(shù)關(guān)系,總線時鐘周期越短,單位時間傳輸?shù)拇a元越多,串口波特率越高。

需要注意的是,波特率并不等同于數(shù)據(jù)傳輸速率(data rate),因為每個符號可以攜帶多個比特的信息。例如,一個波特率為9600 bps的串行通信系統(tǒng),每個符號可以攜帶8個比特的信息,因此其數(shù)據(jù)傳輸速率為9600 bps × 8 = 76800 bps。

常見的有 115200,38400,9600,4800等。

使用外部中斷實現(xiàn)的基本思路和邏輯

常見的中斷在前面的講解中提到過包括定時器中斷,外部硬件中斷,系統(tǒng)異常中斷,系統(tǒng)調(diào)用中斷,信號中斷,NMI中斷,虛擬中斷等,本節(jié)討論的串口收發(fā)會涉及到的中斷類型包括接收中斷和空閑中斷。在大類上歸屬于外部硬件中斷。

使用LINUX依據(jù)空閑中斷和接收中斷實現(xiàn)串口收發(fā)的基本邏輯如下

打開串口操作會返回一個文件描述符,之后我們需要使用該文件描述符對串口進行讀寫操作。配置串口參數(shù)的步驟會設(shè)置串口的輸入輸出波特率、數(shù)據(jù)位、停止位和校驗位等參數(shù),以保證通信的正確性和穩(wěn)定性。

接下來,串口硬件將接收到的數(shù)據(jù)存儲在接收緩沖區(qū)中,并向內(nèi)核發(fā)出中斷信號。中斷處理函數(shù)根據(jù)中斷類型(接收中斷或空閑中斷)選擇相應(yīng)的處理方式。接收中斷處理函數(shù)會將數(shù)據(jù)從接收緩沖區(qū)中讀取并存儲到tty緩沖區(qū)中,然后向應(yīng)用程序發(fā)送SIGIO信號通知有數(shù)據(jù)可讀。應(yīng)用程序監(jiān)聽SIGIO信號并從tty緩沖區(qū)中讀取數(shù)據(jù)進行處理。空閑中斷處理函數(shù)類似,不同之處在于它不需要從接收緩沖區(qū)中讀取數(shù)據(jù),而是在空閑狀態(tài)下觸發(fā)中斷并向應(yīng)用程序發(fā)送SIGIO信號。

#創(chuàng)作者激勵#【FFH】openharmony南向研究(6)-linux驅(qū)動框架-串口-開源基礎(chǔ)軟件社區(qū)

如果對比于STM32單片機實現(xiàn)的邏輯可能更易于理解。

#創(chuàng)作者激勵#【FFH】openharmony南向研究(6)-linux驅(qū)動框架-串口-開源基礎(chǔ)軟件社區(qū)

中斷處理函數(shù)的名稱不同:Linux使用的是irq函數(shù),而STM32使用的是HAL_UART_IRQHandler函數(shù)。STM32的中斷處理函數(shù)包含了發(fā)送中斷和接收中斷,需要在處理函數(shù)內(nèi)部進行區(qū)分,而Linux中的發(fā)送和接收分別有對應(yīng)的中斷處理函數(shù)。在Linux中,可以通過tty設(shè)備文件直接訪問串口,而STM32需要使用串口API進行訪問和操作。STM32需要手動開啟和關(guān)閉中斷,而Linux的中斷處理函數(shù)會在內(nèi)核中自動啟動和停止。Linux中,數(shù)據(jù)的接收和發(fā)送是由tty設(shè)備驅(qū)動完成的,而STM32需要在中斷處理函數(shù)內(nèi)部實現(xiàn)數(shù)據(jù)的接收和發(fā)送。兩者關(guān)鍵差異是LINUX使用內(nèi)核管理中斷函數(shù)的啟停。

以下給出一種示例程序可以根據(jù)需要進行修改編譯合入內(nèi)核實現(xiàn)串口驅(qū)動。

#include <linux/module.h>
#include <linux/init.h>
#include <linux/serial_core.h>
#include <linux/serial.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>

#define DRIVER_NAME "my_serial_driver"

static struct uart_driver my_uart_driver = {
.owner = THIS_MODULE,
.driver_name = DRIVER_NAME,
.dev_name = "ttyMY", // 設(shè)備文件名,例如 /dev/ttyMY0
.major = 0, // 自動分配主設(shè)備號
.minor = 0, // 自動分配從設(shè)備號
.nr = 1, // 支持的最大串口數(shù)量
};

// 串口 probe 函數(shù),用于初始化串口參數(shù)和注冊串口設(shè)備
static int my_serial_probe(struct uart_port *port)
{
// 設(shè)置串口參數(shù)
port->ops = &my_uart_driver.ops;
port->type = PORT_16550A;
port->iotype = UPIO_MEM;
port->ioport = 0x3f8; // 串口的 I/O 端口地址
port->irq = 4; // 串口的中斷號
port->flags = UPF_BOOT_AUTOCONF;

return uart_add_one_port(&my_uart_driver, port); // 注冊串口設(shè)備
}

// 串口 remove 函數(shù),用于注銷串口設(shè)備
static void my_serial_remove(struct uart_port *port)
{
uart_remove_one_port(&my_uart_driver, port); // 注銷串口設(shè)備
}

// 串口操作函數(shù)表,這里只需要實現(xiàn) probe 和 remove 函數(shù)
static struct uart_ops my_uart_ops = {
.tx_empty = NULL,
.set_mctrl = NULL,
.get_mctrl = NULL,
.stop_tx = NULL,
.start_tx = NULL,
.send_xchar = NULL,
.stop_rx = NULL,
.enable_ms = NULL,
.break_ctl = NULL,
.startup = NULL,
.shutdown = NULL,
.flush_buffer = NULL,
.set_termios = NULL,
.type = NULL,
.release_port = NULL,
.request_port = NULL,
.config_port = NULL,
.verify_port = NULL,
.ioctl = NULL,
.send_xchar_locked = NULL,
};

// 模塊初始化函數(shù),在這里注冊串口驅(qū)動
static int my_serial_init(void)
{
int ret = 0;

// 注冊串口驅(qū)動
ret = uart_register_driver(&my_uart_driver);
if (ret) {
printk(KERN_ERR "Failed to register UART driver\n");
return ret;
}

// 設(shè)置串口操作函數(shù)表中的 probe 和 remove 函數(shù)
my_uart_ops.probe = my_serial_probe;
my_uart_ops.remove = my_serial_remove;
my_uart_driver.ops = my_uart_ops;

return ret;
}

// 模塊卸載函數(shù),在這里注銷串口驅(qū)動
static void my_serial_exit(void)
{
uart_unregister_driver(&my_uart_driver);
}

module_init(my_serial_init);
module_exit(my_serial_exit);

MODULE_LICENSE("GPL");

驅(qū)動可以通過makefile編譯為.ko文件后通過insmod合入內(nèi)核。

常規(guī)驅(qū)動的調(diào)用方式

串口驅(qū)動程序在新的板卡上通常由廠家進行設(shè)備樹適配和驅(qū)動開發(fā),在實際使用案例當(dāng)中需要熟練掌握通過文件描述符合tty層調(diào)用串口驅(qū)動即可。以下展示串口驅(qū)動的調(diào)用方式

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>

#define DEVICE "/dev/ttyMY0"

int main()
{
int fd = 0;
struct termios tio;
char buf[256];

// 打開設(shè)備文件
fd = open(DEVICE, O_RDWR | O_NOCTTY | O_NDELAY);
if (fd < 0) {
perror("open");
return -1;
}

// 設(shè)置串口參數(shù)
tcgetattr(fd, &tio);
tio.c_iflag = IGNBRK | IGNPAR;
tio.c_oflag = 0;
tio.c_cflag = CS8 | CREAD | CLOCAL;
tio.c_lflag = 0;
tio.c_cc[VTIME] = 0;
tio.c_cc[VMIN] = 1;
cfsetispeed(&tio, B9600);
cfsetospeed(&tio, B9600);
tcsetattr(fd, TCSANOW, &tio);

// 讀取串口數(shù)據(jù)
printf("Reading from serial port...\n");
while (1) {
int n = read(fd, buf, sizeof(buf));
if (n > 0) {
buf[n] = '\0';
printf("Received: %s", buf);
}
}

// 關(guān)閉設(shè)備文件
close(fd);

return 0;

對于剛剛開發(fā)的驅(qū)動程序可以通過以上程序進行簡單測試和驗證。

實戰(zhàn)案例

接下來展示一種通過UnionPi Tiger開發(fā)板進行串口數(shù)據(jù)收發(fā)的方案,基本思路是通過兩個線程分別控制串口的收發(fā)任務(wù),將收到的數(shù)據(jù)進行處理后再發(fā)送結(jié)果。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
//宏定義
#define OK 0
#define ERR (-1)
//靜態(tài)變量
static int fd1; // 串口設(shè)備文件描述符
static int fd2;
static int send_data; // 傳輸?shù)臄?shù)據(jù)
// 從串口讀的線程
// 轉(zhuǎn)換波特率
speed_t conver_baudrate(int baudrate)
{
switch (baudrate) {
case 9600L:
return B9600;
case 19200L:
return B19200;
case 38400L:
return B38400;
case 115200L:
return B115200;
case 1152000L:
return B1152000;
default:
return 1152000L;
}
}

void set_baud(int fd, int baud)
{
int ret = ERR;
struct termios opt;

tcgetattr(fd, &opt); // tcgetattr用來獲取終端參數(shù),將從終端獲得的信息fd,保存到opt結(jié)構(gòu)體中
tcflush(fd, TCIOFLUSH); // 刷清緩沖區(qū)
cfsetispeed(&opt, baud);
cfsetospeed(&opt, baud);

ret = tcsetattr(fd, TCSANOW, &opt); // 設(shè)置終端參數(shù)到opt中,使之立即生效
if (ret == ERR) {
perror("tcsetattr fd");
exit(0);
}

tcflush(fd, TCIOFLUSH); // 刷清緩沖區(qū)
}

// 設(shè)置數(shù)據(jù)位
int setup_data_bits(int setup_databits, struct termios *options_databits)
{
if (options_databits == NULL) {
perror("setup_data_bits error");
return ERR;
}

switch (setup_databits) {
case 5L:
options_databits->c_cflag |= CS5;
break;
case 6L:
options_databits->c_cflag |= CS6;
break;
case 7L:
options_databits->c_cflag |= CS7;
break;
case 8L:
options_databits->c_cflag |= CS8;
break;
default:
return ERR;
}
return OK;
}

// 設(shè)置校驗位
int set_params_parity(int setup_parity, struct termios *options_parity)
{
switch (setup_parity) {
case 'n':
case 'N': // 無奇偶校驗位
options_parity->c_cflag &= ~PARENB; // Clear parity enable/
options_parity->c_iflag &= ~INPCK; // disable input parity checking/
break;

case 'o':
case 'O': // 設(shè)置為奇校驗
options_parity->c_cflag |= (PARODD | PARENB); // odd parity checking
options_parity->c_iflag |= INPCK; // enable parity checking
break;

case 'e':
case 'E': // 設(shè)置為偶校驗
options_parity->c_cflag |= PARENB; // Enable parity /
options_parity->c_cflag &= ~PARODD; // even parity/
options_parity->c_iflag |= INPCK; // enable parity checking /
break;

case 'M':
case 'm': // 標記奇偶校驗
options_parity->c_cflag |= PARENB | CMSPAR | PARODD;
options_parity->c_iflag |= INPCK; // enable parity checking /
break;

case 'S':
case 's': // 設(shè)置為空格
options_parity->c_cflag |= PARENB | CMSPAR;
options_parity->c_cflag &= ~PARODD;
options_parity->c_iflag |= INPCK; // enable parity checking /
break;

default:
return ERR;
}
return OK;
}

// 設(shè)置校驗位
int set_params(int fd, int databits, int stopbits, int parity)
{
struct termios options;
int ret = ERR;

if (tcgetattr(fd, &options) != 0) {
perror("tcgetattr fail\n");
return ERR;
}

options.c_iflag = 0;
options.c_oflag = 0;

// setup data bits
options.c_cflag &= ~CSIZE;
ret = setup_data_bits(databits, &options);
if (ret == ERR) {
return ERR;
}

// parity
ret = set_params_parity(parity, &options);
if (ret == ERR) {
return ERR;
}

// stop bits/
switch (stopbits) {
case 1:
options.c_cflag &= ~CSTOPB;
break;
case 2L:
options.c_cflag |= CSTOPB;
break;
default:
return ERR;
}

// 請求發(fā)送和清除發(fā)送
options.c_cflag &= ~CRTSCTS;
options.c_lflag = 0;
options.c_cc[VTIME] = 10L;
options.c_cc[VMIN] = 1;

tcflush(fd, TCIFLUSH);
if (tcsetattr(fd, TCSANOW, &options) != 0) {
return ERR;
}

return OK;
}

// 設(shè)置波特率
int uart_init(int fd, int uartBaud)
{
set_baud(fd, conver_baudrate(uartBaud));
// uart param /
if (set_params(fd, 8L, 1, 'n')) {
perror("set uart parameters fail\n");
return ERR;
}
return OK;
}

int data_proce(recv){
if(recv=="hello_world"){
send_data=1;
return 1;
}
else{
send_data =0;
return 0;
}
}

void *_serial_output_task(void){
pthread_detach(pthread_self());
int ret;
ret=write(fd2,(unsigned char *) send_data,1);
if(ret>0)
printf("send success");
else {
printf("send error");
}
usleep(10000);
}
void *_serial_input_task(void)
{
int i = 0;
int ret = ERR; // 函數(shù)返回值
int buf = 0; // 用于保存讀取到的字節(jié)
int recv[FRAME_LEN] = {0}; // 用于保存接收到的數(shù)據(jù)

while (1) {
// 讀取一幀數(shù)據(jù)
for (i = 0; i < FRAME_LEN; i++) {
ret = read(fd1, &buf, 1); // 讀取一個字節(jié)
if (ret == ERR) {
perror("read error\n");
exit(0);
}
recv[i] = buf; // 保存讀取到的字節(jié)
}
// 處理接收到的數(shù)據(jù)
ret = data_proce(recv);
if (ret == ERR) {
perror("data process error\n");
exit(0);
}
}
}

int main(int argc, char **argv)
{
char *uart_dev ="ttyUSB1"; // 串口設(shè)備文件路徑
char *uart_dev_t = "ttyUSB2"; // 串口設(shè)備文件路徑
int ret1 = ERR; // 函數(shù)返回值

// 打開串口設(shè)備文件
fd1 = open(uart_dev, O_RDWR);
fd2= open(uart_dev_t,O_RDWR);
if (fd2== ERR) {
perror("open file fail\n");
return ERR;
}
if (fd1 == ERR) {
perror("open file fail\n");
return ERR;
}
// 初始化串口
ret1 = uart_init(fd1, 9600L);
ret2 = uart_init(fd2,9600L);
if (ret1 == ERR) {
perror("uart init error\n");
return ERR;
}
if (ret2 == ERR) {
perror("uart_t init error\n");
return ERR;
}

// 創(chuàng)建線程,一直執(zhí)行讀串口的操作
pthread_t pid_t;
pthread_create(&pid_t, NULL, (void *)_serial_input_task, 0);
pthread_create(&pid_t, NULL, (void *)_serial_output_task, 0);
while (1) {
sleep(10L); // 主線程等待
}
close(fd1); // 關(guān)閉串口設(shè)備文件

return 0;
}

在上述代碼中實現(xiàn)了接收端對于發(fā)送端發(fā)送信息的校驗,主要流程為通過接受線程收取到來自ttyUSB1的數(shù)據(jù)后進入recv_proc()函數(shù)進行判斷,如果收到的數(shù)據(jù)是“helloworld"則將需要發(fā)出的值send_data 設(shè)置未1,若不是則設(shè)置為0,最后通過發(fā)送線程發(fā)送出去。

在整個流程中核心操作為對文件操作符fd的操作。

總結(jié)和一些思考

串口驅(qū)動開發(fā)是嵌入式系統(tǒng)開發(fā)中的一個基本任務(wù),需要掌握底層硬件編程和Linux內(nèi)核編程知識,硬件配置,驅(qū)動框架的選擇,設(shè)備樹的配置,內(nèi)核模塊的開發(fā),都是其中的重要任務(wù),需要每一個步驟都充分了解仔細設(shè)計,才能得到最終的有效結(jié)果。

在串口操作中需要進行復(fù)雜配置,而對于大部分的設(shè)備開發(fā)而言,有不同類型的接口,接口又有著不同的型號和數(shù)據(jù)協(xié)議,給開發(fā)以及使用帶來了非常多的不便捷性,開源鴻蒙以及鴻蒙操作系統(tǒng)帶來的可能性之一是分布式軟總線,在之后的設(shè)備中只需要部署分布式軟總線子系統(tǒng),只需要專注于本地算法和設(shè)備驅(qū)動的開發(fā),對于多個數(shù)據(jù)接口的適配不需要那么關(guān)注,這對于硬件和設(shè)備開發(fā)是一大變革。我們都將對此拭目以待,對鴻蒙系統(tǒng)的研究是十分值得的。從長遠來看,分布式軟總線將進一步促進設(shè)備開發(fā)的進步和發(fā)展。未來,隨著物聯(lián)網(wǎng)和智能制造等領(lǐng)域的不斷發(fā)展,越來越多的設(shè)備將需要互相連接和通信,分布式軟總線將成為設(shè)備之間通信的主要方式之一。甚至期待有一天可以取代傳統(tǒng)的串口開發(fā)等工作,只需要適配分布式軟總線子系統(tǒng)即可。

??想了解更多關(guān)于開源的內(nèi)容,請訪問:??

??51CTO 開源基礎(chǔ)軟件社區(qū)??

??https://ost.51cto.com??

責(zé)任編輯:jianghua 來源: 51CTO 開源基礎(chǔ)軟件社區(qū)
相關(guān)推薦

2023-03-02 20:52:11

? PWM脈沖寬度調(diào)制

2022-05-11 15:08:52

驅(qū)動開發(fā)系統(tǒng)移植

2023-04-03 15:51:47

2022-04-01 15:18:04

HarmonyHDF 驅(qū)動鴻蒙

2023-03-20 16:05:49

HDF傳感器驅(qū)動開發(fā)

2022-05-12 14:42:17

項目開發(fā)Napi實現(xiàn)

2022-04-21 11:26:31

鴻蒙操作系統(tǒng)

2023-03-08 15:33:11

鴻蒙操作系統(tǒng)

2021-09-07 15:48:28

鴻蒙HarmonyOS應(yīng)用

2017-02-10 15:32:47

2009-08-04 10:46:04

2022-08-29 17:34:05

鴻蒙操作系統(tǒng)

2022-04-20 20:28:40

HDF 驅(qū)動框架鴻蒙操作系統(tǒng)

2022-08-15 22:28:57

串口訪問鴻蒙

2023-08-18 14:28:18

UART異步通信

2021-11-08 15:02:19

鴻蒙HarmonyOS應(yīng)用

2016-08-12 15:08:54

CloudOperaPaaS融合視頻

2022-05-16 11:50:45

HDF驅(qū)動框架

2023-02-28 15:49:09

鴻蒙應(yīng)用開發(fā)

2021-11-26 15:34:27

鴻蒙HarmonyOS應(yīng)用
點贊
收藏

51CTO技術(shù)棧公眾號