文章目录
前言
在当今的电子世界中,温度监控已经成为许多系统和设备中不可或缺的一部分。无论是工业生产、医疗设备,还是家庭应用,温度监控都扮演着重要的角色。因此,本文将介绍如何使用STM32单片机制作一个数字温度计。
一、STM32单片机
STM32单片机是一种广泛使用的微控制器,它具有高性能、低功耗、易于编程等优点。STM32单片机可用于各种应用,包括工业控制、智能家居、物联网等。
二、数字温度计
数字温度计是一种通过数字方式测量温度的仪器。与传统的模拟温度计相比,数字温度计具有测量准确、使用方便、易于读取等优点。数字温度计通常使用传感器来检测温度,并将温度值转换为数字信号进行显示。
一、RTC原理
(一)RTC介绍
RTC (Real Time Clock):实时时钟
RTC是个独立的定时器。RTC模块拥有一个连续计数的计数器,在相应的软件配置下,可以提供时钟日历的功能。修改计数器的值可以重新设置当前时间和日期 RTC还包含用于管理低功耗模式的自动唤醒单元。
在断电情况下 RTC仍可以独立运行 只要芯片的备用电源一直供电,RTC上的时间会一直走。
RTC实质是一个掉电后还继续运行的定时器,从定时器的角度来看,相对于通用定时器TIM外设,它的功能十分简单,只有计时功能(也可以触发中断)。但其高级指出也就在于掉电之后还可以正常运行。
两个 32 位寄存器包含二进码十进数格式 (BCD) 的秒、分钟、小时( 12 或 24
小时制)、星期几、日期、月份和年份。此外,还可提供二进制格式的亚秒值。系统可以自动将月份的天数补偿为 28、29(闰年)、30 和 31
天。
上电复位后,所有RTC寄存器都会受到保护,以防止可能的非正常写访问。
无论器件状态如何(运行模式、低功耗模式或处于复位状态),只要电源电压保持在工作范围内,RTC使不会停止工作
(二)RCT特征:
可编程的预分频系数:分频系数高为220。
32位的可编程计数器,可用于较长时间段的测量。
2个分离的时钟:用于APB1接口的PCLK1和RTC时钟(RTC时钟的频率必须小于PCLK1时钟 频率的四分之一以上)。
可以选择以下三种RTC的时钟源:
HSE时钟除以128;
LSE振荡器时钟;
SI振荡器时钟
2个独立的复位类型:
APB1接口由系统复位;
RTC核心(预分频器、闹钟、计数器和分频器)只能由后备域复位
3个专门的可屏蔽中断:
1.闹钟中断,用来产生一个软件可编程的闹钟中断
2.秒中断,用来产生一个可编程的周期性中断信号(长可达1秒)。
3.溢出中断,指示内部可编程计数器溢出并回转为0的状态。
RTC时钟源:
三种不同的时钟源可被用来驱动系统时钟(SYSCLK):
HSI振荡器时钟
HSE振荡器时钟
PLL时钟
这些设备有以下2种二级时钟源:
40kHz低速内部RC,可以用于驱动独立看门狗和通过程序选择驱动RTC。 RTC用于从停机/待机模式下自动唤醒系统。
32.768kHz低速外部晶体也可用来通过程序选择驱动RTC(RTCCLK)。
(三)RTC原理框图
(四)RTC时钟选择
使用HSE分频时钟或者LSI的时候,在主电源VDD掉电的情况下,这两个时钟来源都会受到影响,因此没法保证RTC正常工作.所以RTC一般都时钟低速外部时钟LSE,频率为实时时钟模块中常用的32.768KHz,因为32768 = 2^15,分频容易实现,所以被广泛应用到RTC模块.(在主电源VDD有效的情况下(待机),RTC还可以配置闹钟事件使STM32退出待机模式).
(五)RTC复位过程
除了RTC_PRL、RTC_ALR、RTC_CNT和RTC_DIV寄存器外,所有的系统寄存器都由系统复位或电源复位进行异步复位。
RTC_PRL、RTC_ALR、RTC_CNT和RTC_DIV寄存器仅能通过备份域复位信号复位。
系统复位后,禁止访问后备寄存器和RCT,防止对后卫区域(BKP)的意外写操作
(六)配置RTC寄存器
必须设置RTC_CRL寄存器中的CNF位,使RTC进入配置模式后,才能写入RTC_PRL、
RTC_CNT、RTC_ALR寄存器。
另外,对RTC任何寄存器的写操作,都必须在前一次写操作结束后进行。可以通过查询
RTC_CR寄存器中的RTOFF状态位,判断RTC寄存器是否处于更新中。仅当RTOFF状态位是’1’
时,才可以写入RTC寄存器。
(七)RTC时钟源
RTC是一个独立的时钟源
(八)RTC寄存器
RTC控制寄存器 (RTC_CRH, RTC_CRL)
RTC预分频装载寄存器 (RTC_PRLH, RTC_PRLL)
RTC预分频余数寄存器 (RTC_DIVH, RTC_DIVL)
RTC计数器寄存器 (RTC_CNTH, RTC_CNTL)
RTC闹钟寄存器 (RTC_ALRH ,RTC_ALRL)
二、CubeMX建立项目
(一)引入库
1.配置SYS
2.配置RCC,(设置高速外部时钟,使能外部晶振LSE)
3.配置RTC(激活时钟源(Activate Clock Source)和日历(Activate Calendar))
4.使能串口
5.时钟树配置
6.项目命名创建
7.用keil打开文件
(二)代码块
1.main.c文件中重写fputc函数,完成printf函数的重定向
//添加头文件#include "stdio.h"
int fputc(int ch,FILE *f){
uint8_t temp[1]={ch};
HAL_UART_Transmit(&huart1,temp,1,2);
return ch;
}
2.main函数中定义时间和日期的结构体用来获取时间和日期
RTC_DateTypeDef GetData; //获取日期结构体
RTC_TimeTypeDef GetTime; //获取时间结构体
3.main函数的while循环中添加以下代码
/* Get the RTC current Time */
HAL_RTC_GetTime(&hrtc, &GetTime, RTC_FORMAT_BIN);
/* Get the RTC current Date */
HAL_RTC_GetDate(&hrtc, &GetData, RTC_FORMAT_BIN);
/* Display date Format : yy/mm/dd */
printf("%02d/%02d/%02d\r\n",2000 + GetData.Year, GetData.Month, GetData.Date);
/* Display time Format : hh:mm:ss */
printf("%02d:%02d:%02d\r\n",GetTime.Hours, GetTime.Minutes, GetTime.Seconds);
printf("\r\n");
HAL_Delay(1000);
/* Display date Format : weekday */
if(GetData.WeekDay==1){
printf("星期一\r\n");
}else if(GetData.WeekDay==2){
printf("星期二\r\n");
}else if(GetData.WeekDay==3){
printf("星期三\r\n");
}else if(GetData.WeekDay==4){
printf("星期四\r\n");
}else if(GetData.WeekDay==5){
printf("星期五\r\n");
}else if(GetData.WeekDay==6){
printf("星期六\r\n");
}else if(GetData.WeekDay==7){
printf("星期日\r\n");
}
四、显示日历和温度
(一)代码
#include "stm32f10x.h"
#include "Delay.h"
#include "OLED.h"
#include "Delay.h"
#include "LED.h"
#include "usart.h"
#include "dht11.h"
extern unsigned int rec_data[4];
int main(void)
{
OLED_Init();
OLED_ShowHZ(3,5,0); //温
OLED_ShowHZ(3,7,2); //度
OLED_ShowHZ(3,9,4); //:
OLED_ShowHZ(3,12,2); //度
OLED_ShowHZ(4,5,8); //湿
OLED_ShowHZ(4,7,10); //度
OLED_ShowHZ(4,9,4); //:
OLED_ShowChar(4,12,'%');//%
int year=2023;
int month=11;
int day=20;
int hour=23;
int min=59;
int s=55;
while (1)
{
OLED_ShowHZ(1,2,18);//日
OLED_ShowHZ(1,4,20);//期
OLED_ShowNum(1,7,year,4);//2023
OLED_ShowHZ(1,11,22);//年
OLED_ShowNum(1,13,month,2);//11
OLED_ShowHZ(1,15,24);//月
OLED_ShowNum(2,1,day,2);//20
OLED_ShowHZ(2,3,26);//日
OLED_ShowNum(2,5,hour,2);//15
OLED_ShowHZ(2,7,30);//时
OLED_ShowNum(2,9,min,2);//40
OLED_ShowHZ(2,11,32);//分
OLED_ShowNum(2,13,s,2);//s
OLED_ShowHZ(2,15,28);//秒
//OLED_ShowString(2,17,"Mon");
DHT11_REC_Data(); //接收dht11数据
OLED_ShowNum(3,10,rec_data[0]-5,2);
OLED_ShowNum(4,10,rec_data[0]-13,2);
s+=1;
if(s>=60)
{
s=0;
min++;
}
if(min>=60)
{
min=0;
hour++;
}
if(hour>=24)
{
hour=0;
day++;
}
if(day>=31)
{
month++;
day=1;
}
if(month>12)
{
year++;
month=1;
}
Delay_s(1);
}
}
AHT20文件代码:
#include "stm32f10x.h" // Device header
#include "dht11.h"
#include "delay.h"
unsigned int rec_data[4];
void DH11_GPIO_Init_OUT(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //????
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
void DH11_GPIO_Init_IN(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
void DHT11_Start(void)
{
DH11_GPIO_Init_OUT();
dht11_high;
Delay_us(30);
dht11_low; //??????18us
Delay_ms(20);
dht11_high; //????20~40us
Delay_us(30);
DH11_GPIO_Init_IN(); //????
}
//??????
char DHT11_Rec_Byte(void)
{
unsigned char i = 0;
unsigned char data;
for(i=0;i<8;i++) //1?????1???byte,1???byte?8?bit
{
while( Read_Data == 0); //?1bit??,???????,???????
Delay_us(30); //??30us???????0???1,0??26~28us
data <<= 1; //??
if( Read_Data == 1 ) //????30us???????????1
{
data |= 1; //??+1
}
while( Read_Data == 1 ); //???????,???????
}
return data;
}
//????
void DHT11_REC_Data(void)
{
unsigned int R_H,R_L,T_H,T_L;
unsigned char RH,RL,TH,TL,CHECK;
DHT11_Start(); //??????
dht11_high; //????
if( Read_Data == 0 ) //??DHT11????
{
while( Read_Data == 0); //???????,???????
while( Read_Data == 1); //???????,???????
R_H = DHT11_Rec_Byte();
R_L = DHT11_Rec_Byte();
T_H = DHT11_Rec_Byte();
T_L = DHT11_Rec_Byte();
CHECK = DHT11_Rec_Byte(); //??5???
dht11_low; //????bit???????,DHT11???? 50us
Delay_us(55); //????55us
dht11_high; //??????????????????
if(R_H + R_L + T_H + T_L == CHECK) //??????,??????????????
{
RH = R_H;
RL = R_L;
TH = T_H;
TL = T_L;
}
}
rec_data[0] = RH;
rec_data[1] = RL;
rec_data[2] = TH;
rec_data[3] = TL;
}
结果如下:
总结
通过结合STM32F103C8T6的RTC实时时钟模块、串口通信接口、AHT20传感器和OLED显示屏模块,我们成功创建了一个功能丰富的系统,它不仅可以读取和显示时间信息,还能够实时监测和显示环境温湿度。这种整合多种技术和模块的方法为我们提供了更广阔的应用前景,无论是在智能家居、环境监测还是其他嵌入式系统中,这种设计方案都具有很高的实用价值和指导意义。。