日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

?

驅(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ù)在同一方向上進(jìn)行傳輸,半雙工則允許數(shù)據(jù)雙向傳輸?shù)窃谕粫r刻僅允許一個方向的數(shù)據(jù)傳輸嗎,不需要獨(dú)立的接收端和放松端,兩者可以合并使用相同端口。全雙工通信則包含兩個方向上的同時傳輸,全雙工通信是兩個半雙工的通信方式的拼接,從而完成的獨(dú)立接收端和發(fā)送端。

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

按照通信方式

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

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

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

UART連接方式

存在兩個引腳:

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

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

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

按照電平標(biāo)準(zhǔn)

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

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

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

單端信號 UART

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

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

USB接口

 USB,是英文Universal Serial Bus(通用串行總線)的縮寫,是一個外部總線標(biāo)準(zhǔn),用于規(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”,輸出負(fù)電壓時表示邏輯“0”,而輸出“0”電壓是沒有意義的,它既不代表“1”,也不代表“0”。而差分通信中,干擾信號會同時進(jìn)入相鄰的兩條信號線中,在信號接收端,兩個相同的干擾信號分別進(jìn)入差分放大器的兩個反相輸入端后,輸出電壓為0。所以說,差分信號技術(shù)對干擾信號具有很強(qiáng)的免疫力。對于串行傳輸來說,LVDS能夠低于外來干擾;而對于并行傳輸來說,LVDS可以不僅能夠抵御外來干擾,還能夠抵御數(shù)據(jù)傳輸線之間的串?dāng)_。因?yàn)樯鲜鲈颍瑢?shí)際電路中只要使用低壓差分信號(Low Voltage Differential Signal,LVDS),350mV左右的振幅便能滿足近距離傳輸?shù)囊蟆<俣ㄘ?fù)載電阻為100Ω,采用LVDS方式傳輸數(shù)據(jù)時,如果雙絞線長度為10m,傳輸速率可達(dá)400 Mbps;當(dāng)電纜長度增加到20m時,速率降為100 Mbps;而當(dāng)電纜長度為100m時,速率只能達(dá)到10 Mbps左右。

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

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

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

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

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

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

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

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

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

長度

功能

起始位

1位

標(biāo)志幀的起始

數(shù)據(jù)位

8位 (有時描述為9位)

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

校驗(yàn)位

無校驗(yàn)(1位奇校驗(yàn)/偶校驗(yàn))

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

停止位

1 (0.5 、1、 1.5、 2)

標(biāo)志幀的結(jié)束

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

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

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

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

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

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

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

打開串口操作會返回一個文件描述符,之后我們需要使用該文件描述符對串口進(jìn)行讀寫操作。配置串口參數(shù)的步驟會設(shè)置串口的輸入輸出波特率、數(shù)據(jù)位、停止位和校驗(yàn)位等參數(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ù)進(jìn)行處理。空閑中斷處理函數(shù)類似,不同之處在于它不需要從接收緩沖區(qū)中讀取數(shù)據(jù),而是在空閑狀態(tài)下觸發(fā)中斷并向應(yīng)用程序發(fā)送SIGIO信號。

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

如果對比于STM32單片機(jī)實(shí)現(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)部進(jìn)行區(qū)分,而Linux中的發(fā)送和接收分別有對應(yīng)的中斷處理函數(shù)。在Linux中,可以通過tty設(shè)備文件直接訪問串口,而STM32需要使用串口API進(jìn)行訪問和操作。STM32需要手動開啟和關(guān)閉中斷,而Linux的中斷處理函數(shù)會在內(nèi)核中自動啟動和停止。Linux中,數(shù)據(jù)的接收和發(fā)送是由tty設(shè)備驅(qū)動完成的,而STM32需要在中斷處理函數(shù)內(nèi)部實(shí)現(xiàn)數(shù)據(jù)的接收和發(fā)送。兩者關(guān)鍵差異是LINUX使用內(nèi)核管理中斷函數(shù)的啟停。

以下給出一種示例程序可以根據(jù)需要進(jìn)行修改編譯合入內(nèi)核實(shí)現(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ù)表,這里只需要實(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 drivern");
        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ū)動程序在新的板卡上通常由廠家進(jìn)行設(shè)備樹適配和驅(qū)動開發(fā),在實(shí)際使用案例當(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] = '';
        printf("Received: %s", buf);
    }
}

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

return 0;

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

實(shí)戰(zhàn)案例

接下來展示一種通過UnionPi Tiger開發(fā)板進(jìn)行串口數(shù)據(jù)收發(fā)的方案,基本思路是通過兩個線程分別控制串口的收發(fā)任務(wù),將收到的數(shù)據(jù)進(jìn)行處理后再發(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è)置校驗(yàn)位
int set_params_parity(int setup_parity, struct termios *options_parity)
{
    switch (setup_parity) {
        case 'n':
        case 'N':                               // 無奇偶校驗(yàn)位
            options_parity->c_cflag &= ~PARENB; // Clear parity enable/
            options_parity->c_iflag &= ~INPCK;  // disable input parity checking/
            break;

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

        case 'e':
        case 'E':                               // 設(shè)置為偶校驗(yàn)
            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': // 標(biāo)記奇偶校驗(yàn)
            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è)置校驗(yàn)位
int set_params(int fd, int databits, int stopbits, int parity)
{
    struct termios options;
    int ret = ERR;

    if (tcgetattr(fd, &options) != 0) {
        perror("tcgetattr failn");
        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 failn");
        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 errorn");
                exit(0);
            }
            recv[i] = buf;    // 保存讀取到的字節(jié)
        }
        // 處理接收到的數(shù)據(jù)
        ret = data_proce(recv);
        if (ret == ERR) {
            perror("data process errorn");
            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 failn");
        return ERR;
    }
    if (fd1 == ERR) {
        perror("open file failn");
        return ERR;
    }
    // 初始化串口
    ret1 = uart_init(fd1, 9600L);
    ret2 = uart_init(fd2,9600L);
    if (ret1 == ERR) {
        perror("uart init errorn");
        return ERR;
    }
    if (ret2 == ERR) {
        perror("uart_t init errorn");
        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;
}

在上述代碼中實(shí)現(xiàn)了接收端對于發(fā)送端發(fā)送信息的校驗(yàn),主要流程為通過接受線程收取到來自ttyUSB1的數(shù)據(jù)后進(jìn)入recv_proc()函數(shù)進(jìn)行判斷,如果收到的數(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ù),需要每一個步驟都充分了解仔細(xì)設(shè)計(jì),才能得到最終的有效結(jié)果。

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

分享到:
標(biāo)簽:Openharmony
用戶無頭像

網(wǎng)友整理

注冊時間:

網(wǎng)站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運(yùn)動步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績評定2018-06-03

通用課目體育訓(xùn)練成績評定