stm32 RTC实时时钟[操作寄存器+库函数]

发布者:科技舞者最新更新时间:2017-02-06 来源: eefocus关键字:stm32  RTC  实时时钟  操作寄存器  库函数 手机看文章 扫描二维码
随时随地手机看文章

"RTC"是Real Time Clock 的简称,意为实时时钟。stm32提供了一个秒中断源和一个闹钟中断源。

 

RTC的技术器是一个32位的计数器,使用32.768khz的外部晶振。

 

2038年问题

 

 在计算机应用上,2038年问题可能会导致某些软件在2038年无法正常工作。所有使用UNIX时间表示时间的程序都将受其影响,因为它们以自1970年1月1日经过的秒数(忽略闰秒)来表示时间。这种时间表示法在类Unix(Unix-like)操作系统上是一个标准,并会影响以其C编程语言开发给其他大部份操作系统使用的软件。

    在大部份的32位操作系统上,此“time_t”数据模式使用一个有正负号的32位元整数(signedint32)存储计算的秒数。也就是说最大可以计数的秒数为 2^31次方 可以算得:

                2^31/3600/24/365 ≈ 68年

所以依照此“time_t”标准,在此格式能被表示的最后时间是2038年1月19日03:14:07,星期二(UTC)。超过此一瞬间,时间将会被掩盖(wrap around)且在内部被表示为一个负数,并造成程序无法工作,因为它们无法将此时间识别为2038年,而可能会依个别实作而跳回1970年或1901年。

 

    对于PC机来说,时间开始于1980年1月1日,并以无正负符号的32位整数的形式按秒递增,这与UNIX时间非常类似。可以算得:

                 2^32/3600/24/365 ≈ 136年

到2116年,这个整数将溢出。

 

    Windows NT使用64位整数来计时。但是,它使用100纳秒作为增量单位,且时间开始于1601年1月1日,所以NT将遇到2184年问题。

 

苹果公司声明,Mac在29,940年之前不会出现时间问题!

 

        由于RTC是一个32位计数器,同样其计时时间是有限的。库函数中使用到了C标准时间库,时间库中的计时起始时间是1900年,可以知道时间库中不是用 有符号位的32位整数来表示时间的,否则在1968年就已经溢出了。如果用32位无符号整数计时,其溢出时间为2036年左右,所以会遇到这个问题。

 

    直接操作寄存器中,可以自由设定这个时间戳起始的年份,RTC的32位寄存器存储的只是距离这个起始年份的总秒数,所以不会遇到这个问题。而且可以用无符号32位的二进制表示时间,这意味着此类系统的时间戳可以表示更多的秒数。但是由于其使用32位寄存器表示秒数,最大只能计时到136年后。

 

本例实现使用stm32每秒输出一次当前的时间,并设置一个闹钟,到时间时输出提醒信息。

 


直接操作寄存器

 

RTC实时时钟的操作原则是 在每次读写前都要保证上一次读写完成。

 

