\qquad 写在前面,不管是C51、MSP430、32也好,DSP也好,总要有一个掌握的比较熟练,不能每次都复制粘贴别人的代码然后修修补补吧。不要再做Ctrl +C、Ctrl+V工程师。主要参考来自于TI的TMS283xx手册、普中DSP教程,C2000助手等。方便下载,资料整理到了一起
时钟是学习一款控制器,不管是硬件还是软件方面最重要的部分.
\qquad 如果不使用片上振荡器,时钟由X1X2或XCLKIN引脚上的外部时钟源输入产生。如果从XCLKIN引脚接入时钟,那么X1保持地电平,X2浮空,外部接入一个3.3V的时钟源。如果从X1和X2接入时钟源,那么XCLKIN保持低电平,X1接外部时钟信号,X2仍然浮空。
\qquad 总的来说,这里OSC电路可以通过在X1和X2之间接入一个晶振来产生(并最好保持XCLKIN接地)。如果没有在X1和X2之间接入晶振,那么就需要保持X1接地,X2浮空,并且在XCLINK之间接入振荡器。注意晶振(Crystal)、共振器(Resonator)、振荡器(Oscillator)的区别。可以简单的认为晶振和共振器是无源晶振(需要接外部电路才可以起振),而振荡器是有源晶振。那么最简单的方式就是在X1和X2之间接入晶振或者共振器,然后利用28335内部电路使得晶振起振。
\qquad 时钟源进来后,首先必须将OSCOFF位置1,这样才能使得时钟源进入直接选择器和经PLL模块到选择器。在PLL模块这里有一个PLLOFF位是用来关断PLL的,选择不关断(默认),接下来就是倍频环节PLLCR寄存器,低四位用来设置倍频。经过倍频到选择器输出后,设置寄存器PLLSTS的第8位和第7位DIVSEL来设置分频系数,在倍频设值之前必须将分频系数置零。通常我们的150MHz是OSCCLK(30MHz) × \times × PLLCR(10)/DIVSEL(2)=150MHz;
\qquad 由于外部环境的干扰,外部时钟源有时不能进入DSP或者不能为DSP提供正常的时钟信号。这里有两个计数器来检测OSCCLK信号,第一个计数器随着OSCCLK信号的到来而计数,令一个计数器是当PLL使能之后,随着VCOCLK信号的到来而计数,当7位的OSCCLK计数器溢出时,他会清除13位的VCOCLK计数器。所以,只要OSCCLK到来,VCOCLK计数器就不会溢出。
\qquad 如果VCOCLK信号丢失,PLL模块的输出就会进入默认的“缓慢模式”(不知道这样翻译对不对,原文:limp mode)频率,并保持增长。由于OSCCLK信号丢失,那么OSCCLK计数器就不在继续增加,者意味着VCOCLK计数器不能被周期性的清零,经过一段时间,VCOCLK计数器就会溢出。当VCOCLK溢出之后,检测信号丢失逻辑器就会reset CPU,外设和其他的设备的逻辑电平。这个reset信号被称为 M C L K R E S ‾ \overline{MCLKRES} MCLKRES信号,这个信号只能由内部产生,外部的 X R S ‾ \overline{XRS} XRS 不会被 M C L K R E S ‾ \overline{MCLKRES} MCLKRES信号拉低。
\qquad 上图是时钟输出的内部结构,时钟输出是直接从系统时钟来的,时钟输出可以是系统时钟,或者1/2的系统时钟,或者1/4的系统时钟,默认在上电后,时钟输出是系统时钟的1/4,或者是振荡频率的1/16;这个输出时钟信号可以给其他需要时钟信号的非片上外设提供时钟信号。
\qquad 复位之后就会有时钟信号输出,当reset是低电平时,XCLKOUT=1/4SYSCLKOUT,可以通过监测这个信号测试在debug状态下时钟是否正确。这个引脚没有上拉或者下拉电阻。从图中可以看出,如果不需要输出这个时钟信号,可以通过设置寄存器XINTCNF2的CLKOFF位为1来关闭这个信号。
(a):当PLL工作在“缓慢模式”(limp mode)时,不要往PLLCR(倍频寄存器)中写值;
(b):接外部时钟时,看门狗不起作用;
(c):上电过程中不会带来limp mode;
(d):当工作在limp mode时,不要进入halt的低功耗模式,这时候要等待内部重启直到外部时钟信号到来。
(e):改变PLL控制寄存器时要遵从正确的上电步骤和寄存器配置;上电后寄存就流程图如下:
\qquad 1.检测MCLKSTS位是否为1(检测是否由信号丢失,如果置位MCLKSTS,软件内部就会采取类似于系统关闭等动作);2.检测PLLSTS的分频系数是否为0;3.设置PLLSTS寄存器的MCLKOFF位=1来禁止振荡器逻辑检测;4.设置倍频PLLCR的值;5.检测PLLSTSP寄存器的PLLOCKS是否等于1(表示PLLCR寄存器已经被写入并且PLL当前是否处于锁定状态位);5.将PLLSTS寄存器的MCLKOFF位=0来使能振荡器逻辑检测;6.这时候可以改变DIVSEL的值了;
\qquad 这里的CLKIN就是前面产生的系统时钟。从这张图中可以很清晰的看到,外设SPI-A,SCI-A/B/C,McBSP-A/B,是挂在低速时钟上的(LSPCLK);外设ADC是挂在高速时钟上的;外设EPWM模块是直接与系统时钟相连(这是为了给pwm提供足够的精度),IIC也是挂在系统时钟上的;Ecan模块是系统时钟经过2分频(不可修改)的来的。
\qquad 这里不介绍看门狗,因为我们一般禁用看门狗就行,这样也不用去写一些喂狗代码。
\qquad 28335共有3个32位定时器Timers0/1/2,其中定时器2既可以用于dsp内部,也可以用于用户外设。28335比不上stm32的定时器多,stm32f103zet6就有8个定时器,当用到多个定时器时中断计时,不如32灵活,但很多时候3个定时器计时已经够用了,32没有单独的PWM模块,只能由定时器产生。很多人对PWM和定时器经常搞混。
\qquad 定时器也称为定时计数器,可以通过计数来计时,也可以通过计数产生PWM,想想32里面的计数模式,向上、向下和向上向下计数模式等,无非就是计数到某一个数值之前保持高或者低,然后到达那个数之后电平翻转。这和我们电力电子里面的三角波(向上向下计数)或锯齿波(向上或向下计数)和一个数值进行比较产生PWM很类似。定时计数器,既可以通过计数器来达到计时或产生PWM信号。DSP内部集成了6个ePWM模块,单论PWM产生个数而言,比f103zet6多,这个后续会介绍。
\qquad 定时器寄存器是连接到28335处理器的存储器总线;定时器的时序和处理器的时序保持一致。
\qquad 从上面的CPU的定时器可以看到,32位的计数器寄存器TIMH:TIM加载周期寄存器的数值PRDH:PDR,计数器每经过TPR[TDDRH:TDDR]+1(这里的 TPR[TDDRH:TDDR]是定时器分频系数)个系统时钟就会减一计数,当减一到0后就会产生一个定时器中断信号,产生的中断信号如下图:
\qquad 1.PLLSTS寄存器写入值;设置PLLCR的值为DSP28_PLLCR(10),设置DIVSEL为DSP28_DIVSEL(2)
if (SysCtrlRegs.PLLSTS.bit.DIVSEL != 0)
{
SysCtrlRegs.PLLSTS.bit.DIVSEL = 0;
}
SysCtrlRegs.PLLSTS.bit.MCLKOFF = 1;//Disabel oscillator detection
SysCtrlRegs.PLLCR.bit.DIV = DSP28_PLLCR;
SysCtrlRegs.PLLSTS.bit.DIVSEL = DSP28_DIVSEL;
\qquad 设置外设时钟(以前的一个小问题:由于SCI是挂在外部时钟上的,外部的uart波特率是250K,如果SCI还选择37.5MHz的画,两个字节就会出错,所以我将低速时钟设置成了150MHz,这样就可以保持传输数据不出错,但低速时钟能设置成150MHz吗?)
//set high speed clock is 75MHz and low is 37.5Mhz
SysCtrlRegs.HISPCP.all = 0x0001;
SysCtrlRegs.LOSPCP.all = 0x0002;
\qquad 设置输出时钟信号,这里将输出时钟信号设置成系统时钟的1/4,
XintfRegs.XINT.bit.XTIMCLK = 1;//XTIMCLK =SYSCLKOUT/2
XintfRegs.XINT.bit.CLKMODE = 1;//XCLKOUT = SYSCLKOUT/2
XintfRegs.XINT.bit.CLKOFF = 1;//Enable XCLKOUT
\qquad 下面使能所有的时钟,熟悉一下由寄存器PCLKCR0控制的外设时钟;
SysCtrlRegs.PCLKCR0.bit.I2CAENCLK = 1;//IIC
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;//McBSPA
SysCtrlRegs.PCLKCR0.bit.MCBSPBENCLK = 1;//McBSPB
SysCtrlRegs.PCLKCR0.bit.ECANAENCLK=1;//eCAN-A
SysCtrlRegs.PCLKCR0.bit.ECANBENCLK=1;//eCAN-B
SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 1;//TBCLK
\qquad 使能PCLKCR1寄存器外设时钟,这个寄存器主要是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.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
\qquad 使能PCLKCR3寄存器(没有PCLKCR2寄存器)时钟
//enable CPU Timer 0,Timer 1 and Timer 2
SysCtrlRegs.PCLKCR3.bit.CPUTIMER0ENCLK = 1;
SysCtrlRegs.PCLKCR3.bit.CPUTIMER1ENCLK = 1;
SysCtrlRegs.PCLKCR3.bit.CPUTIMER2ENCLK = 1;
SysCtrlRegs.PCLKCR3.bit.DMAENCLK = 1;//DMA Clock
SysCtrlRegs.PCLKCR3.bit.XINTFENCLK = 1;// XTIMCLK
SysCtrlRegs.PCLKCR3.bit.GPIOINENCLK = 1;//GPIO input clock
\qquad 通用输入输出口( General Purpose Input and Output GPIO)是一款控制中最基础的部分。
\qquad GPIO复用器(multiplexing,有的也翻译成多路选择器)用于选择共享引脚的操作,这个很好理解,控制器为了节省IO口,通常将一个IO映射寄存器将一个外设的引脚映射到其他引脚;而复用器是通过多路选择器的方式选择IO口的作用,DSP的引脚使用比32更灵活是因为他内部的硬件多路选择器,这个也可以通过寄存器设置。
\qquad GPIO0~GPIO87都可以被设置成数字IO口(也就是常说的GPIO口),或者通过GPxMUxn寄存器(复用寄存器)连接到3个外设IO口中的其中一个。如果作为GPIO,通过GpxDIR寄存器(方向寄存器)来选择该引脚作为输出或者和输入IO口,同时你也可以通过GPxQSELn(量化寄存器),GPxCTRL(控制寄存器)来选择量化输入信号以移除不想要的信号,也就是常说的滤波。
\qquad 28335一共有88个IO口,从GPIO0~GPIO87,其中0-31为Part A,31-63为Part B,64-87为Part C;
\qquad 上图是GPIOA复用器的基本原理图;(a)当寄存器PCLKCR3的GPIOINENCLK位为0时(这个位是给GPIO模块提供时钟的),上图中的阴影部分被禁止,并且各个引脚都设置为输出模式,当引脚配置为输出模式时这可以减小功率损耗。清除GPIOINENCLK位将会重置同步和量化逻辑器,这样之前设置的值就没了。(b)GPxDAT禁止/读取都在相同的内存被访问。
\qquad (a)当GPADIR置1时,1电平经过非门就是0,由于低电平有效,那么此时的IO口是做输出用的。(b)当GPADIR清零时,0电平经过非门就是1,1经过或门输出也是1,由于低电平有效,所以此时信号只能做输入。输入的时候既可以选择同步输入,也可以选择一部输入,当选择同步输入时,可以进行输入滤波设置。值得注意的是,当外部硬件给 X R S ‾ \overline{XRS} XRS 一个低电平复位时,0电平经过非门就是1,经过或门还是1,由于低电平有效,所以此时IO口是做输入使用,这也就是接外部功率电路复位时会烧毁单片机或dsp的原因。图的最左边有一个选择是否上拉PD,这个寄存器是清零代表上拉,置1表示禁止上拉(因为这个寄存器叫做Pull Up Disable Register);右上角是中断信号输出到PIE,中断会在后续讲解。
\qquad 1.开始之前先禁止所有的外部IO口选择;
\qquad 2.使能或者禁止内部上拉电阻, 这是通过GPxPUD寄存器来设置的,除了GPIO0~GPIO11这12个IO口做ePWM模式输出时是默认禁止上拉的,其余情况下都是默认使能上拉;
\qquad 3.选择输入量化寄存器的值, 输入滤波是由GPxCTRL和GPxQSELn寄存器来设置的,默认情况下所有的输入信号都是只和系统时钟同步的;
\qquad 4.配置GPxMUXn寄存器 ,可以设置引脚为GPIO功能或其他外部接口功能,默认在reset后引脚是GPIO功能;
\qquad 5.选择数字IO口后,选择引脚是是输入还是输出,,通过寄存器GPxDIR(方向寄存器)来选择引脚是做输入还是输出使用。默认IO口是输入,想要将IO口作为输出引脚使用,首先通过写入相应的值到GPxCLEAR(清零寄存器)、GPxSET(置位寄存器)或者GPxTOGGLE(翻转寄存器)来记载输出锁存器,一旦锁存寄出去加载了值,通过GPxDIR寄存器来改变引脚的方向。当reset后,所有引脚的锁存器加载的值都会被清零;
\qquad 6.选择低功耗模式唤醒源,选择指定的引脚能从HALT和STANDBY低功耗模式唤醒设备,这个引脚由GPIOLPMSEL(GPIO低功耗模式选择寄存器)指定;
\qquad 7.选择外部中断源,指定XINT1~XINT7,XNMI(Non-Maskable Interruupt)中断源,对于每一次中断可以指定端口A(用于XINT1/2/3)或端口B(XINT4/5/6)作为中断源,这个由寄存器GPIOXINTnSEL(GPIO输入中断源选择寄存器)和GPIOXNMISEL(GPIO中断不可屏蔽寄存器)来指定,中断的极性(引脚的上升沿or下降沿)由寄存器XINTnCR(External Interrupt n Control Register 外部中断控制寄存器)和XNMICR的[3:2]位配置;
\qquad 输入限定的方式非常灵活,你可以通过配置寄存器GPxQSELn来位每一个GPIO口选择输入限定的类型。在GPIO输入的模式下,可以由只能和系统时钟同步来限定,也可以通过采样窗口来限定;在外设输入的模式下除了上述的两种限定方式,还可以通过异步的方式来限定。
\qquad 异步输入限定模式:这个模式是不需要输入同步的情况和外部设备本身可以工作在异步方式下的,如通信端口:SCI、SPI、eCAN,和需要独立于系统时钟的ePWM触发方式。
\qquad 只和系统时钟同步限定模式:这是在所有的引脚都被reset后默认的一种限定模式,在这种模式下,输入信号只能和系统时钟同步,由于输入信号是异步的,为了改变DSP的输入,它需要一个SYSCLKOUT周期的延迟。 对信号不执行进一步的限定。
\qquad 使用采样窗口限定模式:在这种模式下,输入信号首先和系统时钟保持同步,然后在输入信号改变之前通过特定数目的周期数来进行限定。使用这种输入限定模式有两个参数需要确定,a)采样周期,b)采样次数。这样的好处是可以对输入信号进行滤波。
\qquad 上图中的Time between samples 就是采样周期,为了限定信号,输入信号是周期采样,而采样周期取决于用户;采样之间的时间间隔,或者多长时间、多久采样一次和系统时钟(CPU时间)有关。采样周期取决于GPxCTRL寄存器d额QUALPRDn,采样周期可按8个输入信号组进行配置 ,例如GPIO0~GPIO7使用GPACTRL[QUALPDR0],而GPIO8-GPIO15使用GPACTRL[QUALPDR1],从下图的表达式中可以确定在给定的系统时钟下采样的时间的最大值和最小值。
\qquad 最大的采样频率 f m a x f_{max} fmax和最小采样时间 t m i n t_{min} tmin,设置GPxCTRL[QUALPRDn] = 0;一般设置系统时钟频率 f S Y S C L K = 150 M H z f_{SYSCLK} = 150MHz fSYSCLK=150MHz,这个可以知道 f m a x = 150 M H z , t m i n = 6.67 n s f_{max} = 150MHz,t_{min} = 6.67ns fmax=150MHz,tmin=6.67ns;最小的采样频率 f m i n f_{min} fmin和最小采样时间 t m a x t_{max} tmax,设置GPxCTRL[QUALPRDn] = 0xFF; f m i n = 150 M H z × 1 ÷ ( 2 × G P x C T R L [ Q U A L P R D n ] = 0.294 M H z ) f_{min} = 150MHz \times 1 \div (2\times GPxCTRL[QUALPRDn] =0.294MHz) fmin=150MHz×1÷(2×GPxCTRL[QUALPRDn]=0.294MHz), t m a x = 3.4 μ s t_{max}=3.4\mu s tmax=3.4μs;
\qquad 采样次数:信号采样的次数是特定的3个或6个,选择寄存器中指定的(GPAQSEL1、GPAQSEL2、GPBQSEL1和GPBQSEL2)。 当连续3或6个周期相同时,输入变化将被传递到DSP。
寄存器名称 | 寄存器全称 | 说明 |
---|---|---|
GPACTRL | GPIOA Control Reg | 确定采样时间 |
GPAQSELn | GPIOA Quantifier Select Regs(n) | 确定采样间隔 |
GPAMUXn | GPIOA MUx Regs(n) | 配置GPIO |
GPADIR | GPIOA Direction Reg | 设置IO口方向 |
GPAPUD | Pull Up Disable Reg | 是否禁止上拉电阻 |
------------ | -------------------------------- | ---------- |
GPADAT | GPIOA Data Reg | 解释如下 |
GPASET | GPIOA Set Reg | 置位寄存器 |
GPACLEAR | GPIOA Clear Reg | 清零寄存器 |
GPATOGGLE | GPIOA Toggle Reg | 翻转寄存器 |
\qquad GPxDAT寄存器:GPIO数据寄存器在限定之后指示了IO引脚当前的状态。如果引脚被配置成GPIO且位输出状态,且这个位置1,那么这个寄存器会强制其输出高电平;如果这个位清零,则会强制输出0。如果引脚没有被配置成输出下GPIO模式(不是GPIO模式或者是输入下的GPIO模式),如果向相应位写操作,则 这个写入的值被锁存,但引脚会被驱动。复位的时候,所有的值都被清零。当使用这个寄存器去改变输出引脚的电平时,要注意不要改变其他引脚的电平。例如,想要通过向GPADAT写入0来改变GPIOA0的输出电平,当使用一个read-modify-write 指令时,如果其他IO口的信号电平在这个指令时改变就会出问题。当GPIO配置成输入的GPIO时,读取寄存器相应位的值可以反应出引脚的电平状态,如果是1,则该引脚处于高电平,反之就是地电平。为了方便的修改输出GPIO的电平,可以使用SET、CLEAR、TOGGLE寄存器。
//if the GPIO is initialed as output
GpioCtrlRegs.PCLK3.bit.GPIOINENCLK = 1;//enable GPIO clock
GpioCtrlRegs.GPCMUX1.bit.GPIO68 = 0;//configure as GPIO
CpioCtrlRegs.GPCDIR.bit.GPIO68 = 1;//congigure pin as output
GpioCtrlRegs.GPCPUD.bit.GPIO68 = 0;//enable pull up
GpioCtrlRegs.GPCSET.bit.GPIO68 = 1;//set high level
LED2_TOGGLE;//Defined as GpioDataRegs.GPCTOGGLE.bit.GPIO68 = 1
//if the Gpio is initialed as input
GpioCtrlRegs.PCLK3.bit.GPIOINCLK = 1;//enabel GPIO clock
GpioCtrlRegs.GPAMUX1.bit.GPIO12 = 0;//configured as GPIO
GpioCtrlRegs.GPADIR.bit.GPIO12 = 0;//congigure pin as input
GpioCtrlRegs.GPAPUD.bit.GPIO12 = 0;//enable pull up
GpioCtrlRegs.GPAQSEL1.bit.GPIO12 = 2;//qualification using 6 samples
GpioCtrlRegs.GPACTRL.bit.QUALPRD0 = 0;//set the max sampling frequency
GpioDataRegs.GPASET.bit.GPIO12=1;//set high level
if(key_H==0){}//key_H is defined as GpioDataRegs.GPADAT.bit.GPIO12