F28335第二篇——系统控制初始化

看门狗例程

经过我修改后的看门狗例程如下:

//两个头文件,第一个头文件与设备有关,第二个头文件与例程有关
#include "DSP2833x_Device.h"     // Headerfile Include File
#include "DSP2833x_Examples.h"   // Examples Include File

// 声明一个中断函数
interrupt void wakeint_isr(void);
void ServiceDog1(void);
void EnableDog(void);

//声明全局变量
Uint32 WakeCount; //触发看门狗的次数
Uint32 LoopCount; //循环的次数
Uint32 ServiceCount; //喂狗次数

void main(void)
{
//第一步,初始化系统控制
	InitSysCtrl();

//第二步,初始化通用IO口
// InitGpio();  // Skipped for this example  


//第三步,清除所有中断,初始化中断向量表;
	//禁止CPU中断
	DINT;
	//初始化所有的PIE控制寄存器到默认的状态;默认状态是禁止所有的PIE中断及所有标志位清除
	InitPieCtrl();
	//禁止CPU中断,清除CPU中断寄存器。
	IER = 0x0000;
	IFR = 0x0000;
	//初始化中断向量表,中断向量就是采用指针指向中断服务程序入口。
	//这个函数将会填充整个中断向量表,包括例程中没有用到的中断。
	InitPieVectTable();
	//关闭保护
	EALLOW;
	//写入看门狗中断
	PieVectTable.WAKEINT = &wakeint_isr;
	//打开保护
	EDIS;



//第四步,初始外围设备
// InitPeripherals(); // Not required for this example



//第五步,用户自定义功能代码,使能中断
	//清空计数数据
	WakeCount = 0;
	LoopCount = 0;
	ServiceCount = 0;

	//将看门狗中断信号WDENINT使能,当触发看门狗时,会触发中断
	//写入SCSR寄存器以避免清除WDOVERRIDE位
	EALLOW;
	SysCtrlRegs.SCSR =BIT1;   // 0x0002
	EDIS;
	//使能WAKEINT中断:Group 1 interrupt 8
	//使能INT1,让它连接到WAKEINT
	PieCtrlRegs.PIECTRL.bit.ENPIE = 1;   // Enable the PIE block
	PieCtrlRegs.PIEIER1.bit.INTx8 = 1;   // Enable PIE Gropu 1 INT8
	IER |= M_INT1;                       // Enable CPU int1
	EINT;									// Enable Global Interrupts

	//喂狗,复位看门狗计数器
	ServiceDog1();
	//使能看门狗
	EnableDog();

//第六步, 死循环
	for (;;)
	{
		LoopCount++;
		//通过注释决定是否喂狗
		// ServiceDog1();
	}

}

//第七步,中断服务程序
interrupt void wakeint_isr(void)
{
	WakeCount++;
	// Acknowledge this interrupt to get more from group 1
	PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;
}

//其他子程序
void EnableDog(void)
{
	EALLOW;
	SysCtrlRegs.WDCR = 0x0028;  // 使能看门狗
	EDIS;
}
void ServiceDog1(void)
{
	EALLOW;
	SysCtrlRegs.WDKEY = 0x0055;
	SysCtrlRegs.WDKEY = 0x00AA;
	EDIS;
	ServiceCount++;
}
//===========================================================================
// No more.
//===========================================================================

该例程基本上和上篇博文中上篇博客(F28335第一篇——看门狗的开断) 的例程一致。功能上只是增加一个记录喂狗次数的标量ServiceCount;

DSP开发的主要步骤

通过官方的例程,可以总结出TI推荐的开发流程大致可以分成7步:

  1. 初始化系统控制
  2. 初始化通用IO(GPIO)
  3. 清除所有的中断,初始化中断向量表
  4. 初始化外围设备
  5. 用户自定义代码
  6. 死循环
  7. 中断服务程序

当然这七个步骤并不是所有工程都全部需要,在本例程中第2步和第4步就被忽略了,下面重点介绍系统控制初始化。

初始化系统控制

这一步是系统控制初始化,调用了InitSysCtrl()函数。该函数定义在DSP2833x_SysCtrl.c文件中。源代码为:

void InitSysCtrl(void)
{
	//禁止看门狗
   DisableDog();

   //初始化PLL控制,此处确定系统的主时钟
   InitPll(DSP28_PLLCR,DSP28_DIVSEL);//InitPll(10,2);先10倍频,在2分频

   //初始化外设时钟
   InitPeripheralClocks();
}

这一步通过源代码可以发现总共实现了三个功能。

  1. 禁止看门狗
  2. 设置系统时钟频率
  3. 对需要外设时钟进行使能。在这里还设置高低速时钟频率。
