分享

DSP-F2812的C语言心得记录

 molei 2015-04-03

如何把自己所定义的结构体放在一个主程序中使用

我原打算定义一个结构体方便自己对一些变量方便使用,下面是我对定义体的总结:

首先,应该把该头文件加入到DSP_28Device.h的头文件当中去,再到DSP28_GlobalVariableDefs.c文件中写入相应的定义段,并在CMD文件中定义一个区域就可以使用了,针对我自己的结构体(见如下定义所写的),我所写的定一段是

#pragma DATA_SECTION(BaseRegs,"BaseRegFile");
volatile struct BASE_REGS BaseRegs; (红色部分是我自己所定义的一个结构体引用)

以下是我写的一个结构体定义:(以下代码本人保密,如有需要,请联系本人)



以下是我在一个".h"文件中定义的一个结构体:

////////////////////////数字输入输出功能定义的结构体////////////////////////////////
struct PARAMETER{
Uint16 VALUE; 
Uint16 DEFAULT ;
Uint16 INDEX ; // can be saved to EEPROM by this 
Uint16 TYPE ; // 0: cann't be modified when motor running 1: can be modified in anytime
Uint16 SCALE ; // 0: VALUE need not recalculate when using ; >0 Real Value = (VALUE / SCALE);
};

struct DIO_REG{
struct PARAMETER DIProp; //F3.16,输入端子极性设定
struct PARAMETER DI1; //F3.8,输入端子X1功能选择
struct PARAMETER DI2; //F3.9,输入端子X2功能选择
struct PARAMETER DI3; //F3.10,输入端子X3功能选择
struct PARAMETER DI4; //F3.11,输入端子X4功能选择
struct PARAMETER DI5; //F3.12,输入端子X5功能选择
struct PARAMETER DI6; //F3.13,输入端子X6功能选择
struct PARAMETER DI7; //F3.14,输入端子X7功能选择
struct PARAMETER DI8; //F3.15,输入端子X8功能选择

struct PARAMETER DOProp; //F3.25,输出端子极性设定
struct PARAMETER DO1; //F3.17,输出端子Y1功能选择
struct PARAMETER DO2; //F3.18,输出端子Y2功能选择
struct PARAMETER DO3; //F3.19,输出端子Y3功能选择
struct PARAMETER DO4; //F3.20,输出端子Y4功能选择
struct PARAMETER DO5; //F3.21,输出端子Y5功能选择
struct PARAMETER DO6; //F3.22,输出端子Y6功能选择
struct PARAMETER DO7; //F3.23,输出端子Y7功能选择
struct PARAMETER DO8; //F3.24,输出端子Y8功能选择
};

struct USERDIO_BITS{
Uint16 bit00:1;
Uint16 bit01:1;
Uint16 bit02:1;
Uint16 bit03:1;
Uint16 bit04:1;
Uint16 bit05:1;
Uint16 bit06:1;
Uint16 bit07:1;

Uint16 bit10:1; // 0: low level active 1: high level active
Uint16 bit11:1; // 0: low level active 1: high level active
Uint16 bit12:1; // 0: low level active 1: high level active
Uint16 bit13:1; // 0: low level active 1: high level active
Uint16 bit14:1; // 0: low level active 1: high level active
Uint16 bit15:1; // 0: low level active 1: high level active
Uint16 bit16:1; // 0: low level active 1: high level active
Uint16 bit17:1; // 0: low level active 1: high level active
};

union USERDI_REG

Uint16 word;
struct USERDIO_BITS bit;
};

union USERDO_REG

Uint16 word;
struct USERDIO_BITS bit;
};

