STN32入门学习 第五天

提示:今天是STM32学习的第四天,今天的学习笔记是RTC时钟,待机唤醒实验。 

 

目录

第一讲 USMART 调试组件实验

1 USMART 调试组件简介

2.USMART 组件的移植

第二讲 RTC实时时钟+BKP 

1.RTC实时时钟特征与原理图

1.1RTC (Real Time Clock):实时时钟

 1.2 RTC时钟源​编辑

2.RTC常用寄存器+库函数讲解

2.1常用寄存器

2.2uRTC相关库函数讲解

3.实验讲解

第三讲 待机唤醒实验

1.STM32低功耗模式讲解

1.1低功耗模式

1.2待机模式 

2. 寄存器和库函数配置 

2.1 寄存器

2.2库函数

3. 实验程序讲解


第一讲 USMART 调试组件实验

1 USMART 调试组件简介

USMART 是由 ALIENTEK 开发的一个灵巧的串口调试互交组件,通过它你可以通过串口 助手调用程序里面的任何函数,并执行。因此,你可以随意更改函数的输入参数(支持数字(10/16 进制)、字符串、函数入口地址等作为参数),单个函数最多支持 10 个输入参数,并支持函数返 回值显示,目前最新版本为 V3.1。

USMART 的特点如下:

  • 1, 可以调用绝大部分用户直接编写的函数。
  • 2, 资源占用极少(最少情况:FLASH:4K;SRAM:72B)。
  • 3, 支持参数类型多(数字(包含 10/16 进制)、字符串、函数指针等)。
  • 4, 支持函数返回值显示。
  • 5, 支持参数及返回值格式设置。
  • 6, 支持函数执行时间计算(V3.1 版本新特性)。
  • 7, 使用方便。

有了 USMART,可以轻易的修改函数参数、查看函数运行结果,从而快速解决问题。比如调试一个摄像头模块,需要修改其中的几个参数来得到最佳的效果,普通的做法:写函数 ->修改参数->下载->看结果->不满意->修改参数->下载->看结果->不满意….不停的循环,直到满 意为止。这样做很麻烦不说,单片机也是有寿命的啊,老这样不停的刷,很折寿。而利用 USMART,则只需要在串口调试助手里面输入函数及参数,然后直接串口发送给单片机,就执行了一次参数调整,不满意的话,你在串口调试助手修改参数在发送就可以了,直到你满意为止。这样,修改参数十分方便,不需要编译、不需要下载、不会让单片机折寿。

USMART 支持的参数类型基本满足任何调试了,支持的类型有:10 或者 16 进制数字、字 符串指针(如果该参数是用作参数返回的话,可能会有问题!)、函数指针等。因此绝大部分函 数,可以直接被 USMART 调用,对于不能直接调用的,你只需要重写一个函数,把影响调用 的参数去掉即可,这个重写后的函数,即可以被 USMART 调用了。

USMART 的实现流程简单概括就是:第一步,添加需要调用的函数(在 usmart_config.c 里 面的 usmart_nametab 数组里面添加);第二步,初始化串口;第三步,初始化 USMART(通过 usmart_init 函数实现);第四步,轮询 usmart_scan 函数,处理串口数据。

2.USMART 组件的移植

经过以上简单介绍,我们对 USMART 有了个大概了解,接下来我们来简单介绍下 USMART 组件的移植。USMART 组件总共包含 6 文件。其中 redeme.txt 是一个说明文件,不参与编译。其他五个文件,usmart.c 负责与外部互交等。 usmat_str.c 主要负责命令和参数解析。usmart_config.c 主要由用户添加需要由 usmart 管理的函 数。 usmart.h 和 usmart_str.h 是两个头文件,其中 usmart.h 里面含有几个用户配置宏定义,可以 用来配置 usmart 的功能及总参数长度(直接和 SRAM 占用挂钩)、是否使能定时器扫描、是否使 用读写函数等。

