在这种情况下如何避免虚函数?

时间:2017-01-05 02:08:10

标签: inheritance embedded virtual-functions

我有一种情况,我在微控制器上有一个处理脉冲宽度调制的课程。非常简化的例子:

class MotorDriver
{
    int pin_;
public:
    MotorDriver(int pin);
    void init();
    void start();
    void stop();
    void changeDutyCycle(int dc);
};

它具有初始化,启动,停止和更改pwm的功能。如果我将4个电机连接到微控制器,我将创建该类的4个实例并将它们放入一个数组中,然后调用类似

的函数
motors[0].changeDutyCycle(50);
motors[1].changeDutyCycle(40);
....

问题出现是因为没有通用的方法在所述微控制器上配置定时器。例如,一个电机必须使用Timer3,而另一个电机必须使用Timer4。不同的定时器有不同的位大小,寄存器,通道,引脚......我希望能够为每个定时器编写自定义函数,但仍然能够将所有对象放入相同的数组并调用它们的函数,即

class MotorDriver
{
    void changeDutyCycle(int dc) = 0;
};

class MotorDriver1 : public MotorDriver
{
    void changeDutyCycle(int dc)
    {
        TIM3->CCR2 = dc;
    }
};

class MotorDriver2 : public MotorDriver
{
    void changeDutyCycle(int dc)
    {
        TIM4->CCR1 = dc;
    }
};

MotorDriver1 md1();
MotorDriver2 md2();
MotorDriver* mds[] = { &md1, &md2 };

int main()
{
    mds[0]->changeDutyCycle(10);
    mds[1]->changeDutyCycle(20);
}

我知道我可以通过虚拟功能实现我想要的功能。此功能很短并且会经常调用,因此虚拟功能的价格很高。有没有办法在这种情况下避免它们或不同的设计模式?目标是拥有易于在外部使用的可重用代码。在数组中拥有我需要的所有内容会使事情变得更加容易。

编辑:我知道这篇文章Avoiding virtual functions,但答案与我需要的内容有关:

  

如果您处于每次通话的每个周期都很重要的情况,那就是   你在函数调用中做的很少,你正在调用   它来自你在性能关键应用程序中的内循环   可能需要一个完全不同的方法。

2 个答案:

答案 0 :(得分:-1)

定时器之间的差异通常非常小,特别是在配置实际输出宽度时 - 初始化可能不同,但您可以使用虚拟功能。只需在类中存储对基础TIM寄存器和通道索引的引用,这似乎只需要做。如果您使用“互补”渠道之类的东西,那么您可以将它们存储为负面索引。

检查此代码 - 它是用于STM32F4的非常相似的目的(驱动步进电机),但应该给你一个想法。

namespace
{

/// array with all CCR registers
const decltype(&TIM_TypeDef::CCR1) ccrs[]
{
        &TIM_TypeDef::CCR1,
        &TIM_TypeDef::CCR2,
        &TIM_TypeDef::CCR3,
        &TIM_TypeDef::CCR4
};

constexpr bool isAdvancedControlTimer(const TIM_TypeDef& tim)
{
    return &tim == TIM1 || &tim == TIM8;
}

}   // namespace

TIM_TypeDef& HardwareTimer::getTim() const
{
    //  "timBase_" is "uintptr_t timBase_;"
    // initialized with TIM1_BASE, TIM2_BASE, ...
    return *reinterpret_cast<TIM_TypeDef*>(timBase_);
}

int HardwareTimer::start(const int8_t channel, const uint16_t compare) const
{
    if (channel == 0)
        return EINVAL;
    const auto complementaryChannel = channel < 0;
    const auto channelShift = (complementaryChannel == true ? -channel : channel) - 1;
    if (channelShift >= 4)
        return EINVAL;
    auto& tim = getTim();
    const auto advancedControlTimer = isAdvancedControlTimer(tim);
    if (complementaryChannel == true && advancedControlTimer == false)
        return EINVAL;

    tim.*ccrs[channelShift] = compare;
    if (advancedControlTimer == true)
        tim.BDTR |= TIM_BDTR_MOE;
    tim.CR1 |= TIM_CR1_CEN | TIM_CR1_URS;

    return 0;
}

不要太担心性能 - 实际上微控制器非常快,只需使用适当的架构(如RTOS或事件驱动)就会导致它们在80-90%的时间内无聊!

如果实现简单代码并且实际上会导致应用程序太慢,那么 - 假设您无法改进算法或整体架构 - 只需预先计算构造函数中start()的大部分值并且可能会丢弃错误检查(或将其移动到其他地方,从循环中移出)。

或者只是使用虚函数,间接调用的影响通常可以忽略不计。

答案 1 :(得分:-1)

您可以使用单个定时器中断,然后您不会受限于定时器的数量。您可以更改变量,而不是更改定时器的占空比设置,而是可以说每个X刻度切换/设置/重置与此电机对应的引脚。在定时器例程中,您只需创建简单的for循环,迭代次数等于连接的电机数量,如果现在是更改引脚的时间,则检查每个模数运算。在这种情况下,使用定时器中断的软件PWM是不错的选择。