嵌入式Linux系列第7篇:操做UART

1linux

引言ios

串口是咱們實際工做中常用的一個接口,好比咱們在Linux下使用的debug串口,它用來登陸Linux系統,輸出log。另外咱們也會使用串口和外部的一些模塊通訊,好比GPS模塊、RS485等。這裏對Linux下串口使用作個總結,但願對你們有所幫助。

2git

環境介紹github

2.1.硬件
1) 網上的一個第三方作的NUC972開發板:
有興趣購買的朋友,能夠去他們的淘寶店購買:
https://s.click.taobao.com/X8mza8w
此次要控制的是板子底板上DB9串口:
對應NUC972的PE3和PE2引腳。
2) 2根USB轉RS232線,一個用來鏈接板子的debug串口UART0,另一個用來鏈接板子上的串口UART1.
2.2.軟件
1) 咱們在上一篇《Linux學習系列六:操做GPIO》的基礎上改動下Linux內核配置,生成新的970uimage並燒寫到板子裏。
2) uboot、rootfs使用板子裏默認的,爲了增長micorcom命令,須要使用busybox生成,而後經過U盤導入到板子裏。Busybox具體使用參考《Linux學習系列五:Nand Flash根文件系統製做》
3)交叉工具鏈arm_linux_4.8.tar.gz

3web

Busybox生成microcom命令編程

microcom命令相似於windows下的串口調試助手,在調試串口時很是有用,默認狀況下板子裏不支持這個命令,須要用busybox去生成。
1)busybox的使用若是你們有遺忘,能夠參考《Linux 學習系列五:Nand Flash 根文件系統製做》中詳細介紹,首先咱們把原來的~/nuc972/rootfs目錄裏的內容給刪掉
2)進入到busybox目錄,make menuconfig,輸入/, 搜索microcom,找到配置它的位置
而後進入到對應的位置,把microcom選中。
3) 編譯make,安裝make install,而後壓縮一下生成rootfs.tar
4) 經過U盤導入到板子裏,放到根目錄下解壓,這樣板子就支持microcom命令了。

4windows

內核配置緩存

1)爲了使用UART1,須要在內核裏作以下配置:
Device Drivers --->
Character devices --->
 Serial drivers
[*] NUC970/N9H30 UART1 support
保存生成新的.config 文件。
2)make uImage,生成新的970uimage文件,將其單獨下載到板子裏便可。

5微信

UART操做app

5.1.命令行操做
咱們將板子上的兩個串口同時和PC機鏈接,經過debug串口登陸Linux系統操做UART1,PC端打開串口調試助手,選擇UART1對應的串口,這樣板子經過UART1就能夠和PC之間進行數據的收發了。
登陸板子後,輸入下面指令:
microcom -s 115200 /dev/ttyS1
  /dev下的ttyS1對應的就是UART1設備。
  microcom 命令後的-s 115200,表示設置波特率爲115200bps。
  若是你想了解microcom的詳細實現機制,能夠到busybox的目錄miscutils查看microcom.c源代碼便可。
  輸入上述命令後,當此串口收到數據後,就會自動在窗口中顯示出來,若是鍵盤輸入字符,就會自動經過此串口發送出去。咱們能夠雙向收發測試。
注意:
1) micrcom指令退出的方式是Ctrl+x,不是Ctrl+c,若是輸入Ctrl+c,它實際上是發送了0x03字符。
2) 有些工程師喜歡用cat 指令去查看串口就沒有收到數,其實這是不對的,咱們作下面這個測試,爲了方便起見,咱們讓PC端1s一次定時發送
  使用micrcom的話,