代码较多,使用到的寄存器请参见手册  (system.h 和 stm32f10x_it.h 等相关代码参照 stm32 直接操作寄存器开发环境配置

 

User/main.c

#include 	
#include "system.h"
#include "usart.h" 
#include "rtc.h"	 

#define LED1 PAout(4)
#define LED2 PAout(5)

void Gpio_Init(void);

extern const u8* Week_Table[7];

int main(void)
{				  


	Rcc_Init(9); 			 			  //系统时钟设置

	Usart1_Init(72,9600);

	Nvic_Init(0,0,RTC_IRQChannel,0);	  //设置中断

   	
	Gpio_Init();

	Rtc_Init();	

	//Rtc_TIME_AutoSet();				//将当前编译时间作为RTC开始时间
	Rtc_TIME_Set(2012,7,7,20,50,0);		//设定开始时间 参数说明:年,月,日,时,分,秒
	
	Rtc_ALARM_Set(2012,7,7,20,50,30);	//设定闹钟事件时间		   							

	LED1 = 1;
	
	while(1);		
}


void Gpio_Init(void)
{
	RCC->APB2ENR|=1<<2;    //使能PORTA时钟 	

	GPIOA->CRL&=0x0000FFFF; // PA0~3设置为浮空输入,PA4~7设置为推挽输出
	GPIOA->CRL|=0x33334444; 
	
	//USART1 串口I/O设置

	GPIOA -> CRH&=0xFFFFF00F;   //设置USART1 的Tx(PA.9)为第二功能推挽,50MHz;Rx(PA.10)为浮空输入
	GPIOA -> CRH|=0x000008B0;	  
}

User/stm32f103x_it.c

#include "stm32f10x_it.h"
#include "system.h"
#include "stdio.h"
#include "rtc.h"

#define LED1 PAout(4)
#define LED2 PAout(5)
#define LED3 PAout(6)
#define LED4 PAout(7)

//extern void Wwdg_Feed(void);
//extern u16 Read_Bkp(u8 reg);
extern void Rtc_Get(void);
extern const u8* Week_Table[7];

void RTC_IRQHandler(void)
{

	if(RTC->CRL&0x0001)					//秒钟中断
	{
		LED4 = !LED4;
		Rtc_Get();

		printf("\r\n Time : %d - %d - %d,%d : %d : %d ,Today is %s \r\n",
	
	            timer.year,
	
	            timer.month,
	
	            timer.date,
	
	            timer.hour,
	
	            timer.minute,
	
	            timer.second,
				Week_Table[timer.week]
		);		
	}

	if(RTC->CRL&0x0002)					//闹钟中断
	{
		LED3 = 1; 
			
		printf("\r\nIt's time to do sth.\r\n");	

		RTC->CRL &= ~(0x0002);			//清除闹钟中断	

	}

	RTC->CRL &= 0x0FFA; 				//清除溢出,秒钟中断

	while(!(RTC->CRL &(1<<5)));			//等待RTC寄存器操作完成

}

Library/src/rtc.c

#include 	
#include "rtc.h"
#include "stdio.h"

tm timer;					//定义时钟结构体,主函数直接可以调用此结构体读出时间

//平年的月份日期表,月份缩写表
const u8 Days_Table[12]={31,28,31,30,31,30,31,31,30,31,30,31};
const u8 Month_Table[12][3]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};

const u8* Week_Table[7]={"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"};

//月修正数据表																		 
u8 const _Week[12]={0,3,3,6,1,4,6,2,5,0,3,5}; 

void Rtc_Init(void)
{
	RCC->APB1ENR |= 1<<28;	//使能PWR时钟
	RCC->APB1ENR |= 1<<27;  //使能BKP时钟,RTC校准在BKP相关寄存器中
 	PWR->CR |= 1<<8;		//取消BKP相关寄存器写保护

	//RCC->BDCR |= 1<<16;	//备份区域软复位
	//RCC->BDCR |= ~(1<<16);	//备份区域软复位结束

	RCC->BDCR |= 1<<0;		//外部低速时钟(LSE)使能

	while(!(RCC->BDCR & 0x02));	//等待外部时钟就绪

	RCC->BDCR |= 1<<8;			//LSE作为RTC时钟
	RCC->BDCR |= 1<<15;			//RTC时钟使能

	while(!(RTC->CRL & (1<<5)));	//等待RTC寄存器最后一次操作完成
	while(!(RTC->CRL & (1<<3)));	//等待RTC寄存器同步完成

	RTC->CRH |= 0x07;				//允许溢出中断[2],闹钟中断[1],秒中断[0],CRH寄存器低三位有效	

	while(!(RTC->CRL & (1<<5)));	//等待RTC寄存器最后一次操作完成

	RTC->CRL |=  1<<4;				//进入配置模式
	RTC->PRLH = 0x0000;				
	RTC->PRLL = 32767;				//设定分频值

	//Rtc_TIME_AutoSet();				//将当前编译时间写入寄存器
	//Rtc_TIME_Set(2012,7,7,20,50,0);	//年,月,日,时,分,秒

	RTC->CRL &= ~(1<<4);			//退出配置模式,开始更新RTC寄存器
	while(!(RTC->CRL & (1<<5)));	//等待RTC寄存器最后一次操作完成

}


