基于PC与单片机串口通信的温度监控系统程序设计  

1.系统介绍

        1)本系统主要讲解基于PC与单片机串口通信的温度监控系统程序设计(如图1),上位机采用常用的PC机,下位机使用的是STC89C52单片机,温度传感器使用的是DALLAS公司生产的 DS18B20,下位机获得DS18B20采集的温度数据通过串口通信方式传输给PC,PC负责监控温度和显示温度值,并负责下达命令给下位机。PC获得的数据实时显示在监控画面中,同时实时显示温度曲线.主要的功能和特点是有:

1.1 多点监控:可以同时监控多个点温度状况;

1.2 远程监控:可以通过网络查看和控制整个系统的工作过程(如图2);

1.3 显示功能:同时以数值和曲线两种方式实时显示被监控对象的温度情况;

1.4 数据归档:实时温度数据和图像均可由系统自动保存在数据库中,供以后查询或对被监控对象作更深入的分析之用(如图3);

1.5 控制功能:上位机复制接收下位机数据进行分析和监控,并复制下达命令给下位机,控制下位机的动作执行元件;

1.6 报警功能:上位机和下位机上均设置有报警功能,上位机中,采用语音或者警示灯报警,同时还有相关提示;下位机则采用报警灯和报警蜂鸣器提示;





  图1


  图2





  图4

        2)本文主要讲解上位机程序的编写(VC++),下位机串口通信程序编写(C51),以及温度传感器的驱动程序的编写(C51).

2.DS18B20及驱动程序

        美国DALLAS公司生产的 DS18B20,采用独特的单线接口方式,DS18B20在与微处理器连接时仅需要一条口线即可实现微处理器与DS18B20的双向通讯,测温范围 -55℃~+125℃,固有测温分辨率0.5℃,工作电源: 3~5V/DC,在使用中不需要任何外围元件,测量结果以9~12位数字量方式串行传送,DS18B20支持多点组网功能,多个DS18B20可以并联在唯一的三线上,实现组网多点测温。因为一线通信接口,必须在先完成ROM设定,否则记忆和控制功能将无法使用。主要首先提供以下功能命令之一: 1 )读ROM, 2 )ROM匹配, 3 )搜索ROM, 4 )跳过ROM, 5 )报警检查。这些指令操作作用在没有一个器件的64位光刻ROM序列号,可以在挂在一线上多个器件选定某一个器件,同时,总线也可以知道总线上挂有有多少,什么样的设备。

以下给出DS18B20的驱动程序:

typedef unsigned char     BYTE;
typedef unsigned int      WORD;
typedef signed char       CHAR;
sbit DS18B20 = P2^2;  //定义DS18B20温度传感器端口,接单片机P2.2口
 
BYTE bDS18B20Reset(void);       //send reset and initialization command
bit TmpreadByteBit(void);       //read a bit
BYTE bTmpreadByte(void);   //read a byte date
void TmpWriteByte(BYTE);   //write a byte to ds18b20
void TmpChange(void);  //DS18B20 begin change
WORD wGetTmp();               //get the temperature
void Delay1ms1(WORD wTime);//Delay time fuction
 
