智能温度计的设计
设计内容
1)一路温度检测,量程为-40℃~+125℃,误差≤±0.5℃;
2)温度3位数码显示(XX.X℃);
3)开机自检;
4)配简单键盘,如温度上、下限临界报警值设置;
5)配置通信接口,上位机显示温度曲线。
单片机(32、51)、温度传感器()、键盘、LCD、
我们的基本思路是DS180B20温度传感器采集温度信号,经内部ADC转换为数字信号,输出到MCU,通过按键设置温度传感器报警的上下限,并通过USART串口通讯将温度信号传入上位机中,然后温度显示通过0.96寸OLED显示,报警是通过STM32mini自带的LED灯显示报警。
1.OLED显示屏
首先做的是OLED显示屏的显示,具体可以参考基于stm32的OLED温湿度显示_stm32温湿度显示_IT23131的博客-CSDN博客
完成基本的显示之后我采用了多级菜单显示具体效果如下图所示:
多级菜单的实现主要通过构建一个结构体,然后通过按键的触发实现界面的转换,本项目中,使用到了3个按键:下一个(next),确定(enter),退出(back)。所以,接下首先定义一个结构体,结构体中一共有5个变量(3+2),分别为:当前索引序号(current),向下一个(next),确定(enter),退出(back),当前执行函数(void)。
duoji.h
#ifndef __MENU_H
#define __MENU_H
#include "stm32f10x.h"
#define u8 unsigned char
//#define KEY0 HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_4) //KEY0
//#define KEY1 HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_3) //
//#define WK_UP HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) //
#define KEY0 GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_5)//低电平有效
#define KEY1 GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_15)//低电平有效
#define WK_UP GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)//高电平有效
#define KEY0_PRES 1 //KEY0
#define KEY1_PRES 2 //KEY1
#define WKUP_PRES 3 //WK_UP
typedef struct
{
u8 current; //当前状态索引号
u8 next; //向下一个
u8 enter; //确定
u8 back; //退出
void (*current_operation)(void); //当前状态应该执行的操作
} Menu_table;
//界面UI
void home();
void Temperature();
void Maxtem(); //设置最大警告温度值
void Mintem(); //设置最低警告温度值
void KEY_Init(void);//IO???
void Menu_key_set(void);
u8 KEY_Scan(u8 mode);
#endif
注意代码部分引入了很多头文件是和各个模块有关系的如果读者不需要自主删除就好。
duoji.c
#include "duoji.h"
#include "oled.h"
#include "ds18b20.h"
#include "gui.h"
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "ds18b20.h"
#include "Key.h"
#include "led.h"
//UI界面
//主页
/****************************************************/
//UI库
/****************************************************/
void KEY_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC,ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;//PA15
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStructure);//???GPIOA15
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;//PC5
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOC, &GPIO_InitStructure);//???GPIOC5
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;//PA0
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
GPIO_Init(GPIOA, &GPIO_InitStructure);//GPIOA.0
}
void (*current_operation_index)();
Menu_table table[30]=
{
{0,1,0,0,(*home)}, //一级界面(主页面)
{1,2,1,0,(*Setting)}, //温度界面
{2,3,2,0,(*Palygame)}, //上限温度设置界面
{3,1,3,0,(*Temperature)}, //下限温度设置界面
};
uint8_t func_index = 0; //主程序此时所在的程序索引值
void Menu_key_set(void)
{
if((KEY_Scan(0) == KEY0_PRES) && (func_index != 4))
{
func_index=table[func_index].next; //按键next按下后的索引号
printf("1");
OLED_Clear(0);
delay_ms(10);
}
if((KEY_Scan(0) == KEY1_PRES) && (func_index != 4))
{
func_index=table[func_index].enter; //按下enter后的索引号
OLED_Clear(0);
printf("2");
delay_ms(10);
}
if(KEY_Scan(0) == WKUP_PRES)
{
func_index=table[func_index].back; //按键back后的索引号
OLED_Clear(0);
printf("3");
delay_ms(10);
}
//printf("{tem:%d\r\n}",func_index);
current_operation_index=table[func_index].current_operation; //执行当前索引号对应的功能函数
(*current_operation_index)();//执行操作函数
}
//初始界面
void home()
{
GUI_ShowCHinese(10,0,16,"智能温度系统:",1);
GUI_ShowCHinese(20,20,16,"感谢使用",1);
GUI_ShowCHinese(15,40,16,"南理彭于晏",1);
}
u8 data=0; //记录输入最大温度上限的数
u8 data1=0; //记录输入最小温度上限的数
void Temperature()
{
u8 t;
float temperate;
float temperate1;
float temperate2;
float temperate3;
while(1)
{
OLED_Clear(0);
while(DS18B20_Init()) //DS18B20初始化
{
GUI_ShowString(0,15,"DS18B20 Error",16,1);
delay_ms(200);
}
OLED_Clear(0);
GUI_ShowString(15,0,"DS18B20 OK",16,1);
GUI_ShowCHinese(20,20,16,"当前温度:",1);
while(!DS18B20_Init())
{
// 界面设计
// OLED界面显示温度
if(t%10==0)//每100ms读取一次
{
temperate=DS18B20_Get_Temp();
if(temperate<0)
{
GUI_ShowString(20,40,"-",16,1); //显示负号
temperate=-temperate; //转为正数
}else GUI_ShowString(20,40," ",16,1); //去掉负号
temperate=temperate/10;
//温度补偿
temperate2 = 0.9399*exp(0.0157*temperate); //该函数有标定数据得出
temperate3 = temperate+temperate2;//进行补偿
GUI_ShowNum(20,40,(u8)temperate3,3,16,1); //显示正数部分
temperate1=temperate3-(u8)temperate3;
GUI_ShowString(50,40,".",16,1);
GUI_ShowNum(55,40,temperate1*10,1,16,1);
GUI_ShowString(65,40,"^C",16,1);
}
temperate=DS18B20_Get_Temp();
if(temperate3>data)//温度过高,红灯亮
{
GPIO_ResetBits(GPIOA,GPIO_Pin_8); //LED0
GPIO_SetBits(GPIOD,GPIO_Pin_2);//LED1
}
else if(temperate3
2.按键输出
我自己搭建了一个12键的3*4扫描法的键盘,基本原理可参照(48条消息) STM32单片机学习3--STM32控制键盘_stm32 键盘_贝勒里恩的博客-CSDN博客
比较简陋,也可以直接从网上买集成好的键盘,自己焊上杜邦线就可以,我是3+4一共需要7个I/O引脚,连接到stm32上,具体引脚图如下:
第一行 | PB5 |
第二行 | PB6 |
第三行 | PB7 |
第四行 | PB8 |
第一列 | PA1 |
第二列 | PA2 |
第三列 | PA3 |
key.h
#ifndef __KEY_H
#define __KEY_H
#include "sys.h"
#include "stm32f10x.h"
void delay_us(uint32_t delay_us);
void delay_ms(uint16_t delay_ms);
void KEY_GPIO_Config(void);
int scan(void);
#endif
key.c
#include "Key.h"
#include "delay.h"
void KEY_GPIO_Config(void)
{
//定义一个GPIO_InitTypeDef类型的结构体
GPIO_InitTypeDef GPIO_InitStructure;
//开启GPIOA、GPIOB外设时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB,ENABLE);
///控制行 四行
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
///读取列 三列
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
}
int scan(void)
{
uint8_t flag = 0;
//扫描第一行
GPIO_SetBits(GPIOB,GPIO_Pin_5);
GPIO_ResetBits(GPIOB,GPIO_Pin_6);
GPIO_ResetBits(GPIOB,GPIO_Pin_7);
GPIO_ResetBits(GPIOB,GPIO_Pin_8);
//扫描第一列
flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1);
if(flag == 1) {
delay_ms(200);
flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1);
if(flag == 1) {
flag = 2;
return 1;
}
}
flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2);
if(flag == 1) {
delay_ms(200);
flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2);
if(flag == 1) {
flag = 0;
return 4;
}
}
flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_3);
if(flag == 1) {
delay_ms(200);
flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_3);
if(flag == 1) {
flag = 0;
return 7;
}
}
//扫描第二行
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
GPIO_SetBits(GPIOB,GPIO_Pin_6);
GPIO_ResetBits(GPIOB,GPIO_Pin_7);
GPIO_ResetBits(GPIOB,GPIO_Pin_8);
//?????
flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1);
if(flag == 1) {
delay_ms(200);
flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1);
if(flag == 1) {
flag = 0;
return 2;
}
}
flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2);
if(flag == 1) {
delay_ms(200);
flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2);
if(flag == 1) {
flag = 0;
return 5;
}
}
flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_3);
if(flag == 1) {
delay_ms(200);
flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_3);
if(flag == 1) {
flag = 0;
return 8;
}
}
//扫描第三行
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
GPIO_ResetBits(GPIOB,GPIO_Pin_6);
GPIO_SetBits(GPIOB,GPIO_Pin_7);
GPIO_ResetBits(GPIOB,GPIO_Pin_8);
//?????
flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1);
if(flag == 1) {
delay_ms(100);
flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1);
if(flag == 1) {
flag = 0;
return 3;
}
}
flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2);
if(flag == 1) {
delay_ms(100);
flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2);
if(flag == 1) {
flag = 0;
return 6;
}
}
flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_3);
if(flag == 1) {
delay_ms(200);
flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_3);
if(flag == 1) {
flag = 0;
return 9;
}
}
//扫描第四行
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
GPIO_ResetBits(GPIOB,GPIO_Pin_6);
GPIO_ResetBits(GPIOB,GPIO_Pin_7);
GPIO_SetBits(GPIOB,GPIO_Pin_8);
//?????
flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1);
if(flag == 1) {
delay_ms(200);
flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1);
if(flag == 1) {
flag = 0;
return 10;
}
}
flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2);
if(flag == 1) {
delay_ms(200);
flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2);
if(flag == 1) {
flag = 0;
return 0;
}
}
flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_3);
if(flag == 1) {
delay_ms(200);
flag = GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_3);
if(flag == 1) {
flag = 0;
return 11;
}
}
return -1;
}
3. DS180B20温度传感器
这款温度传感器内部已经集成了ADC直接输出数字信号,不需要外接ADC,所以它只有三个口VCC、GND、DATA。
VCC | 3.3V |
GND | GND |
DATA | PA5 |
ds18b20.h
#ifndef __DS18B20_H
#define __DS18B20_H
#include "sys.h"
//IO方向设置
#define DS18B20_IO_IN() {GPIOA->CRL&=0XFF0FFFFF;GPIOA->CRL|=8<<20;}
#define DS18B20_IO_OUT() {GPIOA->CRL&=0XFF0FFFFF;GPIOA->CRL|=3<<20;}
IO操作函数
#define DS18B20_DQ_OUT PAout(5) //数据端口 PA0
#define DS18B20_DQ_IN PAin(5) //数据端口 PA0
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
ds18b20.c
#include "ds18b20.h"
#include "delay.h"
//复位DS18B20
void DS18B20_Rst(void)
{
DS18B20_IO_OUT(); //SET PA0 OUTPUT
DS18B20_DQ_OUT=0; //拉低DQ
delay_us(750); //拉低750us
DS18B20_DQ_OUT=1; //DQ=1
delay_us(15); //15US
}
//等待DS18B20的回应
//返回1:未检测到DS18B20的存在
//返回0:存在
u8 DS18B20_Check(void)
{
u8 retry=0;
DS18B20_IO_IN();//SET PA0 INPUT
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) // read one bit
{
u8 data;
DS18B20_IO_OUT();//SET PA0 OUTPUT
DS18B20_DQ_OUT=0;
delay_us(2);
DS18B20_DQ_OUT=1;
DS18B20_IO_IN();//SET PA0 INPUT
delay_us(12);
if(DS18B20_DQ_IN)data=1;
else data=0;
delay_us(50);
return data;
}
//从DS18B20读取一个字节
//返回值:读到的数据
u8 DS18B20_Read_Byte(void) // read one byte
{
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 PA0 OUTPUT;
for (j=1;j<=8;j++)
{
testb=dat&0x01;
dat=dat>>1;
if (testb)
{
DS18B20_DQ_OUT=0;// Write 1
delay_us(2);
DS18B20_DQ_OUT=1;
delay_us(60);
}
else
{
DS18B20_DQ_OUT=0;// Write 0
delay_us(60);
DS18B20_DQ_OUT=1;
delay_us(2);
}
}
}
//开始温度转换
void DS18B20_Start(void)// ds1820 start convert
{
DS18B20_Rst();
DS18B20_Check();
DS18B20_Write_Byte(0xcc);// skip rom
DS18B20_Write_Byte(0x44);// convert
}
//初始化DS18B20的IO口 DQ 同时检测DS的存在
//返回1:不存在
//返回0:存在
u8 DS18B20_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能PORTA口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //PORTA0 推挽输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_SetBits(GPIOA,GPIO_Pin_5); //输出1
DS18B20_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 convert
DS18B20_Rst();
DS18B20_Check();
DS18B20_Write_Byte(0xcc);// skip rom
DS18B20_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;
}
4. 上位机显示
我直接采用的纸飞机调试助手软件,它可以直接通过usb串口接收数据,并且实时输出图像。
大家可以参照:(48条消息) STM32数据可视化显示——纸飞机串口调试助手的使用_stm32串口调试助手_拾柒#_17的博客-CSDN博客
感谢大家的观看,希望大家点个关注多多支持,有不懂的可以私信或者评论。