164. PWM Register Access Control

In embedded firmware, hardware timer peripherals often expose Capture/Compare Registers (CCR) that directly control PWM duty cycles. Writing invalid values to these registers can cause incorrect output waveforms or hardware glitches.

You are given a PWMDriver class that represents a PWM timer driver. The hardware timer has a fixed period of 100 ticks, meaning the valid range for the Capture/Compare Register is 0 to 100 (inclusive).

Currently, the CCR register is publicly accessible, allowing external code to write invalid values (negative values or values greater than 100).

Your task is to refactor the driver to enforce safe access control:

  • Encapsulate the CCR register by making it private
  • Provide a public setter method that updates the register using saturation logic
  • Values greater than 100 must be clamped to 100
  • Values less than 0 must be clamped to 0

This ensures the internal register value always remains valid, regardless of user input.

Program Flow:

  1. Initialize a PWMDriver instance.
  2. Read an integer N representing the number of input values.
  3. Loop N times:
    • Read an integer request_val
    • Attempt to update the PWM duty cycle using the driver interface
    • Print the actual value stored in the CCR register

Input Format:

  • First line: Integer N (number of test inputs)
  • Next N lines: Integer request_val (requested duty cycle in ticks)

Input is provided via standard input (stdin).

Output Format:

For each input, print the stored register value in the following format:

CCR: <value> 

Each output must appear on a new line.

Example:

Input

3
50
150
-20 

Output

CCR: 50
CCR: 100
CCR: 0 

Constraints:

  • N range: 1 to 20
  • request_val range: -1000 to 1000
  • Hardware period limit: 100 (inclusive)
  • Minimum register value: 0 (inclusive)
  • No dynamic memory allocation is allowed

 

 

 

 

Need Help? Refer to the Quick Guide below

Encapsulation is the mechanism of bundling data (variables) and methods (functions) together into a single unit (a Class) and restricting direct access to some of that object's components.

It is not just about "hiding secrets"; it is about System Integrity. By hiding internal data (private), you ensure that the object cannot be put into an invalid or dangerous state by external code. The object manages its own state through a controlled interface (public).

Access Specifiers (The Security Levels)

C++ provides three keywords to control who can see and modify class members.

SpecifierAccessible ByUsage Scenarios
publicEveryone (External code, main()).The Public API (functions users are meant to call).
privateOnly the Class itself.Internal state, helper functions, hardware register pointers.
protectedThe Class + Derived Classes (Children).Internal logic shared with children but hidden from the world.

Detailed Syntax & Usage

The "Safe Hardware" Example

Imagine a Motor Driver where the duty cycle must never exceed 90% (safety limit) and never be negative.

class MotorDriver {
private: 
    // DATA HIDING:
    // If this were public, a user could write 'duty_cycle = 200;' and burn the motor.
    float duty_cycle;
    uint32_t* pwm_reg; 

    // INTERNAL HELPER:
    // A complex hardware sequence user doesn't need to see.
    void trigger_update() { 
        *pwm_reg = (uint32_t)(duty_cycle * 1000); 
    }

public:
    // CONSTRUCTOR: Initializes state safely.
    MotorDriver() : duty_cycle(0.0f) { /* ... */ }

    // PUBLIC INTERFACE (Setter):
    // Contains Validation Logic (The "Invariant").
    void setSpeed(float speed) {
        if (speed > 90.0f) speed = 90.0f; // Clamp to safe max
        if (speed < 0.0f)  speed = 0.0f;  // Clamp to safe min
        
        duty_cycle = speed; // Update internal state
        trigger_update();   // Update hardware
    }

    // PUBLIC INTERFACE (Getter):
    // Read-only access.
    float getSpeed() const {
        return duty_cycle;
    }
};

Advanced Encapsulation Features

  • The friend Keyword (Controlled Breach):

    Sometimes, you want one specific external class or function to access your private members without making them public to the whole world.

    • Usage: Unit Testing (Test fixture needs to check private state) or tightly coupled classes (e.g., LinkedList accessing Node internals).
    class Box {
    private:
        int secret_data;
        friend class TestSuite; // 'TestSuite' can now touch 'secret_data'
    };
  • Struct vs. Class:

    Technically, they are almost identical. The only difference is the default encapsulation level.

    • class: Members are private by default. (Prefers Hiding).
    • struct: Members are public by default. (Prefers Exposure).
    • Best Practice: Use struct for "Plain Old Data" (POD) where there are no invariants to protect (e.g., Coordinate {x, y}). Use class for everything else.

Why Encapsulation Matters in Embedded Systems

  • Maintaining Invariants:

    An invariant is a condition that must always be true.

    • Example: "The Buffer Head index must always be < BUFFER_SIZE".
    • If head is public, user code buf.head++ might overflow. If head is private, the push() function guarantees the wrap-around logic (head = (head + 1) % SIZE).
  • Read-Only Hardware:

    Many sensors provide data that should be read but never written to by software.

    • Solution: Make the variable private and provide only a getVal() function. No setVal() means the data is effectively read-only.
  • Decoupling Implementation:

    You can change the internal logic without breaking user code.

    • Scenario: You switch from a software timer to a hardware timer.
    • If users accessed the timer_counter variable directly, their code breaks.
    • If users called timer.getTime(), you just update the function body. Their code remains untouched.

Performance Myths (Inlining)

The Fear: "Calling getSpeed() adds function call overhead (stack push/pop). Direct access motor.speed is faster."

The Reality: C++ compilers are incredibly smart. If you define the getter in the header file:

// In header
inline float getSpeed() const { return duty_cycle; }

The compiler performs Inlining. It replaces the function call with the direct assembly instruction to read the memory address.

Result: You get the safety of Encapsulation with Zero Performance Penalty.

Common Pitfalls & Best Practices

PitfallDetails
❌ Getters/Setters for EverythingDon't automatically create getVariable() and setVariable() for every private member. This breaks encapsulation by exposing internals. Only expose what is necessary for the API.
❌ Returning Non-Const PointersIf you have private: int* buffer; and a public function int* getBuf() { return buffer; }, you have broken encapsulation. The user can now trash your private memory through that pointer. Fix: Return const int* or a safe copy.
❌ Friend AbuseOverusing friend turns your code into "Spaghetti Code" where everything touches everything else. Use it only when strictly necessary.
✅ The "Const Correctness"Always mark getters as const (e.g., int get() const). This assures the compiler and the user that this function will not modify the object's state.