STM32-OLED显示时间和温湿度

STM32-OLED显示时间和温湿度

实验任务

  1. 使用AHT20模块,读取实时的温度和湿度;
  2. 使用RTC内部时钟,计算出年月日时分秒以及星期;
  3. 把以上两组数据通过OLED显示出来。

实验过程

OLED模块代码编写

在工程中导入OLED相关文件:OLED.hOLED.cOLED_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作为SCLPB9作为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模块相关信息和代码请到官网下载:下载中心

下面是部分函数的介绍:

  1. 文件中定义了两个变量:T1H1,分别存放温度和湿度的数据。

    uint32_t  H1=0;  //Humility
    uint32_t  T1=0;  //Temperature
    
  2. 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);
    }
    
  3. 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.cMyRTC.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秒,暂时不知道什么原因。

参考

【嵌入式15】I2C总线通信协议及实操stm32通过I2C实现温湿度(AHT20)采集

STM32入门教程

posted @ 2023-11-21 22:33  回文串多放孜然  阅读(179)  评论(0编辑  收藏  举报