chipKIT® Development Platform

Inspired by Arduino™

Wi-Fire external ADC with Timer2 interrupt

Created Fri, 18 Sep 2015 08:54:35 +0000 by Demian


Demian

Fri, 18 Sep 2015 08:54:35 +0000

Hi,

I'm using the ChipKit Wi-Fire board where I use an external ADC to capture data and transmit it over the SPI channel based on the Timer2 interrupt. Getting the Timer2 interrupt and ADC capture to work independently is no problem, it's only when I combine them together that something halts in an odd way. Below is my code, and basically, for testing purposes, I sample 50 samples, after which the Timer2 is disabled and I print out the numbers on the serial monitor. In the interrupt routine I set an interrupt flag, which will be caught in the main loop.

/*
 CS: pin 10
 MOSI: pin 11
 MISO: pin 12
 SCK: pin 13
 */

#include <DSPI.h>
#include <xc.h>             /* contains Vector Name/Number Macros */
#include <sys/attribs.h>    /* contains __ISR() Macros */

DSPI0 spi;
const uint8_t chipSelect = 10;
uint8_t i;
uint16_t buffer[50];

uint16_t num1;
uint8_t num2;
const uint32_t SPI_frequency = 8000000;

uint8_t interrupt_flag;
uint8_t sample_counter;

void setup() {
  pinMode(4, OUTPUT);
  pinMode(chipSelect, OUTPUT);
  digitalWrite(chipSelect, HIGH);
  spi.begin();
  spi.setTransferSize(DSPI_16BIT);
  spi.setSpeed(SPI_frequency);
  Serial.begin(9600);

  interrupt_flag = 0;
  sample_counter = 0;

  // TIMER2 INTERRUPT
  /* Initialize Timer 2 Peripheral Settings */
  // Turn off the timer
  T2CONbits.TON = 0;

  // for 10 Hz sampling:
  T2CONbits.TCKPS = 7; // int clock = 390625 Hz
  PR2 = 39062; // fs = 10 Hz

  // Clear counter
  TMR2 = 0;
  /* Initialize Timer 2 Interrupt Controller Settings */
  // Set the interrupt priority to 1
  IPC2bits.T2IP = 1;
  // Reset the Timer 2 interrupt flag
  IFS0bits.T2IF = 0;
  // Enable interrupts from Timer 2
  IEC0bits.T2IE = 1;
  /* Enable the peripheral */
  T2CONbits.TON = 1;
}

void loop() {
  if(interrupt_flag == 1)
  {
    //digitalWrite(4, !digitalRead(4));
    digitalWrite(chipSelect, LOW);
    num1 = spi.transfer(0x00);
    digitalWrite(chipSelect, HIGH);
    buffer[i] = (uint8_t)(num1 >> 5);
    i++; 
    interrupt_flag = 0;
  }

  if(sample_counter == 50)
  {
    // Disable interrupts from Timer 2
    IEC0bits.T2IE = 0;
    // Disable the peripheral
    T2CONbits.TON = 0;

    for(i = 0; i < 50; i++)
    {
      Serial.print(buffer[i], BIN);
      Serial.print("\t");
      Serial.println(buffer[i], DEC);
    }
    while(1);
  }

}


void __ISR_AT_VECTOR (_TIMER_2_VECTOR, IPL4SRS) T2Interrupt(void)
{
  sample_counter++;
  interrupt_flag = 1;

  // Reset interrupt flag
  IFS0bits.T2IF = 0;
}

This code doesn't work as intended, it seems to halt. Instead, if my routine in the main loop, in case of an interrupt, looks like this:

if(interrupt_flag == 1)
  {
    digitalWrite(4, !digitalRead(4));
    //digitalWrite(chipSelect, LOW);
    //num1 = spi.transfer(0x00);
    //digitalWrite(chipSelect, HIGH);
    buffer[i] = (uint8_t)(num1 >> 5);
    i++; 
    interrupt_flag = 0;
  }

