本文简介
本文主要详细叙述了 S32K144 的时钟模块相关内容。本文代码包含以下内容:
- 一般的时钟初始化流程
- LPIT 通道计数器设置
时钟结构图
在看代码之前,首先要熟悉时钟系统,下图为本程序所用到的时钟和模块示意图。
可以看到外部晶振输入 XTAL(External Crystal Oscillator) 和震荡器 OSC(System Oscillator) 相连,产生了一个时钟信号为 SOSC_CLK 该信号被 SCG_SOSCDIV 分频成为两个 SOSC_DIV1_CLK 和 SOSC_DIV2_CLK 时钟信号。SOSC_CLK 又被锁相环 PLL 升频到 SPLL_CLK 信号,SPLL_CLK 被 SCG_SPLLDIV 分频得到 SPLL_DIV1_CLK 或者得到 SPLL_DIV2_CLK。
FIRC(Fast Internal Reference Clock) 产生一个 48HMz 的时钟 FIRC_CLK, SIRC(Slow Internal Reference Clock) 产生一个 8MHz 的信号 SIRC_SLK, FIRC_CLK 和 SIRC_SLK 和 SPLL_CLK 和 SOSC_CLK 被 SCG_RCCR[SCS] 选择器选择信号,使用 DIVCORE 分频得到 CORE_CLK,SYSCLK ;使用 DIVBUS 分频得到 BUS_CLK ,使用 DIVSLOW 分频得到 FLASH_CLK 和 SCG_SLOW_CLK 在这个地方,分频器的倍率选择是有要求的:
主要是由于结构限制导致的。注意在设置分频器的时候,请在时钟源开启前进行,否则无法改变,分频器在时钟开启后才会被改变其值。
Manual上关于该芯片的时钟模块描述:
1. 系统时钟发生器 System clock generator (SCG)
系统时钟发生器(下称 SCG )给单片机提供了不同的时钟源,包含三个时钟分支:
-
SPLL(System Phase-locked Loop) 系统锁相环,一个压控震荡器。在其他地方也被称作 PLL
- 电压控制振荡器 VCO(Voltage-controlled oscillator)
- 外部参考时钟是锁相环的时钟源
- 模数 VCO 分频器
- 相位/频率探测器
- 可以被选做单片机的系统时钟源
- 双路可编程控制的时钟输出,可以提供给片内外围器件时钟源
-
FIRC/SIRC(Fast/Slow Internal Reference Clock) 两个内部参考时钟 IRC 发生器:
- 快速内部参考时钟 FIRC 可以程序控制高/低频率范围, 高为 48Mhz。
- 快速内部参考时钟 FIRC 和慢速内部参考时钟 SIRC 都可以被选做单片机的系统时钟源
-
SOSC(System Crystal Oscillator) 系统晶振振荡器,在其他地方也被称作 OSC
- 被当做系统 PLL 的时钟源
- 可以被当做单片机的时钟源
注意:
- SPLL,SOSC 时钟可以被时钟监测器检测,提供重启和中断请求。
- SPLL 可以被锁定监测器检测,提供中断请求。
- 每一个时钟源都有一个参考分频器,给片内模块和外围器件提供分频功能,故时钟名中都有 DIV ,譬如:
- SPLLDIV1_CLK / SPLLDIV2_CLK
- FIRCDIV1_CLK / SCG_FIRCDIV2_CLK
- SIRCDIV1_CLK / SIRCDIV2_CLK
- SOSCDIV1_CLK / SOSCDIV2_CLK
2. 低功率振荡器 Low Power Oscillator (LPO)
一个内部的低功率时钟,可以给工作在低功率模式下的器件提供时钟源。
3. 外围时钟控制器 Peripheral Clock Control (PCC)
在上一节详细介绍过这个控制器,用于控制大多数器件的时钟选择。
代码
* hello_clocks.c Copyright NXP 2016
* Description: Example clock and LPIT channel initializations
* 2016 Mar 04 S Mihalik - Initial version
* 2016 Oct 27 SM - Updated for new header files in S32DS v 1.3
#include "S32K144.h" /* include peripheral declarations S32K144 */
#include "clocks_and_modes.h"
int lpit0_ch0_flag_counter = 0; /* LPIT0 timeout counter */
void PORT_init (void) {
PCC-> PCCn[PCC_PORTD_INDEX] = PCC_PCCn_CGC_MASK; /* Enable clock for PORT D */
PTD->PDDR |= 1<<0; /* Port D0: Data Direction= output */
PORTD->PCR[0] = 0x00000100; /* Port D0: MUX = ALT1, GPIO (to blue LED on EVB) */
}
void LPIT0_init (void) {
PCC->PCCn[PCC_LPIT_INDEX] = PCC_PCCn_PCS(6); /* Clock Src = 6 (SPLL2_DIV2_CLK)*/
PCC->PCCn[PCC_LPIT_INDEX] |= PCC_PCCn_CGC_MASK; /* Enable clk to LPIT0 regs */
LPIT0->MCR = 0x00000001; /* DBG_EN-0: Timer chans stop in Debug mode */
/* DOZE_EN=0: Timer chans are stopped in DOZE mode */
/* SW_RST=0: SW reset does not reset timer chans, regs */
/* M_CEN=1: enable module clk (allows writing other LPIT0 regs) */
LPIT0->TMR[0].TVAL = 40000000; /* Chan 0 Timeout period: 40M clocks */
LPIT0->TMR[0].TCTRL = 0x00000001; /* T_EN=1: Timer channel is enabled */
/* CHAIN=0: channel chaining is disabled */
/* MODE=0: 32 periodic counter mode */
/* TSOT=0: Timer decrements immediately based on restart */
/* TSOI=0: Timer does not stop after timeout */
/* TROT=0 Timer will not reload on trigger */
/* TRG_SRC=0: External trigger soruce */
/* TRG_SEL=0: Timer chan 0 trigger source is selected*/
}
void SOSC_init_8MHz(void) {
SCG->SOSCDIV=0x00000101; /* SOSCDIV1 & SOSCDIV2 =1: divide by 1 */
SCG->SOSCCFG=0x00000024; /* Range=2: Medium freq (SOSC betw 1MHz-8MHz)*/
/* HGO=0: Config xtal osc for low power */
/* EREFS=1: Input is external XTAL */
while(SCG->SOSCCSR & SCG_SOSCCSR_LK_MASK); /* Ensure SOSCCSR unlocked */
SCG->SOSCCSR=0x00000001; /* LK=0: SOSCCSR can be written */
/* SOSCCMRE=0: OSC CLK monitor IRQ if enabled */
/* SOSCCM=0: OSC CLK monitor disabled */
/* SOSCERCLKEN=0: Sys OSC 3V ERCLK output clk disabled */
/* SOSCLPEN=0: Sys OSC disabled in VLP modes */
/* SOSCSTEN=0: Sys OSC disabled in Stop modes */
/* SOSCEN=1: Enable oscillator */
while(!(SCG->SOSCCSR & SCG_SOSCCSR_SOSCVLD_MASK)); /* Wait for sys OSC clk valid */
}
void SPLL_init_160MHz(void) {
while(SCG->SPLLCSR & SCG_SPLLCSR_LK_MASK); /* Ensure SPLLCSR unlocked */
SCG->SPLLCSR = 0x00000000; /* SPLLEN=0: SPLL is disabled (default) */
SCG->SPLLDIV = 0x00000302; /* SPLLDIV1 divide by 2; SPLLDIV2 divide by 4 */
SCG->SPLLCFG = 0x00180000; /* PREDIV=0: Divide SOSC_CLK by 0+1=1 */
/* MULT=24: Multiply sys pll by 4+24=40 */
/* SPLL_CLK = 8MHz / 1 * 40 / 2 = 160 MHz */
while(SCG->SPLLCSR & SCG_SPLLCSR_LK_MASK); /* Ensure SPLLCSR unlocked */
SCG->SPLLCSR = 0x00000001; /* LK=0: SPLLCSR can be written */
/* SPLLCMRE=0: SPLL CLK monitor IRQ if enabled */
/* SPLLCM=0: SPLL CLK monitor disabled */
/* SPLLSTEN=0: SPLL disabled in Stop modes */
/* SPLLEN=1: Enable SPLL */
while(!(SCG->SPLLCSR & SCG_SPLLCSR_SPLLVLD_MASK)); /* Wait for SPLL valid */
}
void NormalRUNmode_80MHz (void) { /* Change to normal RUN mode with 8MHz SOSC, 80 MHz PLL*/
SCG->RCCR=SCG_RCCR_SCS(6) /* PLL as clock source*/
|SCG_RCCR_DIVCORE(0b01) /* DIVCORE=1, div. by 2: Core clock = 160/2 MHz = 80 MHz*/
|SCG_RCCR_DIVBUS(0b01) /* DIVBUS=1, div. by 2: bus clock = 40 MHz*/
|SCG_RCCR_DIVSLOW(0b10); /* DIVSLOW=2, div. by 2: SCG slow, flash clock= 26 2/3 MHz*/
while (((SCG->CSR & SCG_CSR_SCS_MASK) >> SCG_CSR_SCS_SHIFT ) != 6) {}
/* Wait for sys clk src = SPLL */
}
void WDOG_disable (void){
WDOG->CNT=0xD928C520; /*Unlock watchdog*/
WDOG->TOVAL=0x0000FFFF; /*Maximum timeout value*/
WDOG->CS = 0x00002100; /*Disable watchdog*/
}
int main(void) {
WDOG_disable();
PORT_init(); /* Configure ports */
SOSC_init_8MHz(); /* Initialize system oscilator for 8 MHz xtal */
SPLL_init_160MHz(); /* Initialize SPLL to 160 MHz with 8 MHz SOSC */
NormalRUNmode_80MHz(); /* Init clocks: 80 MHz sysclk & core, 40 MHz bus, 20 MHz flash */
LPIT0_init(); /* Initialize PIT0 for 1 second timeout */
for (;;) { /* Toggle output to LED every LPIT0 timeout */
while (0 == (LPIT0->MSR & LPIT_MSR_TIF0_MASK)) {} /* Wait for LPIT0 CH0 Flag */
lpit0_ch0_flag_counter++; /* Increment LPIT0 timeout counter */
PTD->PTOR |= 1<<0; /* Toggle output on port D0 (blue LED) */
LPIT0->MSR |= LPIT_MSR_TIF0_MASK; /* Clear LPIT0 timer flag 0 */
}
}
代码详解
老样子,还是从 main()
内开始:
PORT_init(); /* Configure ports */
这一行用于初始化端口,函数内部打开 CGC,配置 GPIO ,设置 PCR MUX 和被动滤波。由于第一节已经有了详细介绍,在这里不做过多描述。
SOSC_init_8MHz(); /* Initialize system oscilator for 8 MHz xtal */
SPLL_init_160MHz(); /* Initialize SPLL to 160 MHz with 8 MHz SOSC */
NormalRUNmode_80MHz(); /* Init clocks: 80 MHz sysclk & core, 40 MHz bus, 20 MHz flash */
这三个放在一起,因为他们都用到了一个指针来进行修改,这个指针就是 SCG
, 在头文件中 SCG
有以下的含义:
/** SCG - Register Layout Typedef */
typedef struct {
__I uint32_t VERID; /**< Version ID Register, offset: 0x0 */
__I uint32_t PARAM; /**< Parameter Register, offset: 0x4 */
uint8_t RESERVED_0[8];
__I uint32_t CSR; /**< Clock Status Register, offset: 0x10 */
__IO uint32_t RCCR; /**< Run Clock Control Register, offset: 0x14 */
__IO uint32_t VCCR; /**< VLPR Clock Control Register, offset: 0x18 */
__IO uint32_t HCCR; /**< HSRUN Clock Control Register, offset: 0x1C */
__IO uint32_t CLKOUTCNFG; /**< SCG CLKOUT Configuration Register, offset: 0x20 */
uint8_t RESERVED_1[220];
__IO uint32_t SOSCCSR; /**< System OSC Control Status Register, offset: 0x100 */
__IO uint32_t SOSCDIV; /**< System OSC Divide Register, offset: 0x104 */
__IO uint32_t SOSCCFG; /**< System Oscillator Configuration Register, offset: 0x108 */
uint8_t RESERVED_2[244];
__IO uint32_t SIRCCSR; /**< Slow IRC Control Status Register, offset: 0x200 */
__IO uint32_t SIRCDIV; /**< Slow IRC Divide Register, offset: 0x204 */
__IO uint32_t SIRCCFG; /**< Slow IRC Configuration Register, offset: 0x208 */
uint8_t RESERVED_3[244];
__IO uint32_t FIRCCSR; /**< Fast IRC Control Status Register, offset: 0x300 */
__IO uint32_t FIRCDIV; /**< Fast IRC Divide Register, offset: 0x304 */
__IO uint32_t FIRCCFG; /**< Fast IRC Configuration Register, offset: 0x308 */
uint8_t RESERVED_4[756];
__IO uint32_t SPLLCSR; /**< System PLL Control Status Register, offset: 0x600 */
__IO uint32_t SPLLDIV; /**< System PLL Divide Register, offset: 0x604 */
__IO uint32_t SPLLCFG; /**< System PLL Configuration Register, offset: 0x608 */
} SCG_Type, *SCG_MemMapPtr;
/** Peripheral SCG base address */
#define SCG_BASE (0x40064000u)
/** Peripheral SCG base pointer */
#define SCG ((SCG_Type *)SCG_BASE)
这是一个相当庞大的结构,翻阅 Manual 瞅一瞅各个小寄存器吧。
Register name | Width (in bits) |
Access |
---|---|---|
Version ID Register (SCG_VERID) | 32 | R |
Parameter Register (SCG_PARAM) | 32 | R |
Clock Status Register (SCG_CSR) | 32 | R |
Run Clock Control Register (SCG_RCCR) | 32 | RW |
VLPR Clock Control Register (SCG_VCCR) | 32 | RW |
HSRUN Clock Control Register (SCG_HCCR) | 32 | RW |
SCG CLKOUT Configuration Register (SCG_CLKOUTCNFG) | 32 | RW |
System OSC Control Status Register (SCG_SOSCCSR) | 32 | RW |
System OSC Divide Register (SCG_SOSCDIV) | 32 | RW |
System Oscillator Configuration Register (SCG_SOSCCFG) | 32 | RW |
Slow IRC Control Status Register (SCG_SIRCCSR) | 32 | RW |
Slow IRC Divide Register (SCG_SIRCDIV) | 32 | RW |
Slow IRC Configuration Register (SCG_SIRCCFG) | 32 | RW |
Fast IRC Control Status Register (SCG_FIRCCSR) | 32 | RW |
Fast IRC Divide Register (SCG_FIRCDIV) | 32 | RW |
Fast IRC Configuration Register (SCG_FIRCCFG) | 32 | RW |
System PLL Control Status Register (SCG_SPLLCSR) | 32 | RW |
System PLL Divide Register (SCG_SPLLDIV) | 32 | RW |
System PLL Configuration Register (SCG_SPLLCFG) | 32 | RW |
Version ID Register (SCG_VERID)
Field | Name | Description |
---|---|---|
VERSION | SCG Version Number | SCG 的版本号 |
Parameter Register (SCG_PARAM)
- 写入会导致错误。
字面意思是参数寄存器,其中 32 位的定义如下:
Field | Name | Description |
---|---|---|
31-27 | DIVPRES |
指示现在的 SCG 分频器使用状态DIVPRES[27]=1 DIVSLOW 正在被使用DIVPRES[28]=1 DIVBUS 正在被使用DIVPRES[31]=1 DIVCORE 正在被使用 |
7-0 | CLKPRES |
指示当前状态下哪个时钟正在被当做 SCG 时钟源 CLKPRES[0] Reserved CLKPRES[1]=1 System OSC (SOSC) 正在被使用 CLKPRES[2]=1 Slow IRC (SIRC) 正在被使用 CLKPRES[3]=1 Fast IRC (FIRC) 正在被使用 CLKPRES[6]=1 System PLL (SPLL) 正在被使用 |
Clock Status Register (SCG_CSR)
- 写入会导致错误。
这个寄存器返回当前的系统时钟源和系统时钟分频器配置,镜像 SCG_RCCR, SCG_VCCR, SCG_HCCR 三个时钟控制器其中之一的配置。
Field | Name | Description |
---|---|---|
27-24 | SCS |
返回当前配置的系统时钟发生器来源 0001 System OSC (SOSC_CLK) 0010 Slow IRC (SIRC_CLK) 0011 Fast IRC (FIRC_CLK) 0110 System PLL (SPLL_CLK) 其余的值无效 |
19-16 | DIVCORE |
指示现在的核心时钟分频比率 * 若 SPLL 被选做时钟源,则最大比率是 4 分频比率 = DIVCORE+1
|
7-4 | DIVBUS |
返回现在的总线(BUS)时钟分频比率 分频比率 = DIVBUS+1
|
3-0 | DIVSLOW |
返回现在的慢速时钟分频比率 分频比率 = DIVSLOW+1
|
Run Clock Control Register (SCG_RCCR)
这个寄存器给核心,平台,外围,总线控制系统时钟源和系统时钟分频器,只在 Run 模式下起作用,这个寄存器写入的时候只能一次写入 32 位。在 Run 模式下选择一个不同的时钟源需要时钟源在系统时钟调整到时钟源前就要开启并且保证有效。如果系统时钟分频器比率改变的同时选择了一个不同的时钟模式,则新的分频器比率只有在新的时钟源有效后才会发生改变。
寄存器见 SCG_CSR
。
VLPR Clock Control Register (SCG_VCCR)
SCG_VCCR 控制的是 VLPR 模式下的,而不是 Run 模式下的,其他的寄存器地图什么的和 SCG_RCCR 一样。写入的时候只能一次写入 32 位。
寄存器见 SCG_CSR
。
HSRUN Clock Control Register (SCG_HCCR)
在 HSRUN 模式下的时钟控制寄存器,同上。
寄存器见 SCG_CSR
。
SCG CLKOUT Configuration Register (SCG_CLKOUTCNFG)
这个寄存器控制哪一个 SCG 时钟源被输出到 CLKOUT 管脚。
Field | Name | Description |
---|---|---|
27-24 | SCS |
选择 SCG 系统时钟作为 CLKOUT 0001 System OSC (SOSC_CLK) 0010 Slow IRC (SIRC_CLK) 0011 Fast IRC (FIRC_CLK) 0110 System PLL (SPLL_CLK) 其余的值无效 |
System OSC Control Status Register (SCG_SOSCCSR)
这个寄存器控制 SCG 模块中的 SOSC 工作状态。
Field | Name | Description |
---|---|---|
26 | SOSCERR |
System OSC Clock Error 此位只能被单片机的上电复位重置,但是软件也可以通过向此位写入 1 清零 0 SOSC 检测器被关闭或者没有检测到错误 1 SOSC 检测器被开启并且检测到一个错误 |
25 | SOSCSEL |
System OSC Selected 此位不可写入 0 SOSC 不是系统时钟源 1 SOSC 是系统时钟源 |
24 | SOSCVLD |
System OSC Valid 此位不可写入 0 SOSC 无效或者没有启用 1 SOSC 有效并且输出有效 |
23 | LK |
Lock Register 此位可以在任何时候清零或者置 1 0 CSR 寄存器可以写入 1 CSR 寄存器不可以写入 |
17 | SOSCCMRE |
System OSC Clock Monitor Reset Enable SOSC 监测器中断/重置开关 0 当错误被检测,监测器产生中断 1 当错误被检测,监测器产生重启 |
16 | SOSCCM |
System OSC Clock Monitor SOSC 监测器开关 0 SOSC 监测器关 1 SOSC 监测器开 |
0 | SOSCEN |
System OSC Enable SOSC 使能 0 SOSC 关 1 SOSC 开 |
System OSC Divide Register (SCG_SOSCDIV)
这个寄存器控制两个时钟输出,既可以用作外围器件的功能时钟,也可以作为时钟模块使用,每一路输出都有分频器提供分频,应该在 SOSC 被关闭的情况下更改该寄存器的值以避免输出的值出现错误。
Field | Name | Description |
---|---|---|
10-8 | SOSCDIV2 |
System OSC Clock Divide 2 设置 SOSC 第二路输出的分频比率 分频比率 = 2^(SOSCDIV2-1) 0 时关闭时钟输出 |
2-1 | SOSCDIV1 |
System OSC Clock Divide 1 设置 SOSC 第一路输出的分频比率 分频比率 = 2^(SOSCDIV1-1) 0 时关闭时钟输出 |
System Oscillator Configuration Register (SCG_SOSCCFG)
此寄存器控制振荡器的工作状态,在 SOSC 运行的时候无法被写入,强行写入会被忽略并不会报赋值错误。
Field | Name | Description |
---|---|---|
5-4 | RANGE |
System OSC Range Select 选择 SOSC 外接晶振的频率范围 10 中频 (4 MHz to 8MHz) 11 高频 (8 MHz to 40 MHz) |
3 | HGO |
High Gain Oscillator Select 高增益振荡器选择 0 低增益运行振荡器 1 高增益运行振荡器 |
2 | EREFS |
External Reference Select 外部参考时钟选择 0 外部参考时钟 1 SOSC 内部晶体振荡器 |
Slow IRC Control Status Register (SCG_SIRCCSR)
Field | Name | Description |
---|---|---|
25 | SIRCSEL |
Slow IRC Selected 选择 SIRC 是否作为系统时钟源 0 Slow IRC 是系统时钟源 1 Slow IRC 不是系统时钟源 |
24 | SIRCVLD |
Slow IRC Valid Slow IRC 有效位 0 Slow IRC 无效或者没有启动 1 Slow IRC 启动并且有效 |
2 | SIRCLPEN |
Slow IRC Low Power Enable Slow IRC 低功率模式开关 0 Slow IRC 在 VLP 模式不启动下 1 Slow IRC 在 VLP 模式下启动 |
1 | SIRCSTEN |
Slow IRC Stop Enable Slow IRC 停止模式开关 0 Slow IRC 在 Stop modes 下不工作 1 Slow IRC 在 Stop modes 下工作 |
0 | SIRCEN |
Slow IRC Enable Slow IRC 使能 0 Slow IRC 关闭 1 Slow IRC 开启 |
Slow IRC Divide Register (SCG_SIRCDIV)
该寄存器控制 SIRC 的分频器工作状态,请确保该寄存器在改变前,SIRC 是关闭着的,以防止出现错误。
Field | Name | Description |
---|---|---|
10-8 | SIRCDIV2 |
Slow IRC Clock Divide 2 提供给需要异步时钟的模块的分频器 2 分频比率 = 2^(SIRCDIV2-1) 0 时关闭时钟输出 |
2-0 | SIRCDIV1 |
Slow IRC Clock Divide 1 提供给需要异步时钟的模块的分频器 1 分频比率 = 2^(SIRCDIV1-1) 0 时关闭时钟输出 |
Slow IRC Configuration Register (SCG_SIRCCFG)
此寄存器控制振荡器的工作状态,在 SIRC 运行的时候无法被写入,强行写入会被忽略并不会报赋值错误。
Field | Name | Description |
---|---|---|
0 | RANGE |
Frequency Range 频率范围 0 Slow IRC low range clock (2 MHz) 1 Slow IRC high range clock (8 MHz) |
剩余的 FIRC 跟这个差不多,但是有一部分细微的变化,到用的时候请查询对应的用户手册。