How to generate PWM signal using MQX

Document created by soledad Employee on Nov 9, 2015Last modified by soledad Employee on Nov 30, 2015
Version 1Show Document
  • View in full screen mode

The Freescale MQX Software Solution includes a comprehensive Board Support Package (BSP) supporting common peripherals and functions. However, some applications will require customization of the available drivers or the development of new ones.

Pulse width modulation (PWM) is a technique used to encode a message into a pulsing signal. PWM is in used in an extensive variety of applications, ranging from motor control, measurement and communications to power control and conversion, among others.

PWM driver is not part of the MQX however there is possible to add your own driver. The purpose of this document is show two different ways to implement PWM signal using MQX and Kinetis devices:

a) using bareboard code

b) using processor expert in order to create a driver.

 

USING BAREBOARD CODE:

 

As there is no MQX driver for FTM peripheral, customers should create their own drivers.

The AN3902 application note guides developers through the process of creating and testing I/O drivers under MQX.

http://cache.freescale.com/files/32bit/doc/app_note/AN3902.pdf

 

A simple PWM code can be as simple as shown below:

/*Using FTM2_CH0 FTM2_CH1 output PWM with 90% high, 10% low wave*/

 

void FTM_EPWM(void)

{

 

SIM_SCGC6 |= SIM_SCGC6_FTM2_MASK;

         SIM_SCGC3 |= SIM_SCGC3_FTM2_MASK;

  

         PORTA_PCR10 = (0|PORT_PCR_MUX(3)); /* FTM2_CH0 enable on PTA10 */

       PORTB_PCR18 = (0|PORT_PCR_MUX(3)); /* FTM2_CH0 enable on PTB18 */

 

       PORTA_PCR11 = (0|PORT_PCR_MUX(3)); /* FTM2_CH1 enable on PTA11 */

       PORTB_PCR19 = (0|PORT_PCR_MUX(3)); /* FTM2_CH1 enable on PTB19 */

 

 

       FTM2_MOD = 0x0063;  /* 0x0063 / 60MHz = 1.6666uS PWM period */

 

       /* Configure timers for edge aligned PWM High True Pulses */

       printf("FTM2_ Edge_Aligned Test 1\r\n");

       printf("Please check the waveform, 90% High True EPWM\r\n");

 

       FTM2_C0SC = 0x28; /* No Interrupts; High True pulses on Edge Aligned PWM */

       FTM2_C1SC = 0x28; 

 

       FTM2_C0V = 0x005A;  /* 90% pulse width */

       FTM2_C1V = 0x005A; 

 

       FTM2_SC = 0x08;     /* Edge Aligned PWM running from BUSCLK / 1 */

}

 

The function shown above can be called from any MQX task.

Only care will be in case interrupts are needed for FTM application. There are two ways to add user interrupts into an MQX system - kernel_isr or MQX managed isr.

Kernel_isr is fast but it bypasses MQX, thus, no MQX API function can be called from such a kernel_isr. MQX managed isr is slower, as it runs dispatcher in case a higher priority task becomes ready during isr.

 

USING PROCESSOR EXPERT IN ORDER TO CREATE A DRIVER:

 

All MQX BSP's are PE capable and Processor Expert is integrated with CodeWarrior and KDS but it is also a standalone tool that can generate code for IAR, Keil, GCC, etc.

 

http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=PE_DRIVER_SUITE&tid=PEH

 

There is an easy way to add device drivers to the MQX RTOS BSP using PE.

It is necessary to activate the PE views, for do that go to Processor Expert -> Show Views

 

Show PE views

After select the PE views there is possible to look at the properties of the each component.

For example, the below figure shows the properties of the MQX1 component. 

Component MQX1

The below figure shows the Cpu component, here it is possible to modify the clock configurations, it is possible to find more information about this in the MQX_CW10_Getting_Started document located, after install MQX, at the path: <Freescale_MQX_4_2>\doc\tools\cw.

 

Component CPU

In order to configure a PWM signal using PE, it is necessary to follow these steps:

     1. Select the PWM component.

 

Component PWM

 

     2. By default, the PWM component configures Channel 0 in Flex Timer 0 a PWM of 4096 timer-ticks, however it is possible to modify this values according the needs.

 

Component PWM properties. 

 

     3. Besides Properties, Components also include Methods and Events that it is possible to enable or disable.

 

Methods and events.

 

 

Methods are user-callable functions/subroutines intended for the component functions control.

 

Init: Initializes the device. Allocates memory for the device data structure, allocates interrupt vectors and sets interrupt priority, sets pin routing, sets timing, etc. If the property "Enable in init. code" is set to "yes" value then the device is also enabled (see the description of the Enable method). In this case the Enable method is not necessary and needn't to be generated. This method can be called only once.

 

SetPeriodTicks: The method sets timer re-initialization period (in timer ticks). This method is available only if the property "Counter restart" is switched to 'on-match' value.

 

ResetCounter: Resets counter. If counter is counting up then it is set to zero. If counter is counting down then counter is updated to the reload value.
The method is not available if HW doesn't allow resetting of the counter.


