LPC2378 PWM generating a clocksignal

Download pre-release library modules and new examples to use with Astrobe for LPC2000. Forum members can also upload their own source code examples.
Post Reply
4GlCoder
Posts: 27
Joined: Fri Jul 22, 2011 2:47 pm

LPC2378 PWM generating a clocksignal

Post by 4GlCoder » Sat Nov 19, 2011 4:44 pm

With a future implementation of an IR-module in mind I toyed with some code from the NXP boys.
After translating it to Oberon I made a simple test and monitored the results from my Logic Scope.

For IR transmission you need a 36 to 40 khz clocksignal with a Duty cycle of about 25%. This means the Clock will be high (1) for 25% of the time and low (0) for 75% of the cycle time.

Code: Select all

MODULE PWM;
(***************************************************************************** 
 *   pwm.c:  PWM module file for NXP LPC23xx/24xx Family Microprocessors 
 *   Copyright(C) 2006, NXP Semiconductor 
 *   All rights reserved. 
 * 
 *   History 
 *   2006.07.20  ver 1.00    Preliminary version, first Release 
 *   2011-10-15 W. Nijland Oberon-07 Port
******************************************************************************) 
IMPORT SYSTEM, LPC := LPC2378, VIC;

CONST
  LER0Enable=0; LER1Enable=1;
  TCRCounterEnable=0; TCRCounterReset=1; TCRPWMEnable=3;
  PCRPWMENA1=9;  (* enable physical port output *)
VAR
  MatchCounter1* : INTEGER; 
(****************************************************************************** 
** Function name:       PWM.Set 
** 
** Descriptions:        PWM cycle setup 
** 
** parameters:          PWM cycle = (Fpclk / pwm-freq), and duty percentage 
** Returned value:      None 
**  
******************************************************************************) 
PROCEDURE ResetCounters*;
BEGIN
  MatchCounter1 := 0;
END ResetCounters;

(****************************************************************************** 
** Function name:       PWM.Set 
** 
** Descriptions:        PWM cycle setup 
** 
** parameters:          PWM cycle = (Fpclk / pwm-freq), and duty percentage 
** Returned value:      None 
**  
******************************************************************************) 
PROCEDURE Set*(CONST cycle, percentage : INTEGER);
BEGIN
  SYSTEM.PUT(LPC.PWM1MR0, cycle);        (* set PWM cycle *)
  SYSTEM.PUT(LPC.PWM1MR1, cycle * percentage DIV 100);
  (* The LER will be cleared when the Match 0 takes place, in order to
     load and execute the new value of match registers, all the PWMLERs need to be reloaded.
     PWM latch enabled *)
  SYSTEM.PUT(LPC.PWM1LER, {LER0Enable, LER1Enable})
END Set;
 
(****************************************************************************** 
** Function name:       PWM.Start 
** 
** Descriptions:        Enable PWM by setting the PCR, PTCR registers 
** 
** parameters:          None
** Returned value:      None 
**  
******************************************************************************) 
PROCEDURE Start*;
VAR
  bits : SET;
BEGIN
  (* All single edge, all enable *) 
  SYSTEM.PUT(LPC.PWM1PCR, {PCRPWMENA1});
  (* Timer Control Register *)
  SYSTEM.PUT(LPC.PWM1TCR, {TCRCounterEnable, TCRPWMEnable})
END Start;
 
(****************************************************************************** 
** Function name:       PWM.Stop 
** 
** Descriptions:        Stop all PWM channels 
** 
** parameters:          None
** Returned value:      None
**  
******************************************************************************) 
PROCEDURE Stop*;
BEGIN
  SYSTEM.PUT(LPC.PWM1PCR, {});
  SYSTEM.PUT(LPC.PWM1TCR, {});        (* Stop all PWMs *) 
END Stop;

(****************************************************************************** 
** Function name:       PWM Handler 
** 
** Descriptions:        PWM1 interrupt handler 
**                      For now, it only deals with PWM1 match 0 
** parameters:          None 
** Returned value:      None 
** 
******************************************************************************) 
PROCEDURE IntHandler[4];
CONST 
  IRPWMMR0=0;  (* Interrupt flag for PWM match on channel 0 *)
VAR
  clearval, reg : SET;
BEGIN
  clearval := {};
  SYSTEM.GET(LPC.PWM1IR, reg);
  IF IRPWMMR0 IN reg THEN
    INC(MatchCounter1);
    clearval := clearval + {IRPWMMR0}
  END;
  IF clearval # {} THEN
    SYSTEM.PUT(LPC.PWM1IR, clearval)        (* clear interrupt flag on match 0 *) 
  END;
  SYSTEM.PUT(LPC.VICAddress, 0)             (* Acknowledge Interrupt *)
END IntHandler;

