Controlling number of PWM pulses with one 16-bit timer?

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

Controlling number of PWM pulses with one 16-bit timer?

1,152 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by ktownsend on Mon Oct 25 07:14:23 MST 2010
I'm using 16-bit timer 1 on the LPC1114/LPC1343 to generate PWM output (easy enough), but also need to precisely limit the total number of PWM signals sent out to a fixed number. Ideally, I need something HW controlled rather than counting pulses manually since the pulses can be very short (maybe a dozen or two ticks wide).

I could accomplish this fairly easily using two timers, but am I correct that there isn't really a way to accomplish this using only a single timer? At the moment, I have the following setup:

- TMR16B1 -> MR3 is used for the pulse width
- TMR16B1 -> MR0 sets the duty cycle
- Pin 1.9 is set for the signal output (CT16B1_MAT0 )
- Timer resets on MR3

If it helps clarify anything, here's the current code (with no duration control) ... though I'm using my own header file for the 1114 since I like to have all the bits defined when debugging:

/**************************************************************************/
/*! 
    @file     pwm.c
    @author   K. Townsend (microBuilder.eu)
 
    @brief    Simple PWM example that can be used to control a motor, dim
              an LED, etc.
 
    @section Example
 
    @code 
    #include "drivers/pwm/pwm.h"
    ...
 
    // Initialises PWM output on 16-bit Timer 1 and
    // sets MAT0 (P1.9) as output
    pwmInit();
 
    // Setup the pulse-width and duty-cycle
    pwmSetDutyCycle(50);                  // Set 50% duty cycle
    pwmSetFrequencyInMicroseconds(100);   // 100 millisecond pulse width
 
    // Enable to PWM output
    pwmStart();
 
    @endcode
 
    @section LICENSE
 
    Software License Agreement (BSD License)
 
    Copyright (c) 2010, microBuilder SARL
    All rights reserved.
 
    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are met:
    1. Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.
    2. 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.
    3. Neither the name of the copyright holders 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 ''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 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 "pwm.h"
 
static uint32_t pwmPulseWidth = CFG_CPU_CCLK / 1000;
static uint32_t pwmPercentage = 50;
 
/**************************************************************************/
/*! 
    Initialises 16-bit Timer 1, and configures the MAT0 output (pin 1.9) 
    to send the PWM output signal.
*/
/**************************************************************************/
void pwmInit(void)
{
  /* Enable the clock for CT16B1 */
  SCB_SYSAHBCLKCTRL |= (SCB_SYSAHBCLKCTRL_CT16B1);
 
  /* Configure PIO1.9 as Timer1_16 MAT0 Output */
  IOCON_PIO1_9 &= ~IOCON_PIO1_9_FUNC_MASK;
  IOCON_PIO1_9 |= IOCON_PIO1_9_FUNC_CT16B1_MAT0;  
 
  /* Set default pulse width (MR3)*/
  TMR_TMR16B1MR3 = pwmPulseWidth;
 
  /* Set default duty cycle (MR0) */
  TMR_TMR16B1MR0 = (pwmPulseWidth * (100 - pwmPercentage)) / 100;
 
  /* Configure match control register to reset on MR3 */
  TMR_TMR16B1MCR = (TMR_TMR16B1MCR_MR3_RESET_ENABLED);
 
  /* External Match Register Settings for PWM */
  TMR_TMR16B1EMR = TMR_TMR16B1EMR_EMC0_TOGGLE | TMR_TMR16B1EMR_EM0;
 
  /* Enable PWM0 and PWM3 */
  TMR_TMR16B1PWMC = TMR_TMR16B1PWMC_PWM0_ENABLED | TMR_TMR16B1PWMC_PWM3_ENABLED;
}
 
/**************************************************************************/
/*! 
    Starts the PWM output
*/
/**************************************************************************/
void pwmStart(void)
{
  /* Enable Timer1 */
  TMR_TMR16B1TCR = TMR_TMR16B1TCR_COUNTERENABLE_ENABLED;
}
 
/**************************************************************************/
/*! 
    Stops the PWM output
*/
/**************************************************************************/
void pwmStop(void)
{
  /* Disable Timer1 */
  TMR_TMR16B1TCR = TMR_TMR16B1TCR_COUNTERENABLE_DISABLED;  
}
 
