基于DSP2834x的程序解说

现在有一个TMDSCNCD28343的控制卡,是TI公司基于TMS320C28343这个鸡肋的MCU开发出来的控制卡,使它用起来不再那么鸡肋,也省去开发控制板的步骤。
由于我刚接触DSP,微机原理也不太记得了,完全的小白。
现在我有一个基于28343控制卡的例程,打算基于程序来分析学习MCU的功能和使用方法。
精力有限,希望能入门DSP。

芯片和控制卡概述

控制卡28343上面搭载了28343的MCU,有了控制卡,我们不用开发板子,不用搭电源电路,接几条线就能用了,控制卡对于28343最大的意义在于加了俩ADC芯片,弥补了这个MCU没有内置ADC的不足。

控制卡有什么

MCU有179个引脚,而控制卡有100个引脚,被阉割掉了很多GPIO,但是够用就行了。控制卡长这样:买控制卡附送扩展坞,什么线都接扩展坞上。
在这里插入图片描述
那么控制卡比起MCU多了什么呢?

  1. 接了晶振20MHz。
    在这里插入图片描述
  2. 接了ADC
    在这里插入图片描述
  3. 接了SOC(ADC相关,属于MCU的输出,控制卡上没有这些接口)
    在这里插入图片描述
  4. 5V,3.3V,1.2V,1.8V电源一应俱全
    在这里插入图片描述
  5. 串口相关
    在这里插入图片描述
  6. 附送512K EEPROM在这里插入图片描述

看完这些感觉并没有什么东西。

系统控制(system control)寄存器的定义SysCtrl.h

振荡器和锁相环(OSC&PLL)——理解锁相环

OSC指的是外部振荡器或者是外部晶振,PLL指的是MCU内部的锁相环模块。
控制卡已经给接了晶振,频率为20MHz。
PLL有一个状态寄存器PLLSTS。下面程序定义了该状态寄存器的内容。

struct PLLSTS_BITS   {    // bits  description
   Uint16 PLLLOCKS:1;     // 0     PLL lock status
   Uint16 rsvd1:1;        // 1     reserved
   Uint16 PLLOFF:1;       // 2     PLL off bit
   Uint16 rsvd2:1;        // 3     Missing clock status bit
   Uint16 rsvd3:1;        // 4     Missing clock clear bit
   Uint16 rsvd4:1;        // 5     Oscillator clock off
   Uint16 rsvd5:1;        // 6     Missing clock detect
   Uint16 DIVSEL:2;       // 8:7   Divide Select
   Uint16 rsvd6:7;        // 15:9  reserved
};

union PLLSTS_REG {
   Uint16              all;
   struct PLLSTS_BITS  bit;
};

它还有一个控制寄存器PLLCR:

// PLL control register bit definitions:
struct PLLCR_BITS {      // bits  description
   Uint16 DIV:5;         // 4:0   Set clock ratio for the PLL
   Uint16 rsvd1:11;      // 15:5  reserved
};

union PLLCR_REG {
   Uint16             all;
   struct PLLCR_BITS  bit;
};

这些参数都不重要,主要看功能。由下图可知OSCCLK就是外部振荡器产生的时钟,而VCOCLK就是PLL的产生的时钟。可见PLL的输入为外部振荡器时钟,根据锁相环的字面意思,就是让输出频率与输入频率相等,但锁相环还可以让输出频率是输入频率的倍数,28343的这个倍数最高为31倍。输入锁相环的5-bit multiplier,就是告诉锁相环这个倍数为多少,而5位正好对应了最高31倍。当multiplier的倍数为0时,就输出OSCCLK,否则输出锁相环时钟。之后还可以进行分频得到CLKIN,给CPU,这个CLKIN就是整个系统的时钟。
如果自己觉得晶振频率太低,想要内部有高速时钟,便可以用锁相环,提高时钟频率。
在这里插入图片描述
补充一下multiplier和分频的DIVSEL是怎么增大频率和分频的。PLLCR就是来增大频率的,PLLSTS中的DIVSEL就是来分频的。
在这里插入图片描述
在这里插入图片描述

外设时钟配置

高低速外设时钟分频寄存器

得到了CLKIN,之后还可以再进行分频操作。高速/低速外设时钟分频寄存器定义如下,虽然这两个寄存器有16位,但是高速的只用了5位,低速的只用了3位。
虽然名字上听起来高速能够提高频率,但是实际上高速和低速都是降低频率。

// High speed peripheral clock register bit definitions:
struct HISPCP_BITS  {   // bits  description
   Uint16 HSPCLK:5;     // 4:0   Rate relative to SYSCLKOUT
   Uint16 rsvd1:11;     // 15:5  reserved
};

union HISPCP_REG {
   Uint16              all;
   struct HISPCP_BITS  bit;
};

// Low speed peripheral clock register bit definitions:
struct LOSPCP_BITS  {   // bits  description
   Uint16 LSPCLK:3;     // 2:0   Rate relative to SYSCLKOUT
   Uint16 rsvd1:13;     // 15:3  reserved
};

union LOSPCP_REG {
   Uint16              all;
   struct LOSPCP_BITS  bit;
};

28335是如下两图这样降低频率的,但是28343的高速有5位有用的,所以分频的情况更多一些,应该差不多。目前28343的分频情况在datasheet上还没有找到,等我找到了再贴上来。
28335高速降频方法
28335低速降频方法

外设时钟控制寄存器

这个东西是来控制时钟信号SYSCLKOUT(和CLKIN相等,但不是一个东西,这不重要)传给外设的,比如EPWM, ADC等等这些东西。程序中都有备注,都是这种东西,没有什么难理解的。可见28343有4个这样的寄存器,但每个寄存器的内容不一样的,搞这么多是因为有很多外设,要一个个控制。

// Peripheral clock control register 0 bit definitions:
struct PCLKCR0_BITS  {   // bits  description
   Uint16 rsvd1:2;       // 1:0   reserved
   Uint16 TBCLKSYNC:1;   // 2     EWPM Module TBCLK enable/sync
   Uint16 rsvd2:1;       // 3     reserved
   Uint16 I2CAENCLK:1;   // 4     Enable SYSCLKOUT to I2C-A
   Uint16 SCICENCLK:1;   // 5     Enalbe low speed clk to SCI-C
   Uint16 rsvd3:1;       // 6     reserved
   Uint16 SPIDENCLK:1;   // 7     Enable low speed clk to SPI-D
   Uint16 SPIAENCLK:1;   // 8     Enable low speed clk to SPI-A
   Uint16 rsvd4:1;       // 9     reserved
   Uint16 SCIAENCLK:1;   // 10    Enable low speed clk to SCI-A
   Uint16 SCIBENCLK:1;   // 11    Enable low speed clk to SCI-B
   Uint16 MCBSPAENCLK:1; // 12    Enable low speed clk to McBSP-A
   Uint16 MCBSPBENCLK:1; // 13    Enable low speed clk to McBSP-B
   Uint16 ECANAENCLK:1;  // 14    Enable system clk to eCAN-A
   Uint16 ECANBENCLK:1;  // 15    Enable system clk to eCAN-B
};

union PCLKCR0_REG {
   Uint16              all;
   struct PCLKCR0_BITS bit;
};

