在Windows下的STM32CubeIDE中本地运行单元测试

引言


每个人都知道单元测试的好处。 首先,与代码同时编写测试可以使您及早发现错误,而不会在随后的耗时的复杂调试中浪费时间。 在嵌入式开发的情况下,单元测试具有以下特性:首先,该代码运行在设备肠道的深处,很难与之交互;其次,该代码与目标硬件紧密相关。 。


如果项目中存在不依赖于硬件的片段,并且同时实现了非常复杂的逻辑,则使用单元测试将为它们带来最大的好处。 例如,它可以是数据传输协议,各种计算或控制状态机的实现。


有三种方法可以运行嵌入式平台的单元测试:


  1. 直接在目标平台上启动。 在这种情况下,您可以使用设备的设备,并且代码将与战斗条件完全相同。 但是,为了进行测试,您将需要对设备进行物理访问。 另外,由于需要不断将代码下载到设备,因此测试周期将很长。
  2. 在模拟器上运行。 此方法之所以不错,主要是因为即使目标平台不可用,它也可以使您工作(例如,因为尚未完成)。 缺点是复制铁(和周围世界)行为的精度有限,以及创建此类仿真器的难度。
  3. 在主机上(本地)运行。 它不适用于该设备(可以改用测试存根),但是测试将快速启动并完成,并且您不需要访问目标设备。 使用此方法的一个很好的例子是在微控制器上测试一种计算算法的实现,该微控制器本身并不依赖于硬件,而是使用设备的传感器数据。 使用真实的数据源测试算法将非常不便,最好一次记录这些测量值并已经对存储的数据进行测试。 该脚本将在本地运行测试,稍后将进行讨论。

该出版物提供了一种基于Eclipse在STM32CubeIDE环境中配置单元测试的方法,旨在用于STM32系列控制器的开发。 开发语言是C,但是测试本身是用C ++编写的。 测试将在使用Cygwin的Windows主机上运行。 作为测试框架,使用了Google Test。 结果将显示在用于单元测试的特殊插件窗口中,并且可以使用STM32项目中的一个按钮启动结果:



所描述的方法适用于其他基于Eclipse的开发环境,除非为了给开发人员带来方便,优秀的制造商将它们削减得太多。 此方法也可以在Linux下与CubeIDE一起使用,而无需费心Cygwin。


您将需要


  1. Cygwin 3.0.7 x86(由于测试针对的是32位微控制器,因此我们还将在64位平台上使用32位环境)
  2. 适用于Windows的STM32CubeIDE 1.0.2。
  3. Google测试框架1.8.1

安装Cygwin和STM32CubeIDE


西格温


安装Cygwin,x86版本。 在安装程序中,选择其他软件包:gcc-core,g ++,binutils,automake,autoconf,cmake,libtool,gdb,make。 您可以安装软件包的最新稳定版本。



您还需要注册环境变量:


路径: ...; C:\ <路径到Cygwin> \ Cygwin \ bin; C:\ <路径到Cygwin> \ Cygwin \ lib
类路径: C:\ <Cygwin路径> Cygwin \ lib


STM32CubeIDE


该环境照常安装。 建议在Cygwin之后安装CubeIDE,因为在这种情况下,Cube将使用现有的Cygwin工具链。


首先,为x86 Cygwin平台创建一个C ++项目。 为了首先检查工具链的功能,我们将需要它,其次,我们将其用作主项目的装配配置的“捐助者”。


选择“文件”>“新建”>“ C / C ++项目”。 选择“ C ++托管版本”。 我们为Cygwin GCC工具链创建一个hello world类型的项目:



接下来,您将需要选择要创建的程序集配置。 只需Debug就足够了。
现在,您可以通过选择“项目”>“全部构建”来验证该项目正在运行。 还建议通过运行“运行”>“调试为”>“本地C / C ++应用程序”在Cygwin下检查调试。 该应用程序会将“ Hello world”输出到CubeIDE内的控制台。