/****************************************************************************************
函数名:DS18B20Reset(void)   
功能:send reset and initialization command
作者:Koby
*****************************************************************************************/
BYTE bDS18B20Reset(void)
{
    WORD wCnt;
    DS18B20=0;
    wCnt=103;
    while(wCnt--);
    DS18B20=1;
    wCnt=4;
    while(wCnt--);
    return(DS18B20);
}
/****************************************************************************************
函数名:TmpreadByteBit(void) 
功能:read a bit
作者:Koby
****************************************************************************************/
bit TmpreadByteBit(void)  
{
    WORD wCnt=0;
    bit fgDat;
    DS18B20=0;wCnt++;          //wCnt++ for Delay1ms
    DS18B20=1;wCnt++;wCnt++;
    fgDat=DS18B20;
    wCnt=8;while(wCnt--);
    return (fgDat);
}
/****************************************************************************************
函数名:TmpreadByte(void)
功能:read a byte date
作者:Koby
****************************************************************************************/
BYTE bTmpreadByte(void)  
{
    BYTE i,j,bData=0;
    for(i=0;i<8;i++){
      j=TmpreadByteBit();
      bData=(j<<7)|(bData>>1);   //读出的数据最低位在最前面,这样刚好一个字节在Data里
    }
    return(bData);
}
/****************************************************************************************
函数名:TmpWriteByte(BYTE bComond)
功能:write a byte to DS18B20
作者:Koby
bData为指令码:
===================================================================================
  ROM指令码                         |
------------------------------------|----------------------------------------------
  0x33                              |   Read ROM(读ROM)
  ----------------------------------|----------------------------------------------
 (指定匹配芯片)
  ----------------------------------|----------------------------------------------
  0xCC                              |   Skip  ROM(跳跃ROM指令)
  ----------------------------------|----------------------------------------------
  0xFO                              |   Search ROM(搜索芯片)
  ----------------------------------|----------------------------------------------
  0xEC                              |   Alarm Search(报警芯片搜索)               
===================================================================================
  储存器指令码                      |                                              
------------------------------------|----------------------------------------------
  0x4E                              |   Write Scratchpad(向RAM中写数据)           
  ----------------------------------|----------------------------------------------
 
  ----------------------------------|----------------------------------------------
  0x48                              |   Copy Scratchpad(将RAM数据复制到EEPROM中)  
  ----------------------------------|----------------------------------------------
  0x44                              |   Convert T(温度转换)                       
  ----------------------------------|----------------------------------------------
  0xB8                              |   Recall EEPROM(将EEPROM中的报警值复制到RAM)
  ----------------------------------|----------------------------------------------
  0xB4                              |   Read Power Supply(工作方式切换)
===================================================================================
************************************************************************************/
void TmpWriteByte(BYTE bComond)   
{
    WORD i;
    BYTE j;
    bit fgTestb;
    for(j=1;j<=8;j++){
        fgTestb=bComond&0x01;
        bComond=bComond>>1;
        if(fgTestb){     //write 1 
            DS18B20=0;
            i++;i++;
            DS18B20=1;
            i=8;while(i--);
        } else {
            DS18B20=0;       //write 0
            i=8;while(i--);
            DS18B20=1;
            i++;i++;
        }
    }
}
/****************************************************************************************
函数名:TmpChange(void) 
功能:DS18B20 begin change
作者:Koby
****************************************************************************************/
void TmpChange(void)
{
    bDS18B20Reset();
    Delay1ms1(1);
    TmpWriteByte(0xCC);  // address all drivers on bus
    TmpWriteByte(0x44);  //  initiates a single temperature conversion
}
/****************************************************************************************
函数名:wGetTmp()  
功能:Get the temperature value
作者:Koby
****************************************************************************************/
WORD wGetTmp()  
{
    WORD wTemp;             // variable of temperature
    float fTemp;
    BYTE bTmp1,bTmp2;
    bDS18B20Reset();
    Delay1ms1(2);
    TmpWriteByte(0xCC);
    TmpWriteByte(0xBE);
    bTmp1=bTmpreadByte();
    bTmp2=bTmpreadByte();
    wTemp=bTmp2;
    wTemp=wTemp<<8;             //two byte  compose a int variable
    wTemp=wTemp|bTmp1;
    fTemp=wTemp*0.0625;
    wTemp=fTemp*10+0.5;//放大10倍,四舍五入
    return wTemp;
}
/****************************************************************************************
函数名:Delay1ms(WORD wTime)
功能:延时
作者:Koby
****************************************************************************************/
void Delay1ms1(WORD wTime)
{
}

3.以下是下位机单片机服务程序

       作为演示只接了一个温度传感器,当上位机侦测到温度超过设定值,会发出报警信号,单片机收到报警信号,会启动蜂鸣器,蜂鸣器会发出警报声作为提示。

        通信协议:第1字节,MSB为1,为第1字节标志,第2字节,MSB为0,为非第一字节标志,其余类推……,最后一个字节为前几个字节后7位的异或校验和。


测试方法:可以将串口调试助手的发送框写上 95 10 20 25,并选上16进制发送,接收框选上16进制显示,如果每发送一次就接收到95 10 20 25,说明测试成功。

