STC89C52RC单片机额外篇 | 07 - 使用Keil搭建与管理项目式多文件工程

Posted Neutionwei

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STC89C52RC单片机额外篇 | 07 - 使用Keil搭建与管理项目式多文件工程相关的知识,希望对你有一定的参考价值。

1 单一式与项目式工程对比

新建一个Keil工程,初学者一般都会新建一个文件夹,然后将每个涉及到的.c.h文件,还有keil的输出文件都一下放在这个文件夹里,如下图:

图中有我们自己建立的.c.h文件以外,那些没有图标的就是Keil输出的一些文件,当然也包括我们要的.hex文件,这样看起来就会感觉乱乱的。

我们再看看下面这个文件夹:



在这个工程下分别建立了以下文件夹:

  • include:用来存放我们头文件.h
  • output:用来存放Keil输出的一些文件。
  • source:用来存放.c的一些源码。
  • utility:是用来存放一些通用的工具,比如将hex转为bin二进制文件。

其他的是一些工程文件。(这里的hexbin文件是通过utility里面的一些工具生成,将在后面讲解)。

看得出来与上一个图比起来是不是稍微有点层次感,清析一点了,(这两个比较并不是很明显,是因为这个工程是比较小的,如果是再多一点的话我相信肯定是很明显的)。

下面博主带领大家逐步使用Keil搭建与管理项目式多文件工程,案例使用上一节模块化编程后的数字温度计程序,不清楚的建议先看这篇:
STC89C52RC单片机额外篇 | 06 - 认识高内聚、低耦合的模块化编程》。

2 项目式工程建立

① 为当前的工程建立一个名为thermometer的文件夹。

② 在thermometer这个目录下分别建个source、include、output、utility四个文件夹(当然也可以在建立工程的时候,临时建立)。

③ 打开Keil新建一个工程名为thermometer的工程。


④ 指定Keil的输出文件夹。



⑤ 指定.h头文件的所在目录。


到目前为止,我们已经完成了Keil的层次管理!

⑥ 咦,不是还有一个utility目录吗?聪明的你会发现utility里面的东西我们并没有说到,其实这里存放的是些通用的附加的功能,很方便很实用的。

前面我们指定了output文件夹为了输出Keil编译时的输出文件,hex文件也是输出文件,也就在这里面,但是由于Keil编译时的中间输出文件还是蛮多的,
要到里面找hex文件还是有点小麻烦的,怎么办呢,这时utility下的小工具就派上用场了。

我们可以往这个目录写一个拷贝hex文件的脚本:

A、首先在utility目录下新建一个copy_hex.bat的文件。

B、右键以记事本打开这个批处理文件,往里面写入以下内容。

copy .\\output\\thermometer.hex thermometer.hex

脚本意思就是将.\\目录下output文件夹里面的thermometer.hex文件复制到.\\目录下,这样我们就可以在工程目录下一眼就看到hex文件啦!

C、在Keil工程中指定utility目录。

3 多文件管理

这里引用上一节模块化编程后的数字温度计程序,不清楚的建议先看这篇:
STC89C52RC单片机额外篇 | 06 - 认识高内聚、低耦合的模块化编程

程序如下:

//*****************************头文件声明****************************
#include <reg51.h>
//****************************数据类型定义***************************
typedef unsigned char uint8;
typedef unsigned int  uint16;
//***************************温度报警值定义**************************
#define MAX_TEMP 0x01F8 // 31.5℃
//****************************I/O口线声明****************************
#define SEG_CODE_PORT P0       
#define BIT_CODE_PORT P2       
sbit    DS18B20_DATA=P3^7;
sbit    BUZZ=P1^0;
//************************常量数组(段码表)声明*********************
uint8 code SegCodeTable[]=
    {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};      
