分享

【单片机C语言基础入门】第六章:函数的入门与编写

 乐山峨眉 2016-11-22


一般来说,一个单片机程序是有很多的部分构成每一个模块来实现某一个特定的功能,比如AD转换程序模块、timer定时器程序模块、PWM程序模块等。单片机开发过程中我们将这些程序模块用函数的方法来实现。这样一个单片机程序就可以由一个main函数和若干和函数模块来构成,同一个函数模块可以被一个或者多个函数调用任意多次。基于这种思想很多的单片机厂家设计了单片机的固件库文件(其库文件是和硬件的资源相配套的,比如timer/counter模块、PWM模块、UART模块等)来驱动设备的内部和外设资源。


下图为STC15标准库文件的函数库的函数和文件。



每个文件都以函数的形式保存为文件,比如GPIO的文件打开如下:


  1. #include  'GPIO.h'

  2. // 函数: u8    GPIO_Inilize(u8GPIO, GPIO_InitTypeDef *GPIOx)

  3. // 描述: 初始化IO口.

  4. // 参数: GPIOx: 结构参数,请参考timer.h里的定义.

  5. // 返回: 成功返回0, 空操作返回1,错误返回2.

  6. // 版本: V1.0, 2012-10-22

  7. //========================================================================

  8. u8   GPIO_Inilize(u8GPIO, GPIO_InitTypeDef *GPIOx)

  9. {

  10.        if(GPIO> GPIO_P5)                       return1; //空操作

  11.        if(GPIOx->Mode> GPIO_OUT_PP)     return 2; //错误

  12.        if(GPIO ==GPIO_P0)

  13.        {

  14.               if(GPIOx->Mode== GPIO_PullUp)              P0M1 &= ~GPIOx->Pin,  P0M0&= ~GPIOx->Pin;   //上拉准双向口

  15.               if(GPIOx->Mode== GPIO_HighZ)        P0M1 |= GPIOx->Pin,    P0M0 &= ~GPIOx->Pin;   //浮空输入

  16.               if(GPIOx->Mode== GPIO_OUT_OD)        P0M1 |= GPIOx->Pin,    P0M0 |= GPIOx->Pin;     //开漏输出

  17.               if(GPIOx->Mode== GPIO_OUT_PP)          P0M1 &= ~GPIOx->Pin,  P0M0|=  GPIOx->Pin;     //推挽输出

  18.        }

  19.        if(GPIO ==GPIO_P1)

  20.        {

  21.               if(GPIOx->Mode== GPIO_PullUp)              P1M1 &= ~GPIOx->Pin,  P1M0&= ~GPIOx->Pin;   //上拉准双向口

  22.               if(GPIOx->Mode== GPIO_HighZ)        P1M1 |= GPIOx->Pin,    P1M0 &= ~GPIOx->Pin;   //浮空输入

  23.               if(GPIOx->Mode== GPIO_OUT_OD)        P1M1 |= GPIOx->Pin,    P1M0 |= GPIOx->Pin;     //开漏输出

  24.               if(GPIOx->Mode== GPIO_OUT_PP)          P1M1 &= ~GPIOx->Pin,  P1M0|=  GPIOx->Pin;     //推挽输出

  25.        }

  26.        if(GPIO ==GPIO_P2)

  27.        {

  28.               if(GPIOx->Mode== GPIO_PullUp)              P2M1 &= ~GPIOx->Pin,  P2M0&= ~GPIOx->Pin;   //上拉准双向口

  29.               if(GPIOx->Mode== GPIO_HighZ)        P2M1 |= GPIOx->Pin,    P2M0 &= ~GPIOx->Pin;   //浮空输入

  30.               if(GPIOx->Mode== GPIO_OUT_OD)        P2M1 |= GPIOx->Pin,    P2M0 |= GPIOx->Pin;     //开漏输出

  31.               if(GPIOx->Mode== GPIO_OUT_PP)          P2M1 &= ~GPIOx->Pin,  P2M0|=  GPIOx->Pin;     //推挽输出

  32.        }

  33.        if(GPIO ==GPIO_P3)

  34.        {

  35.               if(GPIOx->Mode== GPIO_PullUp)              P3M1 &= ~GPIOx->Pin,  P3M0&= ~GPIOx->Pin;   //上拉准双向口

  36.               if(GPIOx->Mode== GPIO_HighZ)        P3M1 |= GPIOx->Pin,    P3M0 &= ~GPIOx->Pin;   //浮空输入

  37.               if(GPIOx->Mode== GPIO_OUT_OD)        P3M1 |= GPIOx->Pin,    P3M0 |= GPIOx->Pin;     //开漏输出

  38.               if(GPIOx->Mode== GPIO_OUT_PP)          P3M1 &= ~GPIOx->Pin,  P3M0|=  GPIOx->Pin;     //推挽输出

  39.        }

  40.        if(GPIO ==GPIO_P4)

  41.        {

  42.               if(GPIOx->Mode== GPIO_PullUp)              P4M1 &= ~GPIOx->Pin,  P4M0&= ~GPIOx->Pin;   //上拉准双向口

  43.               if(GPIOx->Mode== GPIO_HighZ)        P4M1 |= GPIOx->Pin,    P4M0 &= ~GPIOx->Pin;   //浮空输入

  44.               if(GPIOx->Mode== GPIO_OUT_OD)        P4M1 |= GPIOx->Pin,    P4M0 |= GPIOx->Pin;     //开漏输出

  45.               if(GPIOx->Mode== GPIO_OUT_PP)          P4M1 &= ~GPIOx->Pin,  P4M0|=  GPIOx->Pin;     //推挽输出

  46.        }

  47.        if(GPIO ==GPIO_P5)

  48.        {

  49.               if(GPIOx->Mode== GPIO_PullUp)              P5M1 &= ~GPIOx->Pin,  P5M0&= ~GPIOx->Pin;   //上拉准双向口

  50.               if(GPIOx->Mode== GPIO_HighZ)        P5M1 |= GPIOx->Pin,    P5M0 &= ~GPIOx->Pin;   //浮空输入

  51.               if(GPIOx->Mode== GPIO_OUT_OD)        P5M1 |= GPIOx->Pin,    P5M0 |= GPIOx->Pin;     //开漏输出

  52.               if(GPIOx->Mode== GPIO_OUT_PP)          P5M1 &= ~GPIOx->Pin,  P5M0|=  GPIOx->Pin;     //推挽输出

  53.        }

  54.        return 0; //成功

  55. }


