Page 1 of 2

Detecting RISING and FALLING edges on BOTH_EDGES interrupt

Posted: Sat Jun 23, 2012 5:17 am
by trunet
Hi,

I'm trying to interface a futaba receiver(R617FS) with my maple mini STM32F103 micro controller, using the HAL EXT driver.

I'm detecting both edges on interrupt but to calculate I need to know when is rising and when is falling edge to calculate the pulse. How can I do that? Is this the best way to do it?

Code: Select all

systime_t channel_now[4], channel_lastupdate[4];
uint16_t channel_value[4];
void rx_channel1_interrupt(EXTDriver *extp, expchannel_t channel) {
      (void)extp;
      (void)channel;

      chSysLockFromIsr();
      channel_now[0] = chTimeNow();
      channel_value[0] = channel_now[0] - channel_lastupdate[0];
      channel_lastupdate[0] = channel_now[0];
      chSysUnlockFromIsr();
}
void rx_channel2_interrupt(EXTDriver *extp, expchannel_t channel) {
      (void)extp;
      (void)channel;

      chSysLockFromIsr();
      channel_now[1] = chTimeNow();
      channel_value[1] = channel_now[1] - channel_lastupdate[1];
      channel_lastupdate[1] = channel_now[1];
      chSysUnlockFromIsr();
}
void rx_channel3_interrupt(EXTDriver *extp, expchannel_t channel) {
      (void)extp;
      (void)channel;

      chSysLockFromIsr();
      channel_now[2] = chTimeNow();
      channel_value[2] = channel_now[2] - channel_lastupdate[2];
      channel_lastupdate[2] = channel_now[2];
      chSysUnlockFromIsr();
}
void rx_channel4_interrupt(EXTDriver *extp, expchannel_t channel) {
      (void)extp;
      (void)channel;

      chSysLockFromIsr();
      channel_now[3] = chTimeNow();
      channel_value[3] = channel_now[3] - channel_lastupdate[3];
      channel_lastupdate[3] = channel_now[3];
      chSysUnlockFromIsr();
}

static const EXTConfig extcfg = {
   {
       {EXT_CH_MODE_DISABLED, NULL},
          {EXT_CH_MODE_DISABLED, NULL},
          {EXT_CH_MODE_DISABLED, NULL},
       {EXT_CH_MODE_DISABLED, NULL},
      {EXT_CH_MODE_DISABLED, NULL},
      {EXT_CH_MODE_RISING_EDGE | EXT_CH_MODE_AUTOSTART, mpu6050_interrupt_handler},
      {EXT_CH_MODE_DISABLED, NULL},
       {EXT_CH_MODE_DISABLED, NULL},
       {EXT_CH_MODE_DISABLED, NULL},
      {EXT_CH_MODE_DISABLED, NULL},
       {EXT_CH_MODE_DISABLED, NULL},
       {EXT_CH_MODE_FALLING_EDGE | EXT_CH_MODE_AUTOSTART, hmc5883_interrupt_handler},
       {EXT_CH_MODE_BOTH_EDGES | EXT_CH_MODE_AUTOSTART, rx_channel1_interrupt},
       {EXT_CH_MODE_BOTH_EDGES | EXT_CH_MODE_AUTOSTART, rx_channel2_interrupt},
       {EXT_CH_MODE_BOTH_EDGES | EXT_CH_MODE_AUTOSTART, rx_channel3_interrupt},
       {EXT_CH_MODE_BOTH_EDGES | EXT_CH_MODE_AUTOSTART, rx_channel4_interrupt}
   },
   EXT_MODE_EXTI(0, /* 0 */
                 0, /* 1 */
                 0, /* 2 */
                 0, /* 3 */
                 0, /* 4 */
                 EXT_MODE_GPIOB, /* 5 */
                 0, /* 6 */
                 0, /* 7 */
                 0, /* 8 */
                 0, /* 9 */
                 0, /* 10 */
                 EXT_MODE_GPIOA, /* 11 */
                 EXT_MODE_GPIOB, /* 12 */
                 EXT_MODE_GPIOB, /* 13 */
                 EXT_MODE_GPIOB, /* 14 */
                 EXT_MODE_GPIOB) /* 15 */
};

Re: Detecting RISING and FALLING edges on BOTH_EDGES interru

Posted: Sat Jun 23, 2012 5:54 am
by Giovanni
Is the input signal a PWM? if so you can use the ICU driver which will give you both period and pulse width.

Giovanni

Re: Detecting RISING and FALLING edges on BOTH_EDGES interru

Posted: Sat Jun 23, 2012 8:52 am
by trunet
The radio control signal isn’t really PWM, (although it is often called that) it’s a form of pulse position modulation (PPM). I just need to know the width of the pulse and not the period, maybe PWM is overkill. I don't know if the best way is using PWM decode using ICU(I'll have to read 7 PWM channels) or interrupts on 7 pins(on each R/C channel).

I found a post with some pictures of the PPM signal on each channel:
http://rcarduino.blogspot.com.br/2012/01/how-to-read-rc-receiver-with.html
http://rcarduino.blogspot.co.uk/2012/04/how-to-read-multiple-rc-channels-draft.html

You don't need to read all, just look the pictures and you'll see.

If EXT callback function can say if it's rising or falling will be great.

Re: Detecting RISING and FALLING edges on BOTH_EDGES interru

Posted: Sat Jun 23, 2012 10:51 am
by Giovanni
The problem with EXT is that interrupt latency would introduce jitter in your measurement, what the the width range of those pulses? EXT is not able to give you the front direction but you could read the pin in the callback.

The ICU driver measurement is done in HW so it would be far more accurate. The problem is that it supports only 6 timers.

Giovanni