1禁止看门狗
void DisableDog(void)
{
    EALLOW;
    SysCtrlRegs.WDCR= 0x0068;
    EDIS;
}

在F28335中,TI公司对一些寄存器设置了EALLOW保护。代码中的EALLOW和EDIS实质上是执行了汇编程序。他们的定义为:

#define  EALLOW asm(" EALLOW")
#define  EDIS   asm(" EDIS")

以下引用网上的解释,我没有全部验证:

F2812中有一些配置寄存器是受保护的,无法直接操作。在对这些寄存器进行修改之前,需要先去掉保护功能。而保护状态是由状态寄存器中EALLOW标志来指示的。汇编指令“EALLOW”就是将该标志位置位,允许对受保护的寄存器操作。

EALLOW(Edit allow)一般和EDIS(Edit disable)配套使用,在对受保护的寄存器操作之后,用EDIS恢复寄存器的被保护状态。

F280x器件上的几个控制寄存器受EALLOW 保护机制保护以防止虚假的CPU 写入。在复位时EALLOW位被清除以启用EALLOW 保护。在受保护时,CPU 对受保护寄存器进行的所有写入被忽略且只允许CPU读取、JTAG读取和JTAG写入。如果设置了此位,则通过执行EALLOW指令可以允许CPU自由写入受保护的寄存器。在修改寄存器之后,可以通过执行EDIS指令清除EALLOW位使它们再次受保护。

关于EALLOW保护的更多内容,可以参见(F28335第五篇——EALLOW和EDIS)

其中与看门狗相关的寄存器,可以参见上篇博客(F28335第一篇——看门狗的开断) 。

2设置系统时钟频率

InitPll函数的源程序为:

void InitPll(Uint16 val, Uint16 divsel)//本系统中val=10,divsel=2
{

   // Make sure the PLL is not running in limp mode
	//保证PLL没有工作在无序状态
   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
   //先将PLL电路设计成4分频
   if (SysCtrlRegs.PLLSTS.bit.DIVSEL != 0)
   {
       EALLOW;
       SysCtrlRegs.PLLSTS.bit.DIVSEL = 0;//保证PLL是4分频
       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;
    }
}

个人理解

此处主要涉及到PLL电路的初始化。所谓PLL电路,就是锁相环电路(Phase-Locked Loop)。电路通过比较复杂的原理可以将低频信号进行倍频与分频处理。F28335的工作最高频率为150M Hz,而一般在电路设计中,一般采用的晶振频率为30MHz。之所以不直接采用150MHz晶振,一方面是价格高昂,另一方面还要对电路做EMI处理。因此,为了让DSP能够工作在最高频率,需要对晶振信号频率进行5倍频处理。通常采用的方案是通过PLL电路先进行10倍频处理,再2分频处理

程序的大致流程为:

  1. 确保时钟工作在有序状态;
  2. 先将PLL电路设置为4分频(DIVSEL=0);
  3. 设置PLL电路的倍频数div;
    1. 关闭时钟检测电路;
    2. 写入倍频数div
    3. 关闭看门狗
    4. 等待PLL电路锁定(若是没有关闭看门狗,则此步骤需要喂狗)
    5. 开启时钟检测电路
  4. 设置分频数divsel;
    1. 若divsel为1或者2,直接设置对应的分频数
    2. 若divsel为3,则先设定为2分频,延时一段时间,在设定为1分频
相关寄存器

PLLSTS(锁相环状态寄存器)

名称 描述
15~9 保留
8~7 DIVSEL 时钟分频选择;
00,01:4分频;
10:2分频;11:1分频
6 MCLKOFF 时钟检测电路关闭位;
0:默认模式,时钟检测使能;
1:时钟检测电路关闭,系统不会进入limp-mode模式
5 OSCOOF 振荡器时钟禁止位;
0:晶振时钟信号会被送到PLL电路;
1:晶振时钟信号不会被送到PLL电路。
当此位=1时,
此时不要进入HALT或STANDBY模式,不要写入PLLCR寄存器,否则会倒追不可预知的后果;
看门狗行为与输入时钟有关,若X1或X1与X2,看门狗不工作。XCLKIN:看门狗工作。
OSCOFF用于测试时钟监视逻辑电路。
4 MCLKCLR 时钟丢失状态清除位
0:写无影响,读取返回0;
1:强制将时钟检测电路进行复位。如果OSCCLK(晶振时钟)依然丢失,时钟检测电路会再次产生一次系统复位信号,并将MCLKSTS置位,此时CPU的工作时钟为limp-mode模式产生的低速时钟
3 MALKSTS 对该位写无效,写MCLKCLR或者外部复位时候,该位被清除
0:正常模式,时钟信号没有丢失;
1:晶振信号丢失,CPU工作在limp-mode
2 PLLOFF PLL电路关闭位,只有PLLCR=0时候,才能将PLL电路关闭
0:PLL电路正常工作
1:PLL电路关闭
1 保留
0 PLLLOCKS PLL电路状态位
0:表示锁相环依然正在锁相,此时最好等待
1:PLL电路已经锁相完成