GetCounterValue: Returns the content of counter register. This method can be used both if counter is enabled and if counter is disabled. The method is not available if HW doesn't allow reading of the counter.

 

SetOffsetTicks: Sets the new offset value to channel specified by the parameter ChannelIdx. It is user responsibility to use value below selected period. This method is available when at least one channel is configured.

 

GetCaptureValue: Returns the content of capture register specified by the parameter ChannelIdx. This method is available when at least one channel is configured.

 

Events are call-back functions called when an important event occurs.

OnCounterRestart: Called if counter overflow/underflow or counter is reinitialized by modulo or compare register matching. OnCounterRestart event and Timer unit must be enabled. This event is available only if an Interrupt is enabled.

 

OnChannel(x): Called if compare register match the counter registers or capture register has a new content. OnChannel(x) event and Timer unit must be enabled. This event is available only if an Interrupt is enabled.

 

 

     4. If there is some change click on Generate Code and Build the project:

 

 

     5. The PWM driver was created. In order to use the driver it is necessary to create a new MQX project.

 

     6. For this example, we will edit the main task that is defined after create a new MQX project. To use PE driver some ‘handler’ variables must be declared:

 

  

    7. It is necessary to initialize the component.

   8. To enable de component, the PWM_Enable() function is required . 

     9. Finally implement the events.

    

10. At this point you should be able to build and run the example.

Remember to check the jumper settings for the board you are using in order to debug a MQX example. The Getting Started with Freescale MQX™ RTOS document provides board-specific information related to the MQX RTOS, this document is located at the path: <Freescale_MQX_4_2>\doc

It is important to connect the tower board to the elevators and check the PWM signal on A67.

 

 

 

 

Below you can check the entire code:

 

 

 

#include "main.h"

 

 

#if !BSPCFG_ENABLE_IO_SUBSYSTEM

#error This application requires BSPCFG_ENABLE_IO_SUBSYSTEM defined non-zero in user_config.h. Please recompile BSP with this option.

#endif

 

 

#ifndef BSP_DEFAULT_IO_CHANNEL_DEFINED

#error This application requires BSP_DEFAULT_IO_CHANNEL to be not NULL. Please set corresponding BSPCFG_ENABLE_TTYx to non-zero in user_config.h and recompile BSP with this option.

#endif

 

 

TASK_TEMPLATE_STRUCT MQX_template_list[] =

{

/* Task number, Entry point, Stack, Pri, String, Auto? */

{MAIN_TASK,   Main_task,   1500, 9,   "main", MQX_AUTO_START_TASK},

{0,           0,           0,     0, 0,      0,                 }

};

 

 

/*TASK*-----------------------------------------------------

*

* Task Name    : Main_task

* Comments     :

* This task prints " Hello World "

*

*END*-----------------------------------------------------*/

#define TERMINAL_CURSOR_POSITION_MAX    (80)

 

static int                  pwm_task_count;

static LDD_TDeviceData     *PWM_DeviceData;

static LDD_TError           PWM_Error;

volatile PWM_TValueType     PWM_Value;

volatile PWM_TValueType     PWM_MaxValue;

volatile PWM_TValueType     PWM_Step;

volatile int                PWM_Freguency;

 

void Main_task(uint32_t initial_data)

{

 

       static int terminal_cursor_position = 1;

 

       printf("\n Hello World \n");

 

       /* Initialize PWM device on FTM0 device */

            puts("\nInitializing PWM device.....");

           PWM_DeviceData = PWM_Init(NULL);

           if (PWM_DeviceData == NULL)  {

           puts("failed");

               _task_block();

           }

           else  {

               puts("done");

           }

 

           PWM_Value       = 0;

           PWM_Step        = PWM_PERIOD_TICKS / 32;

           PWM_MaxValue    = PWM_PERIOD_TICKS;

           PWM_Freguency   = (PWM_CNT_INP_FREQ_U_0/PWM_PERIOD_TICKS);

 

         printf("\n - PWM frequency              = %d Hz", PWM_Freguency);

           puts("\nThe PWM signal is generated on FTM0 Channel 0");

           puts("\n");

           /* Enable PWM device */

           PWM_Error = PWM_Enable(PWM_DeviceData);

 

           while(1)

           {

               pwm_task_count++;

 

               /* Suspend task for 250ms */

               _time_delay(250);

 

               /* Print dot on console to see that application is running */

               if (terminal_cursor_position++ > TERMINAL_CURSOR_POSITION_MAX) {

                   terminal_cursor_position = 1;

                   puts("\n");

               }

               else {

                   puts(".");

               }

           }

       }

 

 

void PWM_OnCounterRestart(LDD_TUserData *UserDataPtr)

{

/* Increment PWM duty-cycle from 0-100% */

PWM_Value += PWM_Step;

 

if (PWM_Value > PWM_MaxValue) PWM_Value = 0;

 

/* Set new PWM channel value */

PWM_Error = PWM_SetOffsetTicks(PWM_DeviceData, 0, PWM_Value);

}

 

/* EOF */

Attachments

    Outcomes