#include <reg52.h>

 
#define T1MS_1200bps   0xD8;     /* (e8,-24,SMOD=0) @4800bps  pcon&=0x7f @11.0592MHz*/
#define T1MS_2400bps   0xEC;     /* (f4,-12,SMOD=0) @4800bps  pcon&=0x7f */
#define T1MS_4800bps   0xFA;     /* (fa, -6,SMOD=0) @4800bps  pcon&=0x7f */
#define T1MS_9600bps   0xFD;     /* (fd, -3,SMOD=0) @9600bps  pcon&=0x7f */
#define T1MS_19k2bps   0xFD;     /* (fd, -3,SMOD=1) @19.2kbps pcon|=0x80 */ 
 
 
#define INBUF_LEN 4   //数据长度
 
BYTE _rxBuf1[INBUF_LEN];
BYTE _rx_TMP_Buf[2];
BYTE _bCheckSum,_bCNT3;
bit  _fgDataRead=0;
 //蜂鸣器接单片机P2.3口

/****************************************************************************************
  
函数名:void AlarmOn()
  
功能:蜂鸣器打开
  
作者:Koby
  
****************************************************************************************/
  
 
void AlarmOn()
{
WORD k=0;
BEEP=0;
k = 20000;//Delay time
while(k--);
BEEP=1;
k=20000;
while(k--);//Delay time
}
  
/****************************************************************************************
   
函数名:RS232_RS232_Sel_BPS(BYTE bSel)
   
功能:设置串口波特率
   
sel为选择通讯速率:
      
作者:Koby
   
****************************************************************************************/
   
    void RS232_Sel_BPS(BYTE bSel)   
   
{   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
    }     
   
}   
  
/****************************************************************************************
   
函数名:void Init_RS232(void)
   
功能:初始化串口
   
作者:Koby
   
****************************************************************************************/
  
    void Init_RS232(void)   
   
{   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
}   
  
/****************************************************************************************
   
函数名:void Send_Char_Com(unsigned char ch)  
   
功能:向串口发送一个字符 
   
作者:Koby
   
****************************************************************************************/
  
    void Send_Char_Com(unsigned char ch)     
   
{   
   
   
   
   
}   
  
/****************************************************************************************
   
函数名:void Send_String_Com(unsigned char *str,unsigned int strlen)
   
功能:向串口发送一个字符串,strlen为该字符串长度 
   
作者:Koby
   
****************************************************************************************/
  
    void Send_String_Com(unsigned char *str,unsigned int strlen)   
   
{   
   
   
   
   
   
   
}   
  
/****************************************************************************************
   
函数名:void serial () interrupt 4 using 2 
   
功能:串口接收中断函数 
   
作者:Koby
   
****************************************************************************************/
  
    void serial () interrupt 4 using 2    
   
{   
   
   
   
   
       ch=SBUF;     
   
   
   
   
   
   
   
   
   
   
   
   
   
   
   
}   
  
/****************************************************************************************
   
函数名:void TMPControlAndRs232Test(void)   
   
功能:温度监控测试程序
   
作者:Koby
   
****************************************************************************************/
  
    void TMPControlAndRs232Test(void)   
   
{     
   
   
   
   
   
   
   
   
   
if(_fgDataRead)  //如果取数标志已置位,就将读到的数从串口发出      
     
{     
     
_fgDataRead=0; //取数标志清0      
      
TmpChange();      
      
wCnt=5;      
      
while(wCnt--); //Delay      
      
wTemp = wGetTmp();      
      
for(i=0;i<2;i++){      
      
_rx_TMP_Buf[1-i]=wTemp>>(8*i);       
      
}      
      
Send_String_Com(_rxBuf1,INBUF_LEN);//发送串口命令给上位机      
      
Send_String_Com(_rx_TMP_Buf,INBUF_LEN<<1); //发送温度值到串口      
      
if((_rxBuf1[2]==0x10) && (_rxBuf1[3]==0x15)){ //警报提示      
      
do{       
       
wCnt=5;        
        
AlarmOn();        
       
}while(wCnt--);       
      
}        
     
}     
   

            } 
  
   
     
   
}   
   
        /****************************************************************************************
   
函数名:void main(void)   
   
功能:主程序
   
作者:Koby
   
****************************************************************************************/
   
    void main(void)   
   