// Peripheral clock control register 1 bit definitions:
struct PCLKCR1_BITS  {    // bits  description
   Uint16 EPWM1ENCLK:1;   // 0     Enable SYSCLKOUT to EPWM1
   Uint16 EPWM2ENCLK:1;   // 1     Enable SYSCLKOUT to EPWM2
   Uint16 EPWM3ENCLK:1;   // 2     Enable SYSCLKOUT to EPWM3
   Uint16 EPWM4ENCLK:1;   // 3     Enable SYSCLKOUT to EPWM4
   Uint16 EPWM5ENCLK:1;   // 4     Enable SYSCLKOUT to EPWM5
   Uint16 EPWM6ENCLK:1;   // 5     Enable SYSCLKOUT to EPWM6
   Uint16 EPWM7ENCLK:1;   // 6     Enable SYSCLKOUT to EPWM7
   Uint16 EPWM8ENCLK:1;   // 7     Enable SYSCLKOUT to EPWM8
   Uint16 ECAP1ENCLK:1;   // 8     Enable SYSCLKOUT to ECAP1
   Uint16 ECAP2ENCLK:1;   // 9     Enable SYSCLKOUT to ECAP2
   Uint16 ECAP3ENCLK:1;   // 10    Enable SYSCLKOUT to ECAP3
   Uint16 ECAP4ENCLK:1;   // 11    Enable SYSCLKOUT to ECAP4
   Uint16 ECAP5ENCLK:1;   // 12    Enable SYSCLKOUT to ECAP5
   Uint16 ECAP6ENCLK:1;   // 13    Enable SYSCLKOUT to ECAP6
   Uint16 EQEP1ENCLK:1;   // 14    Enable SYSCLKOUT to EQEP1
   Uint16 EQEP2ENCLK:1;   // 15    Enable SYSCLKOUT to EQEP2
};

union PCLKCR1_REG {
   Uint16              all;
   struct PCLKCR1_BITS bit;
};

// Peripheral clock control register 2 bit definitions:
struct PCLKCR2_BITS  {    // bits  description
   Uint16 EPWM9ENCLK:1;   // 0     Enable SYSCLKOUT to EPWM9
   Uint16 rsvd1:7;        // 7:1   reserved
   Uint16 EQEP3ENCLK:1;   // 8     Enable SYSCLKOUT to ECAP1
   Uint16 rsvd2:7;        // 15:9  reserved
};

union PCLKCR2_REG {
   Uint16              all;
   struct PCLKCR2_BITS bit;
};

// Peripheral clock control register 3 bit definitions:
struct PCLKCR3_BITS  {        // bits  description
   Uint16 rsvd1:8;            // 7:0   reserved
   Uint16 CPUTIMER0ENCLK:1;   // 8     Enable SYSCLKOUT to CPU-Timer 0
   Uint16 CPUTIMER1ENCLK:1;   // 9     Enable SYSCLKOUT to CPU-Timer 1
   Uint16 CPUTIMER2ENCLK:1;   // 10    Enable SYSCLKOUT to CPU-Timer 2
   Uint16 DMAENCLK:1;         // 11    Enable the DMA clock
   Uint16 XINTFENCLK:1;       // 12    Enable SYSCLKOUT to XINTF
   Uint16 GPIOINENCLK:1;      // 13    Enable GPIO input clock
   Uint16 rsvd2:2;            // 15:14 reserved
};

union PCLKCR3_REG {
   Uint16              all;
   struct PCLKCR3_BITS bit;
};

低功率模式

顾名思义,这东西的作用就是适当时候关闭CPU,减小功耗,微机原理课好像讲过。下面是寄存器LPMCR0的定义。

struct LPMCR0_BITS {     // bits  description
   Uint16 LPM:2;         // 1:0   Set the low power mode
   Uint16 QUALSTDBY:6;   // 7:2   Qualification
   Uint16 rsvd1:7;       // 14:8  reserved
   Uint16 WDINTE:1;      // 15    Enables WD to wake the device from STANDBY
};

union LPMCR0_REG {
   Uint16              all;
   struct LPMCR0_BITS  bit;
};

低功耗模式共有3个模式,控制模式的方法就是设置LPMCR0的第一位和第二位,也就是上面程序中的Uint16 LPM,如下图:
在这里插入图片描述

  1. 空闲模式(IDLE),低功耗模式不起作用,LPM两位的值为00,看门狗中断和任何使能中断都可以退出该模式。
  2. 待命模式(STANDBY),LPM两位为01,CLKIN关闭,但是外部振荡器和PLL,看门狗还在工作。
  3. 暂停模式(HALT),LPM两位为1X,振荡器,PLL关闭,看门狗不工作,CLKIN关闭。

ADC的SOC

这个部分与SOC有关。外部ADC接口操作被EXTSOCCFG寄存器配置,控制,监控,寄存器定义如下:

struct EXTSOCCFG_BITS {        // bits  description
    Uint16 EXTSOC1APOLSEL:1;   // 0     External ADC SOCA Group 1 polarity select
    Uint16 EXTSOC1AEN:1;       // 1     External ADC SOCA Group 1 enable
    Uint16 EXTSOC1BPOLSEL:1;   // 2     External ADC SOCB Group 1 polarity select
    Uint16 EXTSOC1BEN:1;       // 3     External ADC SOCB Group 1 enable
    Uint16 EXTSOC2APOLSEL:1;   // 4     External ADC SOCA Group 2 polarity select
    Uint16 EXTSOC2AEN:1;       // 5     External ADC SOCA Group 2 enable
    Uint16 EXTSOC2BPOLSEL:1;   // 6     External ADC SOCB Group 2 polarity select
    Uint16 EXTSOC2BEN:1;       // 7     External ADC SOCB Group 2 enable
    Uint16 EXTSOC3APOLSEL:1;   // 8     External ADC SOCA Group 3 polarity select
    Uint16 EXTSOC3AEN:1;       // 9     External ADC SOCA Group 3 enable
    Uint16 EXTSOC3BPOLSEL:1;   // 10    External ADC SOCB Group 3 polarity select
    Uint16 EXTSOC3BEN:1;       // 11    External ADC SOCB Group 3 enable
    Uint16 rsvd1:4;            // 15:12  reserved
};

union EXTSOCCFG_REG {
	Uint16                 all;
	struct EXTSOCCFG_BITS  bit;
};

在这里插入图片描述
28343一共有6个接到外面的SOC信号。28343MCU没有内置的ADC,但它能输出ADC的转换开始信号(SOC信号start of conversion),所以控制卡给它找了俩ADC芯片ADS7865,一个是A一个是B。这每个芯片里面又有两个ADC,每个ADC 3个通道,所以控制卡一共有12个通道。

这里我也没有理解完全。我只说一下我现在得到的信息。如下图,这是控制卡上一个ADS7865的接线原理图,3个SOC信号输入到ADC中,前面已经说过,一个芯片里有两个ADC,最多6个伪差分通道。端口CONVST就是SOC信号,当它为低电平时,采样完成,转换开始。3个SOC信号经过的芯片SN74LVC1G332是或门,也就是只有这仨全是低电平的时候,CONVST信号才能变成低电平。
在这里插入图片描述

看门狗

看门狗电路和单片机的一个I/O引脚相连,该引脚可以向看门狗电路定时送入表示自己还正常工作的信号,这就是喂狗。一旦单片机跑飞了,喂狗的程序就不能执行了,狗便会生气,让单片机复位(reset),单片机从头开始跑,继续喂狗。下图是28343的看门狗架构。
在这里插入图片描述
WDCNTR是一个寄存器,8位,可以进行加法计数,计数达到最大值时便会产生一个脉冲宽度位512个OSCCLK的脉冲,因为它的时钟是OSCCLK经过了512分频。要么禁止看门狗,要么周期性地向WDKEY(8位)这个寄存器写入0x55+0xAA,WDKEY会指导WDCNTR复位。喂了看门狗复位,不喂整个系统复位。
最后输出的WDRST就是重置信号,低电平让系统重置。
WDINT与低功耗模式有关,可以退出IDLE和STANDBY模式。而HALT的时候,看门狗已经不工作了,就没用了。
通过SCSR寄存器可以配置看门狗是复位还是中断

  1. 复位模式:
    当计数器溢出的时候,WDRST信号拉低系统复位引脚(XRTS)512个个时钟周期
  2. 中断模式
    WDINT信号被拉低512个时钟周期,触发PIE中的中断WAKEINT,看门狗中断为WDINT的下降沿触发,所以当WAKEINT中断在WDINT信号起来之前是关着的,那么中断就不会立刻触发,留到下次计数器溢出。
    如果在WDINT还在低电平的时候将看门狗重新从中断状态配置回复位状态,则系统立刻复位,SCSR里面有一位可以改WDINT的电平,防止从中断状态切换到复位状态后,系统立刻复位。