//****************************函数原型声明***************************
void   DS18B20Init();
void   DS18B20BitWrite(bit Bit);
bit    DS18B20BitRead();
void   DS18B20ByteWrite(uint8 Byte);
uint8  DS18B20ByteRead();
void   GetTemperature(int *temperature);
void   DispTemperature(int temperature);
void   Delay(uint16 ms);
void   AlarmCheck(int temperature, int max_temperature);
//*******************************主函数******************************
void main()
{
	int temperature = 0;
    while(1)
    {
      GetTemperature(&temperature);  //采集当前温度
      DispTemperature(temperature);  //显示当前温度
	  AlarmCheck(temperature, MAX_TEMP);       //判断温度是否超过31.5℃
	} 
}
//*************************DS18B20初始化函数*************************
void DS18B20Init()
{
    uint16  i;  
    while(1)
      {
        DS18B20_DATA=0;
        i=640;           
        while(--i);       //延时800us(STC12C5A60S2,11.0592MHz,代码5级优化)  
        DS18B20_DATA=1;
        i=56;
        while(--i);                     //延时70us
        if(DS18B20_DATA==1) continue;   //无响应则重发复位脉冲
        i=224;
        while(--i);                     //延时280us
        if(DS18B20_DATA==1) break;      //复位成功
      }   
    i=160;
    while(--i);                         //延时200us
}
//***********************DS18B20位写操作函数*************************
void DS18B20BitWrite(bit Bit)
{
    uint16  i;
    DS18B20_DATA=0; 
    i=4;
    while(--i);                   //延时5us
    DS18B20_DATA=Bit;             //发送1位数到DS18B20
    i=48;
    while(--i);                   //延时60us
    DS18B20_DATA=1;     
}
//**********************DS18B20位读操作函数**************************
bit DS18B20BitRead()
{
    bit    temp;
    uint16 i;
    DS18B20_DATA=0;
    i=4;
    while(--i);                   //延时5us
    DS18B20_DATA=1;
    i=4;
    while(--i);                   //延时5us
    temp=DS18B20_DATA;            //读来自DS18B20的1位数
    i=48;
    while(--i);                   //延时60us
    return temp;
}     
//**********************DS18B20字节写操作函数************************
void DS18B20ByteWrite(uint8 Byte)
{
    uint8 i;
    for(i=0;i<8;i++)              //一共发送8位
      {
        if( Byte&0x01==1  )     //先发最低位
          DS18B20BitWrite(1);     //发送1
        else
          DS18B20BitWrite(0);     //发送0
        Byte>>=1;
      }    
}
//**********************DS18B20字节读操作函数************************
uint8 DS18B20ByteRead()
{
    uint8 i,temp=0;
    for(i=0;i<8;i++)              //一共读8位
      {
        temp>>=1;                 //字节变量右移
        if(DS18B20BitRead()==1)   //读取1位数据并存入临时变量temp中
          temp|=0x80;             //temp最高位置1
      }
    return temp;                  //返回读到的8位数
}
//*************************DS18B20温度转换函数***********************
void DS18B20Conversion()
{
    DS18B20Init();                //DS18B20初始化
    DS18B20ByteWrite(0xCC);       //跳过ROM匹配(因为只有一个DS18B20)
    DS18B20ByteWrite(0x44);       //启动温度转换
}

//*************************DS18B20温度读取函数***********************
void DS18B20Read(uint8 buff[])
{
    DS18B20Init();                //DS18B20初始化
    DS18B20ByteWrite(0xCC);       //跳过ROM匹配(因为只有一个DS18B20)
    DS18B20ByteWrite(0xbe);       //准备读转换结果
    buff[0]=DS18B20ByteRead();    //读温度值低字节
    buff[1]=DS18B20ByteRead();    //读温度值高字节
}

