背景介绍:因项目需要在ICETEK OMAPL138开发板上实现:能通过uart脚本控制
ICETEK OMAPL138的spi1口,来读写AD/DA器件的寄存器,类似于华为的qshell。很明显,首先需要在ICETEK OMAPL138上实现:接受串口字符串,以及解析字符串,发送字符串到串口等等功能的uart应用程序。
而通常情况下,开发板自带的光盘中会有uart收发应用程序的例程,
ICETEK OMAPL138开发板的uart收发应用程序路径:/usr/local/Omapl138kba/projects/examples/app/comtest/。但是,此例程和网上绝大部分例子一样代码看着不够清晰,所以本文想先从最最最简单的uart收发应用程序开始,介绍一下uart收发应用程序的实现步骤,再把网上常出现的优化后的代码也贴上来以供参考。
参考代码选自作者bg2bkk的两篇文章:Linux串口编程 和 Linux系统串口接收数据编程 。
1. 最简单的uart收发程序
-
#include<stdio.h>
-
#include<stdlib.h>
-
#include<fcntl.h>
-
#include<unistd.h>
-
#include<errno.h>
-
#include<termios.h>
-
#include<sys/types.h>
-
#include<sys/stat.h>
-
int main()
-
{
-
printf("COM1 test program\n");
-
int fd;
-
fd = open("/dev/ttyS2",O_RDWR | O_NDELAY);
-
if(fd == -1)
-
{
-
perror("serialport error\n");
-
}
-
else{
-
printf("open %s succesfully\n", ttyname(fd));
-
}
-
-
struct termios Opt;
-
int status;
-
-
tcgetattr(fd, &Opt);
-
//set speed
-
cfsetispeed(&Opt, B115200);
-
cfsetospeed(&Opt, B115200);
-
//set databits
-
Opt.c_cflag &= ~CSIZE;
-
Opt.c_cflag |= CS8;
-
//set parity
-
Opt.c_cflag &= ~PARENB;
-
Opt.c_iflag &= ~INPCK;
-
//set stopbits
-
Opt.c_cflag &= ~CSTOPB;
-
tcsetattr(fd, TCSANOW, &Opt);
-
status = tcsetattr(fd, TCSANOW, &Opt);
-
if(status != 0)
-
{
-
perror("tcsetattr fd1");
-
return;
-
}
-
-
//write
-
char wrbuff[] = {'h', 'e', 'l', 'l', 'o', '5', 'a', '\n', 'a', 'a', '5', '5', '\0', 'w', 'o', 'r', 'l', 'd', '!'};
-
write(fd, wrbuff, sizeof(wrbuff));//the intervening '\0' will not be printed to UART.
-
-
//read
-
char rdbuff[512];
-
int rdlen;
-
while(1)
-
{
-
if((rdlen = read(fd, rdbuff, sizeof(rdbuff)))>0)//'\n' will also be received as 0x0a.
-
{
-
rdbuff[rdlen] = '\0';
-
printf("receive len:%d receice data:%s", rdlen, rdbuff);
-
}
-
}
-
-
close(fd);
-
return 0;
-
}
很明显,要对串口进行收发要经过以下几个步骤:
1.1 打开串口
在Linux下,与其他的关于设备编程的方法一样,串口编程也是通过操作其设备文件进行的。串口的设备文件是/dev/ttyS0或/dev/ttyS1等。因此要读写串口,我们首先要打开串口。
需要注意的是,open设备文件时的参数:
O_RDWR:读写打开
O_NOCTTY:如果pathname指的是终端设备(tty),则不将此设备分配作为此进程的控制终端
O_NONELAY:如果pathname指的是一个FIFO、一个块特殊文件或一个字符特殊文件,则此选择项为此文件的本次打开操作和后续的I/O操作设置为非阻塞方式
其中,O_NONELAY一定要使用,否则串口因一直被shell占用而不能被此应用程序使用。
1.2 使用tcgetattr函数与tcsetattr函数控制终端
为了便于通过程序来获得和修改终端参数,Linux还提供了tcgetattr函数和tcsetattr函数。tcgetattr用于获取终端的相关参数,而tcsetattr函数用于设置终端参数。
1.3 设置串口通信参数
串口通信参数指的是波特率、数据位、奇偶校验位和停止位,这些参数保存在结构体termios中。
1.3.1 设置波特率
先用tcgetattr获得termios中的相关参数,再用cfsetispeed和cfsetospeed设置termios中存储的输入/输出波特率,最后用tcsetattr将termios中的参数重新设置下去。一般情况下,输入和输出波特率是相等的。
1.3.2 设置数据位、奇偶校验位和停止位
先用tcgetattr获得termios中的相关参数,再通过修改termios中c_cflag成员来实现设置数据位、奇偶校验位和停止位,最后用tcsetattr将termios中的参数重新设置下去。
1.4 串口收发
通过以上设置了串口的基本信息后,就可以用read和write读写串口数据,实现串口的收发功能。
2. 常见常用的uart收发程序
然而,我们常见的且也经常用的串口收发程序,是对设置串口通信参数做了函数封装,然后再循环调用read和write读写串口数据。
另外,如果我们把这种循环read的方式称为循环读取数据,但这种方式会一直占用当前串口而不能在后台运行,所以我们另外再介绍两种方式:通过signal机制读取数据、通过select系统调用进行io多路切换实现异步读取串口数据,此两种方式都能用来后台读取数据,值得学习。后两种方式博主并未亲自验证,有兴趣的同学可以试试。
2.1 循环读取数据
-
#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>
-
-
#define FALSE -1
-
#define TRUE 0
-
-
int speed_arr[] = { B38400, B19200, B9600, B4800, B2400, B1200, B300,B38400, B19200, B9600, B4800, B2400, B1200, B300, };
-
int name_arr[] = {38400, 19200, 9600, 4800, 2400, 1200, 300, 38400, 19200, 9600, 4800, 2400, 1200, 300, };
-
void set_speed(int fd, int speed){
-
int i;
-
int status;
-
struct termios Opt;
-
tcgetattr(fd, &Opt);
-
for ( i= 0; i < sizeof(speed_arr) / sizeof(int); i++) {
-
if (speed == name_arr[i]) {
-
tcflush(fd, TCIOFLUSH);
-
cfsetispeed(&Opt, speed_arr[i]);
-
cfsetospeed(&Opt, speed_arr[i]);
-
status = tcsetattr(fd, TCSANOW, &Opt);
-
if (status != 0) {
-
perror("tcsetattr fd1");
-
return;
-
}
-
tcflush(fd,TCIOFLUSH);
-
}
-
}
-
}
-
-
int set_Parity(int fd,int databits,int stopbits,int parity)
-
{
-
struct termios options;
-
if ( tcgetattr( fd,&options) != 0) {
-
perror("SetupSerial 1");
-
return(FALSE);
-
}
-
options.c_cflag &= ~CSIZE;
-
switch (databits)
-
{
-
case 7:
-
options.c_cflag |= CS7;
-
break;
-
case 8:
-
options.c_cflag |= CS8;
-
break;
-
default:
-
fprintf(stderr,"Unsupported data size\n"); return (FALSE);
-
}
-
switch (parity)
-
{
-
case 'n':
-
case 'N':
-
options.c_cflag &= ~PARENB; /* Clear parity enable */
-
options.c_iflag &= ~INPCK; /* Enable parity checking */
-
break;
-
case 'o':
-
case 'O':
-
options.c_cflag |= (PARODD | PARENB);
-
options.c_iflag |= INPCK; /* Disnable parity checking */
-
break;
-
case 'e':
-
case 'E':
-
options.c_cflag |= PARENB; /* Enable parity */
-
options.c_cflag &= ~PARODD;
-
options.c_iflag |= INPCK; /* Disnable parity checking */
-
break;
-
case 'S':
-
case 's': /*as no parity*/
-
options.c_cflag &= ~PARENB;
-
options.c_cflag &= ~CSTOPB;break;
-
default:
-
fprintf(stderr,"Unsupported parity\n");
-
return (FALSE);
-
}
-
-
switch (stopbits)
-
{
-
case 1:
-
options.c_cflag &= ~CSTOPB;
-
break;
-
case 2:
-
options.c_cflag |= CSTOPB;
-
break;
-
default:
-
fprintf(stderr,"Unsupported stop bits\n");
-
return (FALSE);
-
}
-
/* Set input parity option */
-
if (parity != 'n')
-
options.c_iflag |= INPCK;
-
tcflush(fd,TCIFLUSH);
-
options.c_cc[VTIME] = 150;
-
options.c_cc[VMIN] = 0; /* Update the options and do it NOW */
-
if (tcsetattr(fd,TCSANOW,&options) != 0)
-
{
-
perror("SetupSerial 3");
-
return (FALSE);
-
}
-
return (TRUE);
-
}
-
-
int main()
-
{
-
printf("This program updates last time at %s %s\n",__TIME__,__DATE__);
-
printf("STDIO COM1\n");
-
int fd;
-
fd = open("/dev/ttyS0",O_RDWR | O_NDELAY);
-
if(fd == -1)
-
{
-
perror("serialport error\n");
-
}
-
else
-
{
-
printf("open ");
-
printf("%s",ttyname(fd));
-
printf(" succesfully\n");
-
}
-
-
set_speed(fd,115200);
-
if (set_Parity(fd,8,1,'N') == FALSE) {
-
printf("Set Parity Error\n");
-
exit (0);
-
}
-
-
//write
-
char wrbuff[] = {'h', 'e', 'l', 'l', 'o', '5', 'a', '\n', 'a', 'a', '5', '5', '\0', 'w', 'o', 'r', 'l', 'd', '!'};
-
write(fd, wrbuff, sizeof(wrbuff));//the intervening '\0' will not be printed to UART.
-
-
//read
-
char rdbuff[512];
-
int rdlen;
-
while(1)
-
{
-
if((rdlen = read(fd, rdbuff, sizeof(rdbuff)))>0)//'\n' will also be received as 0x0a.
-
{
-
rdbuff[rdlen] = '\0';
-
printf("receive len:%d receice data:%s", rdlen, rdbuff);
-
}
-
}
-
-
close(fd);
-
return 0;
-
}
2.2 通过signal机制读取数据---未验证
此方式是在用户层通过用信号signal机制模拟软中断,来读取串口数据。
-
#include<stdio.h>
-
#include<stdlib.h>
-
#include<unistd.h>
-
#include<sys/types.h>
-
#include<sys/stat.h>
-
#include<sys/signal.h>
-
#include<fcntl.h>
-
#include<termios.h>
-
#include<errno.h>
-
-
#define FALSE -1
-
#define TRUE 0
-
#define flag 1
-
#define noflag 0
-
-
int wait_flag = noflag;
-
int STOP = 0;
-
int res;
-
-
int speed_arr[] =
-
{ B38400, B19200, B9600, B4800, B2400, B1200, B300, B38400, B19200, B9600,
-
B4800, B2400, B1200, B300, };
-
int name_arr[] =
-
{ 38400, 19200, 9600, 4800, 2400, 1200, 300, 38400, 19200, 9600, 4800, 2400,
-
1200, 300, };
-
void
-
set_speed (int fd, int speed)
-
{
-
int i;
-
int status;
-
struct termios Opt;
-
tcgetattr (fd, &Opt);
-
for (i = 0; i < sizeof (speed_arr) / sizeof (int); i++)
-
{
-
if (speed == name_arr[i])
-
{
-
tcflush (fd, TCIOFLUSH);
-
cfsetispeed (&Opt, speed_arr[i]);
-
cfsetospeed (&Opt, speed_arr[i]);
-
status = tcsetattr (fd, TCSANOW, &Opt);
-
if (status != 0)
-
{
-
perror ("tcsetattr fd1");
-
return;
-
}
-
tcflush (fd, TCIOFLUSH);
-
}
-
}
-
}
-
-
int
-
set_Parity (int fd, int databits, int stopbits, int parity)
-
{
-
struct termios options;
-
if (tcgetattr (fd, &options) != 0)
-
{
-
perror ("SetupSerial 1");
-
return (FALSE);
-
}
-
options.c_cflag &= ~CSIZE;
-
switch (databits)
-
{
-
case 7:
-
options.c_cflag |= CS7;
-
break;
-
case 8:
-
options.c_cflag |= CS8;
-
break;
-
default:
-
fprintf (stderr, "Unsupported data size\n");
-
return (FALSE);
-
}
-
switch (parity)
-
{
-
case 'n':
-
case 'N':
-
options.c_cflag &= ~PARENB; /* Clear parity enable */
-
options.c_iflag &= ~INPCK; /* Enable parity checking */
-
break;
-
case 'o':
-
case 'O':
-
options.c_cflag |= (PARODD | PARENB);
-
options.c_iflag |= INPCK; /* Disnable parity checking */
-
break;
-
case 'e':
-
case 'E':
-
options.c_cflag |= PARENB; /* Enable parity */
-
options.c_cflag &= ~PARODD;
-
options.c_iflag |= INPCK; /* Disnable parity checking */
-
break;
-
case 'S':
-
case 's': /*as no parity */
-
options.c_cflag &= ~PARENB;
-
options.c_cflag &= ~CSTOPB;
-
break;
-
default:
-
fprintf (stderr, "Unsupported parity\n");
-
return (FALSE);
-
}
-
-
switch (stopbits)
-
{
-
case 1:
-
options.c_cflag &= ~CSTOPB;
-
break;
-
case 2:
-
options.c_cflag |= CSTOPB;
-
break;
-
default:
-
fprintf (stderr, "Unsupported stop bits\n");
-
return (FALSE);
-
}
-
/* Set input parity option */
-
if (parity != 'n')
-
options.c_iflag |= INPCK;
-
tcflush (fd, TCIFLUSH);
-
options.c_cc[VTIME] = 150;
-
options.c_cc[VMIN] = 0; /* Update the options and do it NOW */
-
if (tcsetattr (fd, TCSANOW, &options) != 0)
-
{
-
perror ("SetupSerial 3");
-
return (FALSE);
-
}
-
return (TRUE);
-
}
-
-
void
-
signal_handler_IO (int status)
-
{
-
printf ("received SIGIO signale.\n");
-
wait_flag = noflag;
-
}
-
-
int
-
main ()
-
{
-
printf ("This program updates last time at %s %s\n", __TIME__, __DATE__);
-
printf ("STDIO COM1\n");
-
int fd;
-
struct sigaction saio;
-
fd = open ("/dev/ttyUSB0", O_RDWR);
-
if (fd == -1)
-
{
-
perror ("serialport error\n");
-
}
-
else
-
{
-
printf ("open ");
-
printf ("%s", ttyname (fd));
-
printf (" succesfully\n");
-
}
-
-
saio.sa_handler = signal_handler_IO;
-
sigemptyset (&saio.sa_mask);
-
saio.sa_flags = 0;
-
saio.sa_restorer = NULL;
-
sigaction (SIGIO, &saio, NULL);
-
-
//allow the process to receive SIGIO
-
fcntl (fd, F_SETOWN, getpid ());
-
//make the file descriptor asynchronous
-
fcntl (fd, F_SETFL, FASYNC);
-
-
set_speed (fd, 115200);
-
if (set_Parity (fd, 8, 1, 'N') == FALSE)
-
{
-
printf ("Set Parity Error\n");
-
exit (0);
-
}
-
-
char buf[255];
-
while (STOP == 0)
-
{
-
usleep (100000);
-
/* after receving SIGIO ,wait_flag = FALSE,input is availabe and can be read */
-
if (wait_flag == 0)
-
{
-
memset (buf, 0, sizeof(buf));
-
res = read (fd, buf, 255);
-
printf ("nread=%d,%s\n", res, buf);
-
// if (res ==1)
-
// STOP = 1; /*stop loop if only a CR was input */
-
wait_flag = flag; /*wait for new input */
-
}
-
}
-
-
-
close (fd);
-
return 0;
-
}
2.3 通过select系统调用进行io多路切换实现异步读取串口数据---未验证
此方式是通过select系统调用,在没有数据时阻塞进程,串口有数据需要读时唤醒进程。
-
#include<stdio.h>
-
#include<stdlib.h>
-
#include<unistd.h>
-
#include<sys/types.h>
-
#include<sys/stat.h>
-
#include<sys/signal.h>
-
#include<fcntl.h>
-
#include<termios.h>
-
#include<errno.h>
-
-
#define FALSE -1
-
#define TRUE 0
-
#define flag 1
-
#define noflag 0
-
-
int wait_flag = noflag;
-
int STOP = 0;
-
int res;
-
-
int speed_arr[] =
-
{ B38400, B19200, B9600, B4800, B2400, B1200, B300, B38400, B19200, B9600,
-
B4800, B2400, B1200, B300, };
-
int name_arr[] =
-
{ 38400, 19200, 9600, 4800, 2400, 1200, 300, 38400, 19200, 9600, 4800, 2400,
-
1200, 300, };
-
void
-
set_speed (int fd, int speed)
-
{
-
int i;
-
int status;
-
struct termios Opt;
-
tcgetattr (fd, &Opt);
-
for (i = 0; i < sizeof (speed_arr) / sizeof (int); i++)
-
{
-
if (speed == name_arr[i])
-
{
-
tcflush (fd, TCIOFLUSH);
-
cfsetispeed (&Opt, speed_arr[i]);
-
cfsetospeed (&Opt, speed_arr[i]);
-
status = tcsetattr (fd, TCSANOW, &Opt);
-
if (status != 0)
-
{
-
perror ("tcsetattr fd1");
-
return;
-
}
-
tcflush (fd, TCIOFLUSH);
-
}
-
}
-
}
-
-
int
-
set_Parity (int fd, int databits, int stopbits, int parity)
-
{
-
struct termios options;
-
if (tcgetattr (fd, &options) != 0)
-
{
-
perror ("SetupSerial 1");
-
return (FALSE);
-
}
-
options.c_cflag &= ~CSIZE;
-
switch (databits)
-
{
-
case 7:
-
options.c_cflag |= CS7;
-
break;
-
case 8:
-
options.c_cflag |= CS8;
-
break;
-
default:
-
fprintf (stderr, "Unsupported data size\n");
-
return (FALSE);
-
}
-
switch (parity)
-
{
-
case 'n':
-
case 'N':
-
options.c_cflag &= ~PARENB; /* Clear parity enable */
-
options.c_iflag &= ~INPCK; /* Enable parity checking */
-
break;
-
case 'o':
-
case 'O':
-
options.c_cflag |= (PARODD | PARENB);
-
options.c_iflag |= INPCK; /* Disnable parity checking */
-
break;
-
case 'e':
-
case 'E':
-
options.c_cflag |= PARENB; /* Enable parity */
-
options.c_cflag &= ~PARODD;
-
options.c_iflag |= INPCK; /* Disnable parity checking */
-
break;
-
case 'S':
-
case 's': /*as no parity */
-
options.c_cflag &= ~PARENB;
-
options.c_cflag &= ~CSTOPB;
-
break;
-
default:
-
fprintf (stderr, "Unsupported parity\n");
-
return (FALSE);
-
}
-
-
switch (stopbits)
-
{
-
case 1:
-
options.c_cflag &= ~CSTOPB;
-
break;
-
case 2:
-
options.c_cflag |= CSTOPB;
-
break;
-
default:
-
fprintf (stderr, "Unsupported stop bits\n");
-
return (FALSE);
-
}
-
/* Set input parity option */
-
if (parity != 'n')
-
options.c_iflag |= INPCK;
-
tcflush (fd, TCIFLUSH);
-
options.c_cc[VTIME] = 150;
-
options.c_cc[VMIN] = 0; /* Update the options and do it NOW */
-
if (tcsetattr (fd, TCSANOW, &options) != 0)
-
{
-
perror ("SetupSerial 3");
-
return (FALSE);
-
}
-
return (TRUE);
-
}
-
-
void
-
signal_handler_IO (int status)
-
{
-
printf ("received SIGIO signale.\n");
-
wait_flag = noflag;
-
}
-
-
int
-
main ()
-
{
-
printf ("This program updates last time at %s %s\n", __TIME__, __DATE__);
-
printf ("STDIO COM1\n");
-
int fd;
-
fd = open ("/dev/ttyUSB0", O_RDWR);
-
if (fd == -1)
-
{
-
perror ("serialport error\n");
-
}
-
else
-
{
-
printf ("open ");
-
printf ("%s", ttyname (fd));
-
printf (" succesfully\n");
-
}
-
-
set_speed (fd, 115200);
-
if (set_Parity (fd, 8, 1, 'N') == FALSE)
-
{
-
printf ("Set Parity Error\n");
-
exit (0);
-
}
-
-
char buf[255];
-
fd_set rd;
-
int nread = 0;
-
while(1)
-
{
-
FD_ZERO(&rd);
-
FD_SET(fd, &rd);
-
while(FD_ISSET(fd, &rd))
-
{
-
if(select(fd+1, &rd, NULL,NULL,NULL) < 0)
-
{
-
perror("select error\n");
-
}
-
else
-
{
-
while((nread = read(fd, buf, sizeof(buf))) > 0)
-
{
-
printf("nread = %d,%s\n",nread, buf);
-
printf("test\n");
-
memset(buf, 0 , sizeof(buf));
-
}
-
}
-
}
-
}
-
close (fd);
-
return 0;
-
}
阅读(3174) | 评论(0) | 转发(0) |