FreeRTOS PWM Macro: correct usage

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

FreeRTOS PWM Macro: correct usage

Jump to solution
2,933 Views
erikfriedel
Contributor III

Hello

I am currently working on an i.MX7s on the M4 side and try power my LED (for testing purpose) via PWM.

If I hardcode the Register everything works fine. But this is kinda dirty I know.

#define PWM1_Control  0x30660000
#define PWM1_Sample   0x3066000c
#define PWM1_Period   0x30660010

#define READ_PWM1_Control()  (*(volatile uint32_t *)PWM1_Control)

#define WRITE_PWM1_Sample(val)  ((*(volatile uint32_t *)PWM1_Sample) = (val))

#define WRITE_PWM1_Period(val)  ((*(volatile uint32_t *)PWM1_Period) = (val))


#include <stdint.h>
#include <stdbool.h>
#include "debug_console_imx.h"
#include "board.h"

int enable_PWM()
{
      PRINTF("\nenable PWM\n\n\r");
      WRITE_PWM1_Control(0x01c30000); 
      WRITE_PWM1_Sample(0x8000);
      PRINTF("config Sample \n\r");
      WRITE_PWM1_Period(0xf000);
      PRINTF("config Period \n\r");
      WRITE_PWM1_Control(0x01c30001);
      PRINTF("done \n\n\r"); 
      return 0;
}

https://github.com/TimesysGit/freertos-imx7/tree/BSP_1.0.1_TS/platform/devices/MCIMX7D/include

Here (search for "PWM Peripheral Access Layer") are the Makros for the PWM. Can someone give me two or three lines of code so I can try to figure it out how it works? Ref. Manual here: https://www.nxp.com/docs/en/reference-manual/IMX7DRM.pdf Page4207

Thanks

Labels (2)
0 Kudos
1 Solution
2,423 Views
Carlos_Musich
NXP Employee
NXP Employee

Hi Erik,

You can go to http://www.nxp.com/imx7d > SOFTWARE & TOOLS tab >Board Support Packages > FreeRTOS_iMX7D_1.0.1_LINUX

This BSP is released by NXP and contain many examples for M4 may be useful for you.

On the other hand you may use PWM with Linux driver such as it is explained here PWM - UDOO Quad/Dual Docs 


Regards,
Carlos
NXP Technical Support
-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------

View solution in original post

0 Kudos
7 Replies
2,423 Views
solaraeng
Contributor I

Even Better, I have wrote the PWM Generator using the NXP PWM Peripheral which yields greater accuracy and Brightness response for an hypothetical LED Modulator/Integrator.

https://github.com/solaraeng/NXP_M4_PWM/blob/master/freertos-solara/examples/imx7_colibri_m4/demo_ap...

The code is on my github or just read my document above.

https://github.com/solaraeng/NXP_M4_PWM/tree/master/freertos-solara/examples/imx7_colibri_m4/demo_ap...

The video on youtube is here : 

https://www.youtube.com/watch?v=mzYZhHR7xvc

0 Kudos
2,424 Views
Carlos_Musich
NXP Employee
NXP Employee

Hi Erik,

You can go to http://www.nxp.com/imx7d > SOFTWARE & TOOLS tab >Board Support Packages > FreeRTOS_iMX7D_1.0.1_LINUX

This BSP is released by NXP and contain many examples for M4 may be useful for you.

On the other hand you may use PWM with Linux driver such as it is explained here PWM - UDOO Quad/Dual Docs 


Regards,
Carlos
NXP Technical Support
-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------

0 Kudos
2,423 Views
solaraeng
Contributor I

You can also examine my NXP PWM generator source on Github

https://github.com/solaraeng/NXP_M4_PWM

Here is the .pdf description :Readme_PWM.pdf

An Youtube of it in action is here : NXP FreeRTOS PWM modulator/LED current driver on NXP imx7 via GPT on Toradex ASTER - YouTube 

(I wrote it for Toradex Colibri on ASTER) but I will also port it for NXP Sabre, it uses a technique, which is much simpler and uses the NXP GPT Timer to generate PWM by alternating phases...It takes a little bit of intuition to use, but it only requires you enter your timing characteristics and number of steps inside each phase. I might use this to create a better driver for motor control. The code goes as follows :

