1.F28335系统时钟来源
F28335的时钟来源有两种:
(1)采用外部振荡器作为时钟源(外部时钟)——即在XCLKIN引脚上、或复用X1引脚引入其他数字系统或外部振荡器的时钟信号;
(2)采用F28335内部振荡器作为时钟源(内部时钟)——即在X1和X2引脚间连接一个晶体从而产生时钟源。
一般选择采用内部时钟,在X1和X2之间接入一个30MHz的晶振。内部信号时钟源和外部信号时钟源通过异或门后选择接入成为OSCCLK即振荡器时钟信号。OSCCLK受到寄存器PLLSTS(OSCOFF)位控制是否通断。信号经过开关后分两路传输,一路直接通过;另一路经过PLL锁相环模块。之所以不能直接使用OSCCLK信号的原因是它的频率太低。需要进入锁相环倍频和分频后才能使用。通过对寄存器PLLSTS(PLLOFF)位控制,可以使能锁相环。使能后通过寄存器PLLCTR设置倍频数(最大可为10)。倍频数为10时,通过PLL后的时钟信号VCOCLK为300MHz。所以还有对寄存器PLLSTS(DIVSEL)设置分频系数为2.得到最终的150MHz的时钟信号CLKIN来供给CPU。
2.F28335系统控制及外设时钟
CLKIN系统信号提供给C28x内核的时钟,它作为SYSCLKOUT从CPU传出(也就是说,CLKIN与SYSCLKOUT频率相同)。28335DSP中外设的时钟信号则是对系统时钟信号SYSCLKOUT处理后得到的。如下图所示
可以通过外设时钟控制寄存器使能外部时钟。在实际应用中,为了降低系统功耗,不使用的外设最好将其外设时钟禁止。外设时钟包括快速外设和慢速外设两种,分别通过HISPCP和LOSPCP寄存器进行设置。SPI、I2C、MCBSP这些串口通信协议都使用低速外设时钟信号。AD采用高速时钟信号。除此之外,系统时钟经过1/2分频后提供给eCAN模块。系统时钟直接输出给系统控制寄存器模块、DMA模块、EPWM模块、ECAP模块、EQEP模块。
3.F28335时钟单元常用寄存器
①外设时钟控制寄存器PCLCR0/1/3
控制片上各种外设时钟的状态,使能或禁止。
②高/低速外设时钟预分频寄存器HISPCP/LOSPCP
分别控制高/低速的外设时钟。
③锁相环状态寄存器PLLSTS
④锁相环控制寄存器PLLCR
用于控制芯片PLL的倍数,在向PLL控制寄存器进行写操作之前,需要:
(1)在PLL完全锁住后,即PLLSTS【PLLLOCKS】=1
(2)芯片不能工作在LIMP模式,即PLLSTS[MCLKSTS】=0
4.自定义系统时钟
这里我主要想说一下TI提供的和系统时钟相关的文件和函数。
4.1 InitSysCtrl()函数
该函数位于TI提供的库文件DSP2833x_SysCtrl.c 内, 功能是按照需求配置系统控制寄存器值:默认将看门狗关闭,系统时钟配置为150MHz,并分出高速时钟和低速时钟以及使能DSP各外设时钟。
需要提前声明一点的是系统控制寄存器SysCtrlRegs的定义在TI提供的DSP2833x_GlobalVariableDefs.c库文件中
结构体类型SYS_CTRL_REGS的声明与外部变量SysCtrlRegs的定义在DSP2833x_SysCtrl.h头文件中。从下面代码可以看出系统控制寄存器中包含的寄存器即是F28335时钟单元常用寄存器。
// System Control Register File:
//
struct SYS_CTRL_REGS {
Uint16 rsvd7; // 0
union PLLSTS_REG PLLSTS; // 1
Uint16 rsvd1[8]; // 2-9
union HISPCP_REG HISPCP; // 10: High-speed peripheral clock pre-scaler
union LOSPCP_REG LOSPCP; // 11: Low-speed peripheral clock pre-scaler
union PCLKCR0_REG PCLKCR0; // 12: Peripheral clock control register
union PCLKCR1_REG PCLKCR1; // 13: Peripheral clock control register
union LPMCR0_REG LPMCR0; // 14: Low-power mode control register 0
Uint16 rsvd2; // 15: reserved
union PCLKCR3_REG PCLKCR3; // 16: Peripheral clock control register
union PLLCR_REG PLLCR; // 17: PLL control register
// No bit definitions are defined for SCSR because
// a read-modify-write instruction can clear the WDOVERRIDE bit
Uint16 SCSR; // 18: System control and status register
Uint16 WDCNTR; // 19: WD counter register
Uint16 rsvd4; // 20
Uint16 WDKEY; // 21: WD reset key register
Uint16 rsvd5[3]; // 22-24
// No bit definitions are defined for WDCR because
// the proper value must be written to the WDCHK field
// whenever writing to this register.
Uint16 WDCR; // 25: WD timer control register
Uint16 rsvd6[6]; // 26-31
};
InitSysCtrl函数程序代码如下:
// InitSysCtrl:
//---------------------------------------------------------------------------
// 这段函数的功能就是将系统控制寄存器初始化到想要的状态;
//具体步骤可以分成四部分:
// - 禁止看门狗电路
// - 给PLLCR寄存器赋值以获得想要的系统时钟频率
// - 给高、低速外设时钟预定标寄存器赋值以获得想要的高、低速外设时钟频率
// - 对需要使用的外设时钟进行使能
void InitSysCtrl(void)
{
// Disable the watchdog
DisableDog();
// Initialize the PLL control: PLLCR and DIVSEL
// DSP28_PLLCR and DSP28_DIVSEL are defined in DSP2833x_Examples.h
InitPll(DSP28_PLLCR,DSP28_DIVSEL);
// Initialize the peripheral clocks
InitPeripheralClocks();
}
在DsiableDog()函数中禁用了看门狗电路
void DisableDog(void)
{
EALLOW;
SysCtrlRegs.WDCR= 0x0068;
EDIS;
}
在InitPeripheralClocks()函数中
// 函数名:初始化外设时钟
//---------------------------------------------------------------------------
//这个是用来初始化外设模块的时钟,具体分2个步骤:
//首先设置高、低速外设预定标寄存器;
//第二是对各个外设时钟进行有选择的使能;
//为了降低功耗,尽量不使能不用的外设模块时钟;
//
//注:如果某一外设时钟没使能,则不能对其相关的寄存器进行读写;
void InitPeripheralClocks(void)
{
EALLOW;
// HISPCP/LOSPCP prescale register settings, normally it will be set to default values
SysCtrlRegs.HISPCP.all = 0x0001;
SysCtrlRegs.LOSPCP.all = 0x0002;//Lowspeedclock=SYSCLKOUT/4=150/4=37.5MHZ;
// XCLKOUT to SYSCLKOUT ratio. By default XCLKOUT = 1/4 SYSCLKOUT
// XTIMCLK = SYSCLKOUT/2
XintfRegs.XINTCNF2.bit.XTIMCLK = 1;
// XCLKOUT = XTIMCLK/2
XintfRegs.XINTCNF2.bit.CLKMODE = 1;
// Enable XCLKOUT
XintfRegs.XINTCNF2.bit.CLKOFF = 0;
// Peripheral clock enables set for the selected peripherals.
// If you are not using a peripheral leave the clock off
// to save on power.
//
// Note: not all peripherals are available on all 2833x derivates.
// Refer to the datasheet for your particular device.
//
// This function is not written to be an example of efficient code.
SysCtrlRegs.PCLKCR0.bit.ADCENCLK = 0; // ADC
// *IMPORTANT*
// The ADC_cal function, which copies the ADC calibration values from TI reserved
// OTP into the ADCREFSEL and ADCOFFTRIM registers, occurs automatically in the
// Boot ROM. If the boot ROM code is bypassed during the debug process, the
// following function MUST be called for the ADC to function according
// to specification. The clocks to the ADC MUST be enabled before calling this
// function.
// See the device data manual and/or the ADC Reference
// Manual for more information.
ADC_cal();
SysCtrlRegs.PCLKCR0.bit.I2CAENCLK = 0; // I2C
SysCtrlRegs.PCLKCR0.bit.SCIAENCLK = 0; // SCI-A
SysCtrlRegs.PCLKCR0.bit.SCIBENCLK = 0; // SCI-B
SysCtrlRegs.PCLKCR0.bit.SCICENCLK = 0; // SCI-C
SysCtrlRegs.PCLKCR0.bit.SPIAENCLK = 0; // SPI-A
SysCtrlRegs.PCLKCR0.bit.MCBSPAENCLK = 0; // McBSP-A
SysCtrlRegs.PCLKCR0.bit.MCBSPBENCLK = 0; // McBSP-B
SysCtrlRegs.PCLKCR0.bit.ECANAENCLK=0; // eCAN-A
SysCtrlRegs.PCLKCR0.bit.ECANBENCLK=0; // eCAN-B
SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 0; // Disable TBCLK within the ePWM
SysCtrlRegs.PCLKCR1.bit.EPWM1ENCLK = 0; // ePWM1
SysCtrlRegs.PCLKCR1.bit.EPWM2ENCLK = 0; // ePWM2
SysCtrlRegs.PCLKCR1.bit.EPWM3ENCLK = 0; // ePWM3
SysCtrlRegs.PCLKCR1.bit.EPWM4ENCLK = 0; // ePWM4
SysCtrlRegs.PCLKCR1.bit.EPWM5ENCLK = 0; // ePWM5
SysCtrlRegs.PCLKCR1.bit.EPWM6ENCLK = 0; // ePWM6
SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 0; // Enable TBCLK within the ePWM
SysCtrlRegs.PCLKCR1.bit.ECAP3ENCLK = 0; // eCAP3
SysCtrlRegs.PCLKCR1.bit.ECAP4ENCLK = 0; // eCAP4
SysCtrlRegs.PCLKCR1.bit.ECAP5ENCLK = 0; // eCAP5
SysCtrlRegs.PCLKCR1.bit.ECAP6ENCLK = 0; // eCAP6
SysCtrlRegs.PCLKCR1.bit.ECAP1ENCLK = 0; // eCAP1
SysCtrlRegs.PCLKCR1.bit.ECAP2ENCLK = 0; // eCAP2
SysCtrlRegs.PCLKCR1.bit.EQEP1ENCLK = 0; // eQEP1
SysCtrlRegs.PCLKCR1.bit.EQEP2ENCLK = 0; // eQEP2
SysCtrlRegs.PCLKCR3.bit.CPUTIMER0ENCLK = 0; // CPU Timer 0
SysCtrlRegs.PCLKCR3.bit.CPUTIMER1ENCLK = 0; // CPU Timer 1
SysCtrlRegs.PCLKCR3.bit.CPUTIMER2ENCLK = 0; // CPU Timer 2
SysCtrlRegs.PCLKCR3.bit.DMAENCLK = 0; // DMA Clock
SysCtrlRegs.PCLKCR3.bit.XINTFENCLK = 0; // XTIMCLK
SysCtrlRegs.PCLKCR3.bit.GPIOINENCLK = 1; // GPIO input clock
EDIS;
}
在InitPll(DSP28_PLLCR,DSP28_DIVSEL)函数中初始化PLLSTS[CLKINDIV]和PLLCR控制寄存器
void InitPll(Uint16 val, Uint16 divsel)
{
// Make sure the PLL is not running in limp mode
if (SysCtrlRegs.PLLSTS.bit.MCLKSTS != 0)
{
// Missing external clock has been detected
// Replace this line with a call to an appropriate
// SystemShutdown(); function.
asm(" ESTOP0");
}
// DIVSEL MUST be 0 before PLLCR can be changed from
// 0x0000. It is set to 0 by an external reset XRSn
// This puts us in 1/4
if (SysCtrlRegs.PLLSTS.bit.DIVSEL != 0)
{
EALLOW;
SysCtrlRegs.PLLSTS.bit.DIVSEL = 0;
EDIS;
}
// Change the PLLCR
if (SysCtrlRegs.PLLCR.bit.DIV != val)
{
EALLOW;
// Before setting PLLCR turn off missing clock detect logic
SysCtrlRegs.PLLSTS.bit.MCLKOFF = 1;
SysCtrlRegs.PLLCR.bit.DIV = val;
EDIS;
// Optional: Wait for PLL to lock.
// During this time the CPU will switch to OSCCLK/2 until
// the PLL is stable. Once the PLL is stable the CPU will
// switch to the new PLL value.
//
// This time-to-lock is monitored by a PLL lock counter.
//
// Code is not required to sit and wait for the PLL to lock.
// However, if the code does anything that is timing critical,
// and requires the correct clock be locked, then it is best to
// wait until this switching has completed.
// Wait for the PLL lock bit to be set.
// The watchdog should be disabled before this loop, or fed within
// the loop via ServiceDog().
// Uncomment to disable the watchdog
DisableDog();
while(SysCtrlRegs.PLLSTS.bit.PLLLOCKS != 1)
{
// Uncomment to service the watchdog
// ServiceDog();
}
EALLOW;
SysCtrlRegs.PLLSTS.bit.MCLKOFF = 0;
EDIS;
}
// If switching to 1/2
if((divsel == 1)||(divsel == 2))
{
EALLOW;
SysCtrlRegs.PLLSTS.bit.DIVSEL = divsel;
EDIS;
}
// If switching to 1/1
// * First go to 1/2 and let the power settle
// The time required will depend on the system, this is only an example
// * Then switch to 1/1
if(divsel == 3)
{
EALLOW;
SysCtrlRegs.PLLSTS.bit.DIVSEL = 2;
DELAY_US(50L);
SysCtrlRegs.PLLSTS.bit.DIVSEL = 3;
EDIS;
}
}