Re: Detecting RISING and FALLING edges on BOTH_EDGES interru

Posted: Sat Jun 23, 2012 10:58 pm
by trunet
I found a code that uses timer and interrupts to get it done. However I didn't understand that code very well.

I think they are using a timer running on 1Mhz and interrupts making the calculation with this timer. Maybe they're bypassing the chibios hal ext driver and doing it manually on the code. So, I want to rewrite this using EXT Driver. The pulse width is 1-2 microseconds. I want to know how it's possible to know when it's rising and when it's falling on callback function.

Code: Select all

  // set up TIM2 for RC capture to run at ~1MHz (CLK/72)
  // (could probably use SysTick for this?)
  TIM2->CR1 = 0x00000000;
  RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
  TIM2->SMCR = 0; // slave mode disabled
  TIM2->PSC = 72;
  TIM2->ARR = 0xffff;
  TIM2->SR = 0;
  TIM2->DIER = 0;
  TIM2->CR1 = 0x00000001;

  // configure lines as input
  palSetGroupMode(IOPORT1,0x1f,PAL_MODE_INPUT);
  // use lines PA[0-4] for EXTI[0-4]
  AFIO->EXTICR[0] = 0x00000000;
  AFIO->EXTICR[1] = 0x00000000;
  // enable EXTI[0-4] interrupt for RC input capture
  NVICEnableVector(EXTI0_IRQn, STM32_EXTI_PRIORITY);
  NVICEnableVector(EXTI1_IRQn, STM32_EXTI_PRIORITY);
  NVICEnableVector(EXTI2_IRQn, STM32_EXTI_PRIORITY);
  NVICEnableVector(EXTI3_IRQn, STM32_EXTI_PRIORITY);
  NVICEnableVector(EXTI4_IRQn, STM32_EXTI_PRIORITY);
  // mask EXTI[0-4]
  EXTI->IMR = 0x0000001f;
  //trigger on falling edge
  EXTI->FTSR = 0x0000001f;
  // trigger on rising edge
  EXTI->RTSR = 0x0000001f;


Interrupt handler:

Code: Select all

CH_IRQ_HANDLER(Vector60) { // EXTI2 external interrupt handler
  CH_IRQ_PROLOGUE();
  char inp = GPIOA->IDR & (1<<2);
  if (inp) { // rising edge
    unsigned short tmp = TIM2->CNT - RC_INPUT_LAST_TCNT; // channel 3
    if (RC_IN_RANGE(tmp)) RC_INPUT_CHANNELS[2] = tmp;
  }
  else {
    unsigned short tmp = TIM2->CNT - RC_INPUT_LAST_TCNT; // channel 4
    if (RC_IN_RANGE(tmp)) RC_INPUT_CHANNELS[3] = tmp;
  }
  RC_INPUT_LAST_TCNT = TIM2->CNT;
  EXTI->PR |= 0x4;
  CH_IRQ_EPILOGUE();
}

Re: Detecting RISING and FALLING edges on BOTH_EDGES interru

Posted: Sun Jun 24, 2012 5:39 am
by Giovanni
Giovanni wrote:The problem with EXT is that interrupt latency would introduce jitter in your measurement, what the the width range of those pulses? EXT is not able to give you the front direction but you could read the pin in the callback.


This applies to the above code. It could be rewritten using PAL, EXT and the HAL realtime counter. There is the problem of IRQ jitter for such short pulses.

Giovanni

Re: Detecting RISING and FALLING edges on BOTH_EDGES interru

Posted: Wed Jun 27, 2012 3:20 am
by trunet
I made it work. The pulses is 1 to 2 milliseconds not micro sorry. And it's jittering a little bit, around 100 microseconds each pulse. I can survive with this but is a problem. Is there any way to improve the jitter? Why this happens? I tried to search about interrupt jitter and exti jitter on stm32 and didn't find anything useful.

Interrupt code:

Code: Select all

void rx_channel1_interrupt(EXTDriver *extp, expchannel_t channel) {
      (void)extp;
      (void)channel;

      chSysLockFromIsr();
      if (palReadPad(GPIOB, 12) == PAL_LOW) {
         unsigned short tmp = TIM2->CNT - RC_INPUT_LAST_TCNT;
         if (RC_IN_RANGE(tmp)) RC_INPUT_CHANNELS[0] = tmp;
      }
      RC_INPUT_LAST_TCNT = TIM2->CNT;
      chSysUnlockFromIsr();
}


Timer initialization code:

Code: Select all

   TIM2->CR1 = 0x00000000;
   RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
   TIM2->SMCR = 0; // slave mode disabled
   TIM2->PSC = 72;
   TIM2->ARR = 0xffff;
   TIM2->SR = 0;
   TIM2->DIER = 0;
   TIM2->CR1 = 0x00000001;

Re: Detecting RISING and FALLING edges on BOTH_EDGES interru

Posted: Wed Jun 27, 2012 7:34 am
by Giovanni
There is an article about jitter in the documentation, you may put your ISR at priority zero (highest) but you cannot call OS functions at that priority (PAL functions should be OK).

Note, that would make it a "fast interrupt" read about those in the documentation because a different declaration is required.

Giovanni

Re: Detecting RISING and FALLING edges on BOTH_EDGES interru

Posted: Wed Jun 27, 2012 5:39 pm
by trunet
So, EXT HAL Driver is an interrupt wrapper only and fast interrupt isn't supported by EXT right?

Re: Detecting RISING and FALLING edges on BOTH_EDGES interru

Posted: Wed Jun 27, 2012 6:50 pm
by Giovanni
Yes, in that case you cannot use the EXT driver (but can take code from it, the main difference is how the IRQ handler is declared).

Giovanni