Linux串口通信編程

轉載自:http://blog.chinaunix.net/uid-11582448-id-745506.html

串口是計算機上一種非常通用設備通信的協議,常用PC機上包含的是RS232規格的串口,具有連接線少,通訊簡單,得到廣泛的使用。

    Linux對所有設備的訪問是通過設備文件來進行的,串口也是這樣,爲了訪問串口,只需打開其設備文件即可操作串口設備。在linux系統下面,每一個串口設備都有設備文件與其關聯,設備文件位於系統的/dev目錄下面。如linux下的/ttyS0,/ttyS1分別表示的是串口1和串口2。
    在串口編程中,比較重要的是串口的設置,我們要設置的部分包括:波特率,數據位,停止位,奇偶校驗位;要注意的是,每臺機器的串口默認設置可能是不同的,如果你沒設置這些,僅僅按照默認設置進行發送數據,很可能出現n多異想不到而又查不出來的情況。
1) 設置波特率

#include <termios.h>
#include <unistd.h>
int cfsetispeed(struct termios *termios_p, speed_t speed);
int cfsetospeed(struct termios *termios_p, speed_t speed);

2) 設置屬性:奇偶校驗位、數據位、停止位。主要設置中的termios結構體即可:

#define NCCS 19
struct termios {
        tcflag_t c_iflag; /* input mode flags */
        tcflag_t c_oflag; /* output mode flags */
        tcflag_t c_cflag; /* control mode flags */
        tcflag_t c_lflag; /* local mode flags */
        cc_t c_line; /* line discipline */
        cc_t c_cc[NCCS]; /* control characters */
};

有相應的函數供獲取和設置屬性:

int tcgetattr(int fd, struct termios *termios_p);
int tcsetattr(int fd, int optional_actions, struct termios *termios_p);

3) 打開、關閉和讀寫串口。串口作爲設備文件,可以直接用文件描述符來進行操作。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);

#include <unistd.h>
int close(int fd);

ssize_t write(int fd, const void *buf, size_t count);
       
ssize_t read(int fd, void *buf, size_t count);

網上的一個例子:

/*串口設備無論是在工控領域,還是在嵌入式設備領域,應用都非常廣泛。而串口編程也就顯得必不可少。
偶然的一次機會,需要使用串口,而且操作系統還要求是Linux,因此,趁着這次機會,綜合別人的代碼,
進行了一次整理和封裝。具體的封裝格式爲C代碼,這樣做是爲了很好的移植性,使它可以在C和C++環境下,
都可以編譯和使用。代碼的頭文件如下: */


///////////////////////////////////////////////////////////////////////////////

//filename:stty.h

#ifndef __STTY_H__
#define __STTY_H__

//包含頭文件

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <pthread.h>
//

// 串口設備信息結構

typedef struct tty_info_t
{
    int fd; // 串口設備ID

    pthread_mutex_t mt; // 線程同步互斥對象

    char name[24]; // 串口設備名稱,例:"/dev/ttyS0"

    struct termios ntm; // 新的串口設備選項

    struct termios otm; // 舊的串口設備選項

} TTY_INFO;
//

// 串口操作函數

TTY_INFO *readyTTY(int id);
int setTTYSpeed(TTY_INFO *ptty, int speed);
int setTTYParity(TTY_INFO *ptty,int databits,int parity,int stopbits);
int cleanTTY(TTY_INFO *ptty);
int sendnTTY(TTY_INFO *ptty,char *pbuf,int size);
int recvnTTY(TTY_INFO *ptty,char *pbuf,int size);
int lockTTY(TTY_INFO *ptty);
int unlockTTY(TTY_INFO *ptty);

#endif


/*從頭文件中的函數定義不難看出,函數的功能,使用過程如下:
(1) 打開串口設備,調用函數setTTYSpeed();
(2) 設置串口讀寫的波特率,調用函數setTTYSpeed();
(3) 設置串口的屬性,包括停止位、校驗位、數據位等,調用函數setTTYParity();
(4) 向串口寫入數據,調用函數sendnTTY();
(5) 從串口讀出數據,調用函數recvnTTY();
(6) 操作完成後,需要調用函數cleanTTY()來釋放申請的串口信息接口;
其中,lockTTY()和unlockTTY()是爲了能夠在多線程中使用。在讀寫操作的前後,需要鎖定和釋放串口資源。
具體的使用方法,在代碼實現的原文件中,main()函數中進行了演示。下面就是源代碼文件: */


////////////////////////////////////////////////////////////////////////////////

