Linux 串口编程
串行接口
串行接口 (Serial Interface) 是指数据一位一位地顺序传送,其特点是通信线路简单,只要一对传输线就可以实现双向通信,从而大大降低了成本,特别适用于远距离通信,但传送速度较慢。一条信息的各位数据被逐位按顺序传送的通讯方式称为串行通讯。
串行通讯的特点是:数据位的传送,按位顺序进行,最少只需一根传输线即可完成;成本低但传送速度慢。串行通讯的距离可以从几米到几千米;根据信息的传送方向,串行通讯可以进一步分为单工、半双工和全双工三种。
同步串行接口(SynchronousSerialInterface,SSI)是一种常用的工业用通信接口。。
异步串行是指UART(Universal Asynchronous Receiver/Transmitter),通用异步接收/发送。UART是一个并行输入成为串行输出的芯片,通常集成在主板上。UART包含TTL电平的串口和RS232电平的串口。
串口除了RS232,还有RS422,RS485,今天主要说的是RS232,其实USB(Universal Serial Bus)也是串口的一种,它的协议更加复杂,还有采用D+和D-差分信号,抗干扰能力更强,速率更快。
图片都是之前从网上找到
接下来开始讲代码了,主要分成两个部分,一就是串口配置,二是串口读写
int open_uart(char* port)
{
int fd = open(port,O_RDWR|O_NOCTTY);
if (fd < 0){
printf("Open serial %s fail\n",port);
return -1;
}
else{
//printf("Open serial %s successful\n",port);
}
//set bolck mode
if (fcntl(fd,F_SETFL,0)<0){
printf("set %s block failed\n",port);
}
if(0 == isatty(STDIN_FILENO)){
printf("standard input is not a terminal device\n");
}
//else{
// printf("isatty success\n");
//}
return fd;
}
配置串口
int set_uart_config(int fd,uart_cfg_t* s_cfg)
{
int ret;
unsigned int i;
int speed_arr[] = { B115200, B9600};
int name_arr[] = {115200, 9600};
struct termios options;
ret = tcgetattr( fd,&options);
if( ret != 0){
printf("ERROR: Get Serial Failed\n");
return ret;
}
for ( i= 0; i < sizeof(speed_arr) / sizeof(int); i++){
if(s_cfg->speed == name_arr[i]){
cfsetispeed(&options, speed_arr[i]);
cfsetospeed(&options, speed_arr[i]);
}
}
options.c_cflag |= CLOCAL;
options.c_cflag |= CREAD;
switch(s_cfg->flow_ctrl)
{
case 0 :
options.c_cflag &= ~CRTSCTS;
break;
case 1 :
options.c_cflag |= CRTSCTS;
break;
case 2 :
options.c_cflag |= IXON | IXOFF | IXANY;
break;
default:
options.c_cflag |= IXON | IXOFF | IXANY;
break;
}
options.c_cflag &= ~CSIZE;
switch (s_cfg->data_bits){
case 5 :
options.c_cflag |= CS5;
break;
case 6 :
options.c_cflag |= CS6;
break;
case 7 :
options.c_cflag |= CS7;
break;
case 8:
options.c_cflag |= CS8;
break;
default:
options.c_cflag |= CS8;
break;
}
switch (s_cfg->parity[0]){
case 'n':
case 'N':
options.c_cflag &= ~PARENB;
options.c_iflag &= ~INPCK;
break;
case 'o':
case 'O':
options.c_cflag |= (PARODD | PARENB);
options.c_iflag |= INPCK;
break;
case 'e':
case 'E':
options.c_cflag |= PARENB;
options.c_cflag &= ~PARODD;
options.c_iflag |= INPCK;
break;
case 's':
case 'S':
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
break;
default:
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
break;
}
switch (s_cfg->stop_bits){
case 1:
options.c_cflag &= ~CSTOPB;
break;
case 2:
options.c_cflag |= CSTOPB;
break;
default:
options.c_cflag |= CSTOPB;
break;
}
options.c_oflag &= ~OPOST;
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
options.c_cc[VTIME] = 20;
options.c_cc[VMIN] = 255;
tcflush(fd,TCIFLUSH);
ret=tcsetattr(fd,TCSANOW,&options);
if (ret != 0){
printf("ERROR: com set error!\n");
return ret;
}
return ret;
}
下面这些是我在写的时候,从网上查找并记录下来的一些笔记:
tcflag_t 是 unsigned short类型,cc_t 是 unsigned char类型
CRTCTS
使用硬件流控制。在高速(19200bps或更高)传输时,使用软件流控制会使效率降低,这个时候必须使用硬件流控制
~CRTCTS不控制
IXOFF
如果设置,为避免tty设备的输入缓冲区溢出,tty设备可以向终端发送停止符^S和开始符^Q,要求终端停止或重新开始
向计算机发送数据。通过停止符和开始符来控制数据流的方式叫软件流控制,软件流控制方式较少用,我们主要还是用
硬件流控制方式。硬件流控制在c_cflag标志中设置。
IXON
如果设置,接收到^S后会停止向这个tty设备输出,接收到^Q后会恢复输出。
IXANY
如果设置,则接到任何字符都会重新开始输出,而不仅仅是^Q字符。
options.c_cflag |= CS8; //使用8位数据位
options.c_cflag |= CS7; //使用7位数据位
options.c_cflag |= CS6; //使用6位数据位
options.c_cflag |= CS5; //使用5位数据位
CSIZE 先屏蔽其他标志位
options.c_cflag |= CSTOPB; //2位停止位
options.c_cflag &= ~CSTOPB; //1位停止位
PARODD 使用奇校验而不是偶校验
PARENB 校验使能位
INPCK 奇偶校验使能
1. N(None [没有])
2. O Odd 奇校验
3. E Even 偶校验
4. S Space 校验位固定为0
ISIG 当输入INTR、QUIT、SUSP或DSUSP时,产生相应的信号
ICANON 使用标准输入模式
XCASE 在ICANON和XCASE同时设置的情况下,终端只使用大写。
ECHO 显示输入字符
ECHOE 如果ICANON同时设置,ERASE将删除输入的字符
ECHOK 如果ICANON同时设置,KILL将删除当前行
OPOST 处理后输出
open未设置O_NONBLOCK或O_NDELAY的情况下
VTIME定义要求等待的时间量(取值不能大于cc_t)。
VMIN定义了要求等待的最小字节数。
options.c_cc[VTIME] = X; //设置从获取到1个字节后开始计时的超时时间
options.c_cc[VMIN] = Y; ? //设置要求等待的最小字节数
1、X=0,Y!=0。函数read()只有在读取了Y个字节的数据或者收到一个信号的时候才返回;
2、X!=0,Y=0。即使没有数据可以读取,read()函数等待X时间量后返回;
3、X!=0,Y!=0。第一个字节数据到时开始,最先满足收到Y个字节或达超时时间X任意一个条件,read()返回;
4、X=0,Y=0。即使读取不到任何数据,函数read也会立即返回
常用函数介绍。
1、读取当前参数函数:
int tcgetattr(int fd,struct termios *termios_p)
fd:open操作后返回的文件句柄
*termios_p:为前面介绍的结构体
初始化开始前调用这个函数.
2、获取当前波特率函数:
int speed_t cfgetispeed(const struct termios *termios_p)
int speed_t cfgetospeed(const struct termios *termios_p)
*termios_p:为前面介绍的结构体
成功返回0,失败返回-1
3、波特率设置函数:
int cfsetispeed(struct termios *termios_p,speed_t speed)
int cfsetospeed(struct termios *termios_p,speed_t speed)
*termios_p:为前面介绍的结构体
speed:波特率,常用B2400,B4800,B9600,B115200,B460800
成功返回0,失败返回-1
4、清空buffer数据函数:
int tcflush(int fd,int queue_selector)
queue_selector:有三个常用宏定义
TCIFLUSH:清空正读的数据,且不会读出
TCOFLUSH:清空正写入的数据,且不会发送到终端
TCIOFLUSH:清空所有正在发生的I/O数据.
成功返回0,失败返回-1
5、设置串口参数函数:
int tcsetattr(int fd,int optional_actions,cons struct termios *termios_p)
optional_actions:有三个常用宏定义
TCSANOW:不等数据传输完毕,立即改变属性
TCSADRAIN:等所有数据传输完毕,再改变属性
TCSAFLUSH:清空输入输出缓冲区才改变属性
成功返回0,失败返回-1
第二部分:串口读写,在这里我使用的是经tx和rx连接起来,自环
int single_uart_test(uart_cfg_t *s_cfg)
{
int len = 0,res = 0,len_tmp = 0,flag = 0;
char tmp[4096];
bzero(tmp,sizeof(tmp));
int fd = open_uart(uart_map(s_cfg->dev_id));
init_uart(fd,s_cfg);
if(signal(SIGTSTP,func_quit) == SIG_ERR){
printf("signal error exit now!\n");
return -1;
}
if(signal(SIGINT,func_quit) == SIG_ERR){
printf("signal error exit now!\n");
return -1;
}
printf("Welcome to com%d test mode:\n",s_cfg->dev_id);
while(true){
if (!flag) printf("Com%d says:",s_cfg->dev_id);
flag++;
fgets(big_buf,BUFFER_SIZE,stdin);
len = strlen(big_buf);
//printf("len:%d\n",len);
write_uart(fd,big_buf,len,s_cfg->dev_id);
read_uart(fd,s_cfg->dev_id,len,read_buf);
if( (0 == strncmp(read_buf,"quit",4)) || (0 == strncmp(read_buf,"exit",4))){
printf("quit console %d test mode \n",s_cfg->dev_id);
exit(0);
}
if ( 0 != strncmp(read_buf,big_buf,sizeof(read_buf))){
res++;
}
//string -'\0'
if(len == (BUFFER_SIZE-1)){
memcpy(tmp+len_tmp,read_buf,len);
len_tmp += len;
//printf("tmp:%s\n",tmp);
}else{
memcpy(tmp+len_tmp,read_buf,len);
if(res == 0){
printf("Com%d recv:%s\n",s_cfg->dev_id,tmp);
printf("Com%d ------->pass \n",s_cfg->dev_id);
len_tmp = 0;
bzero(tmp,sizeof(tmp));
}else{
printf("Com%d ------->fail \n",s_cfg->dev_id);
//flushes both data received but not read, and data written but not transmitted.
tcflush(fd,TCIOFLUSH);
}
flag = 0;
}
bzero(read_buf,sizeof(read_buf));
bzero(big_buf,sizeof(big_buf));
}
close_uart(fd,s_cfg->dev_id);
return err_no;
}
源码放在资源下载,源码是一个48ports串口服务器的串口测试代码,有删减。
本文地址:https://blog.csdn.net/weixin_38452632/article/details/107972517