Is there any code for programming PWM on M4?

We would like to see if we can start a PWM from the M4 core.
We were not able to find any PWM code or mentioning in the Freescale documentation.
We would appreciate if any of you could point us to any resources.

There are currently no drivers for the PWM peripheral. However, infrastructure is there (interrupt handler PWM1_Handler …, register definitions PWM1_PWMCR… , resource domain controller rdcPdapPwm1…). The PWM peripheral is fairly simple, so you can write the relevant registers directly from your FreeRTOS application. Refer to the i.MX 7 Reference Manual for details about the PWM peripheral.

Are there any plans to provide a driver for the PWM peripherial in the future?

Dear @SimonNu

We plan to improve our offering for the M4 cores on all modules. We are currently running a survey to find out what are the most efficient pain points to tackle.

As PWM is not too complex, chances are good that we can offer a simple driver. However, there are no particular plans nor schedules yet.

Regards, Andy

Check out my PWM modulator/controller using GPT. The video is here…The same PWM technique can be used to control your motor because the M4 is fast enough to do PWM work. I use the Generic Timer inside the imx7. Just got to get your prescaler right but I use a simple milliseconds conversion technique setup by NXP in one of their samples. There is a PWM peripheral driver that I’m writing as we speak.

https://youtu.be/PrF3W6B69Ww

PM me at :solaraeng@gmail.com

I may put the code on Github soon…

Cheers,
Mario

Hi Mario, Thanks for your Input.
Please let us know, once you post the code on Github.

Here it is on Github…Read the .pdf and Import project into KDS…Its one channel but nothing stops you from duplicating it on another pin. I am working on a more professional driver using the multi-channels. I will let everyone know how it goes…

https://github.com/solaraeng/NXP_M4_PWM

Enjoy…
Mario

Solara Engineering
solaraeng@gmail.com

Dear @solaraeng, dear @GBarcan.Veeco

Instead of toggling GPIOs by code, another option would be to use the Flex Timer Module (FTM) of the i.MX7 SoC.
It can generate the PWM signal in hardware, which would allow for much higher PWM frequency with higher resolution, less jitter and no CPU load.

I’m afraid I don’t have any sample code around. I did a quick google research for “i.mx7 ftm pwm sample” and found some code for a different CPU:

I didn’t verify whether the K60’s FTM is compatible to the one in the i.MX7. However, as both CPUs are from NXP, chances are good for an easy migration.

Regards, Andy

I have finished one channel driver of PWM peripheral successfully on the imx7 M4 core. This works on my ASTER board from Toradex. Now to wrap it up and offer x 4 channels into a final driver. I should have the one channel PWM Peripheral code wrapped up by the 4th of JULY…[And I did! ;)]

Enjoy!
Mario

/*
 * Copyright (c) 2015, Freescale Semiconductor, Inc.
 * All rights reserved.
 *
 * ***************************************************************************************
 * Project - mx7_colibri_m4_PWM_periph_demo (NXP PWM Peripheral LED Modulator)
 * Created by : Mario Ghecea
 * Solara Engineering (solaraeng@gmail.com)
 * 7/3/2019
 * Purpose - To facilitate a scalable and programmable PWM peripheral algorithm without FreeRTOS
 * utilizing any number of dividing steps (1-n) for smoothness and PWM resolution
 * Only one PWM Counter for the period is used and a samples FIFO for adequate phase synchronization
 * is used while keeping track of kPWM_FIFOEmptyFlag to enter the next phase.
 * This time the PWM interrupt is used as the feeder system to the samples FIFO which
 * results into a much smoother response and PWM precision.
 *
 * I use a PWM duty-cycle update delay inside the integrator. This results into a nice
 * accordeon like modulation display which I find quite pleasant...
 * This could be used as a generic LED driver, contrast for a display and perhaps
 * motor control through expansion.
 *
 * TO DO - Create a multi-channel driver unless someone else beats me to it!
 *
 * If you reuse or distribute for your purpose please keep this header...
 *****************************************************************************************
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * o Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * o Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * o Neither the name of Freescale Semiconductor, Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include "board.h"
#include "gpio_pins.h"
#include "gpio_imx.h"
#include "debug_console_imx.h"
#include "pwm_imx.h"

/*! @brief PWM period value. PWMO (Hz) = PCLK(Hz) / (period +2) */
#define PWM_PERIOD_DIV			 16										// Choose a larger divider for a faster accordion-like brightness display
#define PWM_PERIOD_VALUE	 	(16000/PWM_PERIOD_DIV) 					// 1 second period/PWM_PERIOD_DIV
#define PWM_STEPS_PER_PHASE		 10	    								// Increment PWM_STEPS_PER_PHASE for a higher resolution
#define PWM_STEP_WIDTH			 (PWM_PERIOD_VALUE/PWM_STEPS_PER_PHASE) // Accordion step width
#define PWM_DELAY_DIV		     2										// Make this value greater for faster accordion fold...
#define PWM_DELAY_CNTR			 (PWM_STEPS_PER_PHASE/PWM_DELAY_DIV)    // This determines how fast accordion folds/unfolds

/*******************************************************************************
 * Prototypes
 ******************************************************************************/

/*******************************************************************************
 * Variables
 ******************************************************************************/
volatile uint32_t pwmDutycycle = 0U;
volatile bool pwmDutyUp = true;         /* Indicate PWM Duty cycle is increase or decrease */
volatile uint8_t stepCounter = PWM_DELAY_CNTR;

