0
\$\begingroup\$

I need to synchronize the rising edge of a PWM with another signal pulse's falling edge on a LPC1114FN28 (trying to drive a TCD1304DG), but am failing to achieve this so far. I have tried the following:

while (LPC_GPIO0->MASKED_ACCESS[ClockMask]);    //wait for the clock to go low
while (!(LPC_GPIO0->MASKED_ACCESS[ClockMask]));   //wait for the start of PWM cycle
LPC_GPIO0->MASKED_ACCESS[ICGMask] = 0x0;   //make the ICG pin go low

where the ClockMask is a mask of the corresponding PWM pin, running at 1MHz frequency, 50% duty cycle and ICGMask a mask of the pin I'm trying to synchronize with the PWM.

The strange problem I encountered is that one of the two whiles seem to block forever for some reason and any code after them does not get executed. When I remove them, it works OK. This could be probably caused by the instructions for the while lasting the same as is the PWM period and thus the condition would always see the pin in the same state, but that seems rather improbable to me...

I am running the chip at 48MHz from the IRC and the PWM measured with my oscilloscope is practically flawless.

So what could I be doing wrong? Is there any better way to do this? Would writing this part of code in assembler help with the timings (they need to be pretty precise)? If so, is there any tutorial for this?

Thanks in advance for any answers!

Edit:
Even using interrupts I was unable to get sufficiently invariable results - the timing of the interrupt after the pulse edge changes. I will try more methods...

Other methods did not work at all. This is probably simpy unachievable, but I do not know, what else could I be doing wrong if not the timing (as the Toshiba CCD is still not responding).

\$\endgroup\$
  • 1
    \$\begingroup\$ You are using a timer in PWM mode, right? I think you can use the interrupt of the match control register, depending on which one you select you'll get the interrupt on falling edge or rising edge. \$\endgroup\$ – alexan_e Feb 19 '14 at 22:54
  • \$\begingroup\$ Good idea! I will try that tomorrow and respond with the result, thanks! \$\endgroup\$ – Matouš Vrba Feb 19 '14 at 22:55
  • 1
    \$\begingroup\$ "the timing of the interrupt after the pulse edge changes" ... This may be interrupt latency due to other interrupts being processed. Have you tried giving this a higher priority? (by the way, your link to the user manual does not seem to work) \$\endgroup\$ – Tut Feb 20 '14 at 21:28
  • 1
    \$\begingroup\$ You might also want to have a look at the IRQLATENCY register mentioned in the user manual. \$\endgroup\$ – Tut Feb 20 '14 at 21:50
  • 1
    \$\begingroup\$ Anything you can do with something silly like a D flip flop to synch your edges? \$\endgroup\$ – Scott Seidman Feb 21 '14 at 11:40
1
\$\begingroup\$

I assume that you have changed the IOCON of the PWM output pin to change its function from GPIO pin to Counter/Timer output pin. Your while statements are then trying to use the same pin as a GPIO input, and my guess is that you just can't do that.

You haven't told us the duty cycle of your PWM signal but at 1MHz you only have 48 clock cycles for the entire period. If the duty cycle gets down below 10% or above 90% it will be very difficult to catch both the low time and the high time. This might be a good place for hand-optimized assembly code.

In any event, it will be difficult to detect an edge on a 1MHz signal and toggle another output bit very quickly. I'm not sure you could still say that the output was "synchronized" to the PWM edge.

\$\endgroup\$
  • 1
    \$\begingroup\$ What you say about reading GPIO while using PWM pin function is not correct, this is from the LPC111x UM The GPIOnDATA register holds the current logic state of the pin (HIGH or LOW), independently of whether the pin is configured as an GPIO input or output or as another digital function. Also A read of the GPIOnDATA register always returns the current logic level (state) of the pin independently of its configuration. and This means that by reading the GPIOnDATA register, the digital output or input value of a function other than GPIO on that pin can be observed. \$\endgroup\$ – alexan_e Feb 19 '14 at 23:23
  • \$\begingroup\$ Edited the question to add the duty cycle. As for the synchronization - I thought that if I could at least sense the edge and after the detection always end up in the same time delta relative to the edge (which could be measured with an oscilloscope), I could just wait (period - time delta) for another cycle and than toggle the pin at the right moment. \$\endgroup\$ – Matouš Vrba Feb 20 '14 at 8:55
  • \$\begingroup\$ Also any tutorial in writing and embedding the assembly code in C would be very helpful... \$\endgroup\$ – Matouš Vrba Feb 20 '14 at 8:56
