现在有一个TMDSCNCD28343的控制卡,是TI公司基于TMS320C28343这个鸡肋的MCU开发出来的控制卡,使它用起来不再那么鸡肋,也省去开发控制板的步骤。
由于我刚接触DSP,微机原理也不太记得了,完全的小白。
现在我有一个基于28343控制卡的例程,打算基于程序来分析学习MCU的功能和使用方法。
精力有限,希望能入门DSP。
芯片和控制卡概述
控制卡28343上面搭载了28343的MCU,有了控制卡,我们不用开发板子,不用搭电源电路,接几条线就能用了,控制卡对于28343最大的意义在于加了俩ADC芯片,弥补了这个MCU没有内置ADC的不足。
控制卡有什么
MCU有179个引脚,而控制卡有100个引脚,被阉割掉了很多GPIO,但是够用就行了。控制卡长这样:买控制卡附送扩展坞,什么线都接扩展坞上。
那么控制卡比起MCU多了什么呢?
- 接了晶振20MHz。
- 接了ADC
- 接了SOC(ADC相关,属于MCU的输出,控制卡上没有这些接口)
- 5V,3.3V,1.2V,1.8V电源一应俱全
- 串口相关
- 附送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上还没有找到,等我找到了再贴上来。
外设时钟控制寄存器
这个东西是来控制时钟信号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,如下图:
- 空闲模式(IDLE),低功耗模式不起作用,LPM两位的值为00,看门狗中断和任何使能中断都可以退出该模式。
- 待命模式(STANDBY),LPM两位为01,CLKIN关闭,但是外部振荡器和PLL,看门狗还在工作。
- 暂停模式(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寄存器可以配置看门狗是复位还是中断
- 复位模式:
当计数器溢出的时候,WDRST信号拉低系统复位引脚(XRTS)512个个时钟周期 - 中断模式
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个寄存器。
- 周期寄存器TPR,决定了一个周期计数的最大值,也就是PWM周期。
- 计数器寄存器TCNT,比如在增减计数模式,TCNT开始从0开始加,加到了TPR的值之后,开始减,减到0,开始一轮新的周期。
- 比较寄存器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进行处理,有以下事件:
- 计数器=PRD
- 计数器=0
- 计数器=CMPA,正在减计数
- 计数器=CMPA,正在增计数
- 计数器=CMPB,正在减计数
- 计数器=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