zoukankan      html  css  js  c++  java
  • 带有详细注释的串口测试程序

    以下都是从友善之臂《04- Tiny6410 Linux开发指南-20111020》复制出来的,我所做的工作就是将友善之臂提供的源程序进行详细注释,另外将一些大函数分解成小函数。这段代码不长,但是涉及到多个比较不容易接触的C语言知识点。

    说明:armcomtest 是友善之臂为了方便测试而开发的linux 下的简易实用串口终端程
    序,它使用标准的系统调用,和硬件无关。一般Linux 系统系统启动后,串口 0,1,2对应的设
    备名分别为/dev/ttySAC0,1,2,3 
     
    测试串口2 需要借助另一台带有串口的PC,使用我们提供的串口线和扩展小板( 选购
    配件
    ) ,连接好 COM2 和另一台PC的串口,并如前所述设置该PC的超级终端为波特率115200 ,
    无流控制,其他默认。
    在命令行下输入:
    #armcomtest  –d /dev/ttySAC1 - o 
    这时如果输入字符会在另一台PC的超级终端出现,反之亦然。
    如果要测试串口3,则需要连接扩展小板的COM3 ,并在命令行输入:
    #armcomtest  –d /dev/ttySAC2  - o 
    下面是测试时的界面:

    image

    /***************************************************************************
    ** 文件: comtest.c
    ** 描述:
    **     以串口通讯的测试程序
    **
    **------------------------------------------------------------------------------------------------------
    ********************************************************************************************************/
    
    
    
    #ifdef HAVE_CONFIG_H
    #include <config.h>
    #endif
    
    #include <stdio.h> /*标准输入输出定义*/
    #include <stdlib.h> /*标准函数库定义*/
    #include <termio.h> /*PPSIX 终端控制定义*/
    #include <unistd.h>/*Unix 标准函数定义,使用exit()*/
    #include <fcntl.h>/*文件控制定义*/
    #include <getopt.h>/*参数提取定义*/
    #include <time.h>
    #include <errno.h>/*错误号定义*/
    #include <string.h>
    #include <assert.h>
    
    
    int OutputHex = 0;//是否以十六进制发送。OutputHex为1时,以十六进制发送;为0,以字符串方式发送
    int OutputToStdout = 0;//是否将消息同样发送一份到标准输出。为1时,发送;为0,不发送
    int UseColor = 0;       //是否使用颜色。为1时,使用颜色;为0,不使用颜色。
    struct termios BackupTtyAttr;//终端属性的备份
    int IsWrite=0;
    
    static void Error(const char *Msg)
    {
        fprintf (stderr, "%s
    ", Msg);
        fprintf (stderr, "strerror() is %s
    ", strerror(errno));
        exit(EXIT_FAILURE);
    }
    static void Warning(const char *Msg)
    {
         fprintf (stderr, "Warning: %s
    ", Msg);
    }
    
    static int SerialSpeed(const char *SpeedString)
    {
        int SpeedNumber = atoi(SpeedString);
    #   define TestSpeed(Speed) if (SpeedNumber == Speed) return B##Speed
        TestSpeed(1200);
        TestSpeed(2400);
        TestSpeed(4800);
        TestSpeed(9600);
        TestSpeed(19200);
        TestSpeed(38400);
        TestSpeed(57600);
        TestSpeed(115200);
        TestSpeed(230400);
        Error("Bad speed");
        return -1;
    }
    
    /**
    *@brief  打印错误信息
    */
    static void PrintUsage(void)
    {
       fprintf(stderr, "comtest - interactive program of comm port
    ");
       fprintf(stderr, "press [ESC] 3 times to quit
    
    ");
       fprintf(stderr, "Usage: comtest [-d device] [-t tty] [-s speed] [-7] [-c] [-x] [-o] [-h]
    ");
       fprintf(stderr, "         -7 7 bit
    ");
       fprintf(stderr, "         -x hex mode
    ");
       fprintf(stderr, "         -o output to stdout too
    ");
       fprintf(stderr, "         -c stdout output use color
    ");
       fprintf(stderr, "         -h print this help
    ");
       exit(-1);
    }
    
    /*******************************************************************************************************
    ** 函数: WaitFdWriteable,  等待文件可写
    **------------------------------------------------------------------------------------------------------
    ** 参数: Fd 文件描述符
    ** 返回: void
    ** 函数说明:使用inline标识符,防止因为函数频繁的调用占用大量的栈空间
    ** 日期: 2013.06.19
    ********************************************************************************************************/
    static inline void WaitFdWriteable(int Fd)
    {
        fd_set WriteSetFD;    //定义可写的设备集合
        FD_ZERO(&WriteSetFD);//将可写的设备集合清空
        FD_SET(Fd, &WriteSetFD);//将fd添加到可写的设备集合中
        //select函数原型:    int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);
        //值得说明的是:int maxfdp是一个整数值,是指需要测试的文件描述符的数目,测试的描述符范围从0到nfds-1.即所有文件描述符的最大值加1,不能错!
        
        if (select(Fd + 1, NULL, &WriteSetFD, NULL, NULL) < 0) {//判断是否有可写的设备,如果没有就一直阻塞,这里没有设置超时,如果没有可写的,会一直阻塞下去
        //select函数中readfds、errorfds描述符集合都为空,表示不进行测试
            Error(strerror(errno));
        }
    }
    void setAttrByArgvs(){
    
    }
    /*******************************************************************************************************
    ** 函数: OutputStdChar,  打开一个串口
    **------------------------------------------------------------------------------------------------------
    ** 参数: FILE *File 文件描述符
            int OutputHex,是否以十六进制发送。OutputHex为1时,以十六进制发送;为0,以字符串方式发送
            unsigned char aCharToSend将要发送的字符
    ** 返回: void
    **
    ** 日期: 2013.06.19
    ********************************************************************************************************/
    void OutputStdChar(FILE *File,int OutputHex,unsigned char aCharToSend) {//向设备写数据
         char Buffer[10];
         int Len = sprintf(Buffer, OutputHex ? "%.2X  " : "%c", aCharToSend);//%.2X表示输出01,02样式的十六进制数
              // int Len = sprintf(Buffer,  0x01);//%.2X表示输出01,02样式的十六进制数
         fwrite(Buffer, 1, Len, File);
    }
    
    /*******************************************************************************************************
    ** 函数: SetFD,  设置文件描述符
    **------------------------------------------------------------------------------------------------------
    ** 参数:int argc 
            char **argv
            int * CommFd 串口文件描述符
            int * TtyFd 终端文件描述符
    ** 返回: void
    **函数说明:
    **本函数通过对main函数的argc、argv进行参数的提取,实现对串口文件描述符(CommFd)和终   端文件描述符TtyFd的设置
    ** 日期: 2013.06.19
    ********************************************************************************************************/
    void SetFD(int argc, char **argv,int * CommFd,int * TtyFd){
        struct termios TtyAttr;         //终端属性
        int DeviceSpeed = B2400;     //串口波特率
        int TtySpeed = B2400;         //终端波特率
        int ByteBits = CS8;             //数据位:8位
        const char *DeviceName = "/dev/ttySAC1";//串口设备名
        const char *TtyName = "/dev/tty";        //终端设备名,防止重要的信息被用户重定向
        opterr = 0;
        printf("init........
    ");
        //通过Argc和Argv设置必要的参数
        for (;;) {
            int c = getopt(argc, argv, "d:s:t:7xoch");//利用getopt将argv参数一个个提取出来
            if (c == -1)
                break;
            switch(c) {
            case 'd'://设置串口的名称
                DeviceName = optarg;
                break;
            case 't'://设置终端的名称
                TtyName = optarg;
                break;
            case 's'://设置比特率
                if (optarg[0] == 'd') {//设置串口的波特率
                    DeviceSpeed = SerialSpeed(optarg + 1);
                } else if (optarg[0] == 't') {//设置终端的波特率
                    TtySpeed = SerialSpeed(optarg + 1);
                 } else
                    TtySpeed = DeviceSpeed = SerialSpeed(optarg);//如果没有带d或t,直接将两个设备的波特率设置成相同的
                break;
            case 'o'://设置同时将消息向标准输出(stdout)输出
                OutputToStdout = 1;
            break;
            case '7'://设置数据位为7位
                ByteBits = CS7;
             break;
            case 'x':
                OutputHex = 1;//以十六进制输出 
                printf("OutputHex = 1
    ");
                break;
            case 'c'://使用颜色标记
                 UseColor = 1;
                 break;
                    case '?':
                    case 'h':
                    default:
                    PrintUsage();//输出main参数的说明
            }
        }//end of for(;;)
        
        printf("end of getopt
    ");
        if (optind != argc)//判断参数是否符合要求
            PrintUsage();  //输出main参数的说明
        *CommFd = open(DeviceName, O_RDWR, 0);//以读写的方式打开 
        if (*CommFd < 0)
            Error("Unable to open device");//不能打开设备
        if (fcntl(*CommFd, F_SETFL, O_NONBLOCK) < 0)//设置文件访问模式为非阻塞
            Error("Unable set to NONBLOCK mode");  //不能使用NONBLOCK模式
    
        memset(&TtyAttr, 0, sizeof(struct termios));
        TtyAttr.c_iflag = IGNPAR;//忽略输入行中的中止状态
        TtyAttr.c_cflag = DeviceSpeed | HUPCL | ByteBits | CREAD | CLOCAL;//DeviceSpeed:
        //HUPCL:关闭时挂断调制解调器;CREAD:启用字符接收器;CLOCAL:忽略所有调制解调器的状态行
        TtyAttr.c_cc[VMIN] = 1;//设置MIN值,read调用将一直等待,直到有MIN个字符可读的时候才返回,返回是读取的字符数量。到达文件结尾的时候返回0
        if (tcsetattr(*CommFd, TCSANOW, &TtyAttr) < 0)//立即对属性进行修改,不等当前输出完成
            Warning("Unable to set comm port");
        *TtyFd = open(TtyName, O_RDWR | O_NDELAY, 0);//只有在CTEAT模式下,才需要第三个参数,这边的第三个参数是没有作用的。
        if (*TtyFd < 0)
            Error("Unable to open tty");
        TtyAttr.c_cflag = TtySpeed | HUPCL | ByteBits | CREAD | CLOCAL;
        if (tcgetattr(*TtyFd, &BackupTtyAttr) < 0)//将当前Tty的属性备份在BackupTtyAttr,以便在程序退出时还原Tty的设置
            Error("Unable to get tty");
        if (tcsetattr(*TtyFd, TCSANOW, &TtyAttr) < 0)
            Error("Unable to set tty");
    }
    /*******************************************************************************************************
    ** 函数: OutputCharUseColor 输出带颜色的字符
    **------------------------------------------------------------------------------------------------------
    ** 参数:char * colorCode 颜色代码
            unsigned char aChar 要输出的字符
    ** 返回: void
    ** 日期: 2013.06.19
    ********************************************************************************************************/
    void OutputCharUseColor(char * colorCode,unsigned char aChar){
        if (OutputToStdout) {    //同时向标准输出写消息
            if (UseColor)
                fwrite(colorCode, 1, 8, stdout);
            OutputStdChar(stdout,OutputHex,aChar);
            if (UseColor)
                fwrite("x1b[00m", 1, 8, stdout);
            fflush(stdout);//将stdout缓冲区中的数据立即输出,即在屏幕上显示
        }
    }
    int uart_pthread(int argc, char **argv)
    {
        printf("into main
    ");
        int SendBufferIndex=0;        
        char * SendBuffer=(char *)malloc(sizeof(char)*20);//发送缓存
        /**
        TtyFD是为了防止与用户交互的信息被重定向,而没有在屏幕上显示。使用TtyFd可以直接将
        不想被重定向的信息直接向用户终端(屏幕)输出。
        **/
        int CommFd, TtyFd;             //串口、终端描述符
        SetFD( argc, argv,&CommFd,&TtyFd);
        for (;;) {
            unsigned char aCharToSend = 0;
            fd_set ReadSetFD;        //可读设备集合
    
            FD_ZERO(&ReadSetFD);    //清空可读设备集合
            FD_SET(CommFd, &ReadSetFD);//将串口加入可读设备集合
            FD_SET( TtyFd, &ReadSetFD);//将终端加入可读设备集合
            # define max(x,y) ( ((x) >= (y)) ? (x) : (y) )//最大值函数,返回两个数中较大的数
            if (select(max(CommFd, TtyFd) + 1, &ReadSetFD, NULL, NULL, NULL) < 0) {//同时测试串口和终端是否可读
                 Error(strerror(errno));
            }
            # undef max
            if (FD_ISSET(CommFd, &ReadSetFD)) {//判断串口是否可读
                 while (read(CommFd, &aCharToSend, 1) == 1) {//从串口中读取一个char型,放在aCharToSend
                    WaitFdWriteable(TtyFd);//会一直阻塞在这里,直到终端设备可写
                    if (write(TtyFd, &aCharToSend, 1) < 0) {//向屏幕输出收到的字符
                        Error(strerror(errno));                //如果写入错误,输出错误信息
                    }
                    OutputCharUseColor("x1b[01;34m",aCharToSend);
                 }
            }
            // printf("------INTO    if (FD_ISSET(TtyFd, &ReadSetFD)) ");
    
            if (FD_ISSET(TtyFd, &ReadSetFD)) {               //判断终端是否可读
            
    
                while (read(TtyFd, &aCharToSend, 1) == 1) {//从终端中读取一个值
                        // fprintf(stderr,"
    ------SendBufferIndex:%d ",SendBufferIndex);
                        SendBuffer[SendBufferIndex++]=aCharToSend;
                        // fprintf(stderr, "
    Read From Tty");
    
                        static int EscKeyCount = 0;            //按下Esc的次数
                        OutputCharUseColor("x1b[01;31m",aCharToSend);
    
                        if (aCharToSend == '
    ') {//监测是否按下回车
                            SendBuffer[SendBufferIndex-1]='';
                            IsWrite=1;
                             fprintf(stderr, "x1b[01;34m you have enter :%s
     x1b[00m",SendBuffer);
                            // fprintf(stderr,"
    ---11---IsWrite==%d ",IsWrite);
                        }
                        if(SendBufferIndex==19){        
                            SendBuffer[SendBufferIndex]='';
                            // fprintf(stderr, "you have enter :%s
    ",SendBuffer);
                            IsWrite=1;
                        }
                        if(IsWrite==1){
                            // fprintf(stderr,"
    ------WaitFdWriteable ");
                            WaitFdWriteable(CommFd);
                                 if (write(CommFd, SendBuffer, SendBufferIndex) < 0) {
                                    Error(strerror(errno));
                            }
                            IsWrite=0;
                            SendBufferIndex=0;
                        }
                        if (aCharToSend == 'x1b') {//监测是否按下Esc
                            fprintf(stderr, "EscKeyPressedx1b
    ");
                            EscKeyCount ++;
                            if (EscKeyCount >= 3)
                            goto ExitLabel;
                        }else{
                            EscKeyCount = 0;
                        }
                        // fprintf(stderr,"
    ---22---IsWrite==%d ",IsWrite);
                } 
                
            }
        }//end of for (;;)
    ExitLabel:
        free(SendBuffer);
        if (tcsetattr(TtyFd, TCSANOW, &BackupTtyAttr) < 0)//恢复之前终端的设置
            Error("Unable to set tty");
        return 0;
    }
  • 相关阅读:
    Linux IO接口 监控 (iostat)
    linux 防火墙 命令
    _CommandPtr 添加参数 0xC0000005: Access violation writing location 0xcccccccc 错误
    Visual Studio自动关闭
    Linux vsftpd 安装 配置
    linux 挂载外部存储设备 (mount)
    myeclipse 9.0 激活 for win7 redhat mac 亲测
    英文操作系统 Myeclipse Console 乱码问题
    Linux 基本操作命令
    linux 查看系统相关 命令
  • 原文地址:https://www.cnblogs.com/kissazi2/p/3203242.html
Copyright © 2011-2022 走看看