USMART 的移植,只需要实现 5 个函数。其中 4 个函数都在 usmart.c 里面,另外一个是串 口接收函数,必须有由用户自己实现,用于接收串口发送过来的数据。

第一个函数,串口接收函数。该函数,我们是通过 SYSTEM 文件夹默认的串口接收来实现 的,该函数在有介绍过。SYSTEM 文件夹里面的串口接收函 数,最大可以一次接收 200 字节,用于从串口接收函数名和参数等。大家如果在其他平台移植, 请参考 SYSTEM 文件夹串口接收的实现方式进行移植。

第二个是 void usmart_init(void)函数,该函数的实现代码如下:

//初始化串口控制器
//sysclk:系统时钟(Mhz)
void usmart_init(u8 sysclk)
{
#if USMART_ENTIMX_SCAN==1
Timer4_Init(1000,(u32)sysclk*100-1); //分频,时钟为 10K ,100ms 中断一次
//注意,计数频率必须为 10Khz,以和 runtime 单位(0.1ms)同步.
#endif
usmart_dev.sptype=1; //十六进制显示参数
}

第三和第四个函数仅用于服务 USMART 的函数执行时间统计功能(串口指令:runtime 1), 分别是:usmart_reset_runtime 和 usmart_get_runtime,这两个函数代码如下:

