文章出處
文章列表
以下都是從友善之臂《04- Tiny6410 Linux開發指南-20111020》復制出來的,我所做的工作就是將友善之臂提供的源程序進行詳細注釋,另外將一些大函數分解成小函數。這段代碼不長,但是涉及到多個比較不容易接觸的C語言知識點。
說明:armcomtest 是友善之臂為了方便測試而開發的linux 下的簡易實用串口終端程
序,它使用標準的系統調用,和硬件無關。一般Linux 系統系統啟動后,串口 0,1,2對應的設
備名分別為/dev/ttySAC0,1,2,3
測試串口2 需要借助另一臺帶有串口的PC,使用我們提供的串口線和擴展小板( 選購
配件) ,連接好 COM2 和另一臺PC的串口,并如前所述設置該PC的超級終端為波特率115200 ,
無流控制,其他默認。
在命令行下輸入:
#armcomtest –d /dev/ttySAC1 - o
這時如果輸入字符會在另一臺PC的超級終端出現,反之亦然。
如果要測試串口3,則需要連接擴展小板的COM3 ,并在命令行輸入:
#armcomtest –d /dev/ttySAC2 - o
下面是測試時的界面:
/*************************************************************************** ** 文件: comtest.c ** 描述: ** 以串口通訊的測試程序 ** **------------------------------------------------------------------------------------------------------ ********************************************************************************************************/ #ifdef HAVE_CONFIG_H #include <config.h> #endif #include <stdio.h> /*標準輸入輸出定義*/ #include <stdlib.h> /*標準函數庫定義*/ #include <termio.h> /*PPSIX 終端控制定義*/ #include <unistd.h>/*Unix 標準函數定義,使用exit()*/ #include <fcntl.h>/*文件控制定義*/ #include <getopt.h>/*參數提取定義*/ #include <time.h> #include <errno.h>/*錯誤號定義*/ #include <string.h> #include <assert.h> int OutputHex = 0;//是否以十六進制發送。OutputHex為1時,以十六進制發送;為0,以字符串方式發送 int OutputToStdout = 0;//是否將消息同樣發送一份到標準輸出。為1時,發送;為0,不發送 int UseColor = 0; //是否使用顏色。為1時,使用顏色;為0,不使用顏色。 struct termios BackupTtyAttr;//終端屬性的備份 int IsWrite=0; static void Error(const char *Msg) { fprintf (stderr, "%s\n", Msg); fprintf (stderr, "strerror() is %s\n", strerror(errno)); exit(EXIT_FAILURE); } static void Warning(const char *Msg) { fprintf (stderr, "Warning: %s\n", Msg); } static int SerialSpeed(const char *SpeedString) { int SpeedNumber = atoi(SpeedString); # define TestSpeed(Speed) if (SpeedNumber == Speed) return B##Speed TestSpeed(1200); TestSpeed(2400); TestSpeed(4800); TestSpeed(9600); TestSpeed(19200); TestSpeed(38400); TestSpeed(57600); TestSpeed(115200); TestSpeed(230400); Error("Bad speed"); return -1; } /** *@brief 打印錯誤信息 */ static void PrintUsage(void) { fprintf(stderr, "comtest - interactive program of comm port\n"); fprintf(stderr, "press [ESC] 3 times to quit\n\n"); fprintf(stderr, "Usage: comtest [-d device] [-t tty] [-s speed] [-7] [-c] [-x] [-o] [-h]\n"); fprintf(stderr, " -7 7 bit\n"); fprintf(stderr, " -x hex mode\n"); fprintf(stderr, " -o output to stdout too\n"); fprintf(stderr, " -c stdout output use color\n"); fprintf(stderr, " -h print this help\n"); exit(-1); } /******************************************************************************************************* ** 函數: WaitFdWriteable, 等待文件可寫 **------------------------------------------------------------------------------------------------------ ** 參數: Fd 文件描述符 ** 返回: void ** 函數說明:使用inline標識符,防止因為函數頻繁的調用占用大量的棧空間 ** 日期: 2013.06.19 ********************************************************************************************************/ static inline void WaitFdWriteable(int Fd) { fd_set WriteSetFD; //定義可寫的設備集合 FD_ZERO(&WriteSetFD);//將可寫的設備集合清空 FD_SET(Fd, &WriteSetFD);//將fd添加到可寫的設備集合中 //select函數原型: int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout); //值得說明的是:int maxfdp是一個整數值,是指需要測試的文件描述符的數目,測試的描述符范圍從0到nfds-1.即所有文件描述符的最大值加1,不能錯! if (select(Fd + 1, NULL, &WriteSetFD, NULL, NULL) < 0) {//判斷是否有可寫的設備,如果沒有就一直阻塞,這里沒有設置超時,如果沒有可寫的,會一直阻塞下去 //select函數中readfds、errorfds描述符集合都為空,表示不進行測試 Error(strerror(errno)); } } void setAttrByArgvs(){ } /******************************************************************************************************* ** 函數: OutputStdChar, 打開一個串口 **------------------------------------------------------------------------------------------------------ ** 參數: FILE *File 文件描述符 int OutputHex,是否以十六進制發送。OutputHex為1時,以十六進制發送;為0,以字符串方式發送 unsigned char aCharToSend將要發送的字符 ** 返回: void ** ** 日期: 2013.06.19 ********************************************************************************************************/ void OutputStdChar(FILE *File,int OutputHex,unsigned char aCharToSend) {//向設備寫數據 char Buffer[10]; int Len = sprintf(Buffer, OutputHex ? "%.2X " : "%c", aCharToSend);//%.2X表示輸出01,02樣式的十六進制數 // int Len = sprintf(Buffer, 0x01);//%.2X表示輸出01,02樣式的十六進制數 fwrite(Buffer, 1, Len, File); } /******************************************************************************************************* ** 函數: SetFD, 設置文件描述符 **------------------------------------------------------------------------------------------------------ ** 參數:int argc char **argv int * CommFd 串口文件描述符 int * TtyFd 終端文件描述符 ** 返回: void **函數說明: **本函數通過對main函數的argc、argv進行參數的提取,實現對串口文件描述符(CommFd)和終 端文件描述符TtyFd的設置 ** 日期: 2013.06.19 ********************************************************************************************************/ void SetFD(int argc, char **argv,int * CommFd,int * TtyFd){ struct termios TtyAttr; //終端屬性 int DeviceSpeed = B2400; //串口波特率 int TtySpeed = B2400; //終端波特率 int ByteBits = CS8; //數據位:8位 const char *DeviceName = "/dev/ttySAC1";//串口設備名 const char *TtyName = "/dev/tty"; //終端設備名,防止重要的信息被用戶重定向 opterr = 0; printf("init........\n"); //通過Argc和Argv設置必要的參數 for (;;) { int c = getopt(argc, argv, "d:s:t:7xoch");//利用getopt將argv參數一個個提取出來 if (c == -1) break; switch(c) { case 'd'://設置串口的名稱 DeviceName = optarg; break; case 't'://設置終端的名稱 TtyName = optarg; break; case 's'://設置比特率 if (optarg[0] == 'd') {//設置串口的波特率 DeviceSpeed = SerialSpeed(optarg + 1); } else if (optarg[0] == 't') {//設置終端的波特率 TtySpeed = SerialSpeed(optarg + 1); } else TtySpeed = DeviceSpeed = SerialSpeed(optarg);//如果沒有帶d或t,直接將兩個設備的波特率設置成相同的 break; case 'o'://設置同時將消息向標準輸出(stdout)輸出 OutputToStdout = 1; break; case '7'://設置數據位為7位 ByteBits = CS7; break; case 'x': OutputHex = 1;//以十六進制輸出 printf("OutputHex = 1\n"); break; case 'c'://使用顏色標記 UseColor = 1; break; case '?': case 'h': default: PrintUsage();//輸出main參數的說明 } }//end of for(;;) printf("end of getopt\n"); if (optind != argc)//判斷參數是否符合要求 PrintUsage(); //輸出main參數的說明 *CommFd = open(DeviceName, O_RDWR, 0);//以讀寫的方式打開 if (*CommFd < 0) Error("Unable to open device");//不能打開設備 if (fcntl(*CommFd, F_SETFL, O_NONBLOCK) < 0)//設置文件訪問模式為非阻塞 Error("Unable set to NONBLOCK mode"); //不能使用NONBLOCK模式 memset(&TtyAttr, 0, sizeof(struct termios)); TtyAttr.c_iflag = IGNPAR;//忽略輸入行中的中止狀態 TtyAttr.c_cflag = DeviceSpeed | HUPCL | ByteBits | CREAD | CLOCAL;//DeviceSpeed: //HUPCL:關閉時掛斷調制解調器;CREAD:啟用字符接收器;CLOCAL:忽略所有調制解調器的狀態行 TtyAttr.c_cc[VMIN] = 1;//設置MIN值,read調用將一直等待,直到有MIN個字符可讀的時候才返回,返回是讀取的字符數量。到達文件結尾的時候返回0 if (tcsetattr(*CommFd, TCSANOW, &TtyAttr) < 0)//立即對屬性進行修改,不等當前輸出完成 Warning("Unable to set comm port"); *TtyFd = open(TtyName, O_RDWR | O_NDELAY, 0);//只有在CTEAT模式下,才需要第三個參數,這邊的第三個參數是沒有作用的。 if (*TtyFd < 0) Error("Unable to open tty"); TtyAttr.c_cflag = TtySpeed | HUPCL | ByteBits | CREAD | CLOCAL; if (tcgetattr(*TtyFd, &BackupTtyAttr) < 0)//將當前Tty的屬性備份在BackupTtyAttr,以便在程序退出時還原Tty的設置 Error("Unable to get tty"); if (tcsetattr(*TtyFd, TCSANOW, &TtyAttr) < 0) Error("Unable to set tty"); } /******************************************************************************************************* ** 函數: OutputCharUseColor 輸出帶顏色的字符 **------------------------------------------------------------------------------------------------------ ** 參數:char * colorCode 顏色代碼 unsigned char aChar 要輸出的字符 ** 返回: void ** 日期: 2013.06.19 ********************************************************************************************************/ void OutputCharUseColor(char * colorCode,unsigned char aChar){ if (OutputToStdout) { //同時向標準輸出寫消息 if (UseColor) fwrite(colorCode, 1, 8, stdout); OutputStdChar(stdout,OutputHex,aChar); if (UseColor) fwrite("\x1b[00m", 1, 8, stdout); fflush(stdout);//將stdout緩沖區中的數據立即輸出,即在屏幕上顯示 } } int uart_pthread(int argc, char **argv) { printf("into main\n"); int SendBufferIndex=0; char * SendBuffer=(char *)malloc(sizeof(char)*20);//發送緩存 /** TtyFD是為了防止與用戶交互的信息被重定向,而沒有在屏幕上顯示。使用TtyFd可以直接將 不想被重定向的信息直接向用戶終端(屏幕)輸出。 **/ int CommFd, TtyFd; //串口、終端描述符 SetFD( argc, argv,&CommFd,&TtyFd); for (;;) { unsigned char aCharToSend = 0; fd_set ReadSetFD; //可讀設備集合 FD_ZERO(&ReadSetFD); //清空可讀設備集合 FD_SET(CommFd, &ReadSetFD);//將串口加入可讀設備集合 FD_SET( TtyFd, &ReadSetFD);//將終端加入可讀設備集合 # define max(x,y) ( ((x) >= (y)) ? (x) : (y) )//最大值函數,返回兩個數中較大的數 if (select(max(CommFd, TtyFd) + 1, &ReadSetFD, NULL, NULL, NULL) < 0) {//同時測試串口和終端是否可讀 Error(strerror(errno)); } # undef max if (FD_ISSET(CommFd, &ReadSetFD)) {//判斷串口是否可讀 while (read(CommFd, &aCharToSend, 1) == 1) {//從串口中讀取一個char型,放在aCharToSend WaitFdWriteable(TtyFd);//會一直阻塞在這里,直到終端設備可寫 if (write(TtyFd, &aCharToSend, 1) < 0) {//向屏幕輸出收到的字符 Error(strerror(errno)); //如果寫入錯誤,輸出錯誤信息 } OutputCharUseColor("\x1b[01;34m",aCharToSend); } } // printf("------INTO if (FD_ISSET(TtyFd, &ReadSetFD)) "); if (FD_ISSET(TtyFd, &ReadSetFD)) { //判斷終端是否可讀 while (read(TtyFd, &aCharToSend, 1) == 1) {//從終端中讀取一個值 // fprintf(stderr,"\n------SendBufferIndex:%d ",SendBufferIndex); SendBuffer[SendBufferIndex++]=aCharToSend; // fprintf(stderr, "\nRead From Tty"); static int EscKeyCount = 0; //按下Esc的次數 OutputCharUseColor("\x1b[01;31m",aCharToSend); if (aCharToSend == '\r') {//監測是否按下回車 SendBuffer[SendBufferIndex-1]='\0'; IsWrite=1; fprintf(stderr, "\x1b[01;34m you have enter :%s\n \x1b[00m",SendBuffer); // fprintf(stderr,"\n---11---IsWrite==%d ",IsWrite); } if(SendBufferIndex==19){ SendBuffer[SendBufferIndex]='\0'; // fprintf(stderr, "you have enter :%s\n",SendBuffer); IsWrite=1; } if(IsWrite==1){ // fprintf(stderr,"\n------WaitFdWriteable "); WaitFdWriteable(CommFd); if (write(CommFd, SendBuffer, SendBufferIndex) < 0) { Error(strerror(errno)); } IsWrite=0; SendBufferIndex=0; } if (aCharToSend == '\x1b') {//監測是否按下Esc fprintf(stderr, "EscKeyPressed\x1b\n"); EscKeyCount ++; if (EscKeyCount >= 3) goto ExitLabel; }else{ EscKeyCount = 0; } // fprintf(stderr,"\n---22---IsWrite==%d ",IsWrite); } } }//end of for (;;) ExitLabel: free(SendBuffer); if (tcsetattr(TtyFd, TCSANOW, &BackupTtyAttr) < 0)//恢復之前終端的設置 Error("Unable to set tty"); return 0; }
文章列表
全站熱搜