zynq操作系统: Linux驱动开发串口篇
一.簡介
??串口( UART)是一種非常常見的外設(shè), 串口在嵌入式開發(fā)領(lǐng)域當(dāng)中一般作為一種調(diào)試手段,通過串口將調(diào)試信息打印出來,或者通過串口發(fā)送指令給主機(jī)端進(jìn)行處理;當(dāng)然除了作為基本的調(diào)試手段之外,還可以通過串口與其他設(shè)備或傳感器進(jìn)行通信, 譬如有些 sensor 就使用了串口通信的方式與主機(jī)端進(jìn)行數(shù)據(jù)交互。
根據(jù)電平標(biāo)準(zhǔn)的不同,串口可以分為 TTL, RS232,RS485, RS422等這些,雖然它們的電平標(biāo)準(zhǔn)不同,但是卻都遵循相同的通信時序協(xié)議,所以呢,不管它是什么樣的電平標(biāo)準(zhǔn),對于我們軟件而言其驅(qū)動程序都是一樣的;一般主機(jī)端直接出來的電平信號就是 TTL
對于 ZYNQ 來說,其 PS 端只提供了兩個串口外設(shè) UART0 和 UART1,很明顯對于大一點的工程而言是不夠用的,此時可以使用 PL 端串口軟核外設(shè), xilinx 同樣提供了相應(yīng)的 IP核 可供調(diào)用如這次使用的UART16550
二.基礎(chǔ)知識準(zhǔn)備
??串口在嵌入式 Linux 系統(tǒng)當(dāng)中經(jīng)常作為系統(tǒng)的標(biāo)準(zhǔn)輸入、輸出設(shè)備,而提到串口,那么就不得不引出另外兩個概念:終端與控制臺。關(guān)于這兩個概念,很多人估計是對此傻傻分不清!
??這里參考了原子文檔里對終端的理解
??1、 什么是終端 Terminal
??終端就是處理主機(jī)輸入、 輸出的一套設(shè)備,它用來顯示主機(jī)運算的輸出,并且接受主機(jī)要求的輸入。 典型的終端包括顯示器鍵盤套件,打印機(jī)打字機(jī)套件等。 其實本質(zhì)上也就一句話,能接受輸入、能顯示輸出,這就夠了,不管到了什么時代,終端始終扮演著人機(jī)接口的角色, 所謂 Terminal,即機(jī)器的邊緣!
只要能提供給計算機(jī)輸入和輸出功能,它就是終端,而與其所在的位置無關(guān)。
??2、終端的分類
??? 本地終端:例如對于我們的個人 PC 機(jī)來說, PC 機(jī)連接了顯示器、鍵盤以及鼠標(biāo)等設(shè)備, 這樣的一個顯示器/鍵盤組合就是一個本地終端;同樣對于開發(fā)板來說也是如此,開發(fā)板也可以連接一個LCD 顯示器、鍵盤和鼠標(biāo)等,同樣可以構(gòu)成本地終端。
??? 用串口連接的遠(yuǎn)程終端:對于嵌入式 Linux 開發(fā)來說,這是最常見的終端—串口終端。 譬如我們的開發(fā)板通過串口線連接到一個帶有顯示器和鍵盤的 PC 機(jī), 在 PC 機(jī)通過運行一個終端模擬程序,譬如 Windows 超級終端、 putty、 MobaXterm、 SecureCRT 等來獲取并顯示開發(fā)板通過串口發(fā)出的數(shù)據(jù)、同樣還可以通過這些終端模擬程序?qū)⒂脩魯?shù)據(jù)通過串口線發(fā)送給開發(fā)板。
??? 基于網(wǎng)絡(luò)的遠(yuǎn)程終端:譬如我們可以通過 ssh、 Telnet 這些協(xié)議登錄到一個遠(yuǎn)程主機(jī)。以上列舉的這些都是終端,前兩類稱之為物理終端; 最后一個稱之為偽終端。 前兩類都是在本地就直接關(guān)聯(lián)了物理設(shè)備的, 譬如顯示器、鼠標(biāo)鍵盤、 串口等之類的,這種終端叫做物理終端,而第三類在本地則沒有關(guān)聯(lián)任何物理設(shè)備,注意,不要把物理網(wǎng)卡當(dāng)成終端關(guān)聯(lián)的物理設(shè)備,它們與終端并不直接相關(guān),所以這類不直接關(guān)聯(lián)物理設(shè)備的終端叫做偽終端。
??3、什么是控制臺 Console
能夠顯示系統(tǒng)信息的終端就叫控制臺。 控制臺的概念與終端含義非常相近,其實現(xiàn)在我們經(jīng)常用它們表示相同的東西, linux 中基本也已經(jīng)淡化了控制臺和終端的區(qū)別。雖然說它們之間的含義非常相近,但還是有一些區(qū)別:
??? 能夠顯示系統(tǒng)信息的終端就叫控制臺,這說明它們之間是一個包含關(guān)系,控制臺一定是終端,而終端則不一定是控制臺,也就是說控制臺是終端的真子集。
??? 控制臺只有一個??吹竭@里大家可能就有疑問了,我們使用的開發(fā)板可以通過串口終端打印信息,同樣我們也可以通過 ssh 協(xié)議登錄連接到開發(fā)板,同樣也會打開一個偽終端,并且也可以在偽終端顯示打印信息,那么它倆不都可以認(rèn)為是控制臺嗎?其實并非如此,上面說到的顯示系統(tǒng)信息指的是開發(fā)板啟動的時候,所有的打印系統(tǒng)信息都會顯示到這個終端上,那么這個終端才叫做控制臺,所有由此可以知道,譬如我們的開發(fā)板在啟動時,所有的打印信息都會通過串口輸出,所以我們的串口終端就是控制臺, 而通過 ssh 遠(yuǎn)程登錄開發(fā)板打開的偽終端并不是控制臺,因為啟動時的打印信息是不可能輸出到這個偽終端上的。
??? 控制臺是計算機(jī)本身的設(shè)備,一個計算機(jī)只有一個控制臺。譬如開發(fā)板的串口這就是開發(fā)板本身的設(shè)備。
講到這里相信大家都應(yīng)該清楚了,它們之間的共同點和小小的區(qū)別,其實我們也不用去刻意區(qū)分它們之間的異同,因為 Linux 中它們之間的區(qū)別基本完全淡化了。
??4、 Linux 下終端對應(yīng)的設(shè)備文件
在 Linux 當(dāng)中,一切皆是文件。當(dāng)然,終端也不例外,每一個終端設(shè)備在/dev 目錄下都
有一個對應(yīng)的設(shè)備文件。
??? /dev/ttyX 設(shè)備文件: tty( teletype 的簡稱) 是最令人熟悉的了,在 Linux 中, /dev/ttyX 代表的都是上述的物理終端,其中, /dev/tty1~/dev/tty63 代表的是本地終端,也就是接到本機(jī)的鍵盤顯示器可以操作的終端。事實上 Linux 內(nèi)核在初始化時會生成 63 個本地終端。
??? /dev/console 設(shè)備文件:這個設(shè)備文件表示當(dāng)前系統(tǒng)的控制臺,如果我們往這個設(shè)備文件輸入信息,它一定會顯示在我們的控制臺終端中,譬如“echo Hello > /dev/console”。
??? 串口終端 ttyPSX:對于我們的調(diào)試版來說,有一個ps的串口,12個PL的串口所以也對應(yīng)了13串口終端設(shè)備文件,如下所示:
【圖片】
當(dāng)然這里的名字 ttyPS0,ttyS0不是固定的,這個具體的命名跟串口的驅(qū)動程序有關(guān),名字不是統(tǒng)一的,但是名字前綴一般都以“ tty”開頭,以表明它是一個終端設(shè)備
三.Linux下的串口調(diào)用
3.1介紹
??串口的工作原理肯定是跟之前裸機(jī)中介紹到的誰一樣的,重點在編程時linux對串口不同的調(diào)用方式
??Linux 系統(tǒng)中 UART 串口驅(qū)動框架結(jié)構(gòu)圖如下所示
??簡單地說可以分為兩層: UART 驅(qū)動層和 tty 驅(qū)動層。 從圖中可以看到,下層 UART 驅(qū)動層直接與硬件相接觸,也就是說它才是真正的 UART驅(qū)動程序,它提供了 UART 硬件操作相關(guān)函數(shù)集 uart_ops;而上層 tty 驅(qū)動層則會將 UART 設(shè)備描述成一個 tty(終端)設(shè)備, 并向內(nèi)核注冊 tty 字符設(shè)備,提供字符設(shè)備操作函數(shù)集ops,其實 ops 函數(shù)集中經(jīng)過層層跳轉(zhuǎn)最終執(zhí)行的就是 uart 驅(qū)動層的 uart_ops。
首先對于驅(qū)動開發(fā)工程師來說, tty 驅(qū)動層并不需要我們?nèi)崿F(xiàn),它會在我們注冊 UART
驅(qū)動的過程中自動構(gòu)建出來,對于我們所使用的內(nèi)核源碼來說, xilinx 官方已經(jīng)提供了串口驅(qū)動程序,所以只需要在應(yīng)用層封裝一層驅(qū)動了(真實令人感到偷懶和開心的消息)
(如果需要自己寫的話,自行查找原子的免費開發(fā)文檔吧,有很詳細(xì)的講解)
3.2 命令行調(diào)用測試
??老規(guī)矩,還是先使用命令行對串口進(jìn)行一個簡單的測試吧
第一步先要看我們有沒有生成的tty串口設(shè)備,不確定是xilinx的默認(rèn)設(shè)置還是怎么的,不論是交叉編譯menuconfig還是官方編譯的config kernel都對最大支持設(shè)備數(shù)有限制,我這邊獨到的最大值為4,所以需要改,配置內(nèi)核時找到如下路徑,對下面的最大支持設(shè)備改為自己的設(shè)備數(shù),(沒用官方推薦模式的可能默認(rèn)連8250的設(shè)備樹都沒有支持,往下翻,找到并勾選)
??stty查看串口參數(shù)
??stty -F /dev/ttyS0 -a
??查看串口1(/dev/ttyS0)當(dāng)前的參數(shù),包括波特率、數(shù)據(jù)位等。
??stty設(shè)置串口參數(shù)
??stty -F /dev/ttyS0 ispeed 115200 ospeed 115200 cs8
??該命令將串口1(/dev/ttyS0)設(shè)置成115200波特率,8位數(shù)據(jù)模式。一般情況下設(shè)置這兩個參數(shù)就可以了,如果顯示數(shù)據(jù)亂碼,可能還需要設(shè)置其它參數(shù),使用man??查看stty其它設(shè)置選項
??cat打印串口數(shù)據(jù)
??cat /dev/ttyS0
??串口數(shù)據(jù)就可以在終端上顯示了。
??發(fā)送數(shù)據(jù)
??echo “HelloWorld” >/dev/ttyS0
3.3測試代碼
然后就是編寫代碼了,下面是增改可用的串口驅(qū)動頭文件:
#ifndef _SERIAL_H_ #define _SERIAL_H_#include<stdio.h> /*Standard input/output definitions*/ #include<stdlib.h> /*Standard function library definitions*/ #include<unistd.h> /*Unix Standard function definition*/ #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> /*File control definition*/ #include<termios.h> /*PPSIX Terminal control definition*/ #include<errno.h> /*Error number definition*/ #include<string.h> #include<sys/time.h>#define FALSE -1 #define TRUE 0 #define B76800 0010020 #define B153600 0010021 #define B307200 0010022 #define B614400 0010023 // /******************************************************************* * name: serial_open * function: Opens the serial port and returns the serial device file description * @param[in]: port :serial number(ttyS0,ttyS1) * @return: Correct returns fd, error returns -1 *******************************************************************/ int serial_open(char* port); /******************************************************************* * name: serial_init() * function: serial_init * @param[in]: fd : File descriptor * @param[in] speed : Serial speed * @param[in] flow_ctrl Data flow control * @param[in] databits The data bits , either 7 or 8 * @param[in] stopbits The stop bit , either 1 or 2 * @param[in] parity The value of effect type ,choose N,E,O, S * * return: Correct returns 0, error returns -1 *******************************************************************/ int serial_init(int fd, int speed,int flow_ctrl,int databits,int stopbits,int parity); /******************************************************************* * name: serial_recv * function: Receive serial data * @param[in]: fd: File descriptor * rcv_buf : The data from the receiving serial port is stored in the rcv_buf buffer * data_len : The length of the data to read * * return: The actual length of the data read *******************************************************************/ int serial_recv(int fd, char *rcv_buf,int data_len); /******************************************************************** * name: serial_send * function: To send data * @param[in]: fd : File descriptor * send_buf :Stores serial port to send data * data_len :The length of the data to send * return: Correct returns 0, error returns -1 *******************************************************************/ int serial_send(int fd, char *send_buf,int data_len); /******************************************************************* * name: serial_set * function: Set serial port data bit, stop bit and effect check bit * @param[in]: fd File descriptor * speed serial speed * flow_ctrl Data flow control * databits The data bits , either 7 or 8 * topbits The stop bit , either 1 or 2 * parity The value of effect type ,choose N,E,O, S *return: Correct returns 0, error returns -1 *******************************************************************/ int serial_set(int fd,int speed,int flow_ctrl,int databits,int stopbits,int parity); /******************************************************************* * name: serial_close * function: Closes the serial port and returns the serial device file description * @param[in]: fd :File descriptor * @param[in]: port :serial number(ttyS0,ttyS1) * @return: void *******************************************************************/ void serial_close(int fd);#endif再然后是包含的庫文件
#ifndef _PACKET_H #define _PACKET_H#include <stdint.h>// #define DEBUG #ifdef DEBUG #define PRINT(fmt, ...) printf(fmt, ##__VA_ARGS__) #else #define PRINT(fmt, ...) #endiftypedef enum {UART_SND_REQ = 0x11,UART_UPLOAD = 0x22,UART_ATTR_SET_REQ = 0x33,UART_ATTR_GET_REQ = 0x44,UART_ATTR_GET_ACK = 0x44,JTAG_CONNECT_SET_REQ = 0x55,JTAG_CONNECT_GET_REQ = 0x66,JTAG_CONNECT_GET_ACK = 0x66,IP_ADDR_SET_REQ = 0x77,IP_ADDR_GET_REQ = 0x71,IP_ADDR_GET_ACK = 0x71,SYSTEM_RESET = 0x72 } MANAGE_TYPE;typedef struct {uint32_t PackageLen;uint8_t ManageType; } __attribute__((__packed__)) HEADER;typedef struct {HEADER header;uint8_t PortID;uint8_t Data[]; } __attribute__((__packed__)) UART_SND;typedef struct {HEADER header;uint8_t PortID;uint8_t Baudrate;uint8_t DataBit;uint8_t StopBit;uint8_t Pirity;uint8_t FlowCtrl;uint8_t CheckSum; } __attribute__((__packed__)) UART_SET_REQ;typedef struct {HEADER header;uint8_t PortID;uint8_t CheckSum; } __attribute__((__packed__)) UART_GET_REQ;#define UART_GET_ACK UART_SET_REQ #define JTAG_SET_REQ UART_GET_REQ #define JTAG_INQ_REQ UART_GET_REQ //Jtag connect status inquire request #define JTAG_INQ_ACK UART_GET_REQ //Jtag connect status inquire answer #define IP_INQ_REQ UART_GET_REQ #define COMMON_ACK UART_GET_REQ #define IP_INQ_ACK IP_SET_REQtypedef struct {HEADER header;uint8_t IpAddr[4];uint8_t NetMask[4];uint8_t GateWay[4];uint8_t CheckSum; } __attribute__((__packed__)) IP_SET_REQ;typedef struct {int sock;char status;uint8_t ip[20]; } TCP_CLIENT;#endif之后就是驅(qū)動c文件了
#include "serial.h" #include "packet.h"/******************************************************************* * 名稱: serial_open * 功能: 打開串口并返回串口設(shè)備文件描述 * 入口參數(shù): fd :文件描述符 port :串口號(ttyS0,ttyS1) * 出口參數(shù): 正確返回為fd,錯誤返回為-1 *******************************************************************/ int serial_open(char* port) { int fd;fd = open( port, O_RDWR|O_NOCTTY);// fd = open( port, O_RDWR|O_NOCTTY|O_NDELAY);if (FALSE == fd) { perror("Can't Open Serial Port"); return(FALSE); } //恢復(fù)串口為阻塞狀態(tài) // if(fcntl(fd, F_SETFL, 0) < 0) // { // PRINT("fcntl failed!\n"); // return(FALSE); // } // else // { // // PRINT("fcntl=%d\n",fcntl(fd, F_SETFL,0)); // } // // //測試是否為終端設(shè)備 // // if(0 == isatty(STDIN_FILENO)) // // { // // PRINT("standard input is not a terminal device\n"); // // return(FALSE); // // } // // else // // { // // // PRINT("%s is a terminal device!\n", port); // // } return fd; } /******************************************************************* * 名稱: serial_close * 功能: 關(guān)閉串口并返回串口設(shè)備文件描述 * 入口參數(shù): fd :文件描述符 port :串口號(ttyS0,ttyS1) * 出口參數(shù): void *******************************************************************/ void serial_close(int fd) { close(fd); } /******************************************************************* * 名稱: serial_set * 功能: 設(shè)置串口數(shù)據(jù)位,停止位和效驗位 * 入口參數(shù): fd 串口文件描述符 * speed 串口速度 * flow_ctrl 數(shù)據(jù)流控制 * databits 數(shù)據(jù)位 取值為 7 或者8 * topbits 停止位 取值為 1 或者2 * parity 效驗類型 取值為N,E,O,,S *出口參數(shù): 正確返回為1,錯誤返回為0 *******************************************************************/ int serial_set(int fd,int speed,int flow_ctrl,int databits,int stopbits,int parity) { int i; //int status; int speed_arr[] = { B115200, B38400, B19200, B9600, B4800, B2400, B1200, B614400,B76800}; //int name_arr[] = {115200, 38400, 19200, 9600, 4800, 2400, 1200, 300}; // int name_arr[] = {4, 3, 2, 1, 0, 2400, 1200, 300}; int name_arr[] = {115200, 38400, 19200, 9600, 4800, 2400, 1200, 614400,76800};struct termios options;/*tcgetattr(fd,&options)得到與fd指向?qū)ο蟮南嚓P(guān)參數(shù),并將它們保存于options,該函數(shù)還可以測試配置是否正確,該串口是否可用等。若調(diào)用成功,函數(shù)返回值為0,若調(diào)用失敗,函數(shù)返回值為1. */ if( tcgetattr( fd,&options) != 0) { perror("SetupSerial 1"); return(FALSE); } //設(shè)置串口輸入波特率和輸出波特率 for ( i= 0; i < sizeof(speed_arr) / sizeof(int); i++) { if (speed == name_arr[i]) { cfsetispeed(&options, speed_arr[i]); // printf("ispeed is %d----------------------------\n",speed_arr[i]); cfsetospeed(&options, speed_arr[i]); // printf("ospeed is %d----------------------------\n",speed_arr[i]); } } //修改控制模式,保證程序不會占用串口 options.c_cflag |= CLOCAL; //修改控制模式,使得能夠從串口中讀取輸入數(shù)據(jù) options.c_cflag |= CREAD;//清bit位 關(guān)閉字符映射 0x0a 0x0doptions.c_iflag &= ~(INLCR|ICRNL);//清bit位 關(guān)閉流控字符 0x11 0x13options.c_iflag &= ~(IXON);options.c_cflag |= CBAUDEX; //設(shè)置特定波特率的標(biāo)志位.//設(shè)置數(shù)據(jù)流控制 switch(flow_ctrl) { case 0 ://不使用流控制 options.c_cflag &= ~CRTSCTS; break; case 1 ://使用硬件流控制 options.c_cflag |= CRTSCTS; break;case 2 ://使用硬件流控制DTR/DSRoptions.c_cflag &= ~CRTSCTS;break;case 3 ://使用軟件流控制 options.c_cflag |= IXON | IXOFF | IXANY; break; } //設(shè)置數(shù)據(jù)位 //屏蔽其他標(biāo)志位 options.c_cflag &= ~CSIZE; switch (databits) { case 5 : options.c_cflag |= CS5; break; case 6 : options.c_cflag |= CS6; break; case 7 : options.c_cflag |= CS7; break; case 8: options.c_cflag |= CS8; break; default: fprintf(stderr,"Unsupported data size\n"); return (FALSE); } //設(shè)置校驗位 switch (parity) { case 0: case 'N': //無奇偶校驗位。 options.c_cflag &= ~PARENB; options.c_iflag &= ~INPCK; break; case 1: case 'O'://設(shè)置為奇校驗 options.c_cflag |= (PARODD | PARENB); options.c_iflag |= INPCK; break; case 2: case 'E'://設(shè)置為偶校驗 options.c_cflag |= PARENB; options.c_cflag &= ~PARODD; options.c_iflag |= INPCK; break; case 's': case 'S': //設(shè)置為空格 options.c_cflag &= ~PARENB; options.c_cflag &= ~CSTOPB; break; default: fprintf(stderr,"Unsupported parity\n"); return (FALSE); } // 設(shè)置停止位 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); } //修改輸出模式,原始數(shù)據(jù)輸出 options.c_oflag &= ~OPOST; options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); //options.c_lflag &= ~(ISIG | ICANON); //設(shè)置等待時間和最小接收字符 options.c_cc[VTIME] = 1; /* 讀取一個字符等待1*(1/10)s */ options.c_cc[VMIN] = 32; /* 讀取字符的最少個數(shù)為1 */ //如果發(fā)生數(shù)據(jù)溢出,接收數(shù)據(jù),但是不再讀取 刷新收到的數(shù)據(jù)但是不讀 tcflush(fd,TCIFLUSH); //激活配置 (將修改后的termios數(shù)據(jù)設(shè)置到串口中) if (tcsetattr(fd,TCSANOW,&options) != 0) { perror("com set error!\n"); return (FALSE); } return (TRUE); } /******************************************************************* * 名稱: serial_init() * 功能: 串口初始化 * 入口參數(shù): fd : 文件描述符 * speed : 串口速度 * flow_ctrl 數(shù)據(jù)流控制 * databits 數(shù)據(jù)位 取值為 7 或者8 * stopbits 停止位 取值為 1 或者2 * parity 效驗類型 取值為N,E,O,,S * * 出口參數(shù): 正確返回為0,錯誤返回為-1 *******************************************************************/ int serial_init(int fd, int speed, int flow_ctrl, int databits, int stopbits, int parity) { //設(shè)置串口數(shù)據(jù)幀格式 //if (serial_set(fd,19200,0,8,1,'N') == FALSE)if((speed < 0) || (speed > 10000000))return 1;if (serial_set(fd,speed,flow_ctrl,databits,stopbits,parity) == FALSE){return FALSE;}else { return TRUE; } } /******************************************************************* * 名稱: serial_recv * 功能: 接收串口數(shù)據(jù) * 入口參數(shù): fd : 文件描述符 * rcv_buf :接收串口中數(shù)據(jù)存入rcv_buf緩沖區(qū)中 * data_len :數(shù)據(jù)的長度 * * 出口參數(shù): 實際讀到的長度 *******************************************************************/ int serial_recv(int fd, char *rcv_buf,int data_len) { int len,fs_sel; fd_set fs_read; struct timeval time; FD_ZERO(&fs_read); FD_SET(fd,&fs_read); time.tv_sec = 10; time.tv_usec = 0; len = read(fd,rcv_buf,data_len); // printf("I am right!(version1.2) len = %d fs_sel = %d\n",len,fs_sel); return len; //使用select實現(xiàn)串口的多路通信 /* 入口參數(shù):①:ndfs:select() 中監(jiān)視的文件句柄,一般設(shè)為要監(jiān)視的文件中的最大文件號加一。②:rdfds:select()監(jiān)視的可讀文件句柄集合,當(dāng)rdfds映象的文件句柄狀態(tài)變成可讀時系統(tǒng)告訴select函數(shù)返回。這個集合中有一個文件可讀,select就會返回一個大于0的值,表示有文件可讀,如果沒有可讀的文件,則根據(jù)timeout參數(shù)再判斷是否超時,若超出timeout的時間,select返回0,若發(fā)生錯誤返回負(fù)值,可以傳入NULL值,表示不關(guān)心任何文件的讀變化;③:wtfds: select()監(jiān)視的可寫文件句柄集合,當(dāng)wtfds映象的文件句柄狀態(tài)變成可寫時系統(tǒng)告訴select函數(shù)返回。如果這個集合中有一個文件可寫,select就會返回一個大于0的值,表示有文件可寫,如果沒有可寫的文件,則根據(jù)timeout參數(shù)再判斷是否超時,若超出timeout的時間,select返回0,若發(fā)生錯誤返回負(fù)值,可以傳入NULL值,表示不關(guān)心任何文件的寫變化。④:exfds:select()監(jiān)視的異常文件句柄集合,當(dāng)exfds映象的文件句柄上有特殊情況發(fā)生時系統(tǒng)會告訴select函數(shù)返回。⑤:timeout:select()的超時結(jié)束時間 *//* FD_ZERO(fd_set *fdset):清空fdset與所有文件句柄的聯(lián)系。FD_SET(int fd, fd_set *fdset):建立文件句柄fd與fdset的聯(lián)系。FD_CLR(int fd, fd_set *fdset):清除文件句柄fd與fdset的聯(lián)系。FD_ISSET(int fd, fdset *fdset):檢查fdset聯(lián)系的文件句柄fd是否可讀寫,>0表示可讀寫。*/// fs_sel = select(fd+1,&fs_read,NULL,NULL,&time); // // printf("fs_sel = %d\n",fs_sel); // if(fs_sel) // { // len = read(fd,rcv_buf,data_len); // // printf("I am right!(version1.2) len = %d fs_sel = %d\n",len,fs_sel); // return len; // } // else // { // // printf("Sorry,I am wrong!"); // return FALSE; // } } /******************************************************************** * 名稱: serial_send * 功能: 發(fā)送數(shù)據(jù) * 入口參數(shù): fd :文件描述符 * send_buf :存放串口發(fā)送數(shù)據(jù) * data_len :一幀數(shù)據(jù)的個數(shù) * 出口參數(shù): 正確返回為0,錯誤返回為-1 *******************************************************************/ int serial_send(int fd, char *send_buf,int data_len) { int len = 0; len = write(fd,send_buf,data_len); if (len == data_len ) { PRINT("send data is %s\n",send_buf); return TRUE; } else { tcflush(fd,TCOFLUSH); return FALSE; } }測試demo
僅僅是修改中,read是阻塞型的,所以測試的話直接while(1)吧,先寫了一路,沒開多線程
#include "packet.h" #include "serial.h" #include<stdio.h> /*標(biāo)準(zhǔn)輸入輸出定義*/ #include<stdlib.h> /*標(biāo)準(zhǔn)函數(shù)庫定義*/ #include<unistd.h> /*Unix 標(biāo)準(zhǔn)函數(shù)定義*/ #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> /*文件控制定義*/ #define __USE_MISC #include<termios.h> /*PPSIX 終端控制定義*/ #include<errno.h> /*錯誤號定義*/ #include<string.h> #include <sys/mman.h> #include <poll.h>#define FALSE -1 #define TRUE 0 #define UART_NUM 12void Uart_Out32(unsigned int * Addr, unsigned int Value) {volatile unsigned int *LocalAddr = (volatile unsigned int *)Addr;*LocalAddr = Value; }unsigned int * Uart_In32(unsigned int * Addr) {return *(volatile unsigned int *) Addr; }uint8_t *uartdev[UART_NUM] = {"/dev/ttyS0", "/dev/ttyS1", "/dev/ttyS2", "/dev/ttyS3", "/dev/ttyS4", "/dev/ttyS5", "/dev/ttyS6", "/dev/ttyS7", "/dev/ttyS8", "/dev/ttyS9", "/dev/ttyS10", "/dev/ttyS11", };int main(int argc, char **argv) { int fd; //文件描述符 int err; //返回調(diào)用函數(shù)的狀態(tài) int len; int i; char rcv_buf[0x100]; char send_buf[20]="HelloWorld";fd = serial_open(uartdev[0]); //打開串口,返回文件描述符 err = serial_init(fd,614400,0,8,1,'N'); printf("Set Port Exactly!\n"); for(i = 0;i < 3;i++) { len = serial_send(fd,send_buf,10); if(len > 0) printf(" %d time send %d data successful\n",i,len); else printf("send data failed!\n"); sleep(2); } // serial_close(fd); while (1) //循環(huán)讀取數(shù)據(jù) { memset(rcv_buf,0,256);len = serial_recv(fd, rcv_buf,100); if(len > 0) { // rcv_buf[len] = '\0'; for(i = 0;i < len;i++)printf("receive data is 0x%x\n",rcv_buf[i]); printf("len = %d\n",len); } else { printf("cannot receive data\n"); } // sleep(2); } // serial_close(fd); }簡單好用,對于波特率的修改和進(jìn)階在補(bǔ)充篇講吧
總結(jié)
以上是生活随笔為你收集整理的zynq操作系统: Linux驱动开发串口篇的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 字符串匹配算法知多少?
- 下一篇: Vscode代码量统计