我有一种情况,我在微控制器上有一个处理脉冲宽度调制的课程。非常简化的例子:
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,但答案与我需要的内容有关:
如果您处于每次通话的每个周期都很重要的情况,那就是 你在函数调用中做的很少,你正在调用 它来自你在性能关键应用程序中的内循环 可能需要一个完全不同的方法。
答案 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是不错的选择。