用于系统控制的寄存器

有些前面简单讲过,上面的部分都讲过,下面有个SCSR,还有俩WD开头似乎与看门狗有关的就没有定义过了。

struct SYS_CTRL_REGS {
   Uint16                rsvd1;     // 0
   union   PLLSTS_REG    PLLSTS;    // 1
   Uint16                rsvd2[7];  // 2-8
   union   PCLKCR2_REG   PCLKCR2;   // 9 : Peripheral clock control register
   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                rsvd3;     // 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[3];  // 26-28
   union   EXTSOCCFG_REG EXTSOCCFG; // 29: External ADC polarity select register
   Uint16                rsvd7;     // 30
   Uint16                rsvd8;     // 31: Reserved
};

CSM寄存器

CSM是(code security module代码安全模块)。这不重要。

系统控制的初始化函数

Examples.h文件

这个文件和系统时钟有关,设定了系统时钟为多少,与前面讲的PLL&OSC部分相关,不过只需要记住PLLCR是乘,DIVSEL是除就可以了。
看该头文件的定义:
#define DSP28_DIVSEL 2 // 这句话是要除以2
#define DSP28_PLLCR 29 // 这句话是要乘30
之前说了外接晶振频率20MHz,乘30再除以2最后出来的CLKIN就是300MHz。
其他都不重要。

系统初始化SysCtrl.c文件

先说一下EALLOW,很多函数的开头都会有。EALLOW通常与EDIS搭配使用,很多寄存器都受到保护,不能往里面随便写,所以要对寄存器进行更改前,应该写一个EALLOW去掉保护,更改完了,再写一个EDIS保护起来。
使用示例:

void ServiceDog(void)
{
    EALLOW;
    SysCtrlRegs.WDKEY = 0x0055;
    SysCtrlRegs.WDKEY = 0x00AA;
    EDIS;
}
void DisableDog(void)
{
    EALLOW;
    SysCtrlRegs.WDCR= 0x0068;
    EDIS;
}

这是喂狗和关闭狗的程序。首先看喂狗程序,前面说过,往WDKEY里定期写55,AA就是喂狗,很明显。然后是关闭狗程序,让WDCR为0068。WDCR是看门狗的控制寄存器。
WDCR的15~8位是没用的,有用的是7至0位。

  • 第7位:WDFLAG,一般为0,不重要
  • 第6位:WDDIS,为1是关闭看门狗,为0时使能看门狗,这个很重要。
  • 第5~3位,WDCHK,给看门狗的暗号,为101时看门狗才会信你,为其他值时看门狗立刻让系统复位。
  • 第2~0位,WDPS,看门狗时钟的分频,如果为000,看门狗时钟就是OSCCLK/512。不重要,一般都是000。

因此如果要关闭看门狗,那么7~0位是0110 1000,也就是68,如程序里面的设定。

系统初始化的主程序:

void InitSysCtrl(void)
{

   // Disable the watchdog
   DisableDog();

   // Initialize the PLL control: PLLCR and DIVSEL
   // DSP28_PLLCR and DSP28_DIVSEL are defined in DSP2834x_Examples.h
   InitPll(DSP28_PLLCR,DSP28_DIVSEL);

   // Initialize the peripheral clocks
   InitPeripheralClocks();
}

首先关闭看门狗,然后初始化PLL,初始化外设时钟。
下面看一下这俩初始化的程序。首先是PLL初始化的:

void InitPll(Uint16 val, Uint16 divsel)
{
   if (SysCtrlRegs.PLLSTS.bit.DIVSEL != 0)
   {
       EALLOW;
       SysCtrlRegs.PLLSTS.bit.DIVSEL = 0;
       EDIS;
   }

   //给乘的PLLCR赋值
   if (SysCtrlRegs.PLLCR.bit.DIV != val)
   {

      EALLOW;
      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();
      }

     }

    // If switching to 1/4
    if (divsel == 1)
    {
		EALLOW;
		SysCtrlRegs.PLLSTS.bit.DIVSEL = divsel;
		EDIS;
	}


    // If switching to 1/2
    // 先到1/4的频率让稳定,然后再到1/2,这里的等待时间是50us* First go to 1/4 and let the power settle
    //   The time required will depend on the system, this is only an example
	// * Then switch to 1/2
	if(divsel == 2)
	{
		EALLOW;
	    SysCtrlRegs.PLLSTS.bit.DIVSEL = 1;
	    DELAY_US(50L);
	    SysCtrlRegs.PLLSTS.bit.DIVSEL = 2;
	    EDIS;
	}

	// If switching to 1/1
	// NOTE: ONLY USE THIS SETTING IF PLL IS BYPASSED (I.E. PLLCR = 0)
	// * First go to 1/4 and let the power settle then 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 = 1;
	    DELAY_US(50L);
	    SysCtrlRegs.PLLSTS.bit.DIVSEL = 2;
	    DELAY_US(50L);
	    SysCtrlRegs.PLLSTS.bit.DIVSEL = 3;
	    EDIS;
    }
}

参数里面val是乘的,divsel是除的。然后先给PLLCR赋值,直接赋值就行了,就是29。然后再给DIVSEL赋值,这个还要磨蹭,如果想要分频到1/4,直接赋值就行了;如果想要分频到1/2,就要先分频到1/4,然后等一会,再分到1/2;如果要1/1,就要先1/4,再1/2,再1/1。说是为了等待稳定下来,确实很有道理。程序里是想分到1/2。传入的参数是29和2。
然后是外设时钟的初始化:

void InitPeripheralClocks(void)
{
   EALLOW;

// HISPCP/LOSPCP prescale register settings, normally it will be set to default values
   SysCtrlRegs.HISPCP.all = 0x001F;
   SysCtrlRegs.LOSPCP.all = 0x0002;

// XCLKOUT to SYSCLKOUT ratio.  By default XCLKOUT = 1/8 SYSCLKOUT
   // XTIMCLK = SYSCLKOUT/2
   XintfRegs.XINTCNF2.bit.XTIMCLK = 1;
   // XCLKOUT = XTIMCLK/2
   XintfRegs.XINTCNF2.bit.CLKMODE = 1;
  // XCLKOUT = XTIMCLK/4
   XintfRegs.XINTCNF2.bit.BY4CLKMODE = 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 2834x derivates.
// Refer to the datasheet for your particular device.
//
// This function is not written to be an example of efficient code.


   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.SPIDENCLK = 1;   // SPI-D
   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.PCLKCR1.bit.EPWM7ENCLK = 1;  // ePWM7
   SysCtrlRegs.PCLKCR1.bit.EPWM8ENCLK = 1;  // ePWM8
   SysCtrlRegs.PCLKCR2.bit.EPWM9ENCLK = 1;  // ePWM9
   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.PCLKCR2.bit.EQEP3ENCLK = 1;  // eQEP3

   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;
}

应该是配置一下是否开启外设,给外设时钟信号。除此之外,程序的上部有一些时钟信号,首先SYSCLKOUT就是300MHz的,XCLKOUT是SYSCLKOUT分频之后的,一般SYSCLKOUT会先二分频,变成XTIMCLK,再2分频或者4分频变成XCLKOUT,XCLKOUT也是MCU的一个引脚。程序里是8分频。
一般外设的时钟是SYSCLKOUT,对于这个控制卡,由于ADC是外接的,所以这东西的时钟要专门从MCU上找个引脚接入,XCLKOUT控制了外接ADC的时钟。
在这里插入图片描述
目前系统控制初始化已结束,感觉这部分主要就是时钟,怎么分频,怎么设置寄存器,还有一个看门狗。