//复位 runtime
//需要根据所移植到的 MCU 的定时器参数进行修改
void usmart_reset_runtime(void)
{
TIM_ClearFlag(TIM4,TIM_FLAG_Update);//清除中断标志位
TIM_SetAutoreload(TIM4,0XFFFF);//将重装载值设置到最大
TIM_SetCounter(TIM4,0); //清空定时器的 CNT
usmart_dev.runtime=0; }
//获得 runtime 时间
//返回值:执行时间,单位:0.1ms,最大延时时间为定时器 CNT 值的 2 倍*0.1ms
//需要根据所移植到的 MCU 的定时器参数进行修改
u32 usmart_get_runtime(void)
{
if(TIM_GetFlagStatus(TIM4,TIM_FLAG_Update)==SET)//在运行期间产生定时器溢出
{
usmart_dev.runtime+=0XFFFF;
}
usmart_dev.runtime+=TIM_GetCounter(TIM4);
return usmart_dev.runtime; //返回计数值}

最后一个是 usmart_scan 函数,该函数用于执行 usmart 扫描,该函数需要得到两个参量, 第一个是从串口接收到的数组(USART_RX_BUF),第二个是串口接收状态(USART_RX_STA)。 接收状态包括接收到的数组大小,以及接收是否完成。

完成这几个函数的移植,你就可以使用 USMART 了。不过,需要注意的是,usmart 同外 部的互交,一般是通过 usmart_dev 结构体实现,所以 usmart_init 和 usmart_scan 的调用分别是 通过:usmart_dev.init 和 usmart_dev.scan 实现的。

第二讲 RTC实时时钟+BKP 

  • 1.RTC实时时钟特征与原理图+BKP备份寄存器原理.
  • 2.RTC常用寄存器+库函数讲解
  • 3.实验讲解

1.RTC实时时钟特征与原理图

1.1RTC (Real Time Clock):实时时钟

RTC是个独立的定时器。RTC模块拥有一个连续计数的计数器,在相应的软件配置下,可以提供时钟日历的功能。修改计数器的值可以重新设置当前时间和日期。RTC模块和时钟配置系统(RCC_BDCR寄存器)是在后备区域,即在系统复位或从待机模式唤醒后RTC的设置和时间维持不变。但是在系统复位后,会自动禁止访问后备寄存器和RTC,以防止对后备区域(BKP)的意外写操作。所以在要设置时间之前, 先要取消备份区域(BKP)写保护。

RTC特征STN32入门学习 第五天_第1张图片

RTC工作原理框图

STN32入门学习 第五天_第2张图片

RTC由两部分组成:
APB1接口:用来和APB1总线相连。通过APB1接口可以访问RTC的相关寄存器(预分频值,计数器值,闹钟值)。
RTC核心:由一组可编程计数器组成。分两个主要模块。
第一个是RTC预分频模块,它可以编程产生最长1秒的RTC时间基TR_CLK。如果设置了秒中断允许位,可以产生秒中断。
第二个是32位的可编程计数器,可被初始化为当前时间。系统时间按TR_CLK周期累加并与存储在RTC_ALR寄存器中的可编程时间相比,当匹配时候如果设置了闹钟中断允许位,可以产生闹钟中断。

RTC内核完全独立于APB1接口,软件通过APB1接口对RTC相关寄存器访问。但是相关寄存器只在RTC APB1时钟进行重新同步的RTC时钟的上升沿被更新。所以软件必须先等待寄存器同步标志位(RTC_CRL的RSF位)被硬件置1才读。

 1.2 RTC时钟源STN32入门学习 第五天_第3张图片

    BKP备份寄存器原理

①备份寄存器是42个16位的寄存器。可用来存储84个字节数据。
②它们处在备份区域,当VDD电源切断,仍然由VBAT维持供电。
③当系统在待机模式下被唤醒,或者系统复位或者电源复位,它们也不会复位。
④执行以下操作将使能对后备寄存器和RTC访问:
ü设置寄存器RCC_APB1ENR的PWREN和BKPEN位,使能电源和后备时钟。
ü设置寄存器PWR_CR的DBP位,使能对RTC和后备寄存器的访问。

 提醒:一共有42个16位备份寄存器。常用来保存一些系统配置信息和相关标志位。

STN32入门学习 第五天_第4张图片

 

2.RTC常用寄存器+库函数讲解

2.1常用寄存器

①RTC控制寄存器             (RTC_CRH,   RTC_CRL)
②RTC预分频装载寄存器  (RTC_PRLH, RTC_PRLL)
③RTC预分频余数寄存器  (RTC_DIVH,  RTC_DIVL)
④RTC计数器寄存器         (RTC_CNTH, RTC_CNTL)
⑤RTC闹钟寄存器             (RTC_ALRH ,RTC_ALRL)

RTC控制寄存器高位(RTC_CRH)

STN32入门学习 第五天_第5张图片

STN32入门学习 第五天_第6张图片

①修改CRH/CRL寄存器,必须先判断RSF位,确定已经同步。

②修改CNT,ALR,PRL的时候,必须先配置CNF位进入配置模式,修改完之后,设置CNF位为0退出配置模式

③同时在对RTC相关寄存器写操作之前,必须判断上一次写操作已经结束,也就是判断RTOFF位是否置位。

STN32入门学习 第五天_第7张图片

STN32入门学习 第五天_第8张图片

STN32入门学习 第五天_第9张图片

STN32入门学习 第五天_第10张图片

STN32入门学习 第五天_第11张图片

STN32入门学习 第五天_第12张图片

2.2uRTC相关库函数讲解

库函数所在文件:

   stm32f10x_rtc.c / stm32f10x_rtc.h

lRTC时钟源和时钟操作函数:

 void RCC_RTCCLKConfig(uint32_t  CLKSource);//时钟源选择

 void RCC_RTCCLKCmd(FunctionalState NewState)//时钟使能

RTC配置函数(预分频,计数值:

void RTC_SetPrescaler(uint32_t PrescalerValue);//预分频配置:PRLH/PRLL

void RTC_SetCounter(uint32_t CounterValue);//设置计数器值:CNTH/CNTL

void RTC_SetAlarm(uint32_t AlarmValue);//闹钟设置:ALRH/ALRL

RTC中断设置函数:

 void RTC_ITConfig(uint16_t RTC_IT, FunctionalState NewState);//CRH

RTC允许配置和退出配置函数:

void RTC_EnterConfigMode(void);//允许RTC配置 :CRL位 CNF

void RTC_ExitConfigMode(void);//退出配置模式:CRL位 CNF

同步函数:

void RTC_WaitForLastTask(void);//等待上次操作完成:CRL位RTOFF

 void RTC_WaitForSynchro(void);//等待时钟同步:CRL位RSF

相关状态位获取清除函数:

FlagStatus RTC_GetFlagStatus(uint16_t RTC_FLAG);

void RTC_ClearFlag(uint16_t RTC_FLAG);

ITStatus RTC_GetITStatus(uint16_t RTC_IT);

void RTC_ClearITPendingBit(uint16_t RTC_IT);

其他相关函数(BKP等)

 PWR_BackupAccessCmd();//BKP后备区域访问使能

RCC_APB1PeriphClockCmd();//使能PWR和BKP时钟

RCC_LSEConfig();//开启LSE,RTC选择LSE作为时钟源 

其他相关函数(BKP等)

 PWR_BackupAccessCmd();//BKP后备区域访问使能

uint16_t BKP_ReadBackupRegister(uint16_t BKP_DR);//读BKP寄存器

void BKP_WriteBackupRegister(uint16_t BKP_DR, uint16_t Data);//写BKP

RTC配置一般步骤

1.使能PWR和BKP时钟:RCC_APB1PeriphClockCmd();
2.使能后备寄存器访问:   PWR_BackupAccessCmd();
3.配置RTC时钟源,使能RTC时钟:
      RCC_RTCCLKConfig();
      RCC_RTCCLKCmd();
      如果使用LSE,要打开LSE:RCC_LSEConfig(RCC_LSE_ON);
4.设置RTC预分频系数:RTC_SetPrescaler();
5.设置时间:RTC_SetCounter();
6.开启相关中断(如果需要):RTC_ITConfig();
7.编写中断服务函数:RTC_IRQHandler();
8.部分操作要等待写操作完成和同步。
   RTC_WaitForLastTask();//等待最近一次对RTC寄存器的写操作完成
   RTC_WaitForSynchro();	//等待RTC寄存器同步 

3.实验讲解

本实验用到的硬件资源有: 1) 指示灯 DS0 2) 串口 3) TFTLCD 模块 4) RTC