PLLCR(锁相环控制寄存器)

名称 描述
15~4 保留
3~0 DIV 0:PLL电路是旁路
其他:DIV为倍频数(最大只能是10倍频,大于10保留)
3 对需要外设时钟进行使能
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 = 1;    // 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 = 1;   // I2C
   SysCtrlRegs.PCLKCR0.bit.SCIAENCLK = 1;   // SCI-A
   SysCtrlRegs.PCLKCR0.bit.SCIBENCLK = 1;   // SCI-B
   SysCtrlRegs.PCLKCR0.bit.SCICENCLK = 1;   // SCI-C
   SysCtrlRegs.PCLKCR0.bit.SPIAENCLK = 1;   // SPI-A
   SysCtrlRegs.PCLKCR0.bit.MCBSPAENCLK = 1; // McBSP-A
   SysCtrlRegs.PCLKCR0.bit.MCBSPBENCLK = 1; // McBSP-B
   SysCtrlRegs.PCLKCR0.bit.ECANAENCLK=1;    // eCAN-A
   SysCtrlRegs.PCLKCR0.bit.ECANBENCLK=1;    // eCAN-B

   SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 0;   // Disable TBCLK within the ePWM
   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.PCLKCR0.bit.TBCLKSYNC = 1;   // Enable TBCLK within the ePWM

   SysCtrlRegs.PCLKCR1.bit.ECAP3ENCLK = 1;  // eCAP3
   SysCtrlRegs.PCLKCR1.bit.ECAP4ENCLK = 1;  // eCAP4
   SysCtrlRegs.PCLKCR1.bit.ECAP5ENCLK = 1;  // eCAP5
   SysCtrlRegs.PCLKCR1.bit.ECAP6ENCLK = 1;  // eCAP6
   SysCtrlRegs.PCLKCR1.bit.ECAP1ENCLK = 1;  // eCAP1
   SysCtrlRegs.PCLKCR1.bit.ECAP2ENCLK = 1;  // eCAP2
   SysCtrlRegs.PCLKCR1.bit.EQEP1ENCLK = 1;  // eQEP1
   SysCtrlRegs.PCLKCR1.bit.EQEP2ENCLK = 1;  // eQEP2

   SysCtrlRegs.PCLKCR3.bit.CPUTIMER0ENCLK = 1; // CPU Timer 0
   SysCtrlRegs.PCLKCR3.bit.CPUTIMER1ENCLK = 1; // CPU Timer 1
   SysCtrlRegs.PCLKCR3.bit.CPUTIMER2ENCLK = 1; // CPU Timer 2

   SysCtrlRegs.PCLKCR3.bit.DMAENCLK = 1;       // DMA Clock
   SysCtrlRegs.PCLKCR3.bit.XINTFENCLK = 1;     // XTIMCLK
   SysCtrlRegs.PCLKCR3.bit.GPIOINENCLK = 1;    // GPIO input clock

   EDIS;
}

以上代码可以大致分成4块内容:

  1. 设置高速和低速外设时钟的分频HISPCP/LOSCP
  2. 初始化外部存储器接口
  3. 初始化AD模块
  4. 初始化外设时钟控制寄存器

其中,步骤2和步骤3在以后的博客中还会详细介绍。下面重点介绍步骤1和步骤4。

高速和低速外设时钟模块分频设置

高速外设时钟(HISPCP)和低速外设时钟(LOSPCP)寄存器配置方式完全相同,如下:

名称 描述
15~3 保留
2~0 HSPCLK(高速)
LSPCLK(低速)
此三位组成的数设为N,相当于SYSCLKOUT的倍频倍数
如果N=0,不分频;若N不为0,分频数为2N;

根据上述寄存描述结合源代码:

   SysCtrlRegs.HISPCP.all = 0x0001;//
   SysCtrlRegs.LOSPCP.all = 0x0002;//Lowspeedclock=SYSCLKOUT/4=150/4=37.5MHZ;