{   
   
   

            RS232_Sel_BPS(3);//选择波特率9600 
  
   
        TMPControlAndRs232Test(); //温度监控测试程序     
   
}



4.上位机温度监控系统程序设计


         4.1.串口通信程序设计


     1.建立项目 :打开VC++6.0,建立一个基于对话框的MFC应用程序SCommTest(与我源代码一致,等会你会方便一点);


     2.在项目中插入MSComm控件   选择Project菜单下Add To Project子菜单中的 Components and Controls…选项,在弹出的对话框中双击Registered ActiveX Controls项(稍等一会,这个过程较慢),则所有注册过的ActiveX控件出现在列表框中。 选择Microsoft Communications Control, version 6.0,,单击Insert按钮将它插入到我们的Project中来,接受缺省的选项。(如果你在控件列表中看不到Microsoft Communications Control, version 6.0,那可能是你在安装VC6时没有把ActiveX一项选上,重新安装VC6,选上ActiveX就可以了),这时在ClassView视窗中就可以看到CMSComm类了,(注意:此类在ClassWizard中看不到,重构clw文件也一样),并且在控件工具栏Controls中出现了电话图标,现在要做的是用鼠标将此图标拖到对话框中,程序运行后,这个图标是看不到的。


     3.利用ClassWizard定义CMSComm类控制对象ClassWizard->Member Viariables选项卡,选择CSCommTestDlg类,为IDC_MSCOMM1添加控制变量:m_ctrlComm,这时你可以看一看,在对话框头文件中自动加入了//{{AFX_INCLUDES() #include "mscomm.h" //}}AFX_INCLUDES (这时运行程序,如果有错,那就再从头开始)。


     4.在对话框中添加控件ID为IDC_EDIT_RXDATA,另一个用于输入发送数据,ID为IDC_EDIT_TXDATA,再添加一个按钮,功能是按一次就把发送编辑框中的内容发送一次,将其ID设为IDC_BUTTON_MANUALSEND。别忘记了将接收编辑框的Properties->Styles中把Miltiline和Vertical Scroll属性选上,发送编辑框若你想输入多行文字,也可选上Miltiline。 

5.添加串口事件消息处理函数OnComm()

这个函数是用来处理串口消息事件的,如每当串口接收到数据,就会产生一个串口接收数据缓冲区中有字符的消息事件,我们刚才添加的函数就会执行,我们在OnComm()函数加入相应的处理代码就能实现自已想要的功能了。请你在函数中加入如下代码:

void CSCommTestDlg::OnComm() {
     // TODO: Add your control notification handler code here
    VARIANT variant_inp;
     COleSafeArray safearray_inp;
     LONG len,k;
     BYTE rxdata[2048]; //设置BYTE数组 An 8-bit integerthat is not signed.
     CString strtemp;
     if(m_ctrlComm.GetCommEvent()==2) //事件值为2表示接收缓冲区内有字符
     {             以下你可以根据自己的通信协议加入处理代码
         variant_inp=m_ctrlComm.GetInput(); //读缓冲区
         safearray_inp=variant_inp; //VARIANT型变量转换为ColeSafeArray型变量
         len=safearray_inp.GetOneDimSize(); //得到有效数据长度
         for(k=0;k<len;k++)
             safearray_inp.GetElement(&k,rxdata+k);//转换为BYTE型数组
         for(k=0;k<len;k++) //将数组转换为Cstring型变量
         {
             BYTE bt=*(char*)(rxdata+k); //字符型
             strtemp.Format("%c",bt); //将字符送入临时变量strtemp存放
             m_strRXData+=strtemp; //加入接收编辑框对应字符串 
         }
     }
     UpdateData(FALSE); //更新编辑框内容
 }

到目前为止还不能在接收编辑框中看到数据,因为我们还没有打开串口,但运行程序不应该有任何错误,不然,你肯定哪儿没看仔细,因为我是打开VC6对照着做一步写一行的,运行试试。没错吧?那么做下一步:

6.打开串口和设置串口参数

