Linux下串口通訊編程

來源:互聯網
上載者:User
具體的封裝格式為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 = 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;

ptty->ntm.c_iflag &= ~INPCK;

break;

case 'o':

case 'O':

ptty->ntm.c_cflag |= (PARODD|PARENB);

ptty->ntm.c_iflag |= INPCK;

break;

case 'e':

case 'E':

ptty->ntm.c_cflag |= PARENB;

ptty->ntm.c_cflag &= ~PARODD;

ptty->ntm.c_iflag |= INPCK;

break;

case 'S':

case 's':

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:X\n",idx++,cc[0]);

}

cleanTTY(ptty);

}

#endif

串口是電腦上一種非常通用裝置通訊的協議,常用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) 設定屬性:同位位元、資料位元、停止位。主要設定<termbits.h>中的termios結構體即可:

#define NCCS 19
struct termios {
tcflag_t c_iflag;
tcflag_t c_oflag;
tcflag_t c_cflag;
tcflag_t c_lflag;
cc_t c_line;
cc_t c_cc[NCCS];
};

有相應的函數供擷取和設定屬性:

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作業系統使用介面標準POSIX的termios介面來控制串口的行為。在Linux系統中,串口等裝置被當作檔案進行處理,其程式模組主體實現如下:

int fd=open("/dev/ttyS1",O_RDWRIO_NOCTTY);//開啟串口

……

new_options.c_cflag &=~PARENB;//無同位

new_options.c_cflag &=~CSIZE;//不隱藏資料位元

new_options.c_cflag &=~CSTOP8;//無停止位

new_options.c_cflag |=CS8;//8位元據位

cfsetispeed(&new_options,B4800);//設定傳輸速率4800bit/s

cfsetospeed(&new_options,B4800);

tcflush(fd,TCIOFLUSH);

tcsetattr(fd,TCSANOW,&new_options);//設定新的裝置方式

完成串口設定後,就可以使用read( )、write( )函數對串口進行操作。需注意的是,串口預設是阻塞型的,當沒有資料到達時,將會阻塞掛起,這時可以通過多線程編程、串口逾時設定或使用select輪詢 等方式進行調整控制。本系統主要採用多線程編程實現對串口阻塞的調控,使用的是QT的Qthread類,也可以直接使用Linux自身的多線程函數進行操作。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.