DSP

DSP28035时钟设置讲解

2019-07-13 10:14发布


TMS320x2803x系列(28035为例)系统时钟与TMS320x280x, 2801x, 2804x 系列时钟是不太一样的。
下面是TMS320x280x, 2801x, 2804x系列的时钟图如下:


这里写图片描述
这里写图片描述
TMS320x2803x系列(28035为例)的时钟与系统框图如下:
这里写图片描述
从上图可以看出SPI-A,SPI-B,SCI-A的时钟来自低速外设时钟LSPCLK; eCAN-A,LIN-A的时钟由SYSCLKOUT的二分频获得; 其它外设的时钟都是SYSCLKOUT。其中LSPCLK的大小由LOSPCP寄存器所设置,如下图:
这里写图片描述
TMS320x2803x系列(28035为例)的时钟源选择,如下图:
这里写图片描述
时钟源选择
2803x系列DSP有两个内部时钟源(INTOSC1和INTOSC2),可以不需要外部时钟。同时,也具有PLL时钟模块。一共有4种时钟源可供选择:
1) INTOSC1(10MHz)
内部时钟源1(INTOSC1),此时钟提供给看门狗块模块,内核和CPU定时器2 。
时钟频率默认为10MHz,可以通过INTOSCnTRIM寄存器修改频率。
2) INTOSC2(10MHz)
功能与INTOSC1是一样的。
3) 外部晶体振荡器
使用外部晶体振荡器给芯片提供时钟,晶振连接于X1/X2 脚。
4) 外部时钟源
如果不使用外部晶振作为时钟源,可以选择这种模式。时钟从外部时钟源的XCLKIN引脚输入生成。
注意:XCLKIN复用于GPIO19或GPIO38脚。可以通过XCLK寄存器的XCLKINSEL位选择是GPIO19还是GPIO38作为XCLKIN输入。
CLKCTL(XCLKINOFF)为0时,不使能此时钟。如果时钟源不使用或作为GPIO引脚时,用户应该在启动引导时禁用。

