从51开始,单片机玩了很长时间了,有51,PIC,AVR等等,早就想跟潮流玩玩ARM,但一直没有开始,原因 -----不知道玩了ARM可以做什么(对我自己而言)。如果为学习而学习,肯定学不好。然后cortex-m3出来了,据说,这东西可以替代单片机,于 是马上开始关注。也在第一时间开始学习,可惜一开始就有点站错了队,选错了型(仍是对我自己而言)。我希望这种芯片应该是满大街都是,随便哪里都可以买得 到,但我选的第一种显然做不到。为此,大概浪费了一年多时间吧,现在,回到对我来说是正确的道路上来啦,边学边写点东西。
闲话多了些,就权当前言了。下面准备开始。
STM32单片机小Tips之RTC实践
BKP还没有搞完,怎么又换到了RTC上了,因为RTC和BKP有些联系,想不关联还不行呢。
以下是数据手册上有关RTC的介绍:
******介绍开始
15.1 RTC 简介
实时时钟是一个独立的定时器。RTC模块拥有一组连续计数的计数器,在相应软件配置下,可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。
RTC模块和时钟配置系统(RCC_BDCR寄存器)是在后备区域,即在系统复位或从待机模式唤醒后RTC的设置和时间维持不变。
系统复位后,禁止访问后备寄存器和RTC,防止对后备区域(BKP)的意外写操作。执行以下操作使能对后备寄存器和RTC的访问:
● 设置寄存器RCC_APB1ENR的PWREN和BKPEN位来使能电源和后备接口时钟
● 设置寄存器PWR_CR的DBP位使能对后备寄存器和RTC的访问。
******介绍结束
下面就以库中所带的实例来玩这个RTC,正好也要解决TAMPER引脚的问题。
这次要用到的例子是这个:
有关配置没有什么可多说的,把上一个例子:BKP-》Backup_Data中修改过的:
stm32_eval.c
stm3210e_eval.h
stm32_eval.h
三个文件复制过来,建立工程,一路顺风,非常顺利地编译、链接好,然后进入调试,结果,出问题了,见下面:
进入调试界面即停在了反汇编界面。一旦执行程序,立即就停下来。这个BKPT指令是个什么东西呢?查了一下指令表,原来是个断点指令,那为什么会停止在这里呢?万能的网啊,一搜就出来了,感谢前人的努力,感谢伟大的网。..。
结论是:“ 凡是用到printf的都会出现这样的问题。”
解决方法:
打开Options for Target项,把那个Use MicroLIB前面打上勾,重新编译链接即可。
出现问题的原因,网上的相关解释是:这是Printf库函数的问题,需要使用MicroLIB这个库来替代默认的库。原因是默认printf并非使用串口,所以要改掉。
在main.c中有这样一段函数:
/**
* @brief Retargets the C library printf function to the USART.
* @param None
* @retval None
*/
PUTCHAR_PROTOTYPE
{
/* Place your implementation of fputc here */
/* e.g. write a character to the USART */
USART_SendData(EVAL_COM1, (uint8_t) ch);
/* Loop until the end of transmission */
while (USART_GetFlagStatus(EVAL_COM1, USART_FLAG_TC) == RESET)
{}
return ch;
}
根据这段函数的说明,就是做这个事情的。
但是还有一些疑问:
(1)这个例子的readme.txt中并没有说明要用MicroLIB这个库的事情。
(2)如果不用硬件,而是用软件仿真,也是不需要使用MicroLIB库的。
因此,有关MicroLIB的问题还不是特别明确,还需要进一步研究。是否是这段程序还用到了scanf的原因?
例子的运行
如果是第一次运行,那么会出现如下画面:
即其中有提示RTC没有初始化,要求进行设置的提示,根据提示分别送入小时,分钟,秒的数值,即完成设置工作,程序不断地将当前时间通过串口送出。
如果在VBAT端接入后备电池,那么断电后再上电显示的信息如下:
其中提示RTC已初始化,不需要再进行初始化,直接将时间不断地从串口送出。
程序解读
/**
* @brief Configures the RTC.
* @param None
* @retval None
*/
void RTC_Configuration(void)
{
/* 开启PWR和BKP模块的时钟 */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
/* 允许对BKP进行存取操作*/
PWR_BackupAccessCmd(ENABLE);
/* 初始化BKP区域*/
BKP_DeInit();
/* LSE允许,这里LSE是指外部接的32768HZ 晶振的一个振荡器,它使用PC14和PC15两条引脚。有关这部分的一些细节描述如下:
当备份区域由V DD (内部模拟开关连到V DD )供电时,下述功能可用:
● PC14和PC15可以用于GPIO或LSE引脚
● PC13可以作为通用I/O口、TAMPER引脚、RTC校准时钟、RTC闹钟或秒输出
当后备区域由VBAT供电时(VDD消失后模拟开关连到VBAT),可以使用下述功能:
● PC14和PC15只能用于LSE引脚
● PC13可以作为TAMPER引脚、RTC闹钟或秒输出
*/
RCC_LSEConfig(RCC_LSE_ON);
/* 等待LSE振荡器就绪 */
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET)
{}
/* 选择LSE作为RTC的时钟源
说明:RTC时钟源可以是以下三种之一:
─ HSE 时钟除以 128
─ LSE 振荡器时钟
─ LSI振荡器时钟
其中LSI是一个内部的低频RC振荡器。关于RTC的这三个时钟各有何特点,见下面的说明。
*/
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
/* RTC时钟允许*/
RCC_RTCCLKCmd(ENABLE);
/* 等待RTC寄存器同步
理由如下:
15.3.3 读 RTC 寄存器
RTC核完全独立于RTC APB1接口。
软件通过APB1接口访问RTC的预分频值、计数器值和闹钟值。但是,相关的可读寄存器只在与RTC APB1时钟进行重新同步的RTC时钟的上升沿被更新。RTC标志也是如此的。
这意味着,如果APB1接口刚刚被开启之后,在第一次的内部寄存器更新之前,从APB1上读出RTC寄存器的第一个值可能被破坏了(通常读到0)。下述几种情况下能够发生这种情形:
● 发生系统复位或电源复位
● 系统刚从待机模式唤醒(参见4.3节)。
● 系统刚从停机模式唤醒(参见4.3节)。
所有以上情况中,APB1接口被禁止时(复位、无时钟或断电)RTC核仍保持运行状态。 因此,若在读取RTC寄存器曾经被禁止的RTC APB1接口,软件首先须等待RTC_CRL寄存器中
的RSF位(寄存器同步标志)被硬件置1。
void RTC_WaitForSynchro(void)
{
/* Clear RSF flag */
RTC-》CRL &= (uint16_t)~RTC_FLAG_RSF;
/* Loop until RSF flag is set */
while ((RTC-》CRL & RTC_FLAG_RSF) == (uint16_t)RESET)
{
}
}
*/
RTC_WaitForSynchro();
/* 等待最后写操作完成*/
RTC_WaitForLastTask();
/* 允许秒中断,相应代码如下:
void RTC_ITConfig(uint16_t RTC_IT, FunctionalState NewState)
{
/* Check the parameters */
assert_param(IS_RTC_IT(RTC_IT));
assert_param(IS_FUNCTIONAL_STATE(NewState));
if (NewState != DISABLE)
{
RTC-》CRH |= RTC_IT;
}
else
{
RTC-》CRH &= (uint16_t)~RTC_IT;
}
}
而调用这段程序的代码中的第一个参数是:
#define RTC_IT_SEC ((uint16_t)0x0001) /*!《 Second interrupt */
可见它是对RTC-》CRH的最低位进行操作,CRH相关内容见下面的图。
*/
RTC_ITConfig(RTC_IT_SEC, ENABLE);
/* 等待最后的写操作完成*/
RTC_WaitForLastTask();
/* 设置RTC预分频器: set RTC period to 1sec */
RTC_SetPrescaler(32767); /* RTC period = RTCCLK/RTC_PR = (32.768 KHz)/(32767+1) */
/* 等待最后的写操作完成*/
RTC_WaitForLastTask();
}
下面的图是有关RTC时钟选择的问题,其中说明了各种时钟的工作特性:
下面的图是有关RTC_CRH寄存器的描述,放在这里是为了说明SECIE标志位。