cancel
Showing results for 
Search instead for 
Did you mean: 

Is there an interaction between SPI1 and TIMER3 on the STM32F103C8T6?

DMark.983
Associate

I am encountering a problem where I am using TIMER3 to generate a periodic interrupt, however, if I transact data over the SPI1 peripheral, the periodic interrupts stop to TIMER3. I am not trying to use the interrupt on the TIMER3 external pins, as these are shared with the SPI1 peripheral. The application is a vector network analyzer, a device to measure RF components. The idea is to demodulate a signal captured on 3 ADC pins that is sampled at each time TIMER3 capture compare is triggered, which is quadrature demodulated. Perhaps there is some errata I don't know about that explains this interaction?

I apologize for the code, but it is for the maple labs stm32duino, because I wrote this code for Arduino before the HAL version of stm32duino became available. I am not ready to move to HAL as it might exhaust the flash space in the microcontroller.

In particular, I have two interrupts set up, one to trigger on the rising edge of PB15, and the other to trigger with a compare capture with TIMER3. The interrupt routine ifClockInterrupt is triggered by a rising edge on the IFCLOCK_PIN (PB15). This signal is generated by an intermediate frequency from a mixer that is run through a zero-crossing comparator detector. Because there is clock jitter due to the SI5351A fractional divider, each period of the IF signal is not exactly the same length. So what I do is measure the time of the last period using the Cortex M3 cycle counter to estimate the time of the next clock cycle. I then set up the timer3 to generate equally spaced interrupts, over that clock cycle for the timerContInterrupt, so that timer3 is paused and resumed each clock cycle to adjust its compare capture value. The timer3 fires, and each time it measures three ADC inputs and then adds these to an accumulated signal to measure the quadrature (real and imaginary) part of the IF signal. After 4 or 12 timer3 clocks have passed (which depends on whether or not we are capturing the first or third harmonic of the IF), the timer3 stops accumulating and when the next edge of the trigger signal on PB15 occurs this cycle repeats until a certain number of periods of the IF signal have been averaged to accumulate a signal without clock jitter. I need the TIMER3 because it is the way I can time acquiring the quadrature samples of the IF inside a single period of the IF signal. The IF signal is set up to be about 10 kHz if measuring the first harmonic and 3.333 kHz for the third harmonic, and so the ADC is sampled at approximately 40 kHz.

At the same time, a separate task is running that uses a ILI9341 display controlled through SPI and if the display is polled, the timerContInterrupt triggered by TIMER3 capture compare stops interrupting and data acquisition stops.

Any ideas you could share are most appreciated.

#define IFCLOCK_PIN PB15
 
void setup_if_clock(void)
{
  nvic_irq_set_priority(NVIC_EXTI_15_10, 2);
  attachInterrupt(IFCLOCK_PIN, ifClockInterrupt, RISING);
 
  nvic_irq_set_priority(NVIC_TIMER3, 2);
  timer_init(TIMER3);
  timer_attach_interrupt(TIMER3, 1, timerContInterrupt);
}
 
void ifClockInterrupt(void)
{
  unsigned int timerval = CPU_CYCLES;
 
  diftick = ((unsigned int)(timerval - lasttick));
  lasttick = timerval;
 
  if (current_summed < number_to_sum)
  {
    if (current_summed == 0)
    {
      sampCURI = 0; sampCURQ = 0;
      sampVOLTI = 0; sampVOLTQ = 0;
      sampCUR2I = 0; sampCUR2Q = 0;
      sampPWR = 0;
    }
    current_summed++;
    timer_pause(TIMER3);
    timer_set_count(TIMER3, 0);
    timerval = diftick / numphases;
    if (timerval > (F_CPU / 200000u))
    {
      timer_set_reload(TIMER3, timerval);
      timer_generate_update(TIMER3);
      curphase = 0;
      timer_resume(TIMER3);
    }
  }
}
 