struct UserDIO_REGS
{
union USERDI_REG UserDI_reg;
union USERDO_REG UserDO_reg;
struct DIO_REG DIO_reg;
Uint16 RUN; //数字输出功能表中,变频器正在运行的标志位
Uint16 FG; //数字输出功能表中,频率到达信号的标志位
Uint16 FGT1; //数字输出功能表中,频率水平检测信号1的标志位
Uint16 FGT2; //数字输出功能表中,频率水平检测信号2的标志位
Uint16 OL; //数字输出功能表中,过载信号的标志位
Uint16 UV; //数字输出功能表中,欠压脉冲封锁的标志位
Uint16 EF; //数字输出功能表中,外部故障停机的标志位
Uint16 UF; //数字输出功能表中,到达频率上限的标志位
Uint16 BF; //数字输出功能表中,到达频率下限的标志位
Uint16 STANDBy; //数字输出功能表中,变频器准备好运行的标志位
Uint16 Fault; //数字输出功能表中,变频器故障的标志位 
};

extern volatile struct UserDIO_REGS UserDIORegs;
void Figure_InputFunction(void); //数字输入函数
void Figure_OutputFunction(void); //数字输出函数
//////////////////////////////////////////////////////////////////////////////

定义完后还需要在DSP28_GlobalVariableDefs.c中加入的语句是:

#pragma DATA_SECTION(UserDIORegs,"UserDIORegFile");
volatile struct UserDIO_REGS UserDIORegs;

然后在".cmd"文件中加入的区间定义:

UserDIORegFile :> PAR_REG, PAGE = 1

**************************************************************************************************************

以上都编写进DSP的程序后就可以使用自己所定义的结构体作变量了



对于TMS320F2812的时钟问题,从我刚开始接触到现在终于明白了它内部的时钟是如何区分的,F2812上有多个部件需要时钟:CPU,看门狗电路,ADC,事件管理器等片上外设.
F2812的CPU时钟电路如下图所示:
电路板中用30MHz外部晶体给F2812提供时钟,并使能F2812片上PLL电路.PLL倍频系数由PLL控制寄存器PLLCR的低4位控制,可有软件动态地修改,外部复位信号(XRS)将此4位控制位被清为0(CCS中的复位命令将不对此4位控制位作清0操作),F2812的CPU最高可工作在150MHz主频下,也即对30MHz输入频率进行5倍频.PLLCR控制位与倍频系数的关系如下图所示:
PLL锁相环的配置模式:
需要时钟信号的片上外设有:看门狗电路WatchDog,CPU定时器(3个32位定时器),eCAN总线控制器;SCI(2个异步串行通信控制器),SPI(1个4线制同步串口),McBSP(1个多通道缓冲型同步串口);EV(2个事件管理器,每个事件管理器包括:2个通用定时器,3个全比较器/PWM单元,3个事件捕捉单元,QEP正交编码脉冲,外部时钟输入,外部比较输入和外部触发输入),ADC(16通道),12位,12.5MSPS,0~3V输入范围.片上外设按输入时钟分为4个组:
SYSOUTCLK组:CPU定时器,eCAN总线
OSCCLK组:看门狗电路
低速组(HSPCLK):SCI,SPI,McBSP,它们的输入时钟信号由SYSOUTCLK经低速外设分频器分频得到
高速组(LSPCLK):EVA/B,ADC,它们的输入时钟信号由SYSOUTCLK经高速外设分频器分频得到.
************************************************************************************************************************
重要点:SYSOUTCLK与DSP片上管脚XCLKOUT(119)的关系:
XINTF模块有两种时钟模式,如下图给出了SYSOUTCLK时钟同XINTF时钟之间的关系。
所有的外部扩展访问都是以内部XINTF的时钟XTIMCLK为参考的,因此在配置XINTF时,首先要通过XINTFCNF2寄存器配置XTIMCLK。XTIMCLK可以配置为SYSOUTCLK,也可以配置为SYSOUTCLK/2,XTIMCLK默认的值是SYSOUTCLK/2。外部接口还提供一个时钟输出XCLKOUT,所有外部接口的访问都是在XCLKOUT的上升沿开始。可以通过XINTFCNF2寄存器的CLKMODE位配置XCLKOUT的频率.
XCLKOUT的功能是:

