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电平变低
}
文章评论(0条评论)
登录后参与讨论