第二章 时钟树介绍及基础使用

1. 时钟源

芯片上所有的时钟均来自四个时钟源中的一个,分别是INTOSC2、INTOSC1、AUXCLKIN、XTAL

1.1 INTOSC2

INTOSC2 为主内部振荡器。上电时,器件由片内10MHz振荡器(INTOSC2)提供时钟。INTOSC2是主要的内部时钟源,也是复位时的默认系统时钟。该振荡器用于运行引导ROM,并可作为应用程序的系统时钟源。

需注意:INTOSC2的频率容差范围较宽,无法满足CAN模块的时序要求。使用CAN模块时必须外接振荡器。当INTOSC2作为系统时钟源时,GPIO19(X1)和GPIO18(X2)可作为通用GPIO引脚使用。

1.2 INTOSC1

INTOSC1 为备份内部振荡器,一个冗余的片上10MHz振荡器。INTOSC1作为备用时钟源,默认仅用于看门狗定时器和缺失时钟检测电路(MCD)的时钟供给。若启用MCD功能且检测到系统时钟丢失,则系统PLL会被旁路,所有系统时钟会自动连接到INTOSC1。此外,开发者也可手动选择INTOSC1作为系统时钟源,用于调试目的。

1.3 AUXCLKIN

AUXCLKIN 为辅助时钟输入。器件支持通过 GPIO29(AUXCLKIN)引脚接入额外的外部时钟源。该时钟必须为单端3.3V的外部时钟信号,可用作MCAN模块的时钟源。其频率限制和时序要求详见《TMS320F28P55x实时微控制器数据手册》。该外部时钟可直接连接至GPIO29引脚。

1.4 XTAL

XTAL 为外部时钟源。可以作为主系统和CAN1位时钟源使用。外部时钟源使用 X1/GPIO19 和 X2/GPIO18 引脚输入。它支持3种输入信号的方式:

  1. 一个单端3.3V外部时钟,可以直接连到X1,X2可以作为GPIO使用。

  1. 一个外部晶振,该晶振可通过X1和X2连接起来,同时负载电容也需要连接到位。

  1. 一个外部谐振器,通过X1和X2与地相连。

2. 基于时钟的延时函数使用

在 TI 的工程模板中,给我们准备好了一个可以微秒时间的延时函数:DEVICE_DELAY_US(x)

使用方法也很简单,要延时多少微秒,就往里面填多少值就好了。

需要注意的是其参数中 DEVICE_SYSCLK_FREQ 为默认的150MHz,如果我们的主频被修改了,它就不准确了。除非我们主动修改 DEVICE_SYSCLK_FREQ 为我们修改后的主频。

后面的代码可以以微秒作为基数,将其再度封装出延时毫秒、秒的功能。这样在处理一些软件时序时就有了延时功能。

void delay_ms(int x)
{
  while(x--)
  {
    DEVICE_DELAY_US(1000);
  }
}
void delay_s(int x)
{
  while(x--)
  {
    delay_ms(1000);
  }
}

2.1 硬件说明

使用外部时钟源:20MHz 的外部晶振输入,将工程原本的 150MHz 主频配置为 50MHz,通过 CCS 自带的 CIO 功能输出当前主频,并在主循环中执行 LED 间隔 1 秒闪烁的代码。

2.2 CCS工程

2.2.1 CCS&syscfg配置

在时钟树选项页下,选择 CPUCLK 选项,开始对 CPUCLK 时钟树进行配置。

  1. 使能 XTAL 时钟源,其输入频率为 20MHz。

  2. XRAL_OR_X1 中选择 XTAL。

  3. OSCLKSRCSEL 中选择使用 X1_XTAL。

  4. 在 SYSPLL 中的 PLL_REFDIV 中,选择 1 分频。

  5. 在 SYSPLL 中的 PLL_IMULT 中,选择 30 倍频。

  6. 在 SYSPLL 中的 PLL_ODIV 中,选择 2 分频。

  7. SYSCLKDIVSEL 中,选择 6 分频。

  8. 最终得到主频 CPUCLK 频率为 50MHz。