//*****************************温度采集函数**************************
void GetTemperature(int *temperature)
{
    uint8  Buff[2],i;
		DS18B20Conversion();		// 启动DS18B20温度转换
    for(i=0;i<250;i++)
    	DispTemperature(*temperature);          // 等待750ms,期间不断刷新LED显示
		DS18B20Read(Buff);			// 读取DS18B20温度数据
    *temperature=(Buff[1]<<8)+Buff[0];  //拼成16位温度值
}
//******************************温度显示函数*************************
void DispTemperature(int temperature)
{
    uint8 temp;
    temp=(temperature>>4)/10;                             //显示十位
    if(temp==0)
      SEG_CODE_PORT=0xFF       ;                             //十位为0则隐去
    else
      SEG_CODE_PORT=SegCodeTable[temp];
    BIT_CODE_PORT=0xF5;                                   //选择显示位置
    Delay(1);
    BIT_CODE_PORT=0xFF;
    SEG_CODE_PORT=SegCodeTable[(temperature>>4)%10]&0x7F; //显示个位(带点)
    BIT_CODE_PORT=0xF6;                                   //选择显示位置
    Delay(1);
    BIT_CODE_PORT=0xFF;
    SEG_CODE_PORT=SegCodeTable[(temperature&0x0F)*10/16]; //显示十分位
    BIT_CODE_PORT=0xF7;                                   //选择显示位置
    Delay(1);
    BIT_CODE_PORT=0xFF;      
}
//******************************蜂鸣器发声函数***************************//
void BuzzerSounds()
{
	uint8 i;
	for(i=0;i<50;i++)
    {
	  BUZZ=~ BUZZ ;
	  Delay(1);             //控制无源蜂鸣器发声50ms
    }
	 BUZZ=1;      
	 Delay(100);
}

//********************************超温报警函数***************************//
void AlarmCheck(int temperature, int max_temperature)
{
	if(temperature > max_temperature)
	{
		BuzzerSounds();	// 蜂鸣器发声
	}
}
//******************************软件延时函数*************************
void Delay(uint16 ms)
{
    uint16 i;
    do{
        i=790;
        while(--i);   //延时1ms(STC12C5A60S2,11.0592MHz,代码5级优化)
    } while(--ms);
}

针对这个数字温度计程序,我们可以把整个程序放到以下几个.c源文件中:

  • main.c:处理用户需求
  • delay.c:软件延时
  • ds18b20.c:DS18B20驱动
  • buzzer.c:蜂鸣器驱动
  • seg.c:数码管驱动

在source目录下新建这五个源文件:


把它们添加到Keil工程中:


下面我们按照我们的编程逻辑分别对各个源文件的程序进行部署。

① 打开main.c函数,添加原始程序框架:

//*****************************头文件声明****************************
#include <reg51.h>

//*******************************主函数******************************
void main()
{
	
}

② 新建一个typedef.h定义数据类型:


main.c中包含这个头文件:

然后点击编译,之后右键选择 Open document “typedef.h” 打开头文件,添加以下内容:

#ifndef _TYPEDEF_H
#define _TYPEDEF_H

//****************************数据类型定义***************************
typedef unsigned char uint8;
typedef unsigned int  uint16;

#endif

③ 新建一个port.h定义单片机端口,添加以下内容:

#ifndef _PORT_H
#define _PORT_H

#include <reg51.h>

//****************************I/O口线声明****************************
#define SEG_CODE_PORT P0       
#define BIT_CODE_PORT P2       
sbit    DS18B20_DATA=P3^7;
sbit    BUZZ=P1^0;

#endif


④ 新建一个ds18b20.h添加DS18B20模块的函数声明:

#ifndef _DS18B20_H
#define _DS18B20_H

void DS18B20Init();
void DS18B20BitWrite(bit Bit);
bit DS18B20BitRead();
void DS18B20ByteWrite(uint8 Byte);
uint8 DS18B20ByteRead();
void DS18B20Conversion();
void DS18B20Read(uint8 buff[]);

#endif

⑤ 打开ds18b20.c文件,添加以下内容:

#include "typedef.h"
#include "port.h"
#include "ds18b20.h"