/**************************************************************************/
/*! 
    Sets the signal's duty cycle in percent (1-100).
*/
/**************************************************************************/
int pwmSetDutyCycle(uint32_t percentage)
{
  if ((percentage < 1) || (percentage > 100))
  {
    /* Duty Cycle must be a value between 1 and 100 */
    return -1;
  }
 
  /* Set Duty Cycle (MR0) */
  TMR_TMR16B1MR0 = (pwmPulseWidth * (100 - (pwmPercentage = percentage))) / 100;
 
  return 0;
}
 
/**************************************************************************/
/*! 
    Sets the signal's frequency/pulse-width to the specified number
    of ticks.
*/
/**************************************************************************/
int pwmSetFrequencyInTicks(uint16_t frequency)
{
  if (frequency < 1)
  {
    return -1;
  }
 
  /* Set Pulse Width (MR3)*/
  TMR_TMR16B1MR3 = (pwmPulseWidth = frequency);
 
  /* Adjust Duty Cycle (MR0) */
  TMR_TMR16B1MR0 = (pwmPulseWidth * (100 - pwmPercentage)) / 100;
 
  return 0;  
}
 
/**************************************************************************/
/*! 
    Sets the signal's frequency/pulse-width to the specified number
    of microseconds.
*/
/**************************************************************************/
int pwmSetFrequencyInMicroseconds(uint16_t frequency)
{
  if (frequency < 1)
  {
    return -1;
  }
 
  uint32_t ticks = (((CFG_CPU_CCLK/SCB_SYSAHBCLKDIV) / 1000000) * frequency);
  if (ticks > 0xFFFF)
  {
    /* Delay exceeds the upper limit for the 16-bit timer */
    return -1;
  }
 
  /* Set Pulse Width (MR3)*/
  TMR_TMR16B1MR3 = (pwmPulseWidth = ticks);
 
  /* Adjust Duty Cycle (MR0) */
  TMR_TMR16B1MR0 = (pwmPulseWidth * (100 - pwmPercentage)) / 100;
 
  return 0;  
} 
0 Kudos
11 Replies

987 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by cyberstudio on Wed Nov 17 15:12:48 MST 2010
Ya, I guess I was not very clear... but for example in TMR32B0EMR, you can automatically change the match outputs, too, with no interrupts required. Quoted from the user manual...

5:4 EMC0 External Match Control 0. Determines the functionality of External Match 0.
0x0 Do Nothing.
0x1 Clear the corresponding External Match bit/output to 0 (CT32Bn_MATm pin is LOW if pinned out).
0x2 Set the corresponding External Match bit/output to 1 (CT32Bn_MATm pin is HIGH if pinned out).
0x3 Toggle the corresponding External Match bit/output.
0 Kudos

987 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by ktownsend on Wed Nov 17 14:18:16 MST 2010

Quote: cyberstudio
What exactly is the functionality difference between PWM and non-PWM? In non-PWM you can set an output to 0, 1, or toggle when a match occurs, which seemed more flexible than PWM.



Perhaps I don't understand the question properly, but the advantage of PWM (to me) rather than manually toggling the lines is that because it's interrupt driven and controlled by the PWM HW I just need to start the process and let it do it's magic in the background, and can continue doing something else rather than sitting around waiting for my PWM code to finish, and I can be certain that the timing is fairly consistent and predictable.
0 Kudos

987 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by ktownsend on Wed Nov 17 14:15:56 MST 2010

Quote: WhyNot
Hi KTownsend. is there the possibility to see at least part of your .h file ? I'm still fairly new to this MCU and I'm not finding correlation between your registers names and the users manual



You can see the header file (and anything else) here:
LPC1343:
http://code.google.com/p/lpc1343codebase/source/browse/#svn/branches/v0.50
LPC1114:
http://code.google.com/p/lpc1114codebase/source/browse/#svn/branches/v0.30

You may find it easier to stick to the LPCXpresso files ... the above is really tailored to my own preferences, since I like having every bit defined, etc., but it's there if you want to look (see lpc134x.h and lpc111x.h respectively).
0 Kudos

