Linux串口编写

最基本的串口编程无非涉及下面的几点:

  1. 打开串口;
  2. 设置串口,如波特率、数位,等;
  3. 读/写串口(接收数据、发送数据);
  4. 关闭串口。

一、打开串口

串口的打开需要使用系统调用open,

int open_port(int port)
{
    int fd = -1;  /* File descriptor for the port, we return it. */ 
    int ret;
    char device[13] = {0};  /* ??? sizeof("/dev/ttyUSB0")=12 */ 

    if (port < 1 || port > 4)
        error_ret("Sorry, the port number must be 1~4.");
    if (USB_SERIAL)
        sprintf(device, "/dev/ttyUSB%d", port-1);
    else
        sprintf(device, "/dev/ttyS%d", port-1);
    //printf("%s %d\n", device, sizeof(device)); 

    fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY);
    if (fd == -1)
        unix_error_ret("Unable to open the port");

     /* block */ 
    ret = fcntl(fd, F_SETFL, 0);
    if (ret < 0)
        unix_error_ret("fcntl");
    ret = isatty(STDIN_FILENO);
    if (ret == 0)
        error_ret("Standard input is not a terminal device.");
    debug_msg("Open the port success!\n");
    
    return fd;
}

我们只能打开前面4个串口(如果存在的话),一般来说足够了。在处理设备文件名称时使用sprintf函数,而不是一一指定文件名称。其中的出错处理函数是自己定义的,可以在在线手册中看到它们的源代码(其实它们是宏定义,不是函数)。

 二、关闭串口

串口的关闭十分简单,直接使用系统调用close就可以了。代码如下:

int close_port(int fd)
{
    if(close(fd) < 0)
        unix_error_ret("Unable to close the port.");
    debug_msg("Close the port success!\n");

    return 0;
}

 

三、设置串口参数

串口的设置是最复杂的,先附上完整的代码,之后再简单说一下。

int setup_port(int fd, int speed, int data_bits, int parity, int stop_bits)
{
    int speed_arr[] = {B115200, B9600, B38400, B19200, B4800};
    int name_arr[] = {115200, 9600, 38400, 19200, 4800};
    struct termios opt;
    int ret=-1;
    int i=0;
    int len=0;

    ret = tcgetattr(fd, &opt); /* get the port attr */ 
    if (ret < 0)
        unix_error_ret("Unable to get the attribute");

    opt.c_cflag |= (CLOCAL | CREAD);  /* enable the receiver, set local mode */ 
    opt.c_cflag &= ~CSIZE; /* mask the character size bits*/ 

     /* baud rate */ 
    len = sizeof(speed_arr) / sizeof(int);
    for (i = 0; i < len; i++)
    {
        if (speed == name_arr[i])
        {
            cfsetispeed(&opt, speed_arr[i]);
            cfsetospeed(&opt, speed_arr[i]);
        }
        if (i == len)
            error_ret("Unsupported baud rate.");
    }
    
     /* data bits */ 
    switch (data_bits)
    {
    case 8:
        opt.c_cflag |= CS8;
        break;
    case 7:
        opt.c_cflag |= CS7;
        break;
    default:
        error_ret("Unsupported data bits.");
    }

     /* parity bits */ 
    switch (parity)
    {
    case 'N':
    case 'n':
        opt.c_cflag &= ~PARENB;
        opt.c_cflag &= ~INPCK;  /* ?? */ 
        break;
    case 'O':
    case 'o':
        opt.c_cflag|=(INPCK|ISTRIP);  /*enable parity check, strip parity bits*/ 
        opt.c_cflag |= (PARODD | PARENB);
        break;
    case 'E':
    case 'e':
        opt.c_cflag|=(INPCK|ISTRIP);  /*enable parity check, strip parity bits*/ 
        opt.c_cflag |= PARENB;
        opt.c_cflag &= ~PARODD;
        break;
    default:
        error_ret("Unsupported parity bits.");
    }

     /* stop bits */ 
    switch (stop_bits)
    {
    case 1:
        opt.c_cflag &= ~CSTOPB;
        break;
    case 2:
        opt.c_cflag |= CSTOPB;
        break;
    default:
        error_ret("Unsupported stop bits.");
    }

     /* raw input */ 
    opt.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
     /* raw ouput */ 
    opt.c_oflag &= ~OPOST;

    tcflush(fd, TCIFLUSH);
    opt.c_cc[VTIME] = 0;  /* no time out */ 
    opt.c_cc[VMIN] = 0;  /* minimum number of characters to read */ 

    ret = tcsetattr(fd, TCSANOW, &opt);  /* update it now */ 
    if (ret < 0)
        unix_error_ret("Unable to setup the port.");
    debug_msg("Setup the port OK!\n");

    return 0;  /* everything is OK! */ 
}

首先使用tcgetattr获取串口属性。termios中的成员只能通过“与”("&")、“或”("|")来操作,而不能直接赋值。之后,设置控制标志CLOCAL和CREAD,这两个选项可以保证程序不会变成端口的所有者,而端口所有者必须去处理发散性作业控制和挂断信号,同时还保证了串行接口驱动会读取过来的数据字节。在设置波特率时使用了点小技巧,就将最常用的放到数组的最前面,校验位也是如此。最后设置原始输入/输出,再使用这些属性重新设置串口。

 四、读/写串口

这两个操作同样简单,就是使用系统调用read/write就行了。

本来打算使用两个线程进行读写操作的,但不成功,后面用minicom读取数据,而我们的串口程序只负责发送。我是短接串口的2、3脚进行测试的。让我不明白的是,为何这个程序不是独占串口?是不是上面的代码设置了?这些将会随着学习的深入而解决的。

下面是发送数据的线程:

void *write_port_thread(void *argc)
{
    int ret;
    int fd;
    int i = 3;
    
    fd = (int)argc;

    //while (i--) 
    while (1)
    {
    debug_msg("writing... ");
    ret = write(fd, buf, strlen(buf));
    
    if (ret < 0)
        pthread_exit(NULL);
    write(fd, "\r\n", 2);
    debug_msg("write: %d\n", ret);
    sleep(1);
    }
    pthread_exit(NULL);
}

我们的main函数:

int main(void)
{
    int fd;
    int ret;
    
    //signal(SIGINT, sig_handle); 
    fd = open_port(1);           /* open the port(com1) */ 
    if (fd < 0)
        exit(0);
    ret = setup_port(fd, 115200, 8, 'N', 1);
    if (ret<0)
        exit(0);

    ret = pthread_create(&write_tid, NULL, write_port_thread, (void*)fd);
    if (ret < 0)
        unix_error_exit("Create write thread error.");

    #if 0
    ret = pthread_create(&read_tid, NULL, read_port_thread, (void*)fd);
    if (ret < 0)
        unix_error_exit("Create read thread error.");
    #endif

    pthread_join(write_tid, NULL);
    //pthread_join(read_tid, NULL); 
    close_port(fd);

    return 0;
}

你可能感兴趣的:(Linux串口编写)