基于STM32的家用多功能温湿度计开发V1.0

阅读: 评论:0

基于STM32的家用多功能温湿度计开发V1.0

基于STM32的家用多功能温湿度计开发V1.0

基于STM32的家用多功能温湿度计设计V1.0

Als ich ging, sah ich eine schöne Kiste, und du sagtest, du könntest sie mir geben.
Von da an habe ich mich entschieden, dir so ein Geschenk zu machen, vielleicht das letzte für dich.
Pass auf dich auf!Komm in die Zukunft.


——人海茫茫,走走散散。
从开始设想到今天凌晨5:40的成功装配,
收获了很多知识。


硬件优化后

目录

    • 基于STM32的家用多功能温湿度计设计V1.0
    • 1.设计需求
    • 2.总体方案
    • 3.硬件设计
      • 3.1系统电路原理图
      • 3.2主机STM32F103C8T6及其接口电路
      • 3.3 测量电路
      • 3.4 锂电池充放电电路
    • 4.软件开发
      • 4.1 DS18B20数据读取
      • 4.2 DHT11数据读取
      • 4.3 DS1302数据读写
      • 4.4 OLED显示操作
    • 5.经费总结
    • 6.附录
      • 6.1主函数代码
      • 6.2 OLED显示代码
      • 6.3 DS18B20温度测量代码
      • 6.4 DHT11湿度测量代码
      • 6.5 DS1302实时时钟操作代码
    • 致谢

1.设计需求

  • 实现正常生活环境下温度测量与显示
  • 实现正常生活环境下空气湿度测量与显示
  • 实现电子时钟实时显示

2.总体方案

向前测控通道 主机及其接口 向后测控通道

考虑应用实际,本项目选用以下模块:

MCU主控芯片STM32F103C8T6 1温度传感器DS18B20 2湿度传感器DHT11 3实时时钟源DS1302 4显示 >>OLED_7P>> size(0.96)

*项目首先基于开发版测试系统,完成软件设计后,装载入新设计的目标硬件。

3.硬件设计

3.1系统电路原理图

第一版原理图:


硬件优化设计后原理图:


PCB设计


PCB加工


PCB焊接

3.2主机STM32F103C8T6及其接口电路


STM32F103C8T6是一款性能优越的主控芯片,是嵌入式设计的优秀选项。

3.3 测量电路

虽然DHT11自带温度测量功能,但其温度测量精度较低,因此,本设计采用DS18B20温度传感器测量高精度温度值;湿度值 采用DHT11直接测量。

本文设计、搭建了DHT11和DS18B20的面包板电路如下图所示。

3.4 锂电池充放电电路

4.软件开发

4.1 DS18B20数据读取

见附录

4.2 DHT11数据读取

见附录

4.3 DS1302数据读写

见附录

4.4 OLED显示操作

见附录

5.经费总结

器件单价/元
STM32F103C8T6 最小系统板37.72
OLED_P711.83
DHT115.61
DS18B205.41
DS13023.31
电池盒1.64
总计65.52 元

6.附录

6.1主函数代码

主函数

/*
Title:基于STM32的家用多功能温湿度计设计
Author:小呼呼哈哈哈
Date:2021-02-28
Das letzte Geschenk für Soffieg.
*/
/*main.c*/
#include "delay.h"
#include "sys.h"
#include "oled.h"
#include "usart.h"
#include "ds18b20.h"
#include "dht11.h"
#include "ds1302.h"int main(void)
{u8 t=0;double temp;u16 temper;int ten,ge,feng;u16 Year;u8  Month;u8  Day;u8  Hour;u8  Minute;u8  Second;u8  Week;u8  DHT11_humidity1=0,DHT11_humidity2=0;u8  DHT11_temper1=0,DHT11_temper2=0;delay_init();NVIC_Configuration();uart_init(115200);//9600?	OLED_Init();OLED_ColorTurn(0);//0正常显示,1 反色显示OLED_DisplayTurn(1);//0正常显示 1 屏幕翻转显示DHT11_Init();delay_ms(5);DS1302_Init();delay_ms(5);/*以下两函数调用 用以设置时间和开启掉电保护*/
//	DS1302_WriteTime();ds1302_Init_time();while(1){//DS18B20数据处理temp=DS18B20_Get_Temp();temper=temp*10;//温度位数分解ten = temper/1000;ge = temper%1000/100;feng = temper%1000%100/10;//温度显示OLED_ShowNum(0+5,42,ten,1,24,1);OLED_ShowNum(12+5,42,ge,1,24,1);OLED_ShowChar(24+5,40,'.',24,1);OLED_ShowNum(34+5,48,feng,1,16,1);OLED_ShowChar(44+5,40,'.',16,1);OLED_ShowChar(49+5,48,'C',16,1);//湿度数据处理 ,有采样周期要求		DHT11_Read_Data(&DHT11_humidity1,&DHT11_humidity2,&DHT11_temper1,&DHT11_temper2);			OLED_ShowNum(78-1,42,DHT11_humidity1,2,24,1);//湿度值OLED_ShowChar(102,48,'%',16,1);OLED_ShowString(112,48,"RH",16,1);//日期显示DS1302_GetTime();Year&#ar;Month&#h;Day=TimeData.day;Hour=TimeData.hour;Minute=TimeData.minute;Second=TimeData.second;Week=TimeData.week;//		OLED_ShowNum(0,0,Year,4,16,1);//显示年份OLED_ShowNum(80,0,Month,2,16,1);//显示月份OLED_ShowChar(100,0,'-',16,1);OLED_ShowNum(110,0,Day,2,16,1);//显示天OLED_ShowNum(0,5,Hour,2,32,1);//显示时OLED_ShowString(32,5,":",32,1);OLED_ShowNum(46,5,Minute,2,32,1);//显示分
//		OLED_ShowString(65,18,":",24,1);
//		OLED_ShowNum(128-24*2,18,Second,2,24,1);//显示秒OLED_ShowChinese(80,20,0,24,1);//显示“周”,24*24OLED_ShowChinese(101,20,Week,24,1);//显示“*”,24*24OLED_Refresh();delay_ms(10);					}
}

