例子
下面是一個簡單的讀取串口數據的例子,使用了上面定義的一些函數和頭文件
| /********************************************************************** * 代碼說明:使用串口二測試的,發送的數據是字符,但是沒有發送字符串結束符號, * 所以接收到后,后面加上了結束符號。我測試使用的是單片機發送數據到第二個串口,測試通過。 **********************************************************************/ #define FALSE -1 #define TRUE 0 /*********************************************************************/ int OpenDev(char *Dev) { //Dev 就是設備,設備就是文件,就是給出該設備文件的路徑 int fd = open(Dev, O_RDWR ); //| O_NOCTTY | O_NDELAY if (-1 == fd) { perror("Can't Open Serial Port"); return -1; } else return fd; } int main(int argc, char **argv) { int fd; int nread; char buff[512]; char *dev = "/dev/ttyS1"; //串口二 fd = OpenDev(dev); set_speed(fd, 19200); if (set_Parity(fd, 8, 1, 'N') == FALSE) { printf("Set Parity Error\n"); exit (0); } while (1) //循環讀取數據 { while ((nread = read(fd, buff, 512))>0) { printf("\nLen %d\n", nread); buff[nread+1] = '\0'; printf("\n%s", buff); } } //close(fd); // exit (0); } |
1、虛擬機下使用串口的方法
使用vmwave,默認串口設備是沒有添加的,通過vmwave將設備加入即可正常使用串口。虛擬機串口打開后,可能會占用windows下的串口。另外,虛擬機的串口收發比正常的速度的確要慢許多。
2、消除Linux串口收發的一些規則
Linux 串口收發有許多模式,如:
(1) 接收返回模式: 如果串口沒有接收到數據,read()函數不返回。
(2) 數據接收\n才返回接收的數據,否則read()函數返回0
(3) 特殊字符解析問題,部分特殊字符接收/發送時,會被屏蔽或者轉義。如發送0x0A 接收變為0x0A 0x0A ,0x0D被屏蔽等。
(4) 接收反饋:如串口接收到數據,立即將該數據發送出去。
(上面是我遇到的一些問題,可能表述不很清楚,呵呵。如果用於收發txt文件,一般不大注意。)
3、解決問題的方法是,消除這些默認規則,關鍵是struct termios 的參數影響。
struct termios {
tcflag_t c_iflag; /**//* 輸入模式旗標 */
tcflag_t c_oflag; /**//* 輸出模式旗標 */
tcflag_t c_cflag; /**//* 控制模式旗標 */
tcflag_t c_lflag; /**//* 區域模式旗標 */
cc_t c_line; /**//* 行控制 (line discipline) */
cc_t c_cc[NCCS]; /**//* 控制特性 */
};
由於研究不深,如果要消除所有上面的規則,我是如下處理的
struct termios options;
串口打開方式:
open ("dev/ttyS0" , O_RDWR|O_NOCTTY| O_NDELAY );
消除收發模式規則:
options.c_lflag = 0;
options.c_oflag = 0;
options.c_iflag = 0;
消除字符屏蔽規則:
options.c_cc[VINTR] = 0; /**//* Ctrl-c */
options.c_cc[VQUIT] = 0; /**//* Ctrl- */
options.c_cc[VERASE] = 0; /**//* del */
options.c_cc[VKILL] = 0; /**//* @ */
options.c_cc[VEOF] = 0; /**//* Ctrl-d */
options.c_cc[VTIME] = 1; /**//* */
options.c_cc[VMIN] = 0; /**//* */
options.c_cc[VSWTC] = 0; /**//* '' */
options.c_cc[VSTART] = 0; /**//* Ctrl-q */
options.c_cc[VSTOP] = 0; /**//* Ctrl-s */
options.c_cc[VSUSP] = 0; /**//* Ctrl-z */
options.c_cc[VEOL] = 0; /**//* '' */
options.c_cc[VREPRINT] = 0; /**//* Ctrl-r */
options.c_cc[VDISCARD] = 0; /**//* Ctrl-u */
options.c_cc[VWERASE] = 0; /**//* Ctrl-w */
options.c_cc[VLNEXT] = 0; /**//* Ctrl-v */
options.c_cc[VEOL2] = 0; /**//* '' */
以上設置,在其它參數串口設置前執行,如果你需要保留部分參數,請參閱http://blog.chinaunix.net/article.php?articleId=15964&blogId=60
在RedHat Feroda 4 下編譯通過
= = = = = = = = = = = 非阻塞read= = = = = = = = = = =
Q:在調用串口read(fd, buff, len);時,如果串口沒有數據,會停在read處,請問有沒有辦法讓這個read動作中止?
A:使用非阻塞方式select函數(I/O多工機制)或者open的時候加O_NONBLOCK參數。
int select(int n,fd_set * readfds,fd_set * writefds,fd_set * exceptfds,struct timeval * timeout);關於這個函數的使用我會在下篇blog中整理。
= = = = = = = = = = = 串口收發源碼= = = = = = = = = = =
一下代碼已經經過我測試,沒有問題。開發環境Redhat9,運行環境s3c2410
= = = = = = receive.c= = = = = =
#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 <string.h>
#define TRUE 1
//初始化串口選項:
void setTermios(struct termios * pNewtio, int uBaudRate)
{
bzero(pNewtio, sizeof(struct termios)); /* clear struct for new port settings */
//8N1
pNewtio->c_cflag = uBaudRate | CS8 | CREAD | CLOCAL;
pNewtio->c_iflag = IGNPAR;
pNewtio->c_oflag = 0;
pNewtio->c_lflag = 0; //non ICANON
/*
initialize all control characters
default values can be found in /usr/include/termios.h, and
are given in the comments, but we don't need them here
*/
pNewtio->c_cc[VINTR] = 0; /* Ctrl-c */
pNewtio->c_cc[VQUIT] = 0; /* Ctrl-\ */
pNewtio->c_cc[VERASE] = 0; /* del */
pNewtio->c_cc[VKILL] = 0; /* @ */
pNewtio->c_cc[VEOF] = 4; /* Ctrl-d */
pNewtio->c_cc[VTIME] = 5; /* inter-character timer, timeout VTIME*0.1 */
pNewtio->c_cc[VMIN] = 0; /* blocking read until VMIN character arrives */
pNewtio->c_cc[VSWTC] = 0; /* '\0' */
pNewtio->c_cc[VSTART] = 0; /* Ctrl-q */
pNewtio->c_cc[VSTOP] = 0; /* Ctrl-s */
pNewtio->c_cc[VSUSP] = 0; /* Ctrl-z */
pNewtio->c_cc[VEOL] = 0; /* '\0' */
pNewtio->c_cc[VREPRINT] = 0; /* Ctrl-r */
pNewtio->c_cc[VDISCARD] = 0; /* Ctrl-u */
pNewtio->c_cc[VWERASE] = 0; /* Ctrl-w */
pNewtio->c_cc[VLNEXT] = 0; /* Ctrl-v */
pNewtio->c_cc[VEOL2] = 0; /* '\0' */
}
#define BUFSIZE 512
int main(int argc, char **argv)
{
int fd;
int nread;
char buff[BUFSIZE];
struct termios oldtio, newtio;
struct timeval tv;
char *dev ="/dev/ttyS1";
fd_set rfds;
if ((fd = open(dev, O_RDWR | O_NOCTTY))<0)
{
printf("err: can't open serial port!\n");
return -1;
}
tcgetattr(fd, &oldtio); /* save current serial port settings */
setTermios(&newtio, B115200);
tcflush(fd, TCIFLUSH);
tcsetattr(fd, TCSANOW, &newtio);
tv.tv_sec=30;
tv.tv_usec=0;
while (TRUE)
{
printf("wait...\n");
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
if (select(1+fd, &rfds, NULL, NULL, &tv)>0)
{
if (FD_ISSET(fd, &rfds))
{
nread=read(fd, buff, BUFSIZE);
printf("readlength=%d\n", nread);
buff[nread]='\0';
printf("%s\n", buff);
}
}
}
tcsetattr(fd, TCSANOW, &oldtio);
close(fd);
}
= = = = = send.c= = = = = =
#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 <string.h>
//初始化串口選項:
void setTermios(struct termios * pNewtio, int uBaudRate)
{
bzero(pNewtio, sizeof(struct termios)); /* clear struct for new port settings */
//8N1
pNewtio->c_cflag = uBaudRate | CS8 | CREAD | CLOCAL;
pNewtio->c_iflag = IGNPAR;
pNewtio->c_oflag = 0;
pNewtio->c_lflag = 0; //non ICANON
/*
initialize all control characters
default values can be found in /usr/include/termios.h, and
are given in the comments, but we don't need them here
*/
pNewtio->c_cc[VINTR] = 0; /* Ctrl-c */
pNewtio->c_cc[VQUIT] = 0; /* Ctrl-\ */
pNewtio->c_cc[VERASE] = 0; /* del */
pNewtio->c_cc[VKILL] = 0; /* @ */
pNewtio->c_cc[VEOF] = 4; /* Ctrl-d */
pNewtio->c_cc[VTIME] = 5; /* inter-character timer, timeout VTIME*0.1 */
pNewtio->c_cc[VMIN] = 0; /* blocking read until VMIN character arrives */
pNewtio->c_cc[VSWTC] = 0; /* '\0' */
pNewtio->c_cc[VSTART] = 0; /* Ctrl-q */
pNewtio->c_cc[VSTOP] = 0; /* Ctrl-s */
pNewtio->c_cc[VSUSP] = 0; /* Ctrl-z */
pNewtio->c_cc[VEOL] = 0; /* '\0' */
pNewtio->c_cc[VREPRINT] = 0; /* Ctrl-r */
pNewtio->c_cc[VDISCARD] = 0; /* Ctrl-u */
pNewtio->c_cc[VWERASE] = 0; /* Ctrl-w */
pNewtio->c_cc[VLNEXT] = 0; /* Ctrl-v */
pNewtio->c_cc[VEOL2] = 0; /* '\0' */
}
int main(int argc, char **argv)
{
int fd;
int nCount, nTotal, i;
struct termios oldtio, newtio;
char *dev ="/dev/ttyS1";
if ((argc!=3) || (sscanf(argv[1], "%d", &nTotal) != 1))
{
printf("err: need tow arg!\n");
return -1;
}
if ((fd = open(dev, O_RDWR | O_NOCTTY))<0)
{
printf("err: can't open serial port!\n");
return -1;
}
tcgetattr(fd, &oldtio); /* save current serial port settings */
setTermios(&newtio, B115200);
tcflush(fd, TCIFLUSH);
tcsetattr(fd, TCSANOW, &newtio);
for (i=0; i<nTotal; i++)
{
nCount=write(fd, argv[2], strlen(argv[2]));
printf("send data\n");
sleep(1);
}
tcsetattr(fd, TCSANOW, &oldtio);
close(fd);
return 0;
}
= = = = = =.makefile= = = = = =
CC = /usr/local/arm/2.95.3/bin/arm-linux-gcc
all:receive send
receive: receive.c
$(CC) receive.c -o receive
send: send.c
$(CC) send.c -o send
clean:
-rm -rf testCOM receive send
到此基本就結束了,可能代碼注釋比較少些,寫的太着急了,等有時間整理一下。最好再看看上一篇blog這樣能更好的理解串口。