//设定RTC开始计时时间
void Rtc_TIME_Set(u16 year,u8 month,u8 date,u8 hour,u8 minute, u8 second)
{
		u32 sec;	

	    sec = Date_TO_Sec(year,month,date,hour,minute,second);
	
		//printf("\nRtc TIME Set  Sec = %x\n",sec);
	
		RCC->APB1ENR |= 1<<28;							//使能PWR时钟,方便独立调用此函数
		RCC->APB1ENR |= 1<<27;							//使能BKP时钟
		PWR->CR |= 1<<8;								//取消写保护
	
		RTC-> CRL |= 1<<4;								//允许配置
		
		RTC-> CNTL = sec&0xffff;						//取低16位
		RTC-> CNTH = sec>>16;							//取高16位
	
		RTC-> CRL &= ~(1<<4);							//开始RTC寄存器更新
	
		while(!(RTC->CRL&(1<<5)));						//等待RTC寄存器操作完成 	
}




//判断是否是闰年函数
//
//判断方法:
//		普通年能整除4且不能整除100的为闰年。(如2004年就是闰年,1900年不是闰年)
//		世纪年能整除400的是闰年。(如2000年是闰年,1900年不是闰年)
//
//返回: 1,是闰年 	0,不是闰年
u8 Is_LeapYear(u16 year)
{			  
	if(year%4==0) 				//必须能被4整除
	{ 
		if(year%100==0) 
		{ 
			if(year%400==0)
				return 1;		//如果以00结尾,还要能被400整除 	   
			else 
				return 0;   
		}else{ 
			return 1;   
		}
	}else{
		 return 0;	
	}
}