//stty.c


#include <stdio.h>
#include <sys/ioctl.h>
#include "stty.h"

///////////////////////////////////////////////////////////////////////////////

// 初始化串口設備並進行原有設置的保存

TTY_INFO *readyTTY(int id)
{
    TTY_INFO *ptty;

    ptty = (TTY_INFO *)malloc(sizeof(TTY_INFO));
    if(ptty == NULL)
        return NULL;
    memset(ptty,0,sizeof(TTY_INFO));
    pthread_mutex_init(&ptty->mt,NULL);
    sprintf(ptty->name,"/dev/ttyS%d",id);
    //

    // 打開並且設置串口

    ptty->fd = open(ptty->name, O_RDWR | O_NOCTTY |O_NDELAY);
    if (ptty->fd <0)
    {
        free(ptty);
        return NULL;
    }
    //

    // 取得並且保存原來的設置

    tcgetattr(ptty->fd,&ptty->otm);
    return ptty;
}

///////////////////////////////////////////////////////////////////////////////

// 清理串口設備資源

int cleanTTY(TTY_INFO *ptty)
{
    //

    // 關閉打開的串口設備

    if(ptty->fd>0)
    {
        tcsetattr(ptty->fd,TCSANOW,&ptty->otm);
        close(ptty->fd);
        ptty->fd = -1;
        free(ptty);
        ptty = NULL;
    }

    return 0;
}


///////////////////////////////////////////////////////////////////////////////

// 設置串口通信速率

// ptty 參數類型(TTY_INFO *),已經初始化的串口設備信息結構指針

// speed 參數類型(int),用來設置串口的波特率

// return 返回值類型(int),函數執行成功返回零值,否則返回大於零的值

///////////////////////////////////////////////////////////////////////////////

int setTTYSpeed(TTY_INFO *ptty, int speed)
{
    int i;
    //

    // 進行新的串口設置,數據位爲8位

    bzero(&ptty->ntm, sizeof(ptty->ntm));
    tcgetattr(ptty->fd,&ptty->ntm);
    ptty->ntm.c_cflag = /*CS8 |*/ CLOCAL | CREAD;

    switch(speed)
    {
    case 300:
        ptty->ntm.c_cflag |= B300;
        break;
    case 1200:
        ptty->ntm.c_cflag |= B1200;
        break;
    case 2400:
        ptty->ntm.c_cflag |= B2400;
        break;
    case 4800:
        ptty->ntm.c_cflag |= B4800;
        break;
    case 9600:
        ptty->ntm.c_cflag |= B9600;
        break;
    case 19200:
        ptty->ntm.c_cflag |= B19200;
        break;
    case 38400:
        ptty->ntm.c_cflag |= B38400;
        break;
    case 115200:
        ptty->ntm.c_cflag |= B115200;
        break;
    }
    ptty->ntm.c_iflag = IGNPAR;
    ptty->ntm.c_oflag = 0;
    //

    //

    tcflush(ptty->fd, TCIFLUSH);
    tcsetattr(ptty->fd,TCSANOW,&ptty->ntm);
    //

    //

    return 0;
}
// 設置串口數據位,停止位和效驗位

// ptty 參數類型(TTY_INFO *),已經初始化的串口設備信息結構指針

// databits 參數類型(int), 數據位,取值爲7或者8

// stopbits 參數類型(int),停止位,取值爲1或者2

// parity 參數類型(int),效驗類型 取值爲N,E,O,,S

// return 返回值類型(int),函數執行成功返回零值,否則返回大於零的值

///////////////////////////////////////////////////////////////////////////////