RTC 属于 STM32 内部资源,其配置也是通过软件设置好就可以 了。不过 RTC 不能断电,否则数据就丢失了,我们如果想让时间在断电后还可以继续走,那么 必须确保有电池。

第三讲 待机唤醒实验

  • 1.STM32低功耗模式讲解
  • 2. 寄存器和库函数配置
  • 3. 实验程序讲解

1.STM32低功耗模式讲解

1.1低功耗模式

很多单片机都有低功耗模式,STM32 也不例外。在系统或电源复位以后,微控制器处于运 行状态。运行状态下的 HCLK 为 CPU 提供时钟,内核执行程序代码。当 CPU 不需继续运行时, 可以利用多个低功耗模式来节省功耗,例如等待某个外部事件时。用户需要根据最低电源消耗, 最快速启动时间和可用的唤醒源等条件,选定一个最佳的低功耗模式。STM32 的 3 种低功耗模式:
STM32 的低功耗模式有 3 种:
1)睡眠模式(CM3 内核停止,外设仍然运行)
2)停止模式(所有时钟都停止)
3)待机模式(1.8V 内核电源关闭)
运行模式下,我们也可以通过降低系统时钟关闭 APB 和 AHB 总线上未被使用的外设的 时钟来降低功耗。
STN32入门学习 第五天_第13张图片

 

在这三种低功耗模式中,最低功耗的是待机模式,在此模式下,最低只需要 2uA 左右的电 流。停机模式是次低功耗的,其典型的电流消耗在 20uA 左右。最后就是睡眠模式了。 

1.2待机模式 

