打印
[51单片机]

从业十年,教你单片机入门基础。(连载)

[复制链接]
楼主: jianhong_wu
手机看帖
扫描二维码
随时随地手机跟帖
61
收藏学习。谢谢。

使用特权

评论回复
62
AFCA| | 2015-4-24 15:11 | 只看该作者
ok!!!!!!!!!!!!!

使用特权

评论回复
63
zhang2015yi| | 2015-4-28 09:52 | 只看该作者

使用特权

评论回复
64
liuziyue| | 2015-4-28 20:43 | 只看该作者
ucuo

使用特权

评论回复
65
lrk2016| | 2015-4-30 13:42 | 只看该作者
学习,突然发现要学的太多了,PID,C语言,MATLAB、、、好多好多不会的

使用特权

评论回复
66
jianhong_wu|  楼主 | 2015-5-2 10:59 | 只看该作者
本帖最后由 jianhong_wu 于 2015-6-3 17:11 编辑

第十六节:加法运算的5种常用格式。
       根据上一节的预告,本来这节应该讲判断语句的,但是考虑到后续章节的连贯性,决定先讲运算语法。
       在讲运算语法之前,先讲一个我在前面忘了讲的知识点,那就是注释语句。何谓注释语句?在我前面一些章节的main函数中,经观察,发现一个规律,凡是中文解说的文字,要么前面有符号”//”,要么就是被包含在“/*”和”*/”之间。符号“//”和“/*  */”都是注释语句。注释语句是用来添加文字备忘,方便程序员阅读**的。在注释语句里的文字是不会被编译器翻译成机器码的,也就是说即使注释里面的文字再多,也不会增加单片机的程序容量,它是被编译器过滤忽略的,仅仅方便程序员做备注文字而已。
符号“//”和“/*  */”都是注释语句,但应用方面有点小差异。符号“//”是用来注释一行文字。而“/*  */”往往是用来注释一段文字,当然“/*  */”也可以注释一行文字。但是符号“//”仅仅能注释一行文字,却不能注释一段文字。
       讲完注释语句,继续回到本节正题。单片机本身具备了简单的加减乘除运算能力,我们只需要通过C语言调用相关的运算语法,即可指示单片机按我们的要求进行简单的运算。至于内部具体的运算细节我们可以不管,除非是涉及到大数据的运算才需要我们额外编写算法。请先看以下的加法语法格式:
       “保存变量”=“加数1+“加数2+...+“加数N”;
        含义是:右边的“加数”与“加数”相加,并且把最终的运算结果赋值给左边的“保存变量”。注意,这里的符号“=”不是等于号的意思,而是赋值的意思。左边的“保存变量”必须是变量,不能是常量,否则编译时会报错。而右边的“加数”既可以是变量,也可以是常量,也可以是“保存变量”本身自己。多说一句,何谓变量和常量?变量就是可以在程序中被更改的,是分配的一个RAM空间。而常量往往就是数字,或者是被分配在ROM空间的一个具体数值。下面根据右边“被加数”与“加数”的不同组合,列出了加法运算的5种常用格式。
        第1种:“加数1”是常量,“加数2”是常量。比如:
unsigned char a;
a=3+15;
数字“3”和“15”都是常量。执行上述语句后,保存变量a变成了18


        第2种:“加数1”是变量,“加数2”是常量。比如:
unsigned char b;
unsigned char x=10;
b=x+15;
x是变量,“15”是常量。由于原来x变量里面的数值是10,执行上述语句后,保存变量b变成了25。而变量x则保持不变,x还是10

        第3种:“加数1”是变量,“加数2”是变量。比如:
unsigned char c;
unsigned char x=10;
unsigned char y=6;
c=x+y;
x是变量,y也是变量。由于原来x变量里面的数值是10y变量里面的数值是6,执行上述语句后,保存变量c变成了16。而变量xy则保持不变,x还是10y还是6

        第4种:“加数1”是保存变量本身,“加数2”是常量。比如:
