STM32 keil MDK仿真测试.docx
《STM32 keil MDK仿真测试.docx》由会员分享,可在线阅读,更多相关《STM32 keil MDK仿真测试.docx(11页珍藏版)》请在冰豆网上搜索。
STM32keilMDK仿真测试
信号函数
当µVision3模拟执行目标程序时,信号函数可以在后台实现信号输入、脉冲输入等重复操作。
信号函数可用于模拟和测试串行I/O、模拟I/O、端口通讯和其他一些重复发生的外部事件。
因为当µVision3模拟目标程序时,信号函数是在后台执行。
因此,信号函数必须在某些地方调用twatch函数来进展延迟,以便让µVision3能运行目标程序。
假设信号函数从不调用twatch,如此µVision3会报告错误。
µVision3提供了一局部能在信号函数中使用的系统变量。
详情请参考
系统变量。
系统变量
系统变量可在程序中任何位置的变量和其他表达式中使用,用于获取一些特殊功能值。
下表列出了允许使用的系统变量,以与数据类型和使用方法。
变量类型描述
$unsignedlong表示当前程序计数器值。
可以使用$表达和修改程序计数器。
例如,$=0x4000可将当前程序计数器值设置为0x4000。
_break_unsignedint令目标程序中止运行。
如果将_break_设置为一个非零值,µVision将挂起目标程序的执行。
在用户和信号函数中使用该变量可挂起目标程序的执行。
_traps_unsignedint假设将_traps_设置为一个非零值,µVision将现实166个硬件陷阱:
未定义的操作码,被保护指令错,非法字操作访问,非法指令访问,堆栈上溢,堆栈下溢等。
statesunsignedlongCPU指令状态计数器的当前值;当目标程序开始执行时,该计数器从0开始每执行一条就加1。
注意
states是一个只读变量。
itraceunsignedint表示目标程序执行时是否记录跟踪过程。
假设itrace为0,如此不记录跟踪过程;假设itrace是一个非0值,如此记录跟踪过程。
更多相关信息可参考TraceRecording。
radixunsignedint设置显示数据的进制,radix可为10或16,默认值为16用于HEX输出.
信号函数的定义由关键字SIGNAL开始,格式如下:
SIGNALvoidfname(parameter_list){
statements
}
fnamei函数名
parameter_listi传递给函数的参数表,每个参数必须包括一个类型声明和一个名字。
如果没有参数,如此用void代替参数表。
多个参数之间用逗号间隔。
statements函数体
{函数起始标识。
只有函数起始标识和函数完毕标识(“}〞)保持平衡,函数才是完整的。
例如
下面的例子表示一个信号函数每隔1,000,000个CPU周期将字符'A'传送至串行输入缓冲区一次。
SIGNALvoidStuffS0in(void){
while
(1){
S1IN='A';
twatch(1000000);
}
}
调用这个函数时,在控制窗口输入如下命令:
StuffS0in()
调用时StuffS0in信号函数会将字符'A'的ASCII值传送至串行输入缓冲区并延迟1,000,000个CPU周期,不断重复。
信号函数受到如下约束:
函数的返回值类型必须为void。
函数最多只能有8个参数。
信号函数可以调用其他重定义函数和用户函数。
信号函数之间不能相互调用。
信号函数可以被用户函数调用。
信号函数必须调用twatch至少一次。
如果信号函数从不调用twatch,如此目标程序将得不到时间执行。
而且由于不能使用Ctrl+C中断信号函数,在这种情况下µVision3将进入死循环。
信号函数的管理
一个信号函数要么处于空闲态,要么处于运行态。
µVision3通过队列来保存被激活的信号函数。
处于空闲态的信号函数将被延迟,直到延迟完指定的CPU周期等待之后调用twatch来唤醒她。
运行态的信号函数如此执行函数的语句。
当一个信号函数被调用时,µVision3将此函数参加队列中并将其状态标明为运行。
一个信号函数只能被激活一次,如果要被激活的函数已经在队列中,如此会发出警告。
通过SIGNALSTATE命令可以查看信号函数的状态,SIGNALKILL命令可以将信号函数从队列中消除掉。
当信号函数调用twatch函数时,经过数个CPU周期处理完twatch函数之后信号函数将进入空闲态,直到用户程序执行指定数目个CPU周期后信号函数才从twatch函数之后的语句开始继续执行。
当信号函数退出时,返回语句自动将该函数从队列中去除。
下面的例子表示一个信号函数在C167上改变模拟输入的值。
函数从0伏特开始以0.5伏为单位增加或减少输入电压,并且增加上限被设置。
该信号函数不定期的重复,每次电压改变将延迟200,000个CPU周期。
signalvoidanalog0(floatlimit){
floatvolts;
printf("Analog0(%f)entered.\n",limit);
while
(1){/*forever*/
volts=0;
while(volts<=limit){
ain0=volts;/*analoginput-0*/
twatch(200000);/*200000statesTime-Break*/
volts+=0.1;/*increasevoltage*/
}
volts=limit;
while(volts>=0.0){
ain0=volts;
twatch(200000);/*200000statesTime-Break*/
volts-=0.1;/*decreasevoltage*/
}
}
}
信号函数analog0能以如下方式被调用:
>ANALOG0(5.0)/*Startof'ANALOG()'*/
ANALOG0(5.000000)ENTERED
SIGNALSTATE命令显示analog0函数的当前状态:
>SIGNALSTATE
1idleSignal=ANALOG0(line8)
该命令列举出部函数的编号,信号函数的状态:
空闲态还是运行态,函数名和执行语句行号。
因为信号函数的处于空闲态,可以推测出analog0正在执行twatch函数(在analog0的第8行)并等待指定的CPU周期时间。
200,000个CPU周期之后,analog0继续执行直到在程序的第8行或14行twatch函数再一次被调用。
以下命令将analog0函数从激活信号函数队列中去除。
>SIGNALKILLANALOG0
建立函数
µVision3中有一个建的函数编辑器,通过Debug–FunctionEditor来打开。
打开函数编辑器时需要输入一个文件名或者打开一个由OptionsforTarget–Debug–InitializationFile指定的文件。
该编辑器的用法与µVision3编辑器一样,允许用户输入和编译调试函数。
参考函数编辑对话框帮助可获取更多对话项目信息。
当建立一个调试函数文件后,可用INCLUDE命令可以读取和处理该文本文件的容。
例如,如果在命令窗口输入如下命令,µVision3将完成对MYFUNCS.INI文件容的读取和解释功能。
MYFUNCS.INI可能包含调试命令和函数定义,通过OptionsforTarget–Debug-InitializationFile可以进入该文件。
每次打开µVision3调试器时,MYFUNCS.INI文件的容将被执行。
INCLUDE命令用于指定一个命令文件,命令被从此文件中一行一行地被读取并被传递给µVision3来执行。
在µVision3中可以利用INCLUDE文件执行重复操作。
例如,定义INCLUDE文件用于载入目标程序,运行程序到主C函数,初始化工具箱按钮与生成几个用户函数。
INCLUDE文件可以嵌套4层,要使用INCLUDE命令时,必须先停止目标程序的执行。
例子
不需要的函数可以通过KILL来删除。
Debug-FunctionEditor
关于创建函数文件的详细信息请参考创建函数。
Open
打开浏览对话框,选择一个要编辑的文件。
New...
创建一个新的文件以参加函数。
Save
保存文件的编辑容。
SaveAs...
在不同的文件中保存当前的编辑容。
pile
编译当前文件,相应的结果在OutputWindow的Build页中显示。
pileErrors
显示编译错误。
选择一个错误可以在编辑窗口定位相应的出错行
KILL命令可用于删除已定义的工具箱按钮和µVision3函数。
KILLBUTTON命令用于移除前面已定义的一个工具箱按钮。
当使用这个命令时,必须要指定要移除工具箱按钮的编号。
这个编号在工具箱窗口中每个按钮的前面。
KILLFUNC*命令用于移除前面已定义的所有用户函数和信号函数。
µVision3部定义函数不能被删除。
KILLFUNCfunction_name命令用于删除指定的用户函数或信号函数。
例子
>KILLFUNCANALOG/*Deleteuserfunctionanalog*/
>KILLFUNCmyregs/*Deleteuserfunctionmyregs*/
>KILLFUNC*/*Deletealluserfunctions*/
>KILLBUTTON3/*KillToolboxButtonnumber3*/
>KILLBUTTON1/*KillToolboxButtonnumber1*/
外围设备变量
根据工程所选择的CPU,µVision3会自动地定义一些符号。
这些符号分为两类:
外围存放器〔或SFRs〕和虚拟仿真存放器〔VTREGs〕。
在SimulationScriptTemplates下,调试函数DebugFunctions可利用这些外围设备变量自动向外设输入信号。
外围存放器(SFRs)
µVision3为外围存放器定义了符号。
外围存放器符号的定义依赖于选择的微控制器。
外围存放器符号都具有相关的地址,可被用于表达式中。
虚拟仿真存放器(VTREGs)
虚拟仿真存放器的存在,使得模拟CPU的引脚进展输入和输出成为可能。
VTREGs不是公有符号,也不能常驻CPU的存。
它们可以用于表达式中,但其值与用法是依赖于CPU的。
VTREGs提供了一种输入来自被仿真硬件上的信号到CPU引脚的方法。
可以使用DIRVTREG命令列出这些符号。
下表描述了VTREG符号。
VTREG符号的可用性依赖于所选的CPU。
VTREG
描述
ADx
片上的一个模拟输入引脚,它的典型代表是A/D转换器输入。
目标程序可以读取写入到ADxVTREGs中的值。
DAx
片上的一个模拟输出引脚。
这个值反响了D/A转换器的输出。
xxVREF
相关引脚的电压输入。
PORTx
片上端口的一组I/O引脚。
例如,PORTA代表了PORTA的所有引脚,这些存放器可以仿真端口I/0.
SxIN
串行接口x的输入缓冲。
可以向SxIN写入一个8位或9位的值,它们可以被目标程序读取。
读取SxIN以取决于什么时候输入缓冲准备好以接收另外的字符。
值0xFFFF意味着前面的值已被处理完,新的值可以写入了。
SxOUT
串行接口x的输出缓冲。
µVision3拷贝8位或9位的值到SxOUTVTREG中。
SxTIME
定义串行端口x的波特率时序。
当SxTIME为1时,µVision3使用编程的波特率来仿真串口的时序。
当SxTIME为0时(默认值),编程的波特率时序被忽略,串行传输时间是即时的。
CLOCK
仿真的CPU的真实频率。
XTAL
仿真的CPU的晶振频率,在Options–Targetdialog下定义。
可以使用VTREGs来仿真外部输入和输出,包括与部外设的接口,如中断和时钟。
例如,假设选中PORT3〔在8051设备上〕的位2,如此CPU驱动仿真外部中断0。
I/O口
µVision3为每个I/O口定义了一个VTREG:
例如PORTA。
不要把每个端口(如PIOA_OSR)的外围存放器与这些VTREGs混淆了。
外围存放器可以在CPU存储空间被访问,VTREGs如此代表了引脚上的信号。
使用µVision3可以很容易模拟来自外部硬件上的输入,假设外部有一串脉冲到达端口引脚,如此可以使用信号函数来模拟这些信号。
如下面的信号函数以1000Hz的频率在端口PORTA的引脚0处输入一个方波。
signalvoidone_thou_hz(void){
while
(1){/*repeatforever*/
PORTA|=1;/*setPORTAbit0*/
swatch(0.0005);/*delayfor.0005secs*/
PORTA&=~1;/*clearPORTAbit0*/
swatch(0.0005);/*delayfor.0005secs*/
}/*repeat*/
}
下面的命令启动了这个函数:
one_thou_hz()
有关用户与信号函数的更多信息请参阅µVision3调试函数〔DebugFunctions)。
仿真一个与输出端口引脚对应的外部硬件稍少一些困难。
需要两步,第一步,写一个µVision3的用户或信号函数来执行希望的操作;第二步,创建一个断点以调用该用户函数。
假设使用了输出引脚(PORTA的位0)来点亮或熄灭LED,下面的信号函数使用PORT2VTREG来检查CPU的输出,并在命令窗口显示信息。
signalvoidcheck_pA0(void){
if(PORTA&1)){/*TestPORTAbit0*/
printf("LEDisON\n");}/*1?
LEDisON*/
else{/*0?
LEDisOFF*/
printf("LEDisOFF\n"):
}
}
现在,必须为端口1的写操作添加一个断点。
下面的命令行将为所有向PORT2的写操作添加一个断点。
BSWRITEPORT2,1,"check_p20()"
现在,不论目标程序何时向端口PORT2写入,check_P20函数都会打印出LED的当前状态。
串行端口
片上的串行端口由S0TIME、S0IN和S0OUT控制。
S0IN和S0OUT代表了CPU上的串行输入和串行输出流。
S0TIME用于指定串行端口的时序是即时的(STIME=0)还是与指定的波特率有关(SxTIME=1)。
当S0TIME为1时,串行窗口中显示的串行数据以指定的波特率被输出。
当S0TIME为0时,,串行窗口中显示的串行数据将被很快地输出。
模拟串行输入就像模拟数字输入一样容易。
假设有一个外部串行设备周期性(间隔1s)地输入指定的数据,可以建一个信号函数来向CPU的串口中输入数据。
signalvoidserial_input(void){
while
(1){/*repeatforever*/
twatch(CLOCK);/*Delayfor1second*/
S0IN='A';/*Sendfirstcharacter*/
twatch(CLOCK/900);/*Delayfor1charactertime*/
/*900isgoodfor9600baud*/
S0IN='B';/*Sendnextcharacter*/
twatch(CLOCK/900);
S0IN='C';/*Sendfinalcharacter*/
}/*repeat*/
}
当信号函数运行时,它延时1s,输入‘A’,‘B’和‘C’到串行输入行且重复下去。
串行输出被仿真的方式与使用用户或信号函数与一个如上所述的写访问断点很相似