(****************************************************************************** 
** Function name:       PWM.Init 
** 
** Descriptions:        PWM initialization, setup  GPI 2.0s to PWM1,
**                      reset counter, latches is enabled, interrupt 
**                      on PWMMR0, install PWM interrupt to the VIC table. 
** 
** parameters:          None
** Returned value:      None
** 
******************************************************************************) 
PROCEDURE Init*;
CONST
  PCPWM1 = 6;
  (* P2.0 = PWM1.1 = 1:0 => b0=1 b1=0 *)
  MCRPWMMR0I=0;  (* Interrupt on PWM MR0 *)
  MCRPWMMR0R=1;  (* Reset on PWM MR0 *)
VAR
  bits : SET;
BEGIN
  ResetCounters; 
  SYSTEM.GET(LPC.PCONP, bits);
  IF ~(PCPWM1 IN bits) THEN
    SYSTEM.PUT(LPC.PCONP, bits + {PCPWM1})      (* Be sure it is enabled *)
  END;
  (* Default Clock runs at 1/4 of 72 Mhz = 18 Mhz *)
  
  SYSTEM.GET(LPC.PINSEL4, bits);
  SYSTEM.PUT(LPC.PINSEL4, bits - {1} + {0});    (* set GPIO for PWM pin PWM1.1  = function=01 for bits=1:0 *) 
 
  SYSTEM.GET(LPC.PINMODE4, bits);
  SYSTEM.PUT(LPC.PINMODE4, bits- {0} + {1});    (* No pull-up/down on PWM1.1  = P2.0 output = function=10 for bits 1:0*)
  
  SYSTEM.PUT(LPC.PWM1TCR, {TCRCounterReset});   (* Counter Reset *)  
  
  SYSTEM.PUT(LPC.PWM1PR, 0);                    (* count frequency:Fpclk = 18 Mhz *) 
  SYSTEM.PUT(LPC.PWM1MCR, {MCRPWMMR0I, MCRPWMMR0R}); (* interrupt on PWMMR0, reset on PWMMR0, reset TC if PWM0 matches *) 
  
  SYSTEM.PUT(LPC.PWM1LER, {LER0Enable, LER1Enable});
    
  VIC.InstallHandler(VIC.Pwm1, SYSTEM.ADR(IntHandler), VIC.NormalPriority);
  (* You need to .Set a cycle, Then .Start it *)
END Init; 

END PWM.
Note that this code implements an interrupt handler, so you need to get my VIC.module as well. If you do not want to deal with interrupts well, there is one flag in the code that deals with that: MCRPWMMR0I, (* Interrupt on PWM MR0 *)
When you do NOT set this in LPC.PWM1MCR, the interrupthandler should not be called.

Code: Select all

MODULE PWMTest;
(* Test on LPC-2378-STK board. 
  Tiny Program to generate a 38 khz PWM signal on PWM1.1 
  This wil be the base of a IR - Sender  *)
  
IMPORT Main, PWM, SYSTEM, Out, SYSClock, Timer;

CONST
  PWMFreq = 38000;  (* PWM frequency *)
  PWMDuty = 25;     (* 25% high, 75 %low *)
  
(* Show relevant info on selected Can-channel *)
PROCEDURE CalcFreq() : INTEGER;
VAR 
  f, val  : INTEGER;
BEGIN
  f := SYSClock.GetFpclk(SYSClock.PWM1PCLKOffset);
  Out.String("PWM1-Clock="); Out.Int(f DIV SYSClock.MHZ, 0); Out.String("MHz"); Out.Ln;
  Out.String("Desired PWM freq="); Out.Int(PWMFreq DIV SYSClock.KHZ, 0); Out.String("kHz"); Out.Ln;
  val := f DIV PWMFreq;
  Out.String("Match Register 0 should use value="); Out.Int(val, 0); Out.Ln;
  Out.String("Match Register 1 has duty frequency of ="); Out.Int(PWMDuty,0); Out.String("%"); Out.Ln;
  Out.Ln;
  RETURN val
END CalcFreq;

PROCEDURE Demo;
VAR
  i : INTEGER;
BEGIN
  Timer.Init(Timer.MSecs);
  PWM.Init;
  PWM.Set(CalcFreq(), PWMDuty);
  PWM.Start;
  Timer.MSecDelay(500);
  PWM.Stop;
  Out.String("Stopped after "); Out.Int(PWM.MatchCounter1, 0); Out.String(" interrupts. "); Out.Ln; 
END Demo;

BEGIN
  Demo
END PWMTest.  
This Test does merely arm the Timer and PWM channels 0 and 1, installs an interrupthandler and runs a clock for 500 milliseconds.
The code does not suspend the PWM generator but stops it. Suspending a PWM signal might sound handy, but you need to make sure the signal will suspend in a known state, preferrably low. The easy way is just to Stop it. To resume just do a Set, and Start.

Post Reply