DSP实验——TSM320F2812

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Imkiimki/article/details/78159360


DSP开发基础实验

实验目的

  1. 了解DSP开发系统的基本配置;
  2. 熟悉DSP集成开发环境(CCS);
  3. 掌握C语言开发的基本流程;
  4. 熟悉代码调试的基本方法。

    实验内容

新建工程,对工程进行编译、链接,下载到目标板上后进行实时调试。利用代码调试工具跟踪程序的运行。根据.map文件,指出各段在存储器空间的地址,体会Memory Map的设计思想。

实验原理

使用CCS进行DSP开发至少需要以下3种文件:1.c或者.asm程序文件,这是用户写的程序;2.cmd文件,即配置命令文件,其指定了链接器在链接时存放各段的物理地址;3.lib文件,是由芯片厂商提供的运行支持库。TSM320C28x系列对应rts2800_ml.lib.当然除了这3类外,建立项目工程后还会产生.pjt文件,这其实就是一个文本文件,记录了项目中芯片的类型,程序目录,输出目录以及编译器设置等信息。

使用C语言在CCS下开发时,C编译器(Complier)首先将C语言翻译成汇编代码,这一步也会对程序进行一些可选的优化。然后汇编器(Assembler)将汇编代码转换符合公共目标格式COFF的机器码。连接器(Linker).obj文件重定位,根据配置命令文件.cmd指定各段的物理地址,输出DSP可执行文件。

DSP的仿真调试可以使用SimulatorEmulator方式。前者使用计算机的CPU进行仿真,可以实现程序功能的验证,但不能进行外设的模拟和实时性验证。后者是利用仿真器硬件,通过JTAG接口与DSP相连,使用边界扫描技术实时获得芯片的状态,程序是全速运行在DSP上的,因此所见即所得。

扫描二维码关注公众号,回复: 3820017 查看本文章

实验步骤

  1. 设备上电,连接计算机。
  2. 设置开发环境。点击"C2000 Setup"图标,设置硬件仿真器的型号,导入配置文件。
  3. 新建工程。打开CCS C2000,新建工程,填写工程名,选择对应的DSP芯片TMS320C28xx,选择工程路径和输出文件格式。点击"确定"即建立了工程。
  4. 添加工程文件。将实验9例程中的.c文件、.h文件、.cmd文件和.lib文件复制到工程目录下。在project窗口中工程名上右键单击,Add File to Project,添加所有上述文件。
  5. 编译链接。
  6. 下载程序到目标板,进行调试。完成实验内容所要求的工作。

程序设计

第一个实验旨在熟悉CCS的开发环境和DSP开发流程,故而实验程序已经提供,无需改动。

通过对源代码的分析,程序主要干了三件事:1、关闭看门狗;2、在CCS的output窗口输出字符串;3、在存储器中,从一片连续地址取数据,乘以一个增益后存入另一片连续地址。

具体实现如下:

1、关闭看门狗:


1看门模块

2 WDCR寄存器定义

直接对WDCR寄存器的WDDIS位写1,关闭看门狗。在对寄存器操作前还要先执行汇编语句eallow,操作完后再执行汇编语句edis.

2、在output窗口输出字符串:

    puts语句即可实现:

puts("SineWave example started\n");

3.数据的复制:

    首先在头文件中声明了一个结构体IOBuffer,结构体内包含两个长度同为BUFFSIZE的数组input[]和output[],类型为16位int. 然后在main函数中定义了1个该类型的结构体,使用下标遍历整个数组,将input[]中的数值乘以增益系数gian,存入output[]中。这些操作在函数processing()中实现。

这个程序已经足够简单,无需使用框图进一步解释。

实验结果

子程序和数据的地址

子程序入口地址:

装载程序到DSP后,将鼠标停留在C语言编辑器的函数上,会显示函数的入口地址。如图6-1(a)6-1(b),显示了dataIO()processing()的入口地址,分别是0x003F81F50x003F81D8.

3 (a). dataIO()的入口地址 (b). processing()的入口地址

从中还可以看到,dataIO()函数的入口地址比processing()函数大了29,说明processing()的汇编指令至多有29条。而且还说明,链接器在存储器中分配程序空间时,函数的物理地址不一定按照C语言编写时的顺序。实际上对于汇编指令调用函数来说,程序入口地址的顺序并不重要。

存储器地址:

    通过菜单上的View – Watch,打开变量查看窗口。输入变量名currentBuffer.input和currentBuffer.output, 查看变量的值。结果如图6-2(a)和(b),两个变量的地址分别是0x00008480和0x00008500, 他们相差了128,也就是一个数组的长度。结构体中的变量是连续存放的。

4 (a). currentBuffer.input的地址 (b). currentBuffer.output的地址

初始时的内存数据

程序载入到DSP后,直接运行整个程序。使用Graph工具,以图像方式显示数组currentBuffer.inputcurrentBuffer.output中的数据。由于数组的类型是int型,长度为128,所以在GraphView中设置类型为"16bit singned int",长度为128,变量地址填6.1节中所述的物理地址,或者直接填变量名。运行一段时间后,两个数组的内容如图6-3所示:

5 (a).input数组中的数据 (b).output数组中的数据

从整体来看,input数组中的数据是随机无需的,这是因为DSP在上电后,RAM中的数据是随机的。再看output中数据和input数据的关系,将光标都设置在第10点处,input中的数据是3430,而output中的数据是-13720,是前者的-4倍。证明output中的数据是从input中复制的,而且乘以了增益-4.

用探针导入数据后运行

processsing()函数之前的位置放置探针,向currentBuffer.input[]中导入正弦波形。程序中的空函数dataIO()正好提供了这样的位置。因此将探针点设置在dataIO()函数上,然后在File-File I/O中对探针进行设置,包括数据文件、地址、长度等。数据文件就是目录下的sine.dat,地址设为input[]数组的物理地址0x00008480,长度为128d.

6探针位置

7探针设置8导入数据

导入数据后,程序复位,重新运行。刷新Graph窗口中的内容,结果图6-7.

9 (a).从探针导入数据后的input[]数据 (b).运行之后的output[]数据

