读取STM32F103C8T6 内部的时钟和AHT20的温湿度并显示在OLED屏上

一、STM32F103中的RTC(实时时钟)简介

1、RTC(Real Time Clock)实时时钟

  RTC是一个独立的定时器。 RTC模块拥有一个连续计数的计数器,在相应的软件配置下,可以提供时钟日历的功能。修改计数器的值可以重新设置当前时间和日期 RTC还包含用于管理低功耗模式的自动唤醒单元。

2、RTC主要特征

  • 可编程的预分频系数:分频系数高为220;
  • 32位的可编程计数器,可用于较长时间段的测量;
  • 2个分离的时钟:用于APB1接口的PCLK1和RTC时钟(RTC时钟的频率必须小于PCLK1时钟频率的四分之一以上);
  • 三种RTC的时钟源:
    • HSE时钟除以128;
    • LSE振荡器时钟;
    • LSI振荡器时钟;
  • 2个独立的复位类型:
    • APB1接口由系统复位;
    • RTC核心(预分频器、闹钟、计数器和分频器)只能由后备域复位;
  • 3个专门的可屏蔽中断:
    • 闹钟中断:用于产生一个软件可编程的闹钟中断;
    • 秒中断:用于产生一个可编程的周期性中断信号(长可达1秒);
    • 溢出中断:指示内部可编程计数器溢出并回转为0的状态。

3、RTC组成

在这里插入图片描述
  RTC时钟的框图可以分为两个部分:

  • APB1:用于和 APB1 总线相连。 此单元还包含一组 16 位寄存器,可通过 APB1 总线对其进行读写操作。APB1 接口由 APB1 总 线时钟驱动,用来与 APB1 总线连接。

  • RTC核心接口:由一组可编程计数器组成,分为RTC的预分频模块一个32位的可编程计数器(RTC_CNT)

    • RTC 的 预分频模块
        它可编程产生 1 秒的 RTC 时间基准 TR_CLK。RTC 的预分频模块包含了一个 20 位的可编程分频器(RTC 预分频器)。如果在 RTC_CR 寄存器中设置了相应的允许位,则在每个 TR_CLK 周期中 RTC 产生一个中断(秒中断)。

    • 一个 32 位的可编程计数器
        可被初始化为当前的系统时间,一个 32 位的时钟计数器,按秒钟计算,可以记 录 4294967296 秒,约合 136 年左右,作为一般应用,这已经是足够了的。

4、RTC寄存器

在这里插入图片描述

  • RTC控制寄存器高位——RTC_CRH
    用于配置三个专门的可屏蔽中断:溢出中断、闹钟中断、秒中断使能

在这里插入图片描述

  • RTC控制寄存器低位——RTC_CRL
    一般用到该寄存器的3、4、5位
    在这里插入图片描述

  • RTC预分频装载寄存器(RTC_PRLH,RTC_PRLL)
    用于配置RTC时钟的分频数
    在这里插入图片描述

  • RTC预分频余数寄存器 (RTC_DIVH, RTC_DIVL)
    用于获得余数,也就是获取更精确的计时,比如:0.1s ,0.01 s等。该寄存器为只读寄存器,其值在RTC_PRL或RTC_CNT寄存器中的值发生改变后,由硬件重新装载。
    在这里插入图片描述

  • RTC计数寄存器——RTC_CNTX,RTC_CNTL
    用于存放计数器内的计数值,也就是用来记录时钟时间。该寄存器由 2 个 16 位的寄存器组成 RTC_CNTH 和 RTC_CNTL,总共 32 位,当进行读操作时,直接返回计数器内的计数值(系统时间)。
    在这里插入图片描述

  • RTC 计数器寄存器——RTC 闹钟寄存器(RTC_ALRH、RTC_ALRL)
    RTC时钟中断控制寄存器。该寄存器也是由 2 个 16 位的寄存器组成 RTC_ALRH 和 RTC_ALRL,也就是32位,当可编程计数器的值与RTC_ALR中的32位值相等时,即触发一个闹钟事件,并且产生RTC闹钟中断。
    在这里插入图片描述