EPWMGPIO

概述

MCU可以通过GPIO口输出PWM信号。前面的系统控制初始化程序里面根本没有管PWM端口,肯定还要搞一下PWM。
首先是初始化函数:

InitEPwm1Gpio();
   InitEPwm2Gpio();
   InitEPwm3Gpio();
   InitEPwm4Gpio();
   InitEPwm5Gpio();
   InitEPwm6Gpio();

可见要使用12个GPIO口做PWM。
所以为什么是12个呢?28343有9个PWM模块,只用了6个PWM模块,每个模块有两路输出,所以一共用了12个输出PWM的口,这12个口也全部通过控制卡输出了。如下图,1A和1B是一个PWM模块,一般1A,1B是互补的信号,控制半桥全桥。上面程序初始化的是6个PWM模块。
在这里插入图片描述
整个程序的目的是为了输出两边的三相PWM信号,每相两路,互补输出。PWM模块1,3,5是一边的三相,模块2,4,6是另一边的三相。
控制什么电路要搞两边三相?LLC谐振DC/DC。
PWM功能也是通过一堆寄存器实现的,需要3个寄存器。

  1. 周期寄存器TPR,决定了一个周期计数的最大值,也就是PWM周期。
  2. 计数器寄存器TCNT,比如在增减计数模式,TCNT开始从0开始加,加到了TPR的值之后,开始减,减到0,开始一轮新的周期。
  3. 比较寄存器CMPR,TCNT增减的过程中,如果发现等于CMPR,就让PWM变化电平。
    虽然看起来非常简单,但是PWM模块里面有7个子模块,这7个子模块又有一堆寄存器,不是上面这3个寄存器能够说完的。

PWM模块的子模块

时间基准子模块(TB)

该模块一个作用是计数,一个作用是让本PWM模块产生的信号与其他PWM模块产生的信号同步。

计数时钟

计数的时候需要有时钟,这个时钟叫TBCLK,A
在这里插入图片描述
在这里插入图片描述

PWM周期和模式

三种计数模式:
在这里插入图片描述
这里涉及了两个寄存器TBCTR计数寄存器,TBPRD周期寄存器。上图的时候TBPRD是4,然后TBCTR在增大或减小或增减,算一算就可以得到PWM周期了,这个问题很简单。具体哪个模式由TBCTL寄存器决定。

映射寄存器

在这里插入图片描述
看上图是与时间基准模块TB有关的寄存器,TBCTL,TBCTR,TBPRD都知道是什么了,有一列是shadowed,叫有没有映射寄存器,只有TBPRD有。意思就是TBPRD有两个,一个是本身,一个是映射的。
当TBCTL的PRDLD位=0时,TBPRD使用映射模式,此时如果写入TBPRD会先写进映射寄存器中,等到TBCTR(计数器)=0时,再把映射寄存器里的内容写进本体中。
当PRDLD=1时,写的时候直接写进本体中,不等了。
一般都要启用映射寄存器。

同步和相位问题

除了几个PWM模块同步,可能有的时候还要搞相位差,比如三相肯定要做相位差。
以PWM1为基准,SYNCI是同步信号输入,SYNCO是同步信号输出,PWM1从SYNCI接收外来的同步信号,再把同步信号通过SYNCO输出给其它PWM模块。
SYNCI的输入引脚:GPIO6和GPIO32都是输入同步信号。
在这里插入图片描述

在这里插入图片描述
TBCTL中有一位PHSEN,可以实现相位控制,当它为1时,说明要施加相位。而相位的多少由TBPHS寄存器来决定。当SYNCI输入时,TBPHS中的值在下一个TBCLK上升沿被装载到计数器TBCTR中,也就是让TBCTR从这个数开始加或减,形成相位差。如果TBCLK=SYSCLKOUT,装载过程有两个TBCLK的延时,如果不相等,只有一个TBCLK的延时。
如果没有SYNCI的输入,有软件强制同步脉冲也行,就是自己产生的脉冲,向TBCTL的SWFSYNC写1,可以产生一个强制同步脉冲。
看来TBCTL这个控制寄存器很重要,贴一下它的描述,推荐一下C2000助手这个软件,虽然有的芯片种类很少,但TI的C2000芯片原理性的方面都差不多,内容大概是从TI官网的用户指南上扒的,但比每个模块分开的用户指南要好看很多。
PHSDIR可以决定控制相位改变计数器值后的是增还是减。这些参数大部分都说过,很好懂。
在这里插入图片描述
在这里插入图片描述

比较功能子模块(CMP)

比较寄存器有两个,CMPA和CMPB.\。
比较的控制寄存器是CMPCTL。这里直接贴出它的内容。大多都与映射寄存器有关,CMPA,CMPB都是有映射寄存器的。是否启用映射由SHDWxMODE决定,LODExMODE是什么时候从映射寄存器中加载数据。
在这里插入图片描述

然后是一个示例,下面是增模式时的情况,这种东西很好理解。TBPHS是相位差,当同步信号STNCI脉冲来的时候,计数器打回TBPHS(相位)的值。
在这里插入图片描述

动作限定子模块(AQ)

就是控制输出PWM信号电平怎么变的模块。计数器发生的事件可以送到AQ进行处理,有以下事件:

  1. 计数器=PRD
  2. 计数器=0
  3. 计数器=CMPA,正在减计数
  4. 计数器=CMPA,正在增计数
  5. 计数器=CMPB,正在减计数
  6. 计数器=CMPB,正在增计数
    有两个控制计数器发生这些事件PWM信号反应的寄存器。下面是其中一个AQCTLA,因为有两路,一个寄存器控制一路。当计数器记到最大值和0的时候都可以翻转。
    在这里插入图片描述
    在这里插入图片描述
    示例:没什么。
    在这里插入图片描述

死区控制器模块(DB)

死区控制寄存器DBCTL
在这里插入图片描述
对于全桥电路一种流行的死区设置方案:

在这里插入图片描述
延时上升沿的寄存器是DCRED,下降沿的是DBFED,以TBCLK为单位。

斩波控制子模块(PC)

展示一下效果就行了,不用的时候把寄存器PCCTL的CHPEN位置0。
在这里插入图片描述

故障捕获子模块(TZ)

如果外部电路有故障了,TZ控制所有PWM输出为低电平。与外部电路的接口:GPIO12和13。
在这里插入图片描述

事件触发子模块(ET)

用来处理计数器,比较模块产生的各种事件,向CPU发出中断请求或者产生ADC启动信号SOC。下面是SOC的解释。每三个PWM模块是一组,每个PWM模块两个通道,把这三个PWM模块的三个SOCA给拉出来,经过或门,再反相或不反向,传出去,一共六路SOC。
在这里插入图片描述
控制卡的这六路SOC的接法,A,B经过或门分别给两个芯片。
在这里插入图片描述
下面是整个ET模块的流程图。说明计数器的这些事件会让ET模块开始处理。就是计数器为0,为最大值,增模式=CMPA。。。。等等。出现了这些事件,可能会让ET发出中断信号或启动ADC转换信号。
不过让PWM模块来控制中断和ADC是什么意思???
在这里插入图片描述

中断

PIE是与中断有关的,后面可能会说到。
简单来说就是通过寄存器设定上面这6个事件哪些可能产生中断,每判断一次可能产生中断的时间计数器加1,还要设定计数器达到多少ET发出中断信号。
与中断有关的ETSEL部分:
在这里插入图片描述
与中断有关的ETPS部分:
在这里插入图片描述

ADC

和中断的逻辑一样,贴一下寄存器:
ETSEL:
在这里插入图片描述
ETPS:
在这里插入图片描述
至此PWM部分介绍完成。在最后,贴上PWM部分的所有模块连接图,就会发现以前看不懂的框图现在全都看懂了。
在这里插入图片描述