it works. Here I use GPIO 4 to give a square pulse train which I can monitor on an oscilloscope, and the serial monitor prints out all 0s, as expected. If I further uncomment the line "digitalWrite(chipSelect, LOW);", things halt again. Is this a digitalWrite problem? As mentioned above, the ADC SPI capture works well without the Timer2 interrupt, when my main loop looks like this:

void loop() {
  for(i = 0; i < 50; i++)
  {
    digitalWrite(chipSelect, LOW);
    num1 = spi.transfer(0x00);
    digitalWrite(chipSelect, HIGH);
    buffer[i] = (uint8_t)(num1 >> 5);
    delayMicroseconds(100);
  }
  
  for(i = 0; i < 50; i++)
  {
    Serial.print(buffer[i], BIN);
    Serial.print("\t");
    Serial.println(buffer[i], DEC);
  }
  while(1);
}

Any ideas what might be wrong?


majenko

Fri, 18 Sep 2015 09:36:28 +0000

You may be breaking the chipKIT interrupt system. The chipKIT core handles all the interrupts in its own RAM based vector table - you need to "hook" into that to use interrupts properly.

You can read more about it all here: http://chipkit.net/interrupts-made-easy-with-chipkit/


Demian

Fri, 18 Sep 2015 11:44:42 +0000

Hi,

Ok, so I tried to incorporate the ISR as described (only difference is that since I'm using the Wi-Fire board, the processor is of the PIC32MZ type, and hence all the _IRQ becomes _VECTOR (if I've understodd things correctly). However, I experience the exact same problems as before. Here's my code:

/*
 CS: pin 10
 MOSI: pin 11
 MISO: pin 12
 SCK: pin 13
 */

#include <DSPI.h>
#include <xc.h>             /* contains Vector Name/Number Macros */
#include <sys/attribs.h>    /* contains __ISR() Macros */

DSPI0 spi;
const uint8_t chipSelect = 10;
uint8_t i;
uint16_t buffer[50];

uint16_t num1;
uint8_t num2;
const uint32_t SPI_frequency = 8000000;

uint8_t interrupt_flag;
uint8_t sample_counter;

/* Define the Interrupt Service Routine (ISR) */
void __attribute__((interrupt)) myISR() {
  sample_counter++;
  interrupt_flag = 1;
  clearIntFlag(_TIMER_2_VECTOR);
}

void setup() {
  pinMode(4, OUTPUT);
  pinMode(chipSelect, OUTPUT);
  digitalWrite(chipSelect, HIGH);
  spi.begin();
  spi.setTransferSize(DSPI_16BIT);
  spi.setSpeed(SPI_frequency);
  Serial.begin(9600);

  interrupt_flag = 0;
  sample_counter = 0;

  setIntVector(_TIMER_2_VECTOR, myISR);
  setIntPriority(_TIMER_2_VECTOR, 4, 0);
  clearIntFlag(_TIMER_2_VECTOR);
  setIntEnable(_TIMER_2_VECTOR);
  start_timer_2();
}

void start_timer_2(void) {
  T2CONbits.TON = 0;         /* Turn the timer off */
  T2CONbits.TCKPS = 7;        /* Set the prescaler  */
  TMR2 = 0;                 /* Clear the counter  */
  PR2 = 39062; // 10 kHz             /* Set the frequency     */
  T2CONbits.TON = 1;         /* Turn the timer on  */

  // for 200 kHz:
  // prescaler:
  //T2CONbits.TCKPS = 1; 
  // period. 50 MHz/PR2.
  //PR2 = 250; // 200 kHz
  //PR2 = 5000; // 10 kHz

  // for 10 Hz sampling:
  //T2CONbits.TCKPS = 7; // int clock = 390625 Hz
  //PR2 = 39062; // fs = 10 Hz
  //PR2 = 390625; // fs = 1 Hz
} 

