Reader Swetha wrote in with a query about performing 10Hz sampling on a dsPIC30F4011 in some kind of energy monitoring system. The following example illustrates a basic structure for interrupt-driven sampling using Timer 1.
// // 10Hz sampling example for dsPIC30F4011 // Written by Ted Burke // Last updated 15-1-2013 // // This program is an example of 10Hz sampling using Timer 1 on // the dsPIC30F4011. It doesn't currently do anything very useful. // the analog voltage on pin AN0 is sampled every 100ms and stored // in a data buffer. Once ten samples have been recorded to the // buffer, the samples are added together and multiplied by a // scaling factor to set the flashing rate on an LED on RD1. // The flashing rate is updated every ten samples. Another LED on // RD0 toggles each time a sample is recorded. The 10Hz sampling // is triggered by an interrupt on Timer 1. // #include <xc.h> #include <libpic30.h> // Configuration settings _FOSC(CSW_FSCM_OFF & FRC_PLL16); // Fosc=16x7.5MHz, Fcy=30MHz _FWDT(WDT_OFF); // Watchdog timer off _FBORPOR(MCLR_DIS); // Disable reset pin // Function prototypes unsigned int read_analog_channel(int n); // Function prototype for Timer 1 interrupt service routine void __attribute__((__interrupt__, __auto_psv__)) _T1Interrupt(void); // This counter variable will be used to identify // every tenth interrupt int counter = 0; // This data buffer stores 10 voltage readings int voltage[10]; // This controls the flashing rate of LED on RD0 long flash_delay = 0; int main() { // Configure RD0 and RD1 as digital outputs TRISD = 0b1100; // Configure AN0-AN8 as analog inputs ADCON3bits.ADCS = 15; // Tad = 266ns, conversion time is 12*Tad ADCON1bits.ADON = 1; // Turn ADC ON // Configure Timer 1 - set PR1 and TCKPS values for 10Hz // Timer 1 period = PR1*prescaler*Tcy = 11719*256*33.33ns = 100ms PR1 = 11719; // Set the Timer 1 period (max 65535) TMR1 = 0; // Reset Timer 1 counter IEC0bits.T1IE = 1; // Enable Timer 1 interrupt T1CONbits.TCKPS = 3; // Prescaler (0=1:1, 1=1:8, 2=1:64, 3=1:256) T1CONbits.TON = 1; // Turn on Timer 1 // Now, just let the interrupt service routine do the work while(1) { __delay32(flash_delay); // Toggle LED on RD1 _LATD1 = 1 - _LATD1; } return 0; } // Timer 1 interrupt service routine void __attribute__((__interrupt__, __auto_psv__)) _T1Interrupt(void) { // Clear Timer 1 interrupt flag IFS0bits.T1IF = 0; // Read a voltage from AN0 voltage[counter] = read_analog_channel(0); // Increment counter and check if 10 samples // have been collected counter = counter + 1; if (counter == 10) { // // DO SOMETHING WITH THE 10 SAMPLES HERE!!! // // For the time being, I'm just using the // average of the 10 samples to set the flashing // rate of the LED on RD0. // flash_delay = 0; while(counter > 0) flash_delay += voltage[--counter]; flash_delay = 1000 * flash_delay; // Reset counter counter = 0; } // Toggle LED on RD0 _LATD0 = 1 - _LATD0; } // This function reads a single sample from the specified // analog input. It should take less than 5us when the // microcontroller is running at 30 MIPS. // The dsPIC30F4011 has a 10-bit ADC, so the value // returned is between 0 and 1023 inclusive. unsigned int read_analog_channel(int channel) { ADCHS = channel; // Select the requested channel ADCON1bits.SAMP = 1; // Start sampling __delay32(30); // 1us delay @ 30 MIPS ADCON1bits.SAMP = 0; // Start Converting while (!ADCON1bits.DONE); // Should take 12 * Tad = 3.2us return ADCBUF0; }
I compiled this program with Microchip’s XC16 C compiler. I don’t use MPLAB, so I used the following build script to create a hex file, which I then downloaded to the dsPIC30F4011 using the PICkit2 software application.
xc16-gcc main.c -mcpu=30F4011 -Wl,--script=p30F4011.gld if errorlevel 0 xc16-bin2hex a.out
Reblogged this on Insight Engineering.
Thanks Ted for this great example. Would it be advisable to disable T1 interrupts at the start of T1 ISR and enable again at end of T1 ISR or is this unnecessary.
Hi David,
Interesting question. The honest answer is that I’m not 100% sure. I’ll explain my understanding of it, but please don’t take this as gospel. If it’s possible that another interrupt could be triggered while an ISR is still running from the previous occurence, then it would be better (or perhaps absolutely necessary?) to disable that interrupt while the ISR is executing.
However, in this particular case I think it’s not necessary. By definition (since this interrupt is being used to perform uniform sampling), this ISR is being triggered at absolutely regular intervals and it runs for the exact same length of time each time, which must be shorter than the sampling interval (Ts = 100us for 10kHz sampling) . The ISR must always complete before the next interrupt occurs, or else uniform sampling will break down. Hence, I don’t think disabling the interrupt makes any difference here. If it was necessary to do that, you’d have a problem even if you did it because the ISR would (at least sometimes) be running for too long to sustain uniform sampling.
Ted
Thanks for your quick reply Ted, So basically it’s only necessary to disable ISR if there’s a chance it may be re- triggered before it has time to complete. I understand now. Thanks again, David.
Yes, that’s my understanding. I should have mentioned though that it probably wouldn’t do any harm to disable and re-enable interrupts, even if it’s not necessary. I suppose disabling and re-enabling would use a few extra instruction cycles, but in practice it probably wouldn’t make any noticeable difference.
Ted
Ted,
Can you show me how to do it for two analog signals at AN0 and AN1. Because in this case ADC settings will matter otherwise ADC will overwrite its buffers depending on selection of our interrupts after how many number of conversions.
Hi. I am trying to implement something similar in a PIC24F04KA200. I have a chopped signal @100Hz. I want the ADC to read 8 samples (4 in positive part and 4 in negative part). For this operation, it is necessary to run a ISR at the instant of the rising edge and the falling edge. Can you give me a few pointers as to how this can be implemented?