I am trying to use an LPC804 to read a PWM input and at the same time generate a defined output pattern on 4 GPIO outputs to drive an H-bridge.
The PWM input is 20kHz fixed, and I need to find its duty cycle.
The output pattern I need to generate on the GPIOs somewhere around 22kHz but I need to track a resonance frequency, so it is variable. The output pattern has 3 different states for the pins, and they should last about 25,3,21 and 3 microseconds each (roughly, changing a bit depending on total frequency).
I wanted to do this by using the multi-rate timer (MRT) for the output and the standard counter timer (CTIMER) to demodulate the input PWM. I wanted to use one channel of the MRT in repeat mode to make the ~22kHz frequency, and then use a second channel of the MRT in one-shot mode to count down the number of microseconds necessary. The code is pretty straightforward; I pasted it below - and it also kind of works - only when I use short time intervals such as ...
LPC_MRT->Channel[1].INTVAL = ForceLoad | (3*15); // 3*15 counts should be 3 us
...the interrupt comes far too late - instead of generating an interrupt after 3us it takes about 10us to make the interrupt (measured with a scope).
I then went on to just toggle a single GPIO with the MRT, with a single channel of the MRT, with repeat mode. That works fine up until values of 150 (giving me a 100kHz timer), but values smaller than 150 seem to have no effect - it remains a 100kHz timer. So I am guessing that the MRT for some reason is unable to go faster than this, and values <150 written to the register are somehow rounded up to 150? However, nothing like this is written in the documentation.
My questions are:
(1) is there really a 100kHz limit on the MRT, and if yes why isn't it documented?
(2) does anyone see a way to achieve my objective of demodulating a PWM while at the same time generating a fast output waveform on 4 GPIOs with the LPC804? The CTIMER seems to be useful to generate PWM but since I think I need to use it to demodulate the input PWM I feel stuck?!
best regards
Martin
FWIW the code:
setup:
LPC_MRT->Channel[0].CTRL = (MRT_Repeat<<MRT_MODE) | (1<<MRT_INTEN);
LPC_MRT->Channel[1].CTRL = (MRT_OneShot<<MRT_MODE) | (1<<MRT_INTEN);
NVIC_EnableIRQ(MRT_IRQn);
LPC_MRT->Channel[0].INTVAL = 750;
With the 15MHz Clock, the value of 750 counter would correspond to 20 kHz.
interrupt service routine:
void MRT_IRQHandler(void) {
static int count = 0;
if (LPC_MRT->IRQ_FLAG & (1<<GFLAG0)) {
chan = 0;
count = 0;
}
else
count++;
// Clear the interrupt flags
LPC_MRT->Channel[0].STAT = 1<<MRT_INTFLAG;
LPC_MRT->Channel[1].STAT = 1<<MRT_INTFLAG;
switch(count) {
case 0: // main ~22kHz timer has triggered
LPC_GPIO_PORT->SET[0] = ((1<<DRV1_PIN) | (1<<DRV4_PIN));
LPC_GPIO_PORT->CLR[0] = ((1<<DRV2_PIN) | (1<<DRV3_PIN));
LPC_MRT->Channel[1].INTVAL = ForceLoad | (25*15); // 25*15 counts should be 25 us
break;
case 1:
LPC_GPIO_PORT->SET[0] = ((1<<DRV2_PIN) | (1<<DRV4_PIN));
LPC_GPIO_PORT->CLR[0] = ((1<<DRV1_PIN) | (1<<DRV3_PIN));
LPC_MRT->Channel[1].INTVAL = ForceLoad | (3*15); // 3*15 counts should be 3 us
break;
case 2:
LPC_GPIO_PORT->SET[0] = ((1<<DRV2_PIN) | (1<<DRV3_PIN));
LPC_GPIO_PORT->CLR[0] = ((1<<DRV1_PIN) | (1<<DRV4_PIN));
LPC_MRT->Channel[1].INTVAL = ForceLoad | (21*15); // 21*15 counts should be 21 us
break;
case 3:
LPC_GPIO_PORT->SET[0] = ((1<<DRV2_PIN) | (1<<DRV4_PIN));
LPC_GPIO_PORT->CLR[0] = ((1<<DRV1_PIN) | (1<<DRV3_PIN));
// no need to load a value to MRT channel 1 as channel 0 will trigger next
}