一般来说,时钟精度、稳定性取决于所采用的时钟源,就MCU S32K来说如内部振荡器SIRC,FIRC,128KLPO,外部晶振等,跟所使用的外设(FTM, LPIT,LPT,RTC等)和哪一路输出时钟(SYS_CLK,BUS_CLK,SPLLDVI1_CLK等)没有直接关系。
由于S32K144提供的时钟源和配置方法比较多,那么如何有效配置得到自己想要的时钟呢?
下面以S32K144为例,从时钟定义图开始,逐步介绍如何寻找S32K144时钟(CORE_CLK, BUS_CLK,)合适的配置方法,并根据测试方法验证配置是否有效。
0. 目标
输出目标
clock clock= 80MHz(以下MHz简称M)
bus clock=40MHz, Normal Mode(默认)
注意:只有支持105℃的部分S32K144型号,core clock才能支持到最高112M(HSRUN),其他型号不论HSRUN模式,还是RUN模式,最高支持80M。
测试工具
PCB: S32K144EVB-Q100
IDE: S32DS for ARM 2018.R1
1.时钟定义
配置时钟先从学会看时钟分配图开始,会看图了,配置就成功了一半。
因为能实现目标的路径可能有多种,这里先选中一条配置路径,再判断是否可行(蓝色箭头+框)。
时钟源选择外部晶振8M,即SOSC=8M => SPLL时钟源 SPLL_CLK_SOURCE = SOSC = 8M,
2.确认内部时钟要求
CORE_CLK/ SYS_CLK 最高支持112M(HSRUN) , 80M(RUN),目标@80M符合要求。
不过,BUS_CLK最高支持56M(HSRUN), 48M(RUN),目标@40M符合要求。
3.确认外部晶振 8M
查看S32KEVB-Q100评估板原理图,知外部晶振8M => System PLL输入 SPLL_SOURCE=8M
4.确认System PLL分频及倍频
这里选择PREDIV=0, MULT=0b11000 (10进制: 24)
VCO_CLK=8M/(0+1) X (24+16) = 320MHz
SPLL_CLK=(VCO_CLK)/2 = 160MHz
考虑:这里MULTI还可以>24 => 倍频因子>40,那么能不能配置VCO_CLK >320M,而最终CORE_CLK=80M呢?
答案是不行,因为VCO_CLK最高支持到320M,可以参考S32K-RM。
5.具体寄存器配置
可参考RM配置方法
6.设置检测方法
1)初步确定为通过IO口直接输出与总线时钟相关的时钟或者分频后的时钟
2)寻找SCH上是否有可直接利用的MCU引脚
MCU完整型号:FS32K144HRT0VLLT, 封装:LQFP100
查RM附件IO描述表,发现能作为CLKOUT pin的有PTD14, PTE10, PTB5。
如有可用CLKOUT pin,配置相应的pin为CLKOUT功能
1)配置PORT的MUX为CLKOUT对应ALT功能,并使能端口时钟;
2)配置SIM_CHIPCTL选择输出的时钟,以及输出的分频系数;
输出时钟结构
CLKOUTSEL - 选择输出时钟,如选择设置CLKOUTSEL=0b1001,则CLKOUT pin输出Bus_Clock
7.时钟配置代码
main.c
int main(void) { volatile uint32_t cnt = 0; Clock_Init(); Port_Init(); Clock_Out_Bus(); while(1) { cnt ++; } return 0; }
clock.c - 时钟配置
/** * 配置MCU时钟, 使SYS_CLK=80MHz, BUS_CLK=40MHz, FLASH_CLK=20MHz, 用外部晶振f_osc=8MHz * SPLL_CLK=160MHz, VCO_CLK=320MHz * @note * 计算公式: * BUS_CLK=SYS_CLK / DIVBUS * SYS_CLK=SPLL_CLK / DIVCORE * SPLL_CLK=(VCO_CLK) / 2 * VCO_CLK=SPLL_SOURCE / (PREDIV + 1) x (MULT + 16) * 选择外部晶振作为SPLL_SOURCE, SPLL_SOURCE=8MHz */ void Clock_Init(void) { // 以下初始化顺序不可改变 Clock_SOSC_Init_8M(); // SOSC&SPLL_SOURCE初始化为输出8M Clock_SPLL_Init_160M(); // SPLL&SCG初始化为输出160M Clock_RUNMode_SYS_Init_80M(); // sys clock = 80M, bus clock = 40M, flash = 20M } void Clock_ConfigureOut(SIM_CLKOUTSEL_Type clkoutsel) { if(clkoutsel <= RTC_CLK) { SIM->CHIPCTL &= ~SIM_CHIPCTL_CLKOUTEN_MASK; // disable CLKOUT SIM->CHIPCTL &= ~SIM_CHIPCTL_CLKOUTSEL_MASK; // clear CLKOUT SIM->CHIPCTL |= SIM_CHIPCTL_CLKOUTSEL(clkoutsel); // select CLKOUT source = clkoutsel SIM->CHIPCTL |= SIM_CHIPCTL_CLKOUTEN_MASK; // enable CLKOUT } } /** * CLKOUT pin输出Bus CLK */ void Clock_Out_Bus(void) { Clock_ConfigureOut(BUS_CLK); } /** * 配置SOSC_CLK=8M * @output SOSCDIV1_CLK * @output SOSCDIV2_CLK * @note * 外部晶振8M * 计算公式 * SOSCDIV1_CLK=SOSC_CLK / SOSCDIV1 * SOSCDIV2_CLK=SOSC_CLK / SOSCDIV2 */ void Clock_SOSC_Init_8M(void) { // Frequency division for SOSCDIV1_CLK, SOSCDIV2_CLK SCG->SOSCDIV=SCG_SOSCDIV_SOSCDIV1(1) // SOSCDIV1=1 : Divide by 1 | SCG_SOSCDIV_SOSCDIV2(1); // SOSCDIV2=1 : Divide by 1 SCG->SOSCCFG=SCG_SOSCCFG_RANGE(0b11) // RANGE=3: 8-40 MHz SOSC // RANGE=3: 8-40 MHz SOSC | SCG_SOSCCFG_HGO(1) // HGO=0: Configure crystal oscillator for high-gain operation | SCG_SOSCCFG_EREFS(1); // EREFS=1: Internal crystal oscillator of OSC selected while(SCG->SOSCCSR & SCG_SOSCCSR_LK_MASK); /* Ensure SOSCCSR unlocked */ SCG->SOSCCSR = SCG_SOSCCSR_SOSCEN(1); // SOSC Enable } /** * 配置MCU时钟SPLL_CLK=160M * @require SOSC_CLK = 8M * @output SPLL_CLK */ void Clock_SPLL_Init_160M(void) { while(SCG->SPLLCSR & SCG_SPLLCSR_LK_MASK); /* Ensure SPLLCSR unlocked */ // 关闭SPLL SCG->SPLLCSR = 0; /* SPLLEN=0: SPLL is disabled (default) */ // 配置SCG_SPLLCFG, 使VCO_CLK=320MHz, SPLL_CLK=160MHz SCG->SPLLCFG = SCG_SPLLCFG_PREDIV(0) | SCG_SPLLCFG_MULT(0b11000); SCG->RCCR = SCG_RCCR_SCS(0b0110) // System Clock Source: System PLL (SPLL_CLK) | SCG_RCCR_DIVCORE(0) // DIVCORE=0 | SCG_RCCR_DIVBUS(1) // DIVBUS=1 | SCG_RCCR_DIVSLOW(1); // DVISLOW=1 // 使能SPLL SCG->SPLLCSR |= SCG_SPLLCSR_SPLLEN_MASK; // Lock SPLL SCG->SPLLCSR |= SCG_SPLLCSR_LK_MASK; } /** * SYS_CLK = 80M, BUS_CLK = 40M, FLASH_CLK=20M * @require SPLL_CLK=160M * @note SYS_CLK=80MHz,BUS_CLK=40MHz, 使用外部晶振f_osc=8MHz * 前提条件使用SPLL_SOURCE=160M作为system clock * 计算公式: * SYS_CLK=SPLL_CLK / DIVCORE * BUS_CLK=SYS_CLK / DIVBUS * FLASH_CLK=SYS_CLK / DIVSLOW * SPLL_CLK=(VCO_CLK) / 2 * VCO_CLK=SPLL_SOURCE / (PREDIV + 1) x (MULT + 16) * * =>DIVCORE=1, DIVBUS=1,DIVSLOW=3 */ void Clock_RUNMode_SYS_Init_80M(void) { // 配置system clock源, DIVCORE, DIVBUS, DIVFLASH SCG->RCCR = SCG_RCCR_SCS(0b0110)// Select System Clock Source: System PLL (SPLL_CLK) | SCG_RCCR_DIVCORE(1) | SCG_RCCR_DIVBUS(1) | SCG_RCCR_DIVSLOW(3); // 检查Clock Status(SCG_CSR) 是否为RCCR配置 while(0b0110 != (SCG->CSR & SCG_CSR_SCS_MASK) >> SCG_CSR_SCS_SHIFT) { } }
Port.c
#define PORTD14_IDX 14 #define PORTD16_IDX 16 #define PORT_CLKOUT_IDX PORTD14_IDX #define PORT_LED_GREEN_IDX PORTD16_IDX /* * PTD14 - CLKOUT * PTD16 - GPO for LED */ void Port_Init(void) { // Enable clock to PORTD if(PCC->PCCn[PCC_PORTD_INDEX] & PCC_PCCn_PR_MASK) { PCC->PCCn[PCC_PORTD_INDEX] |= PCC_PCCn_CGC(1); // Clock enabled to PORTD } // Set Port Data Direction PTD->PDDR |= 1 << PORT_LED_GREEN_IDX; // Port Data Direction: output // set/clear PortD's value // PTD->PTSO |= 1 << PORT_LED_GREEN_IDX; // Port Set Output PTD->PCOR |= 1 << PORT_LED_GREEN_IDX; // Port Clear Output // PTD16 - GPO PORTD->PCR[PORT_LED_GREEN_IDX] = PORT_PCR_MUX(0b0000001); // Port D16: MUX=ALT1, GPIO(to green LED on EVB/PCB) // PTD14 - CLKOUT PORTD->PCR[PORT_CLKOUT_IDX] = PORT_PCR_MUX(0b00000111); // Port D14: MUX=ALT4, CLKOUT }
clock.c - CLKOUT 输出选择
/** * CLKOUT pin输出Bus CLK */ void Clock_Out_Bus(void) { Clock_ConfigureOut(0b1001); } void Clock_ConfigureOut(SIM_CLKOUTSEL_Type clkoutsel) { if(clkoutsel <= 0b1110) { SIM->CHIPCTL &= ~SIM_CHIPCTL_CLKOUTEN_MASK; // disable CLKOUT SIM->CHIPCTL &= ~SIM_CHIPCTL_CLKOUTSEL_MASK; // clear CLKOUT SIM->CHIPCTL |= SIM_CHIPCTL_CLKOUTSEL(clkoutsel); // select CLKOUT source = clkoutsel SIM->CHIPCTL |= SIM_CHIPCTL_CLKOUTEN_MASK; // enable CLKOUT } }
8. 观察CLKPUT pin 输出
需要用示波器观测PTD14,截图略。