5、配置RTC步骤

  • 使能PWR和BKP时钟

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
    
  • 使能后备寄存器访问

    PWR_BackupAccessCmd(ENABLE); //使能 RTC 和后备寄存器访问
    
  • 复位备份区域,开启外部低速振荡器

    BKP_DeInit();//复位备份区域
    
  • 配置RTC时钟源,使能RTC时钟

    RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //选择 LSE 作为 RTC 时钟(RCC_RTCCLKSource_LSI 和 RCC_RTCCLKSource_HSE_Div128)
    RCC_RTCCLKCmd(ENABLE); //使能 RTC 时钟
    
  • 设置RTC预分频系数:RTC_SetPrescaler()

    RTC_EnterConfigMode();/// 允许配置	
    RTC_SetPrescaler(32767); //设置RTC预分频的值
    RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成
    
  • 设置时间:RTC_SetCounter()

    RTC_EnterConfigMode();/// 允许配置
    void RTC_SetCounter(uint32_t CounterValue);
    RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成
    
  • 开启相关中断(可选)

    void RTC_ITConfig(uint16_t RTC_IT, FunctionalState NewState)//RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能 RTC 秒中断
    
  • 编写中断服务函数

    RTC_IRQHandler();
    
  • 部分操作要等待写操作完成和同步

     RTC_WaitForLastTask();//等待最近一次对RTC寄存器的写操作完成
     RTC_WaitForSynchro();	//等待RTC寄存器同步 
    

二、读取内部时钟并输出

1、任务要求

  读取STM32F103C8T6 内部的时钟(年月日时分秒),日历(星期x),1秒周期,通过串口输出到PC上位机;

2、程序设计要点

初始化 RTC 外设;
设置时间以及添加配置标志;
获取当前时间;

3、代码实现

  • 定义时间结构体

    //时间结构体
    typedef struct 
    {
      vu8 hour;
      vu8 min;
      vu8 sec;			
      //公历日月年周
      vu16 w_year;
      vu8  w_month;
      vu8  w_date;
      vu8  week;		 
    }_calendar_obj;	
    extern _calendar_obj calendar;	//日历结构体
    
  • 对RTC进行初始化,配置实时时钟
    设置初始时间为2023/11/18/16:49:00

    //实时时钟配置
    //初始化RTC时钟,同时检测时钟是否工作正常
    //BKP->DR1用于保存是否第一次配置的设置
    //返回0:正常
    //其他:错误代码
    
    u8 RTC_Init(void)
    {
    //检查是不是第一次配置时钟
    u8 temp=0;
      RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);	//使能PWR和BKP外设时钟   
    PWR_BackupAccessCmd(ENABLE);	//使能后备寄存器访问  			
    	BKP_DeInit();	//复位备份区域 	
    	RCC_LSEConfig(RCC_LSE_ON);	//设置外部低速晶振(LSE),使用外设低速晶振
    	while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET&&temp<250)	//检查指定的RCC标志位设置与否,等待低速晶振就绪
    		{
    		temp++;
    		Delay_ms(10);
    		}
    	if(temp>=250)return 1;//初始化时钟失败,晶振有问题	    
    	RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);		//设置RTC时钟(RTCCLK),选择LSE作为RTC时钟    
    	RCC_RTCCLKCmd(ENABLE);	//使能RTC时钟  
    	RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成
    	RTC_WaitForSynchro();		//等待RTC寄存器同步  
    	RTC_ITConfig(RTC_IT_SEC, ENABLE);		//使能RTC秒中断
    	RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成
    	RTC_EnterConfigMode();/// 允许配置	
    	RTC_SetPrescaler(32767); //设置RTC预分频的值
    	RTC_WaitForLastTask();	//等待最近一次对RTC寄存器的写操作完成
    	RTC_Set(2023,11,18,16,49,00);  //设置时间	2023/22/18/16:49
    	RTC_ExitConfigMode(); //退出配置模式  
    	BKP_WriteBackupRegister(BKP_DR1, 0X5050);	//向指定的后备寄存器中写入用户程序数据
    RTC_NVIC_Config();//RCT中断分组设置		    				     
    RTC_Get();//更新时间	
    return 0; //ok
    }
    
  • 中断服务函数

    //RTC时钟中断
    //每秒触发一次  
    //extern u16 tcnt; 
    void RTC_IRQHandler(void)
    {		 
      if (RTC_GetITStatus(RTC_IT_SEC) != RESET)//秒钟中断
      {							
      	RTC_Get();//更新时间   
      }
      if(RTC_GetITStatus(RTC_IT_ALR)!= RESET)//闹钟中断
      {
      	RTC_ClearITPendingBit(RTC_IT_ALR);		//清闹钟中断	  	
        RTC_Get();				//更新时间   
    	printf("Alarm Time:%d-%d-%d %d:%d:%d\n",calendar.w_year,calendar.w_month,calendar.w_date,calendar.hour,calendar.min,calendar.sec);//输出闹铃时间	
      	
    	} 				  								 
      RTC_ClearITPendingBit(RTC_IT_SEC|RTC_IT_OW);		//清闹钟中断
      RTC_WaitForLastTask();	  	    						 	   	 
    }
    
  • 主函数

    #include "sys.h"
    #include "usart.h"
    #include "rtc.h"
    #include "stm32f10x.h"
    #include "delay.h"
    #include "bmp.h"
    #include "oled.h"
    
    void main(void) {
      u8 t;
      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);               /* 设置NVIC中断分组2:2位抢占优先级,2位响应优先级 */
      //delay_init();                                                   /* 延时函数初始化 */
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
      GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);      /* 关闭jtag,使能SWD,可以用SWD模式调试 */
      Delay_ms(500);                                                /* 等待稳定 */
      uart_init(115200);
      IIC_Init();
      RTC_Init();                //RTC初始化
        while (1) {
          if (t != calendar.sec) {
              t = calendar.sec;
              printf("%d-%02d-%02d %02d:%02d:%02d\r\n", calendar.w_year, calendar.w_month, calendar.w_date,
                     calendar.hour, calendar.min, calendar.sec);
              switch (calendar.week) {
                  case 0:
                      printf("Sunday   \r\n");
                      break;
                  case 1:
                      printf("Monday   \r\n");
                      break;
                  case 2:
                      printf("Tuesday  \r\n");
                      break;
                  case 3:
                      printf("Wednesday\r\n");
                      break;
                  case 4:
                      printf("Thursday \r\n");
                      break;
                  case 5:
                      printf("Friday   \r\n");
                      break;
                  case 6:
                      printf("Saturday \r\n");
                      break;
              }
          }
          Delay_ms(10);
      }
    }
    