/* button relevent variables */
#ifdef BOARD_GPIO_KEY_CONFIG
static volatile uint8_t button_pressed_flag;
#endif

/*******************************************************************************
 * Code
 ******************************************************************************/


//Note - All integration/Modulation Magic happens here almost automatically!
//		 By clearing kPWM_FIFOEmptyFlag it guarantees a smooth re-entrancy to this ISR
//		 to automatically, integrate the PWM Duty-cycle. All outputs go straight out of peripheral.

void BOARD_PWM2_HANDLER(void)
{
	//static long counter = 10;
	 /* Gets interrupt kPWM_FIFOEmptyFlag */
	if(PWM_GetStatusFlags(BOARD_PWM2_BASEADDR) & kPWM_FIFOEmptyFlag)
	{

		if (stepCounter == 0U)
		{
			stepCounter = PWM_DELAY_CNTR;

			if(pwmDutyUp)
			{
				/* Increase duty cycle until it reach limited value. */
				if((pwmDutycycle += PWM_STEP_WIDTH) >= PWM_PERIOD_VALUE)
				{
					pwmDutycycle = PWM_PERIOD_VALUE;
					pwmDutyUp = false;
				}
			}
			else // pwmDutyDn
			{
				/* Decrease duty cycle until it reach limited value. */
				if((pwmDutycycle -= PWM_STEP_WIDTH) <= 0U)
				{
					pwmDutycycle = 0U;
					pwmDutyUp = true;
				}
			}
		}
		else
			stepCounter --; // Do all the step counts at same modulation ratio
		/* Write duty cycle to PWM sample register.  */
		PWM_SetSampleValue(BOARD_PWM2_BASEADDR, pwmDutycycle);
		PWM_clearStatusFlags(BOARD_PWM2_BASEADDR, kPWM_FIFOEmptyFlag);
	}

}


/*****************************************************************************
*
* Function Name: main
*
******************************************************************************/
int main(void)
{
	static long incr2 = 0;
	uint32_t inter;
    pwm_config_t pwmConfig;
    
    /* hardware initialiize, include RDC, IOMUX, Uart debug initialize */
    hardware_init();
    PRINTF("\n\r====================== PWM Peripheral driver Example ========================\n\r");
    
    PWM_GetDefaultConfig(&pwmConfig);

    /* Initialize PWM module */
    PWM_Init(BOARD_PWM2_BASEADDR, &pwmConfig);

    inter = PWM_GetEnabledInterrupts(BOARD_PWM2_BASEADDR);

    /* Enable FIFO empty interrupt */
    PWM_EnableInterrupts(BOARD_PWM2_BASEADDR, kPWM_FIFOEmptyInterruptEnable);

    inter = PWM_GetEnabledInterrupts(BOARD_PWM2_BASEADDR);

    /* Initial samples be written to the PWM Sample Register */
    PWM_SetSampleValue(BOARD_PWM2_BASEADDR, pwmDutycycle);

	/* Three initial samples be written to the PWM Sample Register */
	for(pwmDutycycle = 0u; pwmDutycycle < 3; pwmDutycycle++)
	{
	   PWM_SetSampleValue(BOARD_PWM2_BASEADDR, pwmDutycycle);
	}

	/* Check and Clear interrupt status flags */
	if(PWM_GetStatusFlags(BOARD_PWM2_BASEADDR))
	{
		PWM_clearStatusFlags(BOARD_PWM2_BASEADDR, kPWM_FIFOEmptyFlag | kPWM_RolloverFlag | kPWM_CompareFlag | kPWM_FIFOWriteErrorFlag);
	}

    /* Write the period to the PWM Period Register */
    PWM_SetPeriodValue(BOARD_PWM2_BASEADDR, PWM_PERIOD_VALUE);

    /* Set PWM Interrupt priority */
    NVIC_SetPriority(BOARD_PWM2_IRQ_NUM, 5);

    /* Call core API to enable the IRQ. */
    NVIC_EnableIRQ(BOARD_PWM2_IRQ_NUM);

    /* Start PWM Output */
    PWM_StartTimer(BOARD_PWM2_BASEADDR);

    while (true)
    {
    }
;
}

Cheers
Mario

My Github for NXP drivers is here : https://github.com/solaraeng/NXP_M4_PWM

My Github for DSP code is here : https://github.com/solaraeng/ADI_DSP

(Note - Any future PWM drivers on M4 and their expansion will be listed and can be found on my NXP Github)

My NXP/DSP articles are now up on LinkedIn…

NXP Related Articles :

https://www.linkedin.com/pulse/how-properly-perform-pwm-nxp-imx7-processors-utilizing-mario-ghecea

DSP Related Articles :

https://www.linkedin.com/pulse/bf706-adi-blackfin-advanced-dsp-uart-port-mario-ghecea
https://www.linkedin.com/pulse/sine-wave-generator-adjustable-frequency-port-using-my-mario-ghecea
https://www.linkedin.com/pulse/fourier-series-additive-harmonic-signal-generator-mario-ghecea
https://www.linkedin.com/pulse/bf706-uart-dma-terminal-audio-fir-filter-control-mario-ghecea
https://www.linkedin.com/pulse/distortion-modulation-lut-generated-signals-bf706-mario-ghecea

(Note - This list will grow…Keep and eye out every week, for new articles and drivers on NXP Processors…)

Cheers, Mario 
Solara Software Engineering