Short dsPIC30F4011 program to test output compare SFR values

I’ve been playing around with the output compare feature of the dsPIC30F4011 microcontroller for the last day or two and finding out some interesting things about it. There are two output compare channels (OC1 on pin 23, OC2 on pin 18), each of which can generate a rectangular waveform (pulse train). Both channels are driven by the same timer, so when they generate period signals, both channels must have the same period. Apart from that though, the pulse start and stop times for each channel can be configured independently allowing arbitrary pulse width on each channel and phase difference between the channels.

The way I have output compare configured, the waveforms are controlled by the following five special function registers (SFRs): PR2, OC1R, OC1RS, OC2R, OC2RS.

  • I’m driving output compare with timer 2 (TMR2), so the period of both channels is determined by PR2, the timer 2 period register. TMR2 counts up from 0 until it reaches the same value as PR2, at which point it resets.
  • OC1 (pin 23) goes high when TMR2 reaches the same value as OC1R.
  • OC1 (pin 23) goes low when TMR2 reaches the same value as OC1RS.
  • OC2 (pin 18) goes high when TMR2 reaches the same value as OC1R.
  • OC2 (pin 18) goes low when TMR2 reaches the same value as OC1RS.

One thing was confusing me about the output compare waveforms produced – I wrote a program to vary the pulse width sinusoidally, but it seemed to be going awry when the pulse width was close to zero. Specifically, the pulses didn’t seem to be switching off at all! To investigate the problem, I wrote the following program which allows the OC1R, OC1RS, OC2R and OC2RS to be controlled in real time by sending commands (in a specific format) into the dsPIC via the UART. I’ve been sending in the values from the PICkit2 Programmer application.

Here’s the code:

//
// main.c - dsPIC30F4011 output compare test
// Written by Ted Burke
// Last updated 12-10-2012
//

#include <xc.h>
#include <libpic30.h>
#include <stdio.h>

// Configuration settings
_FOSC(CSW_FSCM_OFF & XT_PLL8);	// Fosc=80MHz (10MHz crystal, 8xPLL)
_FWDT(WDT_OFF);					// Watchdog timer off
_FBORPOR(MCLR_DIS);				// Disable reset pin

int main()
{
	long Fcy = 20000000;	// Fcy = Fosc/4
	int Tpwm = 200;			// PWM period in instruction cycles

	// Setup UART
	U1BRG = 31;            // 38400 baud @ 20 MIPS (actually 39062.5 baud)
	U1MODEbits.UARTEN = 1; // Enable UART

	// Make all port D pins outputs
	TRISD = 0;

	// Initialise OC channel 1 & 2 start and stop times
	OC1R = 0;
	OC1RS = 0;
	OC2R = 0;
	OC2RS = 0;

	// Set output compare mode for continuous pulses
	OC1CONbits.OCM = 0b101;
	OC2CONbits.OCM = 0b101;

	// Configure timer 2 (default timer for output compare)
	PR2 = Tpwm;			// 100kHz PWM frequency
	T2CONbits.TON = 1;	// Enable timer 2

	//
	// Now read incoming messages such as:
	//
	// "r1_150x" : Set OC1R = 150
	// "f1_23 "  : Set OC1RS = 23
	// "r2_0."   : Set OC2R = 0
	//
	// First character indicates rising or falling edge
	// Second character indicates channel 1 or 2
	// Digits indicate value to be written to FSR
	// Terminating character is any non-digit
	//
	char c;
	int rising;
	int channel;
	int value;
	while(1)
	{
		// Read 'r' for rising or 'f' for falling
		while (!U1STAbits.URXDA);
		c = U1RXREG;
		if (c != 'r' && c != 'f') continue;
		if (c == 'r') rising = 1; else rising = 0;

		// Read channel digit
		while (!U1STAbits.URXDA);
		c = U1RXREG;
		if (c != '1' && c != '2') continue;
		if (c == '1') channel = 1; else channel = 2;

		// Read underscore character
		while (!U1STAbits.URXDA);
		c = U1RXREG;
		if (c != '_') continue;

		// Read value one digit at a time until a
		// non digit character is encountered
		value = 0;
		while(1)
		{
			while (!U1STAbits.URXDA);
			c = U1RXREG;
			if (c < '0' || c > '9') break;
			value = (10 * value) + (c - 48);
		}

		// Set pulse start or stop times for specified channel
		if (channel == 1 && rising == 1)
		{
			OC1R = value;
			printf("Set OC1R=%d\n", value);
		}
		if (channel == 1 && rising == 0)
		{
			OC1RS = value;
			printf("Set OC1RS=%d\n", value);
		}
		if (channel == 2 && rising == 1)
		{
			OC2R = value;
			printf("Set OC2R=%d\n", value);
		}
		if (channel == 2 && rising == 0)
		{
			OC2RS = value;
			printf("Set OC2RS=%d\n", value);
		}
	}

	return 0;
}

I almost always use the internal fast RC osciallator, but on this occasion I happen to be using an external 10MHz crystal, together with the internal 8x PLL multipler, so the instruction frequency Fcy = 20MHz. This gives an instruction cycle Tcy = 50ns.

I’m using Microchip’s XC16 compiler, but I’m not using MPLAB, so I have the following batch file (filename: build.bat) to automate the build process:

xc16-gcc main.c -mcpu=30F4011 -Wl,--script=p30F4011.gld
if errorlevel 0 xc16-bin2hex a.out

I have LEDs connected to OC1 and OC2, so I can clearly see the effect of changing the pulse width on each channel. Here’s a screenshot of the UART tool in the PICkit2 programming application, which I’m using to send the commands to the dsPIC via its UART. In this example, I’ve sent four commands which have configured a pulse width of OC2 which is twice that on OC1 (1us and 0.5us respectively, I think).

This entry was posted in Uncategorized and tagged , , , , , , , , , , , , , , , , , . Bookmark the permalink.

Leave a Reply

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

WordPress.com Logo

You are commenting using your WordPress.com 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 )

Google+ photo

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

Connecting to %s