单片机串口发送温度_51单片机串口发送温度 - CSDN
  • /********该程序主要是利用DS18B.../*当程序收到上位机发送的命令之后,该程序会将当时的温度值通过串口发送给上位机*/ #include #define uchar unsigned char #define uint unsigned int sbit DQ = P1^0; //定义D
    /********该程序主要是利用DS18B20采集温度,然后通过数码管显示温度*************/
    /*当程序收到上位机发送的命令之后,该程序会将当时的温度值通过串口发送给上位机*/
    #include<reg51.h>
    #define uchar unsigned char
    #define uint unsigned int
    sbit DQ = P1^0;               //定义DS18B20的信号线端口
    uchar i,j;
    uchar dis_buffer[4];//定义数据缓冲数组
    uchar bit_ser[]={0xfe,0xfd,0xfb,0xf7}; //定义数码管片选数组
    uchar seven_seg[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
                                                                        //定义数码管段选数组
    /****************************延时函数*********************************/
    void delay(uint x)
    {
     while(x)   
     x--;
    }
    /*************************DS18B20初始化函数***************************/
    void Init_DS18B20(void)
    {
     unsigned char x=0;
     DQ = 1;         //DQ复位
     delay(8);       //稍做延时
     DQ = 0;               //单片机将DQ拉低
     delay(80);     //精确延时 大于 480us
     DQ = 1;           //拉高总线
     delay(14);
     x=DQ;           //稍做延时后 如果x=0则初始化成功 x=1则初始化失败
     delay(20);
    }
    /***************************从18B20中读一个字节************************/
    uchar ReadOneChar(void)
    {
     uchar i=0;
     uchar dat = 0;
     for (i=8;i>0;i--)
             {
                     DQ = 0;   // 给脉冲信号
                     dat>>=1;
                     DQ = 1;   // 给脉冲信号
                     if(DQ)
                     dat|=0x80;  
                     delay(8);
             }
             return(dat);
    }
    /***************************向18B20中写一个字节************************/
    Write_OneChar(uchar dat)
    {
     uchar i=0;
     for (i=8; i>0; i--)
     {
             DQ = 0;     //给脉冲信号
             DQ = dat & 0x01;
             delay(5); 
             DQ = 1;     //给脉冲信号
             dat >>= 1;
     }
     delay(4);
    }
    /**************************从18B20中读取一个字节***********************/
    int Read_Temperature(void)
    {
     uchar i = 0,t = 0,a,b;
     int temp;
     Init_DS18B20();
     Write_OneChar(0xcc);   // 跳过读序号列号的操作
     Write_OneChar(0x44);   // 启动温度转换
     Init_DS18B20();
     Write_OneChar(0xcc);   //跳过读序号列号的操作
     Write_OneChar(0xbe);   //读取温度寄存器等(共可读9个寄存器) 前两个就是温度
     i = ReadOneChar();           //读取温度值低位
     t = ReadOneChar();           //读取温度值高位
     a = i & 0x0f;
     b = t;
     i = i >> 4;             //低位右移4位,舍弃小数部分
     t = t << 4;             //高位左移4位,舍弃符号位
     t = t | i;
     temp = (t + a * 0.0625) * 100; //得到一个比实际温度扩到100倍的值,主要是为了更好的显示和传输          
     return(temp);                  //返回温度值
    }
    
    
    /***************************初始化定时器0******************************/
    void timer0_init(void)                      //
    {
     TMOD = 0x21; //由于串口通信需要使用定时器1,因此TMOD的值是0x21
     TL0 = (65536-5000) % 256;
     TH0 = (65536-5000) / 256;
     EA = 1;
     ET0 = 1;
     TR0 = 1;
    }
    /*************************发送数据的函数********************************/
    void txd_data(char send_data)    
    {
     SBUF = send_data; //将需要发送的数据放入发送缓冲区
     while(!TI);           //等待发送数据
     TI = 0;
    }
    /********************T0中断处理函数,主要用于显示当前温度***************/
    void timer0_isr(void) interrupt 1
    {
     int temp; 
     TR0 = 0;
     TL0 = (65536-5000) % 256;
     TH0 = (65536-5000) / 256;
     TR0 = 1;
     switch(i)
     {
             case 0:
                     P2 = bit_ser[0];
                     P0 = seven_seg[dis_buffer[0]];                          
                     break;
             case 1:
                     P2 = bit_ser[1];
                     P0 =seven_seg[dis_buffer[1]] & 0x7f;
                     break;
             case 2:
                     P2 = bit_ser[2];
                     P0 =seven_seg[dis_buffer[2]];
                     break;
             case 3:
                     P2 = bit_ser[3];
                     P0 =seven_seg[dis_buffer[3]];
                     break;
     } 
     i++;
     if(i >= 4) 
     {
             i = 0;
             j++;
             if(j >= 10)  //如果到200ms就会读取一次温度,并将温度值放入显示缓冲区
             {
                     j = 0;
                     temp = Read_Temperature();
                     dis_buffer[0] = temp / 1000;
                     dis_buffer[1] = temp % 1000 / 100;
                     dis_buffer[2] = temp % 100 / 10;
                     dis_buffer[3] = temp % 10;
             }
     }
    }
    
    
    /***************************串口通信初始化函数***************************/
    void uart_init(void)
    { 
     SCON = 0x50;         //方式1,充许接收
     TMOD = 0x21;         //T1方式2定时,T0方式是1          
     TH1 = 0xFd;          //波特率9600,Fosc=11.0592MHz
     TL1 = 0xFd;
     TR1 = 1;
     ES = 1;                    // 打开串口中断
    }
    /**************************串口中断处理函数*****************************/
    void uart() interrupt 4                      
    {
     char y,i;
     while(!RI);
     y = SBUF;                       //读取接收的信息,然后判断是否为发送温度命令
     if(y == '0')             //如果为发送命令,就将当前数据缓冲数组内的数据发送给上位机
     {
             for(i = 0;i <= 3;i++)
             {
                     txd_data(dis_buffer[i] + 48);
             }
     }
     RI = 0;           
    }
    /*******************************主函数*********************************/
    void main()
    {
     timer0_init();           //调用T0初始化函数
     uart_init();             //调用串口初始化函数
     while(1)
     {}
    }
    展开全文
  • 本设计采用ds18b20和dht11采集温湿度并且串口传输显示仿真,串口波特率9600,采用虚拟串口显示。
  • 外接晶振为12MHz时,51单片机相关周期的具体值为: 振荡周期=1/12us; 状态周期=1/6us; 机器周期=1us; 指令周期=1~4us; 51单片机定时/计数器的工作由两个特殊功能寄存器控制。TMOD用于设置其工作方式;TCON用于...

    外接晶振为12MHz时,51单片机相关周期的具体值为:

    振荡周期=1/12us;
    状态周期=1/6us;
    机器周期=1us;
    指令周期=1~4us;

    51单片机定时/计数器的工作由两个特殊功能寄存器控制。TMOD用于设置其工作方式;TCON用于控制其启动和中断申请。
    1、工作方式寄存器TMOD
    工作方式寄存器TMOD用于设置定时/计数器的工作方式,低四位用于T0,高四位用于T1。其格式如下:
    在这里插入图片描述
    GATE是门控位, GATE=0时,用于控制定时器的启动是否受外部中断源信号的影响。只要用软件使TCON中的TR0或TR1为1,就可以启动定时/计数器工作;GATA=1时,要用软件使TR0或TR1为1,同时外部中断引脚INT0/1也为高电平时,才能启动定时/计数器工作。即此时定时器的启动条件,加上了INT0/1引脚为高电平这一条件。
    C/T :定时/计数模式选择位。C/T =0为定时模式;C/T =1为计数模式。
    M1M0:工作方式设置位。定时/计数器有四种工作方式。
    在这里插入图片描述
    控制寄存器TCON
    TCON的高4位用于控
    制定时/计数器的启动和中断申请。其格式如下:
    在这里插入图片描述
    TF1(TCON.7):T1溢出中断请求标志位。T1计数溢出时由硬件自动置TF1为1。CPU响应中断后TF1由硬件自动清0。T1工作时,CPU可随时查询TF1的状态。所以,TF1可用作查询测试的标志。TF1也可以用软件置1或清0,同硬件置1或清0的效果一样。
    TR1(TCON.6):T1运行控制位。TR1置1时,T1开始工作;TR1置0时,T1停止工作。TR1由软件置1或清0。所以,用软件可控制定时/计数器的启动与停止。
    TF0(TCON.5):T0溢出中断请求标志位,其功能与TF1类同。
    TR0(TCON.4):T0运行控制位,其功能与TR1类同。

    定时/计数器的工作方式

    1、方式1
    方式1的计数位数是16位,由TL0作为低8位,TH0
    作为高8位,组成了16位加1计数器 。计数个数与计数初值的关系为:X=2(16次方)-N

    2、方式2
    为自动重装初值的8位计数方式。 计数个数与计数初值的关系为:X=28-N
    工作方式2特别适合于用作较精确的脉冲信号发生器。所以串口通信处用此方式。

    计数器初值的计算
    机器周期也就是CPU完成一个基本操作所需要的时间。
    机器周期=1/单片机的时钟频率。
    51单片机内部时钟频率是外部时钟的12分频。也就是说当外部晶振的频率输入到单片机里面的时候要进行12分频。比如说你用的是12MHZ的晶振,那么单片机内部的时钟频率就是12/12MHZ,当你使用12MHZ的外部晶振的时候。机器周期=1/1M=1us。
    而我们定时1ms的初值是多少呢,1ms/1us=1000。也就是要计数1000个数,初值=65535-1000+1(因为实际上计数器计数到66636才溢出)=64536=FC18H

    串口通信
    比特率是每秒钟传输二进制代码的位数,单位是:位/秒(bps)。如每秒钟传送240个字符,而每个字符格式包含10位(1个起始位、1个停止位、8个数据位),这时的比特率为:
    10位×240个/秒 = 2400 bps

    SCON 是一个特殊功能寄存器,用以设定串行口的工作方式、接收/发送控制以及设置状态标志:在这里插入图片描述
    SM0和SM1为工作方式选择位,可选择四种工作方式:
    在这里插入图片描述
    SM2,多机通信控制位,主要用于方式2和方式3。当接收机的SM2=1时可以利用收到的RB8来控制是否激活RI(RB8=0时不激活RI,收到的信息丢弃;RB8=1时收到的数据进入SBUF,并激活RI,进而在中断服务中将数据从SBUF读走)。当SM2=0时,不论收到的RB8为0和1,均可以使收到的数据进入SBUF,并激活RI(即此时RB8不具有控制RI激活的功能)。通过控制SM2,可以实现多机通信。
    在方式0时,SM2必须是0。在方式1时,如果SM2=1,则只有接收到有效停止位时,RI才置1。
    REN,允许串行接收位。由软件置REN=1,则启动串行口接收数据;若软件置REN=0,则禁止接收

    TI,发送中断标志位。在方式0时,当串行发送第8位数据结束时,或在其它方式,串行发送停止位的开始时,由内部硬件使TI置1,向CPU发中断申请。在中断服务程序中,必须用软件将其清0,取消此中断申请。

    RI,接收中断标志位。在方式0时,当串行接收第8位数据结束时,或在其它方式,串行接收停止位的中间时,由内部硬件使RI置1,向CPU发中断申请。也必须在中断服务程序中,用软件将其清0,取消此中断申请。

    PCON中只有一位SMOD与串行口工作有关 :SMOD(PCON.7) 波特率倍增位。在串行口方式1、方式2、方式3时,波特率与SMOD有关,当SMOD=1时,波特率提高一倍。复位时,SMOD=0。

    80C51串行口的工作方式
    这里只介绍方式1:
    方式1是10位数据的异步通信口。TXD为数据发送引脚,RXD为数据接收引脚,传送一帧数据的格式如图所示。其中1位起始位,8位数据位,1位停止位。
    在这里插入图片描述
    在这里插入图片描述
    用软件置REN为1时,接收器以所选择波特率的16倍速率采样RXD引脚电平,检测到RXD引脚输入电平发生负跳变时,则说明起始位有效,将其移入输入移位寄存器,并开始接收这一帧信息的其余位。接收过程中,数据从输入移位寄存器右边移入,起始位移至输入移位寄存器最左边时,控制电路进行最后一次移位。当RI=0,且SM2=0(或接收到的停止位为1)时,将接收到的9位数据的前8位数据装入接收SBUF,第9位(停止位)进入RB8,并置RI=1,向CPU请求中断。

    方式1的波特率 =(2SMOD/32)·(T1溢出率)
    T1 溢出率 = fosc /{12×[256 -(TH1)]}

    在这里插入图片描述

    DS18B20温度传感器原理
    DS18B20温度转换规则
    DS18B20的核心功能是它可以直接读出数字的温度数值。温度传感器的精度为用户可编程的9,10,11或12位,分别以0.5℃,0.25℃,0.125℃和0.0625℃增量递增。在上电状态下默认的精度为12位。

    在这里插入图片描述
    这是12位转化后得到的12位数据,存储在DS18B20的两个8位的RAM中,高字节的前5位是符号位,如果测得的温度大于0,这5位为‘0’,只要将测到的数值乘以0.0625即可得到实际温度;如果温度小于0,这5位为‘1’,测到的数值需要先减1再取反再乘以0.0625即可得到实际温度。

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述
    (1).数据线拉到低电平“0”。
    (2).延时480微妙(该时间的时间范围可以从480到960微妙)。
    (3).数据线拉到高电平“1”。
    (4).延时等待80微妙。如果初始化成功则在15到60微妙时间内产生一个由DS18B20所返回的低电平“0”.根据该状态可以来确定它的存在,但是应注意不能无限的进行等待,不然会使程序进入死循环,所以要进行超时判断。
    (5).若CPU读到了数据线上的低电平“0”后,还要做延时,其延时的时间从发出的高电平算起(第(3)步的时间算起)最少要480微妙。

    读时序
    在这里插入图片描述
    (1).将数据线拉低“0”。
    (2).延时1微妙。
    (3).将数据线拉高“1”,释放总线准备读数据。
    (4).延时10微妙。
    (5).读数据线的状态得到1个状态位,并进行数据处理。
    (6).延时45微妙。
    (7).重复1~7步骤,直到读完一个字节。

    写时序
    在这里插入图片描述
    (1).数据线先置低电平“0”
    (2).延时15微妙。
    (3).按从低位到高位的顺序发送数据(一次只发送一位)。
    (4).延时60微妙。
    (5).将数据线拉到高电平。
    (6).重复1~5步骤,直到发送完整的字节。
    (7).最后将数据线拉高。

    #include<reg51.h>	
    #define LCD1602_DATAPINS P0
    
    typedef	 unsigned int	  uint;
    typedef unsigned char	 uchar;
    
    sbit LCD1602_E=P2^7;
    sbit LCD1602_RW=P2^5;
    sbit LCD1602_RS=P2^6;
    sbit DSPORT=P3^7;
    
    uchar CNCHAR[6] = "摄氏度";
    
    void LcdInit();
    void LcdWriteData(uchar dat);
    void LcdWriteCom(uchar com);
    void LcdDisplay(int);
    void UsartConfiguration();
    uchar init();
    void writebyte(uchar datas);
    void change_temper();
    void read_tempercom();
    uint read_temper();
    uchar readbyte();
    void DelayMs(unsigned int x);
    
    void main(void)	
    {
    	UsartConfiguration();
    	LcdInit();			 //初始化LCD1602
    	LcdWriteCom(0x88);	//写地址 80表示初始地址
    	LcdWriteData('C'); 
    	while(1)
    	{
    		LcdDisplay(read_temper());
    //		Delay1ms(1000);//1s钟刷一次
    	}				
    }
    
    void DelayMs(unsigned int x)   //0.14ms误差 0us
    {
     unsigned char i;
      while(x--)
     {
      for (i = 0; i<13; i++)
     {}
     }
    }
    
    void LcdDisplay(int temp) 	 //lcd显示
    {
        
      	unsigned char i, datas[] = {0, 0, 0, 0, 0}; //定义数组
    	float tp;  
    	if(temp< 0)				//当温度值为负数
      	{
    	  	LcdWriteCom(0x80);		//写地址 80表示初始地址
    		SBUF='-';//将接收到的数据放入到发送寄存器
    		while(!TI);			         //等待发送数据完成
    		TI=0;						 //清除发送完成标志位
    	    LcdWriteData('-');  		//显示负
    		//因为读取的温度是实际温度的补码,所以减1,再取反求出原码
    		temp=temp-1;
    		temp=~temp;
    		tp=temp;
    		temp=tp*0.0625*100+0.5;	
    		//留两个小数点就*100,+0.5是四舍五入,因为C语言浮点数转换为整型的时候把小数点
    		//后面的数自动去掉,不管是否大于0.5,而+0.5之后大于0.5的就是进1了,小于0.5的就
    		//算由?.5,还是在小数点后面。
     
      	}
     	else
      	{			
    	  	LcdWriteCom(0x80);		//写地址 80表示初始地址
    	    LcdWriteData('+'); 		//显示正
    		SBUF='+';//将接收到的数据放入到发送寄存器
    		while(!TI);			         //等待发送数据完成
    		TI=0;						 //清除发送完成标志位
    		tp=temp;//因为数据处理有小数点所以将温度赋给一个浮点型变量
    		//如果温度是正的那么,那么正数的原码就是补码它本身
    		temp=tp*0.0625*100+0.5;	
    		//留两个小数点就*100,+0.5是四舍五入,因为C语言浮点数转换为整型的时候把小数点
    		//后面的数自动去掉,不管是否大于0.5,而+0.5之后大于0.5的就是进1了,小于0.5的就
    		//算加上0.5,还是在小数点后面。
    	}
    	datas[0] = temp / 10000;
    	datas[1] = temp % 10000 / 1000;
    	datas[2] = temp % 1000 / 100;
    	datas[3] = temp % 100 / 10;
    	datas[4] = temp % 10;
    
    	LcdWriteCom(0x82);		  //写地址 80表示初始地址
    	LcdWriteData('0'+datas[0]); //百位 
    	SBUF = '0'+datas[0];//将接收到的数据放入到发送寄存器
    	while (!TI);			         //等待发送数据完成
    	TI = 0;
    	
    	LcdWriteCom(0x83);		 //写地址 80表示初始地址
    	LcdWriteData('0'+datas[1]); //十位
    	SBUF = '0'+datas[1];//将接收到的数据放入到发送寄存器
    	while (!TI);			         //等待发送数据完成
    	TI = 0;
    
    	LcdWriteCom(0x84);		//写地址 80表示初始地址
    	LcdWriteData('0'+datas[2]); //个位 
    	SBUF = '0'+datas[2];//将接收到的数据放入到发送寄存器
    	while (!TI);			         //等待发送数据完成
    	TI = 0;
    
    	LcdWriteCom(0x85);		//写地址 80表示初始地址
    	LcdWriteData('.'); 		//显示 ‘.’
    	SBUF = '.';//将接收到的数据放入到发送寄存器
    	while (!TI);			         //等待发送数据完成
    	TI = 0;
    
    	LcdWriteCom(0x86);		 //写地址 80表示初始地址
    	LcdWriteData('0'+datas[3]); //显示小数点  
    	SBUF = '0'+datas[3];//将接收到的数据放入到发送寄存器
    	while (!TI);			         //等待发送数据完成
    	TI = 0;
    
    	LcdWriteCom(0x87);		 //写地址 80表示初始地址
    	LcdWriteData('0'+datas[4]); //显示小数点 
    	SBUF = '0'+datas[4];//将接收到的数据放入到发送寄存器
    	while (!TI);			         //等待发送数据完成
    	TI = 0;
    	for(i=0; i<6; i++)
    	{
    	 	SBUF = CNCHAR[i];//将接收到的数据放入到发送寄存器
    		while (!TI);			         //等待发送数据完成
    		TI = 0;
    	}
    
    	 
    }
    
    
    void UsartConfiguration()
    {
    	SCON=0X50;			//设置为工作方式1
    	TMOD=0X20;			//设置计数器工作方式2
    	PCON=0X80;			//波特率加倍
    	TH1=0XF3;				//计数器初始值设置,注意波特率是4800的
    	TL1=0XF3;
    //	ES=1;						//打开接收中断
    //	EA=1;						//打开总中断
    	TR1=1;					//打开计数器
    }
    
    
    /****************
    温度传感器部分
    ****************/
    uchar init()
    {
    	uchar i=0;
    	DSPORT=0;
    	i = 70;				   //将总线拉低480us~960us
    	while(i--);//延时642us
    	DSPORT=1;				 //然后拉高总线,如果DS18B20做出反应会将在15us~60us后总线拉低
    	i=0;
    	while(DSPORT)		   //等待DS18B20拉低总线
    	{
    		 DelayMs(1);
    		 i++;		  
    		 if(i>5)
    		 {
    		 	return 0;			 //初始化失败
    		 }
    	}
    	return 1;
    }
    
    void writebyte(uchar datas)
    {
    	uchar i,j=1;
    	for(i=0;i<8;i++)
    	{
    		DSPORT=0;		   //每写入一位数据之前先把总线拉低1us
    		//j++;		  //此处延时1us,好像又影响不大
    		DSPORT=datas&0x01;
    		j=6;
    		while(j--); //延时68us,持续时间最少60us
    		DSPORT=1;		 //然后释放总线,至少1us给总线恢复时间才能接着写入第二个数值
    		datas>>=1;
    	}
    }
    
    uchar readbyte()
    {
    	uchar dat=0,temp;
    	uint i ,j;
    	for(i=0;i<8;i++)
    	{
    		DSPORT=0;	 //先将总线拉低1us
    		j++;
    		DSPORT=1;	//然后释放总线
    		j++;	  //此处延时有变化,等待6us
    		j++;
    		temp=DSPORT;	 //读取数据,从最低位开始读取
    		/*将byte左移一位,然后与上右移7位后的bi,注意移动之后移掉那位补0。*/
    		dat=(dat>>1)|(temp<<7);
    		j = 4;		//读取完之后等待48us再接着读取下一个数
    		while(j--);;			 //好像没影响
    	}
    	return dat;		//不是temp
    }
    
    void change_temper()
    {
    	init();
    	DelayMs(1);			   //无影响
    	writebyte(0xcc);		  //跳过ROM操作命令	
    	writebyte(0x44);		   //温度转换命令
    }
    
    void read_tempercom()
    {
       	init();
    	//DelayMs(1);
    	writebyte(0xcc);
    	writebyte(0xbe);		 //发送读取温度命令
    }
    
    uint read_temper()
    {
    	uchar tml,tmh;
    	uint t=0;
    	change_temper();			//先写入转换命令
    	read_tempercom();			//然后等待转换完后发送读取温度命令
    	tml=readbyte();				//读取温度值共16位,先读低字节
    	tmh=readbyte();				//再读高字节
    	t=tmh;
    	t<<=8;			//移8位
    	t=tml|t;
    
    	return t;
    }
    /********************
     LCD液晶部分
    ********************/
    
    void LcdWriteCom(uchar com)	  //写入命令
    {
    	LCD1602_E = 0;     //使能
    	LCD1602_RS = 0;	   //选择发送命令
    	LCD1602_RW = 0;	   //选择写入
    	
    	LCD1602_DATAPINS = com;     //放入命令
    	DelayMs(1);		//等待数据稳定
    
    	LCD1602_E = 1;	          //写入时序
    	DelayMs(5);	  //保持时间
    	LCD1602_E = 0;
    }
    
    void LcdWriteData(uchar dat)			//写入数据
    {
    	LCD1602_E = 0;	//使能清零
    	LCD1602_RS = 1;	//选择输入数据
    	LCD1602_RW = 0;	//选择写入
    
    	LCD1602_DATAPINS = dat; //写入数据
    	DelayMs(1);
    
    	LCD1602_E = 1;   //写入时序
    	DelayMs(5);   //保持时间
    	LCD1602_E = 0;
    }
    
    void LcdInit()						  //LCD初始化子程序
    {
     	LcdWriteCom(0x38);  //开显示
    	LcdWriteCom(0x0c);  //开显示不显示光标
    	LcdWriteCom(0x06);  //写一个指针加1
    	LcdWriteCom(0x01);  //清屏
    	LcdWriteCom(0x80);  //设置数据指针起点
    }
    

    LCD显示部分可以参考我一篇文章
    51单片机DS1302时钟LCD1602显示

    以上是我在学习过程中的一点总结,用的是普中的51单片机·。

    展开全文
  • 51单片机串口,是个全双工的串口发送数据的同时,还可以接收数据。 当串行发送完毕后,将在标志位 TI 置 1,同样,当收到了数据后,也会在 RI 置 1。 无论 RI 或 TI 出现了 1,只要串口中断处于开放状态,...

    51单片机的串口,是个全双工的串口,发送数据的同时,还可以接收数据。
    当串行发送完毕后,将在标志位 TI 置 1,同样,当收到了数据后,也会在 RI 置 1。
    无论 RI 或 TI 出现了 1,只要串口中断处于开放状态,单片机都会进入串口中断处理程序。
    在中断程序中,要区分出来究竟是发送引起的中断,还是接收引起的中断,然后分别进行处理。
    看到过一些书籍和文章,在串口收、发数据的处理方法上,很多人都有不妥之处。
    接收数据时,基本上都是使用“中断方式”,这是正确合理的。
    即:每当收到一个新数据,就在中断函数中,把 RI 清零,并用一个变量,通知主函数,收到了新数据。
    发送数据时,很多的程序都是使用的“查询方式”,就是执行 while(TI ==0); 这样的语句来等待发送完毕。
    这时,处理不好的话,就可能带来问题。
    看了一些网友编写的程序,发现有如下几条容易出错:
    1.有人在发送数据之前,先关闭了串口中断!等待发送完毕后,再打开串口中断。
    这样,在发送数据的等待期间内,如果收到了数据,将不能进入中断函数,也就不会保存的这个新收到的数据。
    这种处理方法,就会遗漏收到的数据。
    2.有人在发送数据之前,并没有关闭串口中断,当 TI = 1 时,是可以进入中断程序的。
    但是,却在中断函数中,将 TI 清零!
    这样,在主函数中的while(TI ==0);,将永远等不到发送结束的标志。
    3.还有人在中断程序中,并没有区分中断的来源,反而让发送引起的中断,执行了接收中断的程序。
    对此,做而论道发表自己常用的方法:
    接收数据时,使用“中断方式”,清除 RI 后,用一个变量通知主函数,收到新数据。
    发送数据时,也用“中断方式”,清除 TI 后,用另一个变量通知主函数,数据发送完毕。
    这样一来,收、发两者基本一致,编写程序也很规范、易懂。
    更重要的是,主函数中,不用在那儿死等发送完毕,可以有更多的时间查看其它的标志。


    实例:
    求一个PC与单片机串口通信的程序,要求如下:
    1、如果在电脑上发送以$开始的字符串,则将整个字符串原样返回(字符串长度不是固定的)。
    2、如果接收到1,则将P10置高电平,接收到0,P10置低电平。(用来控制一个LED)
    单片机是STC89C52RC/晶振11.0592/波特率要求是9600或4800。谢谢!
    问题补充:可能会将【$ABCD,123456,987654ccc,aasdasd,aaaa,sssd,4D】这样的字符串(字符串长度约为50-150个字符)传送给单片机,只能能原样返回。

    [cpp] view plain copy
    1. 最佳答案:  
    2. 下列程序,已经调试成功。  
    3. #include <REG52.H>  
    4. sbit LED = P1^0;  
    5. unsigned char UART_buff;  
    6. bit New_rec = 0, Send_ed = 1, Money = 0;  
    7. //----------------------------------------------  
    8. void main (void)  
    9. {  
    10.     SCON = 0x50;   //串口方式1, 8-n-1, 允许接收.  
    11.     TMOD = 0x20;   //T1方式2  
    12.     TH1 = 0xFD;    [url=]//9600bps@11.0592MHz[/url]  
    13.     TL1 = 0xFD;  
    14.     TR1 = 1;                          
    15.     ES  = 1;       //开中断.  
    16.     EA  = 1;  
    17.     while(Money == 0);    //等着交费,呵呵,等着接收$.  
    18.     while(1)  {   
    19.       if ((New_rec == 1) && (Send_ed == 1))  {  //如果收到新数据及发送完毕  
    20.         SBUF = UART_buff; //那就发送.  
    21.         New_rec = 0;  
    22.         Send_ed = 0;  
    23.     } }  
    24. }  
    25. //----------------------------------------------  
    26. void ser_int (void) interrupt 4   
    27. {  
    28.     if(RI == 1) {  //如果收到.  
    29.       RI = 0;      //清除标志.  
    30.       New_rec = 1;  
    31.       UART_buff = SBUF;  //接收.  
    32.       if(UART_buff == '1')  LED = 1;  
    33.       if(UART_buff == '0')  LED = 0;  
    34.       if(UART_buff == '$')  Money = 1;  
    35.     }  
    36.     else  {        //如果送毕.  
    37.       TI = 0;      //清除标志.  
    38.       Send_ed = 1;  
    39.     }  
    40. }   
    41. //----------------------------------------------  

    http://bbs.ednchina.com/BLOG_ARTICLE_3007162.HTM

    串口接收程序是基于串口中断的,单片机的串口每次接收到一字节数据产生一次中断,然后再读取某个寄存器就可以得到串口接收的数据了。然而在实际应用当中,基本上不会有单字节接收的情况。一般都是基于一定串口通信协议的多字节通信。在422或者485通信中,还可能是一个主机(一般是计算机)带多个从机(相应的有单片机的板卡)。这就要求我们的单片机能够在连续接收到的串口数据序列中识别出符合自己板卡对应的通信协议,来进行控制操作,不符合则不进行任何操作。简而言之就是,单片机要在一串数据中找到符合一定规律的几个字节的数据。

     

            先来说下怎样定串口协议吧。这个协议指的不是串口底层的协议,而是前面提到的数据帧协议。一般都是有帧头(2~3个字节吧),数据(长度根据需要),结束位(1位,有时候设计成校验字节,最简单的校验也就是前面所有数据求和)。

            比如0xaa 0x55 +(数据部分省略)+校验和(除了aa 55 之外数据的和),如果要是多板卡的话有时候还要在帧头后面加一个板选字节(相当于3字节帧头了)。

     

           第一次写串口接收程序的时候,我首先想到的就是定义一个全局变量(实际上最好是定义局部静态变量),初始值设置为0,然后每进一次中断+1,然后加到串口通信协议的长度的时候再清零。然后判断帧头、校验。写完了之后我自己都觉得不对,一旦数据错开了一位,后面就永远都接收不到数了。无奈看了一下前辈们的代码,跟我的思路差不多,只不过那个计数值跟接收到的数据时同时判断的,而且每次中断都要判断,一旦不对计数的那个变量就清零。

     

           废话少说,直接上一段代码让大家看看就明白了。(通信协议姑且按照简单的aa 55 一个字节数据 一个字节校验,代码是基于51单片机的)。接收成功则在中断程序中把串口接收成功标志位置1。

    [cpp] view plain copy
    1. 然后串口中断部分  
    2. void ser()interrupt 4  
    3. {  
    4. static unsigned char count;//串口接收计数的变量  
    5.   RI=0;//手动清某个寄存器,大家都懂的  
    6.   receive[count]=SBUF;  
    7.   if(count==0&&receive[count]==0xaa)//同时判断count跟收到的数据  
    8.   {  
    9.        count=1;  
    10.   }  
    11.   else if(count==1&&receive[count]==0x55)  
    12.   {  
    13.      count=2;  
    14.   }  
    15.   else if(count==2)  
    16.   {  
    17.        count++;  
    18.   }  
    19.   else if(count==3&&receive[count]== receive [2])//判断校验和,数据多的话是求//和,或者其他的校验方法,也可能是固定的帧尾  
    20.   {  
    21.     count=0;  
    22.      uart_flag =1;//串口接收成功标志,为1时在主程序中回复,然后清零  
    23.    ES=0;      //关中断,回复完了再ES=1;  
    24.   }  
    25.   else  
    26.   {  
    27.      count=0;//判断不满足条件就将计数值清零  
    28.   }  
    29. }  

    第一次做的串口大概就按照这个方法写完了(我后来看过其他的代码,有人用switch语句写的,逻辑跟这个也差不多,不过我还是感觉用if else来写清晰一些),

            不过在测试的时候发现了bug,如果数据帧发送一半,然后突然停止,再来重新发,就会丢失一帧的数据。比如先接受到aa 55,然后断了,再进来aa 55 01 01,就不受控制了。后来我也想到一个bug,如果在多设备通信中,属于其他设备的的帧数据最后一位是aa(或者最后两位为aa 55 ,或者最后3位为aa 55 板选),下一次通信的数据就接收不到了。

     

            当时对于数据突然中断的bug,没有想到很好的解决办法,不过这种情况几率极小,所以一直用这个方法写也没有问题。多设备通信最后一位恰好是aa的几率也很小,出问题的可能也很小。当时项目里面的控制数据跟校验恰好不可能出现aa,于是我把if(count==0&&receive[count]==0xaa)改成了if(receive[count]==0xaa)其他都没变,解决了,没有bug了。

     

            后来我又写了几次单片机程序,才想到了一些解决问题的方法——不过改天再接着写吧,太累了,明天还要上班呢。

     

            在后来的项目中,真的遇到了数据位跟校验位都可能出现aa的情况。我考虑到每次数据都是连续发送的(至少我们用labwindows做的上位机程序是这样的),成功接收到了一帧数据是要有一定时间回复的,也就是说如果接收到一半,但是很长时间没接收到数据,把计数值count清零就ok啦。涉及时间的问题自然要用定时器来实现啦。

    这次的通信协议如下,串口波特率19200,2个帧头aa 55 ,一个板选,6字节数据,一个校验字节(除帧头外其他数据的和)。


    [cpp] view plain copy
    1. 全局变量定义  
    2. unsigned char boardAddr;//板选地址,通过检测几个io引脚,具体怎么得到的就不写了,很简单的  
    3. unsigned char g_DatRev [10]={0};//接收缓存  
    4. bit retFlag=0;//为1代表串口接收到了一帧数据  
    5.    
    6.    
    7. 串口初始化函数,晶振22.1184  
    8.    
    9. void init_uart()  
    10. {  
    11.        SCON = 0x50;                 //串口方式1允许接收  
    12.        TMOD = 0x21;                //定时器1,方式2,8位自动重载,同时配置定时器0,工作方式1  
    13.        PCON = 0x80;                // 波特率加倍  
    14.        TH1 = 0xfa;  
    15.        TL1 = 0xfa;               //写入串口定时器初值  
    16.        TH0=(65536-2000)/256;    //写入定时器0初值,串口传输一个字节时间为(1/19200)*10,计算得0.52ms  
    17.        TL0=(65536-2000)%256;   //定时器0定时大约1ms多  
    18.     EA=1;  
    19.     ET0=1;                  //波特率:19200    22.1184M  初值:250(0xfa)  
    20.        IE |= 0x90;             
    21.     TR1 = 1;                     
    22. }  
    23.    
    24. 串口中断函数  
    25.    
    26. void UART_INT(void) interrupt 4  
    27. {   
    28.        static unsigned char count;//串口接收计数的变量  
    29.    
    30.               RI = 0;  
    31.               g_DatRev[count] = SBUF;  
    32.               if(g_DatRev[count]==0xaa&&count==0)             //帧头  
    33.            {  
    34.                    count=1;                                                   
    35.             }  
    36.                else if(count==1&&g_DatRev[count]==0x55)   
    37.             {    
    38.                          count=2;            
    39.             }  
    40.    
    41.                 else if (count==2&&g_DatRev[2] == boardAddr)  
    42.                {   
    43.                   CK = g_DatRev[count];  
    44.                    count=3;  
    45.                    
    46.                }  
    47.        
    48.                else if(count>=3&&count<9)  
    49.               {       
    50.                   
    51.                      CK += g_DatRev[count];  
    52.                     count ++;  
    53.             }  
    54.                
    55.            else if(count == 9&&CK==g_DatRev[9])  
    56.                      {       
    57.                          ES = 0;   
    58.                         retFlag = 1;  
    59.                          count=0;              
    60.                      }              
    61.               else  
    62.                {  
    63.                     count=0;  
    64.                }   
    65.              resettimer();  
    66.    
    67. }  
    68.    
    69. //判断count不为0的话就启动定时器  
    70. void resettimer()  
    71. {  
    72.        TR0=0;  
    73.        TH0=(65536-2000)/256;  
    74.        TL0=(65536-2000)%256;  
    75.        if(count!=0)  
    76.        {  
    77.               TR0=1;  
    78.        }  
    79. }  
    80.    
    81. 定时器中断函数  
    82. void T0_time()interrupt 1  
    83. {       
    84.     TR0=0;  
    85.        TH0=(65536-2000)/256;  
    86.        TL0=(65536-2000)%256;  
    87.        count=0;  
    88.    
    89. }  

    这种方法的确是本人自己想出来的,别人可能也这样做过,但我这个绝对不是抄袭或者模仿来的。这样写的确可以避免前面提到过的bug,不过代价是多用了一个定时器的资源,而且中断函数里的内容更多了,占用了更多的时间。

     

            要是能把第一种方法改进一下就好了,主要是那个校验不能为aa的那个bug,因为毕竟传输到一半突然断了的可能性是非常小的。后来我想第一个判断if(count==0&&receive[count]==0xaa)好像有点太严格了,考虑到第二字节的帧头,跟板选地址不可能为aa,于是把这个改写为if(count>=0&&count<=2&& receive[count]==0xaa),这样就把bug出现的几率降到了非常小,也只是在前一帧结尾数据恰好为 aa 55 板选 的时候才出现,几率是多少大家自己算一下吧,呵呵。这样我自己觉得,昨天写的那种方法改进到这个程度,应该算可以啦,反正我是很满意了。

     

            实际上我还想过其他的方法,比如缓存的数组采用移位寄存的方式。拿前面的4个字节的协议为例。

    [cpp] view plain copy
    1. void ser()interrupt 4  
    2. {  
    3.  unsigned char i;  
    4.   RI=0;  
    5.    
    6.   for(i=0;i<3;i++)  
    7.   {  
    8.      receive[i]=receive[i+1];  
    9.   }  
    10.   receive[3]=SBUF;  
    11.   if(reveive[0]==0xaa&&receive[1]==0x55&&receive[2]==receive[3])  
    12.   {  
    13.      ret_flag=1;  
    14.        ES = 0;    
    15.   }  
    16.    
    17. }  

    这段代码看上去可是简单明了,这样判断可是不错啊,同时判断帧头跟校验不会产生前面提到的bug。说实话当时我刚想出这种方法并写出来的时候,马上就被我给否了。那个for循环可真是很占时间的啊,延时函数都是这样写的。每次都循环一下,这延时太长,通信速度太快的话就不能接收到下一字节数据了。最要命的是这个时间的长度是随着通信协议帧的字节数增加而增加的,如果一次要接收几十个字节,肯定就玩完了。这种方法我一次都没用过。

     

            不过我居然又想出来了这种方法的改良措施,是前两天刚想出来的,呵呵,还没有实践过呢。

    下面代码的协议就按第二段程序(定时器清零的那个协议,一共10字节)

    全局变量

     

    [cpp] view plain copy
    1. bit ret_flag;  
    2. unsigned char receive[256]={0};  
    3. unsigned char boardaddress;  
    4.    
    5. 中断函数  
    6.    
    7. void ser()interrupt 4  
    8. {  
    9.    
    10.    
    11.   static unsigned char i=0;  
    12.   static unsigned char total=0;  
    13.   RI=0;  
    14.   receive[i]=SBUF;  
    15.   total=total-receive[i-7]+receive[i-1];  
    16.    
    17.   if(receive[i-9]==0xaa&&receive[i-8]==0x55  
    18.   &&receive[i-7]==boardaddress&&receive[i]==total  
    19.   )  
    20.   {  
    21.      ret_flag=1;  
    22.        ES = 0;    
    23.   }  
    24.   i++;  
    25.    
    26. }  


            之所以要定义256个长度的数组,就是为了能够让数组“首尾相接”。因为0 -1 = 255 , 255+1 = 0。而且我在计算校验的时候也改进了算法,不会因为数据长度的增加而增加计算校验值的时间。这种方法也是我不久前才想出来的,所以还没有经过实际的验证。上面的代码可能会有逻辑上的错误,如果真有错误,有网友看出来的话,请在下面留言告诉我。这个方法也是我原创的哦,别人也肯能会想到,不过我这个绝对不是抄袭别人的。

     

            上面的代码最大的缺点就是变量定义的太多了,太占ram资源了,编译的时候可能会出现错误,毕竟51单片机才128字节的ram(有的资源也很丰富的,比如c8051系列的),这一下子就是256字节的变量。不过对于资源多一些的单片机,这样写还是可以的。要是能有4bit在一起的数据类型就好了,呵呵,verilog代码里面是可以的,C语言里貌似不行啊。

     

            要想能在例如51单片机上运行,只能按照下面的折中方式了,也就是把i相关的量都与一个0x0f

     

    [cpp] view plain copy
    1. 全局变量  
    2.    
    3. bit ret_flag;  
    4. unsigned char receive[16]={0};// 可以考虑在定义时加上idata,毕竟还可能是32  
    5. //或者64长度的数组呢unsigned char idata receive[16]={0};  
    6.    
    7. unsigned char boardaddress;  
    8.    
    9. 中断函数  
    10.    
    11. void ser()interrupt 4  
    12. {  
    13.    
    14.    
    15.   static unsigned char i=0;  
    16.   static unsigned char total=0;  
    17.   RI=0;  
    18.   receive[i&0x0f]=SBUF;  
    19.   total=total-receive[(i-7)&0x0f]+receive[(i-1)&0x0f];  
    20.    
    21.   if(receive[(i-9)&0x0f]==0xaa&&receive[(i-8)&0x0f]==0x55  
    22.   &&receive[(i-7)&0x0f]==boardaddress&&receive[i&0x0f]==total  
    23.   )  
    24.   {  
    25.      ret_flag=1;  
    26.        ES = 0;    
    27.   }  
    28.   i++;  
    29.    
    展开全文
  • 基于PC与单片机串口通信的温度监控系统程序设计  1.系统介绍  1)本系统主要讲解基于PC与单片机串口通信的温度监控系统程序设计(如图1),上位机采用常用的PC机,下位机使用的是STC89C52...

    基于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 报警功能:上位机和下位机上均设置有报警功能,上位机中,采用语音或者警示灯报警,同时还有相关提示;下位机则采用报警灯和报警蜂鸣器提示;

    基于PC与单片机串口通信的温度监控系统程序设计 - koby - Koby Ou的博客
     图1
    基于PC与单片机串口通信的温度监控系统程序设计 - koby - Koby Ou的博客
     图2
    基于PC与单片机串口通信的温度监控系统程序设计 - koby - Koby Ou的博客
     图 3
    基于PC与单片机串口通信的温度监控系统程序设计 - koby - koby的博客
     图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)

      ----------------------------------|----------------------------------------------

      0x55                              |   Match ROM (指定匹配芯片)

      ----------------------------------|----------------------------------------------

      0xCC                              |   Skip  ROM(跳跃ROM指令)

      ----------------------------------|----------------------------------------------

      0xFO                              |   Search ROM(搜索芯片)

      ----------------------------------|----------------------------------------------

      0xEC                              |   Alarm Search(报警芯片搜索)               

    ===================================================================================

      储存器指令码                      |                                              

    ------------------------------------|----------------------------------------------

      0x4E                              |   Write Scratchpad(向RAM中写数据)           

      ----------------------------------|----------------------------------------------

      0xBE                              |   Read 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)  //DS18B20 begin change

    {

        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)

    {

     WORD w1,w2;

      if (wTime== 0) return;

     for(w1=wTime;w1>0;w1--)

      for(w2=100;w2>0;w2--);

    }

    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;

           sbit BEEP = P2^3; //蜂鸣器接单片机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为选择通讯速率:
     0=1200,1=2400,2=4800,3=9600,4=19.2k
    作者:Koby
    ****************************************************************************************/
    void RS232_Sel_BPS(BYTE bSel)
    {
        switch(bSel)
        {
           case 0:PCON&=0x7f;
                  TH1=T1MS_1200bps;   /* T1 use sio */
                  TL1=T1MS_1200bps;
                  break;
           case 1:PCON&=0x7f;
                  TH1=T1MS_2400bps;   /* T1 use sio */
                  TL1=T1MS_2400bps;
                  break;
           case 2:PCON&=0x7f;
                  TH1=T1MS_4800bps;   /* T1 use sio */
                  TL1=T1MS_4800bps;
                  break;
           case 3:PCON&=0x7f;
                  TH1=T1MS_9600bps;   /* T1 use sio */
                  TL1=T1MS_9600bps;
                  break;
           case 4:PCON|=0x80;
                  TH1=0xfd;   /* T1 use sio */
                  TL1=0xfd;
                  break;
        }
    }
    /****************************************************************************************
    函数名:void Init_RS232(void)
    功能:初始化串口
    作者:Koby
    ****************************************************************************************/
    void Init_RS232(void)
    {
       PCON&=0x7f;
       TH1=0xFD;   /* T1 use sio */
       TL1=0xFD;
       TMOD=0x20;  /* T1=MODE2,sio; T0=MODE1,16bit,use time */
       PS=1;       /* SIO int high  优先级 */
       ES=1;
       ET1=0;
       SM0=0;
       SM1=1;      /* SM0=0 SM1=1,mode1,10bit          */
       SM2=0;      /* data int,无校验(TB8=bit_duble偶) */
       TR1=1;
       REN=1;
       RI=0;
       TI=0;
       EA=1;
    }
    /****************************************************************************************
    函数名:void Send_Char_Com(unsigned char ch)  
    功能:向串口发送一个字符 
    作者:Koby
    ****************************************************************************************/
    void Send_Char_Com(unsigned char ch)  
    {
       SBUF=ch;
       while(!TI);
       TI=0;
    }
    /****************************************************************************************
    函数名:void Send_String_Com(unsigned char *str,unsigned int strlen)
    功能:向串口发送一个字符串,strlen为该字符串长度 
    作者:Koby
    ****************************************************************************************/
    void Send_String_Com(unsigned char *str,unsigned int strlen)
    {
       unsigned int k=0;
       do {
           Send_Char_Com(*(str + k));
           k++;
       } while(k < strlen);
    }
    /****************************************************************************************
    函数名:void serial () interrupt 4 using 2 
    功能:串口接收中断函数 
    作者:Koby
    ****************************************************************************************/
    void serial () interrupt 4 using 2 
    {
       if(RI){
           unsigned char ch;
           RI = 0;
           ch=SBUF;
           if(ch>127){
                _bCNT3=0;
                _rxBuf1[_bCNT3]=ch;
                _bCheckSum= ch-128;
           }else {
                _bCNT3++;
                _rxBuf1[_bCNT3]=ch;
                _bCheckSum ^= ch;
                if( (_bCNT3==(INBUF_LEN-1)) && (!_bCheckSum) {
                    _fgDataRead=1;  //如果串口接收的数据达到INBUF_LEN个,且校验没错, 
                                  //就置位取数标志 
                }
           }
       }
    }
    /****************************************************************************************
    函数名:void TMPControlAndRs232Test(void)
    功能:温度监控测试程序
    作者:Koby
    ****************************************************************************************/
    void TMPControlAndRs232Test(void)
    {
            WORD wTemp = 0 ,wCnt = 0;
            BYTE i;   
            //Init_RS232();  //初始化串口
            // RS232_Sel_BPS(3);
           TmpChange();
            wTemp = wGetTmp();
            while(1)
            {
    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)
    {
            Init_RS232();  //初始化串口
            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.在对话框中添加控件 向主对话框中添加两个编辑框,一个用于接收显示数据IDIDC_EDIT_RXDATA,另一个用于输入发送数据,IDIDC_EDIT_TXDATA,再添加一个按钮,功能是按一次就把发送编辑框中的内容发送一次,将其ID设为IDC_BUTTON_MANUALSEND。别忘记了将接收编辑框的Properties>Styles中把MiltilineVertical Scroll属性选上,发送编辑框若你想输入多行文字,也可选上Miltiline 

    5.添加串口事件消息处理函数OnComm() 打开ClassWizard->Message Maps,选择类CSCommTestDlg,选择IDC_MSCOMM1,双击消息OnComm,将弹出的对话框中将函数名改为OnComm,(好记而已)OK。

    这个函数是用来处理串口消息事件的,如每当串口接收到数据,就会产生一个串口接收数据缓冲区中有字符的消息事件,我们刚才添加的函数就会执行,我们在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.打开串口和设置串口参数 你可以在你需要的时候打开串口,例如在程序中做一个开始按钮,在该按钮的处理函数中打开串口。现在我们在主对话框的CSCommTestDlg::OnInitDialog()打开串口,加入如下代码:

    // 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.发送数据 先为发送按钮添加一个单击消息即BN_CLICKED处理函数,打开ClassWizard->Message Maps,选择类CSCommTestDlg,选择IDC_BUTTON_MANUALSEND,双击BN_CLICKED添加OnButtonManualsend()函数,并在函数中添加如下代码:

    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所示:

    基于PC与单片机串口通信的温度监控系统程序设计 - koby - Koby Ou的博客
     
    图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#



    展开全文
  • 利用单片机 实现DHT11温湿度传感器的温度,和湿度值,通过单片机串口发送给PC串口终端,并显示出来
  • DS18B20 51单片机串口调试程序,运用串口调试温度十分方便,适合初学者
  • 单片机串口控制+程序

    2020-07-30 23:31:54
    2.实现 串口控制温度模块使单片机显示板能显示当前温度。 3.实现串口控制一个继电器接风扇 4.以上串口的控制,利用单片机上所留的RX、TX口输入数据,实现控制(比如,我往TX口发送字母,于是小灯亮/灭,其他功能同上...
  • DS18B20温度采集+串口发送+模块化编程
  • 基于c51单片机的数据采集串口发送数据到pc的C程序
  • 51单片机每隔1s读取一次DS18B20温度传感器数据,将温度数据不断存储到SD存储卡中,同时有串口不断输出温度数据。带proteus仿真。
  • /*********************************************************************************...* 【程序功能】: 串口工作方式1,8位UART,比特率9600,接收串口数据,数码管以十 进制格式显示,并且把接收到的数据加1后...
  • 串口通信 (1)刚下载完程序,流水灯 (2)通过串口助手发送“ab01”,数码管显示温度,显示完当前温度后,将温度打印到串口助手上 (3)串口助手发送“ab02”,,蜂鸣器响 (4)执行完后都会回复最初的流水灯
  • 51单片机读取DS18b20温度传感器的值,通过无线串口向主机发送温度,并在在LCD1602显示三个从机温度。
  • 上位机作图软件 监测温度,单片机串口通信无压力 本帖最后由 zb12138 于 2018-11-11 13:11 编辑 功能 1、与串口调试助手一样,可设串口,自动扫描串口,波特率等等,可收发汉字 2、作图(散点,折线,曲线),可设x,y...
  • 温度采集串口发送

    2020-07-23 23:31:35
    51单片机温度采集用串口发送 串口接收,并将温度显示在LCD上 仿真和程序
  • 首先说下我所用到的主要器件器件:STC89C...//接上18B20温度传感器(另购)后数码管显示出当前温度 #include <reg52.h> #include <intrins.h> #include <AT24C08.h> #define uchar unsigned char ...
  • 单片机实现 LCD1602显示+串口接收温度
  • 一,DS18B20温度传感器 相关介绍参照此文 DS18B20介绍 二,程序源码: #include "reg51.h" #define uchar unsigned char #define uint unsigned int #define _r 0X0A #define _d 0X0D #define T1VALUE 0XF3 sbit DS...
  • 功能:计算机以十六进制发送数据单片机接收后以十进制在数码管上显示,并且将数据加一再发送到计算机 #include <reg52.h> #include <intrins.h> #define uint unsigned int #define uchar unsigned ...
1 2 3 4 5 ... 20
收藏数 1,760
精华内容 704
关键字:

单片机串口发送温度