显然这是一个GPIO初始化的程序。由此可以看出,运用函数的方法可以有利于维护程序,也使得开发变得更模块化,在介绍函数之前我们先介绍一下程序的组成以及函数与文件的关系。


(1)    一个单片机程序由一个或者多个程序模块组成,每一个程序模块可以作为一个程序源文件。

(2)    一个程序源文件可以由一个或者多个函数构成。

(3)    单片机C程序和只能有一和main函数,并且执行是从main函数开始的。

(4)    函数可以相互调用。


函数可以分为有参数函数和无参数函数。有参数传递函数是指在调用函数的时候,通过参数相被调用的函数传递运算的参数;而无参函数一般指的是参数执行特性的操作(比如对硬件的初始化等)。


无参函数的一般定义如下:


类型标识符 函数名()

{

声明部分;

语句部分;

}

有参函数的定义一般如下:

类型标识符 函数名(形式参数列表)

{

声明部分;

语句部分;

}


其中形式参数列表中的参数可以是数字变量、字符变量、数组变量、结构体变量等。


在调用有参函数的时候,主函数要将传递的参数转到函数的形式参数中,其中主函数的参数就做实际参数,而函数中的变量(函数名后面跟的参数)属于形式变量,当函数讲将数据运算结束后,会释放函数中用到的变量单元,最后控制硬件操作或者通过传递参数的形式将运算后的结果传递给主函数。


函数的返回值用return来完成运算后参数的传递,同样rerun的出现位置也很重要,如果执行了return程序,其return后面的程序将不再运行。和break、continue的区别就在于使用的地方不同,一个是函数中,而另外两个是应用到循环体中。


函数的调用比较方便,直接是函数名来实现函数的调用。


注意在单片机C语言中,如果先声明函数那么函数可以写在任何的位置,如果事先没有声明的话,函数只能写在main()函数的前面,否则会报出错误和警告。


如果将函数作为一个整体文件保存起来,在其他的文件中调用其变量的时候,我们应该用extern来声明变量,同样函数也一样。


extern是在函数的外部来声明全局变量,他的作用域是从变量的定义开始,到本程序文件的结尾。主要用于扩展外部变量的作用域。他可以用于同一文件内声明的外部变量也可以用于多个文件的程序中声明外部变量。


A.同一文件中声明外部变量

比如:


  1. #include

  2. int add();

  3. void main()

  4. {

  5.               externA,B;

  6.     printf(“%d\n”,add(A,B));

  7. }

  8. int A=13,B=10;

  9. int add(int x, int y)

  10. {

  11. return (x+y);

  12. }


运行程序后可以看到输出23;


因为变量A和B在main之前没有定义过,因此在使用A和B变量的时候要在main函数中不能引用外部变量,因此在main函数中声明外部变量,表示A和B变量在外部已经定义过,这样main函数才能合法的使用变量A和B。


B. 在多个文件的程序中声明外部变量


在很多情况下一个程序由多个源文件构成,那多在一个文件中想引用另一个文件中已经定义的外部变量怎么么办呢?那么如果在两个文件中同时定义变量的话就会出现重复定义的错误,正确的办法是在一个文件中定义变量,而在另一个文件中用extern来声明外部变量,从而引用外部变量。比如:


  1. #include

  2. int A;

  3. void main()

  4. {

  5. int fun(int);      

  6.        printf(“%d\n”,fun(2));

  7. }


将此文件保存文main.c文件

在文件fun.c文件中要使用A则fun文件如下:


  1. extern A;

  2. int fun(int x)

  3. {

  4.        return(x+A);

  5. }


这样便可以使用不同文件中的变量了,但是在使用中非常值得慎重,因为在执行一个文件中的函数的时候,可能会改变了该全局变量的值,从而影响到两一个文件中的函数执行的结果。


这就是关于函数的部分的讲解,可能比较粗糙,希望大家仔细去分析并查阅相关文献来更充实完整函数的部分,同时也要多看优秀的程序代码,来加深对函数的理解。谢谢大家!



    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多