void timerContInterrupt(void)
{
  //unsigned int timerval = CPU_CYCLES;
 
  if (curphase < numphases)
  {
    analogReadPins();
    switch (curphase & 0x03)
    {
      case 0: sampCURI += sampCUR;
        sampVOLTI += sampVOLT;
        sampCUR2I += sampCUR2;
        break;
      case 1: sampCURQ -= sampCUR;
        sampVOLTQ -= sampVOLT;
        sampCUR2Q -= sampCUR2;
        break;
      case 2: sampCURI -= sampCUR;
        sampVOLTI -= sampVOLT;
        sampCUR2I -= sampCUR2;
        break;
      case 3: sampCURQ += sampCUR;
        sampVOLTQ += sampVOLT;
        sampCUR2Q += sampCUR2;
        break;
    }
    sampPWR += sampCUR2;
    curphase++;
  }
}
 
int acquire_sample(unsigned int averages, unsigned int timeout)
{
  bool timedout = false;
  unsigned int inittime, curtime;
 
  //timer_init(TIMER3);
  timer_pause(TIMER3);
  timer_attach_interrupt(TIMER3, 1, timerContInterrupt);
  timer_set_prescaler(TIMER3, 0);
  timer_set_mode(TIMER3, 1, TIMER_OUTPUT_COMPARE);
  timer_set_count(TIMER3, 0);
  timer_set_reload(TIMER3,65535);
  timer_set_compare(TIMER3, 1, 0);
  timer_generate_update(TIMER3);
 
  timeout = timeout * (F_CPU / 1000u);
  /* wait for previous acquisition to stop */
  inittime = CPU_CYCLES;
  timedout = false;
  current_summed = 0;
  while (current_summed < number_to_sum)
  {
    curtime = CPU_CYCLES;
    if (((unsigned int)(curtime - inittime)) > timeout)
    {
      timedout = true;
      break;
    }
    if (vif != NULL) vif(vif_data);
  }
  if (timedout) return -1;
  number_to_sum = averages;
  current_summed = 0;
  /* wait for previous acquisition to stop */
  inittime = CPU_CYCLES;
  timedout = false;
  while (current_summed < number_to_sum)
  {
    curtime = CPU_CYCLES;
    if (((unsigned int)(curtime - inittime)) > timeout)
    {
      timedout = true;
      break;
    }
    if (vif != NULL) vif(vif_data);
  }
  if (timedout) return -1;
  return 0;
}
 
bool vna_acquire_dataset(vna_acquisition_state *vs, vna_acquire_dataset_operation vado, void *v)
{
  int n,c;
  unsigned int freqstep;
  vna_acquire_dataset_state vads;
 
  vna_initialize_si5351();
  freqstep = (vs->endfreq - vs->startfreq) / vs->nfreqs;
  for (n = 0; n < vs->nfreqs; n++)
  {
    unsigned int freq = vs->startfreq + freqstep * n;
    setup_frequency_acquire(freq);
    if (n == 0)
    {
      if (acquire_sample(12, vs->timeout) < 0)
        return false;
    }
    if (acquire_sample(vs->num_averages, vs->timeout) < 0)
      return false;
    vads.n = n;
    vads.total = vs->nfreqs;
    vads.freq = freq;
    vads.volti = sampVOLTI;
    vads.voltq = sampVOLTQ;
    vads.curi = sampCURI;
    vads.curq = sampCURQ;
    vads.cur2i = sampCUR2I;
    vads.cur2q = sampCUR2Q; 
    vado(&vads, v);
	  if (console_inchar() == '!')
        return false;
#ifdef VNA_TOUCHSCREEN
    if (touchscreen_enabled && touchscreen_abort())
       return false;
#endif
  }
  return true;
}

1 REPLY 1

> TIMER3 capture compare stops interrupting and data acquisition stops.

At that moment, in the debugger, read out the content of relevant GPIO/AFIO registers and the timer (and maybe also NVIC registers), and check if they are set as expected.

This is *the* basic debugging method, independent of the used language/"libraries", as the mcu does not care about any "library", it works by the registers settings.

JW

PS. Please change your username to a normal nick.