可以发现,高速外设时钟频率是对系统频率进行2分频,即为150/2=75MHz;同理,可以求得低速外设时钟频率为37.5MHz;

初始化外部储存器

源代码为:

// XCLKOUT to SYSCLKOUT ratio.  By default XCLKOUT = 1/4 SYSCLKOUT
   // XTIMCLK = SYSCLKOUT/2
   //设定XTIMCLK的分频数为2,也就是XTIMCLK = SYSCLKOUT/2=150/2=75MHz
   XintfRegs.XINTCNF2.bit.XTIMCLK = 1;
   // XCLKOUT = XTIMCLK/2
   //设定CLKMODE的分频数为2,也就是XCLKOUT = XTIMCLK/2=37.5MHz
   XintfRegs.XINTCNF2.bit.CLKMODE = 1;
   // Enable XCLKOUT
   //打开XCLKOUT信号,使能
   XintfRegs.XINTCNF2.bit.CLKOFF = 0;

这部分代码按照本意是设定外部存储器相关的时钟,并且将时钟打开。如上面注释所示。
但是,在实际的工程中,这句话不起作用。这是因为这几个寄存器的设定条件是先打开PCLKR3寄存器。这行代码被写在下面初始话时钟控制寄存器中。所以,这三行代码无论是怎么设定,都对这个系统不产生影响。PCLK3寄存器的设定方法为:

	SysCtrlRegs.PCLKCR3.bit.XINTFENCLK = 1;     // XTIMCLK
初始化外设时钟控制寄存器

该部分源代码为:

   SysCtrlRegs.PCLKCR0.bit.I2CAENCLK = 1;   // I2C
   SysCtrlRegs.PCLKCR0.bit.SCIAENCLK = 1;   // SCI-A
   SysCtrlRegs.PCLKCR0.bit.SCIBENCLK = 1;   // SCI-B
   SysCtrlRegs.PCLKCR0.bit.SCICENCLK = 1;   // SCI-C
   SysCtrlRegs.PCLKCR0.bit.SPIAENCLK = 1;   // SPI-A
   SysCtrlRegs.PCLKCR0.bit.MCBSPAENCLK = 1; // McBSP-A
   SysCtrlRegs.PCLKCR0.bit.MCBSPBENCLK = 1; // McBSP-B
   SysCtrlRegs.PCLKCR0.bit.ECANAENCLK=1;    // eCAN-A
   SysCtrlRegs.PCLKCR0.bit.ECANBENCLK=1;    // eCAN-B

   SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 0;   // Disable TBCLK within the ePWM
   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.PCLKCR0.bit.TBCLKSYNC = 1;   // Enable TBCLK within the ePWM

   SysCtrlRegs.PCLKCR1.bit.ECAP3ENCLK = 1;  // eCAP3
   SysCtrlRegs.PCLKCR1.bit.ECAP4ENCLK = 1;  // eCAP4
   SysCtrlRegs.PCLKCR1.bit.ECAP5ENCLK = 1;  // eCAP5
   SysCtrlRegs.PCLKCR1.bit.ECAP6ENCLK = 1;  // eCAP6
   SysCtrlRegs.PCLKCR1.bit.ECAP1ENCLK = 1;  // eCAP1
   SysCtrlRegs.PCLKCR1.bit.ECAP2ENCLK = 1;  // eCAP2
   SysCtrlRegs.PCLKCR1.bit.EQEP1ENCLK = 1;  // eQEP1
   SysCtrlRegs.PCLKCR1.bit.EQEP2ENCLK = 1;  // eQEP2

   SysCtrlRegs.PCLKCR3.bit.CPUTIMER0ENCLK = 1; // CPU Timer 0
   SysCtrlRegs.PCLKCR3.bit.CPUTIMER1ENCLK = 1; // CPU Timer 1
   SysCtrlRegs.PCLKCR3.bit.CPUTIMER2ENCLK = 1; // CPU Timer 2

   SysCtrlRegs.PCLKCR3.bit.DMAENCLK = 1;       // DMA Clock
   SysCtrlRegs.PCLKCR3.bit.XINTFENCLK = 1;     // XTIMCLK
   SysCtrlRegs.PCLKCR3.bit.GPIOINENCLK = 1;    // GPIO input clock

外设时钟控制寄存器有PCKCR0、PCKCR1、PCKCR3这三个。他们每一位的含义在程序全部列出。这部分寄存器比较简单有效位置1,则该外设时钟有效,置0则表示禁止该时钟。在实际应用中为了减少功耗,可以将工程中没有用到的外设时钟关掉。例程中为了调试方便,所有时钟都打开了。

你可能感兴趣的:(#,F28335)