原创 采用SHT11与ATMEG168制作温湿度传感器

2008-3-19 22:36 5627 8 8 分类: MCU/ 嵌入式

    SHT11是一款由瑞士Sensirion公司生产的智能型温湿度传感器,该传感器是数字式的输出接口,具有免调试、低功耗、高可靠性和全互换的特点。


    ATMEG168是一款便宜的低功耗8位单片机,应用非常广泛,又有免费的编译器GCC(WINAVR),在线编程工具也很便宜。


    我们将一步一步完成基于这两个芯片的温湿度传感器的设计。


    第一,查询资料


      1,中文简介:


http://www.7613.com/wen/wen_app/sensapp/l05032501.pdf


       2,已经有的原型设计:


 http://www.ondre.de/index.php?section=sht&PHPSESSID=qvaejoph47pql1k7cns8b90db6


    第二,分析工作量


    硬件的连接很方便,由于数字接口跟IIC不兼容,所以不能用硬件IIC来接口。软件部分,上面的原型设计中有c代码,只需要调试通过即可。


    第三,硬件设计


    参考已经有的原型设计,可以将SHT11的DATA和SCK接到mega168的任意两个双向I/O口上。485电路和mega168的外围电路可以参考各种经典示例电路。


    第四,SHT11驱动程序


    借鉴已有的驱动程序,下面贴出能用的源代码:驱动包括头文件和c文件。


     头文件如下:


//sht11.h
#ifndef _MB_MR_POUT_H
#define _MB_MR_POUT_H


#ifdef __cplusplus
PR_BEGIN_EXTERN_C
#endif


void ConnReset(void);
unsigned char measure(unsigned int *p_value, unsigned char *p_checksum, unsigned char mode);


enum {TEMP, HUMI};


#ifdef __cplusplus
PR_END_EXTERN_C
#endif
#endif
源代码C文件如下:


/* ----------------------- System includes ----------------------------------*/
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <util/delay.h>
#include <avr/interrupt.h>//////


/* ----------------------- Platform includes --------------------------------*/
#include <avr/io.h>


#include <Common.h>
#include <Utils.h>
#include <DrvSht11.h>


/* ----------------------- Defines ------------------------------------------*/
#define noACK 0
#define ACK 1


#define STATUS_REG_W  0x06
#define STATUS_REG_R  0x07
#define MEASURE_TEMP  0x03
#define MEASURE_HUMI  0x05
#define RESET         0x1e


/*
#define setREAD       outb(DDRB, 0x01); sbi(PORTB, 1)
#define setWRITE      outb(DDRB, 0x03)
#define SCK(i)        if (i) sbi(PORTB, 0); else cbi(PORTB, 0)
#define DATA(i)       if (i) sbi(PORTB, 1); else cbi(PORTB, 1)
*/



#define setREAD       DDRD |=(1<<PD3); PORTD|=(1<<PD4);DDRD &= ~(1<<PD4)
#define setWRITE      DDRD |=(1<<PD3); DDRD |=(1<<PD4)
#define SCK(i)        if (i) PORTD|=(1<<PD3); else PORTD &= ~(1<<PD3)
#define DATA(i)       if (i) PORTD|=(1<<PD4); else PORTD &= ~(1<<PD4)



/*#define setREAD       DDRD |=(1<<PD4); PORTD|=(1<<PD3);DDRD &= ~(1<<PD3)
#define setWRITE      DDRD |=(1<<PD4); DDRD |=(1<<PD3)
#define SCK(i)        if (i) PORTD|=(1<<PD4); else PORTD &= ~(1<<PD4)
#define DATA(i)       if (i) PORTD|=(1<<PD3); else PORTD &= ~(1<<PD3)
*/


//  enum {TEMP, HUMI};