microcom -s 115200 /dev/ttyS1
會看到在不斷的接收數據
咱們Ctrl+x先關掉microcom,直接輸入
cat /dev/ttyS1
會有什麼結果呢?
什麼都沒有收到。
因此千萬不要直接用cat去判斷串口是否有數據接收,爲何有時能收到呢,那是由於串口設備在某個地方被打開(調用了open函數)了。
好比你讓microcom指令在後臺執行
microcom -s 115200 /dev/ttyS1 &
這時再使用cat指令就能夠顯示數據了。
5.2.C語言串口編程
咱們看下在C代碼裏如何操做串口,下面是一個例子:
#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <fcntl.h>#include <asm/termios.h>#include <memory.h>
#define DEV_NAME "/dev/ttyS1"int main (int argc, char *argv[]){ int fd; int len, i,ret;  char buf[] = "Hello TopSemic! \n"; fd = open(DEV_NAME, O_RDWR | O_NOCTTY);  if(fd < 0) { perror(DEV_NAME); return -1;   } len = write(fd, buf, sizeof(buf)); if (len < 0) { printf("write data error \n"); } memset(buf,0x00,sizeof(buf)); len = read(fd, buf, sizeof(buf));  if (len < 0 { printf("read error \n"); return -1;  }  printf("%s", buf);  return(0);}
將它編譯後放到板子裏,注意上述代碼沒有設置串口波特率,默認值是9600,須要在串口調試助手中正確配置,運行一下咱們先看看效果:
交叉驗證下,咱們把UART1的波特率設置爲115200後,結果以下,能夠看到是沒法正確接收到數據的。
上述程序工做過程是串口先發送一串數據,而後一直停在read函數處不動,直到接收到數據後返回退出。此時串口工做在阻塞模式下。所謂阻塞和非阻塞的含義以下:
阻塞:
對於read,指當串口輸入緩存區沒有數據的時候,read函數將會阻塞在這裏,直到串口輸入緩存區中有數據可讀取,read讀到了須要的字節數以後,返回值爲讀到的字節數;
對於write,指當串口輸出緩衝區滿,或剩下的空間小於將要寫入的字節數,則write將阻塞,一直到串口輸出緩衝區中剩下的空間大於等於將要寫入的字節數,執行寫入操做,返回寫入的字節數。
非阻塞:
對於read,指當串口輸入緩衝區沒有數據的時候,read函數當即返回,返回值爲-1。
對於write,指當串口輸出緩衝區滿,或剩下的空間小於將要寫入的字節數,則write將進行寫操做,寫入當前串口輸出緩衝區剩下空間容許的字節數,而後返回寫入的字節數。
在打開串口文件時,打開模式加上O_NDELAY能夠以非阻塞方式打開串口;反之,不加上O_NDEAY,默認以阻塞方式打開串口。上述第一例子中沒有加O_NDEAY標誌,因此工做在阻塞模式下,下面再看個例子,咱們加上O_NDEAY
#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <fcntl.h>#include <asm/termios.h>#include <memory.h>
#define DEV_NAME "/dev/ttyS1" int main (int argc, char *argv[]){ int fd; int len, i,ret; char buf[] = "Hello TopSemic! \n";
fd = open(DEV_NAME, O_RDWR | O_NOCTTY|O_NDELAY); if(fd < 0) { perror(DEV_NAME); return -1; }
len = write(fd, buf, sizeof(buf)); if (len < 0) { printf("write data error \n"); } while(1) { memset(buf,0x00,sizeof(buf)); len = read(fd, buf, sizeof(buf));
printf("len:%d \n",len); if(len>0) printf("%s", buf); usleep(100000); }}
這時程序運行結果以下,在串口接收不到數據時,read函數當即返回,返回值是-1,當接收到數據後,返回值是接收到數據值長度。
你們可能注意到,上述代碼沒有關於串口的參數配置,好比波特率、校驗位、數據位、中止位的設置,實際應用中極可能是要修改這些參數的,最多見的就是修改波特率,下面例子在上面的基礎上修改以下:
#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <fcntl.h>#include <asm/termios.h>#include <memory.h>#include <signal.h>
#define DEV_NAME "/dev/ttyS1"static struct termios newtios,oldtios; /*termianal settings */
static int saved_portfd=-1; /*serial port fd */
static void reset_tty_atexit(void){ if(saved_portfd != -1) { tcsetattr(saved_portfd,TCSANOW,&oldtios); } }
/*cheanup signal handler */static void reset_tty_handler(int signal){ if(saved_portfd != -1) { tcsetattr(saved_portfd,TCSANOW,&oldtios); } _exit(EXIT_FAILURE);}
static set_port_attr (int portfd,int baudrate){ struct sigaction sa; /*get serial port parnms,save away */ tcgetattr(portfd,&newtios); memcpy(&oldtios,&newtios,sizeof newtios); /* configure new values */ cfmakeraw(&newtios); /*see man page */ newtios.c_iflag |=IGNPAR; /*ignore parity on input */ newtios.c_oflag &= ~(OPOST | ONLCR | OLCUC | OCRNL | ONOCR | ONLRET | OFILL); newtios.c_cc[VMIN]=1; /* block until 1 char received */ newtios.c_cc[VTIME]=0; /*no inter-character timer */ switch(baudrate) { case 9600: cfsetispeed(&newtios,B9600); cfsetospeed(&newtios,B9600); break; case 19200: cfsetispeed(&newtios,B19200); cfsetospeed(&newtios,B19200); break; case 38400: cfsetispeed(&newtios,B38400); cfsetospeed(&newtios,B38400); break; case 115200: cfsetispeed(&newtios,B115200); cfsetospeed(&newtios,B115200); break; } /* register cleanup stuff */ atexit(reset_tty_atexit); memset(&sa,0,sizeof sa); sa.sa_handler = reset_tty_handler; sigaction(SIGHUP,&sa,NULL); sigaction(SIGINT,&sa,NULL); sigaction(SIGPIPE,&sa,NULL); sigaction(SIGTERM,&sa,NULL); /*apply modified termios */ saved_portfd=portfd; tcflush(portfd,TCIFLUSH); tcsetattr(portfd,TCSADRAIN,&newtios); return portfd;
}
int main (int argc, char *argv[]){ int fd; int len, i,ret; char buf[] = "Hello TopSemic! \n"; fd = open(DEV_NAME, O_RDWR | O_NOCTTY|O_NDELAY); if(fd < 0) { perror(DEV_NAME); return -1; } set_port_attr (fd,115200);
len = write(fd, buf, sizeof(buf)); if (len < 0) { printf("write data error \n"); } while(1) { memset(buf,0x00,sizeof(buf)); len = read(fd, buf, sizeof(buf));
printf("len:%d \n",len); if(len>0) printf("%s", buf); usleep(100000); }
return 0;}
這時咱們把波特率修改成115200了,你們能夠驗證下,只有把uart1對應串口波特率設置爲115200時才能夠正確收發。

6

結束語

本期相關的資料在連接:  https://github.com/TopSemic/NUC972_Linux    07 Lesson7 操做UART 中。

嵌入式Linux系列第1篇:開發環境搭建

嵌入式Linux系列第2篇:運行Hello World

嵌入式Linux系列第3篇:uboot編譯下載

嵌入式Linux系列第4篇:Kernel編譯下載

嵌入式Linux系列第5篇:Nand Flash根文件系統製做

嵌入式Linux系列第6篇:操做GPIO

長按下方二維碼,歡迎關注


本文分享自微信公衆號 - TopSemic嵌入式(TopSemic)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索