当前位置: 首页 > article >正文

25/1/18 嵌入式笔记 STM32F103

Unix时间戳

BKP和RTC

RTC基本结构

是一种用于跟踪时间的硬件模块,通常集成在微控制器或独立的芯片中。RTC 可以提供年、月、日、时、分、秒等时间信息,并且在系统断电时通过备用电池保持运行。

RTC 的基本功能

  • 时间跟踪:提供年、月、日、时、分、秒等信息。

  • 日期计算:自动处理闰年、月份天数等。

  • 闹钟功能:在特定时间触发中断。

  • 低功耗:在系统断电时通过备用电池供电。

RTC 的硬件连接

RTC 通常需要以下硬件支持:

  • 主电源:为 RTC 提供运行电源。

  • 备用电池:在系统断电时为 RTC 供电(如纽扣电池)。

  • 晶振:提供精确的时钟信号(通常为 32.768 kHz)。

 RTC 的寄存器

RTC 通常通过一组寄存器来配置和读取时间信息。常见的寄存器包括:

  • 时间寄存器:存储时、分、秒。

  • 日期寄存器:存储年、月、日。

  • 控制寄存器:配置 RTC 的工作模式。

  • 闹钟寄存器:设置闹钟时间。

 RTC 的实现步骤

初始化 RTC
RTC_HandleTypeDef hrtc;

void RTC_Init(void) {
    hrtc.Instance = RTC;
    hrtc.Init.HourFormat = RTC_HOURFORMAT_24;  // 24 小时制
    hrtc.Init.AsynchPrediv = 127;              // 异步预分频
    hrtc.Init.SynchPrediv = 255;               // 同步预分频
    hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;     // 禁用输出
    hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
    hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
    HAL_RTC_Init(&hrtc);
}
解析
  • hrtc.Instance:选择 RTC 外设实例。

  • hrtc.Init.HourFormat:设置时间格式(12 小时制或 24 小时制)。

  • hrtc.Init.AsynchPrediv 和 hrtc.Init.SynchPrediv:设置预分频值,用于生成 1 Hz 的时钟信号。

  • HAL_RTC_Init(&hrtc):初始化 RTC 外设。

设置时间和日期
void RTC_SetTime(uint8_t hour, uint8_t minute, uint8_t second) {
    RTC_TimeTypeDef sTime = {0};
    sTime.Hours = hour;
    sTime.Minutes = minute;
    sTime.Seconds = second;
    HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
}

void RTC_SetDate(uint8_t year, uint8_t month, uint8_t day) {
    RTC_DateTypeDef sDate = {0};
    sDate.Year = year;
    sDate.Month = month;
    sDate.Date = day;
    HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
}
  • RTC 需要初始时间和日期才能开始计时。

  • 通过设置函数初始化 RTC 的时间和日期。

解析
  • RTC_TimeTypeDef 和 RTC_DateTypeDef:时间和日期的结构体。

  • HAL_RTC_SetTime() 和 HAL_RTC_SetDate():HAL 库函数,用于设置时间和日期。

读取时间和日期
void RTC_GetTime(uint8_t *hour, uint8_t *minute, uint8_t *second) {
    RTC_TimeTypeDef sTime = {0};
    HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
    *hour = sTime.Hours;
    *minute = sTime.Minutes;
    *second = sTime.Seconds;
}

void RTC_GetDate(uint8_t *year, uint8_t *month, uint8_t *day) {
    RTC_DateTypeDef sDate = {0};
    HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
    *year = sDate.Year;
    *month = sDate.Month;
    *day = sDate.Date;
}
作用
  • 读取 RTC 的当前时间和日期。

为什么需要
  • 通过读取函数获取 RTC 的当前时间和日期。

解析
  • HAL_RTC_GetTime() 和 HAL_RTC_GetDate():HAL 库函数,用于读取时间和日期。

主函数中使用
int main(void) {
    HAL_Init();
    SystemClock_Config();
    RTC_Init();  // 初始化 RTC

    // 设置初始时间和日期
    RTC_SetTime(12, 0, 0);  // 12:00:00
    RTC_SetDate(23, 10, 15); // 2023 年 10 月 15 日

    uint8_t hour, minute, second, year, month, day;

    while (1) {
        // 读取当前时间和日期
        RTC_GetTime(&hour, &minute, &second);
        RTC_GetDate(&year, &month, &day);

        // 打印时间和日期(通过串口或调试工具)
        printf("Time: %02d:%02d:%02d\n", hour, minute, second);
        printf("Date: 20%02d-%02d-%02d\n", year, month, day);

        HAL_Delay(1000);  // 延时 1 秒
    }
}
解析
  • HAL_Init():初始化 HAL 库。

  • SystemClock_Config():配置系统时钟。

  • RTC_Init():初始化 RTC 外设。

  • RTC_SetTime() 和 RTC_SetDate():设置初始时间和日期。

  • RTC_GetTime() 和 RTC_GetDate():读取当前时间和日期。

  • printf():打印时间和日期(需要重定向串口)。

  • HAL_Delay(1000):延时 1 秒。

PWR电源控制

修改主频的配置