//将时间转化为到1970年1月1日的总秒数
//Bugs:此函数秒数会多20左右,所以函数返回值做了校正,校正后没有问题
//待优化
u32 Date_TO_Sec(u16 year,u8 month,u8 date,u8 hour,u8 minute, u8 second)
{
	u16 t;
	u32 sec;

	if(year >= 1970 && year<= 2106)		  //判断是否为合法年份,RTC的时间是从1970开始,只能由32位表示秒数,最大只能到2106年左右
	{
		for(t= 1970 ;tCNTH; 	//读取RTC的当前时间值(距1970年的总秒数)
	secs <<= 16;
	secs += RTC->CNTL;

	//printf("\nRtc_Get  Sec = %x\n",secs);

	days = secs/86400;
	if(days > 0)			//超过一天
	{
		temp = days;
		while(temp >= 365)	
		{
			if(Is_LeapYear(years))				//是闰年
			{
				if(temp >= 366) 
					temp -=	366;	//闰年的天数
				else
					break;
			}else{
			 	temp -= 365;
			}		 	
			years++;
		}
		
		timer.year = years;			  //得到年份

		while(days >= 28)
		{
			if(Is_LeapYear(years) && months ==1)	   //判断是否为闰年的第二月
			{
				if(temp >= 29) 
					temp -= 29;	
				else
					break;
			}else{
				if(temp >= Days_Table[months]) 		
					temp -= Days_Table[months];
				else
					break;
			}

			months++;	
		}

		timer.month = months+1;				//得到月数
 		timer.date  = temp+1;				//得到日期
	}

	temp = secs % 86400;					//得到剩余秒数
	timer.hour = temp/3600;					//得到小时
	timer.minute = (temp%3600)/60;			
	timer.second = (temp%3600)%60;
	timer.week = Rtc_DAY_Get(timer.year,timer.month,timer.date);

				
}

//判断当前为星期几					

u8 Rtc_DAY_Get(u16 year,u8 month,u8 day)
{	
	u16 temp;
	u8 yearH,yearL;
	
	yearH = year/100;	
	yearL = year%100; 

	// 如果为21世纪,年份数加100  
	if( yearH > 19 ) yearL += 100;

	// 所过闰年数只算1900年之后的  

	temp = yearL+yearL/4;
	temp = temp%7; 
	temp = temp + day + _Week[month-1];

	if( yearL%4 == 0 && month < 3 ) temp--;

	return(temp%7);
}

//设定闹钟时间

void Rtc_ALARM_Set(u16 year,u8 month,u8 date,u8 hour,u8 minute, u8 second)
{

		u32 sec;	

	    sec = Date_TO_Sec(year,month,date,hour,minute,second);


		RTC-> CRL |= 1<<4;								//允许配置

		//while(!(RTC->CRL&(1<<5)));						//RTOFF为1 才可以写入ALRL和ALRH寄存器
		
		RTC-> ALRL = sec&0xffff;						//取低16位
		RTC-> ALRH = sec>>16;							//取高16位
	
		RTC-> CRL &= ~(1<<4);							//开始RTC寄存器更新
	
		while(!(RTC->CRL&(1<<5)));						//等待RTC寄存器操作完成

}

Library/inc/rtc.h

#include 	

typedef struct
{
	u8 hour;
	u8 minute;
	u8 second;

	u16 year;
	u8  month;
	u8 	date;
	u8	week;
}tm;

extern tm timer;

void Rtc_Init(void);
void Rtc_TIME_Set(u16 year,u8 month,u8 date,u8 hour,u8 minute, u8 second);
u8 Is_LeapYear(u16 year);
u32  Date_TO_Sec(u16 year,u8 month,u8 date,u8 hour,u8 minute, u8 second);
void Rtc_TIME_AutoSet(void);
void Rtc_Get(void);
void Rtc_ALARM_Set(u16 year,u8 month,u8 date,u8 hour,u8 minute, u8 second);
u8 Rtc_DAY_Get(u16 year,u8 month,u8 day);

这里用到了MDK的两个关键字 __DATE__ 和 __TIME__获得当前编译的日期和时间,详见代码注释

 

库函数操作

 

ANSI C语言所提供的time.h的头文件中关于unix时间戳是从1900年开始的,和直接操作寄存器不同,所以如果unix时间戳中读出年份为100,则正确年份为1900+100=2000

 

代码如下:

 

main.c

#include "stm32f10x.h"
#include "stdio.h"
#include "time.h"

#define	 PRINTF_ON  1

void RCC_Configuration(void);
void GPIO_Configuration(void);
void NVIC_Configuration(void);
void USART_Configuration(void);
void RTC_Configuration(void);

void TimeShow(void);

void SetAlarm(struct tm t);
void SetCalendarTime(struct tm t);
void SetUnixTime(time_t);
struct tm  ConvUnixToCalendar(time_t t);
u32 ConvCalendarToUnix(struct tm t);
u32 GetUnixTime(void);


vu32  Display;

struct tm CurrentTime = {0,30,10,11,4,2011};
struct tm AlarmTime = {5,30,10,11,4,2011};


int main(void)
{
  	RCC_Configuration();
  	GPIO_Configuration();
	NVIC_Configuration();
	USART_Configuration();
	RTC_Configuration();
	
	SetCalendarTime(CurrentTime);
	SetAlarm(AlarmTime); 
	while(1){ TimeShow(); }
}

void TimeShow(void)
{
	u32	Time = 0;
	if(Display)
	{
		Time = GetUnixTime();
		CurrentTime = ConvUnixToCalendar(Time);

		printf("\r\n Time : %d - %d - %d,%d : %d : %d \r\n",
			CurrentTime.tm_year,
			CurrentTime.tm_mon,
			CurrentTime.tm_mday,
			CurrentTime.tm_hour,
			CurrentTime.tm_min,
			CurrentTime.tm_sec);
		Display = 0;
	}
}

void SetCalendarTime(struct tm t) 
{
	SetUnixTime(ConvCalendarToUnix(t));
}

void SetUnixTime(time_t t)
{
	RTC_WaitForLastTask();
	RTC_SetCounter((u32)t);
	RTC_WaitForLastTask();
}

void SetAlarm(struct tm t)
{
	RTC_WaitForLastTask();
	RTC_SetAlarm(ConvCalendarToUnix(t));
	RTC_WaitForLastTask();
}

u32 GetUnixTime(void)
{
	return (u32)RTC_GetCounter();
}

u32 ConvCalendarToUnix(struct tm t)
{
	t.tm_year -=1900;
	return mktime(&t);
}

struct tm  ConvUnixToCalendar(time_t t)
{
	struct tm *t_tm;
	t_tm = localtime(&t);
	t_tm->tm_year += 1900;
	return *t_tm;
}


void GPIO_Configuration(void)
{
  	GPIO_InitTypeDef GPIO_InitStructure;                                                                                                                                                                                                                                                    
  	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
  	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;			
  	GPIO_Init(GPIOA , &GPIO_InitStructure); 

  	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;			
  	GPIO_Init(GPIOA , &GPIO_InitStructure); 
}

void RTC_Configuration(void)
{
	PWR_BackupAccessCmd(ENABLE);
	BKP_DeInit();
	RCC_LSEConfig(RCC_LSE_ON);
	while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);
	RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
	RCC_RTCCLKCmd(ENABLE);

	RTC_WaitForSynchro();
	RTC_WaitForLastTask();

	RTC_ITConfig(RTC_IT_SEC|RTC_IT_ALR,ENABLE);
	RTC_WaitForLastTask();

	RTC_SetPrescaler(32767);
	RTC_WaitForLastTask();
}


void RCC_Configuration(void)
{
	/* 定义枚举类型变量 HSEStartUpStatus */
	ErrorStatus HSEStartUpStatus;

  	/* 复位系统时钟设置*/
  	RCC_DeInit();
  	/* 开启HSE*/
  	RCC_HSEConfig(RCC_HSE_ON);
  	/* 等待HSE起振并稳定*/
  	HSEStartUpStatus = RCC_WaitForHSEStartUp();
	/* 判断HSE起是否振成功,是则进入if()内部 */
  	if(HSEStartUpStatus == SUCCESS)
  	{
    	/* 选择HCLK(AHB)时钟源为SYSCLK 1分频 */
    	RCC_HCLKConfig(RCC_SYSCLK_Div1); 
    	/* 选择PCLK2时钟源为 HCLK(AHB) 1分频 */
    	RCC_PCLK2Config(RCC_HCLK_Div1); 
    	/* 选择PCLK1时钟源为 HCLK(AHB) 2分频 */
    	RCC_PCLK1Config(RCC_HCLK_Div2);
    	/* 设置FLASH延时周期数为2 */
    	FLASH_SetLatency(FLASH_Latency_2);
    	/* 使能FLASH预取缓存 */
    	FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
    	/* 选择锁相环(PLL)时钟源为HSE 1分频,倍频数为9,则PLL输出频率为 8MHz * 9 = 72MHz */
    	RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
    	/* 使能PLL */ 
    	RCC_PLLCmd(ENABLE);
    	/* 等待PLL输出稳定 */
    	while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
    	/* 选择SYSCLK时钟源为PLL */
    	RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
    	/* 等待PLL成为SYSCLK时钟源 */
    	while(RCC_GetSYSCLKSource() != 0x08);
  	} 
  	/* 打开APB2总线上的GPIOA时钟*/
  	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1, ENABLE);

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR|RCC_APB1Periph_BKP, ENABLE);
		
}

 
void USART_Configuration(void)
{
	USART_InitTypeDef USART_InitStructure;
	USART_ClockInitTypeDef USART_ClockInitStructure;

	USART_ClockInitStructure.USART_Clock = USART_Clock_Disable;
	USART_ClockInitStructure.USART_CPOL = USART_CPOL_Low;
	USART_ClockInitStructure.USART_CPHA = USART_CPHA_2Edge;                                                                                                                                                      
	USART_ClockInitStructure.USART_LastBit = USART_LastBit_Disable;
	USART_ClockInit(USART1 , &USART_ClockInitStructure);

	USART_InitStructure.USART_BaudRate = 9600;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
	USART_Init(USART1,&USART_InitStructure);

 	USART_Cmd(USART1,ENABLE);
}