为了使调试器在源代码文件中显示可执行行,您需要配置路径的显示。 在“窗口”>“首选项”窗口中的“ C / C ++”>“调试”选项卡中,选择“源查找路径”并添加一个新显示:“添加”>“路径映射”。 在窗口中,您需要命名类似新显示的名称,并为系统中的磁盘添加行:


  • \ cygdrive \ c-C:\
  • \ cygdrive \ g-G:\



为了进行漂亮的测试,我们还需要Eclipse插件,该插件支持C ++的单元测试。 它是直接从STM32CubeIDE安装的:菜单“帮助”>“安装新软件”,然后选择Eclipse Repository并安装C / C ++单元测试支持插件。



建立Google测试库


该库的源代码可以在以下网址获取: https : //github.com/google/googletest/tree/release-1.8.1
解压缩源代码,使用Cygwin终端转到googletest-release-1.8.1目录,然后运行:


cmake . make 

成功组装后,静态库文件将位于./googlemock/lib/libgtest.a中,而头文件将位于./googletest/include/gtest/目录中。 它们将需要复制到我们的项目中(或在项目设置中写入这些文件的路径)。


为STM32创建项目


STM32L476G-DISCO调试板的设计。 该示例不会太复杂-板上有两个LED,让它们显示从00到11的二进制计数器。我们将为该计数器实现一个单独的模块,在.h和.c文件对中进行描述,并为此进行测试。
可以使用多维数据集配置器照常创建项目,主要是确保将PB2和PE8引脚配置为数字输出。 创建项目时,最好指定类型-C ++,这将是编译测试所必需的(主要代码仍将由C编译器编译)。 通过单击人民币项目的名称并选择“转换为C ++”,以后可以从C转换项目。


为了在MK下进行编译和测试,我们需要两种不同的程序集配置。 在这些配置中,将收集不同的文件集-主要的将获得用于硬件的模块和经过测试的模块,而测试的将获得相同的经过测试的模块和测试文件。 因此,我们将在项目的根目录创建不同的目录-具有MK应用程序代码的应用程序(您可以简单地重命名Cube创建的Src目录),不依赖铁的模块的Common(我们将对其进行测试)和测试测试。 通过在目录的名称上单击人民币,菜单“资源配置”>“从构建中排除”,可以从目录中排除目录。


将我们的计数器模块添加到Common目录:


Led_counter代码

(led_counter.h):


 #ifndef LED_COUNTER_H_ #define LED_COUNTER_H_ #include <stdint.h> void Led_Counter_Init(); uint8_t Led_Counter_Get_Next(); #endif /* LED_COUNTER_H_ */ 

led_counter.cpp:


 #include "led_counter.h" static uint8_t led_cnt_state = 0; void Led_Counter_Init() { led_cnt_state = 0; } uint8_t Led_Counter_Get_Next() { if(++led_cnt_state > 3) led_cnt_state = 0; return led_cnt_state; } 

必须将Common和Tests目录添加到包含文件的搜索路径中:项目属性(属性)> C / C ++常规>路径和符号>包含。


添加与主LED一起使用


片段main.c

main.c:


 /* USER CODE BEGIN Includes */ #include "led_counter.h" /* USER CODE END Includes */ … int main(void) { … /* USER CODE BEGIN WHILE */ Led_Counter_Init(); uint8_t led_state = 0; while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ led_state = Led_Counter_Get_Next(); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, led_state & (1<<0)); HAL_GPIO_WritePin(GPIOE, GPIO_PIN_8, led_state & (1<<1)); HAL_Delay(500); } /* USER CODE END 3 */ … } 

该项目应编译并运行,并且LED应当闪烁。


编写测试


现在一切都开始了。


通过项目属性-“属性”>“ C / C ++构建”>“设置”>“管理配置”来创建新的构建配置。 CubeIDE只是不允许您在Cygwin下创建用于构建的配置,因此请从我们之前创建的项目中复制它:



现在,您需要切换到此配置,并配置源文件和头文件的路径。 在我们规定的“路径和符号”选项卡的项目属性中(添加条目时,最好在“添加到所有语言”字段中添加一个DAW):


  • 包括-测试/公司,通用/公司
  • 图书馆-GTest
  • 库路径-测试/库
  • 源位置-/ <prj_name> /通用和/ <prj_name> /测试(将<prj_name>替换为项目名称)

接下来,将gtest库-.a文件复制到Tests / Lib目录中,并复制到gtest文件夹中的头文件-复制到Tests / Inc文件夹中。 在“测试”文件夹中,创建一个新的main.cpp文件,将在其中运行测试。 其内容是标准的:


main.cpp:


 /* * Unit tests main file */ #include "gtest/gtest.h" int main(int argc, char *argv[]) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } 

此外,要检查设置,我们将创建一个测试,该测试将检查环境中指针的大小是否为32位(我们要确保它与微控制器上的指针大小相同,为此我们设置了32位Cygwin)。


创建以下test_platform.cpp测试文件:


 #include "gtest/gtest.h" TEST(PlatformTest, TestPointerSize) { //Check pointer size is 32 bit ASSERT_EQ(sizeof(void*)*8, 32U); } 

现在,如果项目按常规C ++应用程序运行,则调试输出将包含来自Google Test的消息,说明所有测试均已通过。


项目结构应如下所示:


现在,我们将为我们的LED计数器模块编写测试。 测试文件可以位于“测试”文件夹中:


test_led_counter.cpp
 #include "gtest/gtest.h" extern "C" { #include "led_counter.h" } // Test fixture class LedCounterTest: public ::testing::Test { protected: void SetUp() { Led_Counter_Init(); } }; // Check initial value TEST_F(LedCounterTest, TestInitialValue) { Led_Counter_Init(); ASSERT_EQ(Led_Counter_Get_Next(), 1); } // Check how value is incremented TEST_F(LedCounterTest, TestIncrementValue) { Led_Counter_Init(); unsigned int val = Led_Counter_Get_Next(); for(int i=0;i<1;i++) { ASSERT_EQ(Led_Counter_Get_Next(), ++val); } } // Check how value return to 0 after 3 TEST_F(LedCounterTest, TestZeroCrossing) { Led_Counter_Init(); for(int i=0;i<3;i++) { Led_Counter_Get_Next(); } ASSERT_EQ(Led_Counter_Get_Next(), 0); } 

为了使测试结果显示在漂亮的窗口中,您需要在“运行”>“调试配置”菜单中创建一个新的启动配置。 安装的插件允许您创建C / C ++单元类型的配置。 创建它,调用运行测试,选择所使用的“测试”程序集配置,然后取消选中“调试器”选项卡上的“启动时停止”复选框。 之后,可以开始配置。


要显示结果窗口,请在“窗口”>“显示视图”>“其他”>“ C / C ++”>“ C / C ++单位”中选择它。



做完了! 现在,该项目可以像往常一样在目标MK下编译并运行。 当需要运行本地测试时,在运行“运行测试”配置时,将自动为x86重建项目,环境将运行测试并显示结果。


文学作品


  1. J.格林宁。 嵌入式C的测试驱动开发-嵌入式系统的单元测试和TDD方法的应用的基础工作。
  2. https://uncannier.com/unit-testing-of-embedded-firmware-part-1-software-confucius/-在Texas Instruments Code Composer Studio CppUTest框架中对x86微控制器代码进行单元测试
  3. http://blog.atollic.com/why-running-your-embedded-arm-cortex-code-on-a-host-pc-is-a-thing-关于为什么运行代码可能有用的文章用于桌面平台上的微控制器

Source: https://habr.com/ru/post/zh-CN469367/


All Articles