<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          嵌入式Linux操作UART實例

          共 2400字,需瀏覽 5分鐘

           ·

          2020-12-17 21:14



          1

          引言


          串口是我們實際工作中經(jīng)常使用的一個接口,比如我們在Linux下使用的debug串口,它用來登錄Linux系統(tǒng),輸出log。另外我們也會使用串口和外部的一些模塊通信,比如GPS模塊、RS485等。這里對Linux下串口使用做個總結,希望對大家有所幫助。

          2

          環(huán)境介紹

          2.1.硬件
          1) 網(wǎng)上的一個第三方做的NUC972開發(fā)板:
          有興趣購買的朋友,可以去他們的淘寶店購買:
          https://s.click.taobao.com/X8mza8w
          這次要控制的是板子底板上DB9串口:
          對應NUC972的PE3和PE2引腳。
          2) 2根USB轉RS232線,一個用來連接板子的debug串口UART0,另外一個用來連接板子上的串口UART1.
          2.2.軟件
          1) 我們在上一篇《Linux學習系列六:操作GPIO》的基礎上改動下Linux內(nèi)核配置,生成新的970uimage并燒寫到板子里。
          2) uboot、rootfs使用板子里默認的,為了增加micorcom命令,需要使用busybox生成,然后通過U盤導入到板子里。Busybox具體使用參考《Linux學習系列五:Nand Flash根文件系統(tǒng)制作》
          3)交叉工具鏈arm_linux_4.8.tar.gz

          3

          Busybox生成microcom命令


          microcom命令類似于windows下的串口調(diào)試助手,在調(diào)試串口時非常有用,默認情況下板子里不支持這個命令,需要用busybox去生成。
          1)busybox的使用如果大家有遺忘,可以參考《Linux 學習系列五:Nand Flash 根文件系統(tǒng)制作》中詳細介紹,首先我們把原來的~/nuc972/rootfs目錄里的內(nèi)容給刪掉
          2)進入到busybox目錄,make menuconfig,輸入/, 搜索microcom,找到配置它的位置
          然后進入到對應的位置,把microcom選中。
          3) 編譯make,安裝make install,然后壓縮一下生成rootfs.tar
          4) 通過U盤導入到板子里,放到根目錄下解壓,這樣板子就支持microcom命令了。

          4

          內(nèi)核配置


          1)為了使用UART1,需要在內(nèi)核里做如下配置:
          Device Drivers --->
          Character devices --->
          ?Serial drivers
          [*] NUC970/N9H30 UART1 support
          保存生成新的.config 文件。
          2)make uImage,生成新的970uimage文件,將其單獨下載到板子里即可。

          5

          UART操作


          5.1.命令行操作
          我們將板子上的兩個串口同時和PC機連接,通過debug串口登錄Linux系統(tǒng)操作UART1,PC端打開串口調(diào)試助手,選擇UART1對應的串口,這樣板子通過UART1就可以和PC之間進行數(shù)據(jù)的收發(fā)了。
          登錄板子后,輸入下面指令:
          microcom -s 115200 /dev/ttyS1
          ? /dev下的ttyS1對應的就是UART1設備。
          ? microcom 命令后的-s 115200,表示設置波特率為115200bps。
          ? 如果你想了解microcom的詳細實現(xiàn)機制,可以到busybox的目錄miscutils查看microcom.c源代碼即可。
          ? 輸入上述命令后,當此串口收到數(shù)據(jù)后,就會自動在窗口中顯示出來,如果鍵盤輸入字符,就會自動通過此串口發(fā)送出去。我們可以雙向收發(fā)測試。
          注意:
          1) micrcom指令退出的方式是Ctrl+x,不是Ctrl+c,如果輸入Ctrl+c,它其實是發(fā)送了0x03字符。
          2) 有些工程師喜歡用cat 指令去查看串口就沒有收到數(shù),其實這是不對的,我們做下面這個測試,為了方便起見,我們讓PC端1s一次定時發(fā)送
          ? 使用micrcom的話,
          microcom -s 115200 /dev/ttyS1
          會看到在不斷的接收數(shù)據(jù)
          我們Ctrl+x先關掉microcom,直接輸入
          cat /dev/ttyS1
          會有什么結果呢?
          什么都沒有收到。
          所以千萬不要直接用cat去判斷串口是否有數(shù)據(jù)接收,為什么有時能收到呢,那是因為串口設備在某個地方被打開(調(diào)用了open函數(shù))了。
          比如你讓microcom指令在后臺執(zhí)行
          microcom -s 115200 /dev/ttyS1 &
          這時再使用cat指令就可以顯示數(shù)據(jù)了。
          5.2.C語言串口編程
          我們看下在C代碼里如何操作串口,下面是一個例子:
          #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!?\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,需要在串口調(diào)試助手中正確配置,運行一下我們先看看效果:
          交叉驗證下,我們把UART1的波特率設置為115200后,結果如下,可以看到是無法正確接收到數(shù)據(jù)的。
          上述程序工作過程是串口先發(fā)送一串數(shù)據(jù),然后一直停在read函數(shù)處不動,直到接收到數(shù)據(jù)后返回退出。此時串口工作在阻塞模式下。所謂阻塞和非阻塞的含義如下:
          阻塞:
          對于read,指當串口輸入緩存區(qū)沒有數(shù)據(jù)的時候,read函數(shù)將會阻塞在這里,直到串口輸入緩存區(qū)中有數(shù)據(jù)可讀取,read讀到了需要的字節(jié)數(shù)之后,返回值為讀到的字節(jié)數(shù);
          對于write,指當串口輸出緩沖區(qū)滿,或剩下的空間小于將要寫入的字節(jié)數(shù),則write將阻塞,一直到串口輸出緩沖區(qū)中剩下的空間大于等于將要寫入的字節(jié)數(shù),執(zhí)行寫入操作,返回寫入的字節(jié)數(shù)。
          非阻塞:
          對于read,指當串口輸入緩沖區(qū)沒有數(shù)據(jù)的時候,read函數(shù)立即返回,返回值為-1。
          對于write,指當串口輸出緩沖區(qū)滿,或剩下的空間小于將要寫入的字節(jié)數(shù),則write將進行寫操作,寫入當前串口輸出緩沖區(qū)剩下空間允許的字節(jié)數(shù),然后返回寫入的字節(jié)數(shù)。
          在打開串口文件時,打開模式加上O_NDELAY可以以非阻塞方式打開串口;反之,不加上O_NDEAY,默認以阻塞方式打開串口。上述第一例子中沒有加O_NDEAY標志,所以工作在阻塞模式下,下面再看個例子,我們加上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! \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); }}
          這時程序運行結果如下,在串口接收不到數(shù)據(jù)時,read函數(shù)立即返回,返回值是-1,當接收到數(shù)據(jù)后,返回值是接收到數(shù)據(jù)值長度。
          大家可能注意到,上述代碼沒有關于串口的參數(shù)配置,比如波特率、校驗位、數(shù)據(jù)位、停止位的設置,實際應用中很可能是要修改這些參數(shù)的,最常見的就是修改波特率,下面例子在上面的基礎上修改如下:
          #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! \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時才可以正確收發(fā)。

          6

          結束語


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


          推薦閱讀:
          專輯|Linux文章匯總
          專輯|程序人生
          專輯|C語言
          我的知識小密圈

          瀏覽 74
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  精品国产精品一区二区金廿莲 | 国产精品美女久久久 | 国产精品乱码一区二区 | 欧美黄色免费第一看 | 激情自拍 9 |