在DSP程序烧进烧写的过程中,程序烧写进去后但不能运行的原因,这很可能是晶振电路的问题,仿真时不用板上的晶振,而是由JTAG接口提供,实际下载时得用示波器测一下,下载不能正常运行检查晶振及复位电路...

几本值得参考的2812数据说明手册

第一本是C281x C/C++ 头文件和外设示例,里面有TI标准的CMD文件,查询号是SPRC097,

网址是http://focus./cn/docs/toolsw/folders/print/sprc097.html,里面主要介绍了头文件的定义和外设的一些只要例程,非常值得编译人员参考的。

第二本是TMS320x281x Boot ROM Reference Guide (Rev. C) ,查询号是SPRU095,里面主要介绍了如何引导相关ROM操作的问题。也很值得参考。

网址



DSP-F2812C语言心得记录6-SCI通信发送模块中FIFO增强特性的使用方法

SCI通信发送的过程中,有两种发送的方式供选择,一种是利用SCI模块的普通功能(即查询等待标志位的方式进行),另一种是利用F2812的增强特性FIFO功能进行数据的传送。比较这两种数据发送的方式发现,前一种查询等待的方式在发送一个字节的过程中会占用一个周期中的1ms时间(这是波特率为9600bps时候的情况),而后一种方式则基本不占用系统的周期等待时间,直接往Buf里面写数据就会自动的往外发送。

以下是FIFO功能的增前特性描述:

我在编程序的时候只用到增强特性中FIFO的发送功能,通过将SCIFFTX寄存器中的SCIFFEN位置1,使能FIFO模式。在任何操作状态下SCIRST都可以复位FIFO模式。

本人对寄存器的设置是

ScibRegs.SCIFFTX.all=0xE040; //1(1表示SCI FIFO可以恢复发送或接收)/1(使能SCI FIFO增强功能)/1(重新使能发送FIFO操作)/00000(发送FIFO是空的)/0(没有TXFIFO中断)/1(清除TXFFINT标志位)/0(中断被禁止)/00000(中断级别为0)

ScibRegs.SCIFFCT.all=0x00; //禁止串口自动检测波特率

for(temp=1;temp<TXindex1;temp++)

{

ScibRegs.SCITXBUF=DateReceiveBuf[temp];

}

//然后FIFO就会把所要发送的数据都会自动的发送出去

ScibRegs.SCIFFTX.bit.SCIFFENA=0; //禁止FIFO功能,以确保利用回SCI的通信接收中断功能

上述的FIFO功能设置并不占用其它定时器中断的时间,这样可以有效的提高周期的资源利用率。提高了通信的效率。

对于第一种查询等待的方式,其中的设置是要等待一个字节完全的发送出去才能够进行其它模块的工作,这样的话是非常耗用时间和DSP的资源利用率的,而且其它模块的时间根本得不到保证,以下是我在调试时的一些相关设置:

ScibRegs.SCITXBUF=DateReceiveBuf[temp];

while(!((1<<6) & ScibRegs.SCICTL2.all)); //等待这个字节的数据完全的发送出去,该指令按照波特率为9600bps来算的话,发送一个字节的时间将耗时1ms,这样是很浪费资源的,其它模块的功能根本执行不了。

根据上述的比较会发现,利用FIFO的功能进行通信是非常合理的,而且资源利用效率高。



链接命令文件(.cmd文件)通过段定位控制命令,分配程序代码和数据运行存储空间。

链接命令文件有两个重要的指令:MEMORY和SECTIONS。

(1)、MEMORY

指出目标系统中物理上存在的存储空间的范围,即可供程序使用的存储空间范围。在默认状态下,PAGE0代表程序空间,PAGE1代表数据空间。

(2)、SECTIONS

1)、描述输入段如何组合成输出段。

2)、定义可执行程序的输出段。

