Detecting RISING and FALLING edges on BOTH_EDGES interrupt

ChibiOS public support forum for all topics not covered by a specific support forum.

Moderators: RoccoMarco, lbednarz, tfAteba

trunet
Posts: 54
Joined: Fri Apr 06, 2012 7:18 pm

Detecting RISING and FALLING edges on BOTH_EDGES interrupt

Post 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 */
};
User avatar
Giovanni
Site Admin
Posts: 14891
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 996 times

Re: Detecting RISING and FALLING edges on BOTH_EDGES interru

Post 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
trunet
Posts: 54
Joined: Fri Apr 06, 2012 7:18 pm

Re: Detecting RISING and FALLING edges on BOTH_EDGES interru

Post 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.
User avatar
Giovanni
Site Admin
Posts: 14891
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 996 times

Re: Detecting RISING and FALLING edges on BOTH_EDGES interru

Post 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
trunet
Posts: 54
Joined: Fri Apr 06, 2012 7:18 pm

Re: Detecting RISING and FALLING edges on BOTH_EDGES interru

Post 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();
}
User avatar
Giovanni
Site Admin
Posts: 14891
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 996 times

Re: Detecting RISING and FALLING edges on BOTH_EDGES interru

Post 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
trunet
Posts: 54
Joined: Fri Apr 06, 2012 7:18 pm

Re: Detecting RISING and FALLING edges on BOTH_EDGES interru

Post 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;
User avatar
Giovanni
Site Admin
Posts: 14891
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 996 times

Re: Detecting RISING and FALLING edges on BOTH_EDGES interru

Post 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
trunet
Posts: 54
Joined: Fri Apr 06, 2012 7:18 pm

Re: Detecting RISING and FALLING edges on BOTH_EDGES interru

Post by trunet »

So, EXT HAL Driver is an interrupt wrapper only and fast interrupt isn't supported by EXT right?
User avatar
Giovanni
Site Admin
Posts: 14891
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 996 times

Re: Detecting RISING and FALLING edges on BOTH_EDGES interru

Post 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
Post Reply