> For the complete documentation index, see [llms.txt](https://nxp.gitbook.io/nxp-cup/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://nxp.gitbook.io/nxp-cup/2025-fall-2026-nxp-cup-mcxn947/mcxn947-software-setup/pwm-tutorial.md).

# PWM Tutorial

## PWM Control with CTIMER on the FRDM–MCXN947 Board

### 🎯 Objective:

Learn how to configure and generate PWM signals using the CTIMER peripheral in MCUXpresso IDE on the FRDM–MCXN947 development board.

***

### 🔍 What are CTIMER and PWM?

**CTIMER (Counter/Timer)** is a versatile on-chip peripheral found in NXP microcontrollers that can operate as a timer, counter, or PWM generator. **PWM (Pulse-Width Modulation)** is a technique to encode analog values using a digital signal by varying the duty cycle. It is widely used for motor control, LED dimming, and signal generation.

***

### 🖥️ Tools and Requirements

* **IDE**: MCUXpresso IDE (v11.5.0 or later)
* **Board**: FRDM–MCXN947 development kit
* **Compiler**: GNU Arm Embedded Toolchain (bundled with IDE)
* **Connectivity**: USB cable for board programming and debugging

***

### 🧭 Workspace Overview

When you open your project in MCUXpresso IDE:

* **Peripherals Perspective**: Configure on-chip peripherals visually.
* **Problems View**: Displays configuration errors and quick-fix suggestions.
* **Pins Perspective**: Assign and route physical pins to peripheral functions.
* **Project Explorer**: Browse source files and configuration.

***

## ✏️ Step-by-Step PWM Configuration

### 🧱 Main Steps

* **Enable CTIMER Driver** in Peripheral Drivers section.
* **Set Mode to PWM** and configure clock prescaler.
* **Define PWM Channels** and default duty cycles.
* **Route Pins** in Pins Perspective or via quick-fix.
* **Generate Code** and call the PWM update function in your application.

***

### 🔹 Step 1: Open Peripherals Perspective

1. In MCUXpresso IDE, click the **Peripherals** tab or switch via the perspective icon.
2. Expand **Peripheral Drivers (Device specific)** and click **+** to add a new driver.

<figure><img src="https://2412084329-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-L9GLtaxrQtBdBRsFIJB%2Fuploads%2Fk3gXgpxYTHMfaU8SeOi6%2F1_peripheral_drivers.png?alt=media&amp;token=c1e905c4-14b1-4fd0-b03b-d672a17c7ebd" alt=""><figcaption></figcaption></figure>

### 🔹 Step 2: Enable CTIMER and Set Mode

1. Search for **ctimer** in the drivers list and press **OK**.

<figure><img src="https://2412084329-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-L9GLtaxrQtBdBRsFIJB%2Fuploads%2FjMUasSbPDhvBXbpZdN3C%2F2_add_ctimer.png?alt=media&amp;token=e751f776-9302-40b8-9b31-b584959838c9" alt=""><figcaption></figcaption></figure>

2. In the CTIMER instance list, select the desired timer (e.g., CTIMER1).&#x20;

<figure><img src="https://2412084329-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-L9GLtaxrQtBdBRsFIJB%2Fuploads%2FAa8gc7qw4MpENEYT4dPQ%2F3_select_ctimer.png?alt=media&amp;token=9c95101c-3953-4820-995a-175ad10a181e" alt=""><figcaption></figcaption></figure>

3. Under **Mode**, choose **PWM**.

<figure><img src="https://2412084329-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-L9GLtaxrQtBdBRsFIJB%2Fuploads%2F32W2oAAjvacpsmhZEyhe%2F4_mode_pwm.png?alt=media&amp;token=3c1af72d-5c74-497d-9ae9-4e32804fc481" alt=""><figcaption></figcaption></figure>

### 🔹 Step 3: Resolve Clock Errors

* Go to the **Problems** tab (right corner).
* Right-click any CTIMER\_clock error (bulb icon) and apply the suggested fix to enable its clock.

<figure><img src="https://2412084329-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-L9GLtaxrQtBdBRsFIJB%2Fuploads%2FZw9J5ntwlAnkbBJaMh4P%2F5_clock_enable.png?alt=media&amp;token=c6b814e4-3ab2-48da-ab84-ea768db50644" alt=""><figcaption></figcaption></figure>

### 🔹 Step 4: Configure Timer Frequency and Prescaler

1. In the CTIMER settings, set the **Input Clock Frequency** or **Prescaler** so that the timer base frequency supports your desired PWM frequency.
   * *Explanation*: The prescaler divides the bus clock to achieve a timer tick rate matching the PWM period requirements.

<figure><img src="https://2412084329-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-L9GLtaxrQtBdBRsFIJB%2Fuploads%2Fc2dsDXOWk6tZ0db4dntG%2F6_set_prescaler.png?alt=media&amp;token=9cfd8ae6-0d4e-4226-9095-d5d35c96e549" alt=""><figcaption></figcaption></figure>

2. **Start the timer in initialization code.**
   * *Explanation*: The CTIMER counter must be running for PWM outputs to be generated. Check “Start the timer in initialization code” so the timer starts automatically during peripherals initialization—no manual CTIMER\_StartTimer(...) call needed.

<figure><img src="https://2412084329-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-L9GLtaxrQtBdBRsFIJB%2Fuploads%2FsnA75GJw9gaZnz41XeRs%2F9_start_timer.png?alt=media&amp;token=b4f772eb-0b0e-4c23-92e0-b8a4b7e90d8e" alt=""><figcaption></figcaption></figure>

3. Enter the **PWM Frequency** for the selected CTIMER channel.

<figure><img src="https://2412084329-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-L9GLtaxrQtBdBRsFIJB%2Fuploads%2FiNwfrpa2U5zAc4QDG8BO%2F7_set_pwm_freq.png?alt=media&amp;token=7b54f88b-4b7f-4539-9156-3f496bbf95e6" alt=""><figcaption></figcaption></figure>

### 🔹 Step 5: Add and Configure PWM Channels

1. In the **PWM Channels** section, click **+** to add a channel.
2. Choose a channel number (1‒n, avoid channel 0) and set the **Default Duty Cycle** (applied at initialization before runtime updates).

<figure><img src="https://2412084329-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-L9GLtaxrQtBdBRsFIJB%2Fuploads%2F3a6g3AJikvmnRsSiHpwJ%2F8_add_pwm_channels.png?alt=media&amp;token=fbefa1ac-381b-4e43-9057-31f3f2720834" alt=""><figcaption></figcaption></figure>

### 🔹 Step 6: Route the PWM Pin

* **Quick-Fix Method**: In the **Problems** view, right-click the PWM pin assignment error and accept the suggested route.

<figure><img src="https://2412084329-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-L9GLtaxrQtBdBRsFIJB%2Fuploads%2FwhMutk1nnjX0Frc5NZAG%2F10_route_quick_fix.png?alt=media&amp;token=43aeb1b1-598a-4782-9488-251b09c2af89" alt=""><figcaption></figcaption></figure>

* **Pins Perspective Method**: Switch to **Pins**, search for `ctimer<no>:match<pwm_channel_no>`, enable the pin, and set **Direction** to **Output** (Routing Details - bottom section).

<figure><img src="https://2412084329-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-L9GLtaxrQtBdBRsFIJB%2Fuploads%2FOXEEBH4dAjMp7xsQjsbj%2F11_route_pin_tool.png?alt=media&amp;token=3a8776ea-0e86-46c4-9a84-173ad7361d50" alt=""><figcaption></figcaption></figure>

> **Tip**: Verify the routed pin is available on a board header by consulting the FRDM–MCXN947 schematic.

### 🔹 Step 7: Generate Code

* After completing configuration, click **Update Code** to regenerate driver initialization based on your configuration.

<figure><img src="https://2412084329-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-L9GLtaxrQtBdBRsFIJB%2Fuploads%2FCJDFOQpypbqS3RkX8xzQ%2F12_update_code.png?alt=media&amp;token=b33e9b86-297b-44a1-9d60-87b61e4ad508" alt=""><figcaption></figcaption></figure>

### 🔹 Step 8: Using PWM in Application Code

Call the generated driver function to update duty cycle at runtime:

```c
CTIMER_UpdatePwmDutycycle(
    CTIMER<no>_PERIPHERAL,                      // e.g., CTIMER1_PERIPHERAL
    CTIMER<no>_PWM_PERIOD_CH,                   // the period channel defined in config
    CTIMER<no>_PWM_<pwm_channel_no>_CHANNEL,    // the channel index
    dutyPercent                                // new duty cycle in percent (integer)
);
```

***

### ⚙️ Advanced: Using Match Registers for Precise Duty Control

When you need more precise control—for example, fractional duty cycles for servos or fine-grained LED brightness—you can calculate and write directly to the timer's match registers.

```c
uint32_t periodTicks = CTIMER<no>_PERIPHERAL->MR[CTIMER<no>_PWM_PERIOD_CH];
uint32_t pulseTicks  = (uint32_t)((periodTicks * dutyPercent) / 100.0);
CTIMER<no>_PERIPHERAL->MR[<channelIndex>] = pulseTicks;
```

#### Explanation of Ticks and Duty Translation

* **`periodTicks`**: Total number of timer ticks for one full PWM period. It is read from the match register `MR[CTIMER<no>_PWM_PERIOD_CH]` which defines the period channel.
* **Timer Tick Duration**: With a bus clock of, say, 48 MHz and a prescaler value `P`, each tick occurs every `P / 48 000 000` seconds.
* **`dutyPercent`**: Desired duty cycle as a percentage (0–100).
* **Calculating `pulseTicks`**:
  1. Multiply `periodTicks` by `dutyPercent / 100.0` to get the fractional portion of the period.
  2. Cast to `uint32_t` to obtain an integer tick count.
* **Writing to `MR[channelIndex]`**: Setting this register tells the timer when (in ticks) to toggle the PWM output from high to low, achieving the specified duty cycle.

This method translates your percentage-based duty into concrete timer ticks, leveraging the full resolution of the CTIMER peripheral for precise PWM control.

***

## 🎉 Congratulations!

You have successfully configured and generated PWM signals on the FRDM–MCXN947 board using the CTIMER peripheral. Explore varying frequencies, multiple channels, and advanced timer features to build complex control applications.
