zoukankan      html  css  js  c++  java
  • 最近单片机编程中的心得

    1.  如果中断和主线程都要访问同一个变量,那么在主线程中访问之前要关闭中断,访问结束后立即打开中断。如果在主线程和中断中频繁改变变量的时候,比如自加、自减,最好先定义一个临时变量,记录自加/减的次数,后面一次性将这个被中断/主线程共享的变量进行加/减,从而减少对共有变量的访问次数,中断中也是如此。

    2.  在有流水线的单片机中,有时候明明在前面执行了一条指令,但是后面进行检验的时候,发现之前那个并没有起效果,其实是因为流水线导致还没有执行到前面那个语句。在1中描述的情况,在主线程中访问变量前关闭中断,如果只把这个语句执行一次,可能中断关闭还没有真正被执行,就开始访问共有变量了,这里需要连续多次执行中断关闭语句,从而达到效果。

      例如:

    volatile unsigned int Total_Num;                  //全局变量,被中断和主线程访问
    volatile unsigned char buffer[1024];	        //数据缓冲区
    void Funct_irq(void)			        //中断函数
    {
    	unsigned int temp1=0,temp2=total_Num,i;
    	i = readFifoNum();			    //读取fifo中的数据量
    	
    	while(i--)
    	{
    	    buff[temp2+temp1] = readFifoDat();	    //读取fifo中的数据
    	    temp1++;
    	}
    	Total_Num += temp1;			    //这里原来可以在while中做自加,但是不要这样,这样会导致频繁访问变量
    }
    
    int main(void)
    {
    	unsigned int total_Num_Temp,send_Temp,send_Return_Value;
    	while(1)
    	{
    		temp = total_Num;
    		if(total_Num>100)		    //如果缓冲区中的数据达到100字节了,那么就要进行发送100个字节
    		{
    			
    			for(send_Temp = 0,send_Return_Value=0;send_Return_Value==0;send_Temp++)
    			{
    				send_Return_Value = sendDataToDst();	//调用发送数据函数,返回0表示发送成功,非0表示失败
    			}
    			closeIRQ();		    //关闭中断,因为多级流水线问题,所以需要多次调用
    			closeIRQ();
    			closeIRQ();
    			total_Num -= send_Temp;	    //更新剩余的数据量,不要在for循环中做自减,这样太频繁访问,并且在访问之前要关闭中断,防止变量同时被访问
    			openIRQ();		    //开启中断,多次执行	
    			openIRQ();
    			openIRQ();
    		}
    		
    		<other code>			    //其它代码,自行添加
    	}
    	return 0;
    }
    

    3.  if、do、while 、switch、for、else、case、default等关键字后面一定要加上大括号{},即使后面只有一个语句,因为我就在Keil下编写单片机程序的时候,遇到过不加{}出现问题的,虽然很难解释,但是加大括号不浪费时间。其中比较难理解的是case和default,不加也可以,但是加上之后使得程序看起来更加清爽!其中break可以放在大括号外面!

    例如:

    int main(void)
    {
        int a;
        cout<<"Please input a"<<endl;
        cin>>a;
        switch(a)
        {
        case 1:
            {
                cout<<"a = 1"<<endl;
            }
            break;
        case 2:
            {
                cout<<"a = 2"<<endl;
            }
            break;
        case 3:
            {
                cout<<"a = 3"<<endl;
            }
            break;
        default:
            {
                cout<<"a is other"<<endl;
            }
            break;
        }
    	return 0;
    }
    

      

    4.  有时候不要想着把C语言代码写的多简洁,更重要的是看看生成的汇编代码的长度,只有几行的递归调用生成的汇编代码大大多于几十行你认为比较简洁的赋值语句。

    5.  相对独立的语句组注释。对这一组语句做特别说明,写在语句组上侧,和此语句组之间不留空行,与当前语句组的缩进一致。注意,说明语句组的注释一定要写在语句组上面,不能写在语句组下面。

    6.  小技巧:想必大家都有过将赋值操作符“=”当作比较相等操作符“==”用过,这个错误比较的隐晦,不易排查,而且编译器从不把这类事情当作是程序员犯下的错。避免的方法有两种,一种是养成良好的编程习惯,在比较数值时小心翼翼的处理;另一种方法见下面给出的代码: 

    if (NULL = = p)
    {
    ……
    }
    是不是觉得这种书写方式很古怪?不是程序写错了? 当然不是! 有经验的程序员为了防止将 if (p = = NULL) 误写成 if (p = NULL),而有意把p 和NULL 颠倒。编译器认为 if (p = NULL) 是合法的,但是会指出 if (NULL = p)是错误的,因为NULL不能被赋值。所以,再次遇到判断整型变量是否与某个数相等时,请这样写吧:

    if (2==flag)
    {
    ……
    }

    7.  形式参数和实际参数:形式参数是在子函数中定义的,生存时间只是子函数调用期间,实际函数是主调函数中传入子函数中的参数。形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只有在函数内部有效。函数调用结束返回主调函数后则不能再使用该形参变量。

      在定义函数时指定的形参在未出现函数调用时,它们并不占内存中的存储单元,因此称它们为形式参数或虚拟参数,表示它们并不是实际存在的数据,只有在发生函数调用时,函数中的形参才被分配内存单元。

       形参单元与实参单元是不同的单元,占用不同的内存空间。

    8.  在进行循环的时候,能用for就尽量用for来执行循环,而不用while,除非像单片机中的while(1),因为for将很多循环控制的变量变化(如自加、自减)都放在for语句中,使得看起来比较直观,不用在程序块中找变化的变量。

    9.  在C51等有类似JZ跳转指令的单片机中,如果进行循环,最好采用自减判断结果是否为0,这样需要的时间比自加是否达到一个值需要的时间要短,虽然段的很少,但是这个一个了解汇编指令的人应该做到的注意的点。

    10.  如果函数值的类型和return语句中表达式的值不一致,则以函数类型为准,即函数类型决定返回值的类型。对数值类型数据,可以自动进行类型转换。

    11.  对函数的定义和声明不是同一件事情。定义是指对函数功能的确立,包括指定函数名、函数类型、形参及其类型、函数体等,它是一个完整的、独立的函数淡味。而声明的作用则是把函数的名字、函数类型以及形参的个数、类型和顺序(注意,不包括函数体)通知编译系统,以便在对包含函数调用的语句进行编译时,据此对其进行对照检查(例如函数名是否正确,实参与形参的类型和个数是否一致)。

    12.  内置函数:即在编译时将所调用函数的代码直接嵌入到主调函数中,而不是将流程传出去。定义内置函数很简单,只要在函数声明和定义时,在前面加上关键字inline即可。

      一般只将规模很小(一般为5个语句一下)而是用频繁的函数(如定时采集数据的函数)生命为内置函数。在函数规模很小的情况下,函数调用的时间开销相当于甚至超过执行函数本身的时间,把它定义为内置函数,可大大减小程序的运行时间。

      应当说明:对函数做inline声明,只是程序设计者对编译系统提出的一个建议,也就是说它是建议性的。并非一经指定为inline,编译系统就必须这样做。编译系统会根据具体情况决定是否这样做。

    13.  匈牙利命名法中,字符串缩写sz的全称:string zero,表示以''结尾的字符串。

     14.  匈牙利命名法表:

    全局变量 g_ 双精度浮点 d
    常量 c_ 计数 c(通常用cnt)
    C++类成员变量 m_ 字符 ch(通常用c)
    静态变量 s_ 整型 i(通常用n)
    指针 p 字节 by
    函数 fn w
    无效 v 实型 r
    句柄 h 无符号 u
    长整型 l 最大 Max
    布尔 b 最小 Min
    浮点型(有时也指文件) f 初始化 Init
    双字 dw 临时变量 T(或Temp)
    字符串 sz(string zero) 源对象 Src

    短整型

    n 目的对象 Dest

    15.  volatile作用:一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。

      注意(摘自C++程序设计(谭浩强)P119):这里需要介绍另一个关键字register,如果将一个局部变量声明为register,那么表示允许将局部变量的值存放在CPU中的寄存器中,需要用时直接从寄存器取出参加运算,不必再到内存中读取。由于对寄存器的存取速度远高于对内存的存取速度,因此这样做可以提高执行效率。

      在程序中定义寄存器变量对编译系统只是建议性(而不是强制性)的。当今的优化编译系统能够识别使用频繁的变量,从而自动地将这些变量放在寄存器中,而不需要程序设计者指定。因此实际上用register声明变量是不必要的。读者对他有一定了解即可。

      解读:从以上摘抄中可以看出,在单片机中,如果变量使用很频繁,那么编译器自动将这个变量放在寄存器中,但是在中断和主线程共享一个变量的时候,变量的值改变的很频繁,我们需要精确的获取这个变量的值,从内存中获取,而不是获取保存着寄存器中的备份。

  • 相关阅读:
    react 入坑笔记(五)
    练习
    高级指令
    进阶指令
    基础指令
    VMware 备份操作系统
    Ubuntu 用户的切换
    形态学转换
    图像模糊
    域名拆分 tld
  • 原文地址:https://www.cnblogs.com/wzd5230/p/3446668.html
Copyright © 2011-2022 走看看