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=1;      
	 Delay(100);
}

⑩ 新建一个seg.h添加段码表外部声明:

#ifndef _SEG_H
#define _SEG_H

#include "typedef.h"

extern uint8 code SegCodeTable[];

#endif

⑪ 打开seg.c文件,添加以下内容:

#include "seg.h"

//************************常量数组(段码表)声明*********************
uint8 code SegCodeTable[]=
    {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};


最后回到我们的main.c函数,各个模块的接口都能调用了,只需包含各个模块对应的头文件即可:

//*****************************头文件声明****************************
#include <reg51.h>
#include "typedef.h"
#include "port.h"
#include "ds18b20.h"
#include "seg.h"
#include "delay.h"
#include "buzzer.h"

//***************************温度报警值定义**************************
#define MAX_TEMP 0x01F8 // 31.5℃

//******************************温度显示函数*************************
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 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 AlarmCheck(int temperature, int max_temperature)
{
	if(temperature > max_temperature)
	{
		BuzzerSounds();	// 蜂鸣器发声
	}
}

//*******************************主函数******************************
void main()
{
	int temperature = 0;
	while(1)
	{
		GetTemperature(&temperature);  //采集当前温度
		DispTemperature(temperature);  //显示当前温度
		AlarmCheck(temperature, MAX_TEMP);       //判断温度是否超过31.5℃
	} 
}

编译结果:

此时再回头看看我们创建的工程:




在Keil工程下点击download图标即可执行copy_hex.bat脚本:



可以看到hex文从output目录被拷贝出来了!

4 总结

使用项目式多文件工程看起来非常有层次感,且清晰可见,特别适合实现更复杂的功能!且对于代码的移植也非常方便,例如我们下次在做其他设计之时,恰好又使用到DS18B20温度传感器,那么我们只需把ds18b20的c文件与头文件直接拷贝那个工程中即可,main函数对里面的接口进行调用即可。

不知不觉写到凌晨了,不过感觉还真爽!!!希望读者一键三连,晚安~~

  • 6
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌入式逍遥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值