查看: 402|回复: 19
收起左侧

[DSP编程] dsp之旅

[复制链接]
     

4

主题

37

帖子

161

积分

中级技术员

 楼主| 发表于 2017-7-14 15:28 | 显示全部楼层 |返回版面||阅读模式
本帖最后由 apple武 于 2017-7-14 15:28 编辑

今年春上,开始学习DSP,中间各种烦事,学习了有半个月的时间,然后就被各种杂事中断了,暑假开始,继续DSP之旅。

在日志里面记录了,通过CCS6如何创建工程,别笑,那时候,创建一个新工程我用了两天,跟着一些书籍进行配置,最后竟然还是会出错,然后就借助网上的大神的帖子,顺利创建新工程,刚又看了下写那篇日志的时间,是4/5,这之间我都做了些什么那??????一直心心念着学习DSP,现在发现自己脑袋里空空如也,没有为之付出很多时间、精力,外在原因有,重要的是自己内在的原因,有时候,一件事情,你可以坚持一天、一周,一个月,但是,慢慢地你的热度就会下降,最后完全抛到脑后,说的就是我自己。有些事情不是一蹴而就的,需要一步一个脚印向前走,目标一点点地靠近自己,最终目标会如期而至,不用着急,慢慢来,每天进步一点点。
开篇
时钟在一个系统中的作用真可谓是心脏之于人,重要性不必说,一个系统若是时钟就不稳定,那这个系统几乎谈不上可靠,故还是从时钟系统学起。

dsp的时钟源可以来自外部无源晶振,还可以通过有源晶振产生时钟信号作为输入。一般用30M有源晶振作为外部时钟源,一般是30M的时钟通过PLL10倍频到300M,然后再2分频产生150M的系统时钟。
这个PLL配置过程参考官方手册,以及官方给出了时钟配置的程序,直接进行调用就行。

例程就是参考这个步骤来写的,有一点不是太明白的是,最后倍频结束后,分频的时候,当分频为1时,要先进行2分频,然后再进行不分频????
官方例程如下:
void  InitPll(Uint16 val, Uint16 divsel)
{
    //
    // Make sure the PLL is not running in limp mode
    //
    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
    //
    if (SysCtrlRegs.PLLSTS.bit.DIVSEL != 0)                                          //分频部分先设定为4分频,在PLLCR改变之前,DIVSEL  必须为0
    {
        EALLOW;
        SysCtrlRegs.PLLSTS.bit.DIVSEL = 0;
        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;                                                                 //改变PLL倍频系数时,要把丢失时钟检测关闭
        SysCtrlRegs.PLLCR.bit.DIV = val;
        EDIS;

        //
        // Optional: Wait for PLL to lock.                                                                        //在对时钟分频时,要保证PLL已经入锁
        // During this time the CPU will switch to OSCCLK/2 until                                      //pLL稳定之前,内核时钟为OSCCLK/2 =15M
        // the PLL is stable.  Once the PLL is stable the CPU will
        // switch to the new PLL value.                                                                         //PLL一旦稳定下来,内核时钟转变为新的值
        //
        // This time-to-lock is monitored by a PLL lock counter.
        //
        // Code is not required to sit and wait for the PLL to lock.                                    //代码不需要等待PLL入锁,但是,如何代码在做一些关键的事情时,需要正确的时钟
        // 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)            //等待PLL入锁
        {
            //
            // 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;
    }

    //
    // NOTE: ONLY USE THIS SETTING IF PLL IS BYPASSED (I.E. PLLCR = 0) OR OFF
    // 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)                                          //??????????????????????不明白为什么要这样来,不是直接赋成3就行了,,求解,我看网上有说是为了防止两个
    {                                                            //分频器同时分频出现错误,可是这样先分成2分频,再分成1分频就行了??????不是太懂,还望大神指点
        EALLOW;
        SysCtrlRegs.PLLSTS.bit.DIVSEL = 2;
        DELAY_US(50L);
        SysCtrlRegs.PLLSTS.bit.DIVSEL = 3;
        EDIS;
    }
}

红色部分是我的一些理解。

通过以上的配置,就可以得到一个稳定可靠的系统时钟了。为了保证这个时钟时时刻刻稳定可靠,就要对他进行时时刻刻地监控

正常情况下,OSCCLOCK counter(7位)对OSCCLOCK进行计数,VCOCLOCK counter(13位)对VCOCLOCK进行计数,当OSCCLOCK counter计数器溢出时,自动对VCOCLOCK counter清零,正常时,OSCCLOCK counter计数器不会溢出
非正常时(OSCCLOCK时钟丢失时)PLL会进入limp - mode模式并会有一个低频时钟产生,VCOCLOCK counter计数器对该低频时钟进行计数,溢出时将产生一个复位信号对CPU、外设等复位,并将PLLSTS[MCLKCLR]位置1,当检测到PLLSTS[MCLKCLR]位为1时,表明系统时钟工作在limp mode模式下。需要检查硬件是否有问题,通过向PLLSTS[MCLKCLR]写1,对该位清零,并时钟电路复位,再次上电观察PLLSTS[MCLKCLR]位,重复该过程,直到正常。

考虑到一些外设有可能会需要时钟,DSP专门引出了一个时钟XCLCKOUT为其所用

默认情况下,是将XCLKOUT设置为系统时钟的四分频37.5M,也可以自己设置,最大不超过系统时钟SYSCLKOUT。

这之上的配置过后就可以得到保证系统时时刻刻稳定的时钟了!TI公司又将这个系统时钟分为低速时钟和高速时钟,分别分给不同的外设,之前学习过TI得msp430,这和430里面的架构感觉完全一致,这样可以降低功耗。

为不同的外设提供相应的时钟,低速的外设给你高速的时钟也只能是浪费,并且还有可能速度太快,你跟不上;高速外设提供低速时钟,只能拖后腿,故合适才是最重要的。中庸之道
当有些外设不用的时候,可以将其关闭,以降低功耗,InitSysCtrl()函数中,InitPeripheralClocks()该函数对外设时钟进行初始化,即将一些不必要的外设时钟关闭,以降低功耗。


至此,DSP的时钟部分基本总结完毕,以上只是个人的见解,有不对的地方还望大神指点迷津!dsp之旅,依旧在路上
                                                                                                                                                                                                      ----smtudou
                                                                                                                                                                                                        2017/7/14

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册 手机登录

x
     

4

主题

37

帖子

161

积分

中级技术员

 楼主| 发表于 2017-7-14 16:27 | 显示全部楼层 |返回版面
第一篇:看门狗

学习TI msp430时候第一句话是关狗:WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT

到了学习DSP时,第一句话依然是关狗:DisableDog();              // Disable the watchdog

这看门狗招惹谁了,就这么不受待见,一上来就要给他关掉,杀死。

虽然,我也是杀狗者,但是,今天我要为其平反,看门狗你的用途很大,只是我们渣渣现在用不到,哈哈哈

看门狗:通过名字就可以猜出来他的功能就是看门,监视,时时刻刻监视程序是否跑飞,在工业中,一旦程序跑飞,产生的未知结果可能对设备造成致命性地损坏,故需要时时刻刻地监视其状态,让设备运行在已知的状态下,这基本就是看门狗的功能介绍。


看门狗的工作原理:其实他是一个计数器,对外部有源晶振或者无源晶振进行计数,故他可以不受系统时钟的干扰,通过上图可以看出,其先对时钟进行512分频,然后,还有一个预分频,接下来就是看门狗使能WDCR(WDDIS),然后送到8位的看门狗计数器进行计数,当计数器溢出时,会产生系统复位信号或者是看门狗中断信号;那如何才可以避免看门狗溢出导致系统复位那???那就喂狗吧,哈哈哈,或者就像我们杀狗者一样残忍,将其杀掉,这样就不会产生复位或者中断信号了,哈哈哈;喂狗,即定时将该计数器进行清零操作,在他溢出之前(饿死之前)对他喂食。那食物是什么那?食物就是一个命令,向WDKEY顺序写入 0x0055 和 0x00AA;

void ServiceDog(void)
{
    EALLOW;
    SysCtrlRegs.WDKEY = 0x0055;
    SysCtrlRegs.WDKEY = 0x00AA;
    EDIS;
}
或者一个XRS复位信号也可以将该计数器清零。
杀狗那?更加简单
只要对WDCR WDDIS位写1即关掉了看门狗模块,但要保证向WDCR WDCHK写101,否则会产生软件看门狗中断或者复位信号,这个位可以用来产生软件看门狗中断或者复位信号。

void DisableDog(void)
{
    EALLOW;
    SysCtrlRegs.WDCR= 0x0068;
    EDIS;
}
通过寄存器SCSR  WDENINT位来使能看门狗复位输出或者是中断输出。
看门狗中断位于group1 channel 8,当要使用看门狗中断时,要将该PIE中断使能


//使能PIE看门狗中断 Group 1 ,interrupt 8
PieCtrlRegs.PIECTRL.bit.ENPIE = 1;                //PIE使能
PieCtrlRegs.PIEIER1.bit.INTx8 = 1;                //使能看门狗中断,第1组  第8中断
IER |= M_INT1;
EINT;                //开总中断

以上就是看门狗部分的一些理解。

In a word,要是你想要保留看门狗,那就定时给他喂食,否则他会乱叫,疯狗乱咬人;要么就干脆给他杀掉,免得麻烦。

                                                                                                                                                                       ----smtudou

                                                                                                                                                                          2017/7/14



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册 手机登录

x
     

589

主题

2万

帖子

6万

积分

版主

发表于 2017-7-14 23:48 | 显示全部楼层 |返回版面
感谢分享   还望继续分享
欢迎进入【TI DSP 论坛】 & 【DSP 技术】
          TI忠诚粉丝!
     

589

主题

2万

帖子

6万

积分

版主

发表于 2017-7-17 19:11 | 显示全部楼层 |返回版面
看门狗  看来很多人不喜欢啊
     

4

主题

37

帖子

161

积分

中级技术员

 楼主| 发表于 2017-7-18 07:11 | 显示全部楼层 |返回版面
zhangmangui 发表于 2017-7-17 19:11
看门狗  看来很多人不喜欢啊

反正我现在不是太喜欢哈~
     

4

主题

37

帖子

161

积分

中级技术员

 楼主| 发表于 2017-10-25 17:13 | 显示全部楼层 |返回版面
I am back!




  最近这段时间一直没有更新,一来是忙着看论文准备开题,二来是各种杂事。。。。。总之就是把这件事情荒芜了,接下来要把他捡起来,因为我发现虽然也学了好多的东西,但是不系统,特别是在开始着手来编写项目的程序时,发现其实好多的东西自己并没有学的很精,故借着这个机会,把dsp系统地过一遍。先立几个flag,那就是在一个半月到两个月完成对DSP的小结,年前完成小论文,投出去(这个必须搞定),以及画好FPGA的板子并争取放假前调通,过完年回来开始FPGA的学习,负重前行,加油!

  对于DSP进行小结的一个大致安排是:前面将时钟总结过了,接下来以我学的顺序开始。1、GPIO。2、中断。3、epwm。4、ADC。5、SCI。以及接下来要学习的其他模块。

                                                                                                                                                                                             smtudou

                                                                                                                                                                                              2017/10/25




     

4

主题

37

帖子

161

积分

中级技术员

 楼主| 发表于 2017-10-25 22:18 | 显示全部楼层 |返回版面
一、GPIO

  刚开始学习单片机时就是从点灯开始,很清晰地记得那时候还是看天翔大哥的视频来跑流水灯,那时候还是大二,现在已是研二,时间似流水。

  要想点灯,还是要对I/O口进行控制,51好像就是直接P1^0 = 1,即可置高,而在越高级的单片机中,需要配置的东西就比较多了,我记得32中就需要配置I/O口的方向,速度,以及是否上拉等等,在DSP这么高级的东西需要配置的东西也不少。

  首先看下GPIO的一个描述:
General  purpose  input / output,即通用输入/输出。不想增加芯片的引脚数,又想增加芯片的功能,那不可避免地要用到引脚的复用功能。功能复用的引脚可以通过MUX寄存器来进行功能的选择,如果选择为普通的数字I/O功能,可以通过GPXDIR寄存器来控制引脚的方向,你也可以通过GPXQSELn 和GPACTRL 和GPBCTRL寄存器配置采样窗的宽度进而去除输入信号的一些高频噪声。28335共有3个32位的I/O接口,PortA Contain GPIO0---GPIO31;PortB contain GPIO32---GPIO63;PortC contain GPIO64---GPIO87。

接下来看下他的一个内部结构图,每一组的内部结构图不是太一致,不过整体的结构类似。如下



  分析这个信号的一个流向:

  INPUT: 首先正常工作时,XRS为高电平,不影响输入输出,输入过来先经过一个GPAPUD,对其进行上拉或者不上拉,然后通过GPASEL1/2对其配置成同步或异步模式;同步模式时可以选择采样窗的大小,这样可以有效地去除干扰,然后就送到了内部,这时候通过GPAMUX1/2将其送往不同的外设模块。与此同时,GPIO0-27可以配置成外部中断1/2,从而触发中断。

  OUTPUT: 正常工作时,XRS高电平,故输出只由GPAMUX1/2选择不同的外设模块,进而将其数据输出。


  输入状态时,如何有效滤除干扰??采用采样窗,他的一个工作原理如下:

  这个采样窗可以用在普通的I/O口和外设的输入引脚,同时外设的输入引脚也可以配置成异步输入。配置成异步输入的模块如下:SCI、SPI、eCAN、I2C、以及ePWM模块的TZ1--6信号。当采样异步输入时,此时采样窗无效。一般引脚复位后默认的量化模式是所有的引脚和SYSCLKOUT同步。


过程:首先由GPXCTRL寄存器中的QUALPRDn来决定采样周期sample period。QUALPRDn是一个八位的数据,最大可以是2^8=256。当数据为零时,此时采样周期为Tsysclkout;当数据不为零时,此时采样周期=2*GPXCTRL[QUALPRDn]*Tsysclkout。

接下来由GPAQSEL1/2来确定采样窗的大小,采样窗即采样几次,共三张选择,可以将图1,采样1/3/6次;当采样3次时,整个采样窗的宽度是2 × 2 × GPxCTRL[QUALPRDn] × TSYSCLKOUT。当采样6次时,采样窗的宽度是5 × 2 × GPxCTRL[QUALPRDn] × TSYSCLKOUT;

如上例,设置采样窗宽度为SYSCLKOUT cycle * 2 * QUALPRD) * 5。此时只有当六次采样都是同一电平时,此时最后才会输出这一电平,当中间有一个毛刺时,该毛刺的宽度不超过采样窗的宽度就被舍弃,认为该信号无效。那是不是这个采样窗的宽度越大越好那???个人觉得并不是,此时导致经过采样窗的信号已经滞后于原始信号采样窗宽度的时间,对于一些实时性要求比较高的系统,这个采样窗的设计一定要仔细。



这接下来就是一个gpio的一个操作步骤:
1、通过GPxMUX寄存器确定引脚的功能。是作为普通I/O还是作为某种外设引脚。

2、若是作为普通I/O,通过GPXDIR来设定引脚方向,是作为输入还是输出。

3、若是作为输入,考虑下是否需要上拉。引脚默认情况下是上拉的,除了ePWM模块未使能。

4、以及考虑采样窗的宽度。

5、最后考虑是否需要产生外部中断,注意:只有PortA 和 PortB可以产生外部中断。GPIO0--31可以产生XINT1/2以及XNMI;GPIO32--63可以产生XINT3/4/5/6/7。



程序部分:在DSP2833x_headers文件夹下的include文件下,可以看到DSP2833x_Gpio.h,在该头文件中对一些寄存器进行了定义,可以看到其实是通过结构体来对寄存器中的某些位进行索引,感觉和32的库函数有点相似,不过感觉没有32的简洁。


extern volatile struct GPIO_CTRL_REGS GpioCtrlRegs;
extern volatile struct GPIO_DATA_REGS GpioDataRegs;
extern volatile struct GPIO_INT_REGS GpioIntRegs;

这是我们经常需要用到的几个  通过GpioCtrlRegs对GPIO的一些控制寄存器进行配置操作;通过GpioDataRegs对GPIO的数据寄存器操作;通过GpioIntRegs对中断寄存器进行操作。
例如我们经常用到的一句话的解释
GpioCtrlRegs.GPAMUX1.bit.GPIO0 = 0X00;                //GPIO功能
他是如何实现该功能的那??其实就是层层控制,有点像中国的分封制
其实GpioCtrlRegs代表一个结构体,如下所示:
struct GPIO_CTRL_REGS {
    union  GPACTRL_REG  GPACTRL;   // GPIO A Control Register (GPIO0 to 31)

    //
    // GPIO A Qualifier Select 1 Register (GPIO0 to 15)
    //
    union  GPA1_REG     GPAQSEL1;

    //
    // GPIO A Qualifier Select 2 Register (GPIO16 to 31)
    //
    union  GPA2_REG     GPAQSEL2;

    //
    // GPIO A Mux 1 Register (GPIO0 to 15)
    //
    union  GPA1_REG     GPAMUX1;

    //
    // GPIO A Mux 2 Register (GPIO16 to 31)
    //
    union  GPA2_REG     GPAMUX2;

    union  GPADAT_REG   GPADIR;    // GPIO A Direction Register (GPIO0 to 31)

    //
    // GPIO A Pull Up Disable Register (GPIO0 to 31)
    //
    union  GPADAT_REG   GPAPUD;

    Uint32              rsvd1;
    union  GPBCTRL_REG  GPBCTRL;   // GPIO B Control Register (GPIO32 to 63)

    //
    // GPIO B Qualifier Select 1 Register (GPIO32 to 47)
    //
    union  GPB1_REG     GPBQSEL1;

    //
    // GPIO B Qualifier Select 2 Register (GPIO48 to 63)
    //
    union  GPB2_REG     GPBQSEL2;  

    union  GPB1_REG     GPBMUX1;   // GPIO B Mux 1 Register (GPIO32 to 47)
    union  GPB2_REG     GPBMUX2;   // GPIO B Mux 2 Register (GPIO48 to 63)
    union  GPBDAT_REG   GPBDIR;    // GPIO B Direction Register (GPIO32 to 63)

    //
    // GPIO B Pull Up Disable Register (GPIO32 to 63)
    //
    union  GPBDAT_REG   GPBPUD;

    Uint16              rsvd2[8];
    union  GPC1_REG     GPCMUX1;   // GPIO C Mux 1 Register (GPIO64 to 79)
    union  GPC2_REG     GPCMUX2;   // GPIO C Mux 2 Register (GPIO80 to 95)
    union  GPCDAT_REG   GPCDIR;    // GPIO C Direction Register (GPIO64 to 95)

    //
    // GPIO C Pull Up Disable Register (GPIO64 to 95)
    //
    union  GPCDAT_REG   GPCPUD;
};




union GPA1_REG {
    Uint32              all;
    struct GPA1_BITS    bit;
};


struct GPA1_BITS {             // bits   description
    Uint16 GPIO0:2;            // 1:0    GPIO0
    Uint16 GPIO1:2;            // 3:2    GPIO1
    Uint16 GPIO2:2;            // 5:4    GPIO2
    Uint16 GPIO3:2;            // 7:6    GPIO3
    Uint16 GPIO4:2;            // 9:8    GPIO4
    Uint16 GPIO5:2;            // 11:10  GPIO5
    Uint16 GPIO6:2;            // 13:12  GPIO6
    Uint16 GPIO7:2;            // 15:14  GPIO7
    Uint16 GPIO8:2;            // 17:16  GPIO8
    Uint16 GPIO9:2;            // 19:18  GPIO9
    Uint16 GPIO10:2;           // 21:20  GPIO10
    Uint16 GPIO11:2;           // 23:22  GPIO11
    Uint16 GPIO12:2;           // 25:24  GPIO12
    Uint16 GPIO13:2;           // 27:26  GPIO13
    Uint16 GPIO14:2;           // 29:28  GPIO14
    Uint16 GPIO15:2;           // 31:30  GPIO15
};

就是结构体的一个嵌套,TI把它写好了,咱们只需要进行调用就行了,但是,前提是,这个索引是没有错误的,当你用这种方法时就已经默认了TI提供的.H文件无错,不过基本不会有错的哈~~~


接下来就是一个控制LED的C文件以及H文件

void LED_Init(void)
{
        EALLOW;                        //将引脚设置为通用I/O    并且设置为输出
        GpioCtrlRegs.GPAMUX1.bit.GPIO0 = 0X00;                //GPIO功能
        GpioCtrlRegs.GPAMUX1.bit.GPIO1 = 0X00;                //GPIO功能
        GpioCtrlRegs.GPAMUX1.bit.GPIO2 = 0X00;                //GPIO功能


        GpioCtrlRegs.GPADIR.bit.GPIO0 = 1;                        //设置为输出
        GpioCtrlRegs.GPADIR.bit.GPIO1 = 1;                        //设置为输出
        GpioCtrlRegs.GPADIR.bit.GPIO2 = 1;                        //设置为输出
        EDIS;

        /*初始化为高电平    即所有LED均灭*/
        GpioDataRegs.GPASET.bit.GPIO0 = 1;                        //置高
        GpioDataRegs.GPASET.bit.GPIO1 = 1;                        //置高
        GpioDataRegs.GPASET.bit.GPIO2 = 1;                        //置高

}




#ifndef LED_H
#define LED_H

#define LED2_ON GpioDataRegs.GPASET.bit.GPIO0
#define LED3_ON GpioDataRegs.GPASET.bit.GPIO1
#define LED4_ON GpioDataRegs.GPASET.bit.GPIO2

#define LED2_OFF GpioDataRegs.GPACLEAR.bit.GPIO0
#define LED3_OFF GpioDataRegs.GPACLEAR.bit.GPIO1
#define LED4_OFF GpioDataRegs.GPACLEAR.bit.GPIO2

#define LED2_TOGGLE GpioDataRegs.GPATOGGLE.bit.GPIO0
#define LED3_TOGGLE GpioDataRegs.GPATOGGLE.bit.GPIO1
#define LED4_TOGGLE GpioDataRegs.GPATOGGLE.bit.GPIO2


void LED_Init(void);


#endif  // end of LED_H definition


                                                                                                                                                        smtudou

                                                                                                                                                          2017/10/25











本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册 手机登录

x
     

589

主题

2万

帖子

6万

积分

版主

发表于 2017-10-25 22:40 | 显示全部楼层 |返回版面
有更新了  帅
欢迎进入【TI DSP 论坛】 & 【DSP 技术】
          TI忠诚粉丝!
     

4

主题

37

帖子

161

积分

中级技术员

 楼主| 发表于 2017-10-26 09:37 | 显示全部楼层 |返回版面

要向你们大佬看齐,哈哈哈~
     

0

主题

5

帖子

15

积分

实习生

发表于 2017-10-27 10:35 | 显示全部楼层 |返回版面
好贴
     

4

主题

37

帖子

161

积分

中级技术员

 楼主| 发表于 2017-10-27 21:33 | 显示全部楼层 |返回版面
二、PIE
  昨天晚上写的帖子,谁知写着写着过了十一点,好像是十一点零二分,点提交,然后出错,返回上一页,一些图片和汉字就没了,好像系统提示晚上11点到早上7点不能发帖。当时真的是崩溃,就瘫坐在椅子上面,洗漱都不想去了,心里感觉真的不想重新写了,就好像自己酝酿好久写出来的作文,突然丢了,就真的没有力气写了,平静了一天,今天晚上给他补补吧!!!下次提交之前一定要把编辑好的东西给另存下,一定要!!!其实之前一直有这个习惯的,昨天就给忘记了,也许写的太尽兴了吧。经过这次痛的领悟,以后肯定记得住。
  前面小结了GPIO的使用,接下来不可避免地就要开始总结中断的使用。在说中断之前先扯点别的,在我个人看来,一个芯片的中断能力可以间接地表明这个芯片的强大。51单片机好像中断优先级没有几个,到了32,中断的优先级好像可以嵌套到几十级,而到了DSP,就更加强到银河系去了。先来看下他可以处理中断的数目--12*8=96个,感觉要甩51几条街了。

  言归正传,再来说说,DSP他是如何管理这么多的中断的,可以做一个类比,将DSP看做是一个company,那DSP内核就是最高司令部,所有的命令要从这里发出,那就相当于是CEO,而外设中断就是所有大的小的要传递给他并且要由他来进行裁决的一些决定,因为CEO的精力也有限,那肯定不能每来一个决定,就不分轻重缓急地处理,一定是按照事情的轻重缓急来进行处理,这样才能保证公司的正常、高效运转,那如何去做那???分级设立各个部门管理人员,类似于中国的分封制一样,由他们先将上传进来的东西一级一级地进行审查,分好轻重缓急的顺序再递交给大BOSS,这样就可以大大滴提高公司的效率,明白人应该都知道这个道理,我只是借他对接下来要说的PIE进行一个引入,其实PIE的一个作用就类似于公司分级的各个高官一样,对产生的中断信号按照重要程度进行一个整理,最后传给内核。

  真正地言归正传,PIE即Peripheral Interrupt Expansion,将众多的中断源复用到很少的中断输入,准确地说是将96个中断复用成12个中断源,大大减轻了内核的压力。这个PIE模块将8个中断归为一组,每一组和一个中断线连接,这样的中断线共有12根。这样就可以知道进入PIE的有96个中断,出去时就成了12个。

  在DSP中,这个分级如下:CPU级(公司高层)、PIE级(公司各级部门)、外设级(公司底层员工)。如下图所示



这样可以直观地看出,每一个外设中断请求必须经过三级中断允许,cpu最终才会执行该外设中断。

外设级中断:

   首先,当某外设产生中断时,其中断标志寄存器IF的相应位会置1,如果中断使能寄存器IE也被置1,则外设产生的中断会被送到PIE控制器。

PIE级中断:

  将8路外设或外部中断复用为一个CPU中断,这些中断分12组,每一组各有一个中断信号连接至CPU。结构图如下所示:

例如:PIE group1 被连接到了INT1;PIE group12 被连接到了INT12。其他连接到cpu级的中断都没有复用,例如:非屏蔽中断,直接将中断请求连接到了CPU。对于复用的中断源,当接收到外设级的中断后,首先PIEIFRx.y置1,若是该路中断使能即PIEIERx.y置1,此时查看对应的PIEACK 是否为0,若是0,则直接将其置1表示PIE中断产生,否则说明上一个中断未执行完毕,等待其执行完毕后将其中断标志位PIEACK置1. 若PIEACK置1,就将该中断信号送到CPU级。那如何才能将该PIEACK标志位清零那???通过软件对其写1就可以对其清零,一般是在进入中断后进行该操作,不影响接下来中断的产生。

CPU级中断:

  一旦接受到从PIE级过来的中断信号,对应通道的IFRx会置1,若是该路的IERx置1(即该中断使能),并且全局中断屏蔽位INTM置0时,此时就会将从下级一步步传上来的中断信号送到CPU,CPU就会对该中断信号做出响应,IFRx=0;IERx=0;INTM=1。从PIE获取中断向量,清除PIEIFRx.y,并且跳转到该中断服务程序去。在中断函数中要做的:用户的服务函数写1到PIEACK对该标志位进行清0重新使能中断INTM=0

以下是他官方给出的一个中断响应的流程图

严格按照这个步骤来进行中断的配置就一定可以实现想要的一个中断响应。

下面是一个芯片的一个中断源

根据上面图片中的框图可以清晰地看出,下面方框中的中断是直接连接到CPU级的,而没有经过PIE模块。可以看出外部中断1-7全部连在了PIE模块上面。在GPIO那一节说过GPIO0-31可以配置成XINT1/2以及非屏蔽中断,而GPIO32-63只能配置成XINT3/4/5/6/7。

中断向量表:

中断向量表:dsp外设中断扩展控制器PIE一共支持96个中断,每个中断对应的中断向量放到RAM中,从而构成了中断向量表。所谓中断向量表即为中断服务程序入口地址,在响应中断时,CPU直接从中断向量表中获取相应的中断向量,用户可以修改中断向量表。

例如:
        EALLOW;
        PieVectTable.XINT2 = &Exint2_Isr;
        EDIS;
以上就是将外部中断2的入口地址改为了Exint2_Isr,当外部中断产生时,直接跳转都该函数处,执行该中断函数。
下面是中断向量表
由上表可以看出,DSP CPU中断优先级有19级,并且每一个中断线INT1--12上面对于优先级又有一个划分,所以,DSP真的是太过强大。

向量的映射:即将所有的外设以及外部中断映射到PIE模块上去,每一个中断对应一个通道。映射表如下所示
由这个表可以快速地找到每一个中断所对应的通道,进而对其使能。


程序例子:

//描述:将GPIO13配置成外部中断2,下降沿触发中断,在中断服务函数中执行中断服务函数。


1、外设级配置,即使能该外设中断,或者外部中断。
//设置GPIO13作为外部中断2输入引脚
void Xint2_GpioInt(void)
{
        EALLOW;
        GpioCtrlRegs.GPAMUX1.bit.GPIO13 = 0;                //普通I/O口
        GpioCtrlRegs.GPADIR.bit.GPIO13 = 0;                        //作为输入引脚
        GpioCtrlRegs.GPAQSEL1.bit.GPIO13 = 2;                //输入信号和sysclkout同步,每5个时钟周期都更新一次,包含6次采样
        GpioCtrlRegs.GPACTRL.bit.QUALPRD1 = 15;                //采样窗的大小为1us
        GpioIntRegs.GPIOXINT2SEL.bit.GPIOSEL = 13;                //GPIO13作为外部中断2的中断源
        EDIS;

        XIntruptRegs.XINT2CR.bit.POLARITY = 0;                //中断产生在下降沿
        XIntruptRegs.XINT2CR.bit.ENABLE = 1;                        //使能XINT2中断


}
使能XIINT2中断,这时候,当产生一个外部中断2时,此时外设级就会向PIE级发出中断请求,此时对应通道的的PIEIFG就会置位,在这里就是INT1.5的标志位会置位。

2、对PIE和CPU级中断进行配置,即通过映射表来使能外设所对应通道的中断。
        InitPieCtrl();                        //初始化PIE模块至一个可知状态
        DINT;                //关总中断
        IER = 0X0000;
        IFR = 0X0000;
        InitPieVectTable();                        //初始化中断向量表
        EALLOW;
        PieVectTable.XINT2 = &Exint2_Isr;
        EDIS;
        PieCtrlRegs.PIECTRL.bit.ENPIE = 1;                //Enable PIE BLOCK
        PieCtrlRegs.PIEIER1.bit.INTx5 = 1;                //打开XINT2相应的通道
        IER |= M_INT1;                //开cpu级中断

        //用户自定义函数初始化
        Xint2_GpioInt();                        //设置GPIO13作为外部中断2引脚     下降沿有效

        EINT;                //开总中断                       其实EINT即为INTM=0;在程序中可以看到    #define  EINT   asm(" clrc INTM")

以上程序更改了中断的入口地址,并且使能了外部中断2对应的PIE级中断和CPU级中断,由映射表可以看出XINT2对应于INT1.5,所以PIE级使能该通道,CPU级使能INT1。这样三级中断全部使能,当有中断产生时,该中断请求就会直接被送至CPU,CPU响应该中断就会直接跳转到新的程序入口地址&Exint2_Isr,执行中断服务程序。


中断服务函数:
//外部中断2服务函数
interrupt void Exint2_Isr(void)
{

        DELAY_US(5000);
        if(GpioDataRegs.GPADAT.bit.GPIO13 == 0)
        {
                         //在这里编写用户服务函数。
        }
       PieCtrlRegs.PIEACK.bit.ACK1 = 1;                //清除中断标志位
       EINT;
}
进中断后,将PIE级的中断标志位清除,以便接下来的中断可以进入。这个标志位一定要自己手动清除。
一般在中断函数里面要做的是:1、清除外设级中断标志位
                                             2、清除PIE级响应标志位
                                             3、开总中断
                                             4、编写用户服务函数

一点疑问:就是在CPU响应中断时,中断流程图中的Stage H  
CPU responds
IFRx=0, IERx=0
INTM=1, EALLOW=0
Context Save performed
此时,将IERx置0了,但是在中断服务函数里面并没有再将该通道所对应的CPU级中断打开,但是可以正常进入中断。这个地方是自己之前没有在意的,这次总结又看用户指南看到这个地方,还请大神们指点。
终于写完了,感觉第二遍没有第一遍的那个feel,总觉得不如第一遍好,哈哈哈
                                                                                                                                                  smtudou
                                                                                                                                                  2017/10/27

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册 手机登录

x
     

4

主题

37

帖子

161

积分

中级技术员

 楼主| 发表于 2017-10-30 22:00 | 显示全部楼层 |返回版面
三、定时器

    前面总结了时钟、GPIO、中断、接下来就真的该说说定时器了,真的是千呼万唤始出来,想在时钟的那个章节就小结下,但是由于没有总结中断,然而定时器最最常用的就是定时器中断了,所以放在了中断之后来说。闲话少说,开始小结定时器。

    定时器:通过名字就知其主要功能是定时。

    在28335里面有三个定时器,分别是Timer0/1/2,其中Timer0/1是为用户所用的;然而Timer2主要是保留为系统实时操作系统来用的额,当你不使用实时操作系统时,Timer2也可以拿来给用户使用。

    下面就是定时器的一个结构图


系统定时器的一个工作过程如下:

     当TCR寄存器第四位置0时,此时启动CPU的定时器对系统时钟SYSCLKOUT进行计数,系统时钟首先经过一个16位的分频器PSCH:PSC,当计够PSCH:PSC个系统时钟时,借位,此时,计数器TIMH:TIM减一并且将TDDRH:TDDR重载到PSCH:PSC中,当最后计数器TIMH:TIM为0时,此时会产生中断信号,并且32位的计数寄存器TIMH:TIM会从PRDH:PRD中自动重载数据。当然,Reset信号和定时器重载信号也可以使分频器和计数器重载。


定时器中断:

三个定时器分别对应不同的中断,Timer0中断通过PIE模块连接到cpu,通过PIE中断映射表可以知道其连接到INT1,Timer1和XINT13中断复用到CPU的INT13,Timer2中断连接到CPU的INT14。如下图所示:

那问题来了???默认状态下是那个中断连接到了INT13哪?来看XNMICR寄存器,该寄存器的Select位决定哪个中断作为进INT13的中断源,看寄存器可以知道默认情况下,是将Timer1作为INT13的中断源,如下所示:





程序:



定时器0的初始化程序:

// This function initializes the timer0 to the period
// specified by the "Freq" and "Period" parameters. The "Freq" is entered as
// "MHz" and the period in "uSeconds". The timer is held in the start state
// after configuration.
void Timer0_Init(Uint32 Freq,Uint32 Period)
{
        Uint32 temp;
        temp = Freq * Period;

        CpuTimer0.RegsAddr = &CpuTimer0Regs;
        CpuTimer0Regs.PRD.all  = temp;                //装载计数值     注意:此时计数寄存器没有分高16位TPRD和低16位PRD,而是:此时代表的是整个32位的计数器

    CpuTimer0Regs.TPR.all  = 0;
    CpuTimer0Regs.TPRH.all = 0;                //计数周期为1个sysclock,相当于是1分频

    // Make sure timer is stopped
    CpuTimer0Regs.TCR.bit.TSS = 1;                //关定时器

    // Reload all counter register with period value
    CpuTimer0Regs.TCR.bit.TRB = 1;                //重新装载给定值

    CpuTimer0Regs.TCR.bit.FREE = 1;
    CpuTimer0Regs.TCR.bit.SOFT = 1;                //自由运行模式

    CpuTimer0Regs.TCR.bit.TIE = 1;                //开定时器中断

    EALLOW;
    PieVectTable.TINT0 = &Timer0_Isr;                //更新中断向量表
    EDIS;

    PieCtrlRegs.PIEIER1.bit.INTx7 = 1;                //PIE中断使能       TIM0---PIE G1  C7           //这句话是定时器0所特有的,因为只有timer0连到了PIE模块
    IER |= M_INT1;                        //开CPU级中断
    EINT;                        //开总中断                                                                                            

    CpuTimer0Regs.TCR.bit.TSS = 0;                //开定时器0

    // Reset interrupt counters
    CpuTimer0.InterruptCount = 0;

}


