• <noscript id="e0iig"><kbd id="e0iig"></kbd></noscript>
  • <td id="e0iig"></td>
  • <option id="e0iig"></option>
  • <noscript id="e0iig"><source id="e0iig"></source></noscript>
  • Linux之串口編程

    標簽: Linux學習之旅  linux  嵌入式

    Linux之串口編程

    一、串口編程的流程分析

    在這里插入圖片描述
    打開串口,一般使用 open 函數,打開之后會返回句柄,這個句柄就可以提供給發送和接收函數使用。串口本質上也是字符設備,但是串口是屬于一種比較特殊的字符設備。
    初始化串口,串口需要配置波特率,數據位,校驗位等等一系列的參數,初始化的過程掌握了,發送和接收都比較容易實現。雖然初始化比較麻煩,但是無論是在 window 下還是在 linux下,串口的初始化都是很容易找到例程的。個人建議只要能夠讀懂代碼,根據實際需求進行驗證和配置即可。
    發送和接收數據,前面提到過串口是屬于字符設備的,可以使用 read 函數和write 函數實現
    關閉,一般使用close函數即可關閉串口。

    二、串口編程的實例

    1、打開串口

    先來學習一下如何打開串口,在幾乎所有的 Linux 系統中,在 dev 目錄下都會有 tty的設備節點,如下圖所示,啟動開發板,在超級終端中,進入 dev 目錄,輸入查找命令“ls tty”。
    在這里插入圖片描述
    如上圖所示,有多種形式的設備節點,在 4412 開發板中,設備節點使用的是 ttySAC*系列,即 ttySAC0,ttySAC1,ttySAC2,ttySAC3。
    以“ttySAC3”為例

    fd = open(uart3,O_RDWR|O_CREAT,0777);
    

    2、串口初始化參數介紹

    串口編程的最大的難度就是初始化,用的參數非常多。
    大家可能查看過網上一些關于串口的資料以及歷史,由于串口的設計之初太過于復雜了,但是到了實際應用中,兩線的串口(tx/rx)應用卻是最廣泛的。在實際應用中幾乎很少看到有多線的,即使復雜一點也最多是添加一根流控。
    本節內容將直接介紹應用中最多的一套初始化代碼。這套代碼盡量貼近實際,以后應用的時候,要是傳輸的協議一樣,可以直接拿來使用,不一樣稍微修改一下參數的配置就能夠通信。
    串口常用參數包括串口號、波特率、數據位、停止位,校驗位、流控

    2.1、Linux內核源碼有關串口參數的結構體

    如下圖所示,使用 source insight 打開內核源碼,串口的初始化最終要將參數傳遞到內核中的,搜索“termios.h”,如下所示。
    在這里插入圖片描述
    如上圖所示,打開“arch\arm\include\asm”目錄下的“termios.h”頭文件。
    如下圖所示,可以看到這個 termio 結構體的定義。
    在這里插入圖片描述
    分析一下上圖中幾個常用的參數。
    成員 tcflag_t c_iflag:輸入模式標志
    成員 tcflag_t c_oflag:輸出模式標志
    成員 tcflag_t c_cflag:控制模式標志
    成員 tcflag_t c_lflag:本地模式標志
    成員 cc_t c_line:line discipline
    成員 cc_t c_cc[NCC]:control characters

    2.2、 串口的初始化常用函數介紹

    在介紹了上面的結構體之后,接著看一下初始化的幾個函數以及用法。
    在給串口初始化之前必須讀取串口的句柄,也就是先要使用 open 函數。

    2.2.1、函數 tcgetattr

    函數 tcgetattr 用于讀取當前串口的參數值,在實際應用中,一般用于先確認該串口是否能夠配置,做檢測用。
    需要用到頭文件 “#include <termios.h>”和“#include <unistd.h>”。
    函數原型為

    int tcgetattr(int fd, struct termios *termios_p)//參數 1:fd 是 open 返回的文件句柄。
    //參數 2:*termios_p 是前面介紹的結構體。
    

    使用這個函數前可以先定義一個 termios 結構體,用于存儲舊的參數。

    2.2.2、 波特率相關的函數

    函數 cfsetispeed 和 cfsetospeed 用于修改串口的波特率,函數 cfgetispeed 和cfgetospeed 可以用于獲取當前波特率。在實際應用中,這個經常需要用到,例如修改默認的波特率。
    波特率相關的函數需要用到頭文件“#include <termios.h>”和“#include <unistd.h>”。
    先介紹設置波特率的函數
    設置輸入波特率函數原型

    int cfsetispeed(struct termios *termios_p, speed_t speed);
    //參數 1:*termios_p 是前面介紹的結構體。
    //參數 2:speed 波特率,常用的 B2400,B4800,B9600,B115200,B460800 等等。
    //執行成功返回 0,失敗返回-1
    

    設置輸出波特率函數原型

    int cfsetospeed(struct termios *termios_p, speed_t speed);
    //參數 1:*termios_p 是前面介紹的結構體。
    //參數 2:speed 波特率,常用的 B2400,B4800,B9600,B115200,B460800 等等。
    //執行成功返回 0,失敗返回-1
    

    下面介紹獲取波特率的函數
    獲取輸入波特率函數原型為

    speed_t cfgetispeed(const struct termios *termios_p)//用于讀取當前串口輸入的波特率。
    //參數 1:*termios_p 是前面介紹的結構體。
    //返回值為 speed_t。
    

    獲取輸出波特率函數原型為

    speed_t cfgetospeed(const struct termios *termios_p)//這個函數用于讀取當前輸出的波特率。
    //參數 1:*termios_p 是前面介紹的結構體。
    //返回值為 speed_t 類型,當前波特率。
    
    2.2.3、 函數 tcflush

    函數 tcflush 用于清空串口中沒有完成的輸入或者輸出數據。在接收或者發送數據的時候,串口寄存器會緩存數據,這個函數用于清除這些數據。
    原型為

    int tcflush(int fd, int queue_selector);
    //參數 1:fd 是 open 返回的文件句柄。
    //參數 2:控制 tcflush 的操作。
    //有三個常用數值,TCIFLUSH 清除正收到的數據,且不會讀取出來;
    //TCOFLUSH 清除正寫入的數據,且不會發送至終端;
    //TCIOFLUSH 清除所有正在發生的 I/O 數據。
    //執行成功返回 0,失敗返回-1
    
    2.2.4、函數 tcsetattr

    前面介紹了讀取串口配置參數的函數,tcsetattr 函數是設置參數的函數。一般在初始化最后會使用這個函數。
    原型為

    int tcsetattr(int fd, int optional_actions,const struct termios *termios_p);
    //參數 1:fd 是 open 返回的文件句柄。
    //參數 2:optional_actions 是參數生效的時間。
    //有三個常用的值:
    //TCSANOW:不等數據傳輸完畢就立即改變屬性;
    //TCSADRAIN:等待所有數據傳輸結束才改變屬性;
    //TCSAFLUSH:清空輸入輸出緩沖區才改變屬性。
    //參數 3:*termios_p 在舊的參數基礎上修改的后的參數。
    //執行成功返回 0,失敗返回-1
    
    2.2.5、對于其他函數

    在串口中還有其它的參數需要了解,其它的函數需要學習。
    大家在 linux 下使用串口的時候,如果有其它的參數需要配置,可以在網上搜索相關的資料,那樣針對性更強,有的放矢,效率更高。
    前面的參數和設置已經可以對付大部分實際應用場景了。

    2.3、串口參數初始化流程分析

    如下圖所示,是串口初始化的流程圖。
    在這里插入圖片描述
    代碼如下:

    int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop)
    {
        //定義結構體 newtio 和 oldtio。
    	struct termios newtio,oldtio;
    	//獲取當前串口狀態
    	if  ( tcgetattr( fd,&oldtio)  !=  0) { 
    		perror("SetupSerial 1");
    		return -1;
    	}
    	//接著將 newtio 清零和設置標志位 c_cflag
    	bzero( &newtio, sizeof( newtio ) );
    	newtio.c_cflag  |=  CLOCAL | CREAD;
    	newtio.c_cflag &= ~CSIZE;
    
    	switch( nBits )
    	{
    	case 7:
    		newtio.c_cflag |= CS7;
    		break;
    	case 8:
    		newtio.c_cflag |= CS8;
    		break;
    	}
    
    	switch( nEvent )
    	{
    	case 'O':
    		newtio.c_cflag |= PARENB;
    		newtio.c_cflag |= PARODD;
    		newtio.c_iflag |= (INPCK | ISTRIP);
    		break;
    	case 'E': 
    		newtio.c_iflag |= (INPCK | ISTRIP);
    		newtio.c_cflag |= PARENB;
    		newtio.c_cflag &= ~PARODD;
    		break;
    	case 'N':  
    		newtio.c_cflag &= ~PARENB;
    		break;
    	}
    	//接著設置波特率
    	switch( nSpeed )
    	{
    	case 2400:
    		cfsetispeed(&newtio, B2400);
    		cfsetospeed(&newtio, B2400);
    		break;
    	case 4800:
    		cfsetispeed(&newtio, B4800);
    		cfsetospeed(&newtio, B4800);
    		break;
    	case 9600:
    		cfsetispeed(&newtio, B9600);
    		cfsetospeed(&newtio, B9600);
    		break;
    	case 115200:
    		cfsetispeed(&newtio, B115200);
    		cfsetospeed(&newtio, B115200);
    		break;
    	case 460800:
    		cfsetispeed(&newtio, B460800);
    		cfsetospeed(&newtio, B460800);
    		break;
    	default:
    		cfsetispeed(&newtio, B9600);
    		cfsetospeed(&newtio, B9600);
    		break;
    	}
    	//接著設置停止位
    	if( nStop == 1 )
    		newtio.c_cflag &=  ~CSTOPB;
    	else if ( nStop == 2 )
    	newtio.c_cflag |=  CSTOPB;
    	newtio.c_cc[VTIME]  = 0;
    	newtio.c_cc[VMIN] = 0;
    	tcflush(fd,TCIFLUSH);
    	//最后配置參數
    	if((tcsetattr(fd,TCSANOW,&newtio))!=0)
    	{
    		perror("com set error");
    		return -1;
    	}
    //	printf("set done!\n\r");
    	return 0;
    }
    

    3、串口發送

    本節實驗演示串口的發送。
    這里提醒一下,在學習中用到的超級終端,里面的打印信息,本質上是用于 linux 的調試的,類似在開發環境的 Ubuntu 下的終端。那個串口已經被內核占用了,不能直接用于本章的實驗的調用,只能作為一個輔助調試的手段。
    如果最終的產品串口不夠,可以使用 usb 轉串口,或者將調試的控制臺屏蔽掉即可。內核經過處理之后才能作為串口使用
    串口發送類似文件操作,非常簡單。使用 write 函數即可,三個參數分別是句柄,傳輸的buffer 以及,傳輸的長度。這個函數前面介紹文件 IO 的時候已經介紹過了,這里就不再重復。
    源碼文件名為“uartwrite.c”。具體代碼如下:

    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <termios.h>
    #include <errno.h>
    
    int set_opt(int,int,int,char,int);
    void main()
    {
    	int fd,wr_static,i=10;
    	char *uart3 = "/dev/ttySAC3";
    	char *buffer = "hello world!\n";
    	
    	printf("\r\nitop4412 uart3 writetest start\r\n");
    	
    	if((fd = open(uart3, O_RDWR|O_NOCTTY|O_NDELAY))<0){
    		printf("open %s is failed",uart3);
    	}
    	else{
    		printf("open %s is success\n",uart3);
    		set_opt(fd, 115200, 8, 'N', 1); 
    		while(i--)
    		{
    			wr_static = write(fd,buffer, strlen(buffer));
    			if(wr_static<0)
    				printf("write failed\n");
    			else{
    				printf("wr_static is %d\n",wr_static);
    			}
    			sleep(1);
    		}
    	}
    	close(fd);
    }
    

    如上圖所示,運行代碼之后,可觀察到如下現象。
    控制臺打印“itop4412 uart3 writetest start”。
    控制臺打印“open %s is success\n”。
    接著打印 i=10 次的"hello world!\n"。
    打印的同時控制臺會打印“wr_static is %d”。
    另外,如果其它打開設備之類的操作出錯,會打印對應的錯誤。

    4、串口接收

    串口接收使用 read 函數,在文件 io 中已經介紹過了。具體代碼如下:

    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <termios.h>
    #include <errno.h>
    
    int set_opt(int,int,int,char,int);
    //"/dev/ttySAC3"是con2,靠近耳機接口的串口
    void main()
    {
    	int fd,nByte;
    	char *uart3 = "/dev/ttySAC3";
    	char buffer[512];
    	char *uart_out = "please input\r\n";
    	memset(buffer, 0, sizeof(buffer));
    	if((fd = open(uart3, O_RDWR|O_NOCTTY))<0)
    		printf("open %s is failed",uart3);
    	else{
    		set_opt(fd, 115200, 8, 'N', 1);
    		write(fd,uart_out, strlen(uart_out));
    		while(1){
    			while((nByte = read(fd, buffer, 512))>0){
    				buffer[nByte+1] = '\0';			
    				write(fd,buffer,strlen(buffer));
    				memset(buffer, 0, strlen(buffer));
    				nByte = 0;
    			}
    		}
    	}
    }
    
    版權聲明:本文為qq_41783046原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
    本文鏈接:https://blog.csdn.net/qq_41783046/article/details/105371205

    智能推薦

    串口通信協議和Linux下的串口編程

    一、串口通信介紹: 串口通信(Serial Communications)的概念非常簡單,串口按位(bit)發送和接收字節,盡管比按位字節(byte)的并行通信慢,但是串口可以使用一根線發送數據的同時用另一根線接收數據。串口通信屬于異步串行通信方式。串口是一種接口標準,它規定了接口的電氣標準,沒有規定接口插件電纜以及使用的協議。 二、串口接頭: 常見的串口接頭有兩種,一種是9針串口(簡單DB-9)...

    Linux 串口編程(基于RAW模式)

    Linux下的串口編程對于大多數的系統編程人員來說肯定不陌生,特別是對于嵌入式系統開發人員來說,其會經常使用串口與各種各樣的硬件設備進行通信。下面總結一下,Linux串口編程的基本模式和常見問題。 編程模式 Linux下的串口以設備文件的形式存在,所以,對于串口設備的所有操作都時圍繞其設備文件而展開。熟悉Linux文件相關操作的開發人員,應該很熟悉下面的模式: 打開文件(open); 配置串口文件...

    C——Linux下的串口編程(轉)

    之前在學習安信可A7模塊時,是在PC上使用串口調試助手做了GPS的坐標數據信息的采集,同時分析了一些語句的含義。在這過程中,涉及到對嵌入式開發人員一個非常重要的知識:串口通信。在前篇也說到,我們將會自己寫程序來對GPS數據進行解析,而這些數據正是靠串口來傳輸的。所以,本篇博文將進行關于串口通信的學習。 一、串口接頭 首先我們得知道串口長什么樣,常用的串口接頭有兩種,一種是9針串口(簡稱DB-9),...

    Linux下C語言串口應用編程

    在編寫Linux串口的C程序之前,需要包含以下頭文件: #include <termios.h> 在Linux系統中,一切皆文件,所以串口設備也是一類文件,學習過Linux驅動程序的學員都知道,Linux有三類設備:字符設備,塊設備,網絡設備。那么串口設備屬于字符設備。所以串口設備的命名一般為/dev/ttySn(n = 0、1、2......),如果該串口為USB轉串口,可能名稱為/...

    Linux 串口編程簡介和實例學習

    Linux 串口編程簡介和實例學習 一、無論是從linux官方直接下載的原生態內核還是任何一家芯片廠家提供的linux內核,都已經把串口驅動寫好了,所以在linux串口編程中,是完全不需要動手去寫串口驅動的。對于一般的程序員來說涉及比較多的是串口應用編程,這里我們就介紹一下如何調用內核提供的接口,如何進行初始化配置以及發送和接收數據。 二、查看串口設備節點,在linux系統,串口的設備節點以tty...

    猜你喜歡

    HTML中常用操作關于:頁面跳轉,空格

    1.頁面跳轉 2.空格的代替符...

    freemarker + ItextRender 根據模板生成PDF文件

    1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...

    電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!

    Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...

    requests實現全自動PPT模板

    http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...

    精品国产乱码久久久久久蜜桃不卡