Simple interrupt-driven sampling example for the dsPIC30F4011

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
		// 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)
		// 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
This entry was posted in PIC and tagged , , , , , , , , . Bookmark the permalink.

7 Responses to Simple interrupt-driven sampling example for the dsPIC30F4011

  1. David Thomas says:

    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.

    • batchloaf says:

      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.


      • David Thomas says:

        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.

      • batchloaf says:

        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.


  2. amitroy1984 says:

    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.

  3. sobo1325 says:

    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?

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s