4、完整代码

具体代码过长,我打包放在了博客结尾

5、硬件实现

在这里插入图片描述

三、显示年月份时分秒、日历和实时温度、湿度

1、任务要求

  读取AHT20的温度和湿度,通过OLED,把年月份时分秒、日历和实时温度、湿度显示出来,2秒周期。

2、代码实现

温湿度读取的具体代码分析可以参考我之前的博客:STM32+OLED屏显应用实例

  • oledfont.h文件中的**cfont16[]**函数中添加需要用得到字模

    const typFNT_GB16 cfont16[] = 
    {
      
    /*--  文字:  温  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
    "温",0x00,0x00,0x23,0xF8,0x12,0x08,0x12,0x08,0x83,0xF8,0x42,0x08,0x42,0x08,0x13,0xF8,0x10,0x00,0x27,0xFC,0xE4,0xA4,0x24,0xA4,0x24,0xA4,0x24,0xA4,0x2F,0xFE,0x00,0x00,
    
    /*--  文字:  湿  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
    "湿",0x00,0x00,0x27,0xF8,0x14,0x08,0x14,0x08,0x87,0xF8,0x44,0x08,0x44,0x08,0x17,0xF8,0x11,0x20,0x21,0x20,0xE9,0x24,0x25,0x28,0x23,0x30,0x21,0x20,0x2F,0xFE,0x00,0x00,
    
    /*--  文字:  度  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
    "度",0x01,0x00,0x00,0x80,0x3F,0xFE,0x22,0x20,0x22,0x20,0x3F,0xFC,0x22,0x20,0x22,0x20,0x23,0xE0,0x20,0x00,0x2F,0xF0,0x24,0x10,0x42,0x20,0x41,0xC0,0x86,0x30,0x38,0x0E,
    
    /*--  文字:  显  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
    "显",0x00,0x00,0x1F,0xF0,0x10,0x10,0x10,0x10,0x1F,0xF0,0x10,0x10,0x10,0x10,0x1F,0xF0,0x04,0x40,0x44,0x44,0x24,0x44,0x14,0x48,0x14,0x50,0x04,0x40,0xFF,0xFE,0x00,0x00,
    
    /*--  文字:  示  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
    "示",0x00,0x00,0x3F,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFE,0x01,0x00,0x01,0x00,0x11,0x10,0x11,0x08,0x21,0x04,0x41,0x02,0x81,0x02,0x05,0x00,0x02,0x00,
      
    /*--  文字:  星  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
    "星",0x00,0x00,0x1F,0xF0,0x10,0x10,0x1F,0xF0,0x10,0x10,0x1F,0xF0,0x01,0x00,0x11,0x00,0x1F,0xF8,0x21,0x00,0x41,0x00,0x1F,0xF0,0x01,0x00,0x01,0x00,0x7F,0xFC,0x00,0x00,
    
    /*--  文字:  期  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
    "期",0x22,0x00,0x22,0x7C,0x7F,0x44,0x22,0x44,0x22,0x44,0x3E,0x7C,0x22,0x44,0x22,0x44,0x3E,0x44,0x22,0x7C,0x22,0x44,0xFF,0x44,0x04,0x84,0x22,0x84,0x41,0x14,0x82,0x08,
    
    /*--  文字:  时  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
    "时",0x00,0x08,0x00,0x08,0x7C,0x08,0x44,0x08,0x45,0xFE,0x44,0x08,0x44,0x08,0x7C,0x08,0x44,0x88,0x44,0x48,0x44,0x48,0x44,0x08,0x7C,0x08,0x44,0x08,0x00,0x28,0x00,0x10,
    
    /*--  文字:  分  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
    "分",0x00,0x40,0x04,0x40,0x04,0x20,0x08,0x20,0x10,0x10,0x20,0x08,0x40,0x04,0x9F,0xE2,0x04,0x20,0x04,0x20,0x04,0x20,0x08,0x20,0x08,0x20,0x10,0x20,0x21,0x40,0x40,0x80,
    
    /*--  文字:  秒  --*/
    /*--  宋体12;  此字体下对应的点阵为:宽x高=16x16   --*/
    "秒",0x08,0x20,0x1C,0x20,0xF0,0x20,0x10,0xA8,0x10,0xA4,0xFC,0xA2,0x11,0x22,0x31,0x20,0x3A,0x24,0x54,0x24,0x54,0x28,0x90,0x08,0x10,0x10,0x10,0x20,0x10,0xC0,0x13,0x00,
    
    //以及文字:一二三四五六年月日
    };
    
  • 主函数程序

    void main(void) {
      u8 t;
      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);               /* 设置NVIC中断分组2:2位抢占优先级,2位响应优先级 */
      //delay_init();                                                   /* 延时函数初始化 */
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
      GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);      /* 关闭jtag,使能SWD,可以用SWD模式调试 */
      Delay_ms(500);                                                /* 等待稳定 */
      uart_init(115200);
      IIC_Init();
      RTC_Init();                //RTC初始化
      OLED_Init();			         //初始化OLED  
      OLED_Clear(0);             //清屏(全黑)
      while (1) {
      	
      	read_AHT20_once();
      	OLED_Clear(0);
          
      	if (t != calendar.sec) {
              t = calendar.sec;
      		GUI_ShowNum(00,00,calendar.w_year,4,16,1);
      		GUI_ShowCHinese(32,00,16,"年",1);
      		GUI_ShowNum(48,00,calendar.w_month,2,16,1);
      		GUI_ShowCHinese(64,00,16,"月",1);
      		GUI_ShowNum(80,00,calendar.w_date,2,16,1);
      		GUI_ShowCHinese(96,00,16,"日",1);
      		GUI_ShowNum(00,20,calendar.hour,2,16,1);
      		GUI_ShowCHinese(16,20,16,"时",1);
      		GUI_ShowNum(32,20,calendar.min,2,16,1);
      		GUI_ShowCHinese(48,20,16,"分",1);
      		GUI_ShowNum(64,20,calendar.sec,2,16,1);
      		GUI_ShowCHinese(80,20,16,"秒",1);
              
              switch (calendar.week) {
                  case 0:
      				GUI_ShowCHinese(00,40,16,"星期日",1);
                      //printf("Sunday   \r\n");
                      break;
                  case 1:
      				GUI_ShowCHinese(00,40,16,"星期一",1);
      			    //printf("Monday   \r\n");
                      break;
                  case 2:
      				GUI_ShowCHinese(00,40,16,"星期二",1);
      				//printf("Tuesday  \r\n");
                      break;
                  case 3:
      				GUI_ShowCHinese(00,40,16,"星期三",1);
      			    //printf("Wednesday\r\n");
                      break;
                  case 4:
      				GUI_ShowCHinese(00,40,16,"星期四",1);
                      //printf("Thursday \r\n");
                      break;
                  case 5:
      				GUI_ShowCHinese(00,40,16,"星期五",1);
                      //printf("Friday   \r\n");
                      break;
                  case 6:
      				GUI_ShowCHinese(00,40,16,"星期六",1);
                      //printf("Saturday \r\n");
                      break;
              }
          }
      		
      	Delay_ms(1500);
          Delay_ms(1500);
      	OLED_Clear(0);
      }
    

