打印
[其他ST产品]

STM32 日历读取与OLED屏显

[复制链接]
5315|15
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
kepe|  楼主 | 2024-1-31 15:00 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
实验内容一、读取STM32F103C8T6 内部的时钟(年月日时分秒)、日历(星期x)
1秒周期,通过串口输出到PC上位机;

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


一、初遇时钟RTC
RTC (Real Time Clock): 实时时钟

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

在断电情况下 RTC仍可以独立运行 只要芯片的备用电源一直供电,RTC上的时间会一直走。

RTC实质是一个掉电后还继续运行的定时器,从定时器的角度来看,相对于通用定时器TIM外设,它的功能十分简单,只有计时功能(也可以触发中断)。但其高级指出也就在于掉电之后还可以正常运行。

两个 32 位寄存器包含二进码十进数格式 (BCD) 的秒、分钟、小时( 12 或 24 小时制)、星期几、日期、月份和年份。此外,还可提供二进制格式的亚秒值。系统可以自动将月份的天数补偿为 28、29(闰年)、30 和 31 天。

上电复位后,所有RTC寄存器都会受到保护,以防止可能的非正常写访问。

无论器件状态如何(运行模式、低功耗模式或处于复位状态),只要电源电压保持在工作范围内,RTC使不会停止工作。


使用特权

评论回复
沙发
kepe|  楼主 | 2024-1-31 15:01 | 只看该作者
RTC特征:

可编程的预分频系数:分频系数高为220。

32位的可编程计数器,可用于较长时间段的测量。

2个分离的时钟:用于APB1接口的PCLK1和RTC时钟(RTC时钟的频率必须小于PCLK1时钟 频率的四分之一以上)。

可以选择以下三种RTC的时钟源:

HSE时钟除以128
LSE振荡器时钟
LSI振荡器时钟
2个独立的复位类型:

APB1接口由系统复位;
RTC核心(预分频器、闹钟、计数器和分频器)只能由后备域复位
3个专门的可屏蔽中断:

1.闹钟中断,用来产生一个软件可编程的闹钟中断。
2.秒中断,用来产生一个可编程的周期性中断信号(长可达1秒)。
3.溢出中断,指示内部可编程计数器溢出并回转为0的状态。
RTC时钟源:
三种不同的时钟源可被用来驱动系统时钟(SYSCLK):

HSI振荡器时钟

HSE振荡器时钟

PLL时钟
这些设备有以下2种二级时钟源:

  1、40kHz低速内部RC,可以用于驱动独立看门狗和通过程序选择驱动RTC。
  RTC用于从停机/待机模式下自动唤醒系统。
  2、32.768kHz低速外部晶体也可用来通过程序选择驱动RTC(RTCCLK)。

使用特权

评论回复
板凳
kepe|  楼主 | 2024-1-31 15:01 | 只看该作者
RTC特征:

RTC原理框图


RTC时钟的框图可以分成两个部分:
APB1 接口:用来和 APB1 总线相连。 此单元还包含一组 16 位寄存器,可通过 APB1 总线对其进行读写操作。APB1 接口由 APB1 总 线时钟驱动,用来与 APB1 总线连接。
通过APB1接口可以访问RTC的相关寄存器(预分频值,计数器值,闹钟值)。

使用特权

评论回复
地板
kepe|  楼主 | 2024-1-31 15:02 | 只看该作者
RTC 核心接口:由一组可编程计数器组成,分成两个主要模块 。 第一个模块是 RTC 的 预分频模块,它可编程产生 1 秒的 RTC 时间基准 TR_CLK。RTC 的预分频模块包含了一个 20 位的可编程分频器(RTC 预分频器)。如果在 RTC_CR 寄存器中设置了相应的允许位,则在每个 TR_CLK 周期中 RTC 产生一个中断(秒中断)。 第二个模块是一个 32 位的可编程计数器(RTC_CNT),可被初始化为当前的系统时间,一个 32 位的时钟计数器,按秒钟计算,可以记 录 4294967296 秒,约合 136 年左右,作为一般应用,这已经是足够了的。

使用特权

评论回复
5
kepe|  楼主 | 2024-1-31 15:03 | 只看该作者
RTC具体流程:
RTCCLK经过RTC_DIV预分频,RTC_PRL设置预分频系数,然后得到TR_CLK时钟信号,我们一般设置其周期为1s,RTC_CNT计数器计数,假如1970设置为时间起点为0s,通过当前时间的秒数计算得到当前的时间。RTC_ALR是设置闹钟时间,RTC_CNT计数到RTC_ALR就会产生计数中断。

  RTC_Second为秒中断,用于刷新时间
  RTC_Overflow是溢出中断
  RTC Alarm 控制开关机
1
2
3
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)的意外写操作

使用特权

评论回复
6
kepe|  楼主 | 2024-1-31 15:03 | 只看该作者
RTC中断:

秒中断
这里时钟自带一个秒中断,每当计数加一的时候就会触发一次秒中断,。注意,这里所说的秒中断并非一定是一秒的时间,它是由RTC时钟源和分频值决定的“秒”的时间,当然也是可以做到1秒钟中断一次。我们通过往秒中断里写更新时间的函数来达到时间同步的效果
闹钟中断
闹钟中断就是设置一个预设定的值,计数每自加多少次触发一次闹钟中断

二、CubeMX配置
1.配置RCC
开启高速外部时钟HSE,使能外部晶振LSE

使用特权

评论回复
7
kepe|  楼主 | 2024-1-31 15:04 | 只看该作者
2.配置RTC
激活时钟源(Activate Clock Source)和日历(Activate Calendar)

Activate Clock Source:     激活时钟源
Activate calendar:         激活日历
RTC_OUT: Not RTC_OUT
Tamper: ×
第一个:使能 tamper(PC13)引脚上输出校正的秒脉冲时钟,
第二个: RTC入侵检测校验功能
RTC校验功能,使能侵入检测功能。RTC时钟经64分频输出到侵入检测引脚TAMPER上
当 TAMPER引脚上的信号从 0变成1或者从 1变成 0(取决于备份控制寄存器BKP_CR的 TPAL位),会产生一个侵入检测事件。
侵入检测事件将所有数据备份寄存器内容清除。

也就是第一个是使能tamper(PC13)引脚作为时钟脉冲输出
第二个是使能tamper(PC13)引脚作为入侵检测功能

下面是两个RTC的中断:
RTC全局中断RTC_IRQHandler()
闹钟中断函数RTCAlarm_IRQHandler()

使用特权

评论回复
8
kepe|  楼主 | 2024-1-31 15:04 | 只看该作者
设置时间为当前时间:2023/11/21 19:25

使能串口

使用特权

评论回复
9
kepe|  楼主 | 2024-1-31 15:05 | 只看该作者
4.时钟树配置

5.项目配置,并生成代码

使用特权

评论回复
10
kepe|  楼主 | 2024-1-31 15:05 | 只看该作者
三、keil代码改写
打开stm32f1xx_hal_rtc.h文件可以看到以下关于时间和日期的函数

使用特权

评论回复
11
kepe|  楼主 | 2024-1-31 15:06 | 只看该作者
日期结构体

使用特权

评论回复
12
kepe|  楼主 | 2024-1-31 15:06 | 只看该作者
时间结构体


开始编写代码

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;
}

使用特权

评论回复
13
kepe|  楼主 | 2024-1-31 15:07 | 只看该作者
在主函数内 / while循环外定义时间和日期的结构体用来获取时间和日期
RTC_DateTypeDef GetData;  //获取日期结构体
RTC_TimeTypeDef GetTime;   //获取时间结构体

在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 */
          /* 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");
                }
      printf("%02d:%02d:%02d\r\n",GetTime.Hours, GetTime.Minutes, GetTime.Seconds);

      printf("\r\n");

      HAL_Delay(1000);

    /* USER CODE END WHILE */

使用特权

评论回复
14
kepe|  楼主 | 2024-1-31 15:08 | 只看该作者
效果展示:

如果串口无法输出内容:
魔术棒>>Target>>UseMicroLIB

四、读取AHT20的温度和湿度
在以上实验基础上,加上读取AHT20的温度和湿度,一起显示在OLED上。




使用特权

评论回复
15
kepe|  楼主 | 2024-1-31 15:11 | 只看该作者
修改后的main.c函数

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "dma.h"
#include "i2c.h"
#include "rtc.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "AHT20-21_DEMO_V1_3.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
int fputc(int ch,FILE *f)
{
uint8_t temp[1]={ch};
HAL_UART_Transmit(&huart1,temp,1,2);
return ch;
}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
        uint32_t CT_data[2]={0,0};
        volatile int  c1,t1;
        Delay_1ms(500);
       
        RTC_DateTypeDef GetData;  //获取日期结构体
        RTC_TimeTypeDef GetTime;   //获取时间结构体
  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_RTC_Init();
  MX_USART1_UART_Init();
  MX_I2C1_Init();
  /* USER CODE BEGIN 2 */
        AHT20_Init();
        Delay_1ms(500);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
          AHT20_Read_CTdata_crc(CT_data);  //crc校验后,读取AHT20的温度和湿度数据
                c1 = CT_data[0]*1000/1024/1024;  //计算得到湿度值c1(放大了10倍)
                t1 = CT_data[1]*2000/1024/1024-500;//计算得到温度值t1(放大了10倍)
          
          /* 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 */
          /* 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");
                }
      printf("%02d:%02d:%02d\r\n",GetTime.Hours, GetTime.Minutes, GetTime.Seconds);
                printf("温度:%d%d.%d",t1/100,(t1/10)%10,t1%10);
                printf("湿度:%d%d.%d",c1/100,(c1/10)%10,c1%10);
               
      printf("\r\n");

      HAL_Delay(1000);

    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE|RCC_OSCILLATORTYPE_LSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.LSEState = RCC_LSE_ON;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC;
  PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

使用特权

评论回复
16
kepe|  楼主 | 2024-1-31 15:12 | 只看该作者
效果展示:

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

25

主题

207

帖子

0

粉丝