在Linux下對裝置的操作方法與對檔案的操作方法是一樣的,因此對串口的讀寫就可以使用簡單的read()、write()函數來完成,所不同的是只是需要對串口的其他參數進行其他配置,本文實現的是宿主機實現寫功能,目標機實現讀功能,採用單工方式的串口通訊,下面針對我個人的mini2440開發板簡單介紹下串口應用開發的步驟。
筆者的作業系統 Ubuntu 10.10; 使用USB轉串口:/dev/ttyUSB0; 開發板mini2440,開發板串口:/dev/ttySAC0 (電腦中串口名稱不一定一樣)
1.開啟串口,這裡使用函數open_port.c實現。
2.設定串口參數,這裡使用函數set_com_config.c實現。
3.讀寫串口,宿主機上是com_writer.c;目標機上是com_reader_arm9.c
以下再分別展開介紹上面步驟的詳細子步驟:
1.開啟串口函數open_port.c實現步驟:
(1)利用open()函數開啟:
1)針對宿主機的串口開啟方法:
fd = open("/dev/ttyUSB0",O_RDWR | O_NOCTTY | O_NDELAY);
2)針對目標機的串口開啟方法:
fd = open("/dev/ttySAC0",O_RDWR | O_NOCTTY | O_NDELAY);
(2)利用fcntl()函數恢複串口的為阻塞狀態,用於等待串口資料的讀入:
fcntl(fd, F_SETFL, 0)
(3)利用isatty()函數測試開啟的檔案描述符是否連結到一個終端裝置,以進一步確認串口是否正確開啟:
isatty(STDIN_FILENO);
(4)開啟宿主機的串口程式open_port.c如下所示:
/*開啟宿主機串口函數*/#define MAX_COM_NUM 3int open_port(int com_port){ int fd;//#if (COM_TYPE == GNR_COM) /*use general Serial port*/// char *dev[] = {"/dev/ttyS0","/dev/ttyS1","/dev/ttyS2"};//#else /*use USB turn Serial port*/ char *dev[] = {"/dev/ttyUSB0","/dev/ttyUSB1","/dev/ttyUSB2"}; //使用USB轉串口。ps:用預先處理沒能成功選擇,望指教??//#endif if((com_port < 0) || (com_port > MAX_COM_NUM)) return -1; /*開啟串口*/ fd = open(dev[com_port - 1], O_RDWR | O_NOCTTY | O_NDELAY); if(fd < 0) { perror("open serial port"); return -1; } /*恢複串口為阻塞狀態*/ if(fcntl(fd, F_SETFL, 0) < 0) { perror("fcntl F_SETFL\n"); return -1; } /*測試是否為終端裝置*/ if(isatty(STDIN_FILENO) == 0) { perror("standard input is not a terminal device"); return -1; } return fd;}
(5)開啟目標機(mini2440開發板)串口程式open_port_arm9.c(不同的就只是在char *dev[]那裡):
/*開啟開發板串口程式open_port_arm9.c (不同的就只是在char *dev[]那裡)*/#define MAX_COM_NUM 3int open_port(int com_port){ int fd;//#if (COM_TYPE == GNR_COM) /*use general Serial port*/// char *dev[] = {"/dev/ttyS0","/dev/ttyS1","/dev/ttyS2"};//#else /*use USB turn Serial port*/// char *dev[] = {"/dev/ttyUSB0","/dev/ttyUSB1","/dev/ttyUSB2"}; char *dev[] = {"/dev/ttySAC0","/dev/SAC1","/dev/SAC2"};//#endif if((com_port < 0) || (com_port > MAX_COM_NUM)) return -1; /*開啟串口*/ fd = open(dev[com_port - 1], O_RDWR | O_NOCTTY | O_NDELAY); if(fd < 0) { perror("open serial port"); return -1; } /*恢複串口為阻塞狀態*/ if(fcntl(fd, F_SETFL, 0) < 0) { perror("fcntl F_SETFL\n"); return -1; } /*測試是否為終端裝置*/ if(isatty(STDIN_FILENO) == 0) { perror("standard input is not a terminal device"); return -1; } return fd;} 2.設定串口參數,這裡使用函數set_com_config.c實現。
(1)串口的設定主要是設定struct termios結構體的各成員值,如下所示:
#include<termios.h>struct termios{ unsigned short c_iflag; /*輸入模式標誌*/ unsigned short c_oflag; /*輸出模式標誌*/ unsigned short c_cflag; /*控制模式標誌*/ unsigned short c_lflag; /*本地模式標誌*/ unsigned char c_line; /*線路規程*/ unsigned char c_cc[NCC]; /*控制特性*/ speed_t c_ispeed; /*輸入速度*/ speed_t c_ospeed; /*輸出速度*/};(2)set_com_config.c程式如下:
int set_com_config(int fd, int baud_rate, \ int data_bits, char parity, int stop_bits){ struct termios new_cfg,old_cfg; int speed; /*步驟一:儲存原先串口配置*/ if(tcgetattr(fd, &old_cfg) != 0) { perror("tcgetattr"); return -1; } new_cfg = old_cfg; /*步驟二:啟用選項*/ cfmakeraw(&new_cfg); //config to raw mode /*步驟三:設定字元大小*/ new_cfg.c_cflag &= ~CSIZE; //set mask /*步驟四:設定傳輸速率*/ switch(baud_rate) { case 2400: speed = B2400; break; case 4800: speed = B4800; break; case 9600: speed = B9600; break; case 19200: speed = B19200; break; case 38400: speed = B38400; break; default: case 115200: speed = B115200; break; } cfsetispeed(&new_cfg, speed); cfsetospeed(&new_cfg, speed); /*步驟五:設定資料位元*/ switch(data_bits) { case 7: new_cfg.c_cflag |= CS7; break; default: case 8: new_cfg.c_cflag |= CS8; break; } /*步驟六:設定同位位元*/ switch(parity) { default: case 'n': case 'N': new_cfg.c_cflag &= ~PARENB; new_cfg.c_cflag &= ~INPCK; break; case 'o': case 'O': new_cfg.c_cflag |= (PARODD | PARENB); new_cfg.c_cflag |= INPCK; break; case 'e': case 'E': new_cfg.c_cflag |= PARENB; new_cfg.c_cflag &= ~PARODD; new_cfg.c_cflag |= INPCK; break; case 's': //as no parity case 'S': new_cfg.c_cflag &= ~PARENB; new_cfg.c_cflag &= ~CSTOPB; break; }//end of 'switch parity' /*步驟七:設定停止位*/ switch (stop_bits) { default: case 1: new_cfg.c_cflag &= ~CSTOPB; break; case 2: new_cfg.c_cflag |= CSTOPB; break; }//end of 'switch stop_bits' /*步驟八:設定最少字元和等待時間*/ new_cfg.c_cc[VTIME] = 0; new_cfg.c_cc[VMIN] = 1; /*步驟九:處理未接收的字元*/ tcflush(fd,TCIFLUSH); /*步驟十:啟用新配置*/ if((tcsetattr(fd, TCSANOW, &new_cfg)) != 0) { perror("tcsetattr"); return -1; } return 0;} 3.讀寫串口,宿主機上是com_writer.c;目標機上是com_reader_arm9.c
(1)宿主機的寫程式com_writer.c程式如下:
/*com_writer.c*/#include<stdio.h>#include<stdlib.h>#include<string.h>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#include<unistd.h>#include<errno.h>#include<termios.h> #include"open_port.c"#include"set_com_config.c" #define HOST_COM_PORT 1 /*使用1表示PC機的串口1*/#define BUFFER_SIZE 30 /*最大緩衝大小*/ int main(void){ int fd; char buff[BUFFER_SIZE]; if((fd = open_port(HOST_COM_PORT)) < 0) /*開啟串口*/ { perror("open_port"); return 1; } if(set_com_config(fd, 115200, 8, 'N', 1) < 0) { perror("set_com_config"); return 1; } do { printf("Input some words(enter 'quit' to exit):"); memset(buff, 0, BUFFER_SIZE); if(fgets(buff, BUFFER_SIZE, stdin) == NULL) { perror("fgets"); break; } write(fd,buff,strlen(buff)); } while(strncmp(buff,"quit",4)); close(fd); return 0;}(2)目標機讀串口程式com_reader_arm9.c:
/*com_reader.c*/#include<stdio.h>#include<stdlib.h>#include<string.h>#include<sys/types.h>#include<sys/stat.h>#include<errno.h> #include<unistd.h>#include<termios.h>#include<fcntl.h> #include"open_port_arm9.c"#include"set_com_config.c" #define BUFFER_SIZE 30 /*最大緩衝區*/#define TARGET_COM_PORT 1 /*用1來表示目標機上的串口1*/ int main(void){ int fd; char buff[BUFFER_SIZE]; if((fd = open_port(TARGET_COM_PORT)) < 0) /*開啟串口*/ { perror("open_port"); return 1; } if(set_com_config(fd, 115200, 8, 'N', 1) < 0) /*配置串口*/ { perror("set_com_config"); return 1; } do { memset(buff, 0, BUFFER_SIZE); if(read(fd, buff, BUFFER_SIZE) > 0) printf("The received words are : %s", buff); } while(strncmp(buff, "quit", 4)); close(fd); return 0;} 最後,進行將com_writer.c用gcc編譯: gcc -g com_writer.c -o com_writer
將com_reader_arm9.c用arm-linux-gcc交叉編譯:arm-linux-gcc -g com_reader_arm9.c -o com_reader_arm9 並且將其NFS或者其他方法放在開發板上,然後開啟。
將串口線直連,這裡我是通過終端用telnet登陸到開發板,然後用nfs掛載檔案到開發板,留下串口用來通訊(之前用kermit利用串口相連通訊收到資料的時候會有異常,且不能正常退出,可能是互相佔用的問題),再開啟com_reader_arm9程式。接著在宿主機上運行com_writer,再裡面輸入字元,開發板上面就會收到。如所示:
宿主機開啟com_writer
目標機開啟com_reader_arm9
最後,我想總結下我弄這個程式所遇到的一些問題,希望在我以後知識提高的時候可以解決掉或者各位現在提供些參考意見。
問題1:能不能只用一個open_port.c函數就直接選擇USB轉串口、普通PC串口、開發板串口的選擇?之前按照書中的條件編譯#if #else #endif沒能實現,預設選擇普通PC串口,所以來源程式中我注釋掉了。
問題2:以上兩圖中,對於com_writer寫入的第一條語句時候com_reader_arm9並不能讀出,只能從第二條開始。之後我試著把new_cfg.c_cc[VMIN] = 1改成"=0”後再進行編譯串連,反倒在目標板上用com_reader_arm9不能讀出任何來自宿主機的寫資訊了。
問題3:在第一次成功實現串口單工通訊後,用quit退出後,想要再次實現串口通訊確不行了,得要開發板重啟後才能再次進行串口通訊。
本文中的程式參考了書籍《嵌入式Linux應用程式開發標準教程(第2版)》
原創文章,歡迎轉載,轉載請註明:blog.csdn.net/jjzhoujun2010
作者:Dream Fly