3)、指出输出段存放在存储空间中的位置。

4)、允许重命名输出段。

.cmd文件编写的目的 确定程序和数椐的装载运行空间,校验数据和程序代码的长度,定义输入/输出文件,安排系统中可用的存储器,程序段、数据段、堆栈以及复位向量和中断向量的地址空间。

链接命令文件通过段定位控制命令,分配程序代码空间、数据代码空间、程序运行空间、堆栈空间。

u .CMD文件命令格式

 MEMORY定义目标板物理存储空间分配,默认状态下,PAGE0为程序空间, PAGE1为数据空间。

 SECTIONS定义程序连接过程中各个输入段与输出段之间的关系,并给出输出段地址。① 给出输入段组合成输出段的方式 定义可执行程序的输出段; 指出输出段存放在存储空间中的位置④ 允许重命名输出段。

例如 .cmd文件应用

MEMORY

{

PAGE 0: PROG: origin = 0x1400, len = 0x2c00

;第0页程序空间开始地址0x1400,空间长度0x2c00

VECT: origin = 0x0080, len = 0x80

;第0页中断向量开始地址0x0080,空间长度0x80

PAGE 1: REGS: origin = 0x60, len = 0x1c

;第1页寄存器空间开始地址0x60,空间长度0x1c

IDATA: origin = 0x80, len = 0x1380

;第1页数据空间开始地址0x80,空间长度0x1380

EDATA: origin = 0x1400,len = 0x8000

;第1页扩展数据空间开始地址0x1400,空间长度0x8000

EDATA1: origin = 0x9400,len = 0x4c00

;第1页扩展数据空间开始地址0x9400,空间长度0x4c00

}

SECTIONS

{

.vectors: {}> VECT PAGE 0 ;中断向量在第0

.text: {}> PROG PAGE 0 ;程序代码在第0

.cinit: {}> PROG PAGE 0 ;初始化变量在第0

.pinit: {} > PROG PAGE0 ;初始化参数在第0

.bss: {} > IDATA PAGE1 ;非初始化变量在第1

.far: {} > IDATA PAGE1 ;长调函数在第1

.const: {} > IDATA PAGE1 ;常数在第1

.switch: {} > IDATA PAGE1 ;数据交换空间在第1

.sysmem: {} > IDATA PAGE1 

.cio: {} > IDATA PAGE 1 C语言I/O调用在第1

.stack: {} > IDATA PAGE 1 ;堆栈在第1

.csldata: {} > IDATA PAGE 1 C语言宏调用在第1

}



分别用C语言和汇编语言进行程序设计,计算正弦函数值,并比较代码效率。

1C语言编程

#include <math.h>

#define NX 180 //最大正弦角度

#define pi 3.14159

short i;

double x[NX] //定义输入正弦角度数组(弧度值)

double r[NX] //定义输出正弦结果数组

void main(void)

{

for (i=0;i<NX;i++) //将角度转换为弧度

{ x[i] =pi*i/180;

r[i]=sin(x[i]); //计算正弦值

}

return;

}

程序编译连接通过后,加载生成的.out文件,点击“Run”图标运行程序。我们在“Watch”窗口中可以观察输出的正弦值数组r[i] 。(见图1)

利用CCS图形工具可以观察这段数据的波形。注意CCS图像显示参数设置中,正确填入数据的起始地址,数据长度设为180,数据类型要设为32位浮点数。(见图2)

2)汇编语言编程

用汇编语言计算正弦值,一般采用泰勒级数展开的方法。角度正弦值的泰勒级数展开式如下:(见图3)

其中:x为弧度值。

注意:

l x01,原因是CPU寄存器ST1中的FRCT位为1CPU处于小数模式。

l 程序文件中所有标号左对齐。

l 编译器默认的程序起始标号_c_int00

3)汇编程序如下:

.mmregs ;伪指令,定义C54x寄存器符号