配置板载的蓝灯引脚 GPIO20 为输出模式。

2.2.2 用户代码

我们修改的时钟树配置代码生成在 clocktree.h 文件中,主要是其中的 DEVICE_OSCSRC_FREQDEVICE_SETCLOCK_CFGDEVICE_SYSCLK_FREQ 宏定义。

而工程下有一个默认配置文件 device.h,其中进行了工程默认时钟主频的配置,它也定义了 DEVICE_SETCLOCK_CFG等3个宏定义。为了不冲突,我们将 clocktree.h 中定义的频率调整后的宏定义直接覆盖更新到 device.h 中。

#define MY_DEVICE_OSCSRC_FREQ          20000000U
//
// Define to pass to SysCtl_setClock(). Will configure the clock as follows:
// SYSPLL ENABLED
// SYSCLK = 50 MHz = 20 MHz (OSCCLK) * 30 (IMULT) / (1 (REFDIV) * 2 (ODIV) * 6 (SYSCLKDIVSEL))
#define MY_DEVICE_SYSCLK_FREQ          ((DEVICE_OSCSRC_FREQ * 30) / (1 * 2 * 6))
//
#define MY_DEVICE_SETCLOCK_CFG         (SYSCTL_OSCSRC_XTAL  | SYSCTL_IMULT(30) | \
                                     SYSCTL_REFDIV(1) | SYSCTL_ODIV(2)| \
                                     SYSCTL_SYSDIV(6) | SYSCTL_PLL_ENABLE | \
                                     SYSCTL_DCC_BASE_0)

更新工程的 empty_driverlib_main.c 文件为以下代码:

#include "driverlib.h"
#include "device.h"
#include "board.h"
#include "c2000ware_libraries.h"
#include "stdio.h" //导入用以支持 printf

//任意毫秒延时
void delay_ms(int x)
{
    while(x--)
    {
        //调用 TI 自带的微秒延时
        DEVICE_DELAY_US(1000);
    }
}

void main(void)
{
    Device_init();

    Device_initGPIO();

    Interrupt_initModule();

    Interrupt_initVectorTable();

    Board_init();

    C2000Ware_libraries_init();

    EINT;
    ERTM;

    //重新配置系统时钟
    SysCtl_setClock(DEVICE_SETCLOCK_CFG);

    //获取当前主频并输出
    printf("clk = %ld\r\n", SysCtl_getClock(DEVICE_OSCSRC_FREQ) );

    while(1)
    {
        GPIO_togglePin(GPIO_BLUE);//蓝灯的引脚状态切换
        delay_ms(1000);//延时1000ms
    }
}

3. C2000 时钟系统与配置总结 (基于DriverLib)

:本总结以TMS320F2837x/Dx系列为例,其架构在C2000产品线中具有代表性。其他型号如F2833x或F28004x概念相通,但具体时钟源、分频器数量和多路选择器可能存在差异,请务必查阅具体芯片的技术参考手册 (TRM)DriverLib用户指南

3.1 时钟树概述与核心组件

C2000的时钟系统通常比ARM Cortex-M系列更简洁,但非常灵活和强大,旨在满足实时控制应用的精确时序要求。

  • 核心时钟路径(简化流程):
    时钟源 -> PLL (倍频) -> CPU时钟 -> SYSCLKOUT -> 外设时钟 (HSPCLK, LSPCLK) / 系统外设时钟

  • 主要组件说明

    组件 功能 C2000特点
    时钟源 提供原始时钟信号。 1. 内部振荡器 (INTOSC):低成本,精度较低。
    2. 外部晶体振荡器 (XTAL) + 内部OSC:高精度。
    3. 外部时钟源 (EXTSYSCLK):直接输入方波。
    PLL 锁相环。将输入时钟倍频到更高的核心系统时钟。 C2000 PLL配置灵活,通过PLLCRPLLSTS寄存器控制。支持旁路模式。
    系统时钟 (SYSCLKOUT) PLL输出的时钟,是CPU和大多数外设的时钟源。 所有时序的核心参考。例如,SYSCLKOUT=200MHz。
    预分频器 对SYSCLKOUT进行分频,产生不同速度的时钟域。 - HSPCLK: 高速外设时钟 (ePWM, HRPWM, eCAP)。
    - LSPCLK: 低速外设时钟 (SCI, SPI, I2C)。
    - ADC时钟: 通常由SYSCLKOUT或HSPCLK分频得到。