3、完整代码

  具体工程文件在博客结尾。

4、硬件实现

在这里插入图片描述

四、参考链接

1、【STM32】RTC实时时钟,步骤超细详解,一文看懂RTC
2、STM32F103C8T6配置RTC显示年月日时分秒(日历)

五、完整工程

链接:https://pan.baidu.com/s/1HfljcwSDTqAPlSdqpR9IkQ
提取码:rhq4

  • 26
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 我可以给你一些建议:1.使用STM32CubeMX可以快速生成STM32F103C8T6的初始化代码;2.使用I2C库函数来完成AHT21与STM32F103C8T6之间的通信;3.根据AHT21的数据手册,编写读取温湿度数据的代码;4.调试代码确保温湿度读取能够正常工作。 ### 回答2: STM32F103C8T6是一款来自STMicroelectronics的微控制器,它采用了ARM Cortex-M3内核,具有丰富的外设和强大的性能。AHT21是一款高精度、低功耗的温湿度传感器。 要完成STM32F103C8T6基于AHT21的温湿度读取程序,我们可以按照以下步骤进行: 1. 确定硬件连接:将AHT21传感器的引脚与STM32F103C8T6的GPIO引脚相连接。一般来说,AHT21的SCL引脚连接到STM32的SCL引脚,而SDA引脚连接到STM32的SDA引脚。 2. 确定软件开发环境:选择合适的集成开发环境(IDE)进行编程,如Keil MDK、STM32CubeIDE等。 3. 创建STM32程序:在IDE中创建一个新的STM32项目。 4. 配置I2C总线:在STM32的I2C外设配置中,设置适当的时钟频率和其他相关参数。确保I2C总线与AHT21的通信速率兼容。 5. 编写读取程序:在主程序代码中,使用适当的I2C读取函数来读取AHT21的温湿度数据。具体的读取流程可以参考AHT21的数据手册,其中包含了读取温湿度数据的详细指令描述。 6. 处理数据:将读取到的温湿度原始数据进行适当的计算和转换,以得到实际的温湿度值。一般来说,AHT21的数据是以二进制格式进行传输的,需要进行一些转换和校准才能得到最终的温湿度值。 7. 输出数据:可以选择以串口、LCD显示屏等方式输出温湿度数据,便于用户查看。 在完成以上步骤后,将编译、烧录程序到STM32F103C8T6微控制器,并将AHT21连接至对应引脚后,程序就可以读取AHT21传感器的温湿度数据了。 ### 回答3: 要基于STM32F103C8T6完成AHT21温湿度读取程序,首先需要了解AHT21传感器的工作原理和STM32F103C8T6的开发环境。 AHT21温湿度传感器是一种数字式温湿度传感器,采用I2C总线进行通信。它具有高精度、低功耗和快速响应的特点。 STM32F103C8T6是一款基于ARM Cortex-M3内核的低功耗微控制器。它具有丰富的外设和强大的计算能力,适用于各种嵌入式应用。 下面是基于STM32F103C8T6完成AHT21温湿度读取程序的步骤: 1. 硬件连接:将AHT21传感器的SDA引脚连接到STM32F103C8T6的I2C1的SDA引脚,将SCL引脚连接到I2C1的SCL引脚。同时,将AHT21的VCC引脚连接到3.3V电源,将GND引脚连接到地。 2. 初始化I2C1外设:在STM32F103C8T6的开发环境中,使用HAL库可以方便地初始化和配置I2C1外设。需要配置I2C1的时钟频率、引脚以及其他参数。 3. 发送读取命令:使用I2C1发送读取AHT21温度和湿度的命令。命令为0xAC,将其发送到AHT21传感器。 4. 接收数据:等待AHT21传感器返回数据,使用I2C1接收数据,并将其保存到缓冲区中。 5. 数据处理:从接收到的数据中提取温度和湿度的数值,并进行必要的处理,例如转换为实际温度和湿度值。 6. 显示结果:将温湿度显示到相应的显示设备,例如OLED屏幕或串口。 以上就是基于STM32F103C8T6完成AHT21温湿度读取程序的大致步骤。具体实现可以根据具体需求和开发环境做相应的调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值