/*
* Copyright (c) 2015, Freescale Semiconductor, Inc.
* All rights reserved.
***************************************************************************************
* Project - mx7_colibri_m4_PWM_imx_demo (PWM Modulator)
* Created by : Mario Ghecea
* Solara Engineering (solaraeng@gmail.com)
* 6/29/2019
* Purpose - To facilitate a scalable and programmable PWM algorithm within FreeRTOS
* utilizing any number of dividing steps (1-n) for smoothness and PWM resolution
* Only one generic timer (GPT) is used as counter for each alternating phase step...
* This could be used as a generic LED driver, contrast for a display and perhaps
* motor control through expansion.
* 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 "FreeRTOS.h"
#include "task.h"
#include "board.h"
#include "debug_console_imx.h"
#include "gpio_ctrl.h"
#include "hw_timer.h"


#define PWM_MIN (0.1f) // Lets assume 10% phase increases
#define PWM_MAX (1.0f) // Lets assume your phase max at 100%
#define PWM_FREQ_DIVIDER 1 // Your PWM period frequency divider in milliseconds (1/PWM_FREQ_DIVIDER) - LED Bink Interval
#define PWM_STEPS_PER_PHASE 20 // Increment PWM_STEPS_PER_PHASE for a higher resolution (Above 20 it suddenly glitches)
#define PWM_RESOLUTION_COUNTER (1000/(PWM_FREQ_DIVIDER * PWM_STEPS_PER_PHASE)) // Value in ms per phase

static volatile uint32_t blinkingIntervalHigh = PWM_RESOLUTION_COUNTER;
static volatile uint32_t blinkingIntervalLow = PWM_RESOLUTION_COUNTER;
static volatile float pwm = PWM_MIN;
static volatile bool start = true;

void SyncPWM();
void Resync();

/******************************************************************************
*
* Function Name: ToggleTask
* Comments: this task is used to turn toggle on/off LED.
* This task has the effect of staring on cue so it will sync the phases
* correctly based on start signal from Phase Synchronizer (SwitchTask).
*
******************************************************************************/
void ToggleTask(void *pvParameters)
{
while (true)
{
if (blinkingIntervalHigh != 0 && start == true)
{
// If we have been signaled to start, lets sync the PWM phase
SyncPWM();
// Process PWM Phase High
GPIO_Ctrl_ToggleLed(true);
/* Use Hardware timer to get accurate delay */
Hw_Timer_Delay(blinkingIntervalHigh);
// Process PWM Phase Low
GPIO_Ctrl_ToggleLed(false);
Hw_Timer_Delay(blinkingIntervalLow);
}
}
}

// Sync PWM Block - Phase Increment
void SyncPWM()
{
blinkingIntervalHigh = PWM_RESOLUTION_COUNTER * pwm ;
blinkingIntervalLow = PWM_RESOLUTION_COUNTER * (1.0f - pwm);

pwm += (0.5f / PWM_STEPS_PER_PHASE);

Resync();
}

void Resync()
{
if (pwm >= PWM_MAX)
{
pwm = PWM_MIN;
}
}


/******************************************************************************
*
* Function Name: main
* Comments: main function, toggle LED and switch the blinking frequency by key.
*
******************************************************************************/
int main(void)
{
/* Initialize board specified hardware. */
hardware_init();

Hw_Timer_Init();
GPIO_Ctrl_Init();

PRINTF("\n\r================= PWM Blinking Demo ==================\n\r");

/* Create a the APP main task. */
xTaskCreate(ToggleTask, "Toggle Task", configMINIMAL_STACK_SIZE,
NULL, tskIDLE_PRIORITY+1, NULL);

/* Start FreeRTOS scheduler. */
vTaskStartScheduler();

/* should never reach this point. */
while (true);
}

/*******************************************************************************
* EOF
******************************************************************************/

0 Kudos
2,423 Views
erikfriedel
Contributor III

Thank you for your reply.

I was using the FreeRTOS iMX7D 1.0.1 BSP befor, but it sadly didn't support the PWM Module. At Linux there is a module already made but I want to use it with the M4.

I solved this, by taking a look at the pwm example of the iMX6UL. I then wrote my own driver so I could use simple commands in the main program.

Regards,

Erik

0 Kudos
2,423 Views
tomaszsliwinski
Contributor III

Erik,

I need to do the same thing.

Can you point me to where you found the pwm example for the iMX6UL?  Or share the pwm module for m4 if possible?

thank you,

Tomasz

0 Kudos
2,423 Views
erikfriedel
Contributor III

Hi Tomasz

You can find the iMX6UL FreeRTOS here: https://github.com/wangben85/imx6ul_freertos

For Driver implementation for the iMX7 you have to do changes to following files: ccm_imx7d.h:

--- ccm_imx7d.h
+++ ccm_imx7d.h
@@ -297,6 +297,10 @@ enum _ccm_ccgr_gate
     ccmCcgrGateGpt2      = (uint32_t)(&CCM_CCGR125), /*!< GPT2 Clock Gate.*/
     ccmCcgrGateGpt3      = (uint32_t)(&CCM_CCGR126), /*!< GPT3 Clock Gate.*/
     ccmCcgrGateGpt4      = (uint32_t)(&CCM_CCGR127), /*!< GPT4 Clock Gate.*/
