有关linux串口通信

有关linux串口通信

Talk is cheap,show me the code.

COM_Linux.h
#ifndef __COM_LINUX_H__
#define __COM_LINUX_H__

#define MAX_BUF 512 //串口数据缓冲最大字节数
#define MIN(A,B) (A<B?A:B)

int COM_open(char*);
int COM_set(int,int,int,int,char,char);
int COM_BufQueSize(int,int*);
int COM_recv(int,char*,int);
int COM_transmit(int,char*,int);

#endif
#include <stdio.h>      /*开头两个头文件就不多说了*/
#include <stdlib.h>
#include <string.h>     /*字符串操作会用到*/
#include <unistd.h>     /*Unix标准函数定义 unix std*/
#include <fcntl.h>      /*文件控制定义 file control*/
#include <termios.h>    /*POSIX 终端控制定义*/
#include <errno.h>      /*错误号定义*/
#include <sys/ioctl.h>
#include "COM_Linux.h"

/******************************************************
 * 名称:  COM_open
 * 功能:  打开串口并返回串口设备文件描述
 * 传参:  fd:文件描述符 port:串口号(ttyS0,ttyUSB0...)
 * 返回:  正确 fd 错误 -1
*******************************************************/
int COM_open(char* port) 
{
    int fd=-1; 
    fd=open(port,O_RDWR|O_NOCTTY|O_NDELAY); 
    /*open函数代表打开串口设备
     * 一般推荐的标志位参数如上述,通过|运算组合
     *O_RDWR 以可读写方式打开文件
     *O_NOCTTY 表明本程序非串口上的控制终端,不受组合键信号影响
     *O_DELAY 表示忽略DCD信号线状态(另一端是否已经链接)
     *否则open函数会阻塞等待DCD信号,导致程序丢失相应*/
     if(-1 == fd){
        perror("Unable to Open Uart\n"); 
        return -1;
     }else{
        printf("fd->open=%d\n",fd);
     }
     //恢复串口为阻塞状态
     if(fcntl(fd,F_SETFL,0)<0){ 
        printf("set flag error!\n");     
        return -1;
     }else{
        printf("fcntl=%d\n",fcntl(fd,F_SETFL,0));
     }
     return fd;
}
/********************************************
 * 名称:                  COM_close
 * 功能:              关闭串口
 * 入口参数:            fd
 * 出口参数:            成功 0    错误 -1
 * ******************************************/
int COM_close(fd)
{
    if(fd<0) return -1;
    if(close(fd)==-1) return -1;
    printf("Close Uart\n");
    return 0;
}
/************************************************************
 * 名称:      COM_set
 * 功能:      置串口各项参数
 * 入口参数:    fd
 *              baud        串口速度,即波特率 4800 9600 115200
 *              data_bit    数据位 一个字节的数据位个数 7、8
 *              stop_bit    停止位 停止位个数 1、2
 *              parity      校验类型 N:无校验 O:奇校验ODD E: 偶校验EVEN
 *              flow_ctrl   数据流控制 N:无 S:软件流 H:硬件流
 * 出口参数:    正确0,错误-1
 * ********************************************************/