回到程序

与PWM有关的在EPWM.c文件中。该文件中主要是9个PWM模块的初始化,同步脉冲(SYNCI,SYNCO)的初始化,故障捕获的TZ接口初始化。

9个PWM模块的初始化

找一个看一下就行了。

void InitEPwm1Gpio(void)
{
   EALLOW;

/* Disable internal pull-up for the selected output pins
   to reduce power consumption */
// Pull-ups can be enabled or disabled by the user.
// Comment out other unwanted lines.

    GpioCtrlRegs.GPAPUD.bit.GPIO0 = 1;    // Disable pull-up on GPIO0 (EPWM1A)
    GpioCtrlRegs.GPAPUD.bit.GPIO1 = 1;    // Disable pull-up on GPIO1 (EPWM1B)

/* Configure ePWM-1 pins using GPIO regs*/
// This specifies which of the possible GPIO pins will be ePWM1 functional pins.
// Comment out other unwanted lines.

    GpioCtrlRegs.GPAMUX1.bit.GPIO0 = 1;   // Configure GPIO0 as EPWM1A
    GpioCtrlRegs.GPAMUX1.bit.GPIO1 = 1;   // Configure GPIO1 as EPWM1B

    EDIS;
}

因为PWM输出信号的接口是GPIO,上面的程序首先取消对应GPIO口的上拉,再把对应GPIO口配置为PWM口。

同步脉冲初始化

把GPIO6作为同步输入脉冲的口,同步信号就是SYSCLKOUT,原版本的注释感觉不对,我自己一个个来说。

// This function initializes GPIO pins to function as ePWM Synch pins
//

void InitEPwmSyncGpio(void)
{

   EALLOW;

/* Configure EPWMSYNCI  */

/* Enable internal pull-up for the selected pins */
// Pull-ups can be enabled or disabled by the user.
// This will enable the pullups for the specified pins.
// Comment out other unwanted lines.

   GpioCtrlRegs.GPAPUD.bit.GPIO6 = 0;    // Enable pull-up on GPIO6 (EPWMSYNCI)
// GpioCtrlRegs.GPBPUD.bit.GPIO32 = 0;   // Enable pull-up on GPIO32 (EPWMSYNCI)

/* Set qualification for selected pins to asynch only */
// This will select synch to SYSCLKOUT for the selected pins.
// Comment out other unwanted lines.

   GpioCtrlRegs.GPAQSEL1.bit.GPIO6 = 0;   // Synch to SYSCLKOUT GPIO6 (EPWMSYNCI)
// GpioCtrlRegs.GPBQSEL1.bit.GPIO32 = 0;  // Synch to SYSCLKOUT GPIO32 (EPWMSYNCI)

/* Configure EPwmSync pins using GPIO regs*/
// This specifies which of the possible GPIO pins will be EPwmSync functional pins.
// Comment out other unwanted lines.

   GpioCtrlRegs.GPAMUX1.bit.GPIO6 = 2;    // Enable pull-up on GPIO6 (EPWMSYNCI)
// GpioCtrlRegs.GPBMUX1.bit.GPIO32 = 2;   // Enable pull-up on GPIO32 (EPWMSYNCI)



/* Configure EPWMSYNC0  */

/* Disable internal pull-up for the selected output pins
   to reduce power consumption */
// Pull-ups can be enabled or disabled by the user.
// Comment out other unwanted lines.

// GpioCtrlRegs.GPAPUD.bit.GPIO6 = 1;    // Disable pull-up on GPIO6 (EPWMSYNC0)
   GpioCtrlRegs.GPBPUD.bit.GPIO33 = 1;   // Disable pull-up on GPIO33 (EPWMSYNC0)

// GpioCtrlRegs.GPAMUX1.bit.GPIO6 = 3;    // Enable pull-up on GPIO6 (EPWMSYNC0)
   GpioCtrlRegs.GPBMUX1.bit.GPIO33 = 2;   // Enable pull-up on GPIO33 (EPWMSYNC0)

}

这里面涉及GPIO引脚设定的部分,本来GPIO引脚设定的部分很容易理解,现在先简单说一下。另外,28343的东西与贴上来的MCU的东西可能有些不一样,不能绝对照着它来,但意思对了就行了。
GpioCtrlRegs.GPAPUD.bit.GPIO6 = 0;
使能GPIO6内部上拉。
在这里插入图片描述
GpioCtrlRegs.GPAQSEL1.bit.GPIO6 = 0;设定GPIO6输入与系统时钟SYSCLKOUT同步。
在这里插入图片描述
GpioCtrlRegs.GPAMUX1.bit.GPIO6 = 2;
把GPIO口6配置为SYNCI口。
在这里插入图片描述
我觉得这里面有问题,GPIO6被配置为SYNCI,那么就不是EPWM4A口了,但是这个程序里面恰恰使用了EPWM1,2,3,4,5,6这六个模块,它们对应的GPIO口还要输出PWM波形来控制全桥。
就算把GPIO32,33拿来做SYNCI和SYNCO,这也不对,因为控制卡里面的512K ROM就是通过GPIO32,33连接的(看最前面的图)。
除非GPIO6被复用为SYNCI或者EPWM输出口都行,这俩属性可以叠加?应该不行吧?

TZ初始化

GPIO12,13,14,15,16,17就是6个TZ口(或者28,29也可以替代16,17),这里配置了这6个。

void InitTzGpio(void)
{
   EALLOW;

/* Enable internal pull-up for the selected pins */
// Pull-ups can be enabled or disabled by the user.
// This will enable the pullups for the specified pins.
// Comment out other unwanted lines.
   GpioCtrlRegs.GPAPUD.bit.GPIO12 = 0;    // Enable pull-up on GPIO12 (TZ1)
   GpioCtrlRegs.GPAPUD.bit.GPIO13 = 0;    // Enable pull-up on GPIO13 (TZ2)
   GpioCtrlRegs.GPAPUD.bit.GPIO14 = 0;    // Enable pull-up on GPIO14 (TZ3)
   GpioCtrlRegs.GPAPUD.bit.GPIO15 = 0;    // Enable pull-up on GPIO15 (TZ4)

   GpioCtrlRegs.GPAPUD.bit.GPIO16 = 0;    // Enable pull-up on GPIO16 (TZ5)
// GpioCtrlRegs.GPAPUD.bit.GPIO28 = 0;    // Enable pull-up on GPIO28 (TZ5)

   GpioCtrlRegs.GPAPUD.bit.GPIO17 = 0;    // Enable pull-up on GPIO17 (TZ6)
// GpioCtrlRegs.GPAPUD.bit.GPIO29 = 0;    // Enable pull-up on GPIO29 (TZ6)

/* Set qualification for selected pins to asynch only */
// Inputs are synchronized to SYSCLKOUT by default.
// This will select asynch (no qualification) for the selected pins.
// Comment out other unwanted lines.

   GpioCtrlRegs.GPAQSEL1.bit.GPIO12 = 3;  // Asynch input GPIO12 (TZ1)
   GpioCtrlRegs.GPAQSEL1.bit.GPIO13 = 3;  // Asynch input GPIO13 (TZ2)
   GpioCtrlRegs.GPAQSEL1.bit.GPIO14 = 3;  // Asynch input GPIO14 (TZ3)
   GpioCtrlRegs.GPAQSEL1.bit.GPIO15 = 3;  // Asynch input GPIO15 (TZ4)

   GpioCtrlRegs.GPAQSEL2.bit.GPIO16 = 3;  // Asynch input GPIO16 (TZ5)
// GpioCtrlRegs.GPAQSEL2.bit.GPIO28 = 3;  // Asynch input GPIO28 (TZ5)

   GpioCtrlRegs.GPAQSEL2.bit.GPIO17 = 3;  // Asynch input GPIO17 (TZ6)
// GpioCtrlRegs.GPAQSEL2.bit.GPIO29 = 3;  // Asynch input GPIO29 (TZ6)


/* Configure TZ pins using GPIO regs*/
// This specifies which of the possible GPIO pins will be TZ functional pins.
// Comment out other unwanted lines.
   GpioCtrlRegs.GPAMUX1.bit.GPIO12 = 1;  // Configure GPIO12 as TZ1
   GpioCtrlRegs.GPAMUX1.bit.GPIO13 = 1;  // Configure GPIO13 as TZ2
   GpioCtrlRegs.GPAMUX1.bit.GPIO14 = 1;  // Configure GPIO14 as TZ3
   GpioCtrlRegs.GPAMUX1.bit.GPIO15 = 1;  // Configure GPIO15 as TZ4

   GpioCtrlRegs.GPAMUX2.bit.GPIO16 = 3;  // Configure GPIO16 as TZ5
// GpioCtrlRegs.GPAMUX2.bit.GPIO28 = 3;  // Configure GPIO28 as TZ5

   GpioCtrlRegs.GPAMUX2.bit.GPIO17 = 3;  // Configure GPIO17 as TZ6
// GpioCtrlRegs.GPAMUX2.bit.GPIO29 = 3;  // Configure GPIO29 as TZ6

   EDIS;
}

