Linux 串口學習記錄

打開串口

和其他的字符設備一樣的,需要先打開相關的設備節點,這次用的串口的設備節點目錄爲/dev/ttySAC3,所以,先打開設備節點並獲取文件句柄

    int fd;
    char *uart3 = "/dev/ttySAC3";

    if((fd = open(uart3,O_CREAT|O_RDWR,0777))<0)
    {
        printf("open %s failed! \n",uart3);
    }
    else
    {
        printf("open %s success! \n",uart3);
    }



初始化串口

串口打開之後,需要對其進行初始化,需要初始化的參數包括波特率,數據位,停止位,校驗位,流控等,串口初始化最終會將參數傳遞到內核中,在源碼的arch\arm\include\asm\termios.h頭文件裏,有一個termio結構體的定義

#define NCC 8
struct termio {
	unsigned short c_iflag;		/* input mode flags 輸入模式標誌*/
	unsigned short c_oflag;		/* output mode flags 輸出模式標誌*/
	unsigned short c_cflag;		/* control mode flags 控制模式標誌*/
	unsigned short c_lflag;		/* local mode flags 本地模式標誌*/
	unsigned char c_line;		/* line discipline */
	unsigned char c_cc[NCC];	/* control characters */
};

函數 tcgetattr 用於讀取當前串口的參數值,在實際應用中,一般用於先確認該串口是否能夠配置,做檢測用。
函數原型爲 int tcgetattr(int fd, struct termios *termios_p)

  • fd爲文件句柄
  • *termios_p則爲上面的結構體

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


函數 cfsetispeedcfsetospeed 用於修改串口的波特率,函數 cfgetispeed 和 cfgetospeed 可以用於獲取當前波特率。在實際應用中,這個經常需要用到,例如修改默認的波特率。
函數原型爲int cfsetispeed(struct termios *termios_p, speed_t speed)int cfsetospeed(struct termios *termios_p, speed_t speed)

  • *termios_p則爲上面的結構體
  • speed爲波特率,常用的有B2400,B4800,B9600,B115200等
  • 該函數執行成功返回0,失敗返回-1。

獲取當前波特率函數speed_t cfgetispeed
函數原型speed_t cfgetispeed(const struct termios *termios_p)speed_t cfgetospeed(const struct termios *termios_p)

  • *termios_p則爲上面的結構體
  • 返回值爲speed_t

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

  • fd爲文件句柄

  • queue_selector爲控制tcflush的操作,有三個常用的值

     - TCIFLUSH :清除正收到的數據,且不會讀取出來
     - TCOFLUSH:清除正在寫入的數據,且不會發送至終端
     - TCIOFLUSH:清除所有正在發送的I/O數據
    

執行成功返回 0,失敗返回-1


函數 tcsetattr
tcsetattr用於設置參數
函數原型int tcsetattr(int fd, int optional_actions,const struct termios *termios_p)

  • fd爲文件句柄

  • optional_actions 參數生效的時間

     - TCSANOW:不等數據傳輸完畢就立即改變屬性
     - TCSADRAIN:數據傳輸結束後才改變屬性
     - TCSAFLUSH:清空緩衝區後才改變屬性
    
  • termios_p 在舊的參數基礎上修改後的參數

執行成功返回0,失敗返回-1


串口初始化代碼

先定義一個初始化函數

int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop)

定義結構體newtio和oldtio

struct termios newtio, oldtio;      
    if(tcgetattr(fd, &oldtio) != 0)         //讀取當前串口的參數值並保存到oldtio結構體中
    {
        perror("Setup Serial 1");
        return -1;
    }

將newtio結構體清0並設置標誌位c_cflag

    bzero(&newtio, sizeof(newtio));         //將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_cflag |= PARENB;
            newtio.c_cflag &= ~PARODD;
            newtio.c_iflag |= (INPCK | ISTRIP);
            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\n");
        return -1;
    }
    printf("set done\n\r");
    return 0;

串口發送

串口發送與文件操作非常類似,使用write函數即可。
write中的三個參數分別爲句柄,傳輸的buffer以及傳輸的長度。

將串口參數配置整合爲一個函數,整體的結構可以顯得更清晰一些

int set_opt(int, int, int, char, int);          //文件句柄,波特率,數據位,校驗位,停止位

主函數

    if((fd = open(uart3, O_RDWR | O_NOCTTY | O_NDELAY))<0)      //打開設備節點  
    {
        printf("open %s failed!\n", uart3);
    }
    else
    {
        printf("open %s 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);

        }        
    }

串口接收

串口接收使用read函數,和發送非常相似

int fd;
    int nByte;
    char *uart3 = "/dev/ttySAC3";
    char buffer[512];                           //定義一個buffer
    char *uart_out = "please input\r\n";
    
    memset(buffer, 0, sizeof(buffer));          //清零緩衝區
    
    if((fd = open(uart3, O_RDWR | O_NOCTTY)) < 0)       //打開串口
    {
        printf("open %s failed \n", 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));          //清空buffer
                nByte = 0;
            }
        }
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章