Mac OS/Linux系統下實現串口通信

最近參加了一個機器人比賽,機器人部分裝置需要靠Arduino板來控制,一共兩塊不同型號的Arduino板,這兩塊板需要進行通信,還需要分別和電腦上的c++程序通信。我的電腦是Mac Pro 2016款,在Mac系統下實現了串口通信。Mac系統下的很多問題其實和Linux系統下的相似問題的解決方法類似。

  1. 瞭解串口通信原理,搜索關鍵詞“Linux系統c++實現串口通信例程”,有很多文檔和教程可以學習。
  2. 提供一段歷程代碼。
#include     <stdio.h>      /*標準輸入輸出定義*/
#include     <stdlib.h>     /*標準函數庫定義*/
#include     <unistd.h>     /*Unix 標準函數定義*/
#include     <sys/types.h>
#include     <sys/stat.h>
#include     "string.h"
#include     <fcntl.h>      /*文件控制定義*/
#include     <termios.h>    /*PPSIX 終端控制定義*/
#include     <errno.h>      /*錯誤號定義*/
#define FALSE  -1
#define TRUE   0
/*********************************************************************/
int OpenDev(char *Dev)
{
    int fd = open( Dev, O_RDWR | O_NOCTTY );         //| O_NOCTTY | O_NDELAY
    if (-1 == fd)
    {
        perror("Can't Open Serial Port");
        return -1;
    }
    else
        return fd;
}
/**
 *@brief  設置串口通信速率
 *@param  fd     類型 int  打開串口的文件句柄
 *@param  speed  類型 int  串口速度
 *@return  void
 */
int speed_arr[] = { B38400, B19200, B9600, B4800, B2400, B1200, B300,
    B38400, B19200, B9600, B4800, B2400, B1200, B300, };
int name_arr[] = {38400,  19200,  9600,  4800,  2400,  1200,  300, 38400,
    19200,  9600, 4800, 2400, 1200,  300, };
void set_speed(int fd, int speed)
{
    int   i;
    int   status;
    struct termios   Opt;
    tcgetattr(fd, &Opt);
    for ( i= 0;  i < sizeof(speed_arr) / sizeof(int);  i++) {
        if  (speed == name_arr[i]) {
            tcflush(fd, TCIOFLUSH);
            cfsetispeed(&Opt, speed_arr[i]);
            cfsetospeed(&Opt, speed_arr[i]);
            status = tcsetattr(fd, TCSANOW, &Opt);
            if  (status != 0) {
                perror("tcsetattr fd1");
                return;
            }
            tcflush(fd,TCIOFLUSH);
        }
    }
}
/**
 *@brief   設置串口數據位,停止位和效驗位
 *@param  fd     類型  int  打開的串口文件句柄
 *@param  databits 類型  int 數據位   取值 爲 7 或者8
 *@param  stopbits 類型  int 停止位   取值爲 1 或者2
 *@param  parity  類型  int  效驗類型 取值爲N,E,O,,S
 */
int set_Parity(int fd,int databits,int stopbits,int parity)
{
    struct termios options;
    options.c_lflag  &= ~(ICANON | ECHO | ECHOE | ISIG);  /*Input*/
    options.c_oflag  &= ~OPOST;   /*Output*/
    if  ( tcgetattr( fd,&options)  !=  0) {
        perror("SetupSerial 1");
        return(FALSE);
    }
    options.c_cflag &= ~CSIZE;
    switch (databits) /*設置數據位數*/
    {
        case 7:
            options.c_cflag |= CS7;
            break;
        case 8:
            options.c_cflag |= CS8;
            break;
        default:
            fprintf(stderr,"Unsupported data size/n"); return (FALSE);
    }
    switch (parity)
    {
        case 'n':
        case 'N':
            options.c_cflag &= ~PARENB;   /* Clear parity enable */
            options.c_iflag &= ~INPCK;     /* Enable parity checking */
            break;
        case 'o':
        case 'O':
            options.c_cflag |= (PARODD | PARENB); /* 設置爲奇效驗*/
            options.c_iflag |= INPCK;             /* Disnable parity checking */
            break;
        case 'e':
        case 'E':
            options.c_cflag |= PARENB;     /* Enable parity */
            options.c_cflag &= ~PARODD;   /* 轉換爲偶效驗*/
            options.c_iflag |= INPCK;       /* Disnable parity checking */
            break;
        case 'S':
        case 's':  /*as no parity*/
            options.c_cflag &= ~PARENB;
            options.c_cflag &= ~CSTOPB;break;
        default:
            fprintf(stderr,"Unsupported parity/n");
            return (FALSE);
    }
    /* 設置停止位*/
    switch (stopbits)
    {
        case 1:
            options.c_cflag &= ~CSTOPB;
            break;
        case 2:
            options.c_cflag |= CSTOPB;
            break;
        default:
            fprintf(stderr,"Unsupported stop bits/n");
            return (FALSE);
    }
    /* Set input parity option */
    if (parity != 'n')
        options.c_iflag |= INPCK;
    tcflush(fd,TCIFLUSH);
    options.c_cc[VTIME] = 150; /* 設置超時15 seconds*/
    options.c_cc[VMIN] = 0; /* Update the options and do it NOW */
    if (tcsetattr(fd,TCSANOW,&options) != 0)
    {
        perror("SetupSerial 3");
        return (FALSE);
    }
    return (TRUE);
}
int main(int argc, char **argv)
{

    int fd, fd2;
    int nread, nwrite;
    char buff[512];
    char buu = 'a';
    char buu2 ;
    char buu3 = 'c';
    char *dev  = "/dev/cu.usbmodem14311"; //串口二
    char *dev2 = "/dev/cu.usbmodem14331";
    fd = OpenDev(dev);
    fd2 = OpenDev(dev2);
    set_speed(fd,9600);
    set_speed(fd2, 9600);
    if (set_Parity(fd,8,1,'N') == FALSE)
    {
        printf("Set Parity Error/n");
        exit (0);
    }
    if (set_Parity(fd2,8,1,'N') == FALSE)
    {
        printf("Set Parity Error/n");
        exit (0);
    }
    printf("look1\n");
    sleep(10);
    nwrite = write(fd, &buu, 1);
    //buff[nread+1] = '\0';
    printf("%d\n%c\n", nwrite, buu);
    sleep(10);
    printf("Look2\n");
    nread = read(fd, &buu2, 1);
    //buff[nread+1] = '\0';
    printf("%d\n%c\n", nread, buu2);

    sleep(10);
    printf("Look3\n");
    nwrite = write(fd2, &buu2, 1);
    printf("%d\n%c\n", nwrite, buu2);

    sleep(10);
    printf("Look4\n");
    nread = read(fd2, &buu2, 1);
    //buff[nread+1] = '\0';
    printf("%d\n%c\n", nread, buu2);

    sleep(10);
    printf("Look5\n");
    nwrite = write(fd2, &buu2, 1);
    printf("%d\n%c\n", nwrite, buu2);

    sleep(10);
    printf("Look6\n");
    nread = read(fd2, &buu2, 1);
    //buff[nread+1] = '\0';
    printf("%d\n%c\n", nread, buu2);

//    }
    //printf("Look\n");
      close(fd);
    //exit (0);
}
  1. 以上代碼比較重要的幾個函數的解釋:首先dev[]和dev2[]需要記錄串口(設備)的名字。查看設備名字的方法,打開終端,進入/dev目錄下,使用命令ls查看當下連接的串口名字。
  2. fd = OpenDev(dev),打開端口函數,確保串口名字沒有問題就可以
  3. set_speed(),設置串口波特率參數,注意查詢參數,否則會出現通信問題
  4. set_Parity(),設置奇偶校驗位參數,注意查詢參數,否則就出現通信問題
  5. 接下來就是使用read()函數進行串口的讀,write()函數進行串口的寫
  6. read()函數和write()函數傳入的參數都是三個,fd選擇哪一個串口,char *讀取或者寫入的字符,第三個參數表示字符的長度。具體使用前可以詳細瞭解一下函數
  7. nread和nwrite用來記錄read()和write()函數的返回值,讀取或者寫入正常的話返回的是讀取或者寫入字符的長度,否則返回負值
  8. 後面的程序是我寫的,加了很多的sleep()函數,這個點是很多教程上都沒有寫到的,一般使用串口通信的時候,一邊是電腦,一邊是其他的硬件設備(這裏以Arduino板爲例),兩邊相互連接。在起始的時候,硬件需要和電腦進行連接,所以這個時候程序最好不要一運行就讀取或者寫入串口。就拿讀取爲例,電腦上的程序讀取的時刻,Arduino板由於與電腦連接初始化還沒完成,因此板上的Serial.print(‘z’)程序還未正式運行,未向串口寫入東西,當Arduino板運行寫入程序的時刻,電腦程序已經運行了讀取的語句,導致電腦無法從串口讀到東西。因此最好加上sleep(),確保這個初始化時間內沒有做串口讀寫的事。同理,當對不同串口進行讀寫之間,最好加上sleep(),給點延遲,讓硬件通信能夠建立完成。
  9. 最後想說的是,硬件的問題有時候會比較迷,解決問題需要耐住性子,不斷地調試軟件程序和檢查硬件設備。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章