STM32有三种不同的时钟源可被用来驱动系统时钟(SYSCLK):
1:HSI振荡器时钟(内部时钟)
2:HSE振荡器时钟 (外部时钟,即晶振所提供)
3:PLL时钟(锁相环时钟)
这些设备有以下2种二级时钟源:
(1)40kHz低速内部RC,可以用于驱动独立看门狗和通过程序选择驱动RTC。RTC用于从停机/待机模式下自动唤醒系统。
(2)32.768kHz低速外部晶体也可用来通过程序选择驱动RTC(RTCCLK)。
当不被使用时,任一个时钟源都可被独立地启动或关闭,由此优化系统功
下面是以外部晶振8M,系统时钟72M为例。
STM32系统时钟初始化流程如下:
1、复位并配置向量表;
2、使能外部高速中断HSEON,并等待就绪;
3、RCC->CFGR=0X00000400;设置APB1,如先把前面的设置好之后,再来设置APB1就已经来不及了,所以必须先二分频,因为要求APB1不超过36MHz,前面最大为72MHz,故二分频即可;
4、RCC->CFGR|=PLL<<18;设置锁相环放大倍数,及时钟树的PLLMUL;
5、RCC->CFGR|=1<<16; 选择HSE作 为PLL输入;
6、FLASH->ACR|=0x32; FLASH 2个延迟周期
7、RCC->CR|=0x01000000; 使能PLL,并等待就绪;
8、RCC->CFGR|=0x00000002;设置PLL作为系统时钟,并等待就绪。
时钟树如下:大致流程如时钟树里面的顺序标号
STM32系统时钟初始化代码如下:
void Stm32_Clock_Init(u8 PLL)
{
unsigned char temp=0;
MYRCC_DeInit(); //复位并配置向量表
RCC->CR|=0x00010000; //外部高速时钟使能
while(!(RCC->CR>>17));//等待外部时钟就绪
RCC->CFGR=0X00000400; //APB1=DIV2;APB2=DIV1;AHB=DIV1; //二分频
PLL-=2;//抵消两个单位,具体看STM32中文参考手册
RCC->CFGR|=PLL<<18; //设置PLL的值,2~16
RCC->CFGR|=1<<16; //PLLSRC ON
FLASH->ACR|=0x32; //FLASH 2个延迟周期
RCC->CR|=0x01000000; //PLLON
while(!(RCC->CR>>25));//等待PLL锁定
RCC->CFGR|=0x00000002;//PLL作为系统时钟
while(temp!=0x02) //等待PLL作为系统时钟设置成功
{
temp=RCC->CFGR>>2; //不断的查询寄存器看系统时钟是否设置成功
temp&=0x03;
}
}
在跑裸机程序时,利用芯片提供的滴答定时器SysTick来进行延迟,是一种比较准确的延迟
Systick :系统心跳定时器,提供系统节拍,裸机程序中可作为独立的延时定时器
它有四个寄存器
STK_CSR, 0xE000E010 -- 控制寄存器
STK_LOAD, 0xE000E014 -- 重载寄存器
STK_VAL, 0xE000E018 -- 当前值寄存器
STK_CALRB, 0xE000E01C -- 校准值寄存器
延时编程原理
systick定时器是24位的递减计数器,设定初值并使能它后,它会每个系统时钟周期计数器减1,
计数到0 时,将从RELOAD 寄存器中自动重装载定时初值。只要不把它在SysTick控制及状态寄存器中的使能位清除,就永不停息.
延时编程步骤
1.计算出产生1us 需要多少个时钟周期 fac_us;
2.计算出RELOAD寄存器的值
也就是产生相应延时所需要的时钟周期数
RELOAD=fac_us * nus
3.开启计数
4.循环检测计数到0的标志位;
5.清空计数器,关闭定时器
寄存器版代码注解(可以看上面的时钟树也能够看出来)
使用外部8M时钟,锁相环里出来的频率是72M,AHB预分频后是72M,
systick固定HCLK时钟的1/8,即9M,那么延时1us是9个时钟
代码如下:
void delay_init(u8 SYSCLK) //系统时钟是72MHz,SYSCLK=72
{
SysTick->CTRL &= 0xfffffffb ; //bit2清0,也就是配置选择外部时钟
fac_us=SYSCLK/8; //硬件8分频,fac_us得出的值是要给下面的时钟函数用的
fac_ms =(u16)fac_us*1000;
}
void delay_us(u32 nus) //nus假如为10us
{
u32 temp;
SysTick->LOAD = nus*fac_us; //延时10us的话就是 10*9=90,装到load寄存器中
SysTick->VAL=0x00;//计数器清0,因为currrent字段被手动清零时,load将自动重装到VAL中
SysTick->CTRL = 0x01;//配置使异常生效,也就是计数器倒数到0时将发出异常通知
do
{
temp = SysTick->CTRL; //时间到了之后,该位将被硬件置1,但被查询后自动清0
}
while(temp & 0x01 && !(tmep &(1<<16))); //查询
SysTick->CTRL = 0x00; //关闭计数器
SysTick->VAL = 0x00; //清空val
}
还有一个注意点:
LOAD寄存器是24位的 最大值0xffffff
那么延时最大值计算公式为
nms<=0xffffff*8*1000/SYSCLK (SYSCLK单位Hz)
则nms的最大值为1864.135ms ,即1864毫秒