这时,input数组中的数据已经是正弦波了,而且运行完processing()函数后,数据也复制到了output中。为了进一步验证复制结果,与上节一样,用光标选取两个数组中相同的位置(本来想都取第35点,写报告时才发现手滑取错了TAT'),input中数据是98,output数据大致是其-4倍。由于乘以了负增益,output中整个波形关于x轴反转了。这些现象都验证了程序功能的正确性。

.map文件与.cmd文件

打开debug目录下编译链接后生成的.map文件,查看其内容。.text.data.bss段的起始地址和长度分别如下:

10 .map文件中各段的地址和长度

其中.text段起始地址为0x003f81c5, 长度为0x0adb;.data没有被分配空间,地址和长度都为0;.bss起始地址为0x00000400,但长度为0.

根据TMS320F2812芯片的memory map(图6-9),可知text位于H0 SARAM中,bss段位于M1 SARAM中。

    打开.cmd文件,确实没有定义.data段;.text分配在0x003f8080起始的存储器中,与.print、.cinit段在一起;.bss段为分配在起始地址为0x0400起始的M1 RAM中。

    可见,根据芯片的memory map,.cmd文件指定了各段的存储地址,而编译链接后.map文件详细报告了各段的地址分配。这三者是统一的。

11 Memory map

 

12 cmd文件中指定的段位置

小结

第一个实验的主要目的是为了熟悉CCS开发环境,了解开发流程。由于之前在CCS上开发过430单片机和5000系列 DSP,何况所有的IDE都大同小异,所以这个实验很快就做完了。作为TI自己的软件,CCS的特点在于对芯片的调试支持非常到位,比如可以通过设置探针,导入和导出数据,可以用Graph工具对内存数据作图。而一般的开发工具似乎只能设置断点慢慢调试。

虽然这个实验很简单,但可以分析的问题还是很多的。实验时,我们记录了在用探针导入数据之前和导入之后数组内的数据,以此说明探针的作用;分析了processing()执行后,input[]output[]中数据的关系,证明这个函数实现了数据的复制,并乘以了增益-4;通过比对.map.cmd中各段的地址和长度,将数据对应到了Memory map中的物理存储器,从而对各段数据的存储位置有了直观的认识。

 

 

  1. 任意信号发生器

实验目的

  1. 熟悉DSP硬件开发平台
  2. 熟悉DSP集成开发环境(CCS)
  3. 掌握TMS320F2812的存储器配置表
  4. 学习TMS320F2812的编程开发
  5. 熟悉代码调试的基本方法

    实验内容

分析实验例程,学习DDS的原理,在此基础上对实验程序进行改写,产生线性调频信号:

 

采样时间内包含1024点离散数值。

实验原理

DDS的结构框图如下所示,其中频率控制字控制相位累加器的步进量,根据相位累加器中的数值,从波形查找表中取对应的数值,送入DAC,最后经过低通滤波器滤除高频,就得到了波形的模拟输出。

1 DDS结构框图

    实验箱上的DAC1信号为AD768,位宽16bit,以无符号数表示,0x8000表示0电位。DAC映射到了DSP的地址0x2900, 因此向DAC写数据只要写地址0x2900即可。

    实验箱上8个LED数码管共阴,地址从0x2000开始,0x100递增。写入相应的码段之后,在0x2C00写任意数值,刷新锁存器即可。

实验设计

在这个实验中,没有用到任何外设,因此可以将所有外设时钟关闭。除此之外,设定主时钟锁相环5倍频。这样,外部时钟为30MCPU工作时钟为150M。实验程序的流程图表示如下:

2程序流程图

    其中,波形查找表存储在0x0010 0000起始的内存中,这个是外部的SRAM,使用指针直接操作地址,实现数据存取。

计算波形数据时,使用math.h中的cos()函数,得到的是有符号数,乘以幅度后转换为int型。而DAC接收的是无符号类型,0x0000-2v0x80000v0xFFFF+2v,相当于将一个补码向上整体搬移了0x8000。因此将int型数据转换为DAC的数据,方法如下:

*(DAC1Addr)=(unsignedint)((*(RamAddr+1*i))<<2)+0x8000;

在将int型转换为unsigned int之前,还要先左移几位,防止向上搬移0x8000时溢出。

实验过程

运行实验例程

建立工程后,添加实验所给的例程,编译链接下载到DSP中。例程中实现了正弦波信号的产生,但很不幸的是,运行实验例程,通过示波器观察发现,实验箱上的DAC坏了..

3损坏的DAC输出

如图3,表现为:幅度大于一定值时,输出突然下降一个台阶。为了确认是DAC的问题,在别的实验箱上运行同样的程序,是没有问题的。

所以我们的实验,包括后面的2个实验,用到DAC时,都没有用到满幅度,这样,实际可用的DAC位数是小于16bit的,最大输出幅值也变小了。将正弦波幅度减小4倍后,输出正常。如图4所示,是减小正弦波幅度后的输出,波形正常,但此时峰峰值只有1v了。

4减小幅度,避开DAC故障后的输出

运行例程的价值在于:除了发现DAC故障外,还可以测出1024点输出所需的时间。在例程中,一个正弦波波形由1024点数据组成,测得正弦波频率为1.04626kHz,说明遍历输出一个正弦查找表需要1/1.04626k=9.5579e-4 s, 所以平均输出每个点需要时间:9.5579e-4/ 1024=9.33e-7 s. 无论输出什么波形,这个时间是不会变的。

生成线性调频信号

按照实验要求,1024点的线性调频信号为:

 

分析可知,在2×0.0128=0.0256s时间内,有1024个离散点,因此每个点代表的时间为0.0256 / 1024 = 1/40000s. 换算成离散坐标,表达式如下:

 

从而,生成查找表的C代码就是:

for(i=0;i<1024;i++)

*(RamAddr+i)=(int)((cos(K*Pi*(-512+i)*(-512+i)/N/N)*2048));

使用Python对上述表达式进行了计算并作图,得到的波形如图5(a).

    编译链接程序,下载到DSP上,然后运行程序。在Graph工具中查看起始地址0x0010 0000,长度1024点的数据图形,结果如图5(b)

 

5 (a)仿真结果 (b)实际运行结果

将输出连到示波器上观察,波形仍然正确。图6显示了示波器测量结果,从右边测量结果看,此时的频率是1.042kHz, 周期为960us.

6频率为1.042kHz的线性调频信号

调节线性调频信号周期

在上一节中,已经正确产生了频率为1.042kHz,周期为960us的线性调频信号,但题目要求周期是0.0128×2=0.0256s,即25600us. 增加周期只需要增加每个点的输出时间即可,因此可以对每一个点重复输出数次,调整到所需的周期。25600是960的26.6倍,所以对每个点重复输出27次左右即可。通过实验,发现重复28次即可以调整到所需周期。这样,在while(1)内,程序代码就是如下的形式:

while(--j)

{

*(DAC1Addr)=(unsignedint)((*(RamAddr+1*i))<<2)+0x8000;

}

j=29;

经过周期调整的输出波形如图7,右边的测量结果显示,此时周期是25.4us,已经很接近要求了。

LED数码管显示

在DSP初始化设置后,操作LED数码管显示字符。数码管的操作方法在第3节原理中已经叙述,直接向特定的地址写数据就行了。我们让数码管显示了NJUST.Lys字样,"Lys"是我们指导老师名字的首字母,感谢老师的教导。

7 LED数码管显示

子程序入口地址与.map文件

下载DSP程序后,鼠标悬停可以看到函数的地址。本程序中除了主函数mian()外,还有dispLED()函数。它们的地址分别是:0x0000 0000 和 0x0000002C.

8子程序的入口地址

根据memory map,它们都位于M0 Vector RAM 中。查看.map文件,.text段确实从0x0000 0000开始。这里本来是用于存放中断向量的,但程序没有用的中断,存放函数也无妨。

section page origin length input sections

-------- ---- ---------- ---------- ----------------

.text 0 00000000 000002eb

除了.text、.data、.bss段,实际占据内存长度的还有:

    

.cinit 0 003f8002 0000002d

.reset 0 003fffc0 00000002 DSECT

.stack 1 00000400 00000400 UNINITIALIZED

其中.cinit段是全局变量和静态变量的C初始化记录,.stack是运行时堆栈。

小结

本实验在第1个实验做完后剩下的时间内接着做的,也比较简单。由于实验箱上的DAC损坏,电压最大幅度只能用到1/4,但这影响不大。首先通过运行实验例程,记录了1024点遍历所需的时间,为后面调整信号周期提供参考(5.1节)。第二步是计算出了线性调频信号的离散表达式(5.2节),并使用python进行了简单的仿真。波形仿真正确后下载到DSP运行,使用graph工具查看波形,与仿真结果相同,示波器也观察到了输出波形。根据题目要求的0.0256s周期,我们通过一个样点多次输出的方法,加大了信号周期,并认为每个点重复28次,可以得到比较接近0.0256s的周期。c程序和调整后的波形在(5.3)节中展示了。但由于对波形查找表的取值是在while(1)中循环的,延时由指令数决定,因而不能做到精确的频率控制。更好的方法是在定时器中断函数内读取查找表,这样就可以通过定时器计数值任意设置波形周期了。此外,还操作了LED数码管,正确显示了预设的字符(5.4节)。最后,查看.map文件,指出了子程序在物理存储器中的位置。

  1. DSP数据采集

实验目的

1. 熟悉DSP的软硬件开发平台;

2. 掌握TMS320F2812的ADC外设的使用;

3. 熟悉TMS320F2812的中断的设置;

4. 掌握代码调试的基本方法;

实验内容

建立工程,根据实验例程,使用ADC进行数据的采集,存储以及模拟还原,验证ADC采样频率的设置,查看存储内容。

实验原理

实验的ADC为TMS320F2821内部集成的12bitADC,输入范围为0-3V,最快转换时间80ns。虽然器件是0-3V的,但外围电路对输入电压进行了转换,可以输入负电压。DAC是外部器件AD768,16bit.

ADC的转换触发由定时器控制,因此本实验涉及许多寄存器的操作,主要包括系统时钟和外设两大类,此外,还需要设置相应的中断。

与时钟有关的寄存器有:PLLCR,设置锁相环倍频数;PCLKCR,控制外设时钟的使能;HISPCP和LOSPCP,控制高速外设和低速外设的时钟分配。ADC和定时器都挂在高速外设上。

ADC的功能设置主要用到3个设置寄存器ADCTRL1,ADCTRL2和ADCTRL3,控制转换通道涉及ADCMAC CONV(转换次数设置),ADCHSELSEQn(输入通道选择)。

ADC的时钟链路如下:

1 ADC的时钟链

F2812具有两个事件管理器(EV),分别为EVA和EVB,事件管理器的结构如图2所示。在本实验中,只用到了通用定时器。

通用定时器有4种工作模式,本实验使用连续增计数模式。这样,定时器的中断周期时间就为(TxPR+1)个定时器输入时钟周期。

2事件管理器框图

    之前叙述过,实验程序通过EVA中断触发ADC采样,所以需要用到ADC中断。TMS320F2812使用外设中断扩展单元(PIE),将外设中断信号分组复用到CPU级中断,可以支持96个中断源。PIE的分组结构如图3,EVA中定时器的中断源对应INT1.6.

3 PIE单元分组结构

程序设计

寄存器头文件的三级结构实现

本实验涉及大量寄存器,因而使用了官方的库进行开发。在库中,每个外设或者模块作为一个头文件,寄存器都被抽象为联合体,可以操作整个寄存器,也可以操作特定的位。以外设使能寄存器寄PCLKCR为例,结构体定义如下。首先在PCLKCR_BITS中用位带(bit band)特性,构造了一个成员变量位数不定,内存连续的结构体。然后定义了一个联合体PCLKCR_REG,成员变量是PCLKCR_BITS和全部位all,联合体的成员占据同一内存。这样,在程序中只要使用联合体就可以描述一个寄存器,既可以读写特定的位,也可以整体一次操作。最后,头文件中又把所有寄存器的联合体包含到了一个结构体中。

例1 PCLKCR_REG寄存器的联合体描述

// Peripheral clock control register bit definitions:

struct PCLKCR_BITS{// bits description

Uint16 EVAENCLK:1;// 0 Enable high speed clk to EV-A

Uint16 EVBENCLK:1;// 1 Enable high speed clk to EV-B

Uint16 rsvd1:1;// 2

Uint16 ADCENCLK:1;// 3 Enable high speed clk to ADC

Uint16 rsvd2:4;// 7:4 reserved

Uint16 SPIENCLK:1;// 8 Enable low speed clk to SPI

Uint16 rsvd3: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 MCBSPENCLK:1;// 12 Enable low speed clk to McBSP

Uint16 rsvd4:1;// 13 reserved

Uint16 ECANENCLK:1;// 14 Enable system clk to eCAN

};

union PCLKCR_REG{

Uint16 all;

struct PCLKCR_BITS bit;

};

以系统控制相关寄存器为例,寄存器在头文件中的三级组织结构如下所示:

4库的寄存器组织结构(以系统控制相关寄存器为例)

程序框图与寄存器配置

本实验的程序框图如下:

其中,在初始化设置中,使用到了以下函数进行设置:

InitSystem(); //初始化DSP内核寄存器

InitPieCtrl(); //调用PIE控制单元初始化函数

InitPieVectTable(); // 调用PIE向量表初始化函数

InitAdc();                    //ADC初始化设置

而设置ADC、定时器、PIE到工作状态的代码在main()中实现,直接设置寄存器相关的位。上述设置完成的功能叙述如下:

初始化函数:

InitSystem();

禁用看门狗,

PLLCR DIV为10(d)(PLL5倍频)

HISPCP=0x01(高速外设时钟除2)

LOSPCP=0x02(低速外设时钟除4)

PCLKCR外设时钟使能寄存器设置为:0x0009,即EVAEN位和ADCEN位置1,打开EVA和ADC时钟

InitPieCtrl();

初始化PIECRTL的ENPIE为0,PIEIERx为0, PIEIFRx为0.

InitPieVectTable();

初始化中断向量表并将ENPIE置1

InitAdc();

ADCCRTL3的ADCBGRFDN设为0x03,打开参考电源;ADCPWDN位设置为1,打开ADC其他部分的电源,其余寄存器都为初始值。

在main()中:

PIE设置:

PieCtrlRegs.PIEIER1.bit.INTx6=1,将PIEIER1设置为0x0020;IER寄存器的INT1位写1,IER=0x0001, 打开INT1中断;用EINT汇编指令开全局中断,INTM设0。

ADC设置:

ADC设置为单通道转换,输入通道为ADCINA1。允许EVA触发SEQ1,并打开SEQ1中断。

 

EVA设置:

T1禁止比较输出,通过T1TOADC启动ADC周期中断方式;设为连续递增计数模式,T1CON的TPS设7,预分频2^7=128,T1PR设置为62,这样中断周期为30M×5/2/128/(62+1)=9.3kHz 而ADC的采样保持时钟根据图1可以算出来为30M×5/2/1/4=18.75M,远大于定时器中断周期。

LED显示切换

按照题目要求,交替显示实验者学号。设计方法为:在ADC中断中计数,一旦计数值到达预设,就切换显示LED。为了不占用中断时间,LED的显示在main函数的while(1)中进行。为此,设计了如下的LED显示函数DispLed(), 使用switch语句切换不同的显示,函数示意如下:

void DispLed(void)

{

staticunsignedchar ca=1;//静态变量,点睛之笔

switch(ca)

{

case1:

// LED8

*LED8= CHAR_NULL;

// …………重复代码省略

//显示第一个学号

*(LEDWR)=0XFF;

ca++;

break;

case2:

*LED8= CHAR_NULL;

//显示第二个学号

*(LEDWR)=0XFF;

ca++;

break;

case3:

 

*LED8= CHAR_NULL;

 

*(LEDWR)=0XFF;

ca++;

break;

 

case4:

*LED8= CHAR_NULL;

//显示第二个学号

*(LEDWR)=0XFF;

ca=1;

break;

default:

ca=1;

}

这个函数本身具有静态变量,是完全封装的,无需输入参数和全局变量,每调用一次,就会将LED内容切换为下一个学号。

在while(1)中,判断定时器中断计数达到阀值后,就调用LED显示切换函数DispLed().这样就实现了LED内容的交替显示。

采集数据存储

实验还要求存储ADC采集到的数据。我们设定存储数量为1024点,使用队列数据结构实现。队列是先进先出的,队列满了之后,每进入一个新数值,最早的数据就会被踢出。考虑到性能,使用循环数组实现队列功能。

图 5 循环数组实现队列的示意图(以长度12为例)

上图展示了本程序中所用到的队列结果,其原理是一个数组和一个游标index. 数组元素在内存中是按顺序排列的,这便于通过指针获得数组内容;而index代表了队列的头。队列满后,每到一个新数据,覆盖最早的数据,index也移动一位,当index指到数组末尾后,就要跳回数组起点。图中展示了长度为12的队列,buffer[7]是队头,buffer[8]是队尾,下一时刻buffer[8]将被覆盖,index从7变为8.

使用这样的数据结构,每到一个数据,只要写入一个数据,同时改变变量index的值,只有2步操作,尽可能地减少了指令数。

实验结果

ADC采样率的计算

ADC的触发由定时器控制,而ADC本身的采样保持时钟很高,4.2节中已经分析其为18.75M. 定时器在连续增模式下,中断周期受到定时器时钟和计数值TxPR的控制,中断频率计算公式为:

 

实验时, =30M×5/2/128/(62+1)=9.3kHz

采样频率验证

粗略验证:

当采样频率为9.3kHz时,输入一个频率为0.93kHz的正弦波,一个周期内将只能采集到10个点. 用信号源产生0.93kHz正弦波,输入ADC, 用示波器测量经过DAC还原后的输出。结果如图6,一个周期内确实有10个量化阶。证实了采样频率为9.3kHz.

6 9.3kHz采样频率输入0.93kHz信号

精确验证:

改写DSP程序,在每次ADC中断内将ADC采样值反转一次后输出到DAC,这样,在没有输出信号时,输出方波,方波翻转周期就是ADC采样率。在采样频率为9.3kHz下,方波周期为4.65kHz,即翻转频率9.3kHz,与理论相符。

7 9.3kHz采样率下DAC反转输出

通过设置TPS的值为2,即4分频,得到新的采样率,此时采样率为: =30M×5/2/4/(62+1)=297.619kHz. 使用同样的方法验证采样率。结果如图8. 结果也与预想相符。

8 297.619kHz采样频率下DAC反转输出

采样存储波形

由4.4节中所述的数据结构方法,存储了1024点采样数据,保持在全局变量adBuff[1024]中,队列头变量为buffIndex.

在9.3kHz采样频率下,输入0.93kHz正弦波,使用Graph工具对信号绘图。图9展示了adBuffer的地址和数据内容,以及buffIndex的数值。

9 adBuffer的地址和数据内容,以及buffIndex的数值

LED交替显示

4.3节中叙述了LED交替显示的具体实现。原理简述为:ADC中断中计数,while(1)函数中判断计数值是否到达阀值,如果到达,调用LEDdisplay()函数。而LEDdisplay()函数内用switch实现了不同的LED模式显示。

实验时,可以看到我组学号的交替显示。

小节

本次实验使用到了ADC、EV外设,加上PIE的设置,设计的寄存器较多。使用官方的库函数进行配置,可以省时省力,减少出错。4.1节论述了库头文件中,使用C对寄存器结构的精妙构思:将每个寄存器的每位,用位带构成结构体;该结构体和整个寄存器内容构成联合体;相似功能的寄存器联合体再组合成结构体。这样不仅可以单独操作寄存器的位,也可以一次读写全部位。

在进实验室之前,我预先已经把要求的程序内容写好。在存储ADC采样值的时候,想到了队列结构,并使用循环数组实现了(4.4节),这样一次中断只需要写一次数据,改变一次队列头变量即可,是最优的存储方法,在约290kHz采样率下存储数据正常。

对采样频率的验证使用了2种方法,一种无需改动程序,输入特定频率信号,观察输出波形一个周期内的量化阶数,可以大致推断采样率;第二种方法是将ADC采样值反转一次后写到DAC输出,示波器上观察到的是50%占空比的方波,2倍方波频率就是采样率。

LED交替显示学号的要求也实现了,在4.3节具体说明了实现,5.4节中也用一句话总结了原理。

  1. FIR滤波器的DSP实现

实验目的

1. 巩固数字FIR滤波器的概念;

2. 理解定点DSP中数的定标、有限字长、溢出等概念;

3. 理解算法实现中实时的概念;

4. 掌握DSP开发过程以及基本调试方法;

5. 理解汇编以及高级语言开发DSP实现算法的区别。

实验内容

确定FIR滤波器参数,设计滤波器系数,完成数据的定标。仿真有限精度截断对FIR冲击响应的影响。验证系统实时性,实验测量幅频响应。

技术指标设定与可行性分析

我组在实验室北排,要求实现FIR带通滤波器。经过考虑,决定实现50kHz,40阶FIR滤波器,截至频率设为1000Hz和3000Hz.

可行性分析:

ADC的采样频率由定时器触发,设置EVA时钟分频位TPS=2,高速外设分频数HSPCLK=2,T1PR=374,此时定时器触发ADC的周期为:

30M×5/ 2 / 2^2 /(374+1)=0.05M=50kHz

即可以得到精确的50kHz采样率。

此时CPU时钟为150M, 平均每个中断周期内可以分配到3000条指令执行时间。40阶FIR滤波器包括40次乘加运算,由于使用了循环数组,只需少量的内存更新操作,再加上DSP的流水线结构,加上逻辑判断操作后,保守估计一个样点计算使用多达20条指令,40阶也只有800条,小于3000的裕量。

在50kHz采样率下,将通带设置在1kHz~3kHz,是满足采样定理的,一个周期可以分到20个样点左右,还原出来的波形直接输到示波器上,尚可辨别。

FIR滤波器系数设计

FIR滤波器可以利用matlab的可视化滤波器设计工具箱fdatool来设计,设计界面如图1所示。利用该工具可以直接得到41个滤波器系数,记为,这些都是绝对值小于1的小数。TMS320F2812是定点DSP,因此在DSP中运算时,使用的是精度有限的定点数。在C语言中实现小数点标定的方法是:使用扩大了2的n次方的int型表示小数,参与运算,在最后的结果上通过右移n位,得到真实的数值。

在本实验中,我们使用16位int型存储滤波器系数,标定为Q14. 从matlab的浮点小数转换为16位定点数,必然会造成精度的损失。利用matlab可以仿真截断后的冲击响应幅频特性。仿真结果如图2.

1 fdatool设置参数

2 FIR系数的定点仿真

程序设计

FIR滤波器实验建立在实验3的基础上。在上次实验中,已经实现了ADC数据的采集和DAC的输出,设计FIR滤波器时,只需要重新设置EVA时钟中断周期,其余无需改动。本实验更多的关注点在于算法的设计。

运算数据类型

F2812的片内ADC位数为16bit 无符号,由于附加了外部电路,输入模拟信号时,0v电平对应的二值数据为0x8000,这与DAC接收数据的格式是一样的。FIR滤波器系数使用Q14定标,转换为了16位int型。根据FIR滤波器的表达式:

(1)

ADC采样值16bit uint 和滤波器系数 16bit int 相乘,最大将扩展到32位。所以运算过程中的使用的数据类型转换如下所示:

3运算中数据类型的转换

其中,运算求和后得到的32bit long,在输给DAC之前,要转换移位为16bit. 滤波器系数定标时使用了Q14,原本对结果也右移14位即可,但实验所用DAC是坏的(实验2中提到过),只能用到14位,所以对求和结果右移了16位,避免了DAC因故障而跳变。

算法设计

上一个实验中,对ADC采样数据的存储使用了循环数组来实现,在40阶FIR滤波器中,这个数组长度是41. 并且由于数组中存储的数据是一个"环", 环的起点下标由一个变量buffIndex来表示,所以需要仔细考虑采样点数据与滤波器系数的对应关系。下图展示了采样值与滤波器系数相乘时的对应关系,这将是整个算法的精华。

4循环数组中x[n]h[n]的对应关系

从图中可以看到,由于存储采样点存储顺序不是与数组下标对应的,需要引入队列头位置变量buffIndex,与FIR系数h[n]对应起来。FIR的乘加可以分为part1和part2两部分计算,在part1中,h[0]对应了buffer[buffIndex], h[1]对应buffer[buffIndex-1],……以此类推,part1中共有buffIndex+1次乘加运算。同样的方法,根据图4,也可以得到part2中两个数组的对应关系。

记当前队列头的数组下标为index,则计算两部分乘加结果的方法可以用伪代码表示如下:

Order=41

Sum=0

firIndex=buffIndex

for(i=0; i<=index; i++) //计算part1的乘加

    sum=sum + h[k]*buffer[firIndex]

    firIndex=firIndex-1

end

firIndex=buffIndex //firIndex归位

for(i=index+1; i<Order; i++) //计算part2的乘加

    sum=sum + h[k]*buffer[firIndex]

    firIndex=firIndex-1

end

实验结果

幅频响应测量

调整信号源产生正弦信号,输出频率从低频扫描高频,覆盖带通区域,调整输出信号幅度尽可能大,但使滤波结果不溢出。测量得到不同频率下输出幅度,作图结果如下:

5测得幅频响应

6实测幅频特性与理论值比较

观察实测结果,滤波器的通带位于1000Hz至3000Hz,这与理论是相符的,在通带之外,测得的衰减值小于理论,这可能是由于32位int型下误差的积累造成的。

失败案例分析

以上分析的算法和所得实验结果是我们第二版程序,第一次实验时没有成功。第一次的实验使用了浮点表示滤波器系数,但由于没有分析清楚数据转换的格式,最后输入DAC的值产生了混乱,同时,在定点器件上模拟浮点运算,造成运算量大,迫近实时性极限。

最后一次实验,在进入实验室之前又重新写了实验程序,使用Q14表示FIR系数,理清了数据转换过程。然而最后DAC输出的波形出现了倒置。

7 DAC输出的波形倒置

分析认为,这是因为最后DAC的数据发生了溢出。在传递给DAC之前,又多右移了2位,输出结果正确。

实验总结

FIR滤波器实验是建立在实验3的基础上的,ADC采样,DAC输出的链路已经调通,寄存器设置也无需作大改动。本实验主要考验软件算法的设计以及小数定标的选择。这个实验写了两个版本,均在进入实验室之前写好,程序中使用了条件编译,可以先测试ADC采样频率,然后切换到FIR计算功能,最大限度利用好实验时的宝贵时间。第一个版本使用了浮点小数做FIR系数,结果运算量巨大,最后输入到DAC的数据格式混乱,没有成功。下一次实验之前,用Q14定标的16位小数作FIR滤波器系数,写了第二个版本。并且为了防止实验时产生实时性问题,我们准备了3组滤波器方案,分别是25kHz采样率30阶,50kHz采样率30阶和50kHz采样率40阶。最后进入实验室实际运行时,50kHz采样率40阶完全是可行的。这得益于精心安排的数据存储结构和系数-样点对应方法(5.2节)。调试成功后,实测了滤波器的幅频特性响应,与理论进行了比较(6.1节),结果显示,在通带内实际和理论符合较好,在通带外的衰减能力实际并没有理论那么高。

好了,写到这里,报告就结束了。这份报告陆陆续续写了5天,在撰写上花费了大约40小时,而编程和实验室操作上用了不到20小时,最后的考试复习用了大约10小时。这份报告上花的时间比例已经远远超过它的分值了,可谓良心力作。全篇约一万字,搭配了大量图表,程序设计部分给出了框图或伪代码,力求说理清晰。总的来说,这个课是很有意思的,目前为止,我接触学习了51,cortex M3,msp430,TI DSP这些器件,本科也是圆满了。

附录

实验3源程序

/****************************************************/

 

#include "DSP281x_Device.h"

 

#define CHAR_NULL 0X00

#define CHAR_L 0X38

 

#define CHAR_DH 0X08

#define CHAR_C 0X39

#define CHAR_A 0X77

#define CHAR_d 0X5E

#define CHAR_0 0X3f

#define CHAR_1 0X06

#define CHAR_2 0X5b

#define CHAR_3 0X4f

#define CHAR_4 0X66

#define CHAR_5 0X6d

#define CHAR_6 0X7d

#define CHAR_7 0X07

#define CHAR_8 0X7f

#define CHAR_9 0X6f

//LED地址

volatileint* LED8 =(volatileint*)0x2000;

volatileint* LEDWR =(volatileint*)0x2C00;

 

void ConfigureGpio(void);

void InitSystem(void);

void InitLed(void);

void DispLed(void);

interruptvoid adc_isr(void);

volatileunsignedint* DAOUT =(volatileunsignedint*)0x002900;

 

unsignedint countForLed=0;

unsignedint adBuff[1024]={0,};//--存放AD读取的值

unsignedint buffIndex=0;//adBuff中最新数值的索引

//adBuff [-------********...]

// |

// buffIndex tobe covered

 

void main(void)

{

InitSystem();//初始化DSP内核寄存器

InitPieCtrl();//调用PIE控制单元初始化函数

InitPieVectTable();//调用PIE向量表初始化函数

InitAdc();//调用ADC模块的基本初始化函数

InitLed();

EALLOW;

PieVectTable.ADCINT=&adc_isr;//重新设置PIE向量表中ADc的中断入口向量

EDIS;

 

PieCtrlRegs.PIEIER1.bit.INTx6=1;//使能PIE中断分组1中的ADC中断

 

IER=1;//使能和ADC中断相连的CPU INT1中断

 

EINT;//使能全局中断位INTM

ERTM;//使能全局实时调试中断DBGM

 

 

/* SET ADC*/

AdcRegs.ADCTRL1.bit.SEQ_CASC=0;

AdcRegs.ADCTRL1.bit.CONT_RUN=0;

AdcRegs.ADCTRL1.bit.CPS=0;

AdcRegs.ADCMAXCONV.all=0x0000;//单通道转换

AdcRegs.ADCCHSELSEQ1.bit.CONV00=0x0;//ADCINA0设置为SEQ1的第一个转换通道

AdcRegs.ADCTRL2.bit.EVA_SOC_SEQ1=1;//使能EVA引起的中断

AdcRegs.ADCTRL2.bit.INT_ENA_SEQ1=1;

AdcRegs.ADCTRL3.bit.ADCCLKPS=2;//ADc模块的核心时钟频率=HSPCLK/4

 

/* SET EVA*/

EvaRegs.GPTCONA.bit.TCMPOE=0;//禁止比较输出

EvaRegs.GPTCONA.bit.T1PIN=0;

EvaRegs.GPTCONA.bit.T1TOADC=2;//设置周期中断标志启动ADC

EvaRegs.T1CON.bit.FREE=0;//防真挂起时,定时器1立即停止工作

EvaRegs.T1CON.bit.SOFT=0;

EvaRegs.T1CON.bit.TMODE=2;//连续增计数模式

EvaRegs.T1CON.bit.TPS=2;//***()设置定时器时钟频率为HSPCLK/(2^TPS)

EvaRegs.T1CON.bit.TENABLE=1;//允许定时器操作

EvaRegs.T1CON.bit.TCLKS10=0;//内部时钟

EvaRegs.T1CON.bit.TCLD10=0;//计数器为0时重载

EvaRegs.T1CON.bit.TECMPR=0;//禁止比较操作

EvaRegs.T1PR=62;//***()定时器周期寄存器

 

/* SET GPIO*/

EALLOW;

GpioMuxRegs.GPAMUX.all=0x0;

GpioMuxRegs.GPADIR.all=0xFFFF;

EDIS;

 

while(1)

{

 

}

}

 

void InitSystem(void)

{

EALLOW;

SysCtrlRegs.WDCR=0x00E8;//禁止看门狗模块

SysCtrlRegs.PLLCR.bit.DIV=10;//CPUPLL倍频系数设为5

 

SysCtrlRegs.HISPCP.all=0x1;//高速时钟的预定标器设置成除以2

SysCtrlRegs.LOSPCP.all=0x2;//低速时钟的预定标器设置成除以4

 

 

//根据需要时能各种外设模块的时钟

SysCtrlRegs.PCLKCR.bit.EVAENCLK=1;

SysCtrlRegs.PCLKCR.bit.EVBENCLK=0;

SysCtrlRegs.PCLKCR.bit.SCIAENCLK=0;

SysCtrlRegs.PCLKCR.bit.SCIBENCLK=0;

SysCtrlRegs.PCLKCR.bit.MCBSPENCLK=0;

SysCtrlRegs.PCLKCR.bit.SPIENCLK=0;

SysCtrlRegs.PCLKCR.bit.ECANENCLK=0;

SysCtrlRegs.PCLKCR.bit.ADCENCLK=1;

EDIS;

}

 

 

void InitLed(void)

{

volatileunsignedint* LED8 =(volatileunsignedint*)0x002000;

volatileunsignedint* LED7 =(volatileunsignedint*)0x002100;

volatileunsignedint* LED6 =(volatileunsignedint*)0x002200;

volatileunsignedint* LED5 =(volatileunsignedint*)0x002300;

volatileunsignedint* LED4 =(volatileunsignedint*)0x002400;

volatileunsignedint* LED3 =(volatileunsignedint*)0x002500;

volatileunsignedint* LED2 =(volatileunsignedint*)0x002600;

volatileunsignedint* LED1 =(volatileunsignedint*)0x002700;

volatileunsignedint* LEDWR =(volatileunsignedint*)0x002C00;

 

* LED8= CHAR_NULL;

* LED7= CHAR_d;

* LED6= CHAR_A;

* LED5= CHAR_NULL;

* LED4= CHAR_C;

* LED3= CHAR_DH;

* LED2= CHAR_3;

* LED1= CHAR_L;

* LEDWR=0x11;

}

 

 

interruptvoid adc_isr(void)

{

 

EALLOW;

GpioDataRegs.GPASET.all=0xFFFF;

* DAOUT= AdcRegs.ADCRESULT0;//ADC中的转换结果直接赋给DA

//------中断服务程序中SGF添加的内容

if(buffIndex==1023)

{

buffIndex=0;

}

*(adBuff+(buffIndex++))=AdcRegs.ADCRESULT0;//共存储1024点,覆盖最早的数据

/*

if(countForLed++>6000) //在中断中计数,为LED切换提供延时

{

countForLed=0;

DispLed();

}

*/

//------

//重新初始化ADC采样序列

AdcRegs.ADCTRL2.bit.RST_SEQ1=1;//复位SEQ1

AdcRegs.ADCST.bit.INT_SEQ1_CLR=1;//清除中断位INT SEQ1

PieCtrlRegs.PIEACK.all=PIEACK_GROUP1;//清除PIE1的中断响应位

GpioDataRegs.GPACLEAR.all=0xFFFF;

EDIS;

 

}

 

void DispLed(void)

{

staticunsignedchar ca=1;//点睛之笔

switch(ca)

{

case1:

// LED8

*LED8= CHAR_NULL;

// LED7

*(LED8+0x100)= CHAR_NULL;

// LED6

*(LED8+0x200)= CHAR_2;

// LED5

*(LED8+0x300)= CHAR_2;

// LED4

*(LED8+0x400)= CHAR_2;

// LED3

*(LED8+0x500)= CHAR_0;

// LED2

*(LED8+0x600)= CHAR_1;

// LED1

*(LED8+0x700)= CHAR_2;

// WIRTE DATA TO LED

*(LEDWR)=0XFF;

ca++;

break;

 

case2:

// LED8

*LED8= CHAR_NULL;

// LED7

*(LED8+0x100)= CHAR_NULL;

// LED6

*(LED8+0x200)= CHAR_5;

// LED5

*(LED8+0x300)= CHAR_1;

// LED4

*(LED8+0x400)= CHAR_2;

// LED3

*(LED8+0x500)= CHAR_0;

// LED2

*(LED8+0x600)= CHAR_1;

// LED1

*(LED8+0x700)= CHAR_2;

// WIRTE DATA TO LED

*(LEDWR)=0XFF;

ca++;

break;

 

case3:

// LED8

// LED8

*LED8= CHAR_NULL;

// LED7

*(LED8+0x100)= CHAR_NULL;

// LED6

*(LED8+0x200)= CHAR_5;

// LED5

*(LED8+0x300)= CHAR_3;

// LED4

*(LED8+0x400)= CHAR_2;

// LED3

*(LED8+0x500)= CHAR_0;

// LED2

*(LED8+0x600)= CHAR_1;

// LED1

*(LED8+0x700)= CHAR_2;

// WIRTE DATA TO LED

*(LEDWR)=0XFF;

ca++;

break;

 

case4:

// LED8

*LED8= CHAR_NULL;

// LED7

*(LED8+0x100)= CHAR_NULL;

// LED6

*(LED8+0x200)= CHAR_9;

// LED5

*(LED8+0x300)= CHAR_2;

// LED4

*(LED8+0x400)= CHAR_1;

// LED3

*(LED8+0x500)= CHAR_0;

// LED2

*(LED8+0x600)= CHAR_1;

// LED1

*(LED8+0x700)= CHAR_2;

// WIRTE DATA TO LED

*(LEDWR)=0XFF;

ca=1;

break;

 

default:

ca=1;

 

}

 

}

 

实验4源程序

/**************************************************×**/

/* ADC sample rate at 50KHz, bandpass 40-order FIR */

//----编译条件设置

#define _LEDSHOW 0

#define _ADC_SAPLPE_RATE_TEST 1

//---------------------------

#include "DSP281x_Device.h"

 

#define CHAR_NULL 0X00

#define CHAR_L 0X38

#define CHAR_DH 0X08

#define CHAR_C 0X39

#define CHAR_A 0X77

#define CHAR_d 0X5E

#define CHAR_0 0X3f

#define CHAR_1 0X06

#define CHAR_2 0X5b

#define CHAR_3 0X4f

#define CHAR_4 0X66

#define CHAR_5 0X6d

#define CHAR_6 0X7d

#define CHAR_7 0X07

#define CHAR_8 0X7f

#define CHAR_9 0X6f

 

#define ORDER 41

//LED地址

volatileint* LED8 =(volatileint*)0x2000;

volatileint* LEDWR =(volatileint*)0x2C00;

 

void ConfigureGpio(void);

void InitSystem(void);

void InitLed(void);

void DispLed(void);

interruptvoid adc_isr(void);

volatileunsignedint* DAOUT =(volatileunsignedint*)0x002900;

 

unsignedint countForLed=0;

unsignedint adBuff[ORDER]={0,};//--存放AD读取的值

int buffIndex=0;//adBuff中最新数值的索引

//adBuff [-------********...]

// |

// buffIndex tobe covered

 

int hdata[ORDER]=

{80,20,-71,-189,-325,-464,-593,-695,-756,-763,

-710,-593,-417,-192,67,339,603,836,1020,1136,

1177,1136,1020,836,603,339,67,-192,-417,-593,

-710,-763,-756,-695,-593,-464,-325,-189,-71,20,80};

// FIR滤波器系数

 

void main(void)

{

InitSystem();//初始化DSP内核寄存器

InitPieCtrl();//调用PIE控制单元初始化函数

InitPieVectTable();//调用PIE向量表初始化函数

InitAdc();//调用ADC模块的基本初始化函数

InitLed();

EALLOW;

PieVectTable.ADCINT=&adc_isr;//重新设置PIE向量表中ADc的中断入口向量

EDIS;

 

PieCtrlRegs.PIEIER1.bit.INTx6=1;//使能PIE中断分组1中的ADC中断

 

IER=1;//使能和ADC中断相连的CPU INT1中断

 

EINT;//使能全局中断位INTM

ERTM;//使能全局实时调试中断DBGM

 

 

/* SET ADC*/

AdcRegs.ADCTRL1.bit.SEQ_CASC=0;

AdcRegs.ADCTRL1.bit.CONT_RUN=0;

AdcRegs.ADCTRL1.bit.CPS=0;

AdcRegs.ADCMAXCONV.all=0x0000;//单通道转换

AdcRegs.ADCCHSELSEQ1.bit.CONV00=0x0;//ADCINA0设置为SEQ1的第一个转换通道

AdcRegs.ADCTRL2.bit.EVA_SOC_SEQ1=1;//使能EVA引起的中断

AdcRegs.ADCTRL2.bit.INT_ENA_SEQ1=1;

AdcRegs.ADCTRL3.bit.ADCCLKPS=2;//ADc模块的核心时钟频率=HSPCLK/4

 

/* SET EVA*/

EvaRegs.GPTCONA.bit.TCMPOE=0;//禁止比较输出

EvaRegs.GPTCONA.bit.T1PIN=0;

EvaRegs.GPTCONA.bit.T1TOADC=2;//设置周期中断标志启动ADC

EvaRegs.T1CON.bit.FREE=0;//防真挂起时,定时器1立即停止工作

EvaRegs.T1CON.bit.SOFT=0;

EvaRegs.T1CON.bit.TMODE=2;//连续增计数模式

EvaRegs.T1CON.bit.TPS=2;//***()设置定时器时钟频率为HSPCLK/(2^TPS)

EvaRegs.T1CON.bit.TENABLE=1;//允许定时器操作

EvaRegs.T1CON.bit.TCLKS10=0;//内部时钟

EvaRegs.T1CON.bit.TCLD10=0;//计数器为0时重载

EvaRegs.T1CON.bit.TECMPR=0;//禁止比较操作

EvaRegs.T1PR=374;//***()定时器周期寄存器

 

/* SET GPIO*/

EALLOW;

GpioMuxRegs.GPAMUX.all=0x0;

GpioMuxRegs.GPADIR.all=0xFFFF;

EDIS;

 

while(1)

{

 

}

}

 

void InitSystem(void)

{

EALLOW;

SysCtrlRegs.WDCR=0x00E8;//禁止看门狗模块

SysCtrlRegs.PLLCR.bit.DIV=10;//CPUPLL倍频系数设为5

 

SysCtrlRegs.HISPCP.all=0x1;//高速时钟的预定标器设置成除以2

SysCtrlRegs.LOSPCP.all=0x2;//低速时钟的预定标器设置成除以4

 

 

//根据需要时能各种外设模块的时钟

SysCtrlRegs.PCLKCR.bit.EVAENCLK=1;

SysCtrlRegs.PCLKCR.bit.EVBENCLK=0;

SysCtrlRegs.PCLKCR.bit.SCIAENCLK=0;

SysCtrlRegs.PCLKCR.bit.SCIBENCLK=0;

SysCtrlRegs.PCLKCR.bit.MCBSPENCLK=0;

SysCtrlRegs.PCLKCR.bit.SPIENCLK=0;

SysCtrlRegs.PCLKCR.bit.ECANENCLK=0;

SysCtrlRegs.PCLKCR.bit.ADCENCLK=1;

EDIS;

}

 

 

void InitLed(void)

{

volatileunsignedint* LED8 =(volatileunsignedint*)0x002000;

volatileunsignedint* LED7 =(volatileunsignedint*)0x002100;

volatileunsignedint* LED6 =(volatileunsignedint*)0x002200;

volatileunsignedint* LED5 =(volatileunsignedint*)0x002300;

volatileunsignedint* LED4 =(volatileunsignedint*)0x002400;

volatileunsignedint* LED3 =(volatileunsignedint*)0x002500;

volatileunsignedint* LED2 =(volatileunsignedint*)0x002600;

volatileunsignedint* LED1 =(volatileunsignedint*)0x002700;

volatileunsignedint* LEDWR =(volatileunsignedint*)0x002C00;

 

* LED8= CHAR_NULL;

* LED7= CHAR_d;

* LED6= CHAR_A;

* LED5= CHAR_NULL;

* LED4= CHAR_C;

* LED3= CHAR_DH;

* LED2= CHAR_3;

* LED1= CHAR_L;

* LEDWR=0x11;

}

 

 

interruptvoid adc_isr(void)

{staticunsignedint flag=1;

int k=0;//FIR计算循环用

long sum=0;//FIR各抽头求和结果

int firIndex=0;//FIR滤波计算时所用的数组偏移量

EALLOW;

GpioDataRegs.GPASET.all=0xFFFF;

//* DAOUT= AdcRegs.ADCRESULT0; //ADC中的转换结果直接赋给DA

//------中断服务程序中SGF添加的内容

#if _ADC_SAPLPE_RATE_TEST

/*测试ACD采样频率,不输入信号,观察输出反转频率即可*/

 

* DAOUT= AdcRegs.ADCRESULT0+flag*0x2000;

flag=(1U==flag?0:1U);

#endif

 

//采集&存储&FIR计算

#if !_ADC_SAPLPE_RATE_TEST

// *DAOUT= AdcRegs.ADCRESULT0;

if(buffIndex==ORDER)

{

buffIndex=0;

}

*(adBuff+(buffIndex))=AdcRegs.ADCRESULT0;//共存储ORDER点,覆盖最早的数据

firIndex=buffIndex;

//最后再buffIndex++,留着还有使用价值TAT

 

for(k=0;k<=buffIndex;k++)//计算firIndex前面部分

{

sum+=hdata[k]*adBuff[firIndex--];

}

firIndex=ORDER-1;//索引归位到数组最后

 

for(k=buffIndex+1;k<ORDER;k++)//计算firIndex后面部分

{

sum+=hdata[k]* adBuff[firIndex--];

}

 

 

*DAOUT=(unsignedint)(sum>>16);

buffIndex++;

 

#endif

/*

if(countForLed++>6000) //在中断中计数,为LED切换提供延时

{

countForLed=0;

DispLed();

}

*/

/* //------ */

//重新初始化ADC采样序列

AdcRegs.ADCTRL2.bit.RST_SEQ1=1;//复位SEQ1

AdcRegs.ADCST.bit.INT_SEQ1_CLR=1;//清除中断位INT SEQ1

PieCtrlRegs.PIEACK.all=PIEACK_GROUP1;//清除PIE1的中断响应位

GpioDataRegs.GPACLEAR.all=0xFFFF;

EDIS;

 

}

 

 

}

 

猜你喜欢

转载自blog.csdn.net/Imkiimki/article/details/78159360
TSM