PWM #3 - Let's Dive into Coding
- BÜNYAMİN BUĞRA KORKMAZER
- May 14, 2023
- 2 min read
Updated: May 15, 2023
In the previous sections, we examined the documents and formulated our own algorithm. Now, we're progressing to the coding phase. We'll be utilizing the M031TB development kit and programming in the Keil uVision IDE.

Before we start coding, it's essential to know precisely what we want from the PWM signal. That is, is the frequency of the PWM signal critical for us, or is its resolution? Generally, a PWM signal is assessed based on these two parameters. In our example, the frequency of the PWM isn't critically significant, though we can manipulate the frequency within specific limits. The PWM signal in our example, which has a 16-bit resolution, takes a value between 0-1000.
Let's determine the adjustments we need to make in the PWM unit based on these requirements:
The PWM0_CLK0 value originates from the HXT at 32MHz.
To achieve the desired resolution, we set the CNT value to 999 (as counting starts from 0).
The frequency of the PWM signal is calculated with the formula (PWM0_CLK0) / ((Prescaler+1) * (CNT+1)).
Given that the CNT value is 999, the PWM frequency drops to 32kHz. For a 1kHz PWM frequency, the Prescaler value should be 31.
The Comparator value changes within the cycle. We initially set the Comparator value to 0.
In the Pulse Generator settings, we set Zero Point to HIGH, CMPU value to LOW, and Period Point value to NOTHING.
Once we activate the Enable unit and start the Counter unit counting, the initial setup of the PWM unit is complete.
As the CMP value changes within the cycle, the Duty Cycle changes accordingly.
First, we set up the clock. As we are focusing on PWM in this article, we will leave the details of clock settings for a future discussion.
The code for the clock setting is:
CLK_EnableXtalRC(CLK_PWRCTL_HXTEN_Msk);
CLK_WaitClockReady(CLK_STATUS_HXTSTB_Msk);
CLK_SetHCLK(CLK_CLKSEL0_HCLKSEL_HIRC, CLK_CLKDIV0_HCLK(1));
CLK->PCLKDIV = (CLK_PCLKDIV_APB0DIV_DIV1 | CLK_PCLKDIV_APB1DIV_DIV1);
CLK_EnableModuleClock(PWM0_MODULE);
CLK_SetModuleClock(PWM0_MODULE, CLK_CLKSEL2_PWM0SEL_PCLK0, MODULE_NoMsk);
SystemCoreClockUpdate();
Then we adjust the pins:
SYS->GPA_MFPL = SYS_GPA_MFPL_PA1MFP_PWM0_CH0;
Next, we perform the initial adjustments:
PWM_SetClockSource(PWM0, 2, PWM_CLKSRC_PWM_CLK); // Selecting PWM0_CLK0 source
PWM_SET_PRESCALER(PWM0, 2, 0x1F); // Setting Prescaler value to 31
PWM_SET_CNR(PWM0, 2, 0x3E7); // Setting Counter value to 999
PWM0->CTL1 &= ~(0x3ul << 4 ); // Setting Counter unit to Up Counter mode
PWM_SET_CMR(PWM0, 2, 0x00); // Set for 0% Duty Cycle at the start
PWM_SET_OUTPUT_LEVEL(PWM0, PWM_CH_2_MASK, PWM_OUTPUT_NOTHING,
PWM_OUTPUT_LOW, PWM_OUTPUT_HIGH,
PWM_OUTPUT_NOTHING); // Setting up the Pulse Generator unit
PWM_EnableOutput(PWM0, PWM_CH_2_MASK); // Activating the key in the Output Control unit
PWM_Start(PWM0, PWM_CH_2_MASK); // Activating the Counter unit
We adjust the CMR value to alter the Duty Cycle value at the desired point. By providing a value between 0 and 1000, we can achieve the desired Duty Cycle value.
PWM_SET_CMR(PWM0, 2, 0x1F4); // %50 Duty Cycle
In our next article, we'll be testing the code we've written.
Comments