int setTTYParity(TTY_INFO *ptty,int databits,int parity,int stopbits)
{
    //

    // 取得串口設置

    if( tcgetattr(ptty->fd,&ptty->ntm) != 0)
    {
        printf("SetupSerial [%s]\n",ptty->name);
        return 1;
    }

    bzero(&ptty->ntm, sizeof(ptty->ntm));
    ptty->ntm.c_cflag = CS8 | CLOCAL | CREAD;
    ptty->ntm.c_iflag = IGNPAR;
    ptty->ntm.c_oflag = 0;
    //

    // 設置串口的各種參數

    ptty->ntm.c_cflag &= ~CSIZE;
    switch (databits)
    { //設置數據位數

    case 7:
        ptty->ntm.c_cflag |= CS7;
        break;
    case 8:
        ptty->ntm.c_cflag |= CS8;
        break;
    default:
        printf("Unsupported data size\n");
        return 5;
    }
    //

    //

    switch (parity)
    { // 設置奇偶校驗位數

    case n:
    case N:
        ptty->ntm.c_cflag &= ~PARENB; /* Clear parity enable */
        ptty->ntm.c_iflag &= ~INPCK; /* Enable parity checking */
        break;
    case o:
    case O:
        ptty->ntm.c_cflag |= (PARODD|PARENB); /* 設置爲奇效驗*/
        ptty->ntm.c_iflag |= INPCK; /* Disnable parity checking */
        break;
    case e:
    case E:
        ptty->ntm.c_cflag |= PARENB; /* Enable parity */
        ptty->ntm.c_cflag &= ~PARODD; /* 轉換爲偶效驗*/
        ptty->ntm.c_iflag |= INPCK; /* Disnable parity checking */
        break;
    case S:
    case s: /*as no parity*/
        ptty->ntm.c_cflag &= ~PARENB;
        ptty->ntm.c_cflag &= ~CSTOPB;
        break;
    default:
        printf("Unsupported parity\n");
        return 2;
    }
    //

    // 設置停止位

    switch (stopbits)
    {
    case 1:
        ptty->ntm.c_cflag &= ~CSTOPB;
        break;
    case 2:
        ptty->ntm.c_cflag |= CSTOPB;
        break;
    default:
        printf("Unsupported stop bits\n");
        return 3;
    }
    //

    //

    ptty->ntm.c_lflag = 0;
    ptty->ntm.c_cc[VTIME] = 0; // inter-character timer unused

    ptty->ntm.c_cc[VMIN] = 1; // blocking read until 1 chars received

    tcflush(ptty->fd, TCIFLUSH);
    if (tcsetattr(ptty->fd,TCSANOW,&ptty->ntm) != 0)
    {
        printf("SetupSerial \n");
        return 4;
    }

    return 0;
}

int recvnTTY(TTY_INFO *ptty,char *pbuf,int size)
{
    int ret,left,bytes;

    left = size;

    while(left>0)
    {
        ret = 0;
        bytes = 0;

        pthread_mutex_lock(&ptty->mt);
        ioctl(ptty->fd, FIONREAD, &bytes);
        if(bytes>0)
        {
            ret = read(ptty->fd,pbuf,left);
        }
        pthread_mutex_unlock(&ptty->mt);
        if(ret >0)
        {
            left -= ret;
            pbuf += ret;
        }
        usleep(100);
    }

    return size - left;
}

int sendnTTY(TTY_INFO *ptty,char *pbuf,int size)
{
    int ret,nleft;
    char *ptmp;

    ret = 0;
    nleft = size;
    ptmp = pbuf;

    while(nleft>0)
    {
        pthread_mutex_lock(&ptty->mt);
        ret = write(ptty->fd,ptmp,nleft);
        pthread_mutex_unlock(&ptty->mt);

        if(ret >0)
        {
            nleft -= ret;
            ptmp += ret;
        }
        //usleep(100);

    }

    return size - nleft;
}

int lockTTY(TTY_INFO *ptty)
{
    if(ptty->fd < 0)
    {
        return 1;
    }

    return flock(ptty->fd,LOCK_EX);
}
int unlockTTY(TTY_INFO *ptty)
{
    if(ptty->fd < 0)
    {
        return 1;
    }

    return flock(ptty->fd,LOCK_UN);
}


#ifdef LEAF_TTY_TEST
///////////////////////////////////////////////////////////////////////////////

// 接口測試

int main(int argc,char **argv)
{
    TTY_INFO *ptty;
    int nbyte,idx;
    unsigned char cc[16];

    ptty = readyTTY(0);
    if(ptty == NULL)
    {
        printf("readyTTY(0) error\n");
        return 1;
    }
    //

    //

    lockTTY(ptty);
    if(setTTYSpeed(ptty,9600)>0)
    {
        printf("setTTYSpeed() error\n");
        return -1;
    }
    if(setTTYParity(ptty,8,N,1)>0)
    {
        printf("setTTYParity() error\n");
        return -1;
    }
    //

    idx = 0;
    while(1)
    {
        cc[0] = 0xFA;
        sendnTTY(ptty,&cc[0],1);
        nbyte = recvnTTY(ptty,cc,1);
        printf("%d:%02X\n",idx++,cc[0]);
    }

    cleanTTY(ptty);

}
#endif

參考:
http://hi.baidu.com/szimshan/blog/item/103f34dcb7c4b6a6cd1166aa.html
http://blog.chinaunix.net/u/19671/showart_194522.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章