首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在这种情况下,如何避免虚拟函数?

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

Stack Overflow用户
提问于 2017-01-05 02:08:10
回答 2查看 96关注 0票数 1

我有一种情况,我在单片机上有一个处理脉宽调制的类。极为简化的例子:

代码语言:javascript
复制
class MotorDriver
{
    int pin_;
public:
    MotorDriver(int pin);
    void init();
    void start();
    void stop();
    void changeDutyCycle(int dc);
};

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

代码语言:javascript
复制
motors[0].changeDutyCycle(50);
motors[1].changeDutyCycle(40);
....

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

代码语言:javascript
复制
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,但答案与我需要的内容有关:

如果您所处的情况是每个调用周期都很重要,也就是说,您在函数调用中所做的工作非常少,并且在性能关键的应用程序中从内部循环调用它,那么您可能需要一种完全不同的方法。

EN

回答 2

Stack Overflow用户

发布于 2017-01-05 07:36:22

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

检查这段代码-这是为了一个非常相似的目的(驱动步进电机)在STM32F4上,但应该给你一个想法。

代码语言:javascript
复制
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
EN

Stack Overflow用户

发布于 2017-01-05 21:03:07

您可以使用单个定时器中断所有的,这样你就不会被限制为计时器的数量。与其改变定时器的占空比设置,你只需改变变量,也就是说,每一个X都会勾选/设置/重置与此马达对应的引脚。在计时器例程中,您只需创建简单的for循环,迭代等于连接的电机数,并检查每一个,即模块操作,如果现在是更换引脚的时间。在这种情况下,使用定时器中断的软件PWM是很好的选择。

票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41476286

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档