Created Fri, 18 Sep 2015 08:54:35 +0000 by 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?
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/
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.
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;
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.
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...
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.
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.
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?
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.
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.