GpioCtrlRegs.GPAQSEL1.bit.GPIO12 = 3;
输入没限制,与SYSCLKOUT不同步也行。

再说一下DINT和EINT,禁止全局中断和使能全局中断,一般初始化的时候会DINT一下。

PWM介绍部分结束。

后续重要程序

本部分将围绕例程的如何使用PWM模块输出两个三相控制全桥的PWM波形展开。

void InitEPwmTimer()
{
   EALLOW;
   SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 0;      // Stop all the TB clocks
   EDIS;

   // Setup Sync
   EPwm1Regs.TBCTL.bit.PHSEN = TB_DISABLE;				// 禁止PWM1的计数器加载相位
   EPwm1Regs.TBCTL.bit.SYNCOSEL = TB_CTR_ZERO;			// PWM1,当计数器为0时,输出同步脉冲SYNCO
   EPwm2Regs.TBCTL.bit.SYNCOSEL = TB_SYNC_IN;  // 其它PWM模块的SYNCO输出与输入一样
   EPwm3Regs.TBCTL.bit.SYNCOSEL = TB_SYNC_IN;  // Pass through
   EPwm4Regs.TBCTL.bit.SYNCOSEL = TB_SYNC_IN;  // Pass through
   EPwm5Regs.TBCTL.bit.SYNCOSEL = TB_SYNC_IN;  // Pass through
   EPwm6Regs.TBCTL.bit.SYNCOSEL = TB_SYNC_IN;  // Pass through

   // Allow each timer to be sync'ed
   EPwm1Regs.TBCTL.bit.PHSEN = TB_ENABLE; // 允许所有PWM模块的计数器加载相位
   EPwm2Regs.TBCTL.bit.PHSEN = TB_ENABLE;
   EPwm3Regs.TBCTL.bit.PHSEN = TB_ENABLE;
   EPwm4Regs.TBCTL.bit.PHSEN = TB_ENABLE;
   EPwm5Regs.TBCTL.bit.PHSEN = TB_ENABLE;
   EPwm6Regs.TBCTL.bit.PHSEN = TB_ENABLE;


   MyPeriod = MyPeriod_divid6 * 6 - 1; // 周期是149

// 下面是PWM1
   EPwm1Regs.TBPRD = MyPeriod;  //设定计数器最大值为149,那么PWM周期就是150TBCLK
   EPwm1Regs.CMPA.half.CMPA = MyPeriod_divid6 * 3 + Highside_plus;			// CMPA的占空比为75,也就是50%

   EPwm1Regs.TBCTL.bit.CTRMODE = TB_COUNT_UP;		// 增模式
   EPwm1Regs.TBPHS.half.TBPHS = 0;					// PWM1是主,不加相位差
   EPwm1Regs.AQCTLA.bit.ZRO = AQ_SET;				// 当计数器为0时,让pwm1a输出高电平
   EPwm1Regs.AQCTLA.bit.CAU = AQ_CLEAR;				// 当计数器为cmpa,让PWM1A输出低电平
   EPwm1Regs.DBCTL.bit.IN_MODE = DBA_ALL;			// 死区控制,PWM1A作为上升下降沿延时信号源
   EPwm1Regs.DBCTL.bit.OUT_MODE = DB_FULL_ENABLE;	// 使能上升下降沿延时
   EPwm1Regs.DBCTL.bit.POLSEL = DB_ACTV_HIC;		// PWM1B为PWM1A的极性反转
   EPwm1Regs.DBRED = DeadTime;						// 上升沿延时时间9
   EPwm1Regs.DBFED = DeadTime;						// 下降沿延时时间还是9

   EPwm1Regs.ETSEL.bit.INTSEL = ET_CTR_ZERO;     // 当计数器为0时产生中断信号
   EPwm1Regs.ETSEL.bit.INTEN = 1;  					// 使能PWM产生中断信号
   EPwm1Regs.ETPS.bit.INTPRD = ET_1ST;           // 每发生一次事件(计数器=0),产生一次中断信号

//PWM2是控制副边的
   EPwm2Regs.TBPRD = MyPeriod;
   EPwm2Regs.CMPA.half.CMPA = MyPeriod_divid6 * 3 + Highside_plus;			// Set duty for PWM1A, from DeadTime to CMPA

   EPwm2Regs.TBCTL.bit.CTRMODE = TB_COUNT_UP;     // Count up
   EPwm2Regs.TBPHS.half.TBPHS =  1 + SR_on_delay; //相对于PWM1,PWM2几乎没有延时
   EPwm2Regs.AQCTLA.bit.ZRO = AQ_SET;				// Set PWM1A on ZRO
   EPwm2Regs.AQCTLA.bit.CAU = AQ_CLEAR;				// Clear PWM1A on event A, up count
   EPwm2Regs.DBCTL.bit.IN_MODE = DBA_ALL;			//
   EPwm2Regs.DBCTL.bit.OUT_MODE = DB_FULL_ENABLE;	//
   EPwm2Regs.DBCTL.bit.POLSEL = DB_ACTV_HIC;		//
   EPwm2Regs.DBRED = SR_DeadTime;						// SR_deadtime还是9
   EPwm2Regs.DBFED = SR_DeadTime;						//


   EPwm3Regs.TBPRD = MyPeriod;
   EPwm3Regs.CMPA.half.CMPA = MyPeriod_divid6 * 3 + Highside_plus;				// Set duty for PWM1A, from DeadTime to CMPA

   EPwm3Regs.TBCTL.bit.CTRMODE = TB_COUNT_UP;     // Count up
   EPwm3Regs.TBPHS.half.TBPHS = MyPeriod_divid6 * 4 + 1; //101,C相,与A相(也就是PWM1)相差了240度
   EPwm3Regs.AQCTLA.bit.ZRO = AQ_SET;				// Set PWM1A on ZRO
   EPwm3Regs.AQCTLA.bit.CAU = AQ_CLEAR;				// Clear PWM1A on event A, up count
   EPwm3Regs.DBCTL.bit.IN_MODE = DBA_ALL;			//
   EPwm3Regs.DBCTL.bit.OUT_MODE = DB_FULL_ENABLE;	//
   EPwm3Regs.DBCTL.bit.POLSEL = DB_ACTV_HIC;		//
   EPwm3Regs.DBRED = DeadTime;						//
   EPwm3Regs.DBFED = DeadTime;						//


   EPwm4Regs.TBPRD = MyPeriod;
   EPwm4Regs.CMPA.half.CMPA = MyPeriod_divid6 * 3 + Highside_plus;				// Set duty for PWM1A, from DeadTime to CMPA

   EPwm4Regs.TBCTL.bit.CTRMODE = TB_COUNT_UP;     // Count up
   EPwm4Regs.TBPHS.half.TBPHS = MyPeriod_divid6 * 4 + 1 + SR_on_delay;
   EPwm4Regs.AQCTLA.bit.ZRO = AQ_SET;				// Set PWM1A on ZRO
   EPwm4Regs.AQCTLA.bit.CAU = AQ_CLEAR;				// Clear PWM1A on event A, up count
   EPwm4Regs.DBCTL.bit.IN_MODE = DBA_ALL;			//
   EPwm4Regs.DBCTL.bit.OUT_MODE = DB_FULL_ENABLE;	//
   EPwm4Regs.DBCTL.bit.POLSEL = DB_ACTV_HIC;		//
   EPwm4Regs.DBRED = SR_DeadTime;						//
   EPwm4Regs.DBFED = SR_DeadTime;						//


   EPwm5Regs.TBPRD = MyPeriod;
   EPwm5Regs.CMPA.half.CMPA = MyPeriod_divid6 * 3 + Highside_plus;				// B相,与A相差了120度
   EPwm5Regs.TBCTL.bit.CTRMODE= TB_COUNT_UP;      // Count up
   EPwm5Regs.TBPHS.half.TBPHS = MyPeriod_divid6 * 2 + 1;
   EPwm5Regs.AQCTLA.bit.ZRO = AQ_SET;				// Set PWM1A on ZRO
   EPwm5Regs.AQCTLA.bit.CAU = AQ_CLEAR;				// Clear PWM1A on event A, up count
   EPwm5Regs.DBCTL.bit.IN_MODE = DBA_ALL;			//
   EPwm5Regs.DBCTL.bit.OUT_MODE = DB_FULL_ENABLE;	//
   EPwm5Regs.DBCTL.bit.POLSEL = DB_ACTV_HIC;		//
   EPwm5Regs.DBRED = DeadTime;						//
   EPwm5Regs.DBFED = DeadTime;						//


   EPwm6Regs.TBPRD = MyPeriod;
   EPwm6Regs.CMPA.half.CMPA = MyPeriod_divid6 * 3 + Highside_plus;				// Set duty for PWM1A, from DeadTime to CMPA

   EPwm6Regs.TBCTL.bit.CTRMODE = TB_COUNT_UP;     // Count up
   EPwm6Regs.TBPHS.half.TBPHS = MyPeriod_divid6 * 2 + 1 + SR_on_delay;
   EPwm6Regs.AQCTLA.bit.ZRO = AQ_SET;				// Set PWM1A on ZRO
   EPwm6Regs.AQCTLA.bit.CAU = AQ_CLEAR;				// Clear PWM1A on event A, up count
   EPwm6Regs.DBCTL.bit.IN_MODE = DBA_ALL;			//
   EPwm6Regs.DBCTL.bit.OUT_MODE = DB_FULL_ENABLE;	//
   EPwm6Regs.DBCTL.bit.POLSEL = DB_ACTV_HIC;		//
   EPwm6Regs.DBRED = SR_DeadTime;						//
   EPwm6Regs.DBFED = SR_DeadTime;						//

   EALLOW;
   SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 1;         // Start all the timers synced
   EDIS;
}