// TODO: Add extra initialization here
if(m_ctrlComm.GetPortOpen())
 m_ctrlComm.SetPortOpen(FALSE);
 m_ctrlComm.SetCommPort(1); //选择com1
 if( !m_ctrlComm.GetPortOpen())
 m_ctrlComm.SetPortOpen(TRUE);//打开串口
 else
 AfxMessageBox("cannot open serial port");
 m_ctrlComm.SetSettings("9600,n,8,1"); //波特率9600,无校验,8个数据位,1个停止位 m_ctrlComm.SetInputMode(1); //1:表示以二进制方式检取数据
 m_ctrlComm.SetRThreshold(1); 
 //参数1表示每当串口接收缓冲区中有多于或等于1个字符时将引发一个接收数据的OnComm事件
 m_ctrlComm.SetInputLen(0); //设置当前接收区数据长度为0
 m_ctrlComm.GetInput();//先预读缓冲区以清除残留数据

现在你可以试试程序了,将串口线接好后,打开串口调试助手,并将串口设在com2,选上自动发送,也可以等会手动发送。再执行你编写的程序,接收框里应该有数据显示了。

7.发送数据

void CSCommTestDlg::OnButtonManualsend() 
 {
     // TODO: Add your control notification handler code here
    UpdateData(TRUE); //读取编辑框内容
     m_ctrlComm.SetOutput(COleVariant(m_strTXData));//发送数据
 }

运行程序,在发送编辑框中随意输入点什么,单击发送按钮,啊!看看,在另一端的串口调试助手(或别的调试工具)接收框里出现了什么。

最后说明一下,由于用到VC控件,在没有安装VC的计算机上运行时要从VC中把mscomm32.ocx、msvcrt.dll、mfc42.dll拷到Windows目录下的System子目录中(win2000为System32)并再进行注册设置,




         4.2.实时温度曲线TEECHART程序设计


下载一个teechart8.ocx文件,这是Activex的文件,使用之前先注册一下,将该文件放入系统目录下,然后打开命令提示符,或者直接在运行窗口输入:regsvr32 TeeChart8.ocx,然后会有一个注册成功的提示框,说明ActiveX控件注册成功,在VC6下就可以使用了。如果不想使用了,可以使用regsvr32 -u TeeChart8.ocx 解除对teechart8的注册。

首先新建一个基于MFC的exe工程,选择对话框,其他默认。进入工程以后可以看到类视图中只有系统生成的默认的几个类,我先在类视图中在工程名上点右键新建两个文件夹,一个叫src,一个叫tee,然后把默认生成的几个类(dlg,app,about三个类)拖放到src文件夹中。做这一步实际上没有什么实质的意义,只是为了看起开比较方便,因为一会儿添加了teechart8以后会有很多的类添加进来,看起来非常困难,所以先提前把他们分开。

进入资源视图,点击主对话框后,删除todo那个静态文本。下一步要在这个对话框上放置一个teechart图表,但是工具栏中没有,所以需要先添加一下。依次点击 工程-->增加到工程-->components and controls,进入对话框后选择registered ActiveX controls,找到TeeChart Pro Activex control v8后点击insert,会弹出添加类的对话框,左边的列表里面是可以选择添加的类,点击确定以后回到资源视图会发现工具栏下面多累一个圆形的小图标,这就是teechart8控件了。这时注意到,类视图中已经添加了一堆的类,把他们都放到tee文件夹中,看起来舒服多了。

下一步回到资源视图,将工具箱中的teechart控件托入对话框,改变一下大小,稍作修改,界面设计就完成了。在对话框上右键点击图标控件,点击属性,选择teechart pro editer 选项卡,点击edit chart ,出现一个名为edit的对话框,该对话框非常重要,包括了teechart的各种属性。首先就是series选项,该属性包含的是需要绘制的图表的类型,如曲线,饼图,柱状图,等等等等,种类很多,这也是teechart的优势。其次是chart,这个选项包含了图表的显示部分的很多属性,如坐标轴,背景,标题,等等。data选项中就是绘制图表需要的数据。export选项中包含了一些将图表导出为图片等格式的选项。print选项包含打印的设置。themes包含图表的主题,主要是显示方式。

接下来试着新建了一个fast line serie,准备做一个实时曲线的显示,更改标题为实时曲线,并将chart选项中的legend的Visible前面的勾去掉。OK,编译,运行。因为现在还没有数据所以还不会出现曲线,所以,还得给该曲线填充一点数据。最简单的方法就是,直接在data选项中添加一些数据,然后运行,这时候图表显示出曲线了。