上面时钟图粗看起来很复杂,如果仔细分析,其实也很简单。从图的中间画一条分隔线,左边部分为4个输入时钟源,其中INTOSC1和INTOSC2是一模一样的,XTAL和XCLKIN是另外的两个时钟源; 右边部分三个时钟模块,从上到下分别是看门狗时钟WDCLK,系统时钟OSCCLK(此时钟到PLL),以及CPU定时器2时钟CPUTMR2CLK。
看完时钟框图后,下面是软件系统时钟的设置
在main函数的最初位置初始化DSP,即调用void InitialDSP(void)函数。
void InitialDSP(void) { DINT; IER = 0x0000; IFR = 0x0000; InitSysCtrl(); InitPieCtrl(); InitPieVectTable(); #ifdef RunInFlash memcpy( &secureRamFuncs_runstart, &secureRamFuncs_loadstart, &secureRamFuncs_loadend - &secureRamFuncs_loadstart); InitFlash(); #endif InitAdc(); InitGpio(); InitSci(); InitSpi(); InitCpuTimers(); EALLOW; PieVectTable.TINT2 = &OSTickISR; PieVectTable.USER12 = &OSCtxSw; PieVectTable.SCIRXINTA = &InterComRxInterrupt; PieVectTable.SCITXINTA = &InterComTxInterrupt; EDIS; … } 函数很多,这里主要讲解InitSysCtrl()。 void InitSysCtrl(void) { EALLOW; SysCtrlRegs.WDCR= 0x0068; //关看门狗 EDIS; EALLOW; SysCtrlRegs.PCLKCR0.bit.ADCENCLK = 1; // 关ADC时钟 (*Device_cal)(); // 用于校准内部振荡器和ADC,这个函数在boot ROM的时候,芯片会自动调用。 SysCtrlRegs.PCLKCR0.bit.ADCENCLK = 0; // 启动ADC时钟 EDIS; XtalOscSel (); //选择外部晶振时钟XTALOSC作为系统时钟源, 且关闭所有未使用的时钟以节省电源。 InitPll(12,1); //12*20M/4=60M InitPeripheralClocks(); //初始化外设时钟 } 下面是XtalOscSel ()函数的分析。 void XtalOscSel (void) { EALLOW; SysCtrlRegs.CLKCTL.bit.XTALOSCOFF = 0; // 开启外部晶振时钟XTALOSC SysCtrlRegs.CLKCTL.bit.XCLKINOFF = 1; // 关闭外部时钟源XCLKIN SysCtrlRegs.CLKCTL.bit.OSCCLKSRC2SEL = 0; // OSCCLKSRC2来自外部晶振时钟源 SysCtrlRegs.CLKCTL.bit.OSCCLKSRCSEL = 1; // OSCCLK来自INTOSC2/ext clk SysCtrlRegs.CLKCTL.bit.WDCLKSRCSEL = 1; // 看门狗时钟来自外部晶振时钟源 SysCtrlRegs.CLKCTL.bit.INTOSC2OFF = 1; // 关闭INTOSC2 SysCtrlRegs.CLKCTL.bit.INTOSC1OFF = 1; // 关闭INTOSC1 EDIS; } 这个函数主要是对CLKCTL寄存器的配置,要对照上面的时钟框图来看才好理解,主要是对图中几个开关状态的设置。CLKCTL寄存器各个位的功能如下:
这里写图片描述
这里写图片描述
这里写图片描述
接下来初始化PLL,函数是InitPll(12,1); void InitPll(Uint16 val, Uint16 divsel) { volatile Uint16 iVol; if (SysCtrlRegs.PLLSTS.bit.MCLKSTS != 0) // 判断时钟是否丢失 { EALLOW; SysCtrlRegs.PLLSTS.bit.MCLKCLR = 1; EDIS; } if (SysCtrlRegs.PLLSTS.bit.DIVSEL != 0) // PLLCR 被修改之前DIVSEL必须设置为 0 { EALLOW; SysCtrlRegs.PLLSTS.bit.DIVSEL = 0; EDIS; } if (SysCtrlRegs.PLLCR.bit.DIV != val) //修改PLLCR { EALLOW; SysCtrlRegs.PLLSTS.bit.MCLKOFF = 1; // 设置PLLCR 之前关闭主时钟丢失检测 SysCtrlRegs.PLLCR.bit.DIV = val; // PLLCR[DIV] = 12 EDIS; EALLOW; SysCtrlRegs.WDCR= 0x0068; EDIS; while(SysCtrlRegs.PLLSTS.bit.PLLLOCKS != 1) //当PLLCR被改写的时候,PLL会上锁。等待解锁完成。 { } EALLOW; SysCtrlRegs.PLLSTS.bit.MCLKOFF = 0; // 打开主时钟丢失检测功能 EDIS; } if((divsel == 1)||(divsel == 2)) { EALLOW; SysCtrlRegs.PLLSTS.bit.DIVSEL = divsel; // divsel = 1,SYSCLKOUT = (OSCCLK*12)/4 EDIS; } if(divsel == 3) { EALLOW; SysCtrlRegs.PLLSTS.bit.DIVSEL = 2; DELAY_US(50L); SysCtrlRegs.PLLSTS.bit.DIVSEL = 3; EDIS; } } 上面的代码都是按照手册里提供的程序流程图来写的,下面把PLLCR更改的程序流程图贴出来。
这里写图片描述
看看PLL是如何配置的,如下图,可以看到有三种配置模式,分别是不使能PLL,使能PLL,PLL旁路(当OSCCLK失效时,自动转到PLL模式)。
本例代码选择使能PLL工作模式(PLLSTS.PLLOFF = 0)。
这里写图片描述
下面是PLL的状态寄存器PLLSTS和控制寄存器PLLCR; 状态寄存器PLLSTS反映了PLL的工作状态,控制寄存器PLLCR用于设置PLL的倍频系数。
这里写图片描述
这里写图片描述
这里写图片描述
最后一步初始化外设时钟,函数是InitPeripheralClocks()
这个函数还是比较简单的,主要是启动各个外设模块的时钟,当然为了节省功耗,也可以关掉没用到的外设模块时钟,将对应的模块时钟使能位设置为0即可。
void InitPeripheralClocks(void) { EALLOW; SysCtrlRegs.LOSPCP.all = 0x0002; SysCtrlRegs.PCLKCR0.bit.ADCENCLK = 1; // ADC SysCtrlRegs.PCLKCR3.bit.COMP1ENCLK = 1; // COMP1 SysCtrlRegs.PCLKCR3.bit.COMP2ENCLK = 1; // COMP2 SysCtrlRegs.PCLKCR3.bit.COMP3ENCLK = 1; // COMP3 SysCtrlRegs.PCLKCR1.bit.ECAP1ENCLK = 1; // eCAP1 SysCtrlRegs.PCLKCR0.bit.ECANAENCLK=1; // eCAN-A SysCtrlRegs.PCLKCR1.bit.EQEP1ENCLK = 1; // eQEP1 SysCtrlRegs.PCLKCR1.bit.EPWM1ENCLK = 1; // ePWM1 SysCtrlRegs.PCLKCR1.bit.EPWM2ENCLK = 1; // ePWM2 SysCtrlRegs.PCLKCR1.bit.EPWM3ENCLK = 1; // ePWM3 SysCtrlRegs.PCLKCR1.bit.EPWM4ENCLK = 1; // ePWM4 SysCtrlRegs.PCLKCR1.bit.EPWM5ENCLK = 1; // ePWM5 SysCtrlRegs.PCLKCR1.bit.EPWM6ENCLK = 1; // ePWM6 SysCtrlRegs.PCLKCR1.bit.EPWM7ENCLK = 1; // ePWM7 SysCtrlRegs.PCLKCR0.bit.HRPWMENCLK = 1; // HRPWM SysCtrlRegs.PCLKCR0.bit.I2CAENCLK = 1; // I2C SysCtrlRegs.PCLKCR0.bit.LINAENCLK = 1; // LIN-A SysCtrlRegs.PCLKCR3.bit.CLA1ENCLK = 1; // CLA1 SysCtrlRegs.PCLKCR0.bit.SCIAENCLK = 1; // SCI-A SysCtrlRegs.PCLKCR0.bit.SPIAENCLK = 1; // SPI-A SysCtrlRegs.PCLKCR0.bit.SPIBENCLK = 1; // SPI-B SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 1; // Enable TBCLK within the ePWM EDIS; }