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;
    }
  • 相关阅读:
    图片延迟加载方法
    mongodb常用命令
    未知尺寸元素水平垂直居中:
    nodejs学习之加密
    nodejs学习之events的使用
    nodejs学习之events
    学习Nodejs之mysql
    PHP之几道面试题
    Jquery学习插件之手风琴
    我的第一篇博客
  • 原文地址:https://www.cnblogs.com/kissazi2/p/3203242.html
Copyright © 2011-2022 走看看