下面的任务是绘制一个实时曲线图,可以实现坐标轴以及曲线随着数据的增加而移动。

要完成该功能,首先应该为该图表指定一个对象用于操作。单击图表,选择 建立类向导,点击member variables选项卡,点击add variable按钮,弹出添加变量对话框,输入m_chart后点击OK,一个与该图表控件对应的控件变量就建立完成了。进入类视图,看到dlg类中多了一个m_chart变量。

首先,利用该变量为该图表填充随机的初始数据。在使用之前,需要包含一些头文件,于是,在XXXDlg.cpp的上面添加如下代码: 

#include "tchart.h"
#include "axis.h"
#include "axes.h"
#include "scroll.h"
#include "series.h" 

然后在OnInitDialog()方法中添加如下代码: 

// TODO: Add extra initialization here
m_chart.Series(0).FillSampleValues(50);

这是点击运行以后,就可以看到图表控件上输出了50个点连成的曲线了。如下图4所示:



图4


 


下一步要做的是让曲线动起来。简单分析一下,要让曲线动起来,可以将左侧的点隐藏,右侧再增加一些新的点。我们可以设置一个定时器,比如1s,每隔1s增加一个数,这样的话数据就可以不断增长了。

即添加如下代码:

//在OnInitDialog()中添加
SetTimer(1,1000,NULL);

然后为dlg类添加WM_TIMER的消息响应函数,并添加代码: 

void CChartDlg::OnTimer(UINT nIDEvent) {
     // TODO: Add your message handler code here and/or call default
     m_chart.Series(0).Add(900,"lable",1);
     CDialog::OnTimer(nIDEvent);
 }

这里需要说明一下,add方法的三个参数,第一个为y值,我添加的是900,第二个为x轴的标签,第三个颜色设置,添加完毕后可以运行了,这时大家可以发现曲线在动,横坐标消失,变成了lable,而且左侧的曲线没有移出界面,而是曲线整个压缩,这不是我们想要的效果。进入资源视图,点击对话框,右键单击编辑属性点击tools选项,单击add添加工具。teechart8有很多的工具给我们选择,这里我们点击axis选项卡,选中axis scroll工具后点击add后回到编辑窗,在axis下拉框中选择bottom axis,然后关闭属性编辑即可。

接下来,需要实现坐标轴的移动。很简单,只需要在OnTimer中添加中间的那一句代码, 

void CChartDlg::OnTimer(UINT nIDEvent) {
     // TODO: Add your message handler code here and/or call default
    m_chart.Series(0).Add(900,"lable",1);
     m_chart.GetAxis().GetBottom().Scroll(1.0,TRUE);
     CDialog::OnTimer(nIDEvent);
 }

完成后运行,基本就可以了。坐标轴和曲线后可以移动了。

----------------------------------------------------------------

4.3 CLASSVIEW中的类都突然消失的解决方法
       1.关闭   vc   
       2.删除该工程目录下的   *.ncb,   *.clw   
       3.重新启动   vc

4.4 关于在VC++ 6.0中如何添加OnInitDialog()函数

OnInitDialog()函数是个virtual(虚函数),在它的类中用添加虚函数的方法,会发现添加的虚函数里面没有这个函数,而这个OnInitDialog函数是很有用的,在一般的添加进去的对话框的类中是不会有这个函数的,必须手工增加进去,当然不用手工写代码,至于用手工写代码是否可行,还不知道,可以通过VC加进去,而要加的这个函数的名字不是OnInitDialog,所以很难找到.其原因是因为要加的函数是WM_INITDIALOG.
这个函数的添加方法是这样的,选择要加入函数的对话框,右键->ClassWiZard(类向导)->Message Maps(消息映射),对象里面选择对话框,Messages(消息)里面选择WM_INITDIALOG,然后点Add Function(增加函数),这样就映射到类中去了,它的函数名称是OnInitDialog,而不是WM_INITDIALOG.这时可以点Edit Fuction,编辑函数了.

4.5 数据库访问

(1)、引入ADO类

#import "c:\program files\common files\system\ado\msado15.dll" \
no_namespace \
rename ("EOF", "adoEOF") 

(2)、初始化COM