3.2 时钟初始化与配置函数

  • 核心配置流程(四步关键操作):

    1. 配置PLL旁路(确保系统从安全时钟启动)

    2. 配置时钟源(选择OSC源,等待稳定)

    3. 配置并启用PLL(设置倍频系数,等待锁定)

    4. 配置外设时钟分频器(设置HSPCLK, LSPCLK等的速度)

  • 基础配置示例(配置外部10MHz晶振,通过PLL倍频到200MHz):

    #include "driverlib.h"
    
    void main(void) {
        // 1. 初始化器件控制(必须首先调用)
        Device_init(); // 此函数会初始化一些基本控制,但通常不会直接配置PLL到最终状态
    
        // 2. 禁用PLL并进入旁路模式(安全操作)
        SysCtl_disablePLL(SYSCTL_PLL_ENABLE_CTL_DISABLE); // 先禁用PLL
        DEVICE_DELAY_US(100); // 短暂延迟
        SysCtl_setPLLBypassMode(SYSCTL_PLL_BYPASS_MODE_ENABLE); // 启用旁路,直接使用时钟源
    
        // 3. 配置时钟源 - 使用外部10MHz晶体
        // 假设XTAL=10MHz,配置内部振荡器电路来驱动它
        SysCtl_setClockSource(SYSCTL_CLOCK_SOURCE_EXTERNAL); // 或 SYSCTL_CLOCK_SOURCE_OSCCLK
        SysCtl_setOscSource(SYSCTL_OSCSRC_XTAL); // 明确设置振荡器源为外部晶体
        // 等待外部晶振稳定(检查XREADY位)
        while(SysCtl_getOscStatus(SYSCTL_OSC_STATUS_XTAL) != SYSCTL_OSC_STATUS_XTAL_READY) {}
    
        // 4. 配置PLL倍频系数并启用PLL
        // 目标频率 = (CLKIN * PLLCR.DIV) / (PLLSTS.DIVSEL) / 2
        // F2837x: 常用配置是 PLLCR.DIV=40, PLLSTS.DIVSEL=0 (除以2) -> (10MHz * 40) / 2 = 200MHz
        SysCtl_setPLLMultiplier(40); // 设置PLLCR寄存器的DIV值
        SysCtl_setPLLDivider(SYSCTL_PLL_DIVIDER_DIV_2); // 设置DIVSEL=0, 即除以2
    
        SysCtl_setPLLBypassMode(SYSCTL_PLL_BYPASS_MODE_DISABLE); // 退出旁路模式,接入PLL
        SysCtl_enablePLL(SYSCTL_PLL_ENABLE_CTL_ENABLE); // 正式使能PLL
    
        // 5. 等待PLL锁定
        while(SysCtl_getPLLStatus() != SYSCTL_PLL_STATUS_LOCK) {}
    
        // 6. 配置外设时钟预分频器
        // 设置高速外设时钟HSPCLK = SYSCLKOUT / 2 = 100MHz
        SysCtl_setHSPCLKDIV(SYSCTL_HSPCLKDIV_DIV_2);
        // 设置低速外设时钟LSPCLK = SYSCLKOUT / 4 = 50MHz
        SysCtl_setLSPCLKDIV(SYSCTL_LSPCLKDIV_DIV_4);
    
        // 此时,系统时钟SYSCLKOUT已运行在200MHz
        // CPU时钟也等于SYSCLKOUT (C28x内核)
    
        // ... 其他外设初始化 ...
    }
    
  • 关键配置函数详解

    函数 (DriverLib - F2837x) 说明 参数示例与解释
    SysCtl_disablePLL() / SysCtl_enablePLL() 禁用/使能PLL SYSCTL_PLL_ENABLE_CTL_DISABLE
    SYSCTL_PLL_ENABLE_CTL_ENABLE
    SysCtl_setPLLBypassMode() 设置PLL旁路模式 SYSCTL_PLL_BYPASS_MODE_ENABLE (使用原始时钟源)
    SYSCTL_PLL_BYPASS_MODE_DISABLE (使用PLL输出)
    SysCtl_setClockSource() 选择系统时钟源 SYSCTL_CLOCK_SOURCE_OSCCLK (主OSC)
    SYSCTL_CLOCK_SOURCE_AUX (辅助时钟源)
    SysCtl_setOscSource() 选择主振荡器源 SYSCTL_OSCSRC_XTAL (外部晶体)
    SYSCTL_OSCSRC_INT (内部振荡器)
    SysCtl_setPLLMultiplier(uint16_t multiplier) 设置PLL倍频系数 (PLLCR.DIV) multiplier = 目标频率 * 2 * DIVSEL / CLKIN
    例如:10MHz -> 200MHz, DIV=40, DIVSEL=0
    SysCtl_setPLLDivider(SysCtl_PLLDivider divider) 设置PLL后分频器 (PLLSTS.DIVSEL) SYSCTL_PLL_DIVIDER_DIV_1 (DIVSEL=2)
    SYSCTL_PLL_DIVIDER_DIV_2 (DIVSEL=0) 常用
    SYSCTL_PLL_DIVIDER_DIV_4 (DIVSEL=1)
    SysCtl_getPLLStatus() 获取PLL锁定状态 返回值需与SYSCTL_PLL_STATUS_LOCK比较
    SysCtl_setHSPCLKDIV() 设置高速外设时钟分频 SYSCTL_HSPCLKDIV_DIV_1 (HSPCLK=SYSCLKOUT)
    SYSCTL_HSPCLKDIV_DIV_2 (HSPCLK=SYSCLKOUT/2)
    SysCtl_setLSPCLKDIV() 设置低速外设时钟分频 SYSCTL_LSPCLKDIV_DIV_2 (LSPCLK=SYSCLKOUT/2)
    SYSCTL_LSPCLKDIV_DIV_4 (LSPCLK=SYSCLKOUT/4)

