单片机:C51的基本运算

C51的基本运算

  • C51语言的基本运算与标准C类似,主要包括算术运算、关系运算、逻辑运算、位运算和赋值运算及其表达式等。

    算术运算符

    算术运算的算术运算符及其说明如下

    符号 说明 举例(设x=10,y=3)
    + 加法运算 z=x+y; //z=13
    - 减法运算 z=x-y; //z=7
    * 乘法运算 z=x*y; //z=30
    / 除法运算 z=x/y; //z=3
    % 取余数运算 z=x%y; //z=1
    ++ 自增1
    -- 自减1

    自增运算符与自减运算符

    运算符 说明 举例(设x初值为4)
    x++ 先用x的值,再让x加1 y=x++; //y为4,x为5
    ++x 先让x+1,再用x的值 y=++x; //y为5,x为5
    x-- 先用x的值,再让x-1 y=x--; //y为4,x为3
    --x 先让x-1,再用x的值 y=--x; //y为3,x为3

    逻辑运算符

    逻辑运算符及其说明

    运算符 说明 举例(设a=2,b=3)
    && 逻辑与 a&&b; //返回值为0
    || 逻辑或 a||b; //返回值为1
    逻辑非 !a; //返回值为0

    例如:条件“10>20”为假,“2<6”为真,则逻辑与运算为:

    (10>20)&&(2<6)=0&&1=0

    关系运算符

    关系运算符及其说明

    符号 说明 举例(设a=2,b=3)
    > 大于 a>b; //返回值为0
    < 小于 a<b; //返回值为1
    >= 大于等于 a>=b; // 返回值为0
    <= 小于等于 a<=b; //返回值为1
    == 等于 a==b; //返回值为0
    != 不等于 a!=0; //返回值为1

    位运算

    位运算及其说明

    符号 说明 举例
    & 按位逻辑与 0x19&0x4d=0x09
    | 按位逻辑或 0x19|0x4d=0x5d
    ^ 按位异或 0x19^0x4d=0x54
    ~ 按位取反 x=0x0f,则~x=0xf0
    << 按位左移(高位丢弃,低位补0) y=0x3a,若y<<2,则y=0xe8
    >> 按位右移(高位补0,低位丢弃) w=0x0f,若w>>2,则w=0x03
    • 0x19=19H=00011001,0x4d=01001101

    ​ 00011001

    相与 01001101 (上下相同就是该数,不同都是0)

    ​ = 00001001=09

    ​ 00011001

    相或 01001101 (上下相同是该数,不同为1)

    ​ = 01011101=5d

    ​ 00011001

    异或 01001101 (上下相同为0,不同为1)

    ​ = 01010100=54

    x=0x0f,则~x=0xf0 按位取反

    按位左移 00111010 (按位左移或右移后,空余的位补0)

    左移两位 11101000

    按位右移 00001111

    右移两位 00000011

    • 在实际应用中,常想改变I/O口某一位的值,而不影响其他位,如果I/O口可位寻址的,这个问题就很简单,但有时外扩的I/O口只能进行字节操作,要想实现单独位控,就要采用位操作

    例:编写程序将扩展的某I/O口PORTA(只能字节操作)的PORTA.5清0,PORTA.1置为1.

/*要想使其第五位清0而其他位不动,那么使用与逻辑运算,使其与11011111相与*/
/*要想使其第一位置1而其他位不变,那么使用或逻辑运算,使其与00000001相或*/
#define<absacc.h>
#define PORTA XBYTE[0xffc0]/*以字节形式去访问片外的地址0ffc0*/
void main(void)
{
    ……
    PORTA=(PORTA&0xdf)|0X02
    ……
}

指针和取地址运算符

  • 指针变量用于存储某个变量的地址

  • C51用''和'&'运算符来提取变量内容和变量地址*

赋值、指针和取值运算及其说明
符号 说明
* 提取变量的内容
& 提取变量的地址
  • 提取变量的内容和变量的地址的一般的形式分别为:

*目标变量=指针变量 //将指针变量所指的存储单元内容赋值给目标变量。

指针变量=&目标变量 //将目标变量的地址赋值给指针变量。

注意:指针变量中只能存放地址(也就是指针型数据),一般情况下不要将非指针类型的数据赋值给一个指针变量。

例如: a=&b; //定义指向b的地址

			   c=*b;             //定义指向b地址的内容
int i;         //定义整型变量i
int*b;         //定义指向整数的指针变量b
b=&i;          //定义指向i的地址
错误写法:b=i,整型变量的值不可以等于指针变量
    

C51的分支与循环程序结构

  • 在C51的程序结构上可以把程序分为三类,即顺序、分支和循环结构