6.2 OLED显示代码

OLED显示函数

/*oled.c*/
#include "oled.h"
#include "stdlib.h"
#include "oledfont.h"  	 
#include "delay.h"u8 OLED_GRAM[144][8];//反显函数
void OLED_ColorTurn(u8 i)
{if(i==0){OLED_WR_Byte(0xA6,OLED_CMD);//正常显示}if(i==1){OLED_WR_Byte(0xA7,OLED_CMD);//反色显示}
}//屏幕旋转180度
void OLED_DisplayTurn(u8 i)
{if(i==0){OLED_WR_Byte(0xC8,OLED_CMD);//正常显示OLED_WR_Byte(0xA1,OLED_CMD);}if(i==1){OLED_WR_Byte(0xC0,OLED_CMD);//反转显示OLED_WR_Byte(0xA0,OLED_CMD);}
}void OLED_WR_Byte(u8 dat,u8 cmd)
{	u8 i;			  if(cmd)OLED_DC_Set();else OLED_DC_Clr();		  OLED_CS_Clr();for(i=0;i<8;i++){			  OLED_SCL_Clr();if(dat&0x80)OLED_SDA_Set();else OLED_SDA_Clr();OLED_SCL_Set();dat<<=1;   }				 		  OLED_CS_Set();OLED_DC_Set();   	  
}//开启OLED显示 
void OLED_DisPlay_On(void)
{OLED_WR_Byte(0x8D,OLED_CMD);//电荷泵使能OLED_WR_Byte(0x14,OLED_CMD);//开启电荷泵OLED_WR_Byte(0xAF,OLED_CMD);//点亮屏幕
}//关闭OLED显示 
void OLED_DisPlay_Off(void)
{OLED_WR_Byte(0x8D,OLED_CMD);//电荷泵使能OLED_WR_Byte(0x10,OLED_CMD);//关闭电荷泵OLED_WR_Byte(0xAE,OLED_CMD);//关闭屏幕
}//更新显存到OLED	
void OLED_Refresh(void)
{u8 i,n;for(i=0;i<8;i++){OLED_WR_Byte(0xb0+i,OLED_CMD); //设置行起始地址OLED_WR_Byte(0x00,OLED_CMD);   //设置低列起始地址OLED_WR_Byte(0x10,OLED_CMD);   //设置高列起始地址for(n=0;n<128;n++)OLED_WR_Byte(OLED_GRAM[n][i],OLED_DATA);}
}
//清屏函数
void OLED_Clear(void)
{u8 i,n;for(i=0;i<8;i++){for(n=0;n<128;n++){OLED_GRAM[n][i]=0;//清除所有数据}}OLED_Refresh();//更新显示
}//画点 
//x:0~127
//y:0~63
//t:1 填充 0,清空	
void OLED_DrawPoint(u8 x,u8 y,u8 t)
{u8 i,m,n;i=y/8;m=y%8;n=1<<m;if(t){OLED_GRAM[x][i]|=n;}else{OLED_GRAM[x][i]=~OLED_GRAM[x][i];OLED_GRAM[x][i]|=n;OLED_GRAM[x][i]=~OLED_GRAM[x][i];}
}//画线
//x1,y1:起点坐标
//x2,y2:结束坐标
void OLED_DrawLine(u8 x1,u8 y1,u8 x2,u8 y2,u8 mode)
{u16 t; int xerr=0,yerr=0,delta_x,delta_y,distance;int incx,incy,uRow,uCol;delta_x=x2-x1; //计算坐标增量 delta_y=y2-y1;uRow=x1;//画线起点坐标uCol=y1;if(delta_x>0)incx=1; //设置单步方向 else if (delta_x==0)incx=0;//垂直线 else {incx=-1;delta_x=-delta_x;}if(delta_y>0)incy=1;else if (delta_y==0)incy=0;//水平线 else {incy=-1;delta_y=-delta_x;}if(delta_x>delta_y)distance=delta_x; //选取基本增量坐标轴 else distance=delta_y;for(t=0;t<distance+1;t++){OLED_DrawPoint(uRow,uCol,mode);//画点xerr+=delta_x;yerr+=delta_y;if(xerr>distance){xerr-=distance;uRow+=incx;}if(yerr>distance){yerr-=distance;uCol+=incy;}}
}
//x,y:圆心坐标
//r:圆的半径
void OLED_DrawCircle(u8 x,u8 y,u8 r)
{int a, b,num;a = 0;b = r;while(2 * b * b >= r * r)      {OLED_DrawPoint(x + a, y - b,1);OLED_DrawPoint(x - a, y - b,1);OLED_DrawPoint(x - a, y + b,1);OLED_DrawPoint(x + a, y + b,1);OLED_DrawPoint(x + b, y + a,1);OLED_DrawPoint(x + b, y - a,1);OLED_DrawPoint(x - b, y - a,1);OLED_DrawPoint(x - b, y + a,1);a++;num = (a * a + b * b) - r*r;//计算画的点离圆心的距离if(num > 0){b--;a--;}}
}//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63
//size1:选择字体 6x8/6x12/8x16/12x24
//mode:0,反色显示;1,正常显示
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size1,u8 mode)
{u8 i,m,temp,size2,chr1;u8 x0=x,y0=y;if(size1==8)size2=6;else size2=(size1/8+((size1%8)?1:0))*(size1/2);  //得到字体一个字符对应点阵集所占的字节数chr1=chr-' ';  //计算偏移后的值for(i=0;i<size2;i++){if(size1==8){temp=asc2_0806[chr1][i];} //调用0806字体else if(size1==12){temp=asc2_1206[chr1][i];} //调用1206字体else if(size1==16){temp=asc2_1608[chr1][i];} //调用1608字体else if(size1==24){temp=asc2_2412[chr1][i];} //调用2412字体	else if(size1==32){temp=asc2_3216[chr1][i];} //调用3216字体else if(size1==40){temp=asc2_4020[chr1][i];} //调用4020字体	else return;for(m=0;m<8;m++){if(temp&0x01)OLED_DrawPoint(x,y,mode);else OLED_DrawPoint(x,y,!mode);temp>>=1;y++;}x++;if((size1!=8)&&((x-x0)==size1/2)){x=x0;y0=y0+8;}y=y0;}
}//显示字符串
//x,y:起点坐标  
//size1:字体大小 
//*chr:字符串起始地址 
//mode:0,反色显示;1,正常显示
void OLED_ShowString(u8 x,u8 y,u8 *chr,u8 size1,u8 mode)
{while((*chr>=' ')&&(*chr<='~'))//判断是不是非法字符!{OLED_ShowChar(x,y,*chr,size1,mode);if(size1==8)x+=6;else x+=size1/2;chr++;}
}//m^n
u32 OLED_Pow(u8 m,u8 n)
{u32 result=1;while(n--){result*=m;}return result;
}//显示数字
//x,y :起点坐标
//num :要显示的数字
//len :数字的位数
//size:字体大小
//mode:0,反色显示;1,正常显示
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size1,u8 mode)
{u8 t,temp,m=0;if(size1==8)m=2;for(t=0;t<len;t++){temp=(num/OLED_Pow(10,len-t-1))%10;if(temp==0){OLED_ShowChar(x+(size1/2+m)*t,y,'0',size1,mode);}else {OLED_ShowChar(x+(size1/2+m)*t,y,temp+'0',size1,mode);}}
}//显示汉字
//x,y:起点坐标
//num:汉字对应的序号
//mode:0,反色显示;1,正常显示
void OLED_ShowChinese(u8 x,u8 y,u8 num,u8 size1,u8 mode)
{u8 m,temp;u8 x0=x,y0=y;u16 i,size3=(size1/8+((size1%8)?1:0))*size1;  //得到字体一个字符对应点阵集所占的字节数for(i=0;i<size3;i++){if(size1==16){temp=Hzk1[num][i];}//调用16*16字体else if(size1==24){temp=Hzk2[num][i];}//调用24*24字体else if(size1==32)       {temp=Hzk3[num][i];}//调用32*32字体else if(size1==64){temp=Hzk4[num][i];}//调用64*64字体else return;for(m=0;m<8;m++){if(temp&0x01)OLED_DrawPoint(x,y,mode);else OLED_DrawPoint(x,y,!mode);temp>>=1;y++;}x++;if((x-x0)==size1){x=x0;y0=y0+8;}y=y0;}
}//num 显示汉字的个数
//space 每一遍显示的间隔
//mode:0,反色显示;1,正常显示
void OLED_ScrollDisplay(u8 num,u8 space,u8 mode)
{u8 i,n,t=0,m=0,r;while(1){if(m==0){OLED_ShowChinese(128,24,t,16,mode); //写入一个汉字保存在OLED_GRAM[][]数组中t++;}if(t==num){for(r=0;r<16*space;r++)      //显示间隔{for(i=1;i<144;i++){for(n=0;n<8;n++){OLED_GRAM[i-1][n]=OLED_GRAM[i][n];}}OLED_Refresh();}t=0;}m++;if(m==16){m=0;}for(i=1;i<144;i++)   //实现左移{for(n=0;n<8;n++){OLED_GRAM[i-1][n]=OLED_GRAM[i][n];}}OLED_Refresh();}
}//x,y:起点坐标
//sizex,sizey,图片长宽
//BMP[]:要写入的图片数组
//mode:0,反色显示;1,正常显示
void OLED_ShowPicture(u8 x,u8 y,u8 sizex,u8 sizey,u8 BMP[],u8 mode)
{u16 j=0;u8 i,n,temp,m;u8 x0=x,y0=y;sizey=sizey/8+((sizey%8)?1:0);for(n=0;n<sizey;n++){for(i=0;i<sizex;i++){temp=BMP[j];j++;for(m=0;m<8;m++){if(temp&0x01)OLED_DrawPoint(x,y,mode);else OLED_DrawPoint(x,y,!mode);temp>>=1;y++;}x++;if((x-x0)==sizex){x=x0;y0=y0+8;}y=y0;}}
}
//OLED的初始化
void OLED_Init(void)
{GPIO_InitTypeDef  GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);	 //使能A端口时钟GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_15;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHzGPIO_Init(GPIOA, &GPIO_InitStructure);	  //初始化GPIOAGPIO_SetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_15);OLED_RES_Clr();delay_ms(200);OLED_RES_Set();OLED_WR_Byte(0xAE,OLED_CMD);//--turn off oled panelOLED_WR_Byte(0x00,OLED_CMD);//---set low column addressOLED_WR_Byte(0x10,OLED_CMD);//---set high column addressOLED_WR_Byte(0x40,OLED_CMD);//--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)OLED_WR_Byte(0x81,OLED_CMD);//--set contrast control registerOLED_WR_Byte(0xCF,OLED_CMD);// Set SEG Output Current BrightnessOLED_WR_Byte(0xA1,OLED_CMD);//--Set SEG/Column Mapping     0xa0左右反置 0xa1正常OLED_WR_Byte(0xC8,OLED_CMD);//Set COM/Row Scan Direction   0xc0上下反置 0xc8正常OLED_WR_Byte(0xA6,OLED_CMD);//--set normal displayOLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)OLED_WR_Byte(0x3f,OLED_CMD);//--1/64 dutyOLED_WR_Byte(0xD3,OLED_CMD);//-set display offset	Shift Mapping RAM Counter (0x00~0x3F)OLED_WR_Byte(0x00,OLED_CMD);//-not offsetOLED_WR_Byte(0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequencyOLED_WR_Byte(0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/SecOLED_WR_Byte(0xD9,OLED_CMD);//--set pre-charge periodOLED_WR_Byte(0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 ClockOLED_WR_Byte(0xDA,OLED_CMD);//--set com pins hardware configurationOLED_WR_Byte(0x12,OLED_CMD);OLED_WR_Byte(0xDB,OLED_CMD);//--set vcomhOLED_WR_Byte(0x40,OLED_CMD);//Set VCOM Deselect LevelOLED_WR_Byte(0x20,OLED_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02)OLED_WR_Byte(0x02,OLED_CMD);//OLED_WR_Byte(0x8D,OLED_CMD);//--set Charge Pump enable/disableOLED_WR_Byte(0x14,OLED_CMD);//--set(0x10) disableOLED_WR_Byte(0xA4,OLED_CMD);// Disable Entire Display On (0xa4/0xa5)OLED_WR_Byte(0xA6,OLED_CMD);// Disable Inverse Display On (0xa6/a7) OLED_Clear();OLED_WR_Byte(0xAF,OLED_CMD);
}

oled.h函数

/*oled.h*/
#ifndef __OLED_H
#define __OLED_H #include "sys.h"
#include "stdlib.h"	//-----------------OLED端口定义---------------- #define OLED_SCL_Clr() GPIO_ResetBits(GPIOA,GPIO_Pin_0)//SCL
#define OLED_SCL_Set() GPIO_SetBits(GPIOA,GPIO_Pin_0)#define OLED_SDA_Clr() GPIO_ResetBits(GPIOA,GPIO_Pin_1)//SDA
#define OLED_SDA_Set() GPIO_SetBits(GPIOA,GPIO_Pin_1)#define OLED_RES_Clr() GPIO_ResetBits(GPIOA,GPIO_Pin_2)//RES
#define OLED_RES_Set() GPIO_SetBits(GPIOA,GPIO_Pin_2)#define OLED_DC_Clr()  GPIO_ResetBits(GPIOA,GPIO_Pin_3)//DC
#define OLED_DC_Set()  GPIO_SetBits(GPIOA,GPIO_Pin_3)#define OLED_CS_Clr()  GPIO_ResetBits(GPIOA,GPIO_Pin_4)//CS
#define OLED_CS_Set()  GPIO_SetBits(GPIOA,GPIO_Pin_4)#define OLED_CMD  0	//写命令
#define OLED_DATA 1	//写数据void OLED_ClearPoint(u8 x,u8 y);
void OLED_ColorTurn(u8 i);
void OLED_DisplayTurn(u8 i);
void OLED_WR_Byte(u8 dat,u8 mode);
void OLED_DisPlay_On(void);
void OLED_DisPlay_Off(void);
void OLED_Refresh(void);
void OLED_Clear(void);
void OLED_DrawPoint(u8 x,u8 y,u8 t);
void OLED_DrawLine(u8 x1,u8 y1,u8 x2,u8 y2,u8 mode);
void OLED_DrawCircle(u8 x,u8 y,u8 r);
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size1,u8 mode);
void OLED_ShowChar6x8(u8 x,u8 y,u8 chr,u8 mode);
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size1,u8 mode);
void OLED_ShowChinese(u8 x,u8 y,u8 num,u8 size1,u8 mode);
void OLED_Showlargenum(u8 x,u8 y,u8 num,u8 size1,u8 mode);
void OLED_ScrollDisplay(u8 num,u8 space,u8 mode);
void OLED_ShowPicture(u8 x,u8 y,u8 sizex,u8 sizey,u8 BMP[],u8 mode);
void OLED_Init(void);#endif

6.3 DS18B20温度测量代码

#include "ds18b20.h"
#include "delay.h"//复位DS18B20
void DS18B20_Rst(void)	   
{                 DS18B20_IO_OUT(); DS18B20_DQ_OUT=0; 	//拉低DQdelay_us(750);    	//拉低750usDS18B20_DQ_OUT=1; 	//DQ=1 delay_us(15);     	//15US
}
//等待DS18B20的回应
//返回1:未检测到DS18B20的存在
//返回0:存在
u8 DS18B20_Check(void) 	   
{   u8 retry=0;DS18B20_IO_IN();		 while (DS18B20_DQ_IN&&retry<200){retry++;delay_us(1);};	 if(retry>=200)return 1;else retry=0;while (!DS18B20_DQ_IN&&retry<240){retry++;delay_us(1);};if(retry>=240)return 1;	    return 0;
}
//从DS18B20读取一个位
//返回值:1/0
u8 DS18B20_Read_Bit(void) 	 
{u8 data;DS18B20_IO_OUT();	DS18B20_DQ_OUT=0; delay_us(2);DS18B20_DQ_OUT=1; DS18B20_IO_IN();	delay_us(12);if(DS18B20_DQ_IN)data=1;else data=0;	 delay_us(50);           return data;
}
//从DS18B20读取一个字节
//返回值:读到的数据
u8 DS18B20_Read_Byte(void)     
{        u8 i,j,dat;dat=0;for (i=1;i<=8;i++) {j=DS18B20_Read_Bit();dat=(j<<7)|(dat>>1);}						    return dat;
}
//写一个字节到DS18B20
//dat:要写入的字节
void DS18B20_Write_Byte(u8 dat)     {             u8 j;u8 testb;DS18B20_IO_OUT();	//SET PG11 OUTPUT;for (j=1;j<=8;j++) {testb=dat&0x01;dat=dat>>1;if (testb) {DS18B20_DQ_OUT=0;	// Write 1delay_us(2);                            DS18B20_DQ_OUT=1;delay_us(60);             }else {DS18B20_DQ_OUT=0;	// Write 0delay_us(60);             DS18B20_DQ_OUT=1;delay_us(2);                          }}
}
//开始温度转换
void DS18B20_Start(void) 
{   						               DS18B20_Rst();DS18B20_Check();	 DS18B20_Write_Byte(0xcc);	// skip romDS18B20_Write_Byte(0x44);	// convert
} //初始化DS18B20的IO口 DQ 同时检测DS的存在
//返回1:不存在
//返回0:存在    	 
u8 DS18B20_Init(void)
{GPIO_InitTypeDef  GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable,ENABLE);//把调试设置普通IO口GPIO_InitStructure.GPIO_Pin = dq;			GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIO_ds18b20, &GPIO_InitStructure);GPIO_SetBits(GPIO_ds18b20,dq);    //输出1DS18B20_Rst();return DS18B20_Check();
}  
//从ds18b20得到温度值
//精度:0.1C
//返回值:温度值 (-550~1250) 
short DS18B20_Get_Temp(void)
{u8 temp;u8 TL,TH;short tem;DS18B20_Start ();  			// ds1820 start convertDS18B20_Rst();DS18B20_Check();	 DS18B20_Write_Byte(0xcc);	// skip romDS18B20_Write_Byte(0xbe);	// convert	    TL=DS18B20_Read_Byte(); 	// LSB   TH=DS18B20_Read_Byte(); 	// MSB  if(TH>7){TH=~TH;TL=~TL; temp=0;					//温度为负  }else temp=1;				//温度为正	  	  tem=TH; 					//获得高八位tem<<=8;    tem+=TL;					//获得底八位tem=(float)tem*0.625;		//转换     if(temp)return tem; 		//返回温度值else return -tem;    
}

DS18B20.h函数

#ifndef _DS18B20_H
#define _DS18B20_H#include "sys.h"#define dq (GPIO_Pin_15) 
#define GPIO_ds18b20 GPIOA//IO方向设置
#define DS18B20_IO_IN()  {GPIOA->CRH&=0X0FFFFFFF;GPIOA->CRH|=8<<28;}
#define DS18B20_IO_OUT() {GPIOA->CRH&=0X0FFFFFFF;GPIOA->CRH|=3<<28;}//IO操作函数											   
#define	DS18B20_DQ_OUT PAout(15) //数据端口	 
#define	DS18B20_DQ_IN  PAin(15)  //数据端口	 u8 DS18B20_Init(void);//初始化DS18B20
short DS18B20_Get_Temp(void);//获取温度
void DS18B20_Start(void);//开始温度转换
void DS18B20_Write_Byte(u8 dat);//写入一个字节
u8 DS18B20_Read_Byte(void);//读出一个字节
u8 DS18B20_Read_Bit(void);//读出一个位
u8 DS18B20_Check(void);//检测是否存在DS18B20
void DS18B20_Rst(void);//复位DS18B20 #endif

6.4 DHT11湿度测量代码

#include "DHT11.h"
#include "delay.h"GPIO_InitTypeDef GPIO_InitStructure;	//后面会改变输入输出状态static void GPIO_SETOUT(void);
static void GPIO_SETIN(void);
static u8 DHT11_Check(void);/**********************************************
函数名:static void DHT11_Rst(void)
参数说明:无
返回值:无
函数作用:主机发送开始信号
***********************************************/
static void DHT11_Rst(void)
{                 GPIO_SETOUT();											//配置成输出模式GPIO_ResetBits(DHT11_IO,DHT11_PIN); //拉低数据线delay_ms(20);    										//拉低至少18msGPIO_SetBits(DHT11_IO,DHT11_PIN); 	//拉高数据线 delay_us(30);     									//主机拉高20~40usGPIO_ResetBits(DHT11_IO,DHT11_PIN);
}/**********************************************
函数名:u8 DHT11_Init(void)
参数说明:无
返回值:u8 ,返回1代表初始化成功,0则失败
函数作用:配置IO口,并发送开始信号
***********************************************/
u8 DHT11_Init(void){//IO口配置RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//换IO口需要修改GPIO_InitStructure.GPIO_Pin = DHT11_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出,如果需要考虑到IC的电流驱动能力时要接上拉电阻(5K)GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;GPIO_Init(DHT11_IO,&GPIO_InitStructure);DHT11_Rst();//发送开始信号return DHT11_Check();//检测DHT11的响应
}/**********************************************
函数名:static void GPIO_SETOUT(void)
参数说明:无
返回值:无
函数作用:配置IO口为推挽输出模式
***********************************************/
static void GPIO_SETOUT(void)
{GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出,如果需要考虑到IC的电流驱动能力时要接上拉电阻(5K)GPIO_Init(DHT11_IO,&GPIO_InitStructure);}/**********************************************
函数名:static void GPIO_SETIN(void)
参数说明:无
返回值:无
函数作用:配置IO口为浮空输入模式
***********************************************/
static void GPIO_SETIN(void)
{GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入模式GPIO_Init(DHT11_IO,&GPIO_InitStructure);
}/**********************************************
函数名:static u8 DHT11_Check(void)
参数说明:无
返回值:检测到回应-->返回1,否则0
函数作用:检测DHT11的响应信号
***********************************************/
static u8 DHT11_Check(void) 	   
{   u8 retry=0;GPIO_SETIN();			//设置为输入模式	while (!GPIO_ReadInputDataBit(DHT11_IO,DHT11_PIN) && retry<100)//DHT11会拉低40~50us{retry++;delay_us(1);}if(retry >= 100)	//超时未响应/未收到开始信号,退出检测return 0;else retry = 0;while (GPIO_ReadInputDataBit(DHT11_IO,DHT11_PIN) && retry<100)//DHT11拉低后会再次拉高40~50us{retry++;delay_us(1);}if(retry>=100)		//超时,DHT11工作出错,退出检测return 0;return 1;					//设备正常响应,可以正常工作
}/**********************************************
函数名:static u8 DHT11_Read_Bit(void)
参数说明:无
返回值:返回从DHT11上读取的一个Bit数据
函数作用:从DHT11上读取一个Bit数据
***********************************************/
static u8 DHT11_Read_Bit(void)
{u8 retry = 0;//DHT11的Bit开始信号为12-14us低电平while(GPIO_ReadInputDataBit(DHT11_IO,DHT11_PIN) && retry<100)//等待变为低电平(等待Bit开始信号){retry++;delay_us(1);}retry = 0;while(!GPIO_ReadInputDataBit(DHT11_IO,DHT11_PIN) && retry<100)//等待变高电平(代表数据开始传输){retry++;delay_us(1);}delay_us(30);//等待30us//0信号为26-28us,1信号则为116-118us,所以说超过30us去读取引脚状态就可以知道传输的值了if(GPIO_ReadInputDataBit(DHT11_IO,DHT11_PIN)) return 1;else return 0;		   
}/***********************************************************************
函数名:static u8 DHT11_Read_Byte(void)
参数说明:无
返回值:返回从DHT11上读取的一个byte数据
函数作用:从DHT11上读取一个byte数据
************************************************************************/
static u8 DHT11_Read_Byte(void)    
{        u8 i,dat;dat=0;for (i=0;i<8;i++) {dat<<=1; dat|=DHT11_Read_Bit();}	return dat;
}/**************************************************************************
函数名:u8 DHT11_Read_Data(u8 *temp,u8 *humi)
参数说明:temp:用于存放温度值(范围:0~50°),humi:用于存放湿度值(范围:20%~90%)
返回值:1:成功读取数据,0:读取数据出错
函数作用:从DHT11上读取温湿度数据(这里省略小数值)
***************************************************************************/
u8 DHT11_Read_Data(u8 *humi1,u8 *humi2,u8 *temp1,u8 *temp2)
{        u8 buf[5];u8 i;DHT11_Rst();if(DHT11_Check()==1)	//设备响应正常{for(i=0;i<5;i++)//读取40位数据{buf[i]=DHT11_Read_Byte();}if((buf[0]+buf[1]+buf[2]+buf[3])==buf[4])//进行校验{*humi1=buf[0];*humi2=buf[1];*temp1=buf[2];*temp2=buf[3];}}else return 0;		//设备未成功响应,返回0return 1;					//读取数据成功返回1
}

dht11.h头文件

#ifndef __DHT11_H
#define __DHT11_H
#include "stm32f10x.h"
#include "delay.h"/* 设置GPIO脚,默认为PB7 */
#define DHT11_IO 		GPIOB
#define DHT11_PIN		GPIO_Pin_7/* 初始化函数,如果DHT11存在响应则返回1,否则0 */
u8 DHT11_Init(void);
/* 从DHT11读取数据,没有小数部分 */
u8 DHT11_Read_Data(u8 *humi1,u8 *humi2,u8 *temp1,u8 *temp2);#endif

6.5 DS1302实时时钟操作代码

#include "DS1302.h"
#include "delay.h"
#include "usart.h"
/*
DS1302接口:GPIOA_5 ->DS1302_RSTGPIOA_6 ->DS1302_DATGPIOA_7 ->DS1302_CLK
*/
struct TIMEData TimeData;//DS1302地址定义
#define DS1302_SEC_ADDR           0x80		//秒数据地址
#define DS1302_MIN_ADDR           0x82		//分数据地址
#define DS1302_HOUR_ADDR          0x84		//时数据地址
#define DS1302_DAY_ADDR           0x86		//日数据地址
#define DS1302_MONTH_ADDR         0x88		//月数据地址
#define DS1302_WEEK_ADDR          0x8a		//星期数据地址
#define DS1302_YEAR_ADDR          0x8c		//年数据地址
#define DS1302_CONTROL_ADDR       0x8e		//控制数据地址
#define DS1302_CHARGER_ADDR       0x90 		//充电功能地址			 
#define DS1302_CLKBURST_ADDR      0xbe#define WRITE_FLAG_ADDR 0xc0
#define READ_FLAG_ADDR  0xc1
#define FLAG_VAL        0x3a//初始时间定义
u8 time_buf[8] = {0x20,0x21,0x03,0x04,0x04,0x43,0x00,0x04};//初始时间2021年3月4日04点**分00秒 星期四
u8 readtime[15];//当前时间
u8 sec_buf=0;  //秒缓存
u8 sec_flag=0; //秒标志位//DS1302初始化函数
void DS1302_Init() 
{/*1.GPIOA时钟*/RCC->APB2ENR |= 1<<2;/*2. 配置GPIOA_5/6/7模式*/GPIOA->CRL &= 0X000FFFFF;GPIOA->CRL |= 0X33300000;GPIOA->ODR |=1<<6;//printf("DS1302_Init OK!n");}
//向DS1302写入一字节数据
void DS1302_WriteByte(u8 addr,u8 data) 
{u8 i;DS1302_RST=0; //禁止数据传输 !!!这条很重要DS1302_CLK=0; //确保写数据前SCLK为低电平DS1302_RST=1;	//启动DS1302总线	DS1302_OutPut_Mode();addr=addr&0xFE;  //最低位置零,寄存器0位为0时写,为1时读for(i=0;i<8;i++) //写入目标地址:addr{if (addr&0x01) DS1302_OUT=1;else DS1302_OUT=0;DS1302_CLK=1; //时钟上升沿写入数据DS1302_CLK=0;addr=addr>>1;}	for (i=0;i<8;i++) //写入数据:data{if(data&0x01) DS1302_OUT=1;else DS1302_OUT=0;DS1302_CLK=1;    //时钟上升沿写入数据DS1302_CLK=0;data = data >> 1;}DS1302_CLK=1;    // 将时钟电平置于高电平状态 ,处于已知状态DS1302_RST=0;	//停止DS1302总线
}//从DS1302读出一字节数据
u8 DS1302_ReadByte(u8 addr) 
{u8 i,temp;	DS1302_RST=0; //这条很重要DS1302_CLK=0; //先将SCLK置低电平,确保写数居前SCLK被拉低DS1302_RST=1; //启动DS1302总线DS1302_OutPut_Mode();//写入目标地址:addraddr=addr|0x01; //最低位置高,寄存器0位为0时写,为1时读for(i=0;i<8;i++) {if (addr&0x01) DS1302_OUT=1;else DS1302_OUT=0;DS1302_CLK=1; //写数据DS1302_CLK=0;addr = addr >> 1;}	//从DS1302读出数据:tempDS1302_InPut_Mode();for(i=0;i<8;i++){temp=temp>>1;if (DS1302_IN) temp|=0x80;else temp&=0x7F;DS1302_CLK=1;DS1302_CLK=0;}	DS1302_CLK=1;  //将时钟电平置于已知状态DS1302_RST=0;	//停止DS1302总线return temp;
}
//向DS1302写入时钟数据,用于时间校准修改
void DS1302_WriteTime() 
{DS1302_WriteByte(DS1302_CONTROL_ADDR,0x00);       //关闭写保护DS1302_WriteByte(WRITE_FLAG_ADDR,FLAG_VAL); 			//写入已经设置时间标记,新增的DS1302_WriteByte(DS1302_SEC_ADDR,0x80);           //暂停时钟 //DS1302_WriteByte(DS1302_CHARGER_ADDR,0xa9);     //涓流充电 DS1302_WriteByte(DS1302_YEAR_ADDR,time_buf[1]);   //年 DS1302_WriteByte(DS1302_MONTH_ADDR,time_buf[2]);  //月 DS1302_WriteByte(DS1302_DAY_ADDR,time_buf[3]);    //日 DS1302_WriteByte(DS1302_HOUR_ADDR,time_buf[4]);   //时 DS1302_WriteByte(DS1302_MIN_ADDR,time_buf[5]);    //分DS1302_WriteByte(DS1302_SEC_ADDR,time_buf[6]);    //秒DS1302_WriteByte(DS1302_WEEK_ADDR,time_buf[7]);	  //周 DS1302_WriteByte(DS1302_CHARGER_ADDR,0xA5);//打开充电功能 选择2K电阻充电方式DS1302_WriteByte(DS1302_CONTROL_ADDR,0x80);//打开写保护     
}
//从DS1302读出时钟数据
void DS1302_ReadTime(void)  
{time_buf[1]=DS1302_ReadByte(DS1302_YEAR_ADDR);          //年 time_buf[2]=DS1302_ReadByte(DS1302_MONTH_ADDR);         //月 time_buf[3]=DS1302_ReadByte(DS1302_DAY_ADDR);           //日 time_buf[4]=DS1302_ReadByte(DS1302_HOUR_ADDR);          //时 time_buf[5]=DS1302_ReadByte(DS1302_MIN_ADDR);           //分 time_buf[6]=(DS1302_ReadByte(DS1302_SEC_ADDR))&0x7f;    //秒,屏蔽秒的第7位,避免超出59time_buf[7]=DS1302_ReadByte(DS1302_WEEK_ADDR);          //周 	
}void ds1302_Init_time(void)
{if(DS1302_ReadByte(READ_FLAG_ADDR)!= FLAG_VAL){DS1302_WriteTime();}else printf("OK!");
}
//主函数
void DS1302_GetTime()
{ DS1302_ReadTime(); //读取时间ar=(time_buf[0]>>4)*1000+(time_buf[0]&0x0F)*100+(time_buf[1]>>4)*10+(time_buf[1]&0x0F); //计算年份h=(time_buf[2]>>4)*10+(time_buf[2]&0x0F);  //计算月份TimeData.day=(time_buf[3]>>4)*10+(time_buf[3]&0x0F);    //计算日期TimeData.hour=(time_buf[4]>>4)*10+(time_buf[4]&0x0F);   //计算小时TimeData.minute=(time_buf[5]>>4)*10+(time_buf[5]&0x0F); //计算分钟TimeData.second=(time_buf[6]>>4)*10+(time_buf[6]&0x0F); //计算秒钟TimeData.week=(time_buf[7]&0x0F);                       //计算星期}

DS1302实时时钟头文件

#ifndef DS1302_H
#define DS1302_H
#include "stm32f10x.h"
#include "sys.h"
//DS1302引脚定义,可根据实际情况自行修改端口定义
#define DS1302_OutPut_Mode() {GPIOA->CRL &= 0xF0FFFFFF;GPIOA->CRL |= 0x03000000;}
#define DS1302_InPut_Mode()  {GPIOA->CRL &= 0xF0FFFFFF;GPIOA->CRL |= 0x08000000;}#define DS1302_IN  PAin(6)
#define DS1302_OUT PAout(6)
#define DS1302_RST PAout(5)
#define DS1302_CLK PAout(7)struct TIMEData
{u16 year;u8  month;u8  day;u8  hour;u8  minute;u8  second;u8  week;
};
extern struct TIMEData TimeData;
extern u8 readtime[15];
void DS1302_Init();
void DS1302_WriteByte(u8 addr,u8 data);
u8   DS1302_ReadByte(u8 addr);
void DS1302_WriteTime();
void DS1302_ReadTime(void);
void ds1302_Init_time(void);
void DS1302_GetTime();#endif

致谢

感谢各位前辈对相关程序的开源,感谢我们的相遇。

本文发布于:2024-02-01 11:28:35,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170675811636277.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:温湿度   多功能
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23