为什么我们需要向下转换一个变量,即使函数会在返回之前再次向上转换它?

Why do we need to downcast a variable even if the function will upcast it again right before returning?

在阅读一本关于现代 C++ 的书时,我看到了一段让我感到困惑的代码片段。编写的代码是为 8 位 AVR 微控制器设置 PWM(16 位定时器)。代码是这样的:

class pwm_base : private util::noncopyable
  {
  public:
    typedef std::uint_fast16_t duty_type;

    pwm_base(const duty_type resol,
             const duty_type duty = 0U) : resolution(resol),
                                          counter   (0U),
                                          duty_cycle(duty),
                                          shadow    (duty) { }

    duty_type get_resolution() const { return resolution; }

    void set_duty(const duty_type duty)
    {
      // Set a new duty cycle in the shadow register.
      mcal::irq::disable_all();

      shadow = static_cast<std::uint_fast8_t>((std::min)(duty, get_resolution())); // ???? (1)

      mcal::irq::enable_all();
    }

    duty_type get_duty() const
    {
      // Retrieve the duty cycle.
      mcal::irq::disable_all();

      const volatile std::uint_fast8_t the_duty = duty_cycle; // ???? (2)

      mcal::irq::enable_all();

      return the_duty;
    }

    virtual void service() = 0;

  protected:
    const duty_type resolution;
          duty_type counter;
          duty_type duty_cycle;
          duty_type shadow;
  };

我对 ???? 指示的行有问题。可以清楚地看到,shadowduty_cycle 都被定义为 uint_fast16_t。因此,这意味着它们必须至少为 16 位。如果是这样,为什么作者在第 ???? (1) 行将 min 方法的结果向下转换为 uint_fast8_t 而不是转换为 uint_fast16_t ?而且,为什么在 ???? (2) 行他再次将变量向下转换为 uint_fast8_t,即使函数 return 类型是 uint_fast16_t?这些向下转换是必需的吗?他们的目的是什么?

此代码似乎严重过度设计,只要允许使用 C++,这始终是最大的危险。该语言倾向于鼓励让事情不必要地复杂化,而不是利用 KISS principle.

硬性要求是:在给定的硬件上,CPU 在 8 位类型下工作速度最快,但 PWM 寄存器是 16 位。期间.

因此,任何描述周期或占空比的变量都需要正好 16 位。声明为uint_fast16_t是没有意义的,因为相应的硬件寄存器总是恰好是16位,不多也不少。

通常,uint_fast 类型仅在计划以某种方式将其移植到 32 位系统时才有用,但不太可能存在具有相同 PWM 外设的 32 位 MCU。因此在这种情况下 uint_fast 只是无用的噪音,因为代码永远不会被移植。

据推测,转换为 uint8_t 会将 16 位值截断为最低 8 位。如果是这样,则转换不正确,它不应该是 static_cast<std::uint_fast8_t>,而是 static_cast<std::uint8_t>。否则我不太了解代码应该做什么,我假设在某处更新占空比。

此外,禁用中断屏蔽作为重入保护功能可能没有意义,因为这会导致意外的时序问题。如果要从 ISR 内部使用此 class,则另一种保护机制可能更合适,例如信号量或原子 access/inline asm。 PWM 尤其挑剔 when/how 你改变占空比,否则你可能会出现故障。

总的来说,我强烈建议不要在古老的、资源严重受限的 8 位微控制器上使用 C++。或者在学习嵌入式系统时使用古老的 8 位微控制器而不是 ARM,8 位比 32 位更难用 C(或 C++)编程。