上一节我们成功点灯,但是没有细说RCC的配置流程。我们这节详细看下时钟树。
寄存器手册,P46
一. Air32F103的时钟树
比STM32的简单不少(好多没画),咱们整理下
来几个常用的缩写熟悉下先:
1. 4个时钟源
a. HSE - 高速外部时钟 - 般选8MHZ
b. LSE - 外部低速时钟 - 一般选32.768KHZ
c. HSI - 内部高速时钟 - 8MHZ 听说温漂大,一般不使用
d. LSI - 内部低速时钟 -40KHZ
2. 内部时钟:
a.PLL:锁相环,不懂,反正挺牛皮的,能倍频。出来的时钟叫PLLCLK,CLK就是Clock的缩写,时钟。
b.AHB(Advanced High performance Bus):先进高性能总线
c.APB(Advanced Peripheral Bus):先进外设总线,也有叫外围的。。。
d.AMBA:AMBA规范主要包括了AHB(Advanced High performance Bus)和APB(Advanced Peripheral Bus)外围总线。
看着比较多哈,但是只要抓住1条主线就行了
1. 板子使用外部8MHZ的晶振作为HSE(紫色框框)
2. 不分频直接走到选择器那里,然后到达PLL锁相环,经过9倍频,PLLCLK变为8M*9=72MHZ
3. AHB不分频,即HCLK=72MHZ
4. APB1预分频器,2分频,所以APB1是36MHZ
5. APB2预分频器,不分频,所以APB2是72MHZ
系统时钟SysTick是多少呢?其实在core.cm3.h中,已经替我们都配置好了。
注:core.cm3是内核寄存器头文件
/**
\brief System Tick Configuration
\details Initializes the System Timer and its interrupt, and starts the System Tick Timer.
Counter is in free running mode to generate periodic interrupts.
\param [in] ticks Number of ticks between two interrupts.
\return 0 Function succeeded.
\return 1 Function failed.
\note When the variable <b>__Vendor_SysTickConfig</b> is set to 1, then the
function <b>SysTick_Config</b> is not included. In this case, the file <b><i>device</i>.h</b>
must contain a vendor-specific implementation of this function.
*/
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)
{
return (1UL); /* Reload value impossible */
}
SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */
NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */
SysTick->VAL = 0UL; /* Load the SysTick Counter Value */
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
return (0UL); /* Function successful */
}
#endif
/*@} end of CMSIS_Core_SysTickFunctions */
SysTick->CTRL是用来控制系统时钟源,系统时钟源初始化,以及开启系统时钟的。
#define SysTick ((SysTick_Type *) SysTick_BASE ) /*!< SysTick configuration struct */
这里定义了一个结构体指针SysTick,基于基地址SysTick_BASE
SysTick_BASE = SCS_BASE + 0x0010UL UL是无符号long型变量
#define SysTick_BASE (SCS_BASE + 0x0010UL) /*!< SysTick Base Address */
SCS_BASE = 0xE000E000UL
#define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */
那我们根据以上计算下SysTick的地址=0xE000E000UL+0x0010UL=0xE000E010UL
因为SysTick属于M3内核寄存器,所以在Air32F103寄存器手册中是看不到描述的,需要查看ARM的【Cortex-M3编程手册-英文版】,在第4章,Core peripherals中,起始有对应的寄存器基地址:
可以看到System timer系统定时器的寄存器地址范围是0xE000E010-0xE000E01F
那么System timer系统定时器的基地址就是0xE000E010
在转到4.5 System Timer(STK),看下寄存器偏移地址是多少:
偏移是0x00,那么STK_CTRL的地址就是 0xE000E010+0x00,即0xE000E010
这和我们刚刚在core.cm3.h中看到的宏定义一致。
再看下这4位分别是干嘛的:
Bit0: ENABLE, 置1,启动系统定时器
Bit1: SysTick中断请求使能,中断都上来了。
Bit2: CLKSOURCE时钟选择,这里可以为AHB/8(0)或者直接就是AHB(置1)
Bit16:COUNTFLAG,计数中断标志位,如果计数到0了,就自动置1;这个方便我们知道啥时候计数完成了,挺好的。而且读这位时,硬件自动清0,贴心~
在上面我们已经将AHB配置为72MHZ,所以这里就看我们怎么选了。
但是core.cm3.h文件里已经帮我们配置好了,包括之前讲的所有配置(HSE,PLL,APB1,APB2),都是默认配置的,我们只要按照AHB=APB2=72MHZ,APB1=36MHZ使用即可。
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk;
这里我们重点看下SysTick_CTRL_CLKSOURCE_Msk是啥意思
又是宏定义:
#define SysTick_CTRL_CLKSOURCE_Pos 2U //CLK时钟源(左移)位置 /*!< SysTick CTRL: CLKSOURCE Position */
#define SysTick_CTRL_CLKSOURCE_Msk (1UL << SysTick_CTRL_CLKSOURCE_Pos) //时钟源0x01左移2位 /*!< SysTick CTRL: CLKSOURCE Mask */
0x1UL左移两位(2U),那就是Bit2被置1了呗,置1,时钟源是AHB=72MHZ
搞清楚了,系统计数器的频率是AHB=72MHZ,也就是说每秒计数72M次。
回到刚刚的时钟树,这里我有个不明白的地方,我觉得厂商的图上有问题。
Systick=AHB/8=9MHZ?
基于上面对core.cm3.h的研究,发现systick的时钟完全取决于配置,可以直接等于AHB,也可以等于AHB/8。
ST的参考手册里,也写了。改Systick的控制和状态寄存器就可以选择时钟源。
ST的手册里,SYSCLK直接等于从前面的选择器出来的HSE,而下面的8分频是系统定时器。
所以这里的时钟树,我改了下
/**
\brief System Tick Configuration
\details Initializes the System Timer and its interrupt, and starts the System Tick Timer.
Counter is in free running mode to generate periodic interrupts.
\param [in] ticks Number of ticks between two interrupts. //计数值
\return 0 Function succeeded.
\return 1 Function failed.
\note When the variable <b>__Vendor_SysTickConfig</b> is set to 1, then the
function <b>SysTick_Config</b> is not included. In this case, the file <b><i>device</i>.h</b>
must contain a vendor-specific implementation of this function.
*/
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks) //ticks换成计数值
{
if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) //这里有个条件判断,计数值,不能大于2的24次方
{
return (1UL); /* Reload value impossible */
}
SysTick->LOAD = (uint32_t)(ticks - 1UL); /* set reload register */
NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */
SysTick->VAL = 0UL; /* Load the SysTick Counter Value */
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
return (0UL); /* Function successful */
}
虽然ticks是无符号32位的数,但是,systicks是一个24位的计数器,也就说他的最大值是16777215 (2^24-1,从0开始)。我们要是想获得1秒延时,直接填72000000会溢出。
咱们测试下,写个Systick的配置函数试试。
因为Systick系统嘀嗒定时器,是内核外设,所以咱们单独给他建一个文件夹就叫System。
在里面创建两个文件,Delay.c和Delay.h
Delay.c用来放延时函数,Delay.h用于声明函数,方便其他文件调用Delay.c里的函数
/* Delay.c code */
#include "air32f10x.h"
#define SYSCLK_FREQ_72MHz 72000000
void Delay_ms(uint32_t ticks)
{
uint16_t i;
SysTick_Config((SYSCLK_FREQ_72MHz/1000)); //ms,divided by 1000
for(i=0;i<ticks;i++) //loop ticks times
{
while((SysTick->CTRL&(0x01<<16))==0); //wait, until countflag=1
}
SysTick->CTRL&=~0x01; //stop counting
}
Delay.h
#ifndef __DELAY_H__
#define __DELAY_H__
void Delay_ms(uint32_t ticks);
#endif
main.c
#include "air32f10x.h"
#include "Delay.h"
int main()
{
GPIO_InitTypeDef GPIO_InitiateStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitiateStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitiateStructure.GPIO_Pin=GPIO_Pin_10;
GPIO_InitiateStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitiateStructure);
while(1)
{
GPIO_WriteBit(GPIOB, GPIO_Pin_10, Bit_SET);
Delay_ms(1000);
GPIO_WriteBit(GPIOB, GPIO_Pin_10, Bit_RESET);
Delay_ms(1000);
}
}
实验现象:
用示波器看下波形