STM32-OLED显示时间和温湿度
STM32-OLED显示时间和温湿度
实验任务
- 使用AHT20模块,读取实时的温度和湿度;
- 使用RTC内部时钟,计算出年月日时分秒以及星期;
- 把以上两组数据通过OLED显示出来。
实验过程
OLED模块代码编写
在工程中导入OLED相关文件:OLED.h
、OLED.c
、OLED_Font.h
。源代码来自:江科大
OLED.h
中声明了引脚的初始化函数以及各种函数,用于显示不同的数据类型:
#ifndef __OLED_H
#define __OLED_H
void OLED_Init(void); // OLED初始化
void OLED_Clear(void); // 清屏
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char); // 指定位置显示一个字符
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String); // 指定位置显示一个字符串
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length); // 指定位置显示一个数字
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length); // 指定位置显示一个有符号数字
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length); // 指定位置显示一个十六进制数
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length); // 指定位置显示一个二进制数
#endif
在OLED_I2C_Init()
函数中,我们指定PB8
作为SCL
,PB9
作为SDA
。
/*引脚配置*/
#define OLED_W_SCL(x) GPIO_WriteBit(GPIOB, GPIO_Pin_8, (BitAction)(x))
#define OLED_W_SDA(x) GPIO_WriteBit(GPIOB, GPIO_Pin_9, (BitAction)(x))
GPIO_InitTypeDef GPIO_InitStructure;
/*引脚初始化*/
void OLED_I2C_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_Init(GPIOB, &GPIO_InitStructure);
OLED_W_SCL(1);
OLED_W_SDA(1);
}
I2C协议读取AHT20模块
本次实验将采用软件I2C进行温湿度的采集。
AHT20模块相关信息和代码请到官网下载:下载中心
下面是部分函数的介绍:
-
文件中定义了两个变量:
T1
和H1
,分别存放温度和湿度的数据。uint32_t H1=0; //Humility uint32_t T1=0; //Temperature
-
AHT20芯片的使用过程
read_AHT20_once
函数:void read_AHT20_once(void) { delay_ms(10); reset_AHT20();//重置AHT20芯片 delay_ms(10); init_AHT20();//初始化AHT20芯片 delay_ms(10); startMeasure_AHT20();//开始测试AHT20芯片 delay_ms(80); read_AHT20();//读取AHT20采集的到的数据 delay_ms(5); }
-
AHT20芯片读取数据
read_AHT20
函数,由于本实验需要在OLED上显示数据,因此在该函数中进行了部分修改,也就是把得到的数据显示到OLED上。void read_AHT20(void) { uint8_t i; for(i=0; i<6; i++) { readByte[i]=0; } I2C_Start();//I2C启动 I2C_WriteByte(0x71);//I2C写数据 ack_status = Receive_ACK();//收到的应答信息 readByte[0]= I2C_ReadByte();//I2C读取数据 Send_ACK();//发送应答信息 readByte[1]= I2C_ReadByte(); Send_ACK(); readByte[2]= I2C_ReadByte(); Send_ACK(); readByte[3]= I2C_ReadByte(); Send_ACK(); readByte[4]= I2C_ReadByte(); Send_ACK(); readByte[5]= I2C_ReadByte(); SendNot_Ack(); //Send_ACK(); I2C_Stop();//I2C停止函数 //判断读取到的第一个字节是不是0x08,0x08是该芯片读取流程中规定的,如果读取过程没有问题,就对读到的数据进行相应的处理 if( (readByte[0] & 0x68) == 0x08 ) { H1 = readByte[1]; H1 = (H1<<8) | readByte[2]; H1 = (H1<<8) | readByte[3]; H1 = H1>>4; H1 = (H1*1000)/1024/1024; T1 = readByte[3]; T1 = T1 & 0x0000000F; T1 = (T1<<8) | readByte[4]; T1 = (T1<<8) | readByte[5]; T1 = (T1*2000)/1024/1024 - 500; AHT20_OutData[0] = (H1>>8) & 0x000000FF; AHT20_OutData[1] = H1 & 0x000000FF; AHT20_OutData[2] = (T1>>8) & 0x000000FF; AHT20_OutData[3] = T1 & 0x000000FF; } else { AHT20_OutData[0] = 0xFF; AHT20_OutData[1] = 0xFF; AHT20_OutData[2] = 0xFF; AHT20_OutData[3] = 0xFF; printf("读取失败!!!"); } // 下面是在OLED上显示数据 OLED_ShowString(1, 1, "Temperature:"); OLED_ShowNum(1, 13, T1/100, 1); OLED_ShowNum(1, 14, (T1/10)%10, 1); OLED_ShowChar(1, 15, '.'); OLED_ShowNum(1, 16, T1%10, 1); OLED_ShowString(2, 1, "Humidity:"); OLED_ShowNum(2, 10, H1/100, 1); OLED_ShowNum(2, 11, (H1/10)%10, 1); OLED_ShowChar(2, 12, '.'); OLED_ShowNum(2, 13, H1%10, 1); }
RTC时钟代码编写
创建两个文件:MyRTC.c
,MyRTC.h
,进行实时时钟的计算和显示。
-
MyRTC.h
:#include "stm32f10x.h" // Device header #include <time.h> #include "OLED.h" uint16_t MyRTC_Time[] = {2023, 11, 21, 14, 54, 00}; void MyRTC_SetTime(void); char* weekday; uint16_t iWeek; // 根据日期计算星期 void CalculateWeekDay(uint16_t year, uint16_t month, uint16_t day) { if (month == 1 || month ==2) { month +=12; year--; } iWeek = (day + 2 * month + 3 * (month + 1)/5 + year + year/4 - year/100 + year/400) % 7; switch (iWeek) { case 0: weekday = "Monday"; break; case 1: weekday = "Teusday"; break; case 2: weekday = "Wednesday"; break; case 3: weekday = "Thursday"; break; case 4: weekday = "Friday"; break; case 5: weekday = "Saturday"; break; case 6: weekday = "Sunday"; break; default: break; } } // 初始化函数 void MyRTC_Init(void) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE); PWR_BackupAccessCmd(ENABLE); if (BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5) { RCC_LSEConfig(RCC_LSE_ON); while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) != SET); RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); RCC_RTCCLKCmd(ENABLE); RTC_WaitForSynchro(); RTC_WaitForLastTask(); RTC_SetPrescaler(32768 - 1); RTC_WaitForLastTask(); MyRTC_SetTime(); BKP_WriteBackupRegister(BKP_DR1, 0xA5A5); } else { RTC_WaitForSynchro(); RTC_WaitForLastTask(); } } void MyRTC_SetTime(void) { time_t time_cnt; struct tm time_date; time_date.tm_year = MyRTC_Time[0] - 1900; time_date.tm_mon = MyRTC_Time[1] - 1; time_date.tm_mday = MyRTC_Time[2]; time_date.tm_hour = MyRTC_Time[3]; time_date.tm_min = MyRTC_Time[4]; time_date.tm_sec = MyRTC_Time[5]; time_cnt = mktime(&time_date) - 8 * 60 * 60; RTC_SetCounter(time_cnt); RTC_WaitForLastTask(); } // 读取时钟并显示 void MyRTC_ReadTime(void) { time_t time_cnt; struct tm time_date; time_cnt = RTC_GetCounter() + 8 * 60 * 60; time_date = *localtime(&time_cnt); MyRTC_Time[0] = time_date.tm_year + 1900; MyRTC_Time[1] = time_date.tm_mon + 1; MyRTC_Time[2] = time_date.tm_mday; MyRTC_Time[3] = time_date.tm_hour; MyRTC_Time[4] = time_date.tm_min; MyRTC_Time[5] = time_date.tm_sec; CalculateWeekDay(MyRTC_Time[0], MyRTC_Time[1], MyRTC_Time[2]); // 下面把数据显示到OLED上 OLED_ShowNum(3, 1, MyRTC_Time[0], 4); OLED_ShowChar(3, 5, '-'); OLED_ShowNum(3, 6, MyRTC_Time[1], 2); OLED_ShowChar(3, 8, '-'); OLED_ShowNum(3, 9, MyRTC_Time[2], 2); OLED_ShowNum(4, 1, MyRTC_Time[3], 2); OLED_ShowChar(4, 3, ':'); OLED_ShowNum(4, 4, MyRTC_Time[4], 2); OLED_ShowChar(4, 6, ':'); OLED_ShowNum(4, 7, MyRTC_Time[5], 2); OLED_ShowString(4, 10, weekday); }
-
OLED.h
:#ifndef __MYRTC_H #define __MYRTC_H extern uint16_t MyRTC_Time[]; void MyRTC_Init(void); void MyRTC_SetTime(void); void MyRTC_ReadTime(void); #endif
以上就可以实现实时读取时钟并显示。(具体看这里)
main.c 代码编写
#include "delay.h"
#include "usart.h"
#include "bsp_i2c.h"
#include "OLED.h"
#include "MyRTC.h"
int main(void)
{
delay_init();
uart_init(9600);
OLED_Init();
MyRTC_Init();
IIC_Init();
MyRTC_SetTime();
while(1)
{
MyRTC_ReadTime(); // 读取时钟
read_AHT20_once(); // 读取AHT20
}
}
实验效果
将模块连接好后,烧录代码,可以看到OLED实时显示的数据:
总结体会
通过本次实验,我熟悉了AHT20模块、RTC时钟以及OLED的使用,增强了自己的多个模块综合开发的能力。
遇到的问题:时钟部分秒数不准确,有时过了几秒钟OLED的显示才增加1秒,有时又会一次增加2秒,暂时不知道什么原因。