Keil μVision 5.30 CppUTest测试框架模拟器Simulator测试(2)-被测试工程
作者:互联网
被测项目介绍
此项目是一个以C++编写的框架,已经应用到了数十个产品中,虽然已经自认为比较成熟,但是近期出现了比较多的由于未进行数据有效性验证而出现的问题,这虽然可以归结为编程习惯欠佳的问题,但是也暴露了此前项目缺少完整测试导致对可能出现的问题没有预先处理的缺陷
故项目所需要为此前的代码添加测试.这里正好把添加测试的过程记录下来,因为此项目是一个真实的项目,其具有较高的实际意义.
为项目添加测试库
首先添加一个新的Target.因为测试项目要运行在Simulator上,需要修改一些项目配置,所以为了不影响此前的项目配置要新建一个.
向项目中添加测试库文件CppUTestCM0.lib
在
Options->C/C++/Include Paths
中,添加CppUTest包含目录,具体的路径要根据你CppUTest实际的路径来.
/cpputest-3.8/include/
添加平台相关文件
在这一步编译项目的话,会看到提示很多L6218E错误,此时要添加平台文件,这个文件中有CppUTest与平台相关的这些函数.
在Projext->TestLib上点击右键,添加文件,导航至:
cpputest-3.8->src->platforms->keil->UtestPlatform.cpp
添加这个Cpp文件,此时可编译通过.需要注意的是这里有个获取执行时间的函数:
///////////// Time in millis
/*
* In Keil MDK-ARM, clock() default implementation used semihosting.
* Resolutions is user adjustable (1 ms for now)
*/
static long TimeInMillisImplementation() {
//clock_t t = clock();
//t = t * 10;
//return t;
return 0;
}
如果需要实际时间则需要添加进去获取systemtick等时间基准的函数.
在主函数中运行测试
至此CppUTest已经添加到了项目中,但是还需要在Main中添加代码让测试运行起来.
由于CppUTest是基于C++的框架,因此如果你的Main函数是C语言的,就需要重新创建一个Main.cpp,并
- 包含文件
#include "CppUTest/CommandLineTestRunner.h"
- 在main函数加入以下代码
#ifdef SIMULATOR_TEST
const char * av_override[] = { "exe", "-v" };
CommandLineTestRunner::RunAllTests(2, av_override);
#endif
- 在项目选项中添加宏
Options->C/C++->Define:SIMULATOR_TEST
这样其他Target就可以不包含测试代码(即使使用了同一个main.cpp文件).
- 将输出重定向到Debug(Printf)窗口
这里要注意M0内核不支持Itm,无法使用ITM_SendChar函数,所以在模拟器运行的时候需要选择M4内核或者M3内核来模拟.
/typedef int FILEHANDLE;
struct __FILE { int handle; /* Add whatever you need here */ };
FILE __stdout;
int fputc(int ch, FILE *f) {
#if(USE_DEBUG_LOG)
return (sendchar(ch));
#elif SIMULATOR_TEST
#include "core_cm4.h"
return(ITM_SendChar(ch));//此处将输出重定向到Debug(printf)窗口
#else
return (ch);
#endif
}
这里注意下图中的选项要勾选,否则模拟器运行的时候会出错.
- 使用Simulator运行测试
此时点击Debug按钮进入模拟器测试,发现可以运行,但是Command窗口会不断提示error 65:,如下图所示:
此时需要为Simulator添加初始化脚本文件,路径如下: - 在MDK根目录下新建一个.ini文件,此处为
SimulatorDebugInit.ini
- 参考数据手册中的地址映射,允许访问这些地址
这里以M3的初始化文件为例,.ini中的内容为:
MAP 0x00000000, 0x000FFFFF READ EXEC WRITE // ALIASED FLASH
MAP 0x08000000, 0x080FFFFF READ EXEC // FLASH MEM
MAP 0x20000000, 0x20017FFF READ EXEC WRITE // SRAM
MAP 0x1044EBC0, 0x1044FFFF EXEC READ // unclear
MAP 0x1FFFE000, 0x1FFFF7FF READ EXEC WRITE
MAP 0x20018000, 0x3FFFFFFF READ WRITE
MAP 0x40000000, 0x400003FF READ WRITE
MAP 0x40000400, 0x400007FF READ WRITE
MAP 0x40000800, 0x40000BFF READ WRITE
MAP 0x40000C00, 0x40000FFF READ WRITE
MAP 0x40001000, 0x400013FF READ WRITE
MAP 0x40001400, 0x400017FF READ WRITE
MAP 0x40001800, 0x40001BFF READ WRITE
MAP 0x40001C00, 0x40001FFF READ WRITE
MAP 0x40002000, 0x400023FF READ WRITE
MAP 0x40003C00, 0x40003FFF READ WRITE // SPI3
MAP 0x40007000, 0x400073FF READ WRITE
MAP 0x40010000, 0x400103FF READ WRITE // AFIO
MAP 0x40010400, 0x400107FF READ WRITE // EXIT
MAP 0x40010800, 0x40010bFF READ WRITE // GPIOA
MAP 0x40010C00, 0x40010FFF READ WRITE // GPIOB
MAP 0x40011000, 0x400113FF READ WRITE // GPIOC
MAP 0x40011400, 0x400117FF READ WRITE // GPIOD
MAP 0x40011800, 0x40011bFF READ WRITE // GPIOE
MAP 0x40011C00, 0x40011FFF READ WRITE // GPIOF
MAP 0x40012000, 0x400123FF READ WRITE // GPIOG
MAP 0x40012400, 0x400127FF READ WRITE // ADC1
MAP 0x40012800, 0x40012bFF READ WRITE // ADC2
MAP 0x40012C00, 0x40012FFF READ WRITE // TIM1
MAP 0x40013000, 0x400133FF READ WRITE // SPI1
MAP 0x40013400, 0x400137FF READ WRITE // TIM8
MAP 0x40013800, 0x40013bFF READ WRITE // USART1
MAP 0x40013C00, 0x40013FFF READ WRITE // ADC3
MAP 0x40014C00, 0x40014FFF READ WRITE // TIM9
MAP 0x40015000, 0x400153FF READ WRITE // TIM10
MAP 0x40015400, 0x400157FF READ WRITE // TIM11
MAP 0x40018000, 0x400183FF READ WRITE // SDIO
MAP 0x40020000, 0x400203FF READ WRITE // DMA1
MAP 0x40020400, 0x400207FF READ WRITE // DMA1
MAP 0x40021000, 0x400213FF READ WRITE // RCC
MAP 0x40022000, 0x400223FF READ WRITE // FLASH INTERFACES 1&2
MAP 0x40023000, 0x400233FF READ WRITE // CRC
MAP 0x40021000, 0x400213FF READ WRITE
MAP 0x60000000, 0x63FFFFFF READ EXEC WRITE
MAP 0x70000000, 0x73FFFFFF READ EXEC
MAP 0x78000000, 0x78FFFFFF READ EXEC // FSMC BANK 2
具体的指令可以参考ArmKeil官方文档
这里简单解释一下,可以看到这里为每个地址都指定了可访问性,这样程序在运行的时候就可以去对应的:读写/执行这部分区域,虽然这里读写这些寄存器并没有什么实际的作用,而如果按照硬件的逻辑去忙等待一个标记为,那么程序在模拟器中运行的时候就会卡死.后面会看到相关的部分.
在添加了初始化文件之后,再次运行,发现已经已经有了打印,但之后便出发了hardfault_handler
这是因为当前分配的内存不够CppUTest使用,在模拟器运行的时候我们需要分配充裕的内存,因为没有硬件的限制.
修改栈的空间只应该针对当前Target,不能影响正常工程的配置,因此:
此时复制一份startup.s
汇编文件,并重命名为:startup_simulator.s
.
栈空间更改为0x00002000,堆空间改为0x00008000
Stack_Size EQU 0x00002000
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp
Heap_Size EQU 0x00008000
AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem SPACE Heap_Size
后面点击Debug以及全速运行开始测试,并打断点看下卡住的语句,如果是忙等待硬件赋值,那么将将此语句注释掉(最好新建一个文件在新建文件中操作,以免影响正常的硬件功能).
至此可以正常运行测试,结果如下图
可以看到现在系统中并无测试,所以此处测试为0.
添加一个测试
标签:CppUTest,MAP,Keil,READ,WRITE,添加,测试 来源: https://www.cnblogs.com/crz2014/p/16428167.html