友善comtest.c串口编译

来源:互联网 发布:优化设计 英文 编辑:程序博客网 时间:2024/04/29 15:43

友善comtest.c串口编译程序详解 希望对大家有帮助


}# include <stdio.h>
# include <stdlib.h>
# include <termio.h>
# include <unistd.h>
# include <fcntl.h>
# include <getopt.h>
# include <time.h>
# include <errno.h>
# include <string.h>

/*
在命令行下输入:
#armcomtest –d /dev/ttySAC1 -o
这时如果输入字符会在另一台PC 的超级终端出现,反之亦然。
如果要测试串口 3,则需要连接扩展小板的COM3,并在命令行输入:
#armcomtest –d /dev/ttySAC2 -o
*/

static void Error(const char *Msg)
{
    fprintf (stderr, "%s\n", Msg);
    fprintf (stderr, "strerror() is %s\n", strerror(errno));
    exit(1);
}
static void Warning(const char *Msg)
{
     fprintf (stderr, "Warning: %s\n", 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;
}

static void PrintUsage(void)  //打印消息
{

   fprintf(stderr, "comtest - interactive program of comm port\n");
   fprintf(stderr, "press [ESC] 3 times to quit\n\n");

   fprintf(stderr, "Usage: comtest [-d device] [-t tty] [-s speed] [-7] [-c] [-x] [-o] [-h]\n");
   fprintf(stderr, "         -7 7 bit\n");
   fprintf(stderr, "         -x hex mode\n");
   fprintf(stderr, "         -o output to stdout too\n");
   fprintf(stderr, "         -c stdout output use color\n");
   fprintf(stderr, "         -h print this help\n");
   exit(-1);
}

static inline void WaitFdWriteable(int Fd)
/*
类似于一种宏定义
这种宏定义在形式上类似于一个函数,
但在使用它时,仅仅只是做预处理器符号表中的简单替换,
因此它不能进行参数有效性的检测,
也就不能享受C++编译器严格类型检查的好处,
另外它的返回值也不能被强制转换为可转换的合适的类型,
这样,它的使用就存在着一系列的隐患和局限性。
*/
{
    fd_set WriteSetFD;
    FD_ZERO(&WriteSetFD);
    FD_SET(Fd, &WriteSetFD);
    if (select(Fd + 1, NULL, &WriteSetFD, NULL, NULL) < 0) {
      Error(strerror(errno));
    } //select用来监控
    
}

int main(int argc, char **argv)
{
    int CommFd, TtyFd;

    struct termios TtyAttr;
    struct termios BackupTtyAttr;

    int DeviceSpeed = B115200;
    int TtySpeed = B115200;
    int ByteBits = CS8;
    const char *DeviceName = "/dev/ttyS0";
    const char *TtyName = "/dev/tty";
    int OutputHex = 0;
    int OutputToStdout = 0;
    int UseColor = 0;

    opterr = 0;
    for (;;) {
        int c = getopt(argc, argv, "d:s:t:7xoch");
        /*
        函数说明 getopt()用来分析命令行参数。
        参数argc和argv是由main()传递的参数个数和内容。
        参数optstring 则代表欲处理的选项字符串。
        此函数会返回在argv 中下一个的选项字母,
        此字母会对应参数optstring 中的字母。
        如果选项字符串里的字母后接着冒号“:”,
        则表示还有相关的参数,全域变量optarg 
        即会指向此额外参数。如果getopt()
        找不到符合的参数则会印出错信息,
        并将全域变量optopt设为“?”字符,
        如果不希望getopt()印出错信息,
        则只要将全域变量opterr设为0即可。        
        */
        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);
            break;
    case 'o':
        OutputToStdout = 1;
        break;
    case '7':
        ByteBits = CS7;
        break;
        case 'x':
            OutputHex = 1;
            break;
    case 'c':
        UseColor = 1;
        break;
        case '?':
        case 'h':
        default:
        PrintUsage();
        }
    }
    if (optind != argc)
        PrintUsage();//打印消息

    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");
    /*
    F_SETFL 设置文件描述词状态旗标,
    参数arg为新旗标,但只允许O_APPEND、
    O_NONBLOCK和O_ASYNC位的改变,其他位的改变将不受影响。
    
    O_NONBLOCK 如果路径名指向 FIFO/块文件/字符文件,则把文件的打开和后继 I/O
    */


    memset(&TtyAttr, 0, sizeof(struct termios));
    /*
    函数原型  void *memset(void *s, int ch, unsigned n);
    
    将s所指向的某一块内存中的每个字节的内容全部设置为ch指定的ASCII值,
    块的大小由第三个参数指定,这个函数通常为新申请的内存做初始化工作, 
    其返回值为指向S的指针。

    将TtyAttr 所指向的内存区域的值全部设置为0 的Ascii值
    

    */
    
    //设置串口的一些参数
    TtyAttr.c_iflag = IGNPAR;    //IGNPAR   忽略奇偶校验错误

    //c_iflag:输入模式标志,控制终端输入方式,具体参数如表6.3所示。

    TtyAttr.c_cflag = DeviceSpeed | HUPCL | ByteBits | CREAD | CLOCAL;
    
    //CREAD 使用接收器  HUPCL  关闭设备时挂起  CLOCAL  忽略调制解调器线路状态
    //c_cflag:控制模式标志,指定终端硬件控制信息,具体参数如表6.5所示。

    TtyAttr.c_cc[VMIN] = 1;  //VMIN 非规范模式读取时的最小字符数

    
    //c_cc[NCCS]:控制字符,用于保存终端驱动程序中的特殊字符,
    //如输入结束符等。c_cc中定义了如表6.7所示的控制字符。



    if (tcsetattr(CommFd, TCSANOW, &TtyAttr) < 0)
        Warning("Unable to set comm port");
    /*
    为了使程序控制终端参数,
    在标准库中POSIX需要几个函数,
    最重要的两个函数为tcsetattr和tcgetattr。
    tcgetattr取回一个如图3-34中显示的数据结构的一份拷贝,
    该结构为termios,它包含用来改变特殊字符、
    设置模式和修改终端其他特性的所有信息。
    程序可以检查当前的设置,
    当需要时对这些设置进行修改,
    然后调用tcsetattr把这个结构写回终端任务中。
    
    
    头文件
    <termios.h>

    <unistd.h>

    函数形式
    int tcgetattr(int fd, struct termios *termios_p);

    int tcsetattr(int fd, int optional_actions, const struct termios *termios_p);

    */


    TtyFd = open(TtyName, O_RDWR | O_NDELAY, 0);  //ndelay 自我解释应该是延时的意思
    if (TtyFd < 0)
    Error("Unable to open tty");

    TtyAttr.c_cflag = TtySpeed | HUPCL | ByteBits | CREAD | CLOCAL;
    if (tcgetattr(TtyFd, &BackupTtyAttr) < 0)
    Error("Unable to get tty");
    //BackupTtyAttr 用于存储获得的终端参数信息

    if (tcsetattr(TtyFd, TCSANOW, &TtyAttr) < 0)
    //使用tcsetattr函数将修改后的终端参数设置到标准输入中
    Error("Unable to set tty");


    for (;;) {
    unsigned char Char = 0;
    fd_set ReadSetFD;

    void OutputStdChar(FILE *File) 
    {
        char Buffer[10];
        int Len = sprintf(Buffer, OutputHex ? "%.2X  " : "%c", Char);
        fwrite(Buffer, 1, Len, File);
    }
/*
    int select(int maxfd,fd_set *rdset,fd_set *wrset,fd_set *exset,struct timeval *timeout); 
    参数maxfd是需要监视的最大的文件描述符值+1;rdset,wrset,exset分别对应于需要检测的可读文件描述符的集合,
    可写文件描述符的集 合及异常文件描述符的集合。struct timeval结构用于描述一段时间长度,如果在这个时间内,
    需要监视的描述符没有事件发生则函数返回,返回值为0。 
    fd_set(它比较重要所以先介绍一下)是一组文件描述字(fd)的集合,
    它用一位来表示一个fd(下面会仔细介绍),对于fd_set类型通过下面四个宏来操作: 
    FD_ZERO(fd_set *fdset);将指定的文件描述符集清空,
    在对文件描述符集合进行设置前,必须对其进行初始化,如果不清空,
    由于在系统分配内存空间后,通常并不作清空处理,所以结果是不可知的。 
    FD_SET(fd_set *fdset);用于在文件描述符集合中增加一个新的文件描述符。 
    FD_CLR(fd_set *fdset);用于在文件描述符集合中删除一个文件描述符。 
    FD_ISSET(int fd,fd_set *fdset);用于测试指定的文件描述符是否在该集合中。        
    过去,一个fd_set通常只能包含<32的fd(文件描述字),
    因为fd_set其实只用了一个32位矢量来表示fd;现在,
    UNIX系统通常会在头文件<sys/select.h>中定义常量FD_SETSIZE,
    它是数据类型fd_set的描述字数量,其值通常是1024,这样就能表示<1024的fd。
    根据fd_set的位矢量实现,我们可以重新理解操作fd_set的四个宏: 
    fd_set set;
    FD_ZERO(&set);     
    FD_SET(0, &set);   
    FD_CLR(4, &set);     
    FD_ISSET(5, &set);   
*/
    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)) {
    /*
    int FD_ISSET(int fd,fd_set *fdset)   
    宏说明:在调用select()函数后,用FD_ISSET来检测fdset中文件fd有无发生变化 
    */
        while (read(CommFd, &Char, 1) == 1) {

        WaitFdWriteable(TtyFd);
        //监控TtyFd这个文件描述符中是否有数据发生变化
        //这个函数在上面有定义
        
        // ssize_t write (int fd,const void * buf,size_t count);
        //write()会把参数buf所指的内存写入count个字节到参数fd所指的文件内。
        //当然,文件读写位置也会随之移动。
        if (write(TtyFd, &Char, 1) < 0) {
              Error(strerror(errno));
        }
        //如果用到了-O的属性  OutputTostdout 就会变成1就会输出下面的信息
        if (OutputToStdout) {
            if (UseColor)
            fwrite("\x1b[01;34m", 1, 8, stdout);
            OutputStdChar(stdout);
            if (UseColor)
            fwrite("\x1b[00m", 1, 8, stdout);
            fflush(stdout);
        }
        }
    }

    if (FD_ISSET(TtyFd, &ReadSetFD)) {
        while (read(TtyFd, &Char, 1) == 1) {
               static int EscKeyCount = 0;
        WaitFdWriteable(CommFd);
               if (write(CommFd, &Char, 1) < 0) {
              Error(strerror(errno));
        }
        if (OutputToStdout) {
            if (UseColor)
            fwrite("\x1b[01;31m", 1, 8, stderr);
            OutputStdChar(stderr);
            if (UseColor)
            fwrite("\x1b[00m", 1, 8, stderr);
            fflush(stderr);
            }

              if (Char == '\x1b') {
                    EscKeyCount ++;
                    if (EscKeyCount >= 3)
                        goto ExitLabel;
                       // 跳出到退出label
                } else
                    EscKeyCount = 0;
        } 
        }

    }

ExitLabel:
    if (tcsetattr(TtyFd, TCSANOW, &BackupTtyAttr) < 0)
    Error("Unable to set tty");

    return 0;
原创粉丝点击