/******************************************
* 名称    :  Init_Timer0
* 功能描述:  定时器0初始化函数
* 输入参量: 
* 输出参量:  无
* 调用子程: 
* 使用方法:  产生尽量长的定时,用来计算一段时间内是否有脉冲
--------------------------*/
/*void UART_Init() {
  outb(UBRR, UBRRVAL);  // Baudrate einstellen
  sbi(UCR,TXEN);        // TX aktivieren
}


void UART_SendChar(char s) {
  loop_until_bit_is_set(USR, UDRE);
  outb (UDR, s);
}


void UART_SendStr(char *s) {
  while (*s) {
    loop_until_bit_is_set(USR, UDRE);
    outb (UDR, *s++);
  }
}
*/
void delay_us(unsigned int us) {
  while (us)
  {
     _delay_us(10);
     us--;
   }
}



/******************************************
* 名称    :  延时
* 功能描述:  定时器0初始化函数
* 输入参量: 
* 输出参量:  无
* 调用子程: 
* 使用方法: 
--------------------------*/
void delay_ms(unsigned int ms) {
  unsigned int outer1, outer2;
  outer1 = 2000;
  while (outer1) {
    outer2 = 10000;
    while (outer2) {
      while ( ms ) ms--;
      outer2--;
    }
    outer1--;
  }
}



/******************************************
* 名称    :  TransStart
* 功能描述:  开始传输
* 输入参量: 
* 输出参量:  无
* 调用子程: 
* 使用方法:  产生尽量长的定时,用来计算一段时间内是否有脉冲
--------------------------*/
void TransStart(void)
{
  DATA(1);//数据位置1 
  SCK(0);//sck清0
  delay_us(1);//延时
  SCK(1);//sck置1
  delay_us(1);//延时
  DATA(0);//数据变低
  delay_us(1);//延时
  SCK(0);//sck降低
  delay_us(5);//延时
  SCK(1);//sck置高
  delay_us(1);//延时
  DATA(1);//数据置高
  delay_us(1);//延时
  SCK(0);//sck清0
  delay_us(1);//延时
}


/******************************************
* 名称    :  ConnReset
* 功能描述:  复位连接,data为高,不断翻转sck
* 输入参量: 
* 输出参量:  无
* 调用子程: 
* 使用方法: 
--------------------------*/
void ConnReset(void)
{
  unsigned int i;
  setWRITE;
  delay_us(1);//延时
  DATA(1); SCK(0);
  for(i=0; i<9; i++) {
    SCK(1);
 delay_us(1);//延时
    SCK(0);
 delay_us(1);//延时
  }
  delay_us(1);//延时
  TransStart();//发送开始信号
}


/******************************************
* 名称    :  GetDATA
* 功能描述:  获取数据,看这个位是高还是低
* 输入参量: 
* 输出参量:  无
* 调用子程: 
* 使用方法: 
--------------------------*/
unsigned int GetDATA()
{
  return bit_is_set(PIND, PD4);
}


/******************************************
* 名称    :  WriteByte
* 功能描述:  写字节
* 输入参量: 
* 输出参量:  无
* 调用子程: 
* 使用方法: 
--------------------------*/
unsigned char WriteByte(unsigned char value) {
  unsigned char i, error="0";
  setWRITE; 
  delay_us(1);//延时                //设置为写
  for(i=0x80;i>0;i/=2)
  {
    if (i & value) DATA(1);//先输出最高位
    else DATA(0);
 delay_us(1);//延时
    SCK(1);                 //产生一个下降沿
    delay_us(5);
    SCK(0);
 delay_us(1);//延时
  }
  delay_us(1);//延时
  setREAD;                  //设置为读
  delay_us(1);//延时
  SCK(1);                   //SCK设置为高
  delay_us(1);              //延时
  error="GetDATA"();          //获取SCK数据
  SCK(0); 
  delay_us(1);//延时                
  return error;
}