.def _c_int00 ;伪指令,声明在当前模块中定义的标号

.def sin_start

;;;;;;;;;;;;;;;;;"coeff"段开始;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

d_coff .sect "coeff"

.word 01c7h ;1/72 *(215-1) Q15格式

.word 030bh ;1/42 *(215-1)

.word 0666h ;1/20 *(215-1)

.word 1556h ;1/6 *(215-1)

;;;;;;;;; 预留中间运算结果存储空间段;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

d_x .usect “sin_vars”,1

;伪指令,在 "sin_vars"段中预留1个字的空间

d_squr_x .usect "sin_vars",1

d_temp .usect "sin_vars",1

d_sinx .usect "sin_vars",1

C_1 .usect "sin_vars",1

;;;;;;;;;;;.text段开始;;;;;;;;;;;;;;;;;;;;;;;;;;;

.text

_c_int00: CALL sin_start

sin_start:

SSBX FRCT ; ST1中的FRCT位置1

STM #d_coff,AR3

STM #d_x,AR2

STM #C_1,AR4

LD #d_x, DP

ST #06487h,d_x ; input x = PI/4

ST #7fffh,C_1

SQUR *AR2+,A ; A = x^2

ST A,*AR2 ; AR2 = x^2

|| LD *AR4,B ; B = C_1

MASR *AR2+,*AR3+,B,A ; A = (1-x^2/72), T = x^2

MPYA A ; A = T*A = x^2*(1-x^2/72)

STH A,*AR2 ; d_temp = A = x^2*(1-x^2/72)

MASR *AR2-,*AR3+,B,A ; A = 1-x^2/42(1-x^2/72),

; T = x^2(1-x^2/72)

MPYA *AR2+ ; B = x^2*(1-x^2/42(1-x^2/72))

ST B,*AR2

|| LD *AR4,B

MASR *AR2-,*AR3+,B,A ; A = 1-x^2/20(1-x^2/42(1-x^2/72)

MPYA *AR2+ ; B = x^2*(1-x^2/20(1-x^2/42(1-x^2/72))

MASR *AR2-,*AR3+,B,A ; A = 1-x^2/6(1-x^2/20(1-x^2/42(1-x^2/72))

MPYA d_x ; B = x(1-x^2/6(1-x^2/20(1-x^2/42(1-x^2/72)))

STH B, d_sinx ; d_sinx = sin(x)

RET

.end

;;;;;;;;;;;;;;;;;.text段结束;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

4)算法分析

l 运算速度:CCS主界面选择

Profile>Enable Clock,启动时钟分析 Profile>View Clock,观察分析结果

C语言执行时间(见图4)

汇编语言执行时间(见图5)

l 代码长度:Profile>Start New Sessions

输入name_profile --Range 分析程序代码长度。

(见图6、7)

图1:

图2:

图3:

图4:

图5:

图6:

图7:


F2812的运行过程
1)上电运行时根据MP/MC引脚的状态决定是从片外的3fffc0处读取复位向量或者从片内的rom的3fffc0处读取复位向量。MC方式下从片内读取,MP方式下从片外读取。
2)一般我们用的是MC方式,即从片内读取复位向量,在片内的rom 3fffc0处有一个地址为3ffc00。
3)复位后处理器从3fffc0处读取3ffc00这个地址,所以程序从3ffc00处开始执行
4)3ffc00处开始的就是initboot过程,根据IO管脚的状态判断该进入哪一种引导方式
5)在SCITXA引脚为高电平时就是flash boot方式,此时置PC=3F7FF6
6)在片内flash的3F7FF6和3F7FF7处有一个跳转指令,该跳转指令就是LB _c_int00
7)执行这个跳转指令后程序就开始运行c_int00这个函数了
8)这个函数就是建立一个c程序的运行环境,等建立完c运行环境后c_int00调用main函数
9)main函数开始就是我们自己编写的应用程序了。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多