+    ccmCcgrGatePwm1      = (uint32_t)(&CCM_CCGR132), /*!< PWM1 Clock Gate.*/
+    ccmCcgrGatePwm2      = (uint32_t)(&CCM_CCGR133), /*!< PWM2 Clock Gate.*/
+    ccmCcgrGatePwm3      = (uint32_t)(&CCM_CCGR134), /*!< PWM3 Clock Gate.*/
+    ccmCcgrGatePwm4      = (uint32_t)(&CCM_CCGR135), /*!< PWM4 Clock Gate.*/
     ccmCcgrGateI2c1      = (uint32_t)(&CCM_CCGR136), /*!< I2C1 Clock Gate.*/
     ccmCcgrGateI2c2      = (uint32_t)(&CCM_CCGR137), /*!< I2C2 Clock Gate.*/
     ccmCcgrGateI2c3      = (uint32_t)(&CCM_CCGR138), /*!< I2C3 Clock Gate.*/

MCIMX7D_M4.h:

--- MCIMX7D_M4.h
+++ MCIMX7D_M4.h
@@ -33289,16 +33289,23 @@ typedef struct {
 #define PWM_PWMCR_POUTC(x)                       (((uint32_t)(((uint32_t)(x))<<PWM_PWMCR_POUTC_SHIFT))&PWM_PWMCR_POUTC_MASK)
 #define PWM_PWMCR_HCTR_MASK                      0x100000u
 #define PWM_PWMCR_HCTR_SHIFT                     20
+#define PWM_PWMCR_HCTR(x)                        (((uint32_t)(((uint32_t)(x)) << PWM_PWMCR_HCTR_SHIFT)) & PWM_PWMCR_HCTR_MASK)
 #define PWM_PWMCR_BCTR_MASK                      0x200000u
 #define PWM_PWMCR_BCTR_SHIFT                     21
+#define PWM_PWMCR_BCTR(x)                        (((uint32_t)(((uint32_t)(x)) << PWM_PWMCR_BCTR_SHIFT)) & PWM_PWMCR_BCTR_MASK)
 #define PWM_PWMCR_DBGEN_MASK                     0x400000u
 #define PWM_PWMCR_DBGEN_SHIFT                    22
+#define PWM_PWMCR_DBGEN(x)                       (((uint32_t)(((uint32_t)(x)) << PWM_PWMCR_DBGEN_SHIFT)) & PWM_PWMCR_DBGEN_MASK)
 #define PWM_PWMCR_WAITEN_MASK                    0x800000u
 #define PWM_PWMCR_WAITEN_SHIFT                   23
+#define PWM_PWMCR_WAITEN(x)                      (((uint32_t)(((uint32_t)(x)) << PWM_PWMCR_WAITEN_SHIFT)) & PWM_PWMCR_WAITEN_MASK)
 #define PWM_PWMCR_DOZEN_MASK                     0x1000000u
 #define PWM_PWMCR_DOZEN_SHIFT                    24
+#define PWM_PWMCR_DOZEN(x)                       (((uint32_t)(((uint32_t)(x)) << PWM_PWMCR_DOZEN_SHIFT)) & PWM_PWMCR_DOZEN_MASK)
 #define PWM_PWMCR_STOPEN_MASK                    0x2000000u
 #define PWM_PWMCR_STOPEN_SHIFT                   25
+#define PWM_PWMCR_STOPEN(x)                      (((uint32_t)(((uint32_t)(x)) << PWM_PWMCR_STOPEN_SHIFT)) & PWM_PWMCR_STOPEN_MASK)
 #define PWM_PWMCR_FWM_MASK                       0xC000000u
 #define PWM_PWMCR_FWM_SHIFT                      26
 #define PWM_PWMCR_FWM(x)                         (((uint32_t)(((uint32_t)(x))<<PWM_PWMCR_FWM_SHIFT))&PWM_PWMCR_FWM_MASK)

and add the drivers in /inc, /src (I think these are the same as in the iMX6UL FreeRTOS but not sure anymore)

You can use parts of the main file, but you have to pay attention to the names of the Makros.

in the hardware_init.c file just add

    /* In this example, we need to grasp PWM1 module exclusively */
    RDC_SetPdapAccess(RDC, BOARD_PWM1_RDC_PDAP, 3 << (BOARD_DOMAIN_ID * 2), false, false);

    /* Enable PWM1 clock */
    CCM_ControlGate(CCM, BOARD_PWM1_CCM_CCGR, ccmClockNeededRunWait);

If you have any other questions please let me know.

Best Regards

Erik

2,423 Views
tomaszsliwinski
Contributor III

Thanks a lot Erik.  Will reach out if I have more trouble.

thanks again!

Tomasz

0 Kudos