unsigned char d=2;
d=d+18;
d=d+7;
d是保存变量,“18”是常量。这类语句有一个特点,具备了自加功能,可以更改自己本身自己的数值。比如原来保存变量d的数值是2,执行“d=d+18;”语句后,d变成了20,接着再执行完“d=d+7;”语句后,d最后变成了27


        第5种:“加数1”是保存变量本身,“加数2”是变量。比如:
unsigned char e=2;
unsigned char x=10;
unsigned char y=6;
e=e+x;
e=e+y;
e是保存变量,xy都是变量。这类语句有一个特点,具备了自加功能,可以更改自己本身自己的数值。比如原来保存变量e的数值是2,执行“e=e+x;”语句后,e变成了12,接着再执行完“e=e+y;”语句后,e最后变成了18

        现在编写一个程序来练习上述5种格式的加法语句,最后把程序编译后下载到坚鸿51学习板观察结果。请直接复制第十节模板程序,修改的main程序代码如下:


void main() //主程序
{
/*---C语言学习区域的开始---------------------------------------------------------------------------*/
        
  unsigned char a;       //定义一个变量a,并且分配了1个字节的RAM空间。
  unsigned char b;       //定义一个变量b,并且分配了1个字节的RAM空间。
  unsigned char c;       //定义一个变量c,并且分配了1个字节的RAM空间。
  unsigned char d=2;       //定义一个变量d,并且分配了1个字节的RAM空间。初始化默认为2.
  unsigned char e=2;       //定义一个变量e,并且分配了1个字节的RAM空间。初始化默认为2.
        
  unsigned char x=10;    //定义一个变量x,并且分配了1个字节的RAM空间。初始化默认为10.
  unsigned char y=6;     //定义一个变量y,并且分配了1个字节的RAM空间。初始化默认为6.        

  //第1种:“加数1”是常量,“加数2”是常量。
        a=3+15;
        
        
        //第2种:“加数1”是变量,“加数2”是常量。
        b=x+15;
        
        
        //第3种:“加数1”是变量,“加数2”是变量。
  c=x+y;
        
        
  //第4种:“加数1”是保存变量本身,“加数2”是常量。
  d=d+18;
  d=d+7;
        
        
        //第5种:“加数1”是保存变量本身,“加数2”是变量。
        e=e+x;
  e=e+y;


  GuiWdData0=a;   //把变量a这个数值放到窗口变量0里面显示
  GuiWdData1=b;   //把变量b这个数值放到窗口变量1里面显示
  GuiWdData2=c;   //把变量c这个数值放到窗口变量2里面显示
  GuiWdData3=d;   //把变量d这个数值放到窗口变量3里面显示
  GuiWdData4=e;   //把变量e这个数值放到窗口变量4里面显示

        
/*---C语言学习区域的结束---------------------------------------------------------------------------*/
   while(1)  
   {
      initial();
      key_service();
      display_service();
   }

}

       如何在坚鸿51学习板上观察a,b,c,d,e5个变量?按下S1或者S5按键即可切换显示不同的窗口,从而显示不同的变量。坚鸿51学习板观察程序执行的结果如下:
变量a为18
变量b为25
变量c为16
变量d为27
变量e为18
     下节预告:加法的连写和自加运算的简写。
(未完待续)



使用特权

评论回复
67
佳人难再得| | 2015-5-6 22:21 | 只看该作者
在莫坛看过吴老师的帖子,感谢分享!

使用特权

评论回复
68
火狐free| | 2015-5-8 11:46 | 只看该作者
好好

使用特权

评论回复
69
jianhong_wu|  楼主 | 2015-5-10 08:06 | 只看该作者
本帖最后由 jianhong_wu 于 2015-6-3 17:13 编辑

第十七节:连加以及自加运算的简写。
       上一节我列举的加法例子中,右边的加数个数都是两个。实际上,C语言规则没有限制加数的个数,它的通用格式如下:
“保存变量”=“加数1+“加数2+...+“加数N”;
        当右边的加数个数超过两个的时候,这种情况就是我所说的“连加”,每个加数的属性没有限定,可以是常量,也可以是变量。比如:
a=1+69+102;     //加数全部是常量。
b=q+x+y+k+r;    //加数全部是变量。
c=3+x+y+5+k;   //加数有的是常量,有的是变量。
        连加的运行顺序是,赋值符号“=”右边的加数挨个相加,把每一次的运算结果放在一个临时的隐蔽变量里,这个隐蔽的变量我们看不到,是单片机系统内部参与运算时的专用寄存器,等右边所有的加数连加的计算结果出来后,再把这个隐蔽变量所保存的计算结果赋值给左边的“保存变量”。
        讲完了连加的格式,接着讲自加的简写。何谓自加?当右边的加数只要其中有一个是“保存变量”本身时,这种情况就是我所说的“自加”。比如:
“保存变量”=“保存变量”+“加数1”;
“保存变量”=“保存变量”+“加数1+“加数2+...+“加数N”;
        当这类自加计算式中,右边的加数有且仅有一个是“保存变量”本身时,那么上述自加计算式可以简写成如下格式:
“保存变量”+=“加数1”;
“保存变量”+=“加数1+“加数2+...+“加数N”;
        这种格式就是我所说的自加简写。现在举几个例子如下:
d+=6;  //相当于d=d+6;
e+=x;  //相当于e=e+x;
f+=18+y+k; //相当于f=f+18+y+k;
       这些例子都是很常规的自加简写,再跟大家讲一种很常用的特殊简写。当右边只有2个加数,当一个加数是“保存变量”,另一个是常数1时,格式如下:
“保存变量”=“保存变量”+1
       这时候,可以把上述格式简写成如下两种格式:
“保存变量”++;
++“保存变量”;
       这两种格式也是俗称的“自加1”操作。比如:
g++;  //相当于g=g+1或者g+=1;
++h;  //相当于h=h+1或者h+=1;
       也就是说自加1符号“++”可以在变量的左边,也可以在变量的右边,它们在这里本质是一样的,没有差别。当然,如果是在循环条件语句中,这时自加1符号“++”在左边还是在右边是有一点点微弱的差别,这方面的内容以后再讲。

       现在编写一个程序来练习刚才讲到的内容,最后把程序编译后下载到坚鸿51学习板观察结果。请直接复制第十节模板程序,修改的main程序代码如下:


void main() //主程序
{
/*---C语言学习区域的开始---------------------------------------------------------------------------*/
        
  unsigned char a;       //定义一个变量a,并且分配了1个字节的RAM空间。
  unsigned char b;       //定义一个变量b,并且分配了1个字节的RAM空间。
  unsigned char c;       //定义一个变量c,并且分配了1个字节的RAM空间。
  unsigned char d=5;       //定义一个变量d,并且分配了1个字节的RAM空间。初始化默认为5.
  unsigned char e=5;       //定义一个变量e,并且分配了1个字节的RAM空间。初始化默认为5.
  unsigned char f=5;       //定义一个变量f,并且分配了1个字节的RAM空间。初始化默认为5.
        unsigned char g=5;       //定义一个变量g,并且分配了1个字节的RAM空间。初始化默认为5.        
  unsigned char h=5;       //定义一个变量h,并且分配了1个字节的RAM空间。初始化默认为5.

        
  unsigned char q=1;    //定义一个变量q,并且分配了1个字节的RAM空间。初始化默认为1.
  unsigned char x=3;    //定义一个变量x,并且分配了1个字节的RAM空间。初始化默认为3.
  unsigned char y=6;     //定义一个变量y,并且分配了1个字节的RAM空间。初始化默认为6.        
  unsigned char k=2;     //定义一个变量k,并且分配了1个字节的RAM空间。初始化默认为2.
  unsigned char r=8;     //定义一个变量r,并且分配了1个字节的RAM空间。初始化默认为8.
        
        
         //第1个知识点:连加。
   a=1+69+102;     //加数全部是常量。a的结果为:172。
   b=q+x+y+k+r;    //加数全部是变量。b的结果为:20。
   c=3+x+y+5+k;   //加数有的是常量,有的是变量。c的结果为:19。

         //第2个知识点:自加的常规格式。
   d+=6;  //相当于d=d+6;  d的结果为:11。
   e+=x;  //相当于e=e+x;  e的结果为:8。
   f+=18+y+k; //相当于f=f+18+y+k;  f的结果为:31。
         
         
         //第3个知识点:自加的特殊格式。
   g++;  //相当于g=g+1或者g+=1;  g的结果为:6。
   ++h;  //相当于h=h+1或者h+=1;  h的结果为:6。




   GuiWdData0=a;   //把变量a这个数值放到窗口变量0里面显示
   GuiWdData1=b;   //把变量b这个数值放到窗口变量1里面显示
   GuiWdData2=c;   //把变量c这个数值放到窗口变量2里面显示
   GuiWdData3=d;   //把变量d这个数值放到窗口变量3里面显示
   GuiWdData4=e;   //把变量e这个数值放到窗口变量4里面显示
   GuiWdData5=f;   //把变量f这个数值放到窗口变量5里面显示
   GuiWdData6=g;   //把变量g这个数值放到窗口变量6里面显示
   GuiWdData7=h;   //把变量h这个数值放到窗口变量7里面显示



        
/*---C语言学习区域的结束---------------------------------------------------------------------------*/
   while(1)  
   {
      initial();
      key_service();
      display_service();
   }

}

       如何在坚鸿51学习板上观察a,b,c,d,e,f,g,h8个变量?按下S1或者S5按键即可切换显示不同的窗口,从而显示不同的变量。坚鸿51学习板观察程序执行的结果如下:

