Using Ambiq Micro Apollo1 or Apollo2 MCUs CTIMER as PWM

A Pulse Width Modelation (PWM) has a duty and a cycle. The duty can be something between 0% and 100% of the cycle time.

For Ambiq Micro the CTIMER peripheral can be used to generate a PWM.

GPIO setup

First of all the GPIOs have to be set, see also Using GPIOs for more details.

The resulting code for the initialization of GPIO43 / PAD43 as PWM of timer TCTB0:

GPIO->PADKEY = 0x00000073; //unlock pin selection
GPIO->PADREGK_b.PAD43FNCSEL = 2; //set TCTB0 on pin 43
GPIO->PADREGK_b.PAD43INPEN = 1; //enable input on pin 43
GPIO->CFGF_b.GPIO43OUTCFG = 1; //output is push-pull
GPIO->PADKEY = 0; //lock pin selection

Timer setup

Next the CTIMER must be initialized. The CTIMER consists of several timers in one module. Following schematic can be found in the datasheet:

Ambiq Micro Apollo CTIMER

In general the maximum amount of resulting PWMs are 8, located to the output functionalies TCTA0, TCTB0, TCTA1, TCTB1, TCTA2, TCTA2, TCTA3 and TCTB3.

In the example TCTB0 at GPIO43 / PAD43 will be used.

First of all the timer must be cleared:

CTIMER->CTRL0_b.TMRB0CLR = 1; //clear timer B

Now different features can be set:

CTIMER->CTRL0_b.TMRB0PE = 1; //enable output timer B
CTIMER->CTRL0_b.TMRB0IE = 1; //enable IRQ timer B
CTIMER->CTRL0_b.TMRB0FN = 3; //repeated pulse count timer B
CTIMER->CTRL0_b.TMRB0CLK = 0x07; //XT div 2 == 48KHz timer B

In general the cycle time is related to the selected clock and divisor in the TMR<A/B><n>CLK field. In the case of XT div 2 the clock source is 48KHz. The cycle value defines the maximum value of the duty value and also the frequency. The frequency is f = fin / cycle_value. If the cycle value is 64, and fin 48KHz, the resulting frequency is 750Hz.

Now the cycle register and the duty register will be set:

CTIMER->CMPRB0_b.CMPR0B0 = 1; //periode – on-time timer B
CTIMER->CMPRB0_b.CMPR1B0 = 64; //on-time timer B

For Apollo it makes sense to update the duty value in a IRQ rouine, so IRQs must be enabled. For this all IRQ flags must be cleared and the interrupt for the compare value 0 must be enabled:

CTIMER->INTEN_b.CTMRB0INT = 1; //enable interrupt for compare value 0

Now the NVIC must be configured to use IRQs for the corresponding CTIMER as well. The NVIC is part of CMSIS and is decribed at the CMSIS website:

The initialization looks like this:

NVIC_ClearPendingIRQ(CTIMER_IRQn); //clear pending flag for ADC
NVIC_EnableIRQ(CTIMER_IRQn); //enable IRQ
NVIC_SetPriority(CTIMER_IRQn,1); //set priority of ADC IRQ, smaller value means higher priority

At last the timer must be released and started:

CTIMER->CTRL0_b.TMRB0CLR = 0; //release clear timer B
CTIMER->CTRL0_b.TMRB0EN = 1; //start timer B

Interrupt Service Routine

First a global variable should be defined:

volatile uint32_t u32Duty = 1;

The interrupt service routine is using this variable to update the duty value with the correct timing:

void CTIMER_IRQHandler(void)
     if (CTIMER->INTSTAT_b.CTMRB0INT == 1)
         CTIMER->INTCLR_b.CTMRB0INT = 1; //cear interrupt flag
         CTIMER->CMPRB0_b.CMPR0B0 = u32Duty; //set new duty value

The main application can now write the duty value into the variable and the ISR will update the HW automatically:

     for(int i = 1;i < 63;i++)
        u32Duty = i;
        SCB->SCR |= (1 << SCB_SCR_SLEEPDEEP_Pos); //set goto deepsleep (only possible if TPIU or SWO is not enabled
         __WFI(); //wait for interrupt


Run the code

The complete example is workable with MCU Templates for Apoll1 and Apollo2 available via or by contacting  FEEU directly

See also MCU Templates

Hardware Availability

The Apollo1 and Apollo2 MCUs and EVKs are available at the webshop of Fujitsu Electronics Europe GmbH (FEEU):


Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.