最近在试验中想用RTC闹钟做低STM32单片机的功耗唤醒,在设置闹钟中断时很走了一段弯路。网上搜到到资料基本也没得到太多帮助,遇到问题其实很简单,现在整理一下,发出来,希望对遇到这问题的朋友有帮助。程序我特意简化了一下,超简单。 主程序,只包含基本的时钟设置、RTC初始化、LED端口设置,循环体只有两条,其实完全可以是一条,T++是本人喜好,不加难受。PCout(13)=LED;完成将LED状态输出到GPIOC13口,驱动LED。
#include "stm32f10x.h" //STM32头文件 #include "sys.h" extern u8 LED; u32 T; int main (void)//主程序 { RCC_Configuration(); //时钟设置 RTC_Config(); //RTC初始化 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); //GPIOC外设时钟使能 GPIOC->CRH |=0x00100000; //LED端口设置GPIOC13针,输出模式 RTC_Alarm_Init(); //RTC闹钟初始化 RTC_Alarm_Set(5); //RTC闹钟设置,5秒后闹钟中断 T=0; while(1) { PCout(13)=LED; T++; }//while }//mian
RCC_Configuration(); RTC_Config(); 这两个不必说,大家都有,这里不涉及到时钟的具体时间设置,所以也不去列出。现在主要讲讲 RTC_Alarm_Init() ;RTC_Alarm_Set();这两个程序
void RTC_Alarm_Init(void) { NVIC_InitTypeDef NVIC_InitStructure; //定义优先级结构体 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//先占优先级0级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //从优先级0级 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); RTC->CRH |= 0x02; //ALRIE:允许闹钟中断 }
RTC_Alarm_Init()这个程序完全可以加到RTC_Config()中,但为了清晰问题,所以我单独做 了一个子程序。前面的几句为设置RTC全局中断优先级,最后一句作用为设置RTC->CRH的ALRIE位,来使能闹钟中断 。
void RTC_Alarm_Set(u16 PY) { uint32_t tmp = 0; RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP | RCC_APB1Periph_PWR,ENABLE); PWR_BackupAccessCmd(ENABLE); RTC->CRL |= 0x10; //RTC进入配置模式 tmp = RTC->CNTL+PY; RTC->ALRH = RTC->CNTH +(tmp>>16); RTC->ALRL = tmp; RTC->CRL &= 0xEF; //RTC退出配置模式 RTC_WaitForLastTask(); //等待寄存器写入完成 PWR_BackupAccessCmd(DISABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP | RCC_APB1Periph_PWR,DISABLE); }
RTC_Alarm_Set(u16 PY),这个程序完成闹钟设定,其中
RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP | RCC_APB1Periph_PWR,ENABLE); //开启PWR、BKP时钟 PWR_BackupAccessCmd(ENABLE); //解锁后备域读写
这两句最重要,必须加上,因为我们在使用RTC时钟时,基本上电源时钟和后备域使能这两项是禁止的,所以后来想使用闹钟时,直接设置闹钟是不能成功的,因为RTC的特殊性,本身有自己的时钟,所以有无系统时钟和后备域的使能在RTC运行中并无影响。
tmp = RTC->CNTL+PY; RTC->ALRH = RTC->CNTH +(tmp>>16); RTC->ALRL = tmp;
中间三句这完成将当前时间(CNT计数值)读出加上闹钟需要延时的时间(单位秒),再写入闹钟寄存器(ALRH,YALRL);这里对RTC_WaitForLastTask();说一下我的见解,通过实验及查阅手册,发现现在网上一些代码在使用中存在误区,这个语句其实不必每次写RTC寄存器时都跟上一句,只要在退出配置模式后(RTC->CRL &= 0xEF;),加一句就可以了,因为RTC只有在推出配置模式后才后执行一次写 *** 作。
最后 关闭后备域写保护,关闭PWR、BKP时钟。建议这两句要加上。
下面的一个重要部分就是,在中断服务程序stm32f10x_it.c里的RTC闹钟中断处理程序,
void RTC_IRQHandler(void){ //RTC时钟全局中断函数(名称固定不可修改) if(RTC_GetITStatus(RTC_IT_ALR) != RESET) { RTC_ClearITPendingBit(RTC_IT_ALR); //清Alarm 中断标志位(ALRF) LED=(LED==0); //LED状态翻转 RTC_Alarm_Set(2); //设定2s后闹钟 } }
这里没什么说的了,效果就是连续设置闹钟,通过LED显示效果。整个过程就是复位后LED亮(低电平亮)5S,后续每隔大约2SLED亮灭切换一次。
下面把 RCC_Configuration(); RTC_Config(); //RTC的程序附上,保证大家程序调通,这里没什么讲的了。
void RCC_Configuration(void){ //RCC时钟的设置 ErrorStatus HSEStartUpStatus; RCC_DeInit(); RCC_HSEConfig(RCC_HSE_ON); HSEStartUpStatus = RCC_WaitForHSEStartUp(); if(HSEStartUpStatus == SUCCESS){ RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); //RCC_PLLMul_x(枚举2~16)是倍频值。当HSE=8MHZ,RCC_PLLMul_9时PLLCLK=72MHZ RCC_HCLKConfig(RCC_SYSCLK_Div1); //RCC_SYSCLK_Div1——AHB时钟 = 系统时钟(SYSCLK) = 72MHZ(外部晶振8HMZ) RCC_PCLK1Config(RCC_HCLK_Div2); //设置低速AHB时钟(PCLK1),RCC_HCLK_Div2——APB1时钟 = HCLK/2 = 36MHZ(外部晶振8HMZ) RCC_PCLK2Config(RCC_HCLK_Div1); //设置高速AHB时钟(PCLK2),RCC_HCLK_Div1——APB2时钟 = HCLK = 72MHZ(外部晶振8HMZ) FLASH_SetLatency(FLASH_Latency_2); //设置FLASH存储器延时时钟周期数 FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); //选择FLASH预取指缓存的模式,预取指缓存使能 RCC_PLLCmd(ENABLE); //使能PLL while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); //等待PLL输出稳定 RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); //选择SYSCLK时钟源为PLL while(RCC_GetSYSCLKSource() != 0x08); //等待PLL成为SYSCLK时钟源 } } void RTC_First_Config(void){ //首次启用RTC的设置 RCC->APB1ENR|=(0x10000000+0x08000000);//启用PWR和BKP的时钟(from APB1) PWR->CR|=0x0100; //后备域解锁 RCC->BDCR|=0x10000; //备份寄存器模块复位 RCC->BDCR=0x0001; //外部32.768KHZ晶振开启 while ((RCC->BDCR&0x02) != 0x02);//等待稳定 RCC->BDCR|=0x00000100; RCC->BDCR|=0x8000;//RTC开启 while ((RTC->CRL&0x08) != 0x08);//开启后需要等待APB1时钟与RTC时钟同步,才能读写寄存器 //RTC_SetPrescaler(32762);//设置RTC分频器,使RTC时钟为1Hz,RTC period = RTCCLK/RTC_PR = (32.768 KHz)/(32767+1) RTC->CRL |= 0x10; //RTC进入配置模式 RTC->PRLH = 0x00; RTC->PRLL = 32767; RTC->CRL &= 0xEF; //RTC退出配置模式 RTC_WaitForLastTask();//等待寄存器写入完成 } void RTC_Config(void){ //实时时钟初始化 if (BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5)//判断寄存数据是否丢失 RTC_First_Config();//重新配置RTC else { RCC_ClearFlag();//清除RCC中复位标志 RCC_RTCCLKCmd(ENABLE);//使能RTCCLK RTC_WaitForSynchro();//等待RTC时钟与APB1时钟同步 } }
不差这个了,一并送上
#ifndef __SYS_H #define __SYS_H #include "stm32f10x.h" //位带 *** 作,实现51类似的GPIO控制功能 //具体实现思想,参考<>第五章(87页~92页). //IO口 *** 作宏定义 #define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) #define MEM_ADDR(addr) *((volatile unsigned long *)(addr)) #define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum)) //IO口地址映射 #define GPIOA_ODR_Addr (GPIOA_base+12) //0x4001080C #define GPIOB_ODR_Addr (GPIOB_base+12) //0x40010C0C #define GPIOC_ODR_Addr (GPIOC_base+12) //0x4001100C #define GPIOD_ODR_Addr (GPIOD_base+12) //0x4001140C #define GPIOE_ODR_Addr (GPIOE_base+12) //0x4001180C #define GPIOF_ODR_Addr (GPIOF_base+12) //0x40011A0C #define GPIOG_ODR_Addr (GPIOG_base+12) //0x40011E0C #define GPIOA_IDR_Addr (GPIOA_base+8) //0x40010808 #define GPIOB_IDR_Addr (GPIOB_base+8) //0x40010C08 #define GPIOC_IDR_Addr (GPIOC_base+8) //0x40011008 #define GPIOD_IDR_Addr (GPIOD_base+8) //0x40011408 #define GPIOE_IDR_Addr (GPIOE_base+8) //0x40011808 #define GPIOF_IDR_Addr (GPIOF_base+8) //0x40011A08 #define GPIOG_IDR_Addr (GPIOG_base+8) //0x40011E08 //IO口操作,只对单一的IO口! //确保n的值小于16! #define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) //输出 #define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n) //输入 #define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) //输出 #define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) //输入 #define PCout(n) BIT_ADDR(GPIOC_ODR_Addr,n) //输出 #define PCin(n) BIT_ADDR(GPIOC_IDR_Addr,n) //输入 #define PDout(n) BIT_ADDR(GPIOD_ODR_Addr,n) //输出 #define PDin(n) BIT_ADDR(GPIOD_IDR_Addr,n) //输入 #define PEout(n) BIT_ADDR(GPIOE_ODR_Addr,n) //输出 #define PEin(n) BIT_ADDR(GPIOE_IDR_Addr,n) //输入 #define PFout(n) BIT_ADDR(GPIOF_ODR_Addr,n) //输出 #define PFin(n) BIT_ADDR(GPIOF_IDR_Addr,n) //输入 #define PGout(n) BIT_ADDR(GPIOG_ODR_Addr,n) //输出 #define PGin(n) BIT_ADDR(GPIOG_IDR_Addr,n) //输入 void NVIC_Configuration(void); //嵌套中断控制器的设置 void RCC_Configuration(void); //RCC时钟类的设置 #endif
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)