int COM_set(int fd,int baud,int data_bit,int stop_bit,char parity,char flow_ctrl)
{
    struct termios options;
    //tcgetattr(fd,&options)得到fd指向对象的相关参数
    //保存在termios定义的结构体内
    //且可测试配置是否正确,调用成功返回0,失败返回-1
    if(tcgetattr(fd,&options)!=0){
        perror("Set com error\n");  
        return -1;
    }
    switch(baud){
    case 4800:
        /*c_cflag成员用于描述串口硬件控制 用于波特率 校验 数据位 停止 控制流等
         * 所有的都是位操作,通过位运算的与或非关系来设置或清楚相关标志 */
        options.c_cflag=B4800;
        break;
    case 9600:
        options.c_cflag=B9600;
        break;
    case 115200:
        options.c_cflag=B115200;
        break;
    default:
        options.c_cflag=B9600;
        break;
    }
    switch(data_bit){
    case 7:
        options.c_cflag&=~CSIZE;
        options.c_cflag|=CS7;
        break;
    case 8:
        options.c_cflag&=~CSIZE;
        options.c_cflag|=CS8;
        break;
    default:
        options.c_cflag&=~CSIZE;
        options.c_cflag|=CS8;
        break;
    }
    switch(stop_bit){
    case 1:
        options.c_cflag&=~CSTOPB;
        break;
    case 2:
        options.c_cflag|=CSTOPB;
        break;
    default:
        options.c_cflag&=~CSTOPB;
        break;
    }
    switch(parity){
    case 'N':
        options.c_cflag&=~PARENB; //无奇偶校验
        //c_iflag 控制串口数据输入 如剥离输入字符为8位 奇偶校验生效等
        options.c_iflag&=~(INPCK|ISTRIP); //禁用输入奇偶校验
        options.c_iflag|=IGNPAR; //忽略奇偶校验错误
        break;
    case 'O':
        options.c_cflag|=(PARENB|PARODD); //启用校验并设置为奇校验
        options.c_iflag|=(INPCK|ISTRIP); //启用奇偶检查并从接受字符串中脱去奇偶位
        options.c_iflag&=~IGNPAR; //不忽略奇偶校验错误
        break;
    case 'E':
        options.c_cflag|=PARENB; //启用奇偶校验
        options.c_cflag&=~PARODD; //设置为偶校验
        options.c_iflag|=(INPCK|ISTRIP); //启用检查并从中脱去奇偶校验位
        options.c_iflag&=~IGNPAR; //不忽略校验错误
        break;
    default:
        options.c_cflag&=~PARENB;
        options.c_iflag&=~(INPCK|ISTRIP);
        options.c_iflag|=IGNPAR;
        break;
    }
    switch(flow_ctrl){
    case 'N':
        options.c_cflag&=~CRTSCTS; //停用硬件流控制
        options.c_iflag&=~(IXON|IXOFF|IXANY);//停用软件控制流
        options.c_cflag|=CLOCAL; //不使用流控制
        break;
    case 'S':
        options.c_cflag&=~CRTSCTS; //停用硬件流控制
        options.c_iflag|=(IXON|IXOFF|IXANY);//使用软件流控制
        options.c_cflag&=~CLOCAL; //使用流控制
        break;
    case 'H':
        options.c_cflag|=CRTSCTS;
        options.c_iflag&=~(IXON|IXOFF|IXANY);//使用硬件流控制
        options.c_cflag&=~CLOCAL; //使用流控制
        break;
    default:
        options.c_cflag&=~CRTSCTS;
        options.c_iflag&=~(IXON|IXOFF|IXANY);
        options.c_cflag|=CLOCAL;
        break;
    }
    options.c_cflag|=CREAD; //启用接收器,能够从串口中读取数据
    options.c_iflag|=IGNBRK; //忽略输入行终止条件

    //c_lflag设置串口驱动与用户间界面
    options.c_lflag=0;//非加工输入,即显示原始数据
    options.c_oflag=0;//非加工输出
    //options.c_lflag &= ~(ICANON|ECHO|ECHOE|ISIG);
    //options.c_oflag &= ~OPOST;

    /*c_cc成员数组,索引宏常量
     * 还可以设置超时参数,通过VMIN和VTIME引用
     * 但只在非加工方式才有实际意义
     * 分别控制等待字节数和等待时长,单位0.1s*/
    //如果串口输入队列没有数据,将在read调用处阻塞
    options.c_cc[VMIN] = 1;
    options.c_cc[VTIME] = 0;

    if(tcsetattr(fd,TCSANOW,&options)==-1){
        printf("set error\n");  
        return -1;
    }
    tcflush(fd,TCIFLUSH); //刷新收到的数据,但是不读
    tcflush(fd,TCOFLUSH); //刷新写入的数据,但不传送
    return 0;
}
/*****************************************************
 * 名称:      COM_BufQueSize
 * 功能:      得到串口输入队列中的字节数
 * 传参:      fd
 *              buf_size 字节数会保存在该指针指向内存
 * 返回:      正确 0 错误 -1
 ****************************************************/
 int COM_BufQueSize(int fd,int* buf_size)
 {
    int byte_count=0;
    if(fd == -1)
        return -1;
    //使用ioctl系统调用来实现配置串口
    //任何IO操作都可以交给它
    //此处的FIONREAD为返回输入队列中的字节数
    if(ioctl(fd,FIONREAD,&byte_count)!=-1){
        *buf_size=byte_count;
        return 0;
    }
    return -1;
 }
/*****************************************************
 * 名称:      COM_recv
 * 功能:      接受串口数据
 * 传参:      fd
 *              recv_buf    读出串口数据存入recv_buf字符串
 *              data_len    一帧读取数据的长度
 * 返回:      正确 所读字节数    错误 -1
 * *****************************************************/