修改主频是通过配置系统时钟源和分频器来实现的。STM32 的时钟源包括:

  • HSI:内部高速时钟(通常为 8 MHz)。

  • HSE:外部高速时钟(通常为 8-25 MHz)。

  • PLL:锁相环,用于倍频时钟。

代码实现
void SystemClock_Config(void) {
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

    // 配置HSE和PLL
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    RCC_OscInitStruct.PLL.PLLM = 8;
    RCC_OscInitStruct.PLL.PLLN = 336;
    RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
    RCC_OscInitStruct.PLL.PLLQ = 7;
    HAL_RCC_OscConfig(&RCC_OscInitStruct);

    // 配置时钟树
    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_DIV4;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
    HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5);
}
  1. HSE(外部高速时钟)

    • HSE通常是一个外部晶振(如8MHz),提供更精确和稳定的时钟源。

    • RCC_OscInitStruct.HSEState = RCC_HSE_ON:启用HSE。

  2. PLL(锁相环)

    • PLL用于将输入时钟倍频到更高的频率。

    • PLLM = 8:将HSE时钟分频为1MHz(8MHz / 8 = 1MHz)。

    • PLLN = 336:将1MHz倍频到336MHz。

    • PLLP = RCC_PLLP_DIV2:将PLL输出分频为168MHz(336MHz / 2 = 168MHz),作为系统时钟(SYSCLK)。

    • PLLQ = 7:用于生成USB、SDIO等外设的时钟。

  3. 时钟树配置

    • SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK:选择PLL输出作为系统时钟。

    • AHBCLKDivider = RCC_SYSCLK_DIV1:AHB总线时钟与系统时钟相同(168MHz)。

    • APB1CLKDivider = RCC_HCLK_DIV4:APB1总线时钟为42MHz(168MHz / 4)。

    • APB2CLKDivider = RCC_HCLK_DIV2:APB2总线时钟为84MHz(168MHz / 2)。

  4. FLASH_LATENCY

    • 根据主频设置FLASH延迟,确保CPU能够正确读取FLASH中的数据。168MHz需要FLASH_LATENCY_5

睡眠模式的配置

睡眠模式下,CPU停止运行,但外设和时钟仍然运行。可以通过WFI(等待中断)或WFE(等待事件)指令进入睡眠模式。

代码解析:

void Enter_Sleep_Mode(void) {
    HAL_SuspendTick();  // 挂起SysTick
    HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);
    HAL_ResumeTick();   // 恢复SysTick
}

配置原因:

  1. 挂起SysTick

    • HAL_SuspendTick():在进入睡眠模式前,挂起SysTick定时器,避免其产生中断唤醒CPU。

  2. 进入睡眠模式

    • HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI)

      • PWR_MAINREGULATOR_ON:保持主稳压器开启,以快速恢复运行。

      • PWR_SLEEPENTRY_WFI:使用WFI指令进入睡眠模式。

  3. 恢复SysTick

    • HAL_ResumeTick():退出睡眠模式后,恢复SysTick定时器。

停止模式的配置

停止模式下,所有时钟都停止,但SRAM和寄存器内容保持不变。可以通过WFIWFE指令进入停止模式。

代码解析:

void Enter_Stop_Mode(void) {
    HAL_SuspendTick();  // 挂起SysTick
    HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
    HAL_ResumeTick();   // 恢复SysTick
    SystemClock_Config();  // 重新配置时钟
}

配置原因:

  1. 挂起SysTick

    • HAL_SuspendTick():挂起SysTick定时器。

  2. 进入停止模式

    • HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI)

      • PWR_LOWPOWERREGULATOR_ON:启用低功耗稳压器,进一步降低功耗。

      • PWR_STOPENTRY_WFI:使用WFI指令进入停止模式。

  3. 恢复时钟

    • 退出停止模式后,时钟会被关闭,因此需要重新配置时钟树(SystemClock_Config())。

 待机模式的配置

待机模式下,除了备份域和待机电路,所有时钟和电源都被关闭。可以通过WFIWFE指令进入待机模式。

代码解析:

void Enter_Standby_Mode(void) {
    HAL_PWR_EnterSTANDBYMode();
}

配置原因:

  1. 进入待机模式

    • HAL_PWR_EnterSTANDBYMode():关闭所有时钟和电源,仅保留备份域和待机电路。

    • 唤醒后,系统会从头开始执行(类似于复位)。

总结

  • 修改主频:通过配置时钟树,选择合适的时钟源和PLL参数,达到所需的主频。

  • 睡眠模式:挂起SysTick,使用WFI进入睡眠模式,适用于需要快速唤醒的场景。

  • 停止模式:挂起SysTick,使用WFI进入停止模式,适用于需要更低功耗的场景。

  • 待机模式:直接进入待机模式,适用于需要最低功耗的场景。

WDG看门狗

看门狗(Watchdog,简称WDG)是嵌入式系统中用于检测和恢复系统故障的重要机制。它的核心原理是通过定时器监控系统的运行状态,如果系统在一定时间内未能正常“喂狗”(即重置看门狗定时器),看门狗会强制复位系统,从而防止系统因软件错误或硬件故障而进入死循环或卡死状态。