//*************************DS18B20初始化函数*************************
void DS18B20Init()
{
    uint16  i;  
    while(1)
      {
        DS18B20_DATA=0;
        i=640;           
        while(--i);       //延时800us(STC12C5A60S2,11.0592MHz,代码5级优化)  
        DS18B20_DATA=1;
        i=56;
        while(--i);                     //延时70us
        if(DS18B20_DATA==1) continue;   //无响应则重发复位脉冲
        i=224;
        while(--i);                     //延时280us
        if(DS18B20_DATA==1) break;      //复位成功
      }   
    i=160;
    while(--i);                         //延时200us
}
//***********************DS18B20位写操作函数*************************
void DS18B20BitWrite(bit Bit)
{
    uint16  i;
    DS18B20_DATA=0; 
    i=4;
    while(--i);                   //延时5us
    DS18B20_DATA=Bit;             //发送1位数到DS18B20
    i=48;
    while(--i);                   //延时60us
    DS18B20_DATA=1;     
}
//**********************DS18B20位读操作函数**************************
bit DS18B20BitRead()
{
    bit    temp;
    uint16 i;
    DS18B20_DATA=0;
    i=4;
    while(--i);                   //延时5us
    DS18B20_DATA=1;
    i=4;
    while(--i);                   //延时5us
    temp=DS18B20_DATA;            //读来自DS18B20的1位数
    i=48;
    while(--i);                   //延时60us
    return temp;
}     
//**********************DS18B20字节写操作函数************************
void DS18B20ByteWrite(uint8 Byte)
{
    uint8 i;
    for(i=0;i<8;i++)              //一共发送8位
      {
        if( Byte&0x01==1  )     //先发最低位
          DS18B20BitWrite(1);     //发送1
        else
          DS18B20BitWrite(0);     //发送0
        Byte>>=1;
      }    
}
//**********************DS18B20字节读操作函数************************
uint8 DS18B20ByteRead()
{
    uint8 i,temp=0;
    for(i=0;i<8;i++)              //一共读8位
      {
        temp>>=1;                 //字节变量右移
        if(DS18B20BitRead()==1)   //读取1位数据并存入临时变量temp中
          temp|=0x80;             //temp最高位置1
      }
    return temp;                  //返回读到的8位数
}
//*************************DS18B20温度转换函数***********************
void DS18B20Conversion()
{
    DS18B20Init();                //DS18B20初始化
    DS18B20ByteWrite(0xCC);       //跳过ROM匹配(因为只有一个DS18B20)
    DS18B20ByteWrite(0x44);       //启动温度转换
}

//*************************DS18B20温度读取函数***********************
void DS18B20Read(uint8 buff[])
{
    DS18B20Init();                //DS18B20初始化
    DS18B20ByteWrite(0xCC);       //跳过ROM匹配(因为只有一个DS18B20)
    DS18B20ByteWrite(0xbe);       //准备读转换结果
    buff[0]=DS18B20ByteRead();    //读温度值低字节
    buff[1]=DS18B20ByteRead();    //读温度值高字节
}

⑥ 新建一个delay.h添加软件延时的函数声明:

#ifndef _DELAY_H
#define _DELAY_H

#include "typedef.h"

void Delay(uint16 ms);

#endif

⑦ 打开delay.c文件,添加以下内容:

#include "delay.h"

//******************************软件延时函数*************************
void Delay(uint16 ms)
{
    uint16 i;
    do{
        i=790;
        while(--i);   //延时1ms(STC12C5A60S2,11.0592MHz,代码5级优化)
    } while(--ms);
}

⑧ 新建一个buzzer.h添加BuzzerSounds的函数声明:

#ifndef _BUZZER_H
#define _BUZZER_H

#include "typedef.h"

void BuzzerSounds();

#endif

⑨ 打开buzzer.c文件,添加以下内容:

#include "port.h"
#include "delay.h"
#include "buzzer.h"

//******************************蜂鸣器发声函数***************************//
void BuzzerSounds()
{
	uint8 i;
	for(i=0;i<50;i++)
    {
	  BUZZ=~ BUZZ ;
	  Delay(1);             //控制无源蜂鸣器发声50ms
    }
	 BUZZ=STC89C52RC单片机额外篇 | 04 - 认识头文件<intrins.h>与_nop_函数

STC89C52RC单片机额外篇 | 02 - 认识串行通信波特率以及数据包

STC89C52RC单片机额外篇 | 06 - 认识高内聚低耦合的模块化编程

STC89C52RC单片机额外篇 | 05 - 把NOP指令封装成微秒级延时函数

STC89C52RC单片机额外篇 | 03 - 认识C51编译器支持的数据类型

STC89C52RC单片机额外篇 | 08 - 认识I2C协议以及E2PROM存储器(AT24Cxx)