单片机程序的编程规范

说明:本文中所说的编程规范只是我个人的一些想法和实践,有些经验可能并不能适合所有场合,当然也不可避免的有比较偏颇的看法,请大家多多批评指正。
aa4b049d-df7b-41ae-bb67-e7770ba71959?from=pc.jpg

1.1. 实验目的
之前我们已经学习了如何把复杂的单片机程序按照模块功能分为多个程序模块,然后使用头文件包含的方式将不同的程序模块有机的联系起来。经过模块化编写的程序,阅读、查询起来会很清晰。
本例中,我们进一步对单片机程序的编程规范做一些简单说明,目的是让我们编写的程序看起来不会那么乱,并且让看我们程序的人都能看懂。

1.2. 设计思路
本实例我们从变量的命名规范、程序注释、程序体结构划分几方面来简单了解如何使程序看起来干净、不杂乱、变量一看就知道含义,程序中每一段都是独立的,但是又能有机的结合起来。

1.3. 基础知识
我们从变量的命名规范、变量的初始化、函数体的结构化、注释的方法这几方面来了解一下。至于其它的编程规范和方法,我们在随后的实例中逐步了解。
d74d4965-ffbc-45dd-b142-2fb131b6283b?from=pc.jpg

1.4. 变量名称的命名规范
变量名应使用带有具体含义的英文单词组合或者简写,避免使用i,j,k这种无任何含义的变量名。
但是i,j,k这类变量并不是完全就不能用,在一定场合下,还是建议使用i,j,k这类变量的。什么场合下可以使用这种无含义的变量呢?在循环语句中是可以使用的,例如for循环、while循环、do...while循环,循环次数可以使用i,j,k这些变量表示。循环次数等一些很简单的,目的很明确的场合,使用这些变量不但不会引起歧义,还可以使程序更加简洁。
而在大部分情况下,还是建议使用有具体含义的变量名称。我个人习惯于使用动词+名称的方式,其中每一个档次的头一个字母大写。例如定义一个向DS1302写数据的变量,可以定义为WriteData,当程序里包含多个外设模块,这些模块都有相似的操作时,最好再把外设模块名字加上,与具体操作的变量名之间用下划线“_”将二者组合在一起,用以区别是对那个模块的操作。例如程序中有LCD1602和DS1302两个外设模块,都有写数据的操作,分别命名为:LCD1602_WriteData,DS1302_WriteData。也可以定义为WriteData_LCD1602,WriteData_DS1302。模块名称可以在前面,也可以在后面,根据自己的习惯就行。
对于英文名字比较长的单词,可以使用简写,但是简写的名字要让人看了容易理解,或者使用约定俗成的简写。例如对于串口数据收发的变量,一般约定用Rev表示接收数据变量、用Trn表示发送数据变量,对于数组变量,一般使用Buff命名。
还有一种命名习惯是命名时带上变量的数据类型,例如定义一个无符号整型数据,可以这样定义 unsigned int uiCounter。这样的话,一看到这个变量名字前面的“ui”,就知道该变量是无符号整型数据。
b2012ab9-177c-486c-8f1e-f3087ee68b9a?from=pc.jpg

1.4.1. 变量的初始化
变量在定义的时候可以直接初始化,例如unsigned int uiCounter=0;也可以只定义变量但不初始化变量的值。
如果变量在定义时没有初始化其数值,则程序中使用该变量时应对其设置明确的数值,不能直接使用,否则容易出错。例如先定义了变量unsigned int uiCounter;未对其值初始化,这时如果直接作如下操作P0=uiCounter,则P0端口8个位的状态是随机的,并且每调用一次这个语句,P0口8个位的状态是不一样的。

1.4.2. 函数体的结构化
一般情况下,函数体的内容包括:局部变量定义及初始化,具体函数语句、函数返回值语句。
其中局部变量定义及初始化以及函数返回值语句可以没有。另外函数返回值语句可能在函数体的任何位置,不一定局限于函数的最后。
函数内的局部变量初始化、执行语句、返回值这三部分之间最好用一个空行隔开。例如下面的代码。
bit BusyTest(void)
  •   {
  •     bit result;
  •                
  •                 RS=0;       //根据规定,RS为低电平,RW为高电平时,可以读状态
  •     RW=1;
  •     E=1;        //E=1,才允许读写
  •     _nop_();   //空操作
  •     _nop_();
  •     _nop_();
  •     _nop_();   //空操作四个机器周期,给硬件反应时间       
  •     result=BF;  //将忙碌标志电平赋给result
  •    E=0;         //将E恢复低电平
  •                
  •    return result;
  •   }
  • 复制代码
    这样做的好处是,可以使函数结构清晰。
    并且建议函数体内根据不同功能,内容,适当在语句中添加空行,这样的程序看起来不会让人感觉到累。

    1.4.3. 程序的注释
    程序的注释分为两大部分:函数功能的注释和程序语句的注释。
    函数语句的注释一般包括函数功能,入口参数、出口参数等。对于较为复杂的函数,有些还建议添加函数建立时间,更改时间,更改内容等。
    如下面代码所示。
    /*****************************************************
  • 函数功能:将模式设置指令或显示地址写入液晶模块
  • 入口参数:dictate
  • ***************************************************/
  • void WriteInstruction (unsigned char dictate)
  • {   
  •    
  • }
  • 复制代码
    程序语句的注释一般放在语句后面,应当于与语句保持适当的空格,这样不至于把程序语句和注释看成一体。
    对于比较长的语句,或者这个语句的注释比较长,一般把注释放在该语句的上方。如下所示。
    /*****************************************************
  • 函数功能:指定字符显示的实际地址
  • 入口参数:x
  • ***************************************************/
  • void WriteAddress(unsigned char x)
  • {
  •   //显示位置的确定方法规定为"80H+地址码x"
  •          WriteInstruction(x|0x80);
  • }
  • 复制代码
    1.5. 电路设计
    本实例没有电路。
    6a1eb0cf-a8fc-4409-918b-5f57a1f69917?from=pc.jpg

    1.6. 程序设计
    本实例没有具体的程序代码。

    1.7. 实例仿真
    本实例没有仿真。

    1.8. 总结
    单片机项目设计不单单是把功能实现了,还要把程序设计得更合理、更清晰、易移植,因为多数情况下,在公司里面的单片机项目不会只是设计者一个人看的,更不会一直是同一个人在管理、修改这个项目。所以要养成良好的编程习惯。