在MFC中可以用AfxOleInit();非MFC环境中用: CoInitialize(NULL);
CoUnInitialize();

(3)#import 包含后就可以用3个智能指针了:_ConnectionPtr、_RecordsetPtr和_CommandPtr

1.连接和关闭数据库 (1)连接

例子:连接Access数据库

m_pConnection.CreateInstance(__uuidof(Connection));
 try                 
 { 
  // 打开本地Access库Demo.mdb
  m_pConnection->Open("Provider=Microsoft.Jet.OLEDB.4.0;DataSource=Demo.mdb",
   "","",adModeUnknown);
 }
 catch(_com_error e)
 {
  AfxMessageBox("数据库连接失败,确认数据库Demo.mdb是否在当前路径下!");
  return FALSE;
 }


(2)、关闭

//如果数据库连接有效
 if(m_pConnection->State)
        m_pConnection->Close();
 m_pConnection= NULL;

(3)、设置连接时间 //设置连接时间----------------------------------- pConnection->put_ConnectionTimeout(long(5));
2.打开一个结果集

(1)打开,首先创建一个_RecordsetPtr实例,然后调用Open()得到一条SQL语句的执行结果

_RecordsetPtr m_pRecordset;
 m_pRecordset.CreateInstance(__uuidof(Recordset));// 在ADO操作中建议语句中要常用try...catch()来捕获错误信息,
 // 因为它有时会经常出现一些意想不到的错误。jingzhou xu
 try
 {
  m_pRecordset->Open("SELECT * FROM DemoTable",// 查询DemoTable表中所有字段
  m_pConnection.GetInterfacePtr(),  // 获取库接库的IDispatch指针
  adOpenDynamic,
  adLockOptimistic,
  adCmdText);
 }
 catch(_com_error *e)
 {
  AfxMessageBox(e->ErrorMessage());
 }


  
(2)关闭结果集 m_pRecordset->Close();

3.操作一个结果集

(1)、遍历(读取)a)、用pRecordset->adoEOF来判断数据库指针是否已经移到结果集的末尾了;m_pRecordset->BOF判断是否 在第一条记录前面:

while(!m_pRecordset->adoEOF)
 {
  var = m_pRecordset->GetCollect("Name");
  if(var.vt != VT_NULL)
   strName = (LPCSTR)_bstr_t(var);
  var = m_pRecordset->GetCollect("Age");
  if(var.vt != VT_NULL)
   strAge = (LPCSTR)_bstr_t(var);
  m_AccessList.AddString( strName + " --> "+strAge );
  m_pRecordset->MoveNext();
 }


  
b)、取得一个字段的值的办法有两种办法

一是

//表示取得第0个字段的值 m_pRecordset->GetCollect("Name");

或者 m_pRecordset->GetCollect(_variant_t(long(0));

二是
pRecordset->get_Collect("COLUMN_NAME");

或者 pRecordset->get_Collect(long(index));

(2)、添加

a)、调用m_pRecordset->AddNew();
b)、调用m_pRecordset->PutCollect();给每个字段赋值
c)、调用m_pRecordset->Update();确认

(3)、修改
       (4)、删除

a)、把记录指针移动到要删除的记录上,然后调用Delete(adAffectCurrent) try

{
  // 假设删除第二条记录
  m_pRecordset->MoveFirst();
  m_pRecordset->Move(1);        
  // 从0开始
  m_pRecordset->Delete(adAffectCurrent);  
  // 参数adAffectCurrent为删除当前记录
  m_pRecordset->Update();
 }
 catch(_com_error *e)
 {
  AfxMessageBox(e->ErrorMessage());
 }

4.直接执行SQL语句,除了要用到结果集其余的大部分功能都可以直接用SQL语言实现

(1)、用_CommandPtr和_RecordsetPtr配合
_CommandPtr  m_pCommand;
m_pCommand.CreateInstance(__uuidof(Command));
// 将库连接赋于它
m_pCommand->ActiveConnection = m_pConnection;  
// SQL语句
m_pCommand->CommandText = "SELECT * FROM DemoTable";  
// 执行SQL语句,返回记录集
m_pRecordset = m_pCommand->Execute(NULL, NULL,adCmdText);  
(2)、直接用_ConnectionPtr执行SQL语句
_RecordsetPtr Connection15::Execute ( _bstr_t CommandText, 
                                      VARIANT * RecordsAffected, 
                                      long Options )