987 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by cyberstudio on Wed Nov 17 11:26:41 MST 2010
What exactly is the functionality difference between PWM and non-PWM? In non-PWM you can set an output to 0, 1, or toggle when a match occurs, which seemed more flexible than PWM.
0 Kudos

987 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by WhyNot on Tue Nov 16 02:21:33 MST 2010
Hi KTownsend. is there the possibility to see at least part of your .h file ? I'm still fairly new to this MCU and I'm not finding correlation between your registers names and the users manual
http://http://knowledgebase.nxp.com/member.php?u=116
0 Kudos

987 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by ktownsend on Tue Nov 09 09:59:30 MST 2010

Quote: Mike45
sorry to hijack this thread, but i wanted to ask you if you are using this microbuilder codebase with an embedded artists board. is it possible?

i am confused on how this codebase compiles code for the 1343. the lpcxpresso directories has /bin and /tools and i can see how code is compiled, but i can't find anything like it in this codebase download. i can see that some of the code for the peripherals are similar with different definitions, what is specific( files ) that allows you to link them to a 1343?

any help would be greatly appreciated by this noob.

thank you,



Sorry for the very slow reply. The LPC1343 Code Base on the website has some similarities with the LPCXpresso examples, though I largely based it on the Keil examples from NXP since they were far more complete, and just filled in the blanks and added drivers where required (waking up from deep sleep with a timer, along with a number of drivers like ILI9325 TFT LCDs, etc.). There are a number of important differences, though. It doesn't use CMSIS (though I did borrow a few lines of code) and I wrote my own custom header files for both the 1114 and 1343. It was a crapload of work (more than I expected), but I really hate seeing magic numbers everywhere and am used to have a 'flat' header file. It's something I made for myself, so it's kind of a take it or leave it affair, but I thought it was worth publishing since other people may find some of it helpful.

I originally wrote it using Crossworks for ARM ($$$ but GCC-based like LPCXpresso), but to allow people to use free and open-source tools I also added a CodeLite project (free) and people have been able to get it working in Eclipse/Yagarto as well. Basically, though, it's just a Makefile with a provided linker and startup script so you can do whatever you want with it in any IDE that can run Make.

I'm really used to working with Crossworks for serious debugging and wouldn't have a clue how to get everything working in LPCXpresso, though it someone want's to contribute the project files I'd be more than happy to add them to the CodeBase. It should compile right out of the box once the project files are made (keeping in mind I use a custom make file, linker script, and startup code, etc.).  I think LPCXpresso is a great deal and don't want to take away from that, though .. the stuff I publish is really aimed more at people interested in making their own products and HW based on the 1114 and 1343 chips, and I'm just sticking with the tools I know best ... though if LPCXpresso was around when I got started I likely would have seriously considered that.

EDIT: Sorry ... forgot the original question. I'm using this with my own 1114 and 1343 boards, but you should be able to use it with any board. You may need to modify some pin assignments though if you use things like the LCD drivers, SD card, 802.15.4 stack (Chibi), etc., since the default pins may not be available.
0 Kudos

987 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Luis Digital on Wed Oct 27 07:04:00 MST 2010

Quote: Mike45
sorry to hijack this thread, but i wanted to ask you if you are using this microbuilder codebase with an embedded artists board.  is it possible? 

i am confused on how this codebase compiles code for the 1343.  the lpcxpresso directories has /bin and /tools and i can see how code is compiled, but i can't find anything like it in this codebase download.  i can see that some of the code for the peripherals are similar with different definitions, what is specific( files ) that allows you to link them to a 1343?

any help would be greatly appreciated by this noob.

thank you,



They have good code, modular, and well documented. I think the best idea is that you write to them asking to change their libraries as LPCXpresso projects.

They recommend Yagarto (Windows only).
0 Kudos

987 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Mike45 on Wed Oct 27 00:46:30 MST 2010
sorry to hijack this thread, but i wanted to ask you if you are using this microbuilder codebase with an embedded artists board.  is it possible? 

i am confused on how this codebase compiles code for the 1343.  the lpcxpresso directories has /bin and /tools and i can see how code is compiled, but i can't find anything like it in this codebase download.  i can see that some of the code for the peripherals are similar with different definitions, what is specific( files ) that allows you to link them to a 1343?