PIE

概述

PIE并不是中断的全部,中断有CPU级中断,PIE级中断,外设级中断。
PIE级中断是专门处理外设中断的扩展模块(Peripheral Interrupt Expansion Block),它能够对来自外设或其他外部引脚的中断作出判断和决策。
PIE可以支持96个不同的中断,并把这些中断分成12个组,每组8个中断,每组都被反馈到CPU的12个可屏蔽中断线上,(CPU一共有14个可屏蔽中断线)。看下面的表,就知道什么意思了,外设比如EPWM产生的中断请求会由PIE进行处理。但是,只有6个EPWM可以被处理,另外EPWM7~9是没有的。这个地方有点奇怪。但是现在我也不知道中断是来干什么的。
在这里插入图片描述
在这里插入图片描述
优先级:同组内1比2高。不同组组1全部比组2高。

PIE寄存器

一共12组,每组都有自己的PIE中断使能寄存器PIEIER,标志寄存器PIEIFR,应答寄存器PIEACK。
PIE中断使能寄存器PIEIER:使能该组内对应外设的中断。
在这里插入图片描述
PIE中断标志寄存器PIEIFR:看看对应的中断有没有出现。
在这里插入图片描述
PIE中断应答寄存器PIEACK:如果PIE中有中断产生,那么响应的中断标志位被置1,如果使能位也是1,PIE将检查应答位,以确定CPU是否准备响应这个中断,如果应答位是0,PIE便向CPU申请中断;如果是1,PIE将等待直到该位为0再向CPU申请中断。向应答位写1可以让该位清0,12组每组一个应答位。(因为用了CPU的12条中断线)
在这里插入图片描述
有12个PIEIER,12个PIEIFR,12个PIEACK,但只有1个PIECTRL,也就是PIE控制寄存器。
在这里插入图片描述

PIE中断初始化程序

void InitPieCtrl(void)
{
    // 关闭CPU级的中断
    DINT;

    // 关闭PIE
    PieCtrlRegs.PIECTRL.bit.ENPIE = 0;

	// 让PIE使能寄存器为禁止状态
	PieCtrlRegs.PIEIER1.all = 0;
	PieCtrlRegs.PIEIER2.all = 0;
	PieCtrlRegs.PIEIER3.all = 0;	
	PieCtrlRegs.PIEIER4.all = 0;
	PieCtrlRegs.PIEIER5.all = 0;
	PieCtrlRegs.PIEIER6.all = 0;
	PieCtrlRegs.PIEIER7.all = 0;
	PieCtrlRegs.PIEIER8.all = 0;
	PieCtrlRegs.PIEIER9.all = 0;
	PieCtrlRegs.PIEIER10.all = 0;
	PieCtrlRegs.PIEIER11.all = 0;
	PieCtrlRegs.PIEIER12.all = 0;

	// 让PIE标志寄存器所有位为0,表示PIE没有中断
	PieCtrlRegs.PIEIFR1.all = 0;
	PieCtrlRegs.PIEIFR2.all = 0;
	PieCtrlRegs.PIEIFR3.all = 0;	
	PieCtrlRegs.PIEIFR4.all = 0;
	PieCtrlRegs.PIEIFR5.all = 0;
	PieCtrlRegs.PIEIFR6.all = 0;
	PieCtrlRegs.PIEIFR7.all = 0;
	PieCtrlRegs.PIEIFR8.all = 0;
	PieCtrlRegs.PIEIFR9.all = 0;
	PieCtrlRegs.PIEIFR10.all = 0;
	PieCtrlRegs.PIEIFR11.all = 0;
	PieCtrlRegs.PIEIFR12.all = 0;


}	

PIE中断向量表初始化程序

void InitPieVectTable(void)
{
	int16	i;
	Uint32 *Source = (void *) &PieVectTableInit;
	Uint32 *Dest = (void *) &PieVectTable;

	EALLOW;
	for(i=0; i < 128; i++)
		*Dest++ = *Source++;
	EDIS;

	// Enable the PIE Vector Table
	PieCtrlRegs.PIECTRL.bit.ENPIE = 1;

}

PieVectTableInit是PIE中断向量表,在之前已经被定义了。

