使用的开发板为stm32F407VET6的芯片,主要介绍stm32的时钟树与滴答计时器的一些理论和一个自己编写的delay函数。
时钟树的结构图可以在STM32F4xx中文参考手册.pdf
中的时钟这块找到。而滴答计时器是内核资源,需要到Cortex M3与M4权威指南.pdf
.
在 STM32F4 中,有5 个
最重要的时钟源,为 HSI、HSE、LSI、LSE、PLL。
其中 PLL 实际是分为两个时钟源,分别为主 PLL 和专用 PLL。
从时钟频率
来分可以分为高速时钟源
和低速时钟源
,
在这 5 个中 HSI,HSE 以及 PLL 是高速时钟
;
==LSI ==和 LSE 是低速时钟
。
从来源
可分为外部时钟源和内部时钟源,外部时钟源就是从外部通过接晶振的方式
获取时钟源,其中 HSE 和LSE
是外部时钟源,其他的是内部时钟源。
器件具有以下两个次级时钟源:
● 32 kHz 低速内部 RC (LSI RC),该 RC 用于驱动独立看门狗,也可选择提供给 RTC 用
于停机/待机模式下的自动唤醒。
● 32.768 kHz 低速外部晶振(LSE 晶振),用于驱动 RTC 时钟 (RTCCLK)
主要要知道的就是主 PLL 时钟第一个高速时钟输出 PLLP 的计算方法。
上图中,主 PLL 时钟的时钟源要先经过一个分频系数为 M 的分频器
,然后经过倍频系数为 N 的倍频器
出来之后的时候还需要经过一个分频系数为 P
(第一个输出 PLLP)或者 Q
(第二个输出 PLLQ)的分频器分频之后,最后才生成最终的主 PLL 时钟。
例如我们的外部晶振选择 8MHz。同时我们设置相应的分频器 M=8,倍频器倍频系数 N=336,分频器分频系数 P=2,那么主 PLL 生成的第一个输出高速时钟 PLLP 为:
PLL=8MHz * N/ (MP)=8MHz 336 /(8*2) = 168MHz
如果选择HSE为PLL时钟源,同时SYSCLK时钟源为PLL,那么SYSCLK时钟为 168MHz。
在SystemInit()函数中设置的系统时钟大小:
SYSCLK(系统时钟) =168MHz
AHB 总线时钟(HCLK=SYSCLK) =168MHz
APB1 总线时钟(PCLK1=SYSCLK/4) =42MHz
APB2 总线时钟(PCLK2=SYSCLK/2) =84MHz
PLL 主时钟 =168MHz
CM4 内核的处理和 CM3 一样,内部都包含了一个 SysTick 定时器
,SysTick 是一个 24 位
的倒计数定时器
,当计到 0 时,将从 RELOAD 寄存器中自动重装载定时初值。只要不把它在 SysTick 控制及状态寄存器中的使能位清除,就永不停息。
SysTick 在《STM32xx 中文参考手册》里面基本没有介绍,其详细介绍,请参阅《STM32F3与 F4 系列Cortex M4内核编程手册》第230 页。我们就是利用 STM32的内部 SysTick来实现延时的,这样既不占用中断,也不占用系统定时器。
滴答计时器属于内核,使一个精确度很高的定时器,所以在内核资料中。
因为stm32F407VET6是M4的内核,所以在M4的权威手册中可以找到滴答计时器的相关寄存器。
控制寄存器如下:
设计内核的函数在misc.c中
定时器的本质是数脉冲的个数
来一个脉冲,数值就会加一或者减一,由于晶振产生的脉冲是固定的,也就是每一次脉冲时间是一样的,就可以通过对脉冲计数来记录时间
关键:知道一个脉冲是多少时间
频率:单位时间内产生脉冲的次数 21MHZ(延迟更长,168主频延时的时间短不利于定时)
所以一个脉冲的时间是 1/21MHZ = 1/(211000 000)=4.76110^(-8)s 【得到的数值,单位是s】–>转换一下大概就是多少us
1/21=。。us,所以可以使用us为最小单位
通过这个us来进行次数的累加来定时/计时。
如果计数器达到0,从上次读寄存器开始读为1;清楚当读取或当前计数器值被清除时自动O
下面是自己写的一个延迟函数,不过一般使用的都是移植的。
void my_delay()
{
u32 temp;//定义一个临时变量
//选择systick的时钟源 选择8分频的时钟,分频之后是21MHZ
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
//配置定时时间
SysTick->LOAD = 21*1000*500;//500ms
SysTick->VAL=0x00;//清空计数器的值
//启动定时器
//SysTick->CTRL |=0x00000001;//使能位
SysTick->CTRL |=SysTick_CTRL_ENABLE_Msk;
do
{
temp=SysTick->CTRL;
}
while((temp&0X01)&& !(temp&(1<<16)));//等待计时完成,1.判断16位是否在工作,并且判断状态寄存器的状态位是否为0(跳出)
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; //关闭计数器
SysTick->VAL=0x00;//清空计数器的值
}