void NVIC_Configuration(void)
{
	NVIC_InitTypeDef NVIC_InitStructure;

	NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);

}





#if	 PRINTF_ON

int fputc(int ch,FILE *f)
{
	USART_SendData(USART1,(u8) ch);
	while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);
	return ch;
}

#endif

stm32f10x_it.c:


#include "stm32f10x_it.h"

#include "stdio.h"

extern vu32 Display;

void RTC_IRQHandler(void)
{
	if(RTC_GetFlagStatus(RTC_FLAG_ALR) != RESET){
		printf("\r\nIt's time to do sth.\r\n");		
	}else{
		Display =1 ;
	
	}

	RTC_ClearITPendingBit(RTC_IT_ALR|RTC_IT_SEC);
}


关键字:stm32  RTC  实时时钟  操作寄存器  库函数 引用地址:stm32 RTC实时时钟[操作寄存器+库函数]

上一篇:stm32 BKP寄存器操作[操作寄存器+库函数]
下一篇:stm32 i2c通信 [操作寄存器+库函数]

推荐阅读最新更新时间:2024-03-16 15:32

STM32 学习笔记--SPI通信配置
SPI时序 SPI时序图如下: STM32做为主机设计SPI时序一般选用CPOL=1/CPHA=1; SPI通信配置 1、时钟使能。GPIO时钟使能RCC- APB2ENR,SPI时钟使能RCC- APB2ENR设置。 (为什么还要连接GPIO时钟,参见STM32参考手册8.1.4节。手册上这么说的:对于复用输出功能,端口必须配置成复用功能输出模式(推挽或开漏)。) 2、配置GPIO工作模式。配置GPIO片选,由软件管理(即自定义引脚),推挽输出,上拉; 配置SPI引脚SCK、MOSI、MISO所用到的引脚为复用功能;GPIOX- CR1 GPIOX- ODR; 3、SPI设置工作模式。通过配置SPIx- CR1来设置SPI
[单片机]
<font color='red'>STM32</font> 学习笔记--SPI通信配置
STM32串口控制不同亮度等级的小灯
利用串口的输入控制LED灯的亮度,从而达到PC机与MAU交互通信的功能 此功能由三个部分组成,分别是:串口通信、LED灯亮度等级的设定、以及按键按下LED灯熄灭。 首先串口通信过程中要配置串口重定义: /* USER CODE BEGIN 4 */ #ifdef __GNUC__ #define PUTCHAR_PROTOTYPE int __io_putchar(int ch) #else #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f) #define GETCHAR_PROTOTYPE int fgetc(FILE *f) #endif PUTCHAR
[单片机]
STM32 F4 从bootloader跳转用户代码遇到的问题
代码跳转后运行用户程序遇到的问题: DMA2_Stream3_IRQHandler DMA2_Stream4_IRQHandler ETH_IRQHandler ETH_WKUP_IRQHandler CAN2_TX_IRQHandler CAN2_RX0_IRQHandler CAN2_RX1_IRQHandler
[单片机]
获取STM32代码运行时间的技巧
前言 测试代码的运行时间的两种方法: 1、使用单片机内部定时器,在待测程序段的开始启动定时器,在待测程序段的结尾关闭定时器。为了测量的准确性,要进行多次测量,并进行平均取值。 2、借助示波器的方法是:在待测程序段的开始阶段使单片机的一个GPIO输出高电平,在待测程序段的结尾阶段再令这个GPIO输出低电平。用示波器通过检查高电平的时间长度,就知道了这段代码的运行时间。显然,借助于示波器的方法更为简便。 借助示波器方法的实例 Delay_us函数使用STM32系统滴答定时器实现: #include systick.h /* SystemFrequency / 1000 1ms中断一次 * SystemFrequency /
[单片机]
stm32关于BOOT0和BOOT1
BOOT0和BOOT1 STM32三种启动模式对应的存储介质均是芯片内置的,它们是: 1)用户闪存 = 芯片内置的Flash。 2)SRAM = 芯片内置的RAM区,就是内存啦。 3)系统存储器 = 芯片内部一块特定的区域,芯片出厂时在这个区域预置了一段Bootloader,就是通常说的ISP程序。这个区 域的内容在芯片出厂后没有人能够修改或擦除,即它是一个ROM区。 在每个STM32的芯片上都有两个管脚BOOT0和BOOT1,这两个管脚在芯片复位时的电平状态决定了芯片复位后从哪个区域开始执 行程序,见下表: BOOT1=x BOOT0=0 从用户闪存启动,这是正常的工作模式。 BOOT1=0 BOOT0=1 从系统存储器启
[单片机]
IAR首次给单片机STM32下载解锁flash
新板子焊接好后,在编译下载的时候第一次遇到了这样的问题: Warning:STack pointer issetupto incorrect alignment. Stack addr = 0xAAAAAAAA 开始以为是调试器的问题,我用的Jlink的SWD接口模式,换了STLink还是这个毛病,后来提示需要板子初始化,试了无果,后来查看IAR的帮助文档发现是Flash被保护了,以下摘自Help文档: 按照说明,在IAR安装目录下找到指定的运行程序JLinkSTM32.exe(D:Program Files (x86)IAR SystemsEmbedded Workbench 6.0armbin)在JLink与
[单片机]
IAR首次给单片机<font color='red'>STM32</font>下载解锁flash
STM32的Flash缓冲区
当我们看到STM32 的系统架构时,我们看到Cortex-M3 内核是通过一个专门的I-总线连接到内部FLASH的。此总线与CPU 运行在相同的频率,因此当PLL使能时核心将试图全速运行在72 MHz。由于本质上Cortex CPU是一个单周期的机器,它会试图以每次1.3ns 的速度访问内部FLASH。当STM32启动时,它是从频率为8 MHz 的内部振荡器运行的,因此内部FLASH 的访问时间不是一个问题。然而,一旦PLL启用并作为时钟源,对于运行在最高性能的Cortex CPU来说,FLASH的访问时间太长了(35ns)。为了允许Cortex CPU 运行在72 MHz具有零等待状态,FLASH 存储器具有由两个64 位缓存器
[单片机]
一文了解STM32启动过程
1 概述 说明 每一款芯片的启动文件都值得去研究,因为它可是你的程序跑的最初一段路,不可以不知道。通过了解启动文件,我们可以体会到处理器的架构、指令集、中断向量安排等内容,是非常值得玩味的。 STM32作为一款高端 Cortex-M3系列单片机,有必要了解它的启动文件。打好基础,为以后优化程序,写出高质量的代码最准备。 本文以一个实际测试代码--START_TEST为例进行阐述。 整体过程 STM32整个启动过程是指从上电开始,一直到运行到 main函数之间的这段过程,步骤为(以使用微库为例): ①上电后硬件设置SP、PC ②设置系统时钟 ③软件设置SP ④加载.data、.
[单片机]
一文了解<font color='red'>STM32</font>启动过程
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved