dsp之旅
本帖最后由 apple武 于 2017-7-14 15:28 编辑今年春上,开始学习DSP,中间各种烦事,学习了有半个月的时间,然后就被各种杂事中断了,暑假开始,继续DSP之旅。
在日志里面记录了,通过CCS6如何创建工程,别笑,那时候,创建一个新工程我用了两天,跟着一些书籍进行配置,最后竟然还是会出错,然后就借助网上的大神的帖子,顺利创建新工程,刚又看了下写那篇日志的时间,是4/5,这之间我都做了些什么那??????一直心心念着学习DSP,现在发现自己脑袋里空空如也,没有为之付出很多时间、精力,外在原因有,重要的是自己内在的原因,有时候,一件事情,你可以**一天、一周,一个月,但是,慢慢地你的热度就会下降,最后完全抛到脑后,说的就是我自己。有些事情不是一蹴而就的,需要一步一个脚印向前走,目标一点点地靠近自己,最终目标会如期而至,不用着急,慢慢来,每天进步一点点。
开篇时钟在一个系统中的作用真可谓是心脏之于人,重要性不必说,一个系统若是时钟就不稳定,那这个系统几乎谈不上可靠,故还是从时钟系统学起。
dsp的时钟源可以来自外部无源晶振,还可以通过有源晶振产生时钟信号作为输入。一般用30M有源晶振作为外部时钟源,一般是30M的时钟通过PLL10倍频到300M,然后再2分频产生150M的系统时钟。
这个PLL配置过程参考官方手册,以及官方给出了时钟配置的程序,直接进行调用就行。
例程就是参考这个步骤来写的,有一点不是太明白的是,最后倍频结束后,分频的时候,当分频为1时,要先进行2分频,然后再进行不分频????
官方例程如下:
voidInitPll(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位置1,当检测到PLLSTS位为1时,表明系统时钟工作在limp mode模式下。需要检查硬件是否有问题,通过向PLLSTS写1,对该位清零,并时钟电路复位,再次上电观察PLLSTS位,重复该过程,直到正常。
考虑到一些外设有可能会需要时钟,DSP专门引出了一个时钟XCLCKOUT为其所用
默认情况下,是将XCLKOUT设置为系统时钟的四分频37.5M,也可以自己设置,最大不超过系统时钟SYSCLKOUT。
这之上的配置过后就可以得到保证系统时时刻刻稳定的时钟了!TI公司又将这个系统时钟分为低速时钟和高速时钟,分别分给不同的外设,之前学习过TI得msp430,这和430里面的架构感觉完全一致,这样可以降低功耗。
为不同的外设提供相应的时钟,低速的外设给你高速的时钟也只能是浪费,并且还有可能速度太快,你跟不上;高速外设提供低速时钟,只能拖后腿,故合适才是最重要的。中庸之道
当有些外设不用的时候,可以将其关闭,以降低功耗,InitSysCtrl()函数中,InitPeripheralClocks()该函数对外设时钟进行初始化,即将一些不必要的外设时钟关闭,以降低功耗。
至此,DSP的时钟部分基本总结完毕,以上只是个人的见解,有不对的地方还望大神指点迷津!dsp之旅,依旧在路上
----smtudou
2017/7/14
第一篇:看门狗
学习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;
}
通过寄存器SCSRWDENINT位来使能看门狗复位输出或者是中断输出。
看门狗中断位于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
https://bbs.21ic.com/data/attachment/forum/201707/14/152750uy4tyert1ptwubtg.png
感谢分享 还望继续分享 看门狗看来很多人不喜欢啊 zhangmangui 发表于 2017-7-17 19:11
看门狗看来很多人不喜欢啊
反正我现在不是太喜欢哈~ I am back!
最近这段时间一直没有更新,一来是忙着看论文准备开题,二来是各种杂事。。。。。总之就是把这件事情荒芜了,接下来要把他捡起来,因为我发现虽然也学了好多的东西,但是不系统,特别是在开始着手来编写项目的程序时,发现其实好多的东西自己并没有学的很精,故借着这个机会,把dsp系统地过一遍。先立几个flag,那就是在一个半月到两个月完成对DSP的小结,年前完成小论文,投出去(这个必须搞定),以及画好FPGA的板子并争取放假前调通,过完年回来开始FPGA的学习,负重前行,加油!
对于DSP进行小结的一个大致安排是:前面将时钟总结过了,接下来以我学的顺序开始。1、GPIO。2、中断。3、epwm。4、ADC。5、SCI。以及接下来要学习的其他模块。https://bbs.21ic.com/data/attachment/forum/201707/14/152750uy4tyert1ptwubtg.png
smtudou
2017/10/25
一、GPIO
刚开始学习单片机时就是从点灯开始,很清晰地记得那时候还是看天翔大哥的视频来跑流水灯,那时候还是大二,现在已是研二,时间似流水。
要想点灯,还是要对I/O口进行控制,51好像就是直接P1^0 = 1,即可置高,而在越高级的单片机中,需要配置的东西就比较多了,我记得32中就需要配置I/O口的方向,速度,以及是否上拉等等,在DSP这么高级的东西需要配置的东西也不少。
首先看下GPIO的一个描述:Generalpurposeinput / 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*Tsysclkout。
接下来由GPAQSEL1/2来确定采样窗的大小,采样窗即采样几次,共三张选择,可以将图1,采样1/3/6次;当采样3次时,整个采样窗的宽度是2 × 2 × GPxCTRL × TSYSCLKOUT。当采样6次时,采样窗的宽度是5 × 2 × GPxCTRL × 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 {
unionGPACTRL_REGGPACTRL; // GPIO A Control Register (GPIO0 to 31)
//
// GPIO A Qualifier Select 1 Register (GPIO0 to 15)
//
unionGPA1_REG GPAQSEL1;
//
// GPIO A Qualifier Select 2 Register (GPIO16 to 31)
//
unionGPA2_REG GPAQSEL2;
//
// GPIO A Mux 1 Register (GPIO0 to 15)
//
unionGPA1_REG GPAMUX1;
//
// GPIO A Mux 2 Register (GPIO16 to 31)
//
unionGPA2_REG GPAMUX2;
unionGPADAT_REG GPADIR; // GPIO A Direction Register (GPIO0 to 31)
//
// GPIO A Pull Up Disable Register (GPIO0 to 31)
//
unionGPADAT_REG GPAPUD;
Uint32 rsvd1;
unionGPBCTRL_REGGPBCTRL; // GPIO B Control Register (GPIO32 to 63)
//
// GPIO B Qualifier Select 1 Register (GPIO32 to 47)
//
unionGPB1_REG GPBQSEL1;
//
// GPIO B Qualifier Select 2 Register (GPIO48 to 63)
//
unionGPB2_REG GPBQSEL2;
unionGPB1_REG GPBMUX1; // GPIO B Mux 1 Register (GPIO32 to 47)
unionGPB2_REG GPBMUX2; // GPIO B Mux 2 Register (GPIO48 to 63)
unionGPBDAT_REG GPBDIR; // GPIO B Direction Register (GPIO32 to 63)
//
// GPIO B Pull Up Disable Register (GPIO32 to 63)
//
unionGPBDAT_REG GPBPUD;
Uint16 rsvd2;
unionGPC1_REG GPCMUX1; // GPIO C Mux 1 Register (GPIO64 to 79)
unionGPC2_REG GPCMUX2; // GPIO C Mux 2 Register (GPIO80 to 95)
unionGPCDAT_REG GPCDIR; // GPIO C Direction Register (GPIO64 to 95)
//
// GPIO C Pull Up Disable Register (GPIO64 to 95)
//
unionGPCDAT_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:10GPIO5
Uint16 GPIO6:2; // 13:12GPIO6
Uint16 GPIO7:2; // 15:14GPIO7
Uint16 GPIO8:2; // 17:16GPIO8
Uint16 GPIO9:2; // 19:18GPIO9
Uint16 GPIO10:2; // 21:20GPIO10
Uint16 GPIO11:2; // 23:22GPIO11
Uint16 GPIO12:2; // 25:24GPIO12
Uint16 GPIO13:2; // 27:26GPIO13
Uint16 GPIO14:2; // 29:28GPIO14
Uint16 GPIO15:2; // 31:30GPIO15
};
就是结构体的一个嵌套,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
https://bbs.21ic.com/data/attachment/forum/201707/14/152750uy4tyert1ptwubtg.png
smtudou
2017/10/25
有更新了帅 zhangmangui 发表于 2017-10-25 22:40
有更新了帅
要向你们大佬看齐,哈哈哈~ 好贴 学习!非常感谢您的分享,谢谢! 朋友 ,我也不会创建工程,我照着你的做了,但是还是有50个警告,能不能帮帮我 我又重新弄了一遍,弄好了,不过能不能加你一个好友,想向你请教东西 zhongduanwuxiao 发表于 2017-11-1 20:34
我又重新弄了一遍,弄好了,不过能不能加你一个好友,想向你请教东西
不好意思哈,这几天忙着开题,一直没登~ zhongduanwuxiao 发表于 2017-11-1 20:34
我又重新弄了一遍,弄好了,不过能不能加你一个好友,想向你请教东西
什么方面的,我不一定会的,你说下你的问题 cnqg 发表于 2017-11-1 12:52
学习!非常感谢您的分享,谢谢!
一起进步~ apple武 发表于 2017-10-30 22:00
三、定时器
前面总结了时钟、GPIO、中断、接下来就真的该说说定时器了,真的是千呼万唤始出来,想在时 ...
大哥,小弟我最近实习也在用dsp,能不能加个好友讨论一下呢 流浪豪 发表于 2017-11-10 10:44
大哥,小弟我最近实习也在用dsp,能不能加个好友讨论一下呢
论坛里面有很多大神的,咱们一起学习,一起进步 十一月份
就做了一件事,那就是准备开题!
好久没有更新,接下来继续进行思路梳理!!!
不可懈怠!!!
页:
[1]
2