变量a为172
变量b为20
变量c为19
变量d为11
变量e为8
变量f为31
变量g为6
变量h为6
     下节预告:加法的溢出和优先级
(未完待续)






使用特权

评论回复
70
shmilyzlp| | 2015-5-11 21:40 | 只看该作者
菜鸟学习中,感谢分享:lol

使用特权

评论回复
71
向阳花开的春天| | 2015-5-12 15:49 | 只看该作者
期待

使用特权

评论回复
72
gurong60| | 2015-5-14 08:01 | 只看该作者
mark

使用特权

评论回复
73
zrb5688| | 2015-5-14 17:11 | 只看该作者
初学者,有的看的似懂非懂,不过得谢谢楼主提供了一些有用的思路。

使用特权

评论回复
74
1031565585| | 2015-5-16 19:10 | 只看该作者
顶起,写的很好!

使用特权

评论回复
75
jianhong_wu|  楼主 | 2015-5-17 00:01 | 只看该作者
本帖最后由 jianhong_wu 于 2015-6-3 17:14 编辑

第十八节:加法运算的溢出。
       我前面介绍的三种数据类型unsigned char ,unsigned int ,unsigned long,都是有最大范围限制的,它们最大范围分别是255,65535,4294967295,如果加法运算的结果超过了参与运算的变量本身,会出现什么结果,有什么规律,这就是本节要讲解的溢出问题。
     (1)何谓溢出?比如以下例子:
unsigned char a;
a=0x8536;
分析:
因为aunsigned char变量,位数是8位,也就是1个字节,而0x853616位,2个字节,这种情况下,把0x8536赋值给单字节变量a,变量a只能接收到最低位的一个字节0x36,而高位字节的0x85就被丢失了,这个就是本节所说的“溢出”了。
     (2)再看一个例子如下:
unsigned char b=0xff;
b=b+1;
分析:
b默认值是0xff,再加1后,变成了0x0100保存在一个隐藏的中间变量,然后再把这个中间变量赋值给单字节变量bb只能接收到低位字节0x00,所以运算后b的数值由于溢出变成了0x00
     (3)再看一个例子如下:
unsigned char c=0xff;
c=c+2;
分析:
c默认值是0xff,再加2后,变成了0x0101保存在一个隐藏中间变量,然后再把这个中间变量赋值给单字节变量cc只能接收到低位字节0x01,所以运算后c的数值由于溢出变成了0x01
     (4)再看一个例子如下:
Unsigned int d=0xfffe;
d=d+5;
分析:
d默认值是0xfffe,再加5后,变成了0x10003保存在一个隐藏中间变量,由于这个隐藏的中间变量是unsigned int类型,只能保存2个字节的数据,所以在中间变量这个环节就溢出了,实际上隐藏的中间变量只保存了0x0003,然后再把这个中间变量赋值给双字节变量dd理所当然也是0x0003
     (5)再看一个例子如下:
unsigned long e=0xfffffffe;
e=e+5;
分析:
e默认值是0xfffffffe,再加5后,变成了0x100000003保存在一个隐藏中间变量,由于这个隐藏的中间变量是unsigned long类型,只能保存4个字节的数据,所以在中间变量这个环节就溢出了,实际上隐藏的中间变量只保存了0x00000003,然后再把这个中间变量赋值给4字节变量ee理所当然也是0x00000003
        现在编写一个程序来练习上述前面4个例子,最后把程序编译后下载到坚鸿51学习板观察结果。请直接复制第十节模板程序,修改的main程序代码如下:

void main() //主程序
{
/*---C语言学习区域的开始---------------------------------------------------------------------------*/
        
   unsigned char a;
         unsigned char b=0xff;
         unsigned char c=0xff;
          unsigned int  d=0xfffe;

        
   a=0x8536;
   b=b+1;
   c=c+2;
   d=d+5;




   GuiWdData0=a;   //把变量a这个数值放到窗口变量0里面显示
   GuiWdData1=b;   //把变量b这个数值放到窗口变量1里面显示
   GuiWdData2=c;   //把变量c这个数值放到窗口变量2里面显示
   GuiWdData3=d;   //把变量d这个数值放到窗口变量3里面显示





        
/*---C语言学习区域的结束---------------------------------------------------------------------------*/
   while(1)  
   {
      initial();
      key_service();
      display_service();
   }

}

        如何在坚鸿51学习板上观察a,b,c,d4个变量的十六进制?按下S1或者S5按键即可切换显示不同的窗口,从而显示不同的变量,只要按住S9按键不放,此时显示的就是该变量的十六进制。坚鸿51学习板观察程序执行的结果如下:

变量a为0x36。
变量b为0x00。
变量c为0x01。
变量d为0x0003。

       这一节提到了一个“隐藏中间变量”的概念,这个神秘的“隐藏中间变量”到底是unsigned int类型还是unsigned long类型?有什么规律?如果运算中存在多种不同变量类型该怎么办,实际应用中有解决的办法吗?预知详情,请看一节内容。
      下节预告:加法运算中,神秘中间变量的类型以及解决“掺杂多种变量类型”的办法。
(未完待续)


使用特权

评论回复
76
jianhong_wu|  楼主 | 2015-5-22 07:09 | 只看该作者
第十九节:加法运算中,神秘中间变量的类型以及解决“掺杂多种变量类型”的办法。
       在开始本节内容之前,先告诉大家前面第十一节内容有一处笔误,unsigned long的数据长度应该是4个字节,而不是3个字节。
       上一节提到了一个“隐藏中间变量”的概念,两个加数相加,其结果先保存在一个“隐藏中间变量”里,然后再把这个“隐藏中间变量”赋值给左边的“保存变量”。这里的“隐藏中间变量”到底是unsigned int类型还是unsigned long类型?为了研究它的规律,在keil自带的C51编译环境下,我专门编写了好几个测试程序来观察实际运行的结果。
        “保存变量”=“加数1”+“加数2”;
       我测试的程序如下:
     (1)“保存变量”为 unsigned int类型,“加数1”为unsigned char类型,“加数2”为unsigned char 类型。
unsigned int a;
unsigned char x=0x12;
unsigned char y=0xfe;
a=x+y;

运行结果:a等于0x0110。
分析过程:两个char类型的数相加,当运算结果大于char本身时,并没有发生溢出现象,int型的“保存变量”a最终得到了完整的结果。
初步结论:这种情况,“隐藏中间变量”应该为unsigned int 类型。
     (2)“保存变量”为 unsigned long类型,“加数1”为unsigned int类型,“加数2”为unsigned char 类型。
unsigned long a;
unsigned int x=0xfffe;
unsigned char y=0x12;
a=x+y;

