C51单片机应用与C语言程序设计
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

任务3 用函数调用简化运动程序

在后面讲节中,机器人将需要执行各种运动来避开障碍物和完成其他动作。不过,无论机器人要执行何种动作,都离不开前面讨论的各种基本动作。为了各种应用程序方便使用这些基本动作程序,可以将这些基本动作放在函数中,供其他函数调用来简化程序。

C语言提供了强大的函数定义功能。在本书第1讲中已经介绍过,一个C程序就是由一个主函数和若干个其他函数构成,由主函数调用其他函数,其他函数也可以相互调用。同一个函数可以被一个或多个函数调用任意多次。

实际上,为了实现复杂的程序设计,在所有的计算机高级语言中都有子程序或者子过程的概念。在C语言程序中,子程序的作用就是由函数来完成的。

从函数定义的角度来看,函数有两种

(1)标准函数,即库函数。由开发系统提供,用户不必自己定义而直接使用,只需在程序前包含有该函数原型的头文件即可在程序中直接调用,例如前面已经用到的串口标准输入(printf)和输出(scanf)函数。应该说明,不同的语言编译系统提供的库函数的数量和功能会有一些不同,但许多基本函数是共同的。

(2)用户定义函数,以解决你的专门需要。不仅要在程序中定义函数本身,而且在主调函数模块中还必须对该被调函数进行类型说明,然后才能使用。

从有无返回值角度来看,函数又分为以下两种

(1)有返回值函数。函数被调用执行完后将向调用者返回一个执行结果,称为函数返回值。由用户定义的返回函数值的函数,必须在函数定义中明确返回值的类型。

(2)无返回值函数。此类函数用于完成某项特定的处理任务,执行完成后不向调用者返回函数值。用户在定义此类函数时可指定它的返回为“空类型”,即“void”。

从主调函数和被调函数之间数据传送的角度看,函数也可分为两种

(1)无参函数。函数定义、说明及调用中均不带参数,主调函数和被调函数之间不进行参数传送。此类函数通常用来完成一组指定的功能,可以返回或不返回函数值。

(2)有参函数。在函数定义及说明时都有参数,称为形式参数(简称为形参)。在函数调用时也必须给出参数,称为实际参数(简称为实参)。进行函数调用时,主调函数将把实参的值传送给形参,供被调函数使用。

第1讲就已经给出了函数定义的一般形式:

    类型标志符 函数名(形式参数列表)
    {
        声明部分
        语句
    }

其中类型标志符和函数名称为函数头。类型标志符指明了本函数的类型,函数的类型实际上是函数返回值的类型。函数名是由用户定义的标志符,函数名后有一个括号(不可少写)。若函数无参数,则括号内可不写内容或写“void”;若有参数,则形式参数列表给出各种类型的变量,各参数之间用逗号间隔。

{}中的内容称为函数体。函数体中的声明部分,是对函数体内部用到的变量的类型说明。在很多情况下都不要求函数有返回值,此时函数类型符可以写为void。

main函数的返回值

前面说过,main函数是不能被其他函数调用的,那它的返回值类型int是怎么回事呢?

其实不难理解,main函数执行完后,它的返回值是给操作系统的。虽然在main函数体内并没有什么语句来指出返回值的大小,但系统默认的处理方式是:当main函数成功执行时,它的返回值为1;否则为0。

现在看看下面的函数定义。

    void Forward(void)
    {
        int i;
        for(i=1;i<=65;i++)
        {
          P1_1=1;
          delay_nus(1700);
          P1_1=0;
          P1_0=1;
          delay_nus(1300);
          P1_0=0;
          delay_nms(20);
        }
    }

Forward函数可以使机器人向前运动约1.5s,该函数没有形式参数,也没有返回值。在主程序中,可以调用它来让你的机器人向前运动约1.5s。但是这个函数并没有太大的使用价值,如果想让你的机器人向前运动2s,该怎么办呢?是重新写一个函数来实现这个运动吗?当然不是!通过修改上面的函数,给它增加两个形式参数,一个是脉冲数量,另一个是速度参数。这样主程序调用时就可以按照你的要求灵活设置这些参数,从而使函数真正成为一个有用的模块。重新定义向前运动函数如下:

    void Forward(int PulseCount,int Velocity)
    /*Velocity should be between 0 and 200 */
    {
        int i;
        for(i=1;i<=PulseCount;i++)
        {
          P1_1=1;
          delay_nus(1500+Velocity);
          P1_1=0;
          P1_0=1;
          delay_nus(1500-Velocity);
          P1_0=0;
          delay_nms(20);
        }
    }

函数定义下方,增加了一行注释,提醒你在调用该函数时,速度参量的值必须在0~200之间。

注释符

除“//”外,C语言还提供了另一种语句注释符——“/*”和“*/”。

“/*”和“*/”必须成对使用,在它们之间的内容将被注释掉。它的作用范围比“//”大:“//”仅仅对它所在的一行起注释作用;但“/*…*/”可以对多行注释。

注释是你在学习程序设计时要养成的良好习惯。

下面是一个完整的使用向前、左转、右转和向后四个函数的例程。

例程:MovementsWithFunctions.c

输入、保存、编译、下载并运行程序MovementsWithFunctions.c。

    #include<BoeBot.h>
    #include<uart.h>
    void Forward(int PulseCount,int Velocity)
    /*Velocity should be between 0 and 200 */
    {
        int i;
        for(i=1;i<=PulseCount;i++)
        {
            P1_1=1;
            delay_nus(1500+Velocity);
            P1_1=0;
            P1_0=1;
            delay_nus(1500-Velocity);
            P1_0=0;
            delay_nms(20);
        }
    }
    void Left(int PulseCount,int Velocity)
    /*Velocity should be between 0 and 200 */
    {
        int i;
        for(i=1;i<=PulseCount;i++)
        {
            P1_1=1;
            delay_nus(1500-Velocity);
            P1_1=0;
            P1_0=1;
            delay_nus(1500-Velocity);
            P1_0=0;
            delay_nms(20);
        }
    }
    void Right(int PulseCount,int Velocity)
    /*Velocity should be between 0 and 200 */
    {
        int i;
        for(i=1;i<=PulseCount;i++)
        {
            P1_1=1;
            delay_nus(1500+Velocity);
            P1_1=0;
            P1_0=1;
            delay_nus(1500+Velocity);
            P1_0=0;
            delay_nms(20);
        }
    }
    void Backward(int PulseCount,int Velocity)
    /*Velocity should be between 0 and 200 */
    {
        int i;
        for(i=1;i<=PulseCount;i++)
        {
            P1_1=1;
            delay_nus(1500-Velocity);
            P1_1=0;
            P1_0=1;
            delay_nus(1500+Velocity);
            P1_0=0;
            delay_nms(20);
        }
    }
    int main(void)
    {
        uart_Init();
        printf("Program Running!\n");
        Forward(65,200);
        Left(26,200);
        Right(26,200);
        Backward(65,200);
        while(1);
    }

这个程序的运行结果与程序ForwardLeftRightBackward.c产生的效果是相同的。很明显,还有许多方法可以构造一个程序而得到同样的结果。实际上,四个函数的具体实现部分几乎完全一样,有没有可能将这些函数进行归纳,用一个函数来实现所有这些功能呢?当然有,前面的四个函数都用了两个形式参数,一个是控制时间的脉冲个数,另一个是控制运动速度的参数,而四个函数实际上代表了四个不同的运动方向。如果能够通过参数控制运动方向,显然这四个函数就完全可以简化成为一个更为通用的函数,它不仅可以涵盖以上四个基本运动,同时还可以使机器人朝你希望的方向运动。

由于机器人由两个轮子驱动,实际上两个轮子的不同速度组合控制着机器人的运动速度和方向,因此可以直接用两个车轮的速度作为形式参数,就可以将所有的机器人运动用一个函数来实现。

例程:MovementsWithOneFuntion.c

这个例子使你的机器人做同样动作,但是它只用了一个子函数来实现。

    #include<BoeBot.h>
    #include<uart.h>
    void Move(int counter,int PC1_pulseWide,int PC0_pulseWide)
    {
        int i;
        for(i=1;i<=counter;i++)
        {
            P1_1=1;
            delay_nus(PC1_pulseWide);
            P1_1=0;
            P1_0=1;
            delay_nus(PC0_pulseWide);
            P1_0=0;
            delay_nms(20);
        }
    }
    int main(void)
    {
      uart_Init();
        printf("Program Running!\n");
        Move(65,1700,1300);
        Move(26,1300,1300);
        Move(26,1700,1700);
        Move(65,1300,1700);
        while(1);
    }

● 输入、保存并运行程序MovementsWithOneFuntion.c;

● 你的机器人是否执行了前、左、右、后运动呢?

● 修改MovementsWithOneFuntion.c,使机器人走一个正方形。第一边和第二边向前走,另外两个边向后走。