3.3 时钟源与PLL配置详解

  • PLL配置公式(F2837x)
    SYSCLKOUT = (CLKIN * PLLCR.DIV) / (PLLSTS.DIVSEL_VALUE) / 2

    DIVSEL (寄存器值) Divider Value DriverLib 宏
    0 /2 SYSCTL_PLL_DIVIDER_DIV_2
    1 /4 SYSCTL_PLL_DIVIDER_DIV_4
    2 /1 SYSCTL_PLL_DIVIDER_DIV_1
    3 Reserved

    计算示例

    • 目标:CLKIN=10MHz, SYSCLKOUT=200MHz。

    • 计算:选择DIVSEL=0 (/2)。代入公式:200 = (10 * DIV) / 2 => DIV = 40

    • 代码SysCtl_setPLLMultiplier(40); + SysCtl_setPLLDivider(SYSCTL_PLL_DIVIDER_DIV_2);

  • 常见时钟源配置

    场景 配置步骤
    内部振荡器 (INTOSC) 1. SysCtl_setOscSource(SYSCTL_OSCSRC_INT);
    2. (可选) 调整内部振荡器精度(如果有相关函数)。
    3. PLL配置公式中的CLKIN为内部振荡器频率(如10MHz)。
    外部晶体 (10MHz) 1. 硬件连接晶体和负载电容。
    2. SysCtl_setOscSource(SYSCTL_OSCSRC_XTAL);
    3. 等待稳定 while(!SysCtl_getOscStatus(SYSCTL_OSC_STATUS_XTAL));
    4. PLL配置公式中的CLKIN为晶体频率。
    外部有源时钟 1. 将时钟信号连接到XCLKIN或X1引脚(取决于芯片)。
    2. 配置GPIO复用为时钟输入功能。
    3. SysCtl_setClockSource(...) 选择正确的源。
    4. PLL配置公式中的CLKIN为外部时钟频率。