看门狗的基本原理

看门狗的核心是一个递减计数器(或递增计数器),当计数器达到特定值时,看门狗会触发系统复位。为了防止复位,系统需要定期“喂狗”,即在计数器达到特定值之前重置计数器。

工作流程:

  1. 初始化看门狗

    • 配置看门狗的计数器初始值和超时时间。

  2. 启动看门狗

    • 启动看门狗定时器,计数器开始递减(或递增)。

  3. 喂狗

    • 在程序正常运行期间,定期重置看门狗计数器。

  4. 超时复位

    • 如果程序未能及时喂狗,计数器达到超时值,看门狗触发系统复位。

  1. 独立看门狗(IWDG,Independent Watchdog)

    • 由独立的硬件模块实现,通常使用内部低速时钟(LSI)作为时钟源。

    • 独立于主系统时钟,即使主系统时钟失效,独立看门狗仍能正常工作。

    • 适用于检测系统死机或严重故障。

  2. 窗口看门狗(WWDG,Window Watchdog)

    • 由主系统时钟驱动,通常用于检测软件逻辑错误。

    • 喂狗时间必须在规定的“窗口”内,过早或过晚喂狗都会触发复位。

    • 适用于检测程序跑飞或逻辑错误。

独立看门狗(IWDG)配置示例(STM32 HAL库)

#include "stm32f4xx_hal.h"

void IWDG_Init(void) {
    // 初始化独立看门狗
    hiwdg.Instance = IWDG;
    hiwdg.Init.Prescaler = IWDG_PRESCALER_32;  // 预分频系数
    hiwdg.Init.Reload = 4095;                 // 计数器初值
    HAL_IWDG_Init(&hiwdg);
}

void IWDG_Feed(void) {
    // 喂狗
    HAL_IWDG_Refresh(&hiwdg);
}

窗口看门狗(WWDG)配置示例(STM32 HAL库):

#include "stm32f4xx_hal.h"

void WWDG_Init(void) {
    // 初始化窗口看门狗
    hwwdg.Instance = WWDG;
    hwwdg.Init.Prescaler = WWDG_PRESCALER_8;  // 预分频系数
    hwwdg.Init.Window = 0x5F;                // 上窗口值
    hwwdg.Init.Counter = 0x7F;               // 计数器初值
    hwwdg.Init.EWIMode = WWDG_EWI_DISABLE;   // 禁用早期唤醒中断
    HAL_WWDG_Init(&hwwdg);
}

void WWDG_Feed(void) {
    // 喂狗
    HAL_WWDG_Refresh(&hwwdg);
}

 Flash 内存的特点

  1. 非易失性

    • 数据在断电后不会丢失。

  2. 按扇区管理

    • Flash 内存通常分为多个扇区,每个扇区可以独立擦除和编程。

  3. 有限的擦写次数

    • Flash 内存的擦写次数有限(通常为 10,000 到 100,000 次),因此需要谨慎使用。

  4. 读写速度较慢

    • 相比 RAM,Flash 的读写速度较慢,尤其是写操作需要较长时间。

  5. 写前需擦除

    • Flash 内存的写操作需要先擦除,擦除的最小单位通常是一个扇区。

STM32 的 Flash 内存结构

以 STM32F4 系列为例,Flash 内存的结构如下:

  • 主存储区:用于存储程序代码和数据。

  • 系统存储区:存储 Bootloader 代码(由 ST 提供,用于串口或 USB 下载程序)。

  • 选项字节:用于配置读写保护、看门狗、复位模式等。


http://www.kler.cn/a/509330.html

相关文章:

  • mono3d汇总
  • 金融项目实战 06|Python实现接口自动化——日志、实名认证和开户接口
  • Spark任务提交流程
  • 4.Spring AI Prompt:与大模型进行有效沟通
  • 在服务器上增加新网段IP的路由配置
  • 【Pytorch实用教程】TCN(Temporal Convolutional Network,时序卷积网络)简介
  • Golang——常用库sync
  • QT 使用QSqlTableModel对数据库进行创建,插入,显示
  • github登录用的TOTP和恢复码都丢失了怎么办
  • linux m、mm、mmm函数和make的区别
  • 与“神”对话:Swift 语言在 2025 中的云霓之望
  • Qt的核心机制概述
  • Google Titans: 测试阶段的学习与记忆
  • OLED--软件I2C驱动__标准库和HAL库
  • Windows 上安装 MongoDB 的 zip 包
  • Git简介
  • Rust 强制类型转换和动态指针类型的转换
  • Springboot的自动装配原理
  • 第23篇 基于ARM A9处理器用汇编语言实现中断<五>
  • 麒麟系统中删除权限不够的文件方法
  • 什么是 OpenResty
  • python编程-OpenCV(图像读写-图像处理-图像滤波-角点检测-边缘检测)角点检测
  • springboot基于微信小程序的智慧小区管理系统
  • 基于SSM实现的乡村振兴文化平台系统功能实现六
  • PyTorch使用教程(2)-torch包
  • C# 多线程 Task TPL任务并行