0
点赞
收藏
分享

微信扫一扫

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


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

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

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

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

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

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


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


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

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

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

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

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

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

2 项目式工程建立

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

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

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

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

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


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

④ 指定Keil的输出文件夹。

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


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


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

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

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


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

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

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

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

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

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


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

copy .\output\thermometer.hex thermometer.hex

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

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

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

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目录下新建这五个源文件:

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


把它们添加到Keil工程中:

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


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

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

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

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

}

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

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


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

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


然后点击编译,之后右键选择 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℃
}
}

编译结果:

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


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

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


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


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


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


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

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


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


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


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

4 总结

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

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


举报

相关推荐

0 条评论