您的位置:首页>it >

Linux下应用层操作UART的四种方式_天天滚动

2023-06-24 10:36:10    来源:面包芯语

串口文件

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 函数用于获取和设置串口参数。cfsetispeed 和 cfsetospeed 函数用于设置串口的输入和输出波特率,这里设置为 115200。options.c_cflag 表示控制标志位,用于配置串口控制参数,具体含义如下:

CLOCAL:忽略调制解调器的状态线,只允许本地使用串口。

CREAD:允许从串口读取数据。

PARENB:启用奇偶校验。&= ~PARENB则为禁用校验。

CSTOPB:使用两个停止位而不是一个。&= ~CSTOPB停止位为1。

CSIZE:表示字符长度的位掩码。在这里设置为 0,表示使用默认的 8 位数据位。

CS8:表示使用 8 位数据位。

CRTSCTS:启用硬件流控制,即使用 RTS 和 CTS 状态线进行流控制。

在示例程序中,我们将 CLOCAL 和 CREAD 标志位置为 1,表示允许本地使用串口,并允许从串口读取数据。我们将 PARENB、CSTOPB 和 CRTSCTS 标志位都设置为 0,表示不启用奇偶校验、使用一个停止位和禁用硬件流控制。最后,我们将 CSIZE 标志位设置为 0,然后将 CS8 标志位设置为 1,以表示使用 8 位数据位。

3. 读写

read(fd, buf, sizeof(buf)); // 返回接收个数write(fd, buf, strlen(buf)); // 返回发送长度,负值表示发送失败

4. 关闭串口

close(fd);

完整示例

int open_port(const char *port){    int fd;    struct termios options;    // 打开串口设备    fd = open(port, O_RDWR | O_NOCTTY | O_NDELAY);    if (fd == -1) {        perror("open_port: Unable to open serial port");        return -1;    }    // 配置串口参数    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\n");        exit(1);    }    while (1)    {        // 读取串口数据        n = read(fd, buf, sizeof(buf));        if (n > 0) {            printf("Received: %.*s\n", n, buf);        }        // 发送串口数据        strcpy(buf, "Hello, world!\n");        n = write(fd, buf, strlen(buf));        if (n < 0) {            perror("write failed\n");        }        usleep(10 * 1000);    }    // 关闭串口设备    close(fd);    printf("close uart\n");    return 0;}

方法2:中断读取示例

以下是一个简单的使用中断方式接收串口数据的示例程序:

#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;    }    // 配置串口参数    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 函数监听串口文件描述符的可读事件        FD_ZERO(&rfds);        FD_SET(fd, &rfds);        select(fd + 1, &rfds, NULL, NULL, NULL);        // 读取串口数据        char buf[256];        int n = read(fd, buf, sizeof(buf));        if (n > 0) {            printf("Received data: %.*s\n", n, buf);        }    }    // 关闭串口设备    close(fd);    return 0;}

需要注意的是,在使用中断方式接收串口数据时,需要对串口文件描述符设置为非阻塞模式,以便在 select 函数返回时立即读取串口数据。可以使用 fcntl 函数来设置文件描述符的标志位,如下所示:

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

方法3:信号的方式接收数据

#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;    // 打开串口设备    fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY);    if (fd < 0) {        perror("open");        return -1;    }    // 配置串口参数    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标志    /* 将串口文件描述符设置为非阻塞模式,从而允许该文件描述符异步地接收数据和信号。*/    fcntl(fd, F_SETFL, flags | O_ASYNC);    // 设置 SIGIO 信号的处理函数    sa.sa_handler = sigio_handler;    sigemptyset(&sa.sa_mask);    sa.sa_flags = 0;    /* 设置了 SIGIO 信号的处理函数为 sigio_handler,从而在该信号被触发时读取串口数据并进行处理。*/    sigaction(SIGIO, &sa, NULL);    while (1) {        // 等待 SIGIO 信号        sleep(1);    }    // 关闭串口设备    close(fd);    return 0;}

上述代码中,使用了 fcntl 函数将串口文件描述符设置为异步通知模式,并使用 SIGIO 信号来通知程序串口数据已经可读。当程序接收到 SIGIO 信号时,会调用 sigio_handler 函数来读取并处理串口数据。

方法4:使用线程接收串口数据:

#include #include #include #include #include #include void *read_thread(void *arg) {    int fd = *(int *)arg;    char buf[256];    int n;    while (1) {        // 读取串口数据        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;    // 打开串口设备    fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY);    if (fd < 0) {        perror("open");        return -1;    }    // 配置串口参数    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);    // 创建读取线程    if (pthread_create(&tid, NULL, read_thread, &fd) != 0) {        perror("pthread_create");        return -1;    }    while (1) {        // 主线程的其他处理逻辑        sleep(1);    }    // 关闭串口设备    close(fd);    return 0;}

上述代码中,创建了一个读取线程,不断读取串口数据并进行处理。主线程可以在读取线程运行的同时进行其他处理逻辑。

来源:一口Linux

关键词:

相关阅读