在这三种低功耗模式中,最低功耗的是待机模式,在此模式下,最低只需要 2uA 左右的电 流。停机模式是次低功耗的,其典型的电流消耗在 20uA 左右。最后就是睡眠模式了。
STN32入门学习 第五天_第14张图片
从待机模式唤醒后的代码执行等同于复位后的执行(采样启动模式引脚,读取复位向量等)。 电源控制/状态寄存器(PWR_CSR)将会指示内核由待机状态退出。 在进入待机模式后,除了复位引脚以及被设置为防侵入或校准输出时的 TAMPER 引脚和被 使能的唤醒引脚(WK_UP 脚),其他的 IO 引脚都将处于高阻态。

2. 寄存器和库函数配置 

2.1 寄存器

进入待机模式的通用步骤,其中涉及到 2 个寄存器,即电源控 制寄存器(PWR_CR)和电源控制/状态寄存器(PWR_CSR)。下面我们介绍一下这两个寄存器:
电源控制寄存器(PWR_CR)
电源控制/状态寄存器(PWR_CSR)

2.2库函数

通过以上介绍,我们了解了进入待机模式的方法,以及设置 WK_UP 引脚用于把 STM32 从待机模式唤醒的方法。具体步骤如下:

1)使能电源时钟。 因为要配置电源控制寄存器,所以必须先使能电源时钟。 在库函数中,使能电源时钟的方法是: RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); //使能 PWR 外设时钟 

2) 设置 WK_UP 引脚作为唤醒源。 使能时钟之后后再设置 PWR_CSR 的 EWUP 位,使能 WK_UP 用于将 CPU 从待机模式唤 醒。在库函数中,设置使能 WK_UP 用于唤醒 CPU 待机模式的函数是:  PWR_WakeUpPinCmd(ENABLE); //使能唤醒管脚功能

3)设置 SLEEPDEEP 位,设置 PDDS 位,执行 WFI 指令,进入待机模式。 进入待机模式,首先要设置 SLEEPDEEP 位(该位在系统控制寄存器(SCB_SCR)的第 二位,详见《CM3 权威指南》,第 182 页表 13.1),接着我们通过 PWR_CR 设置 PDDS 位,使 得 CPU 进入深度睡眠时进入待机模式,最后执行 WFI 指令开始进入待机模式,并等待 WK_UP 中断的到来。在库函数中,进行上面三个功能进入待机模式是在函数 PWR_EnterSTANDBYMode 中实现的: void PWR_EnterSTANDBYMode(void);

4)最后编写 WK_UP 中断函数。 因为我们通过 WK_UP 中断(PA0 中断)来唤醒 CPU,所以我们有必要设置一下该中断函 数,同时我们也通过该函数里面进入待机模式。

3. 实验程序讲解

通过以上几个步骤的设置,我们就可以使用 STM32 的待机模式了,并且可以通过 WK_UP 来唤醒 CPU,我们最终要实现这样一个功能:通过长按(3 秒)WK_UP 按键开机,并且通过 DS0 的闪烁指示程序已经开始运行,再次长按该键,则进入待机模式,DS0 关闭,程序停止运 行。类似于手机的开关机。

wkup.c 和 wkup.h 文件相关的用 户代码写在这两个文件中。我们需要引入 stm32f10x_pwr.c 和 stm32f0x_pwr.h 文件。

void Sys_Standby(void)
{ 
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); //使能PWR外设时钟
PWR_WakeUpPinCmd(ENABLE); //使能唤醒管脚功能
PWR_EnterSTANDBYMode(); //进入待命(STANDBY)模式
}
//系统进入待机模式
void Sys_Enter_Standby(void)
{
RCC_APB2PeriphResetCmd(0X01FC,DISABLE); //复位所有 IO 口
Sys_Standby();
}
//检测 WKUP 脚的信号
//返回值 1:连续按下 3s 以上
// 0:错误的触发

