命令及串口命令_嵌入式Linux系列第7篇:使用串口
1.引言
串口是我們實(shí)際工作中經(jīng)常使用的一個(gè)接口,比如我們?cè)贚inux下使用的debug串口,它用來登錄Linux系統(tǒng),輸出log。另外我們也會(huì)使用串口和外部的一些模塊通信,比如GPS模塊、RS485等。這里對(duì)Linux下串口使用做個(gè)總結(jié),希望對(duì)大家有所幫助。
2.環(huán)境介紹
2.1.硬件
1) NUC972開發(fā)板:
這次要控制的是板子底板上DB9串口:
對(duì)應(yīng)NUC972的PE3和PE2引腳。
2) 2根USB轉(zhuǎn)RS232線,一個(gè)用來連接板子的debug串口UART0,另外一個(gè)用來連接板子上的串口UART1.
2.2.軟件
1) 我們?cè)谏弦黄禠inux學(xué)習(xí)系列六:操作GPIO》的基礎(chǔ)上改動(dòng)下Linux內(nèi)核配置,生成新的970uimage并燒寫到板子里。
2) uboot、rootfs使用板子里默認(rèn)的,為了增加micorcom命令,需要使用busybox生成,然后通過U盤導(dǎo)入到板子里。Busybox具體使用參考《Linux學(xué)習(xí)系列五:Nand Flash根文件系統(tǒng)制作》
3)交叉工具鏈arm_linux_4.8.tar.gz
3.Busybox生成microcom命令
microcom命令類似于windows下的串口調(diào)試助手,在調(diào)試串口時(shí)非常有用,默認(rèn)情況下板子里不支持這個(gè)命令,需要用busybox去生成。
1)busybox的使用如果大家有遺忘,可以參考《Linux 學(xué)習(xí)系列五:Nand Flash 根文件系統(tǒng)制作》中詳細(xì)介紹,首先我們把原來的~/nuc972/rootfs目錄里的內(nèi)容給刪掉
2)進(jìn)入到busybox目錄,make menuconfig,輸入/, 搜索microcom,找到配置它的位置
然后進(jìn)入到對(duì)應(yīng)的位置,把microcom選中。
3)編譯make,安裝make install,然后壓縮一下生成rootfs.tar
4) 通過U盤導(dǎo)入到板子里,放到根目錄下解壓,這樣板子就支持microcom命令了。
4.內(nèi)核配置
1)為了使用UART1,需要在內(nèi)核里做如下配置:
Device Drivers --->
Character devices --->
Serial drivers
[*] NUC970/N9H30 UART1 support
保存生成新的.config 文件。
2)make uImage,生成新的970uimage文件,將其單獨(dú)下載到板子里即可。
5.UART操作
5.1.命令行操作
我們將板子上的兩個(gè)串口同時(shí)和PC機(jī)連接,通過debug串口登錄Linux系統(tǒng)操作UART1,PC端打開串口調(diào)試助手,選擇UART1對(duì)應(yīng)的串口,這樣板子通過UART1就可以和PC之間進(jìn)行數(shù)據(jù)的收發(fā)了。
登錄板子后,輸入下面指令:
microcom -s 115200 /dev/ttyS1
/dev下的ttyS1對(duì)應(yīng)的就是UART1設(shè)備。
microcom 命令后的-s 115200,表示設(shè)置波特率為115200bps。
如果你想了解microcom的詳細(xì)實(shí)現(xiàn)機(jī)制,可以到busybox的目錄miscutils查看microcom.c源代碼即可。
輸入上述命令后,當(dāng)此串口收到數(shù)據(jù)后,就會(huì)自動(dòng)在窗口中顯示出來,如果鍵盤輸入字符,就會(huì)自動(dòng)通過此串口發(fā)送出去。我們可以雙向收發(fā)測(cè)試。
注意:
1) micrcom指令退出的方式是Ctrl+x,不是Ctrl+c,如果輸入Ctrl+c,它其實(shí)是發(fā)送了0x03字符。
2) 有些工程師喜歡用cat 指令去查看串口就沒有收到數(shù),其實(shí)這是不對(duì)的,我們做下面這個(gè)測(cè)試,為了方便起見,我們讓PC端1s一次定時(shí)發(fā)送
使用micrcom的話,
microcom -s 115200 /dev/ttyS1
會(huì)看到在不斷的接收數(shù)據(jù)
我們Ctrl+x先關(guān)掉microcom,直接輸入
cat /dev/ttyS1
會(huì)有什么結(jié)果呢?
什么都沒有收到。
所以千萬(wàn)不要直接用cat去判斷串口是否有數(shù)據(jù)接收,為什么有時(shí)能收到呢,那是因?yàn)榇谠O(shè)備在某個(gè)地方被打開(調(diào)用了open函數(shù))了。
比如你讓microcom指令在后臺(tái)執(zhí)行
microcom -s 115200 /dev/ttyS1 &
這時(shí)再使用cat指令就可以顯示數(shù)據(jù)了。
5.2.C語(yǔ)言串口編程
我們看下在C代碼里如何操作串口,下面是一個(gè)例子:
//--------------------------------------------------// Copyright (c) Topsemic//--------------------------------------------------#include #include #include #include #include #include #define DEV_NAME "/dev/ttyS1" int main (int argc, char *argv[]){ int fd;int len, i,ret;char buf[] = "Hello TopSemic! "; 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 ");}memset(buf,0x00,sizeof(buf));len = read(fd, buf, sizeof(buf)); if (len < 0) {printf("read error ");return -1; }printf("%s", buf);return(0);}將它編譯后放到板子里,注意上述代碼沒有設(shè)置串口波特率,默認(rèn)值是9600,需要在串口調(diào)試助手中正確配置,運(yùn)行一下我們先看看效果:
交叉驗(yàn)證下,我們把UART1的波特率設(shè)置為115200后,結(jié)果如下,可以看到是無法正確接收到數(shù)據(jù)的。
上述程序工作過程是串口先發(fā)送一串?dāng)?shù)據(jù),然后一直停在read函數(shù)處不動(dòng),直到接收到數(shù)據(jù)后返回退出。此時(shí)串口工作在阻塞模式下。所謂阻塞和非阻塞的含義如下:
阻塞:
對(duì)于read,指當(dāng)串口輸入緩存區(qū)沒有數(shù)據(jù)的時(shí)候,read函數(shù)將會(huì)阻塞在這里,直到串口輸入緩存區(qū)中有數(shù)據(jù)可讀取,read讀到了需要的字節(jié)數(shù)之后,返回值為讀到的字節(jié)數(shù);
對(duì)于write,指當(dāng)串口輸出緩沖區(qū)滿,或剩下的空間小于將要寫入的字節(jié)數(shù),則write將阻塞,一直到串口輸出緩沖區(qū)中剩下的空間大于等于將要寫入的字節(jié)數(shù),執(zhí)行寫入操作,返回寫入的字節(jié)數(shù)。
非阻塞:
對(duì)于read,指當(dāng)串口輸入緩沖區(qū)沒有數(shù)據(jù)的時(shí)候,read函數(shù)立即返回,返回值為-1。
對(duì)于write,指當(dāng)串口輸出緩沖區(qū)滿,或剩下的空間小于將要寫入的字節(jié)數(shù),則write將進(jìn)行寫操作,寫入當(dāng)前串口輸出緩沖區(qū)剩下空間允許的字節(jié)數(shù),然后返回寫入的字節(jié)數(shù)。
在打開串口文件時(shí),打開模式加上O_NDELAY可以以非阻塞方式打開串口;反之,不加上O_NDEAY,默認(rèn)以阻塞方式打開串口。上述第一例子中沒有加O_NDEAY標(biāo)志,所以工作在阻塞模式下,下面再看個(gè)例子,我們加上O_NDEAY
#include #include #include #include #include #include #define DEV_NAME "/dev/ttyS1" int main (int argc, char *argv[]){ int fd; int len, i,ret; char buf[] = "Hello TopSemic! "; 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 "); } while(1) { memset(buf,0x00,sizeof(buf)); len = read(fd, buf, sizeof(buf)); printf("len:%d ",len); if(len>0) printf("%s", buf); usleep(100000); }}這時(shí)程序運(yùn)行結(jié)果如下,在串口接收不到數(shù)據(jù)時(shí),read函數(shù)立即返回,返回值是-1,當(dāng)接收到數(shù)據(jù)后,返回值是接收到數(shù)據(jù)值長(zhǎng)度。
大家可能注意到,上述代碼沒有關(guān)于串口的參數(shù)配置,比如波特率、校驗(yàn)位、數(shù)據(jù)位、停止位的設(shè)置,實(shí)際應(yīng)用中很可能是要修改這些參數(shù)的,最常見的就是修改波特率,下面例子在上面的基礎(chǔ)上修改如下:
#include #include #include #include #include #include #include #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! "; 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 ");}while(1){ memset(buf,0x00,sizeof(buf));len = read(fd, buf, sizeof(buf));printf("len:%d ",len);if(len>0)printf("%s", buf);usleep(100000);} return 0;}這時(shí)我們把波特率修改為115200了,大家可以驗(yàn)證下,只有把uart1對(duì)應(yīng)串口波特率設(shè)置為115200時(shí)才可以正確收發(fā)。
6.結(jié)束語(yǔ)
本篇為大家介紹了Linux下UART的使用,如果實(shí)現(xiàn)收發(fā)數(shù)據(jù),如何配置波特率等參數(shù),以及如何使用microcom 命令調(diào)試等。
總結(jié)
以上是生活随笔為你收集整理的命令及串口命令_嵌入式Linux系列第7篇:使用串口的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: input在type=number时去右
- 下一篇: Linux 线程并发拷贝,【Linux】