any help would be greatly appreciated by this noob.

thank you,
0 Kudos

987 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by ktownsend on Mon Oct 25 10:56:22 MST 2010
Perfect. Still can't believe that didn't cross my mind, though. :rolleyes:
0 Kudos

987 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by ktownsend on Mon Oct 25 09:58:14 MST 2010
Duh ... doesn't get much more obvious.  I ruled out counting individual pulses since I was worried about accuracy on really short pulse widths, but it didn't even cross my dull little mind to use the interrupt on MAT3.  No worries about missing a pulse that way.:p I'm obviously making things more complicated in my head than they need to be, even though complicated is usually more fun.

Thanks for the reality check.  Sometimes pays to step back a minute and see what's staring you right in the face.
0 Kudos

987 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by Ex-Zero on Mon Oct 25 09:01:38 MST 2010
If you change your match reset condition from something like

[SIZE=2]LPC_TMR16B0->[/SIZE][SIZE=2][COLOR=#0000c0][SIZE=2][COLOR=#0000c0]MCR[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2] = 1<<10; [/SIZE][SIZE=2][COLOR=#3f7f5f][SIZE=2][COLOR=#3f7f5f]/* Reset on MR3 */[/COLOR][/SIZE][/COLOR][/SIZE]


to

[SIZE=2]LPC_TMR16B0->[/SIZE][SIZE=2][COLOR=#0000c0][SIZE=2][COLOR=#0000c0]MCR[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2] = (1<<9)|(1<<10); [/SIZE][SIZE=2][COLOR=#3f7f5f][SIZE=2][COLOR=#3f7f5f]/* Reset & int on MR3 */[/COLOR][/SIZE][/COLOR][/SIZE]


the timer interrupt could be used to count :

[B][SIZE=2][COLOR=#7f0055][SIZE=2][COLOR=#7f0055]void[/B][/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2] [B]TIMER16_0_IRQHandler[/B]([/SIZE][B][SIZE=2][COLOR=#7f0055][SIZE=2][COLOR=#7f0055]void[/B][/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2])[/SIZE]
[SIZE=2]{[/SIZE]
[B][SIZE=2][COLOR=#7f0055][SIZE=2][COLOR=#7f0055]if[/B][/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2] (LPC_TMR16B0->[/SIZE][SIZE=2][COLOR=#0000c0][SIZE=2][COLOR=#0000c0]IR[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2] & (0x1<<3) ) [/SIZE][SIZE=2][COLOR=#3f7f5f][SIZE=2][COLOR=#3f7f5f]//match[/COLOR][/SIZE][/COLOR][/SIZE]
[SIZE=2]{[/SIZE]
[SIZE=2]LPC_TMR16B0->[/SIZE][SIZE=2][COLOR=#0000c0][SIZE=2][COLOR=#0000c0]IR[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2] = (1<<3); [/SIZE][SIZE=2][COLOR=#3f7f5f][SIZE=2][COLOR=#3f7f5f]/* clear interrupt flag */[/COLOR][/SIZE][/COLOR][/SIZE]
[SIZE=2]timer16_0_counter++;
[LEFT][/SIZE][B][SIZE=2][COLOR=#7f0055][SIZE=2][COLOR=#7f0055]if[/B][/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2](timer16_0_counter ==10) [/SIZE][SIZE=2][COLOR=#3f7f5f][SIZE=2][COLOR=#3f7f5f]//switch off after 10 times[/COLOR][/SIZE][/COLOR][/SIZE]
[SIZE=2]{[/SIZE]
[SIZE=2]LPC_TMR16B0->[/SIZE][SIZE=2][COLOR=#0000c0][SIZE=2][COLOR=#0000c0]PWMC[/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2] =0;[/SIZE]
[SIZE=2]}
[/SIZE][B][SIZE=2][COLOR=#7f0055][SIZE=2][COLOR=#7f0055]return[/B][/COLOR][/SIZE][/COLOR][/SIZE][SIZE=2];[/SIZE]
[SIZE=2]}[/SIZE]
[/LEFT]
0 Kudos