c51语言

[!note]

C51是在标准C的基础上,根据单片机存储器硬件结构及内部资源,扩展了相应的数据类型和变量,而C51在语法规定、程序结构与设计方法上,都与标准C相同。

4.1 编程语言Keil C51简介

  • Keil C51是在标准C语言基础上发展起来的
  • Keil C51语言是在ANSI C的基础上针对51单片机的硬件特点进行的扩展

C51与标准C的主要区别

  1. 头文件的差异

    51系列单片机厂家有多个,它们的差异在于内部资源如定时器、中断、I/O等数量以及功能的不同,只需要将相应的功能寄存器的头文件加载在程序内,就可实现所具有的功能

  2. 数据类型的不同

    位操作指令,因此Keil C51与ANSI C相比又扩展了4种类型

  3. 数据存储类型的不同

    51系列单片机有片内、外程序存储器,还有片内、外数据存储器。标准C并没有提供这部分存储器的地址范围的定义。此外,对于AT89C51单片机中大量的特殊功能寄存器也没有定义。

  4. 标准C语言没有处理单片机中断的定义

  5. 标准C的中的部分库函数不适于嵌入式处理器系统,如字符屏幕和图形函数

  6. 程序结构的差异

    由于51单片机的硬件资源有限,它的编译系统不允许太多的程序嵌套

    标准C所具备的递归特性不被Keil C51支持

    在Keil C51中递归函数必须是可重入的,可重入的函数需要加上reentrant。


C51语言程序设计基础

c52语言中的数据

数据类型

  • 扩展的四种数据类型:
数据类型 位数 字节数 取值范围
位变量 bit 1 0/1
特殊功能寄存器 sfr 8 1 0~255
- sfr16 16 2 0~65535
片内特殊功能寄存器的可寻址位 sbit 1 可进行位寻址的特殊功能寄存器的某位绝对地址

[!warning]

扩展的4种数据类型,不能使用指针对它们存取。

bit用来定义普通的位变量,而sbit定义的是特殊功能

  • example
sfr P1=0x90; //定义P1口在片内的寄存器
sfr P1=0xff;//使P1的所有引脚输出为高电平
sfr16  DPTR=0x82;
sfr   PSW=0xd0;/*定义PSW寄存器地址为0xd0*/
sbit  PSW^2 = 0xd2;/*定义OV位为PSW.2*/
  • 符号“^”前面是特殊功能寄存器的名字,
  • “^”的后面数字定义特殊功能寄存器可寻址位在寄存器中的位置
  • 取值必须是0~7

数据的存储类型

  • C51编译器通过将变量、常量定义成不同存储类型的方法将它们定义在不同的存储区中。
  • 片内片外两个独立的寻址空间,特殊功能寄存器与片内RAM统一编址,数据存储器与I/O端口统一编址

片内数据存储器

  • data:片内直接寻址区,位于片内RAM的低128字节。
  • bdata:片内位寻址区,位于片内RAM位寻址区20H~2FH。
  • idata:片内间接寻址区,片内RAM所有地址单元(00H~FFH)。

片外数据存储器

  • pdata:片外数据存储器页,一页为256字节。
  • xdata:片外数据存储器RAM的64KB空间。

片外程序存储器

code:外部程序存储器的64KB空间。

定义变量类型

[!note]

  1. 程序运行时该变量可能的取值范围
  2. 尽量选择8位即一个字节的char型,特别是unsiged char
  3. 尽量使用灵活巧妙的算法来避免浮点变量的引入。
  4. 尽量选择内部直接寻址的存储类型data,然后选择idata即内部间接寻址
  5. 在内部数据存储器数量有限或不能满足要求的情况下才使用外部数据存储器。选择外部数据存储器可先选择pdata类型,最后选用xdata类型。

C51的位变量定义

  • C51通过关键字“bit”来定义位变量,格式为:bit bit-name
  • C51程序函数可以包含类型为“bit”的参数,也可将其作为返回值
  • 位变量不能用来定义指针和数组

一个简单的c51程序

  • 在AT89S51的P1.0脚接有一只发光二极管,二极管的阴极接P1.0脚,阳极通过限流电阻接+5V,现在让发光二极管每隔800ms闪灭,占空比为50%。已知单片机时钟晶振为12MHz,即每个机器周期1μs,采用软件延时的方法,参考程序如下:
#include <reg51.h>       /* 包含reg51.h 头文件*/
sbit  P10=P1^0;  /* 定义位变量P1.0 ,也可使用sbit  P10=0x90*/
//延迟1ms
void  Delay(unsigned int i)       /* 延时函数Delay( ),i是形式参数 */
{            /* 两个花括号之间为函数Delay( )的函数体*/
    unsigned int j;        /* 定义变量j */
    for(;i>0;i--)                /* 如果i>0,则i减1 */
        for(j=0;j<333;j++) /* 如果j <333,则j加1 */    
}

void  main(void)        /* 主函数main( )*/
{
    while1/* 主程序轮询 */
    {    
          P10=1;        /*  P1.0输出高电平,发光二极管灭*/
           Delay(800) ;        /*  将实际参数800传递给形式参数i,                    延时800ms*/
        P10=0;         /*  P1.0输出低电平,发光二极管亮*/
        Delay(800); /*  将实际参数800传递给形式参数i,延时800ms*/
    }
}
  • 文件“reg51.h”包含了51单片机全部的特殊功能寄存器的字节地址及可寻址位的位地址定义。为了使用P1这个符号
  • 打开reg51.h文件可以看到“sfr P1=0x90;”,即定义符号P1与地址0x90对应