0
\$\begingroup\$

What solved this in the end was using the External Match Register functions to clear and set the desired pins with the exact same timings as the PWM. This is achieved by using the same timer and Match Register for the PWM and for the synchronized signal.
Here is the code I used:

#include <LPC11xx.h>            /* LPC11xx Peripheral Registers */
#include "system_LPC11xx.h"

#define NOOP ((void)0)
#define LED       (1<<9)      /* LED D1 connect to PIO1_9 */

void ClockInit(void);
void SetICG_synced(void);   //Sets the ICG pin synchronously with PWM
void ClrICG_synced(void);   //Sets the ICG pin synchronously with PWM

#define ClockMask 256//0x01 << 8;   //pin 0_8
#define SHMask 512//0x01 << 9;  //pin 0_9
#define ICGMask 1024//0x01 << 10;   //pin 0_10

int timer_IRQ_done = 0;

int main(void)
{
     int i;

   ClockInit();
   LPC_IOCON->PIO0_9       &= ~0x07;        // Set pin dp2 as output
   LPC_IOCON->SWCLK_PIO0_10       &= ~0x043F;       // Set pin dp3 as CT16B0_MAT2
   LPC_IOCON->SWCLK_PIO0_10       |= 0x03;       // Set pin dp3 as CT16B0_MAT2

   LPC_GPIO0->DIR |= ICGMask | SHMask | ClockMask;

   LPC_GPIO0->MASKED_ACCESS[SHMask] = 0x0;
   LPC_GPIO0->MASKED_ACCESS[ICGMask] = 0x0FFF;


 while (1)
 {

  ClrICG_synced();
  NOOP;  //~200ns pause
  NOOP;
  NOOP;
  NOOP;
  NOOP;
  NOOP;
  LPC_GPIO0->MASKED_ACCESS[SHMask] = 0x0FFF;
  for (i = 0; i < 25; i++);
  LPC_GPIO0->MASKED_ACCESS[SHMask] = 0x0;
  for (i = 0; i < 25; i++);

  SetICG_synced();

  for (i = 0; i < 47135; i++);  //wait
 }
 // unreachable
 return 0;
}

void ClockInit(void)    //sets the PWM on pin dp1
{
 LPC_SYSCON->SYSAHBCLKCTRL |= (1<<7);    // enable clock for Timer0_16
 LPC_TMR16B0->PR  = 23;   // set divider to 23 (48/(2*(23+1)) = 1us)

 LPC_IOCON->PIO0_8       &= ~0x07;  //clear pin settings
 LPC_IOCON->PIO0_8       |= 0x02;        // set as Timer0_16 MAT0

 LPC_TMR16B0->PWMC       = (1<<3)        // set the Match3
                        | (1<<0)        // set PWM0
                        | (1<<1)        // set PWM1
                        ;

 LPC_TMR16B0->MR2 = 1;  // set the period to 1us
 LPC_TMR16B0->MR0 = 1;    // 50% duty cycle
 LPC_TMR16B0->MR1 = 1;    // 50% duty cycle

 LPC_TMR16B0->MCR = (3<<6);              // Reset tht TC when it matches the MR2 and generate interrupt

 LPC_TMR16B0->TCR = (1<<0);              // start Timer0_16

}

void SetICG_synced(void)
{
 #define MASK 512
 LPC_TMR16B0->EMR = (MASK);
 #undef MASK
}
void ClrICG_synced(void)
{
 #define MASK 260
 LPC_TMR16B0->EMR = (MASK);
 #undef MASK
}

Note: this code is designed for a very specific purpose and to be used in a more general application, it would have to be edited (most notably the SetICG_synced and ClrICG_synced functions, which would overwrite any other settings in the EMR).

Thanks for all the comments, the oscilloscope says all is fine now, so I think I can close this question.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.