void loop() {
  
  if(interrupt_flag == 1)
  {
    //digitalWrite(4, !digitalRead(4));
    digitalWrite(chipSelect, LOW);
    //num1 = spi.transfer(0x00);
    digitalWrite(chipSelect, HIGH);
    buffer[i] = (uint8_t)(num1 >> 5);
    i++; 
    interrupt_flag = 0;
  }

  if(sample_counter == 50)
  {
    // Disable interrupts from Timer 2
    IEC0bits.T2IE = 0;
    // Disable the peripheral
    T2CONbits.TON = 0;

    for(i = 0; i < 50; i++)
    {
      Serial.print(buffer[i], BIN);
      Serial.print("\t");
      Serial.println(buffer[i], DEC);
    }
    while(1);
  }

}

If I comment the "digitalWrite(chipSelect, LOW);" and "digitalWrite(chipSelect, HIGH);" lines and uncomment "digitalWrite(4, !digitalRead(4));", things work as expected.


majenko

Fri, 18 Sep 2015 13:18:40 +0000

One thing I did just notice - your interrupt_flag isn't volatile. It needs to be marked as volatile or it may well get optimized out of your main loop.

volatile uint8_t interrupt_flag;

Demian

Fri, 18 Sep 2015 13:29:42 +0000

Thanks, but it still doesn't work. A bit strange, cause all I'm doing is doing a digitalWrite, which works when I'm not using an interrupt routine, rather a delay function.


majenko

Fri, 18 Sep 2015 15:02:10 +0000

Another thing - you should move the sample_counter++ into the main loop, not the interrupt routine - since that is where you are doing the sampling...


Demian

Fri, 18 Sep 2015 17:26:35 +0000

Thanks for the coding advice, I'll do that when I'm back in office on Wednesday. It's still just an odd problem though, doesn't make much sense that the program halts depending on what pin that is changing state.


Demian

Fri, 18 Sep 2015 17:28:45 +0000

... although, the chip attached to the Wi-Fire board is a pre-production silicon chip. Who knows what kind of bugs can come from it.


Demian

Thu, 24 Sep 2015 07:31:41 +0000

I've tried to debug a bit further, but with no luck getting it to work. However, I did try it on an WF32 board I have as well, and the same results there. By inserting a serial print at the end of the if-interrupt statement like this:

if(interrupt_flag == 1)
  { 
    digitalWrite(4, !digitalRead(4));
    digitalWrite(chipSelect, LOW);
    //num1 = spi.transfer(0x00);
    //digitalWrite(chipSelect, HIGH);
    buffer[i] = (uint8_t)(num1 >> 5);
    i++; 
    interrupt_flag = 0;
    sample_counter++;
    Serial.println(sample_counter, DEC);
  }

it shows that the if statement is called on the first interrupt, but never again afterwards. As mentioned before, if I comment out "digitalWrite(chipSelect, LOW);", everything works as expected. Any ideas, anyone, what might be wrong?


Demian

Tue, 29 Sep 2015 14:26:17 +0000

I guess I'll conclude this thread by saying that I solved the problem by using Timer3 instead of Timer2. Guess there must've been some conflict somewhere, but I have no idea where.


majenko

Tue, 29 Sep 2015 15:01:42 +0000

I think you have just identified the root cause - or at least provided enough data for me to find it :)

Timer 2 is one of the two timers available for generating PWM. It seems that is the timer that has been picked for use by the chipKIT core (hard coded).

Looking at the core code for digitalWrite:

if (timer != NOT_ON_TIMER)
        {
            turnOffPWM(timer);
        }

So is pin 10 "NOT_ON_TIMER" or is it in some way associated with PWM?

_TIMER_IC6 | _TIMER_OC9,    //    10  RG09    EBIA2/AN11/C2INC/ERXCLK/EREFCLK/AERXCLK/AEREFCLK/RPG9/PMA2/RG9

It's associated with PWM channel OC9. So it would go to turnOffPWM(), and that does:

if (pwm_active == 0)
    {
        T2CONCLR = TBCON_ON;
    }

So yes, accessing pin 10 with a digitalWrite will disable timer 2.

And that means your interrupt flag would never get set because the interrupt would never happen because the timer is turned off.

So basically stay away from timer 2, since it's already in use. At least on the MZ boards you have plenty of timers to play with.