zynq操作系统: Linux驱动开发串口篇
一.簡介
??串口( UART)是一種非常常見的外設, 串口在嵌入式開發領域當中一般作為一種調試手段,通過串口將調試信息打印出來,或者通過串口發送指令給主機端進行處理;當然除了作為基本的調試手段之外,還可以通過串口與其他設備或傳感器進行通信, 譬如有些 sensor 就使用了串口通信的方式與主機端進行數據交互。
根據電平標準的不同,串口可以分為 TTL, RS232,RS485, RS422等這些,雖然它們的電平標準不同,但是卻都遵循相同的通信時序協議,所以呢,不管它是什么樣的電平標準,對于我們軟件而言其驅動程序都是一樣的;一般主機端直接出來的電平信號就是 TTL
對于 ZYNQ 來說,其 PS 端只提供了兩個串口外設 UART0 和 UART1,很明顯對于大一點的工程而言是不夠用的,此時可以使用 PL 端串口軟核外設, xilinx 同樣提供了相應的 IP核 可供調用如這次使用的UART16550
二.基礎知識準備
??串口在嵌入式 Linux 系統當中經常作為系統的標準輸入、輸出設備,而提到串口,那么就不得不引出另外兩個概念:終端與控制臺。關于這兩個概念,很多人估計是對此傻傻分不清!
??這里參考了原子文檔里對終端的理解
??1、 什么是終端 Terminal
??終端就是處理主機輸入、 輸出的一套設備,它用來顯示主機運算的輸出,并且接受主機要求的輸入。 典型的終端包括顯示器鍵盤套件,打印機打字機套件等。 其實本質上也就一句話,能接受輸入、能顯示輸出,這就夠了,不管到了什么時代,終端始終扮演著人機接口的角色, 所謂 Terminal,即機器的邊緣!
只要能提供給計算機輸入和輸出功能,它就是終端,而與其所在的位置無關。
??2、終端的分類
??? 本地終端:例如對于我們的個人 PC 機來說, PC 機連接了顯示器、鍵盤以及鼠標等設備, 這樣的一個顯示器/鍵盤組合就是一個本地終端;同樣對于開發板來說也是如此,開發板也可以連接一個LCD 顯示器、鍵盤和鼠標等,同樣可以構成本地終端。
??? 用串口連接的遠程終端:對于嵌入式 Linux 開發來說,這是最常見的終端—串口終端。 譬如我們的開發板通過串口線連接到一個帶有顯示器和鍵盤的 PC 機, 在 PC 機通過運行一個終端模擬程序,譬如 Windows 超級終端、 putty、 MobaXterm、 SecureCRT 等來獲取并顯示開發板通過串口發出的數據、同樣還可以通過這些終端模擬程序將用戶數據通過串口線發送給開發板。
??? 基于網絡的遠程終端:譬如我們可以通過 ssh、 Telnet 這些協議登錄到一個遠程主機。以上列舉的這些都是終端,前兩類稱之為物理終端; 最后一個稱之為偽終端。 前兩類都是在本地就直接關聯了物理設備的, 譬如顯示器、鼠標鍵盤、 串口等之類的,這種終端叫做物理終端,而第三類在本地則沒有關聯任何物理設備,注意,不要把物理網卡當成終端關聯的物理設備,它們與終端并不直接相關,所以這類不直接關聯物理設備的終端叫做偽終端。
??3、什么是控制臺 Console
能夠顯示系統信息的終端就叫控制臺。 控制臺的概念與終端含義非常相近,其實現在我們經常用它們表示相同的東西, linux 中基本也已經淡化了控制臺和終端的區別。雖然說它們之間的含義非常相近,但還是有一些區別:
??? 能夠顯示系統信息的終端就叫控制臺,這說明它們之間是一個包含關系,控制臺一定是終端,而終端則不一定是控制臺,也就是說控制臺是終端的真子集。
??? 控制臺只有一個??吹竭@里大家可能就有疑問了,我們使用的開發板可以通過串口終端打印信息,同樣我們也可以通過 ssh 協議登錄連接到開發板,同樣也會打開一個偽終端,并且也可以在偽終端顯示打印信息,那么它倆不都可以認為是控制臺嗎?其實并非如此,上面說到的顯示系統信息指的是開發板啟動的時候,所有的打印系統信息都會顯示到這個終端上,那么這個終端才叫做控制臺,所有由此可以知道,譬如我們的開發板在啟動時,所有的打印信息都會通過串口輸出,所以我們的串口終端就是控制臺, 而通過 ssh 遠程登錄開發板打開的偽終端并不是控制臺,因為啟動時的打印信息是不可能輸出到這個偽終端上的。
??? 控制臺是計算機本身的設備,一個計算機只有一個控制臺。譬如開發板的串口這就是開發板本身的設備。
講到這里相信大家都應該清楚了,它們之間的共同點和小小的區別,其實我們也不用去刻意區分它們之間的異同,因為 Linux 中它們之間的區別基本完全淡化了。
??4、 Linux 下終端對應的設備文件
在 Linux 當中,一切皆是文件。當然,終端也不例外,每一個終端設備在/dev 目錄下都
有一個對應的設備文件。
??? /dev/ttyX 設備文件: tty( teletype 的簡稱) 是最令人熟悉的了,在 Linux 中, /dev/ttyX 代表的都是上述的物理終端,其中, /dev/tty1~/dev/tty63 代表的是本地終端,也就是接到本機的鍵盤顯示器可以操作的終端。事實上 Linux 內核在初始化時會生成 63 個本地終端。
??? /dev/console 設備文件:這個設備文件表示當前系統的控制臺,如果我們往這個設備文件輸入信息,它一定會顯示在我們的控制臺終端中,譬如“echo Hello > /dev/console”。
??? 串口終端 ttyPSX:對于我們的調試版來說,有一個ps的串口,12個PL的串口所以也對應了13串口終端設備文件,如下所示:
【圖片】
當然這里的名字 ttyPS0,ttyS0不是固定的,這個具體的命名跟串口的驅動程序有關,名字不是統一的,但是名字前綴一般都以“ tty”開頭,以表明它是一個終端設備
三.Linux下的串口調用
3.1介紹
??串口的工作原理肯定是跟之前裸機中介紹到的誰一樣的,重點在編程時linux對串口不同的調用方式
??Linux 系統中 UART 串口驅動框架結構圖如下所示
??簡單地說可以分為兩層: UART 驅動層和 tty 驅動層。 從圖中可以看到,下層 UART 驅動層直接與硬件相接觸,也就是說它才是真正的 UART驅動程序,它提供了 UART 硬件操作相關函數集 uart_ops;而上層 tty 驅動層則會將 UART 設備描述成一個 tty(終端)設備, 并向內核注冊 tty 字符設備,提供字符設備操作函數集ops,其實 ops 函數集中經過層層跳轉最終執行的就是 uart 驅動層的 uart_ops。
首先對于驅動開發工程師來說, tty 驅動層并不需要我們去實現,它會在我們注冊 UART
驅動的過程中自動構建出來,對于我們所使用的內核源碼來說, xilinx 官方已經提供了串口驅動程序,所以只需要在應用層封裝一層驅動了(真實令人感到偷懶和開心的消息)
(如果需要自己寫的話,自行查找原子的免費開發文檔吧,有很詳細的講解)
3.2 命令行調用測試
??老規矩,還是先使用命令行對串口進行一個簡單的測試吧
第一步先要看我們有沒有生成的tty串口設備,不確定是xilinx的默認設置還是怎么的,不論是交叉編譯menuconfig還是官方編譯的config kernel都對最大支持設備數有限制,我這邊獨到的最大值為4,所以需要改,配置內核時找到如下路徑,對下面的最大支持設備改為自己的設備數,(沒用官方推薦模式的可能默認連8250的設備樹都沒有支持,往下翻,找到并勾選)
??stty查看串口參數
??stty -F /dev/ttyS0 -a
??查看串口1(/dev/ttyS0)當前的參數,包括波特率、數據位等。
??stty設置串口參數
??stty -F /dev/ttyS0 ispeed 115200 ospeed 115200 cs8
??該命令將串口1(/dev/ttyS0)設置成115200波特率,8位數據模式。一般情況下設置這兩個參數就可以了,如果顯示數據亂碼,可能還需要設置其它參數,使用man??查看stty其它設置選項
??cat打印串口數據
??cat /dev/ttyS0
??串口數據就可以在終端上顯示了。
??發送數據
??echo “HelloWorld” >/dev/ttyS0
3.3測試代碼
然后就是編寫代碼了,下面是增改可用的串口驅動頭文件:
#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之后就是驅動c文件了
#include "serial.h" #include "packet.h"/******************************************************************* * 名稱: serial_open * 功能: 打開串口并返回串口設備文件描述 * 入口參數: fd :文件描述符 port :串口號(ttyS0,ttyS1) * 出口參數: 正確返回為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); } //恢復串口為阻塞狀態 // if(fcntl(fd, F_SETFL, 0) < 0) // { // PRINT("fcntl failed!\n"); // return(FALSE); // } // else // { // // PRINT("fcntl=%d\n",fcntl(fd, F_SETFL,0)); // } // // //測試是否為終端設備 // // 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 * 功能: 關閉串口并返回串口設備文件描述 * 入口參數: fd :文件描述符 port :串口號(ttyS0,ttyS1) * 出口參數: void *******************************************************************/ void serial_close(int fd) { close(fd); } /******************************************************************* * 名稱: serial_set * 功能: 設置串口數據位,停止位和效驗位 * 入口參數: fd 串口文件描述符 * speed 串口速度 * flow_ctrl 數據流控制 * databits 數據位 取值為 7 或者8 * topbits 停止位 取值為 1 或者2 * parity 效驗類型 取值為N,E,O,,S *出口參數: 正確返回為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指向對象的相關參數,并將它們保存于options,該函數還可以測試配置是否正確,該串口是否可用等。若調用成功,函數返回值為0,若調用失敗,函數返回值為1. */ if( tcgetattr( fd,&options) != 0) { perror("SetupSerial 1"); return(FALSE); } //設置串口輸入波特率和輸出波特率 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; //修改控制模式,使得能夠從串口中讀取輸入數據 options.c_cflag |= CREAD;//清bit位 關閉字符映射 0x0a 0x0doptions.c_iflag &= ~(INLCR|ICRNL);//清bit位 關閉流控字符 0x11 0x13options.c_iflag &= ~(IXON);options.c_cflag |= CBAUDEX; //設置特定波特率的標志位.//設置數據流控制 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; } //設置數據位 //屏蔽其他標志位 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); } //設置校驗位 switch (parity) { case 0: case 'N': //無奇偶校驗位。 options.c_cflag &= ~PARENB; options.c_iflag &= ~INPCK; break; case 1: case 'O'://設置為奇校驗 options.c_cflag |= (PARODD | PARENB); options.c_iflag |= INPCK; break; case 2: case 'E'://設置為偶校驗 options.c_cflag |= PARENB; options.c_cflag &= ~PARODD; options.c_iflag |= INPCK; break; case 's': case 'S': //設置為空格 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); } //修改輸出模式,原始數據輸出 options.c_oflag &= ~OPOST; options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); //options.c_lflag &= ~(ISIG | ICANON); //設置等待時間和最小接收字符 options.c_cc[VTIME] = 1; /* 讀取一個字符等待1*(1/10)s */ options.c_cc[VMIN] = 32; /* 讀取字符的最少個數為1 */ //如果發生數據溢出,接收數據,但是不再讀取 刷新收到的數據但是不讀 tcflush(fd,TCIFLUSH); //激活配置 (將修改后的termios數據設置到串口中) if (tcsetattr(fd,TCSANOW,&options) != 0) { perror("com set error!\n"); return (FALSE); } return (TRUE); } /******************************************************************* * 名稱: serial_init() * 功能: 串口初始化 * 入口參數: fd : 文件描述符 * speed : 串口速度 * flow_ctrl 數據流控制 * databits 數據位 取值為 7 或者8 * stopbits 停止位 取值為 1 或者2 * parity 效驗類型 取值為N,E,O,,S * * 出口參數: 正確返回為0,錯誤返回為-1 *******************************************************************/ int serial_init(int fd, int speed, int flow_ctrl, int databits, int stopbits, int parity) { //設置串口數據幀格式 //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 * 功能: 接收串口數據 * 入口參數: fd : 文件描述符 * rcv_buf :接收串口中數據存入rcv_buf緩沖區中 * data_len :數據的長度 * * 出口參數: 實際讀到的長度 *******************************************************************/ 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實現串口的多路通信 /* 入口參數:①:ndfs:select() 中監視的文件句柄,一般設為要監視的文件中的最大文件號加一。②:rdfds:select()監視的可讀文件句柄集合,當rdfds映象的文件句柄狀態變成可讀時系統告訴select函數返回。這個集合中有一個文件可讀,select就會返回一個大于0的值,表示有文件可讀,如果沒有可讀的文件,則根據timeout參數再判斷是否超時,若超出timeout的時間,select返回0,若發生錯誤返回負值,可以傳入NULL值,表示不關心任何文件的讀變化;③:wtfds: select()監視的可寫文件句柄集合,當wtfds映象的文件句柄狀態變成可寫時系統告訴select函數返回。如果這個集合中有一個文件可寫,select就會返回一個大于0的值,表示有文件可寫,如果沒有可寫的文件,則根據timeout參數再判斷是否超時,若超出timeout的時間,select返回0,若發生錯誤返回負值,可以傳入NULL值,表示不關心任何文件的寫變化。④:exfds:select()監視的異常文件句柄集合,當exfds映象的文件句柄上有特殊情況發生時系統會告訴select函數返回。⑤:timeout:select()的超時結束時間 *//* FD_ZERO(fd_set *fdset):清空fdset與所有文件句柄的聯系。FD_SET(int fd, fd_set *fdset):建立文件句柄fd與fdset的聯系。FD_CLR(int fd, fd_set *fdset):清除文件句柄fd與fdset的聯系。FD_ISSET(int fd, fdset *fdset):檢查fdset聯系的文件句柄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 * 功能: 發送數據 * 入口參數: fd :文件描述符 * send_buf :存放串口發送數據 * data_len :一幀數據的個數 * 出口參數: 正確返回為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> /*標準輸入輸出定義*/ #include<stdlib.h> /*標準函數庫定義*/ #include<unistd.h> /*Unix 標準函數定義*/ #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; //返回調用函數的狀態 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) //循環讀取數據 { 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); }簡單好用,對于波特率的修改和進階在補充篇講吧
總結
以上是生活随笔為你收集整理的zynq操作系统: Linux驱动开发串口篇的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 字符串匹配算法知多少?
- 下一篇: Linux串口驱动加载过程