定时器1的初始化程序:
void Timer1_Init(Uint32 Freq,Uint32 Period)
{
        Uint32 temp;
        temp = Freq * Period;


        CpuTimer1.RegsAddr = &CpuTimer1Regs;
        CpuTimer1Regs.PRD.all = temp;                                //装载计数值


        CpuTimer1Regs.TPR.all = 0;
        CpuTimer1Regs.TPRH.all = 0;                        //不分频,计数频率是SYSCLK


        // Make sure timer is stopped
        CpuTimer1Regs.TCR.bit.TSS = 1;                //关定时器1


        // Reload all counter register with period value
        CpuTimer1Regs.TCR.bit.TRB = 1;                //重新装载给定值


        CpuTimer1Regs.TCR.bit.FREE =1;
        CpuTimer1Regs.TCR.bit.SOFT =1;                //自由运行模式


        CpuTimer1Regs.TCR.bit.TIE = 1;                //开定时器中断


        EALLOW;
        PieVectTable.XINT13 = &Timer1_Isr;        //将定时器1中断服务函数的地址存放在相应的向量地址中
        EDIS;


        //TIMER1 的中断是直接连到CPU的,没有经过PIE模块
        IER |= M_INT13;                //使能CPU的INT13中断,timer1的中断信号直接连接到了INT13上
        EINT;                        //开总中断


        CpuTimer1Regs.TCR.bit.TSS = 0;                //开定时器1


        // Reset interrupt counters
        CpuTimer1.InterruptCount = 0;


}



定时器2的初始化程序:
void Timer2_Init(Uint32 Freq,Uint32 Period)
{
        Uint32 temp;
        temp = Freq * Period;


        CpuTimer2.RegsAddr = &CpuTimer2Regs;
        CpuTimer2Regs.PRD.all = temp;                                //装载计数值


        CpuTimer2Regs.TPR.all = 0;
        CpuTimer2Regs.TPRH.all = 0;                        //不分频,计数频率是SYSCLK


        // Make sure timer is stopped
        CpuTimer2Regs.TCR.bit.TSS = 1;                //关定时器2


        // Reload all counter register with period value
        CpuTimer2Regs.TCR.bit.TRB = 1;                //重新装载给定值


        CpuTimer2Regs.TCR.bit.FREE =1;
        CpuTimer2Regs.TCR.bit.SOFT =1;                //自由运行模式


        CpuTimer2Regs.TCR.bit.TIE = 1;                //开定时器中断


        EALLOW;
        PieVectTable.TINT2 = &Timer2_Isr;        //将定时器1中断服务函数的地址存放在相应的向量地址中
        EDIS;


        //TIMER2 的中断是直接连到CPU的,没有经过PIE模块
        IER |= M_INT14;                //使能CPU的INT14中断,timer2的中断信号直接连接到了INT14上
        EINT;                        //开总中断


        CpuTimer2Regs.TCR.bit.TSS = 0;                //开定时器1


        // Reset interrupt counters
        CpuTimer2.InterruptCount = 0;


}



相应的中断函数如下:

interrupt void Timer0_Isr(void)
{
        CpuTimer0.InterruptCount++;
        PieCtrlRegs.PIEACK.bit.ACK1 = 1;                //写1清除相应的中断标志位
}


interrupt void Timer1_Isr(void)
{
        CpuTimer1.InterruptCount++;

}

interrupt void Timer2_Isr(void)
{
        CpuTimer2.InterruptCount++;

}

定时器0中断经过了PIE模块,故在中断函数里面要将该标志位清除,以便可以响应接下来的中断。

至此,系统内部的资源基本总结完了,接下来就是各个外设了,加油哈!!!


                                                                                                                                                              smtudou

                                                                                                                                                               2017/10/30


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册 手机登录

x
     

0

主题

2

帖子

6

积分

实习生

发表于 2017-11-1 12:52 | 显示全部楼层 |返回版面
学习!非常感谢您的分享,谢谢!
     

1

主题

4

帖子

12

积分

实习生

发表于 2017-11-1 20:11 | 显示全部楼层 |返回版面
朋友 ,我也不会创建工程,我照着你的做了,但是还是有50个警告,能不能帮帮我
     

1

主题

4

帖子

12

积分

实习生

发表于 2017-11-1 20:34 | 显示全部楼层 |返回版面
我又重新弄了一遍,弄好了,不过能不能加你一个好友,想向你请教东西
     

1

主题

19

帖子

57

积分

初级技术员

发表于 2017-11-2 15:30 | 显示全部楼层 |返回版面
     

4

主题

37

帖子

161

积分

中级技术员

 楼主| 发表于 2017-11-8 20:56 | 显示全部楼层 |返回版面
zhongduanwuxiao 发表于 2017-11-1 20:34
我又重新弄了一遍,弄好了,不过能不能加你一个好友,想向你请教东西

不好意思哈,这几天忙着开题,一直没登~
     

4

主题

37

帖子

161

积分

中级技术员

 楼主| 发表于 2017-11-8 20:57 | 显示全部楼层 |返回版面
zhongduanwuxiao 发表于 2017-11-1 20:34
我又重新弄了一遍,弄好了,不过能不能加你一个好友,想向你请教东西

什么方面的,我不一定会的,你说下你的问题
     

4

主题

37

帖子

161

积分

中级技术员

 楼主| 发表于 2017-11-8 20:57 | 显示全部楼层 |返回版面
cnqg 发表于 2017-11-1 12:52
学习!非常感谢您的分享,谢谢!

一起进步~
     

1

主题

6

帖子

18

积分

实习生

发表于 2017-11-10 10:44 | 显示全部楼层 |返回版面
apple武 发表于 2017-10-30 22:00
三、定时器
    前面总结了时钟、GPIO、中断、接下来就真的该说说定时器了,真的是千呼万唤始出来,想在时 ...

大哥,小弟我最近实习也在用dsp,能不能加个好友讨论一下呢
您需要登录后才可以回帖 登录 | 注册 手机登录

本版积分规则

关闭

热门推荐上一条 /2 下一条

分享 快速回复 返回顶部 返回列表