亚洲综合图片区自拍_思思91精品国产综合在线观看_一区二区三区欧美_欧美黑人又粗又大_亚洲人成精品久久久久桥本

Linux下應用層操作UART的四種方式

2023-06-28 09:10:30 來源:亞洲程序員盟主

作者:亞洲程序員盟主

串口文件


【資料圖】

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

在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ù)。cfsetispeed 和 cfsetospeed 函數(shù)用于設置串口的輸入和輸出波特率,這里設置為 115200。options.c_cflag 表示控制標志位,用于配置串口控制參數(shù),具體含義如下:

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

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

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

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

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

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

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

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

3. 讀寫

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

4. 關閉串口

close(fd);

完整示例

int open_port(const char *port){    int fd;    struct termiosoptions;    // 打開串口設備    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;    // 打開串口設備    fd = open_port("/dev/ttyUSB0");    if (fd == -1) {        printf("open err");        exit(1);    }    while (1)    {        // 讀取串口數(shù)據(jù)        n = read(fd, buf, sizeof(buf));        if (n > 0) {            printf("Received: %.*s", n, buf);        }        // 發(fā)送串口數(shù)據(jù)        strcpy(buf, "Hello, world!");        n = write(fd, buf, strlen(buf));        if (n < 0) {            perror("write failed");        }        usleep(10 * 1000);    }    // 關閉串口設備    close(fd);    printf("close uart");    return 0;}

方法2:中斷讀取示例

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

要使用中斷方式接收串口數(shù)據(jù),可以使用 select 函數(shù)來監(jiān)聽串口文件描述符的可讀事件。當串口數(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;    // 打開串口設備    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, buf);        }    }    // 關閉串口設備    close(fd);    return 0;}

需要注意的是,在使用中斷方式接收串口數(shù)據(jù)時,需要對串口文件描述符設置為非阻塞模式,以便在 select 函數(shù)返回時立即讀取串口數(shù)據(jù)??梢允褂?fcntl 函數(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, buf);    }}int main() {    struct termios options;    struct sigaction sa;    // 打開串口設備    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);    // 設置串口文件描述符為異步通知模式    /* 將串口文件描述符設置為當前進程的擁有者,從而接收該文件描述符相關的信號。*/    fcntl(fd, F_SETOWN, getpid());     int flags = fcntl(fd, F_GETFL, 0); // 先獲取當前配置, 下面只更改O_ASYNC標志    /* 將串口文件描述符設置為非阻塞模式,從而允許該文件描述符異步地接收數(shù)據(jù)和信號。*/    fcntl(fd, F_SETFL, flags | O_ASYNC);    // 設置 SIGIO 信號的處理函數(shù)    sa.sa_handler = sigio_handler;    sigemptyset(&sa.sa_mask);    sa.sa_flags = 0;    /* 設置了 SIGIO 信號的處理函數(shù)為 sigio_handler,從而在該信號被觸發(fā)時讀取串口數(shù)據(jù)并進行處理。*/    sigaction(SIGIO, &sa, NULL);    while (1) {        // 等待 SIGIO 信號        sleep(1);    }    // 關閉串口設備    close(fd);    return 0;}

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

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

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

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

方法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, buf);        }    }    return NULL;}int main() {    int fd;    struct termios options;    pthread_t tid;    // 打開串口設備    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);    }    // 關閉串口設備    close(fd);    return 0;}

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

審核編輯:湯梓紅

標簽:

上一篇:LM7805穩(wěn)壓器應用電路講解
下一篇:最后一頁