Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/61.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 将代码组织成模块化的、自包含的块_C_Embedded_Stm32_Libopencm3_Ceedling - Fatal编程技术网

C 将代码组织成模块化的、自包含的块

C 将代码组织成模块化的、自包含的块,c,embedded,stm32,libopencm3,ceedling,C,Embedded,Stm32,Libopencm3,Ceedling,我正在寻求智慧的帮助。:-) 这不是关于测试本身的技术帮助,而是一个代码组织问题。 我正在做一个STM32项目,使用libopencm3 HAL和ceedling作为测试套件 我只是简单地引用了一小部分来证明这一点,显然代码中还有其他类似的模块 直接进入这个问题,main函数调用一个函数“Init”,它位于自己的模块“Init”中。Init从IO模块调用两个函数-IOInit和IOBlink,它们初始化GPIO并以特定模式闪烁GPIO IOInit和IOBlink的形式如下 IOInit(GPI

我正在寻求智慧的帮助。:-) 这不是关于测试本身的技术帮助,而是一个代码组织问题。 我正在做一个STM32项目,使用libopencm3 HAL和ceedling作为测试套件

我只是简单地引用了一小部分来证明这一点,显然代码中还有其他类似的模块

直接进入这个问题,main函数调用一个函数“Init”,它位于自己的模块“Init”中。Init从IO模块调用两个函数-IOInit和IOBlink,它们初始化GPIO并以特定模式闪烁GPIO

IOInit和IOBlink的形式如下

IOInit(GPIO)
IOBlink(PATTERN)
其中,GPIO和模式分别是状态指示灯和通电指示灯,它们可以是枚举或定义,具体取决于我选择如何构造它们

第一个问题是,在定义IOInit和IOBlink的参数时有什么意义??如果我想要某个参数在这里是详细的,但稍后在较低级别的函数调用中得到解决?我该如何定义这个论点??作为uint8_t,enum,#定义

所以它们看起来像

IOInit(GPIO)
{
    //Something like this 
    if (GPIO == STATUS_LED)
        {
            InitIO(STATUS_LED_PORT, OUTPUT, STATUS_LED);
        }
    else (GPIO == MODE_IO)
        {
            //Do something else..
        }
    ...
}

IOBlink(PATTERN)
{
    if (PATTERN == POWERUP)
        {
            //GPIOToggle(Pin, delay, repetitions)
            GPIOToggle(STATUS_LED, 500, 4);
        }
         else if (PATTERN == ERROR)
          // Do something else
}
现在,IOInit和IOBlink在位于HAL顶部的“DriverIO”模块中调用它们的低级函数。DriverIO具有相应的函数——InitIO和GPIOToggle,它们可能看起来像这样

InitIO(PORT, Mode, Pins)
{
    rcc_periph_clock_enable(PORT);
    gpio_set_mode(PORT, GPIO_MODE_OUTPUT_2_MHZ, Mode, Pins);
    //Need to find a way to resolve PORT, Mode and Pins into a HAL compatible format
}

GPIOToggle(Pin, delay, repetitions)
{
    for (uint8_t i = 0; i<= repetitions, i++)
        {
            gpio_toggle(STATUS_LED_PORT, Pin);
            delay_ms(delay);
        }
}
void rcc_periph_clock_enable(enum rcc_periph_clken clken);
它在垫片层中的外观如何??考虑到司机不知道enum rcc_periph_clken是什么样子

整体架构在设计时感觉良好。。但在实施过程中,它确实崩溃了。原则上,main只调用状态函数,每个状态函数只调用存在大部分控制逻辑的中间层函数,中间层调用位于HAL上的驱动层函数。 很抱歉我写了这么长的文章,但是我在设计和架构阶段花了大量的时间在这方面,我无法直接和连贯地思考。我患有某种分析麻痹症。这可能是一个非常简单的解决方案,但我无法直接看到它

再一次,如果我说的不太有道理,我向你道歉。。我已经盯着它看了好几天了,我认为它对其他人来说没有多大意义(如果有的话)。我试着让它变得可以理解,但那可能只是因为我缺乏睡眠交谈。请让我知道是否有一个具体的部分,我可以更好地解释,并明确

我们衷心感谢您的帮助

第一个问题是,在定义IOInit和IOBlink的参数时有什么意义

对于
IOInit
,它需要端口、pin和掩码。如果在同一端口上允许其他不相关的功能,或者如果驱动程序对其具有独占控制权,则必须小心。从程序中多个不相关的位置更新GPIO寄存器是一个坏主意,因为这可能会导致争用条件和其他类似的异常情况

根据我的经验,在简单的GPIO上编写抽象层往往弊大于利

在您的情况下,GPIO驱动程序实际上应该是一个“LED闪烁应用模块”,其级别略高于GPIO,因为它还必须利用定时器和/或PWM硬件外围设备。所以考虑把这个命名为别的。

对不同的闪烁模式使用枚举是一个好主意。然后,驱动程序可以在内部使用该枚举作为查找表等的索引,其中指定了时间和模式。除非出于某种原因希望计时器周期是可变的,否则也需要将其作为参数传递

现在,IOInit和IOBlink在位于HAL顶部的“DriverIO”模块中调用它们的低级函数

为什么!?然后,您就有了一些3-4个抽象层,用于一些完全简单的、不需要抽象的东西。这是非常糟糕的设计

1层有意义,处理LED图案的层。其余无用的臃肿软件中间层必须离开:它们只占用资源并充当bug的来源。您应该直接从LED模式模块访问寄存器。ST所谓的“HAL库”通常是有害的,应该避免使用。这意味着您必须重新设计其中的大部分

DriverIO不能被模仿和测试

以在功能性、可读性、可维护性和速度方面最有意义的方式设计程序。不要设计适合某些TDD buzzword测试套件模板的程序。在设计代码时,您完全可以将测试牢记在心,但不应该让它主宰设计

例如,您可以设计特定数量的测试函数,而不是模拟,这些函数随驱动程序一起提供,并且可以直接访问驱动程序的私有成员,但仅在调试构建中链接。这允许进行比模拟更深入的测试,模拟实际上只是一个“黑盒”测试

值得注意的是,在某个模式后闪烁LED非常容易测试,您实际上不需要功能模拟或其他任何东西,只需要强制性的示波器。这也是对所有嵌入式固件进行基准测试的正确工具


一般来说,关于TDD和测试,应该是这样的:

规范->程序设计->程序实施,包括测试->测试

因此,对于每个需求,程序中都有一个模块,对于每个这样的模块,您都有一个测试。测试的目的是查看代码是否符合其编写的要求,从而使产品符合指定的功能

这意味着您必须先有一个规范。程序设计不应该是突然发明的。您不应该实现您不需要的功能。测试应该