其中CommandText是命令字串,通常是SQL命令。 
参数RecordsAffected是操作完成后所影响的行数, 
参数Options表示CommandText中内容的类型,Options可以取如下值之一: 
adCmdText:表明CommandText是文本命令 
adCmdTable:表明CommandText是一个表名 
adCmdProc:表明CommandText是一个存储过程 
adCmdUnknown:未知
   
例子:
_variant_t RecordsAffected;
m_pConnection->Execute("UPDATE users SET old = old+1",&RecordsAffected,adCmdText);  
5.调用存储过程
(1)、利用_CommandPtr
_CommandPtr m_pCommand;
m_pCommand.CreateInstance(__uuidof(Command));
m_pCommand->ActiveConnection = m_pConnection;  // 将库连接赋于它
m_pCommand->CommandText = "Demo";  
m_pCommand->Execute(NULL,NULL, adCmdStoredProc);   
(2)、直接用_ConnectionPtr直接调用(见4.(2))

6.遍历数据库中的所有表名 _ConnectionPtr m_pConnect; 

_RecordsetPtr pSet; 
 HRESULT hr; 
 try 
 {  
  hr = m_pConnect.CreateInstance("ADODB.Connection");    
  if(SUCCEEDED(hr))  
  {   
   CString dd;   
   dd.Format("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=%s",file);   
   hr = m_pConnect->Open((_bstr_t)dd,"","",adModeUnknown);   
   pSet = m_pConnect->OpenSchema(adSchemaTables);      
   while(!(pSet->adoEOF))   
   {         
    //获取表格    
    _bstr_t table_name = pSet->Fields->GetItem("TABLE_NAME")->Value;
    
    //获取表格类型        
    _bstr_t table_type = pSet->Fields->GetItem("TABLE_TYPE")->Value;
    
    //过滤一下,只输出表格名称,其他的省略
    if ( strcmp(((LPCSTR)table_type),"TABLE")==0){
     CString tt;
     tt.Format("%s",(LPCSTR)table_name);     
     AfxMessageBox(tt);        
    }       
    pSet->MoveNext();    
   }   
   pSet->Close();  
  }  
  m_pConnect->Close();  
 }catch(_com_error e)///捕捉异常 
 {  
  CString errormessage;  
  errormessage.Format("连接数据库失败!rn错误信息:%s",e.ErrorMessage()); AfxMessageBox(errormessage);
  return -1;
 }


7.遍历一个表中的所有字段

Field *   field = NULL;  
 HRESULT   hr;
 Fields *  fields = NULL;
 hr = m_pRecordset->get_Fields (&fields); //得到记录集的字段集和 
   
 if(SUCCEEDED(hr)) 
     fields->get_Count(&ColCount); //得到记录集的字段集合中的字段的总个数 
 for(i=0;iItem[i]->get_Name(&bstrColName); //得到记录集//中的字段名
  strColName=bstrColName; 
  nameField = strColName;
  m_FieldsList.AddString(nameField);
 }
 if(SUCCEEDED(hr))
  fields->Release();//释放指针

附:
1、_variant_t
(1)、一般传给这3个指针的值都不是MFC直接支持的数据类型,而要用_variant_t转换一下
_variant_t(XX)可以把大多数类型的变量转换成适合的类型传入:
(2)、_variant_t var;_variant_t -> long: (long)var;
_variant_t -> CString: CString strValue = (LPCSTR)_bstr_t(var);
CString -> _variant_t: _variant_t(strSql);
2、BSTR宽字符串与CString相互转换

BSTR bstr;
CString strSql;
CString -> BSTR: bstr = strSql.AllocSysString();
BSTR -> CString: strSql = (LPCSTR)bstr;
3、_bstr_t与CString相互转换

_bstr_t bstr;
CString strSql;
CString -> _bstr_t: bstr = (_bstr_t)strSql;
_bstr_t -> CString: strSql = (LPCSTR)bstr;
4、关于时间
Access:表示时间的字符串#2004-4-5#
Sql:表示时间的字符串''2004-4-5''
DateField(时间字段) select * from my_table where DateField > #2004-4-10#