int COM_recv(int fd,char* recv_buf,int data_len)
{
    int rCount=0; //实际读到的字节数
    int recvBytes=0; //实际接受字节数,可能大于最大缓冲
    int QueSize=0; //输入队列字节数

    if(fd<0){
        perror("file description is valid\n");  
        return -1;
    }
    if(recv_buf==NULL){
        perror("read buf is NULL\n");   
        return -1;
    }
    if(data_len>MAX_BUF) //大于最大缓冲区的分开处理
        recvBytes=MAX_BUF;
    else 
        recvBytes=data_len;
    memset(recv_buf,'\0',recvBytes); //清空字符串
    if(COM_BufQueSize(fd,&QueSize)!=-1){ //获取输入队列字节数
        printf("Uart Queue have %d bytes\n",QueSize);   
        recvBytes=MIN(recvBytes,QueSize); //取其中的较小值作为接受字节数
    }
    if(!recvBytes) return -1;
    rCount=read(fd,recv_buf,recvBytes);
    if(rCount<0){
        perror("read error\n"); 
        return -1;
    }
    return rCount;
}
/*************************************************
 * 名称:      COM_transmit
 * 功能:      发送数据
 * 传参:      fd  
 *              mit_buf 将指向缓冲区数据写入串口
 *              data_len 写入的字节数长度   
 * 返回:      成功返回 实际写入字节数  失败 -1
 * **********************************************/
int COM_transmit(int fd,char* mit_buf,int data_len)
{
    int wCount=0;  //实际写入字节数长度  
    int mitBytes=data_len;
    if(fd<0){
        perror("file description is valid\n");  
        return -1;
    }
    if((mitBytes>MAX_BUF)||!mitBytes) return -1;
    wCount=write(fd,mit_buf,mitBytes);
    if(wCount<0){
        perror("write error\n");    
        return -1;
    }
    while(tcdrain(fd)==-1); //保证输出队列中所有数据被传送
    return wCount;
}
main.c
#include "COM_Linux.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define CMD_MAX_LEN 255

int main(int argc,char *argv[])
{
    char Read_Buf[MAX_BUF+1]; //存储读取数据
    char Write_Buf[MAX_BUF+1]; //存储发送数据
    char CMD[CMD_MAX_LEN+1]; //命令控制字符串
    int rCount=0;
    int wCount=0;
    int fd,com_port;
    if(argc<3){
        printf("please fill the parameter\n");  
        return -1;
    }
    if((fd=COM_open(argv[1]))==-1) return -1;   
    //if((fd=COM_open("/dev/ttyS1"))==-1) return -1;

    com_port=atoi(argv[2]); //将字符串转换为int整数
    if(COM_set(fd,com_port,8,1,'N','N')==-1){
        COM_close(fd);  
        return -1;
    }

    while(1){
        memset(CMD,'\0',CMD_MAX_LEN+1); //清空
        printf("Enter Command: \n");
        if(!fgets(CMD,CMD_MAX_LEN+1,stdin)){ //从stdin流,键盘获取输入
            perror("fget error\n"); 
            return -1;
        }
        /*fgets函数当遇到换行符和缓冲区已满,即会停止返回
         *所以在不填满缓冲区的正常情况下
          fgets获取的数据包括输入字符串加上回车换行符并在最后加上'\0'
          如输入hello后回车,得到的strlen长度应为hello的5个字符长度
          加一个\n回车以及最后的\0结束符,共7个
          但strlen函数不包括结束符号,所以只有共6个
          此处的CMD字符串应当将'\n'替换成'\0',便于后续比较str compare
          '\n'位于strlen长度最后一位,数组索引需减1*/
        //printf("111%c222%c333",CMD[strlen(CMD)],CMD[strlen(CMD)-1]);
        CMD[strlen(CMD)-1]='\0';
        if(strncmp(CMD,"quit",sizeof("quit"))==0) break; //退出命令 
        if(strncmp(CMD,"read",sizeof("read"))==0){
            memset(Read_Buf,'\0',MAX_BUF+1);    
            rCount=COM_recv(fd,Read_Buf,MAX_BUF);
            if(rCount>0){
                printf("ReadBuffer: %s\n",Read_Buf); //向屏幕打印获取数据
                printf("Read com char num: %d\n",rCount);
            }
        }
        if(strncmp(CMD,"write",sizeof("write"))==0){
            memset(Write_Buf,'\0',MAX_BUF+1);   
            strcpy(Write_Buf,"Hello");
            printf("Write_Buf:%s\n",Write_Buf);
            wCount=COM_transmit(fd,Write_Buf,strlen((char*)Write_Buf));
            sleep(1);
            memset(Read_Buf,'\0',MAX_BUF+1);
        }
    }
    COM_close(fd);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值