分支控制语句

  • 分为if语句switch语句

    • if语句:if语句是用来判定所给定的条件是否满足,根据判定结果执行两种操作之一。if语句的基本结构if(表达式){语句}
    1. 形式1:if(表达式){语句}

    if(x>y){max=x;min=y};/*若表达式为真就执行后面的语句*/

    1. 形式2:if(表达式){语句1;} else{语句2;}
    if(x>y)/*if后表达式为真执行语句1,否则执行语句2*/
    {max=x;}
    else{min=y;}
    
    1. if语句的嵌套格式if,else if
    if(表达式1){语句1;}/*依次向下判定,是否符合表达式1,执行或往下递推,直到是否符合表达式n-1,符合就是语句n-1,不符合就是语句n*/
    else if(表达式2){语句2;}
    else if(表达式3){语句3;}
    ......
        else{语句n;}
    

    例如:

    if(x>100){y=1;}
    else if{x>50}{y=2;}
    else if(x>30){y=3;}
    else if(x>20){y=4;}
    else {y=5;}
    
    • switch语句:switch语句是多分支选择语句,一般形式如下
    switch(表达式1)/*当表达式1和常量表达式1相同时,就执行后面的语句1,之后遇到break就退出switch语句,以此类推,当所有常量表达式没有与表达式相对应的值,那么就执行default后面的语句n+1*/
    {
        case 常量表达式1:{语句1;}break;
        case 常量表达式2:{语句2;}break;
            ……
        case 常量表达式n:{语句n;}break;
        default:{语句n+1;}
    /*每一case常量表达式需互不相同,否则将混乱*/
    /*各个case和default出现次序,不影响程序执行的结果*/
    /*如果在case中遗忘了break语句,则程序执行了本行之后,不会按规定退出switch语句,而是将执行后续的case语句。在执行一个case分支后,使流程跳出switch结构,即终止switch语句的执行,可以用一条break语句完成*/
    }
    

    例如:在单片机程序设计中,常用switch语句作为键盘中按键按下的判别,并根据按下键的键号跳向各自的分支处理程序。

    input:  keynum=keyscan()/*keyscan是键盘扫描函数*/
    switch(keynum)
    {
          case 1:key1();break;//如果按下键为1键,则执行函数key1()
          case 2:key2();break;//如果按下键为2键,则执行函数key2()
          case 3:key3();break;//如果按下键为3键,则执行函数key3()
          case 4:key4();break;//如果按下键为4键,则执行函数key4()
      ……
          default:goto input
             
    }
    

循环结构流程控制语句

  • 实现循环结构的语句有以下三种:while语句、do-while语句和for语句。

    • while语句:语法形式如下
    while(表达式)
    {
        循环体语句;
    }
    /*while是先判断表达式真假,若为假程序不会执行,为真才执行*/
    

    例如:

    while((P1&0x80))= =0)
    {}
    

    while中的条件语句对AT89S52单片机的P1口第七位进行测试,如果P1 7为低(0),则由循环体无实际操作语句,故继续测试下去(等待),一旦P1 7变为高电平(1),则循环终止

    • do while语句:语法形式如下
    do
    {
        循环体语句;
    }
    while(表达式);
    /*do while 先执行循环体语句,再判断表达式真假,若为假则停止程序,但前提数据已经录入,即程序已经运行了。*/
    

    例:实型数组SAMPLE存有10个采样值,编写程序段,要求返回其平均值(平均值滤波)

    float avg(float *sample)
    {
        float sum=0;
        char n=0;
        do
        {
            sum+=sample[n];/*sum+=sample与sum=sum+sample等价*/
            n++;
        }while(n<10);
        return(sum/10);
    }
    
    • 基于for语句的循环:for循环的一般格式如下:
    for(表达式1;表达式2;表达式3)
    {
        循环体语句;
    }
    

    例:编写一个延时1ms程序

    void delayms(unsigned char int j)/*delayms延时函数*/
    {
        unsigned char i;
        while{j- -}
        {
            for(i=0;i<125;i++)/*执行一个for语句大概要延时8us,要执行125次,共延时1ms,利用for语句来延时*/
            {;}/*等待*/
        }
    }
    

    例:求1+2+3+...+100的累加和

    #include<reg51.h>
    #include<stdio.h>
    main()
    {
    	int nvar1,nsum;
    	for(nvarl=0,nsum=1;nsum<=100;nsum++)
    	nvar1+=nsum;  //累加求和
    	while(1);
    }
    
    • break语句

    例:

    void main(void)
    {
        int i,sum;
        sum=0;
        for(i=1;i<=10;i++)
        {
            sum=sum+i;
            if(sum>5)break;
            print("sum=%d\n",sum);/*通过串口向计算机屏幕输出显示sum值*/
        }
    }
    
    • continue语句

    例:输出整数1~100的累加值,但要求跳过所有个位为3的数。

    void main(void)
    {
        int i,sum=0;
        for(i=1;i<=100;i++)
        {
            if(i%10= =3)
            continue;
            sum=sum+i;
        }    
        print("sum=%d\n",sum);/*在计算机屏幕显示sum值,了解本语句的功能即可*/
    }
    
    
    • goto语句:基本格式如下:goto 标号

    例:计算整数1~100的累加值,存放到sum中。

    void main(void)
    {
        unsigned char i
        int sum;
         sumadd;
        sum=sum+i;
        i++;
        if(i<101)
        {
            goto sumadd/*无条件跳转到sumadd*/
        }
    }