运行结果:a等于十六进制的0x0010。
分析过程:一个int类型的数与一个char类型的数相加,当运算结果大于其中最大加数int类型本身时,本来以为运算结果应该是long类型的0x00010010,结果是int类型的0x0010,发生了溢出现象。
初步结论:这种情况,“隐藏中间变量”应该为unsigned int 类型。
     (3)“保存变量”为 unsigned long类型,“加数1”与“加数2”都为常量。
unsigned long a;
a=50000+50000;

运行结果:a等于100000。
分析过程:int的最大数据范围是65535,而两个常量相加,其结果超过了65535还能完整保存下来。
初步结论:这种情况,“隐藏中间变量”等于左边的“保存变量”类型。
     (4)“保存变量”为 unsigned long类型,“加数1”为unsigned int类型,“加数2”为常量。
unsigned long a;
unsigned long b;
unsigned  int x=50000;
a=x+30000;
b=x+50000;

运行结果:a等于14464,b等于100000。
分析过程:本来以为a应该等于80000的,结果是14464发生了溢出。而b是100000没有发生溢出。
初步结论:这是一种很怪异的现象,为什么同样的类型,因为常量的不同,一个发生了溢出,另外一个没有发生溢出?这时的“隐藏中间变量”到底是int类型还是long类型我无法下结论。
       经过上述简单的测试,我发现规律是模糊的,模糊的规律就不能成为规律。如果真要按这种思路研究下去,那真是没完没了,因为还有很多情况要研究,当超过3个以上加数相加,同时存在long,int,char,常量这4种类型时又是什么规律?在不同的C编译器里又会是什么现象?即使把所有情况的规律摸清楚了又能怎么样,因为那么繁杂很容易忘记导致出错。有什么解决的办法吗?现在跟大家分享一种很简单的解决办法。
       当遇到有争议的问题时,还有一种解决思路是:与其参与争议越陷越深,还不如想办法及时抽身绕开争议。在上述运算中,只要经过简单的变换,让它们遵循“所有参与运算的变量,左边的变量类型必须跟右边的保存变量类型一致”这个原则,那么就不会存在这些争议了。
      (5)比如上述第(4)个例子,其转换方法如下:
unsigned long a;
unsigned long b;
unsigned  int x=50000;
Unsigned  long t;  //多增加一个long类型的变量,用来变换类型
t=0;  //把变量的高位和低位全部清零。
t=x;   //把x的数值先放到一个long类型的变量里,让”加数”跟”保存变量”类型一致。
a=t+30000;
b=t+50000;

运行结果:a等于80000,b等于100000。都没有发生溢出。
      (6)比如上述第(2)个例子,其转换方法如下:
unsigned long a;
unsigned int x=0xfffe;
unsigned char y=0x12;
unsigned  long t;  //多增加一个long类型的变量,用来变换类型。
unsigned  long r;  //多增加一个long类型的变量,用来变换类型。
t=0;//把变量的高位和低位全部清零。
t=x;   //把x的数值先放到一个long类型的变量里,让”加数”跟”保存变量”类型一致。
r=0;  //把变量的高位和低位全部清零。
r=y   //把y的数值先放到一个long类型的变量里,让”加数”跟”保存变量”类型一致。
a=t+r;

运行结果:a等于十六进制的0x00010010,没有发生溢出现象。

        下节预告:减法运算的常见格式。
(未完待续)





使用特权

评论回复
77
yuanquan12345| | 2015-5-22 13:23 | 只看该作者
学习 。

使用特权

评论回复
78
e08610318| | 2015-5-22 15:12 | 只看该作者
先会用,才能激起你的欲望去进一步了解其更深入的原理。

使用特权

评论回复
79
lgr164622819| | 2015-5-23 11:49 | 只看该作者
支持用ATMEL的SAMD20系列芯片,M0+内核,带电容触摸按键功能,通过工业级测试,稳定性一流,欢迎加Q:164622819,或拨打:15817111671,一起讨论交流。

使用特权

评论回复
80
allwind20nn| | 2015-5-27 11:33 | 只看该作者
留个名,方便以后学习。

使用特权

评论回复
发新帖 本帖赏金 10.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则