AT89C51不同存储区的C51定义

  • AT89C51有不同的存储区。

  • 利用绝对地址的头文件absace.h可对不同的存储区进行访问。

  • 该头文件的函数包括:

    CBYTE        //(访问code区,字符型)
    DBYTE        //(访问data区,字符型)
    PBYTE        //(访问pdata区或I/O口,字符型)
    XBYTE        //(访问xdata区或I/O口,字符型)
    

    还有CWORD、DWORD、PWORD、XWORD四个函数,它们的访问区域同上,只是访问的数据类型为int型

[!warning]

  • AT89S51片内的4个并行I/O口(P0~P3),都是SFR,故对P0~P3采用定义SFR的方法。
  • 对于片外扩展的I/O端口,根据硬件译码地址,将其看作片外RAM的一个单元,使用语句#define进行定义。
  • 也可把片外I/O口的定义放在一个头文件中,然后在程序中通过#include语句调用。

C51中断服务函数的定义

  • C51编译器对函数的定义进行了扩展,增加了一个扩展关键字interrupt,使用该关键字可以将一个函数定义成中断服务函数。
  • C51编译器在编译时对声明为中断服务程序的函数自动添加了相应的现场保护

中断服务函数的一般形式为:

函数类型 函数名(形式参数表)[interrupt n] [using n]

其中关键字interrupt后面的 n是中断号,对于AT89S51,取值为0~4,编译器从8×n+3处产生中断向量。

关键字using,专门用来选择AT89S51的4个不同的工作寄存器区

[!tip]

使用关键字using在函数中确定一个工作寄存器区时必须小心,要保证工作寄存器区切换都只在指定的控制区域中发生,否则将产生不正确的函数结果。

using属性的函数原则上不能返回bit类型的值,且关键字using和关键字interrupt都不允许用于外部函数,另外也都不允许有一个带运算符的表达式。

编写AT89S51中断程序时,应遵循以下规则

  1. 中断函数没有返回值
  2. 中断函数不能进行参数传递
  3. 在任何情况下都不能直接调用中断函数
  4. 如果在中断函数中再调用其他函数,则被调用的函数所使用的寄存器区必须与中断函数使用的寄存器区不同。

例如:void int1( ) interrupt 2 using 0/*中断号n=2,选择0区工作寄存器区*/


C51程序的开发流程

  • 单片机C51程序的开发属于嵌入式开发,遵循主流的交叉编译模式,即在宿主机(运行Keil µVision3的PC机)上开发编译,
  • 在目标机(51系列单片机)上运行。在宿主机上的编译结果为COFF文件或是满足Intel规范的文件,文件包含了可执行的机器码,要经过编程器烧写到单片机的程序区(Flash ROM)才能执行。

[!note]

新建项目工程→建立并编辑源文件→编译调试项目工程→生成目标文件→下载到程序存储器中→程序运行。


C51与MCS-51汇编语言的比较

  • C51语言还是汇编语言,源程序都要转换成机器码,单片机才能执行

  • MCS-51汇编语言编程

    • 片内数据存储器与特殊功能寄存器的必须合理正确使用
    • 按实际地址处理端口数据
    • 执行效率高,占用存储空间少,但是可读性和可移植性差
  • C51语言

    • 可读性好,模块化结构,可移植性,调试时间短,库函数

    • 寄存器分配、不同存储器的寻址以及数据类型等细节可由编译器来管理

    • 对数据类型和变量的定义,必须与AT89C51的存储器的存储器结构相关联

[!tip]

C51编写的程序与标准C程序编写的不同之处必须根据AT89C51的存储器结构以及内部资源定义相应的数据类型和变量。


C51与汇编语言混合编程

  • 汇编语言程序加入到C语言程序前,须使汇编语言和C51程序一样具有明确的边界、参数、返回值和局部变量
  • 保证汇编程序用来传递参数的存储区和C51函数使用的存储区是一样的
  1. C51代码中嵌入汇编代码

    预编译指令“asm",用#pragma语句

    #pragma asm
    //汇编指令行
    #pragma endasm
    

    通过asm和endasm告诉C51编译器,中间的行不用编译为汇编行

    [!warning]

    Keil µVision3的默认设置不支持asm和endasm,采用本法进行混合编程,需要在.c文件的option设置中,允许用伪指令asm和endasm选项

  2. 控制命令SRC控制

    先用C51编写代码,然后用SRC控制命令将C51文件编译生成汇编文件(. SRC)

    在该汇编文件中对要求严格的部分进行修改,保存为汇编文件.ASM,再用A51进行编译生成机器代码

  3. 模块间接口

    通常由C51程序模块调用汇编语言程序模块的变量和函数,例如调用汇编语言编写的中断服务程序。

    • 分别用C51和A51对源文件进行编译,然后用L51连接obj文件即可
    • 注意参数传递方法:寄存器传递,固定存储区传递

©OZY all right reserved该文件修订时间: 2025-09-20 05:42:10

评论区 - 04_C51语言

results matching ""

    No results matching ""