stm32duino / STM32Examples

Arduino library to provide several examples for the Arduino core for STM32 MCUs.
140 stars 54 forks source link

How to decode protocol using input capture #27

Closed morcibacsi closed 4 years ago

morcibacsi commented 4 years ago

I would like to decode a 125kbs signal, where one bit is 8 microseconds long. The protocol is called VAN bus which is an automotive bus which was used in the early 2000s. I am using a blue pill (STM32F103CBT6). My first idea was to use the input capture to measure the pulse widths. I found the example for the hardware timer here: https://github.com/stm32duino/STM32Examples/blob/master/examples/Peripherals/HardwareTimer/InputCapture/InputCapture.ino

If I use TIMER_INPUT_CAPTURE_BOTHEDGE then the data I am capturing seems to be fine as it is the multiple of 8 which is the length of 1 bit in the protocol.

However I got stuck with the InputCapture_IT_callback as I can't find the way to decide the direction of the pulse (whether I had an interrupt on the falling or on the rising edge).

As you can see in the code below I tried a TIMER_INPUT_CAPTURE_FALLING and tried to switch the capture direction every time an interrupt occured but it seems it is too slow and it seems I miss some transitions.

So my question is there any way to get the direction of the pulse? Or maybe the input capture is not what I should use? If that is the case what should I try? Any hint/tip is greatly appreciated.

#if !defined(STM32_CORE_VERSION) || (STM32_CORE_VERSION  < 0x01090000)
#error "Due to API change, this sketch is compatible with STM32_CORE_VERSION  >= 0x01090000"
#endif

#define pin  PA6

volatile uint32_t channel;
volatile int32_t PulseWidth, LastCapture = 0, CurrentCapture;
uint32_t input_freq = 0;
volatile uint32_t rolloverCompareCount = 0;
volatile uint8_t is_first_captured = 0;
volatile uint32_t val1, val2;
volatile TimerModes_t currentMode;
HardwareTimer *MyTim;

void InputCapture_IT_callback(void)
{
  LastCapture = CurrentCapture;
  CurrentCapture = MyTim->getCaptureCompare(channel);
//
/*
  currentMode = MyTim->getMode(channel);
  if (currentMode == TIMER_INPUT_CAPTURE_FALLING){
    PulseWidth = (CurrentCapture - LastCapture)*-1;
    MyTim->pause();
    MyTim->setMode(channel, TIMER_INPUT_CAPTURE_RISING, pin);
    MyTim->resume();
  }else if (currentMode == TIMER_INPUT_CAPTURE_RISING){
    MyTim->pause();
    MyTim->setMode(channel, TIMER_INPUT_CAPTURE_FALLING, pin);
    MyTim->resume();
    PulseWidth = CurrentCapture - LastCapture;
  }
  //*/
  PulseWidth = CurrentCapture - LastCapture;
}

/* In case of timer rollover, frequency is to low to be measured set value to 0
   To reduce minimum frequency, it is possible to increase prescaler. But this is at a cost of precision. */
void Rollover_IT_callback(void)
{
  rolloverCompareCount++;

  if (rolloverCompareCount > 1)
  {
    PulseWidth = 0;
  }

}

void setup()
{
    Serial.begin(115200);
    pinMode(PA6, INPUT);

    // Automatically retrieve TIM instance and channel associated to pin
    // This is used to be compatible with all STM32 series automatically.
    TIM_TypeDef *Instance = (TIM_TypeDef *)pinmap_peripheral(digitalPinToPinName(pin), PinMap_PWM);
    channel = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(pin), PinMap_PWM));

    // Instantiate HardwareTimer object. Thanks to 'new' instantiation, HardwareTimer is not destructed when setup() function is finished.
    MyTim = new HardwareTimer(Instance);

    // Configure rising edge detection to measure frequency
    //MyTim->setMode(channel, TIMER_INPUT_CAPTURE_FALLING, pin);
    //MyTim->setMode(channel, TIMER_INPUT_CAPTURE_RISING, pin);
    MyTim->setMode(channel, TIMER_INPUT_CAPTURE_BOTHEDGE, pin);

    uint32_t PrescalerFactor = 72;
    MyTim->setPrescaleFactor(PrescalerFactor);
    MyTim->setOverflow(0x10000); // Max Period value to have the largest possible time to detect rising edge and avoid timer rollover
    MyTim->attachInterrupt(channel, InputCapture_IT_callback);
    MyTim->attachInterrupt(Rollover_IT_callback);
    MyTim->resume();

    // Compute this scale factor only once
    input_freq = MyTim->getTimerClkFreq() / MyTim->getPrescaleFactor();
}

void loop()
{
    if (PulseWidth>0){
        Serial.println((String)"PulseWidth = " + PulseWidth);
    }  
}
AnHardt commented 4 years ago

How about reading the pins state. The transition should always have been toward that state. Or set state = !state in every interrupt (but that could get out of sync).

ABOSTM commented 4 years ago

Hi @morcibacsi , I would be better to raise such question in the forum: https://www.stm32duino.com/ Otherwise I agree with @AnHardt , reading pin state should do the job (like any GPIO pin).

0xCAFEDECAF commented 4 years ago

Have a look at the ISR functions here: https://github.com/0xCAFEDECAF/VanBus .