前面已经了解了串口的基础知识,下面将介绍在Linux下如何编程。以下的程序是,直接串口读,读完写的例程。
串口编程思路:
打开串口,设置波特率,数据位,停止位,奇偶校验位;
读写串口;(直接用read,write函数)
关闭串口。
直接见代码
串口的头文件,先定义一个结构体,含有其串口必备的要设置的数据位,奇偶校验位,停止位,是否连接,波特率,设备名等。
声明串口初始化函数,设置端口,关闭串口,打开串口的函数。
/********************************************************************************
* Copyright: (C) 2018 NULL
* All rights reserved.
*
* Filename: comport.h
* Description: This head file is for the common TTY/Serial port operator library.
*
* Version: 1.0.0(7/30/2018)
* Author: DingHuanhuan <[email protected]>
* ChangeLog: 1, Release initial version on "2018年07月30日 11时16分20秒"
*
********************************************************************************/
#ifndef _COMPORT_H
#define _COMPORT_H
#include <stdio.h> /*standard input and output definition*/
#include <string.h>
#include <stdlib.h> /*standard function library definition*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <unistd.h> /*UNIX standard function definition*/
#include <termios.h> /* PPSIX terminal control definition*/
#include <fcntl.h> /*file control definition*/
#include <errno.h> /*error number definition*/
#include <sys/wait.h>
#define DEVNAME_LEN 64
#define buf_size 4096
typedef struct _COM_PORT
{
unsigned char databit,parity,stopbit,flowctrl,is_connect;
char dev_name[DEVNAME_LEN];
int fd;
int frag_size;
long baudrate;
} COM_PORT;
COM_PORT *Comport_Init(const char *dev_name,unsigned long baudrate, const char *settings);
void Setting_Comport(COM_PORT *comport, const char *settings);
void Comport_close(COM_PORT *comport);
int Comport_open(COM_PORT *comport);
#endif
串口源文件详解
1.按照头文件声明的顺序来,先串口初始化函数,返回一个结构体指针;在初始化函数里含有设置端口函数,结构体指针作为设置端口函数的入口参数;
2.设置端口函数:根据传进来的参数来设置结构体的数据位,奇偶校验位和停止位;
3.关闭串口函数:关闭之前 的串口文件描述符并设置其连接状态为0;
4.打开串口函数:
先来介绍一个最重要的结构体:很多系统都支持POSIX终端(串口)接口.程序可以利用这个接口来改变终端的参数,比如,波特率,字符大小等等.要使用这个端口的话,你必须将
struct termios
{
tcflag_t c_iflag; //输入选项
tcflag_t c_oflag; //输出选项
tcflag_t c_cflag; //控制选项
tcflag_t c_lflag; //行选项
cc_t c_cc[NCCS]; //控制字符
};
这个结构中最重要的是c_cflag,通过对它赋值,用户可以设置数据传输率、字符大小、数据位、停止位、奇偶效验位和硬件流控等。另外c_iflag和c_cc也是比较常用的标志。
c_cflag支持很多常量名称,其中设置数据传输率为相应的数据传输率前要加上“B”。
c_cflag成员不能直接对其初始化,而要将其通过与、或操作使用其中的某些选项。
输入模式c_iflag成员控制端口接收端的字符输入处理;c_cc包含了超时参数和控制字符的定义。
其具体意义如下。
c_iflag:输入模式标志,控制终端输入方式,具体参数如表1所示。
表1 c_iflag参数表
c_oflag:输出模式标志,控制终端输出方式,具体参数如表2所示。
表2 c_oflag参数
c_cflag:控制模式标志,指定终端硬件控制信息,具体参数如表3所示。
表3 c_cflag参数
c_lflag:本地模式标志,控制终端编辑功能,具体参数如表4所示。
表4 c_lflag参数
c_cc[NCCS]:控制字符,用于保存终端驱动程序中的特殊字符,如输入结束符等。c_cc中定义了如表5所示的控制字符。
表5 c_cc支持的控制字符
1)先根据这段判断是否是 我们所要打开的串口。
/* not a TTY device */
if(!strstr(comport->dev_name, "tty"))
{
printf("open not tty device \"%s\".\n",comport->dev_name);
comport->fd = open(comport->dev_name, O_RDWR);
retval = comport->fd <0 ? -2 : comport->fd;
goto CleanUp;
}
2)打开串口,将串口的文件描述符赋值给先前定义的串口结构体中的fd
int tcgetattr(int fd, struct termios *termios_p);
用于获取与终端相关的函数,成功返0,失败返回非零,发生失败接口将设置errno错误标识
eg.保存原先串口配置 tcgetattr(fd,&old_cfg)
该函数得到有fd指向的终端的配置参数,并将它们保存于termios结构变量old_cfg中。若调试成功,函数返回值为0,若调试失败,函数返回值为-1.
comport->fd = open(comport->dev_name,O_RDWR | O_NOCTTY | O_NONBLOCK);
if(comport->fd <0)
{
retval = -3;
goto CleanUp;
}
printf("open device \"%s\"\n",comport->dev_name);
if(0 != tcgetattr(comport->fd, &old_cfg))
{
retval = -4; /* failed to get com settings*/
goto CleanUp;
}
memset(&new_cfg, 0, sizeof(new_cfg));
3)之后就是进行串口配置,之前Setting_Comport函数是将配置信息赋到串口结构体中,接下来这个是直接来改变终端的参数。
/* Configure comport */
new_cfg.c_cflag &= ~CSIZE; /*character length is 0 */
new_cfg.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
new_cfg.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
new_cfg.c_oflag &= ~(OPOST);
/* set the databit */
switch(comport->databit)
{
case 0x07:
new_cfg.c_cflag |= CS7;
break;
case 0x08:
default:
new_cfg.c_cflag |= CS8;
break;
}
/* set the parity */
switch(comport->parity)
{
case 0x01: //Odd check
new_cfg.c_cflag |= PARENB;
new_cfg.c_iflag |= (INPCK | ISTRIP);
new_cfg.c_cflag |= PARODD;
break;
case 0x02: //Even check
new_cfg.c_cflag |= PARENB;
new_cfg.c_iflag |= (INPCK | ISTRIP);
new_cfg.c_cflag &= ~PARODD;
break;
case 0x00:
default:
new_cfg.c_cflag &= ~PARENB;
break;
}
/* set stop bit */
if(0x01 !=comport->stopbit)
new_cfg.c_cflag != CSTOPB;
else
new_cfg.c_cflag &= ~CSTOPB;
switch(comport->baudrate)
{
case 115200:
temp = B115200;
break;
case 9600:
temp = B9600;
break;
case 4800:
temp = B4800;
break;
case 2400:
temp = B2400;
break;
case 1800:
temp = B1800;
break;
case 1200:
temp = B1200;
break;
default:
temp = B115200;
break;
}
cfsetispeed(&new_cfg, temp);
cfsetospeed(&new_cfg, temp);
/* Set the com port timeout settings */
new_cfg.c_cc[VMIN] = 0;
new_cfg.c_cc[VTIME] = 0;
tcflush(comport->fd, TCIFLUSH);
if(0 != tcsetattr(comport->fd, TCSANOW, &new_cfg))
{
retval = -5; //failed to set device comport settings
goto CleanUp;
}
printf("Open device \"%s\".\n", comport->dev_name);
comport->is_connect = 0x01;
retval = comport->fd;
全:串口源码
/*********************************************************************************
* Copyright: (C) 2018 NULL
* All rights reserved.
*
* Filename: comport.c
* Description: It's the comport operate library.
*
* Version: 1.0.0(7/30/2018)
* Author: DingHuanhuan <[email protected]>
* ChangeLog: 1, Release initial version on "2018年07月30日 11时59分34秒"
*
********************************************************************************/
#include "comport.h"
COM_PORT *Comport_Init(const char *dev_name, unsigned long baudrate, const char *settings)
{
COM_PORT *comport = NULL;
if(NULL == (comport = (COM_PORT *)malloc(sizeof(COM_PORT))))
{
return NULL;
}
memset(comport, 0, sizeof(COM_PORT));
comport->is_connect = 0;
comport->frag_size = 128;
strncpy(comport->dev_name, dev_name, DEVNAME_LEN);
comport->baudrate = baudrate;
Setting_Comport(comport, settings);
return comport;
}
void Setting_Comport(COM_PORT *comport, const char *settings)
{
if(NULL == comport || NULL == settings)
return;
switch(settings[0]) /* databit */
{
case '7':
comport->databit = 7;
break;
case '8':
comport->databit = 8;
break;
default:
comport->databit = 8;
break;
}
switch(settings[1]) /*parity check*/
{
case 'O': /*Odd check*/
case 'o':
comport->parity = 1;
break;
case 'E': /* Even check */
case 'e':
comport->parity = 2;
break;
case 'N': /* None parity check */
case 'n':
default:
comport->parity = 0;
break;
}
switch(settings[2]) /*stop bit*/
{
case '1':
comport->stopbit = 1;
break;
case '2':
default:
comport->stopbit = 2;
break;
}
}
void Comport_close(COM_PORT *comport)
{
if(0 != comport->fd)
close(comport->fd);
comport->is_connect = 0x00;
comport->fd = -1;
}
int Comport_open(COM_PORT *comport)
{
int retval = -1;
struct termios old_cfg, new_cfg;
long temp;
if(NULL == comport)
return -1;
Comport_close(comport);
/* not a TTY device */
if(!strstr(comport->dev_name, "tty"))
{
printf("open not tty device \"%s\".\n",comport->dev_name);
comport->fd = open(comport->dev_name, O_RDWR);
retval = comport->fd <0 ? -2 : comport->fd;
goto CleanUp;
}
comport->fd = open(comport->dev_name,O_RDWR | O_NOCTTY | O_NONBLOCK);
if(comport->fd <0)
{
retval = -3;
goto CleanUp;
}
printf("open device \"%s\"\n",comport->dev_name);
if(0 != tcgetattr(comport->fd, &old_cfg))
{
retval = -4; /* failed to get com settings*/
goto CleanUp;
}
memset(&new_cfg, 0, sizeof(new_cfg));
/* Configure comport */
new_cfg.c_cflag &= ~CSIZE; /*character length is 0 */
new_cfg.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
new_cfg.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
new_cfg.c_oflag &= ~(OPOST);
/* set the databit */
switch(comport->databit)
{
case 0x07:
new_cfg.c_cflag |= CS7;
break;
case 0x08:
default:
new_cfg.c_cflag |= CS8;
break;
}
/* set the parity */
switch(comport->parity)
{
case 0x01: //Odd check
new_cfg.c_cflag |= PARENB;
new_cfg.c_iflag |= (INPCK | ISTRIP);
new_cfg.c_cflag |= PARODD;
break;
case 0x02: //Even check
new_cfg.c_cflag |= PARENB;
new_cfg.c_iflag |= (INPCK | ISTRIP);
new_cfg.c_cflag &= ~PARODD;
break;
case 0x00:
default:
new_cfg.c_cflag &= ~PARENB;
break;
}
/* set stop bit */
if(0x01 !=comport->stopbit)
new_cfg.c_cflag != CSTOPB;
else
new_cfg.c_cflag &= ~CSTOPB;
switch(comport->baudrate)
{
case 115200:
temp = B115200;
break;
case 9600:
temp = B9600;
break;
case 4800:
temp = B4800;
break;
case 2400:
temp = B2400;
break;
case 1800:
temp = B1800;
break;
case 1200:
temp = B1200;
break;
default:
temp = B115200;
break;
}
cfsetispeed(&new_cfg, temp);
cfsetospeed(&new_cfg, temp);
/* Set the com port timeout settings */
new_cfg.c_cc[VMIN] = 0;
new_cfg.c_cc[VTIME] = 0;
tcflush(comport->fd, TCIFLUSH);
if(0 != tcsetattr(comport->fd, TCSANOW, &new_cfg))
{
retval = -5; //failed to set device comport settings
goto CleanUp;
}
printf("Open device \"%s\".\n", comport->dev_name);
comport->is_connect = 0x01;
retval = comport->fd;
CleanUp:
printf("open device \"%s\" %s.\n",comport->dev_name,retval >0 ? "successfully" : "failure");
return retval;
}
main函数
int main(int argc,char **argv)
{
char *devname = "/dev/ttyS1";
unsigned long baudrate = 115200;
COM_PORT *comport;
char buf[buf_size];
comport = Comport_Init(devname, baudrate, "8N1");
if(NULL == comport)
{
printf("init serial port failure\n");
return -1;
}
Comport_open(comport);
if(comport->fd <0)
{
printf("open error :%s\n",strerror(errno));
exit(1);
}
printf("OPEN THE DEVICE\n");
while(1)
{
sleep(3);
if(read(comport->fd,buf,buf_size)<0)
return -2;
printf("read is ok\n");
printf("read : %s\n",buf);
write(comport->fd,buf,buf_size);
printf("write is ok\n");
sleep(3);
}
close(comport->fd);
free(comport);
return 0;
}
接下来使用Makefile编译
#This Makefile used to call function to compile all the C source in current folder and links all the objects file into a excutable binary file.
PWD=$(shell pwd)
prom = comport
CROSS_COMPILE=/opt/xtools/arm920t/bin/arm-linux-
export CC=${CROSS_COMPILE}gcc
export CXX=${CROSS_COMPILE}g++
export AR=${CROSS_COMPILE}ar
export AS=${CROSS_COMPILE}as
export RANLIB=${CROSS_COMPILE}ranlib
export STRIP=${CROSS_COMPILE}strip
VPATH= .
SRCS = $(wildcard ${VPATH}/*.c)
DEPS = $(shell find ${VPATH}/*.h)
OBJS = $(patsubst %.c,%.o,$(SRCS))
$(prom): $(OBJS)
@$(CC) -o $(prom) $(OBJS)
@rm -rf *.o
clean:
@rm -rf *.o
make之后编译出可执行文件comport,下载到开发板(tftp -gr comport 192.168.0.137 )后,给以777的权限即可执行。
打开串口调试助手,就可以看到自己串口发送的字符,在SecureCRT(连的是开发板)上可以看到串口所接收的字符。