/******************************************
* 名称    :  ReadByte
* 功能描述:  读字节
* 输入参量: 
* 输出参量:  无
* 调用子程: 
* 使用方法: 
--------------------------*/
unsigned char ReadByte(unsigned char ack) {
  unsigned char i,val=0;
  setREAD;                 //设置为读
  delay_us(1);//延时
  for(i=0x80;i>0;i/=2)
  {
    SCK(1);                //置高
 delay_us(1);//延时
    if (GetDATA()) val=(val | i);  //获取数据
    SCK(0);                        //拉低
 delay_us(1);//延时
  }
  delay_us(1);//延时
  setWRITE;                        //设置为写
  delay_us(1);//延时
  DATA(!ack);
  delay_us(1);//延时
  SCK(1);
  delay_us(5);
  SCK(0);
  delay_us(1);//延时
  return val;
}



/******************************************
* 名称    :  Conv_byte
* 功能描述:  转换数据
* 输入参量: 
* 输出参量:  无
* 调用子程: 
* 使用方法: 
--------------------------*/
/*
void Conv_byte(unsigned char in, unsigned char *out)
{
  unsigned char a;
  a="in"&0xf0;
  a="a">>4;
  if(a>9) {
    out[0]=a-10+0x41;
  } else {
    out[0]=a+0x30;
  }
  a="in"&0x0f;
  if(a>9) {
    out[1]=a-10+0x41;
  } else {
    out[1]=a+0x30;
  }
}


void conv_int(int in, unsigned char * out) {
  unsigned int temp;
  unsigned char a;
  if(in>=0) {
    temp="in"&0xff00;
    a=(unsigned char)(temp>>8);
    conv_byte(a,out);
    a=(in&0x00ff);
    conv_byte(a,out+2);
  } else {
    *out='-';
    out++;
    in*=-1;
    temp="in"&0xff00;
    a=(unsigned char)(temp>>8);
    conv_byte(a,out);
    a=(in&0x00ff);
    conv_byte(a,out+2);
  }
}
*/


/******************************************
* 名称    :  Conv_byte
* 功能描述:  转换数据
* 输入参量: 
* 输出参量:  无
* 调用子程: 
* 使用方法: 
--------------------------*/
unsigned char measure(unsigned int *p_value, unsigned char *p_checksum, unsigned char mode) {
  unsigned char lsb,msb,check;
  unsigned char error="0";
  unsigned int i,t;


//cli();//////////////////////////////
  TransStart();//开始传输
  switch(mode) //发送温度或者湿度
  {
    case TEMP : error+=WriteByte(MEASURE_TEMP); break;
    case HUMI : error+=WriteByte(MEASURE_HUMI); break;
    default   : break;
  }
  setREAD;     //设置为读



  for(i=0;i<65535;i++)
  {
   _delay_ms(1);
    if (GetDATA()==0)
    break;//等待DATA电平变高
  }
  if (GetDATA())
  {
      error+=1;  //如果变高,加1
   }
 


 *p_value = ReadByte(ACK); //读字节
  *p_value<<=8;
  *p_value+= ReadByte(ACK);//再读一个字节
/*
*(p_value+1)  =ReadByte(ACK);    //read the first byte (MSB) 
  *(p_value)=ReadByte(ACK);    //read the second byte (LSB) 


*/
  *p_checksum = ReadByte(noACK);//读CRC
//sei();//////////////////////////////


  return error;
 
}



    更近一步的,如果为这个小系统增加modbus从协议栈的支持。那么这模块就可以变成modbus智能温湿度传感器模块,该模块中温度湿度和露点都分别对应一个寄存器。通过标准的modbus协议就可以对这个模块进行读数了。


 


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


在sht11的官方网站,有一个针对sht11的例子程序,这个程序针对的mcu是8051系列的cpu。当应用到avr上时候,需要注意延时。


特别的,在函数measure中,有个延时等待信号变低的语句:  for(i=0;i<65535;i++) if (GetDATA()==0) break;//等待DATA电平变低


如果avr跑10M的化,这个时间内不能转换完成,需要增加一条延时语句。如,可以写成这样:


  for(i=0;i<65535;i++)


{


   _delay_ms(1);////////////////////////////////////////////////////////////增加的延时语句


    if (GetDATA()==0) break;//等待DATA电平变低


}


 


 

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
我要评论
0
8
关闭 站长推荐上一条 /3 下一条