免费xxxx大片国产片_精品在线一区_亚洲午夜福利av_亚洲色大成人一区二区_无码熟妇人妻AV在线影片免费

您的位置:首頁 >熱點資訊 >

天天觀天下!Linux下應(yīng)用層操作UART的四種方式

2023-06-22 15:15:54 來源:面包芯語
點擊左上方藍色“一口Linux”,選擇“設(shè)為星標(biāo)”

作者:亞洲程序員盟主

串口文件

在linux中,針對所有的周邊設(shè)備都提供了設(shè)備文件供用戶訪問,所以如果要訪問串口,只要打開相關(guān)的設(shè)備文件即可。


(資料圖片)

在LInux下串口文件是位于/dev下的

COM1串口一為/dev/ttyS0

COM2串口2為/dev/ttyS1

或者

COM1串口一為/dev/ttyUSB0

COM2串口2為/dev/ttyUSB1

命令查詢串口:

~$ ls /dev/ttyS*/dev/ttyS0   /dev/ttyS12  /dev/ttyS16  /dev/ttyS2   /dev/ttyS23  /dev/ttyS27  /dev/ttyS30  /dev/ttyS6/dev/ttyS1   /dev/ttyS13  /dev/ttyS17  /dev/ttyS20  /dev/ttyS24  /dev/ttyS28  /dev/ttyS31  /dev/ttyS7/dev/ttyS10  /dev/ttyS14  /dev/ttyS18  /dev/ttyS21  /dev/ttyS25  /dev/ttyS29  /dev/ttyS4   /dev/ttyS8/dev/ttyS11  /dev/ttyS15  /dev/ttyS19  /dev/ttyS22  /dev/ttyS26  /dev/ttyS3   /dev/ttyS5   /dev/ttyS9

方法1:輪詢

1. 打開串口

fd = open(port, O_RDWR | O_NOCTTY | O_NDELAY);if (fd == -1) {    perror("open_port: Unable to open serial port");    return -1;}

2. 配置串口

tcgetattr(fd, &options);cfsetispeed(&options, B115200);cfsetospeed(&options, B115200);options.c_cflag |= (CLOCAL | CREAD);options.c_cflag &= ~PARENB;options.c_cflag &= ~CSTOPB;options.c_cflag &= ~CSIZE;options.c_cflag |= CS8;options.c_cflag &= ~CRTSCTS;tcsetattr(fd, TCSANOW, &options);

其中,tcgetattr 和 tcsetattr 函數(shù)用于獲取和設(shè)置串口參數(shù)。cfsetispeed 和 cfsetospeed 函數(shù)用于設(shè)置串口的輸入和輸出波特率,這里設(shè)置為 115200。options.c_cflag 表示控制標(biāo)志位,用于配置串口控制參數(shù),具體含義如下:

CLOCAL:忽略調(diào)制解調(diào)器的狀態(tài)線,只允許本地使用串口。

CREAD:允許從串口讀取數(shù)據(jù)。

PARENB:啟用奇偶校驗。&= ~PARENB則為禁用校驗。

CSTOPB:使用兩個停止位而不是一個。&= ~CSTOPB停止位為1。

CSIZE:表示字符長度的位掩碼。在這里設(shè)置為 0,表示使用默認(rèn)的 8 位數(shù)據(jù)位。

CS8:表示使用 8 位數(shù)據(jù)位。

CRTSCTS:啟用硬件流控制,即使用 RTS 和 CTS 狀態(tài)線進行流控制。

在示例程序中,我們將 CLOCAL 和 CREAD 標(biāo)志位置為 1,表示允許本地使用串口,并允許從串口讀取數(shù)據(jù)。我們將 PARENB、CSTOPB 和 CRTSCTS 標(biāo)志位都設(shè)置為 0,表示不啟用奇偶校驗、使用一個停止位和禁用硬件流控制。最后,我們將 CSIZE 標(biāo)志位設(shè)置為 0,然后將 CS8 標(biāo)志位設(shè)置為 1,以表示使用 8 位數(shù)據(jù)位。

3. 讀寫

read(fd, buf, sizeof(buf)); // 返回接收個數(shù)write(fd, buf, strlen(buf)); // 返回發(fā)送長度,負(fù)值表示發(fā)送失敗

4. 關(guān)閉串口

close(fd);

完整示例