3.4 外设时钟配置

系统时钟配置好后,需要为各个外设配置其运行时钟。

  • ePWM (HRPWM) 时钟
    ePWM模块的时钟来自HSPCLK。

    // 假设SYSCLKOUT=200MHz, HSPCLK配置为100MHz
    SysCtl_setHSPCLKDIV(SYSCTL_HSPCLKDIV_DIV_2); // HSPCLK = 200MHz / 2 = 100MHz
    
    // 在初始化ePWM时,其时间基准周期(TBPRD)和比较值(CMPA)的计算都基于HSPCLK(100MHz)
    EPWM_setTimeBasePeriod(myEPWM1_BASE, 10000); // 周期 = 10000 TBCLK counts
    // TBCLK = HSPCLK / (HSDIVIDER * CLKDIV) -> 通常直接分频HSPCLK
    EPWM_setClockPrescaler(myEPWM1_BASE, EPWM_CLOCK_DIVIDER_1, EPWM_HSCLOCK_DIVIDER_1); // TBCLK = HSPCLK = 100MHz
    
  • 串口 (SCI) 时钟
    SCI模块的时钟来自LSPCLK。

    // 假设SYSCLKOUT=200MHz, LSPCLK配置为50MHz
    SysCtl_setLSPCLKDIV(SYSCTL_LSPCLKDIV_DIV_4); // LSPCLK = 200MHz / 4 = 50MHz
    
    // 配置SCI波特率,计算基于LSPCLK
    SCI_setBaudRate(mySCI_BASE, 115200, 50000000); // 波特率115200, LSPCLK=50MHz
    
  • ADC时钟
    ADC时钟通常有独立的分频器,源可以是SYSCLKOUT或HSPCLK(取决于芯片)。

    // 配置ADC核心时钟分频器
    // ADC_CLK = (SYSCLKOUT / ADCDIV)
    ADC_setCoreClockDivider(myADC_BASE, ADC_CORE_CLOCK_DIVIDER_4); // ADC clock = 200MHz / 4 = 50MHz
    // 配置采样窗口周期(基于ADC_CLK)
    ADC_setSamplingWindow(myADC_BASE, ADC_SAMPLE_WINDOW_10_ADC_CLKS);
    

4. 关键注意事项

  1. 启动顺序:配置PLL时必须遵循 “禁用 -> 旁路 -> 配置 -> 使能 -> 等待锁定” 的顺序,否则可能导致芯片锁死或运行不稳定。

  2. 时钟验证

  • 在调试时,可以使用GPIO toggle或CCS的寄存器查看器来验证时钟频率是否配置正确。

  • 例如,将GPIO配置为输出,在主循环中翻转,用示波器测量实际频率,应与计算值相符。

  1. 低功耗模式
  • C2000支持IDLE、STANDBY、HALT等低功耗模式,这些模式会动态地门控(关闭)时钟。

  • 使用 SysCtl_setLowPowerMode(SYSCTL_LOW_POWER_MODE_IDLE); 等函数进入,通过中断唤醒。

  1. 看门狗时钟
  • 看门狗有独立的时钟源(通常是INTOSC),即使系统主时钟失效,看门狗依然能工作,这是重要的安全特性。配置看门狗时需注意其分频设置。
  1. 芯片间差异
  • F28004x等较新的系列,其时钟配置API和寄存器名称可能与F2837x有所不同(例如使用CLK_setPLLMultiplier等函数),但核心概念(时钟源、PLL、分频)是一致的。始终以您所使用的芯片的文档为准

文中完整参考代码:https://github.com/hazy1k/C2000-Quick-Start-Guide-CCS/tree/main/TMS320F28P550/2.code

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