http://www.geek-workshop.com/thread-1884-1-1.html
我们在上一讲中实现了一个TN901红外温度传感器51程序到Arduino程序的转换,如果代码越来越多这样程序的可维护性会随之降低,也不适合团度开发。我们应该把常用的文件封装成C++库,这样在复用的时候就会方便很多。
首先让我们来看下官方的C++类库是怎样的结构,以官方的LCD类库为例,如下图所示:
上面的文件大体是这样的结构:
文件名 | 文件类型 | 文件说明 |
keywords.txt | keywords.txt | Arduino库色标文件 |
LiquidCrystal.h | .h | C++头文件 |
LiquidCrystal.cpp | .cpp | C++程序文件 |
以下几种文件的作用如下:
.h 头文件:头文件作为一种包含功能函数、数据接口声明的载体文件,用于保存程序的声明(declaration),而定义文件用于保存程序的实现 (implementation)。
.cpp 文件:C++程序源文件主要的逻辑写在这里。
keywords.txt 文件:用来定义库在程序中显示关键字的颜色。
下面让我们来做个实际的例子将我们昨天写的程序改造成C++类库 可以点击这里
TH901.rar (992 Bytes, 下载次数: 174)
下载上一讲中的程序
首先我们来尝试下头文件的编写 有关宏定义的具体用法可以参见5楼zcbzjx的详细说明:)
#ifndef TH901_H //根据条件进行编译 #define TH901_H //我们在这中间添加程序 #endif //条件编译结束
#ifndef TH901_H //根据条件进行编译 #define TH901_H //我们在这中间添加程序 #endif //条件编译结束
我们在这中间添加程序主体的代码部分,首先来添加程序的头文件,需要引用什么都可以加进来
#include <inttypes. h> //引用相关的头文件
#include <inttypes. h> //引用相关的头文件
之后我们可以预定义一些需要的常量,这样维护起来比较方便,如果常量值改变只要统一修改这里就好
#define TN901_OTADDRESS 0x4c #define TN901_ETADDRESS 0x66 #define TN901_ENDADDRESS 0x0d
#define TN901_OTADDRESS 0x4c #define TN901_ETADDRESS 0x66 #define TN901_ENDADDRESS 0x0d
接下来我们来定义变量和声明程序的方法,需要外部调用和访问的就声明成public,不需要外部访问的就声明为private
class TN901 //定义类主体及类名 { public: //以下定义为公共方法 short ET; //环境温度输出 short OT; //目标温度输出 void Init ( int TN_Data, int TN_Clk, int TN_ACK ); //程序初始化 void Read ( ); //读取方法 void ReadData ( char flag ); //读取指定地址的数据 int GetData ( ); //获取数据 private: //以下定义为私有方法 int _dataPin; //数据引脚 int _clkPin; //时钟引脚 int _ackPin; //反馈引脚 unsigned char Data [ 5 ]; //数据数组 };
class TN901 //定义类主体及类名 { public: //以下定义为公共方法 short ET; //环境温度输出 short OT; //目标温度输出 void Init ( int TN_Data, int TN_Clk, int TN_ACK ); //程序初始化 void Read ( ); //读取方法 void ReadData ( char flag ); //读取指定地址的数据 int GetData ( ); //获取数据 private: //以下定义为私有方法 int _dataPin; //数据引脚 int _clkPin; //时钟引脚 int _ackPin; //反馈引脚 unsigned char Data [ 5 ]; //数据数组 };
这样我们一个头文件就写好了
/** * TN901 Library * 说明: 台湾燃太红外TN901传感器库 * 作者: 水乐天 * Email: [email]45268175@qq.com[/email] */ #ifndef TH901_H #define TH901_H #include <inttypes. h> //引用相关的头文件 #define TN901_OTADDRESS 0x4c #define TN901_ETADDRESS 0x66 #define TN901_ENDADDRESS 0x0d class TN901 //定义类主体及类名 { public: //以下定义为公共方法 short ET; //环境温度输出 short OT; //目标温度输出 void Init ( int TN_Data, int TN_Clk, int TN_ACK ); //程序初始化 void Read ( ); //读取方法 void ReadData ( char flag ); //读取数据 int GetData ( ); //获取数据 private: //以下定义为私有方法 int _dataPin; //数据引脚 int _clkPin; //时钟引脚 int _ackPin; //反馈引脚 unsigned char Data [ 5 ]; //数据数组 }; #endif //程序结束
/** * TN901 Library * 说明: 台湾燃太红外TN901传感器库 * 作者: 水乐天 * Email: [email]45268175@qq.com[/email] */ #ifndef TH901_H #define TH901_H #include <inttypes. h> //引用相关的头文件 #define TN901_OTADDRESS 0x4c #define TN901_ETADDRESS 0x66 #define TN901_ENDADDRESS 0x0d class TN901 //定义类主体及类名 { public: //以下定义为公共方法 short ET; //环境温度输出 short OT; //目标温度输出 void Init ( int TN_Data, int TN_Clk, int TN_ACK ); //程序初始化 void Read ( ); //读取方法 void ReadData ( char flag ); //读取数据 int GetData ( ); //获取数据 private: //以下定义为私有方法 int _dataPin; //数据引脚 int _clkPin; //时钟引脚 int _ackPin; //反馈引脚 unsigned char Data [ 5 ]; //数据数组 }; #endif //程序结束
接下来我们来书写程序的主体,就是CPP文件。首先我们引用已经写好的程序头文件
#include "TN901.h"
#include "TN901.h"
之后我们引用一些需要的库文件,需要哪些可以参见标准C++的库文件说明
#include "TN901.h" #include <stdio. h> #include <string. h> #include <inttypes. h> #include "Arduino.h"
#include "TN901.h" #include <stdio. h> #include <string. h> #include <inttypes. h> #include "Arduino.h"
然后我们逐一实现在头文件中定义的方法,注意类型要与定义类型相对应。所有的方法都要属于你定义的类名,格式如下
类名::方法名
类名::方法名
另外i啊我们要为程序的封装考虑程序应有的结构,总之是怎样使你的库使用起来最方便,在一般的逻辑中尽量减少调用的次数。我们可以把程序的逻辑分割为几个部分来书写。如:
1.初 始 化 2.实现功能 3.显示数据
1.初 始 化 2.实现功能 3.显示数据
当然这个逻辑以具体的程序为准。
我们来尝试写第一个初始化的方法,这样我们可以自己定义程序的端口。
//初始化TN901传感器 void TN901:: Init ( int TN_Data, int TN_Clk, int TN_ACK ) { //定义私有端口 _dataPin=TN_Data; _clkPin=TN_Clk; _ackPin=TN_ACK; pinMode (_clkPin, INPUT ); pinMode (_ackPin, OUTPUT ); digitalWrite (_ackPin, HIGH ); }
//初始化TN901传感器 void TN901:: Init ( int TN_Data, int TN_Clk, int TN_ACK ) { //定义私有端口 _dataPin=TN_Data; _clkPin=TN_Clk; _ackPin=TN_ACK; pinMode (_clkPin, INPUT ); pinMode (_ackPin, OUTPUT ); digitalWrite (_ackPin, HIGH ); }
然后我们书写程序的主要逻辑部分
//读取数据 void TN901:: Read ( ) { digitalWrite (_ackPin, LOW ); ReadData (TN901_OTADDRESS ); //目标温度的第一个字节为0x4c if ( (Data [ 0 ]==TN901_OTADDRESS )&& (Data [ 4 ]==TN901_ENDADDRESS ) ) //每帧的最后一个字节为0x0d { GetData_OT ( ); } delay ( 1 ); //等待1毫秒 digitalWrite (_ackPin, LOW ); ReadData (TN901_ETADDRESS ); //环境温度的第一个字节为0x66 if ( (Data [ 0 ]==TN901_ETADDRESS )&& (Data [ 4 ]==TN901_ENDADDRESS ) ) //每帧的最后一个字节为0x0d { GetData_ET ( ); } }
//读取数据 void TN901:: Read ( ) { digitalWrite (_ackPin, LOW ); ReadData (TN901_OTADDRESS ); //目标温度的第一个字节为0x4c if ( (Data [ 0 ]==TN901_OTADDRESS )&& (Data [ 4 ]==TN901_ENDADDRESS ) ) //每帧的最后一个字节为0x0d { GetData_OT ( ); } delay ( 1 ); //等待1毫秒 digitalWrite (_ackPin, LOW ); ReadData (TN901_ETADDRESS ); //环境温度的第一个字节为0x66 if ( (Data [ 0 ]==TN901_ETADDRESS )&& (Data [ 4 ]==TN901_ENDADDRESS ) ) //每帧的最后一个字节为0x0d { GetData_ET ( ); } }
最后我们逐一实现程序的运算过程
//读取数据 void TN901:: ReadData ( char flag ) { char i,j,k; byte BitState = 0; //每次发七帧 for (k= 0;k< 7;k++ ) { for (j= 0;j< 5;j++ ) //每帧5个字节 { for (i= 0;i< 8;i++ ) { int temp= digitalRead (_clkPin ); while (temp ) { temp = digitalRead (_clkPin ); } temp= digitalRead (_clkPin ); BitState= digitalRead (_dataPin ); Data [j ]= Data [j ]<< 1; Data [j ]= Data [j ]|BitState; while (!temp ) { temp = digitalRead (_clkPin ); } } } if (Data [ 0 ]==flag ) k= 8; } digitalWrite (_ackPin, HIGH ); } //计算环境温度 void TN901:: GetData_ET ( ) { ET= (Data [ 1 ]<< 8 )|Data [ 2 ]; ET = int ( ( ( float )ET/ 16 - 273.15 )* 100 ); } //计算目标温度 void TN901:: GetData_OT ( ) { OT= (Data [ 1 ]<< 8 )|Data [ 2 ]; OT = int ( ( ( float )OT/ 16 - 273.15 )* 100 ); }
//读取数据 void TN901:: ReadData ( char flag ) { char i,j,k; byte BitState = 0; //每次发七帧 for (k= 0;k< 7;k++ ) { for (j= 0;j< 5;j++ ) //每帧5个字节 { for (i= 0;i< 8;i++ ) { int temp= digitalRead (_clkPin ); while (temp ) { temp = digitalRead (_clkPin ); } temp= digitalRead (_clkPin ); BitState= digitalRead (_dataPin ); Data [j ]= Data [j ]<< 1; Data [j ]= Data [j ]|BitState; while (!temp ) { temp = digitalRead (_clkPin ); } } } if (Data [ 0 ]==flag ) k= 8; } digitalWrite (_ackPin, HIGH ); } //计算环境温度 void TN901:: GetData_ET ( ) { ET= (Data [ 1 ]<< 8 )|Data [ 2 ]; ET = int ( ( ( float )ET/ 16 - 273.15 )* 100 ); } //计算目标温度 void TN901:: GetData_OT ( ) { OT= (Data [ 1 ]<< 8 )|Data [ 2 ]; OT = int ( ( ( float )OT/ 16 - 273.15 )* 100 ); }
这样我们的程序就写好了,需要完整代码的朋友可以点击这里
TN901.rar (1.87 KB, 下载次数: 227)
下载
之后我们来尝试为我们的库编写一个范例程序来测试我们的程序是否运行正常。
#include <TN901. h> //引用库文件 TN901 tn; //实 例 化 void setup ( ) { Serial. begin ( 9600 ); tn. Init ( 7, 9, 8 ); //初 始 化 } void loop ( ) { tn. Read ( ); //数据读取 SerialValue ( ); delay ( 200 ); } void SerialValue ( ) { Serial. print ( "OT: " ); Serial. print (tn. OT, DEC ); //输出目标温度 Serial. println ( " C" ); Serial. print ( "ET: " ); Serial. print (tn. ET, DEC ); //输出环境温度 Serial. println ( " C" ); }
#include <TN901. h> //引用库文件 TN901 tn; //实 例 化 void setup ( ) { Serial. begin ( 9600 ); tn. Init ( 7, 9, 8 ); //初 始 化 } void loop ( ) { tn. Read ( ); //数据读取 SerialValue ( ); delay ( 200 ); } void SerialValue ( ) { Serial. print ( "OT: " ); Serial. print (tn. OT, DEC ); //输出目标温度 Serial. println ( " C" ); Serial. print ( "ET: " ); Serial. print (tn. ET, DEC ); //输出环境温度 Serial. println ( " C" ); }
好了,我们看到虽然我们花了一点时间编写了库文件,但是在我们使用程序的时候可以非常方便的调用,而且这样逻辑会比较清晰。呵呵。
最后送给大家一个小礼物,就是如何为我们的库编写色标文件,色标文件就是定义你的库中的方法在编译器中显示的颜色
没有色标文件的库显示起来是这样的:
我们可以看到库文件的类名和方法名都是黑色的这样看起来不是很清楚。加上色标文件之后是这样的:
我们可以看到TN901这样的类名都被加亮了这样看起来要清楚许多。
实现这个需要定义一个色标文件放在库文件的目录中,格式如下
####################################### # Syntax Coloring Map For 你的类名 ####################################### ####################################### # Datatypes (KEYWORD1 ) 数据类型关键字 ####################################### TN901 KEYWORD1 ####################################### # Methods and Functions (KEYWORD2 ) 方法类型关键字 ####################################### Init KEYWORD2 Read KEYWORD2 ReadData KEYWORD2 GetData KEYWORD2 ####################################### # Constants (LITERAL1 ) 常量类型关键字 ####################################### ET LITERAL1 OT LITERAL1
####################################### # Syntax Coloring Map For 你的类名 ####################################### ####################################### # Datatypes (KEYWORD1 ) 数据类型关键字 ####################################### TN901 KEYWORD1 ####################################### # Methods and Functions (KEYWORD2 ) 方法类型关键字 ####################################### Init KEYWORD2 Read KEYWORD2 ReadData KEYWORD2 GetData KEYWORD2 ####################################### # Constants (LITERAL1 ) 常量类型关键字 ####################################### ET LITERAL1 OT LITERAL1
这样我们的色标文件就写好了,我们把它保存成文件名为keywords.txt的文件放在库文件目录下就可以了。
当我们些库文件全部做好了之后我们就可以把这些文件放在,这样的目录下
energia:energia-0101E0008\hardware\msp430\libraries\ arduino:arduino -1.0 .1-windows\arduino -1.0 .1\libraries\
energia:energia-0101E0008\hardware\msp430\libraries\ arduino:arduino -1.0 .1-windows\arduino -1.0 .1\libraries\
这样当你重新打开官方编译器的时候可以看到如下的内容
这样我们就可以很方便的调用了。