int open_port(const char *port){    int fd;    struct termios options;    // 打開串口設(shè)備    fd = open(port, O_RDWR | O_NOCTTY | O_NDELAY);    if (fd == -1) {        perror("open_port: Unable to open serial port");        return -1;    }    // 配置串口參數(shù)    tcgetattr(fd, &options);    cfsetispeed(&options, B115200);    cfsetospeed(&options, B115200);    options.c_cflag |= (CLOCAL | CREAD);    options.c_cflag &= ~PARENB;    options.c_cflag &= ~CSTOPB;    options.c_cflag &= ~CSIZE;    options.c_cflag |= CS8;    options.c_cflag &= ~CRTSCTS;    tcsetattr(fd, TCSANOW, &options);    return fd;}int main(){    int fd;    char buf[255];    int n;    // 打開串口設(shè)備    fd = open_port("/dev/ttyUSB0");    if (fd == -1) {        printf("open err\n");        exit(1);    }    while (1)    {        // 讀取串口數(shù)據(jù)        n = read(fd, buf, sizeof(buf));        if (n > 0) {            printf("Received: %.*s\n", n, buf);        }        // 發(fā)送串口數(shù)據(jù)        strcpy(buf, "Hello, world!\n");        n = write(fd, buf, strlen(buf));        if (n < 0) {            perror("write failed\n");        }        usleep(10 * 1000);    }    // 關(guān)閉串口設(shè)備    close(fd);    printf("close uart\n");    return 0;}

方法2:中斷讀取示例

上面給出的串口示例是使用輪詢的方式讀取串口數(shù)據(jù),這種方式在某些場景下可能會占用大量 CPU 資源。實際上,對于 Linux 系統(tǒng)來說,還可以使用中斷方式接收串口數(shù)據(jù),這樣可以大大減少 CPU 的占用率,并且能夠更快地響應(yīng)串口數(shù)據(jù)。

要使用中斷方式接收串口數(shù)據(jù),可以使用 select 函數(shù)來監(jiān)聽串口文件描述符的可讀事件。當(dāng)串口數(shù)據(jù)可讀時,select 函數(shù)將返回,并且可以調(diào)用 read 函數(shù)來讀取串口數(shù)據(jù)。這種方式可以避免輪詢操作,只有在串口數(shù)據(jù)可讀時才會執(zhí)行讀取操作,因此能夠減少 CPU 的占用率。

以下是一個簡單的使用中斷方式接收串口數(shù)據(jù)的示例程序:

#include #include #include #include #include #include int main() {    int fd;    struct termios options;    fd_set rfds;    // 打開串口設(shè)備    fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY);    if (fd < 0) {        perror("open");        return -1;    }    // 配置串口參數(shù)    tcgetattr(fd, &options);    options.c_cflag = B9600 | CS8 | CLOCAL | CREAD;    options.c_iflag = IGNPAR;    options.c_oflag = 0;    options.c_lflag = 0;    options.c_cc[VTIME] = 0;    options.c_cc[VMIN] = 1;    tcsetattr(fd, TCSANOW, &options);    while (1) {        // 使用 select 函數(shù)監(jiān)聽串口文件描述符的可讀事件        FD_ZERO(&rfds);        FD_SET(fd, &rfds);        select(fd + 1, &rfds, NULL, NULL, NULL);        // 讀取串口數(shù)據(jù)        char buf[256];        int n = read(fd, buf, sizeof(buf));        if (n > 0) {            printf("Received data: %.*s\n", n, buf);        }    }    // 關(guān)閉串口設(shè)備    close(fd);    return 0;}

需要注意的是,在使用中斷方式接收串口數(shù)據(jù)時,需要對串口文件描述符設(shè)置為非阻塞模式,以便在 select 函數(shù)返回時立即讀取串口數(shù)據(jù)??梢允褂?fcntl 函數(shù)來設(shè)置文件描述符的標(biāo)志位,如下所示:

// 設(shè)置串口文件描述符為非阻塞模式int flags = fcntl(fd, F_GETFL, 0);fcntl(fd, F_SETFL, flags | O_NONBLOCK);

方法3:信號的方式接收數(shù)據(jù)

#include #include #include #include #include #include int fd;void sigio_handler(int sig) {    char buf[256];    int n = read(fd, buf, sizeof(buf));    if (n > 0) {        printf("Received data: %.*s\n", n, buf);    }}int main() {    struct termios options;    struct sigaction sa;    // 打開串口設(shè)備    fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY);    if (fd < 0) {        perror("open");        return -1;    }    // 配置串口參數(shù)    tcgetattr(fd, &options);    options.c_cflag = B9600 | CS8 | CLOCAL | CREAD;    options.c_iflag = IGNPAR;    options.c_oflag = 0;    options.c_lflag = 0;    options.c_cc[VTIME] = 0;    options.c_cc[VMIN] = 1;    tcsetattr(fd, TCSANOW, &options);    // 設(shè)置串口文件描述符為異步通知模式    /* 將串口文件描述符設(shè)置為當(dāng)前進程的擁有者,從而接收該文件描述符相關(guān)的信號。*/    fcntl(fd, F_SETOWN, getpid());     int flags = fcntl(fd, F_GETFL, 0); // 先獲取當(dāng)前配置, 下面只更改O_ASYNC標(biāo)志    /* 將串口文件描述符設(shè)置為非阻塞模式,從而允許該文件描述符異步地接收數(shù)據(jù)和信號。*/    fcntl(fd, F_SETFL, flags | O_ASYNC);    // 設(shè)置 SIGIO 信號的處理函數(shù)    sa.sa_handler = sigio_handler;    sigemptyset(&sa.sa_mask);    sa.sa_flags = 0;    /* 設(shè)置了 SIGIO 信號的處理函數(shù)為 sigio_handler,從而在該信號被觸發(fā)時讀取串口數(shù)據(jù)并進行處理。*/    sigaction(SIGIO, &sa, NULL);    while (1) {        // 等待 SIGIO 信號        sleep(1);    }    // 關(guān)閉串口設(shè)備    close(fd);    return 0;}

上述代碼中,使用了 fcntl 函數(shù)將串口文件描述符設(shè)置為異步通知模式,并使用 SIGIO 信號來通知程序串口數(shù)據(jù)已經(jīng)可讀。當(dāng)程序接收到 SIGIO 信號時,會調(diào)用 sigio_handler 函數(shù)來讀取并處理串口數(shù)據(jù)。

在這段代碼中,sigemptyset(&sa.sa_mask);的作用是將信號處理函數(shù)在執(zhí)行時要屏蔽的信號集合清空,即將其設(shè)置為空集。

每個進程都有一個信號屏蔽字,它表示了當(dāng)前被阻塞的信號集合。當(dāng)一個信號被阻塞時,它將被加入到信號屏蔽字中,而當(dāng)信號被解除阻塞時,它將被從信號屏蔽字中移除。如果信號處理函數(shù)在執(zhí)行時需要屏蔽其他的信號,則可以使用sigaddset等函數(shù)將需要屏蔽的信號添加到信號屏蔽字中。但是,在本例中,我們需要處理的信號是SIGIO,它通常不需要被屏蔽,因此我們使用sigemptyset函數(shù)將信號屏蔽字清空,以確保在處理SIGIO信號時不會屏蔽任何其他信號。

在Linux系統(tǒng)中,使用sigaction函數(shù)注冊信號處理函數(shù)時,可以設(shè)置一些標(biāo)志來指定信號處理的行為。例如,可以使用SA_RESTART標(biāo)志來指定當(dāng)系統(tǒng)調(diào)用被信號中斷時自動重啟該系統(tǒng)調(diào)用。在本例中,由于我們并不需要設(shè)置任何標(biāo)志,因此將sa.sa_flags字段設(shè)置為0即可。這表示信號處理函數(shù)不需要任何特殊的行為,只需要按照默認(rèn)的方式處理信號即可。

方法4:使用線程接收串口數(shù)據(jù):

#include #include #include #include #include #include void *read_thread(void *arg) {    int fd = *(int *)arg;    char buf[256];    int n;    while (1) {        // 讀取串口數(shù)據(jù)        n = read(fd, buf, sizeof(buf));        if (n > 0) {            printf("Received data: %.*s\n", n, buf);        }    }    return NULL;}int main() {    int fd;    struct termios options;    pthread_t tid;    // 打開串口設(shè)備    fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY);    if (fd < 0) {        perror("open");        return -1;    }    // 配置串口參數(shù)    tcgetattr(fd, &options);    options.c_cflag = B9600 | CS8 | CLOCAL | CREAD;    options.c_iflag = IGNPAR;    options.c_oflag = 0;    options.c_lflag = 0;    options.c_cc[VTIME] = 0;    options.c_cc[VMIN] = 1;    tcsetattr(fd, TCSANOW, &options);    // 創(chuàng)建讀取線程    if (pthread_create(&tid, NULL, read_thread, &fd) != 0) {        perror("pthread_create");        return -1;    }    while (1) {        // 主線程的其他處理邏輯        sleep(1);    }    // 關(guān)閉串口設(shè)備    close(fd);    return 0;}

上述代碼中,創(chuàng)建了一個讀取線程,不斷讀取串口數(shù)據(jù)并進行處理。主線程可以在讀取線程運行的同時進行其他處理邏輯。

這是一口君的新書,感謝大家支持!

精彩文章合集

文章推薦

關(guān)鍵詞: