最近參加了一個機器人比賽,機器人部分裝置需要靠Arduino板來控制,一共兩塊不同型號的Arduino板,這兩塊板需要進行通信,還需要分別和電腦上的c++程序通信。我的電腦是Mac Pro 2016款,在Mac系統下實現了串口通信。Mac系統下的很多問題其實和Linux系統下的相似問題的解決方法類似。
- 瞭解串口通信原理,搜索關鍵詞“Linux系統c++實現串口通信例程”,有很多文檔和教程可以學習。
- 提供一段歷程代碼。
#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);
}
- 以上代碼比較重要的幾個函數的解釋:首先dev[]和dev2[]需要記錄串口(設備)的名字。查看設備名字的方法,打開終端,進入/dev目錄下,使用命令ls查看當下連接的串口名字。
- fd = OpenDev(dev),打開端口函數,確保串口名字沒有問題就可以
- set_speed(),設置串口波特率參數,注意查詢參數,否則會出現通信問題
- set_Parity(),設置奇偶校驗位參數,注意查詢參數,否則就出現通信問題
- 接下來就是使用read()函數進行串口的讀,write()函數進行串口的寫
- read()函數和write()函數傳入的參數都是三個,fd選擇哪一個串口,char *讀取或者寫入的字符,第三個參數表示字符的長度。具體使用前可以詳細瞭解一下函數
- nread和nwrite用來記錄read()和write()函數的返回值,讀取或者寫入正常的話返回的是讀取或者寫入字符的長度,否則返回負值
- 後面的程序是我寫的,加了很多的sleep()函數,這個點是很多教程上都沒有寫到的,一般使用串口通信的時候,一邊是電腦,一邊是其他的硬件設備(這裏以Arduino板爲例),兩邊相互連接。在起始的時候,硬件需要和電腦進行連接,所以這個時候程序最好不要一運行就讀取或者寫入串口。就拿讀取爲例,電腦上的程序讀取的時刻,Arduino板由於與電腦連接初始化還沒完成,因此板上的Serial.print(‘z’)程序還未正式運行,未向串口寫入東西,當Arduino板運行寫入程序的時刻,電腦程序已經運行了讀取的語句,導致電腦無法從串口讀到東西。因此最好加上sleep(),確保這個初始化時間內沒有做串口讀寫的事。同理,當對不同串口進行讀寫之間,最好加上sleep(),給點延遲,讓硬件通信能夠建立完成。
- 最後想說的是,硬件的問題有時候會比較迷,解決問題需要耐住性子,不斷地調試軟件程序和檢查硬件設備。