const struct PIE_VECT_TABLE PieVectTableInit = {

      PIE_RESERVED,  // 0  Reserved space
      PIE_RESERVED,  // 1  Reserved space
      PIE_RESERVED,  // 2  Reserved space
      PIE_RESERVED,  // 3  Reserved space
      PIE_RESERVED,  // 4  Reserved space
      PIE_RESERVED,  // 5  Reserved space
      PIE_RESERVED,  // 6  Reserved space
      PIE_RESERVED,  // 7  Reserved space
      PIE_RESERVED,  // 8  Reserved space
      PIE_RESERVED,  // 9  Reserved space
      PIE_RESERVED,  // 10 Reserved space
      PIE_RESERVED,  // 11 Reserved space
      PIE_RESERVED,  // 12 Reserved space


// Non-Peripheral Interrupts
      INT13_ISR,     // XINT13 or CPU-Timer 1
      INT14_ISR,     // CPU-Timer2
      DATALOG_ISR,   // Datalogging interrupt
      RTOSINT_ISR,   // RTOS interrupt
      EMUINT_ISR,    // Emulation interrupt
      NMI_ISR,       // Non-maskable interrupt
      ILLEGAL_ISR,   // Illegal operation TRAP
      USER1_ISR,     // User Defined trap 1
      USER2_ISR,     // User Defined trap 2
      USER3_ISR,     // User Defined trap 3
      USER4_ISR,     // User Defined trap 4
      USER5_ISR,     // User Defined trap 5
      USER6_ISR,     // User Defined trap 6
      USER7_ISR,     // User Defined trap 7
      USER8_ISR,     // User Defined trap 8
      USER9_ISR,     // User Defined trap 9
      USER10_ISR,    // User Defined trap 10
      USER11_ISR,    // User Defined trap 11
      USER12_ISR,    // User Defined trap 12

// Group 1 PIE Vectors
      rsvd_ISR,        // 1.1
      rsvd_ISR,        // 1.2
      rsvd_ISR,        // 1.3
      XINT1_ISR,       // 1.4
      XINT2_ISR,       // 1.5
      rsvd_ISR,        // 1.6
      TINT0_ISR,       // 1.7 Timer 0
      WAKEINT_ISR,     // 1.8 WD, Low Power

// Group 2 PIE Vectors
      EPWM1_TZINT_ISR, // 2.1 EPWM-1 Trip Zone
      EPWM2_TZINT_ISR, // 2.2 EPWM-2 Trip Zone
      EPWM3_TZINT_ISR, // 2.3 EPWM-3 Trip Zone
      EPWM4_TZINT_ISR, // 2.4 EPWM-4 Trip Zone
      EPWM5_TZINT_ISR, // 2.5 EPWM-5 Trip Zone
      EPWM6_TZINT_ISR, // 2.6 EPWM-6 Trip Zone
      EPWM7_TZINT_ISR, // 2.7 EPWM-7 Trip Zone
      EPWM8_TZINT_ISR, // 2.8 EPWM-8 Trip Zone

// Group 3 PIE Vectors
      EPWM1_INT_ISR,   // 3.1 EPWM-1 Interrupt
      EPWM2_INT_ISR,   // 3.2 EPWM-2 Interrupt
      EPWM3_INT_ISR,   // 3.3 EPWM-3 Interrupt
      EPWM4_INT_ISR,   // 3.4 EPWM-4 Interrupt
      EPWM5_INT_ISR,   // 3.5 EPWM-5 Interrupt
      EPWM6_INT_ISR,   // 3.6 EPWM-6 Interrupt
      EPWM7_INT_ISR,   // 3.7 EPWM-7 Interrupt
      EPWM8_INT_ISR,   // 3.8 EPWM-8 Interrupt

// Group 4 PIE Vectors
      ECAP1_INT_ISR,   // 4.1 ECAP-1
      ECAP2_INT_ISR,   // 4.2 ECAP-2
      ECAP3_INT_ISR,   // 4.3 ECAP-3
      ECAP4_INT_ISR,   // 4.4 ECAP-4
      ECAP5_INT_ISR,   // 4.5 ECAP-5
      ECAP6_INT_ISR,   // 4.6 ECAP-6
      rsvd_ISR,        // 4.7
      rsvd_ISR,        // 4.8

// Group 5 PIE Vectors
      EQEP1_INT_ISR,   // 5.1 EQEP-1
      EQEP2_INT_ISR,   // 5.2 EQEP-2
      EQEP3_INT_ISR,   // 5.3 EQEP-3
      rsvd_ISR,        // 5.4
      rsvd_ISR,        // 5.5
      rsvd_ISR,        // 5.6
      rsvd_ISR,        // 5.7
      rsvd_ISR,        // 5.8


// Group 6 PIE Vectors
      SPIRXINTA_ISR,   // 6.1 SPI-A
      SPITXINTA_ISR,   // 6.2 SPI-A
      MRINTB_ISR,      // 6.3 McBSP-B
      MXINTB_ISR,      // 6.4 McBSP-B
      MRINTA_ISR,      // 6.5 McBSP-A
      MXINTA_ISR,      // 6.6 McBSP-A
      SPIRXINTD_ISR,   // 6.7 SPI-D
      SPITXINTD_ISR,   // 6.8 SPI-D


// Group 7 PIE Vectors
      DINTCH1_ISR,     // 7.1  DMA channel 1
      DINTCH2_ISR,     // 7.2  DMA channel 2
      DINTCH3_ISR,     // 7.3  DMA channel 3
      DINTCH4_ISR,     // 7.4  DMA channel 4
      DINTCH5_ISR,     // 7.5  DMA channel 5
      DINTCH6_ISR,     // 7.6  DMA channel 6
      rsvd_ISR,        // 7.7
      rsvd_ISR,        // 7.8

// Group 8 PIE Vectors
      I2CINT1A_ISR,    // 8.1  I2C
      I2CINT2A_ISR,    // 8.2  I2C
      rsvd_ISR,        // 8.3
      rsvd_ISR,        // 8.4
      SCIRXINTC_ISR,   // 8.5  SCI-C
      SCITXINTC_ISR,   // 8.6  SCI-C
      rsvd_ISR,        // 8.7
      rsvd_ISR,        // 8.8

// Group 9 PIE Vectors
      SCIRXINTA_ISR,   // 9.1 SCI-A
      SCITXINTA_ISR,   // 9.2 SCI-A
      SCIRXINTB_ISR,   // 9.3 SCI-B
      SCITXINTB_ISR,   // 9.4 SCI-B
      ECAN0INTA_ISR,   // 9.5 eCAN-A
      ECAN1INTA_ISR,   // 9.6 eCAN-A
      ECAN0INTB_ISR,   // 9.7 eCAN-B
      ECAN1INTB_ISR,   // 9.8 eCAN-B

// Group 10 PIE Vectors
      EPWM9_TZINT_ISR, // 10.1 EPWM-9 Trip Zone
      rsvd_ISR,        // 10.2
      rsvd_ISR,        // 10.3
      rsvd_ISR,        // 10.4
      rsvd_ISR,        // 10.5
      rsvd_ISR,        // 10.6
      rsvd_ISR,        // 10.7
      rsvd_ISR,        // 10.8

// Group 11 PIE Vectors
      EPWM9_INT_ISR,   // 11.1 EPWM-9 Interrupt
      rsvd_ISR,        // 11.2
      rsvd_ISR,        // 11.3
      rsvd_ISR,        // 11.4
      rsvd_ISR,        // 11.5
      rsvd_ISR,        // 11.6
      rsvd_ISR,        // 11.7
      rsvd_ISR,        // 11.8

// Group 12 PIE Vectors
      XINT3_ISR,       // 12.1
      XINT4_ISR,       // 12.2
      XINT5_ISR,       // 12.3
      XINT6_ISR,       // 12.4
      XINT7_ISR,       // 12.5
      rsvd_ISR,        // 12.6
      LVF_ISR,         // 12.7
      LUF_ISR,         // 12.8
};

那个循环里的意思好像是要让PieVectTable与PieVectTableInit相等。赋完值后使能PIECTRL的一位,使能PIE从中断向量表中获取中断向量。

后续与PIE有关的程序

EALLOW;  // This is needed to write to EALLOW protected registers
   PieVectTable.EPWM1_INT = &epwm1_timer_isr;
   EDIS;    // This is needed to disable write to EALLOW protected registers
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值