u8 Check_WKUP(void) 
{
u8 t=0; //记录按下的时间
LED0=0; //亮灯 DS0 
while(1)
{
if(WKUP_KD)
{
t++; //已经按下了
delay_ms(30);
if(t>=100) //按下超过 3 秒钟
{
LED0=0; //点亮 DS0 
return 1; //按下 3s 以上了
}
}else 
{ 
LED0=1;
return 0; //按下不足 3 秒
}
}
} 
//中断,检测到 PA0 脚的一个上升沿. 
//中断线 0 线上的中断检测
void EXTI0_IRQHandler(void)
{ 
EXTI_ClearITPendingBit(EXTI_Line0); // 清除 LINE10 上的中断标志位 
if(Check_WKUP()) //关机?
{ 
Sys_Enter_Standby(); 
}
} 
//PA0 WKUP 唤醒初始化
void WKUP_Init(void)
{ GPIO_InitTypeDef GPIO_InitStructure; 
NVIC_InitTypeDef NVIC_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | 
RCC_APB2Periph_AFIO, ENABLE); //使能 GPIOA 和复用功能时钟
GPIO_InitStructure.GPIO_Pin =GPIO_Pin_0; //PA.0
GPIO_InitStructure.GPIO_Mode =GPIO_Mode_IPD; //上拉输入

GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 IO
 //使用外部中断方式
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
//中断线 0 连接 GPIOA.0
 EXTI_InitStructure.EXTI_Line = EXTI_Line0; //设置按键所有的外部线路
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //外部中断模式
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; //上升沿触发
 EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure); // 初始化外部中断
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //使能外部中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //先占优先级 2 级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //从优先级 2 级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //外部中断通道使能
NVIC_Init(&NVIC_InitStructure); //初始化 NVIC
if(Check_WKUP()==0) Sys_Standby(); //不是开机,进入待机模式 
}
1,在 void Sys_Enter_Standby(void)函数里面,我们要在进入待机模式前把所有开启的外设 全部关闭,我们这里仅仅复位了所有的 IO 口,使得 IO 口全部为浮空输入。其他外设(比如 ADC 等),大家根据自己所开启的情况进行一一关闭就可,这样才能达到最低功耗!
2,在 void WKUP_Init(void)函数里面,我们要先判断 WK_UP 是否按下了 3 秒钟,来决定 要不要开机,如果没有按下 3 秒钟,程序直接就进入了待机模式。所以在下载完代码的时候, 是看不到任何反应的。我们必须先按 WK_UP 按键 3 秒钟以开机,才能看到 DS0 闪烁。
int main(void)
{
delay_init(); //延时函数初始化 
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置 NVIC 中断分组 2
uart_init(115200); //串口初始化波特率为 115200
LED_Init(); //LED 端口初始化
WKUP_Init(); //待机唤醒初始化
LCD_Init(); //LCD 初始化
POINT_COLOR=RED;
LCD_ShowString(30,50,200,16,16,"Warship STM32");
LCD_ShowString(30,70,200,16,16,"WKUP TEST");
LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
LCD_ShowString(30,110,200,16,16,"2014/1/14");
while(1)
{ LED0=!LED0;
delay_ms(250);
}
}
里我们先初始化 LED 和 WK_UP 按键(通过 WKUP_Init()函数初始化),如果检测到 有长按 WK_UP 按键 3 秒以上,则开机,并执行 LCD 初始化,在 LCD 上面显示一些内容,如 果没有长按,则在 WKUP_Init 里面,调用 Sys_Enter_Standby 函数,直接进入待机模式了。 开机后,在死循环里面等待 WK_UP 中断的到来,在得到中断后,在中断函数里面判断 WK_UP 按下的时间长短,来决定是否进入待机模式,如果按下时间超过 3 秒,则进入待机, 否则退出中断,继续执行 main 函数的死循环等待,同时不停的取反 LED0,让红灯闪烁。

你可能感兴趣的:(单片机,学习,stm32)