Difference between revisions of "PIC32MX: Sinusoidal Analog Output"
LawrenceChen (talk | contribs) |
LawrenceChen (talk | contribs) |
||
Line 38: | Line 38: | ||
==Sample Code== |
==Sample Code== |
||
This sample code creates an analog output on PINS D0 and D1 using 200 points at a sine frequency of 25Hz. |
|||
Include your sample code (this may need cleaned up a bit). |
|||
/****************************************************** |
|||
To make a box around the code, put a 'space' in front of each line. |
|||
Sinusoidal Output |
|||
*/ |
|||
#include "HardwareProfile.h" |
|||
#include "math.h" |
|||
#define DESIRED_BAUDRATE (9600) // The desired BaudRate |
|||
#define NUM_POINTS_PER_PERIOD 200 |
|||
int timeStep = 0; |
|||
int LookupTable[NUM_POINTS_PER_PERIOD]; |
|||
int main(void) |
|||
{ |
|||
int sineFreq = 25; |
|||
int interruptFreq = NUM_POINTS_PER_PERIOD * sineFreq; |
|||
int periodValue = (SYS_FREQ / interruptFreq / 1) - 1; |
|||
// Configure the proper PB frequency and the number of wait states |
|||
SYSTEMConfigPerformance(SYS_FREQ); |
|||
// Allow vector interrupts |
|||
INTEnableSystemMultiVectoredInt(); |
|||
// init OC1 module, on pin D0 |
|||
OpenOC1( OC_ON | OC_TIMER2_SRC | OC_PWM_FAULT_PIN_DISABLE, 0, 0); |
|||
// init OC1 module, on pin D1 |
|||
OpenOC2( OC_ON | OC_TIMER2_SRC | OC_PWM_FAULT_PIN_DISABLE, 0, 0); |
|||
// init Timer2 mode and period (PR2) // produces 20kHz PWM |
|||
OpenTimer2( T2_ON | T2_PS_1_1 | T2_SOURCE_INT, 3999); |
|||
int i; |
|||
for (i = 0; i < NUM_POINTS_PER_PERIOD; i++) |
|||
{ |
|||
LookupTable[i] = (int)(3999/2 * sin(2*M_PI/NUM_POINTS_PER_PERIOD*i)+ 3999/2); |
|||
} |
|||
// init Timer3 for Sinusoid Interrupt |
|||
// Assume Prescalar is 2 |
|||
// Need to verify that SYS_FREQ / sineFreq / numPoints < 65534 |
|||
OpenTimer3( T3_ON | T3_PS_1_1 | T3_SOURCE_INT, periodValue); |
|||
mT3SetIntPriority( 7); // set Timer3 Interrupt Priority |
|||
mT3ClearIntFlag(); // clear interrupt flag |
|||
mT3IntEnable( 1); // enable timer3 interrupts |
|||
// this will store the string for the LCD |
|||
char LCDbuffer[33]; |
|||
while(1) |
|||
{ |
|||
// Timer 3 ISR takes care of the code |
|||
} |
|||
CloseOC1(); |
|||
}//end main |
|||
// interrput code |
|||
void __ISR( _TIMER_3_VECTOR, ipl7) T3Interrupt( void) |
|||
{ |
|||
if (++timeStep >= NUM_POINTS_PER_PERIOD) |
|||
{ |
|||
timeStep = 0; |
|||
} |
|||
SetDCOC1PWM(LookupTable[timeStep]); |
|||
SetDCOC2PWM(LookupTable[timeStep]); |
|||
// clear interrupt flag and exit |
|||
mT3ClearIntFlag(); |
|||
} // T3 Interrupt |
Revision as of 17:40, 2 May 2010
The PIC32 is actually incapable of directly creating an analog output. Instead, the PIC32 can perform pulse width modulation (PWM) in combination with an external RC circuit to create the analog output. Essentially, PWM varies the time that the output pin is on (output is 3.3V) and off (output is 0V) to create the duty cycle which can simulate voltages between 0V and 3.3V. For example, if the duty cycle puts out 3.3V for 50% of the time and 0V for the other 50%, the effective output voltage averages out to be 1.65V. Thus, to create the sinusoidal output, the sine wave must be divided into a number of points so that the PIC32 will know what duty cycle to output at what time. In essence, this method is similar to connecting the dots.
The three most important parameters in creating the sinusoidal analog output are the cutoff frequency, the number of points per period, and the RC time constant. The combination of the cutoff frequency and the points per period helps tell the PIC when to change the duty cycle. The RC constant of an external RC circuit is important in creating smooth and accurate curves and in improving the quality of the output signal.
The code for the analog output will first generate a lookup table. This table contains values for the specific duty cycles and will generate the number of duty cycles based on the number of points specified by the user. After generating the table, the code goes through a loop which tells the board to output the appropriate PWM corresponding to the duty cycle in the lookup table at each time step. By looping through the duty cycles at the same time intervals, the output voltage is varied to match that of a sine wave. The external RC circuit acts as a buffer to store these voltages to smooth the final output voltage to yield the sine wave. Without this external RC circuit, the output would only be a series of pulses matching the duty cycle at a given time.
The examples below have a PWM frequency of 20kHz.
Cutoff Frequency
How to choose cut-off frequency -- what is the equation?
Show picture if you choose your frequency to high and if you choose your frequency to low.
Say something about how the amplitude decreases with high frequency
Number of Points Per Period
Selecting the number of points to use to generate the sine wave is important because choosing too few will result in a very rough sine wave. When the number of points is too low, you can actually see multiple RC charging graphs between each point. However, if we choose too many points, a peak appears at what should be 0V on the sine wave. To prevent this peek from occurring, one could insert an “if” statement in the lookup table generation loop that replaces all of the occurring 0’s into 1’s. Inclusion of the if statement would result in no true 0V output for that particular output. The ideal number of points for 25Hz is around 200 points.
RC Time Constant
The RC time constant plays a big role in creating the sine wave. By increasing the RC constant, the quality of the output signal is increased, where "quality" refers to the width of the output signals. Despite having such advantages, a larger RC constant will result in a phase lag which causes a delay between the output of the PIC and the output from the whole circuit.
Sample Code
This sample code creates an analog output on PINS D0 and D1 using 200 points at a sine frequency of 25Hz.
/****************************************************** Sinusoidal Output */ #include "HardwareProfile.h" #include "math.h" #define DESIRED_BAUDRATE (9600) // The desired BaudRate #define NUM_POINTS_PER_PERIOD 200 int timeStep = 0; int LookupTable[NUM_POINTS_PER_PERIOD]; int main(void) { int sineFreq = 25; int interruptFreq = NUM_POINTS_PER_PERIOD * sineFreq; int periodValue = (SYS_FREQ / interruptFreq / 1) - 1; // Configure the proper PB frequency and the number of wait states SYSTEMConfigPerformance(SYS_FREQ); // Allow vector interrupts INTEnableSystemMultiVectoredInt(); // init OC1 module, on pin D0 OpenOC1( OC_ON | OC_TIMER2_SRC | OC_PWM_FAULT_PIN_DISABLE, 0, 0); // init OC1 module, on pin D1 OpenOC2( OC_ON | OC_TIMER2_SRC | OC_PWM_FAULT_PIN_DISABLE, 0, 0); // init Timer2 mode and period (PR2) // produces 20kHz PWM OpenTimer2( T2_ON | T2_PS_1_1 | T2_SOURCE_INT, 3999); int i; for (i = 0; i < NUM_POINTS_PER_PERIOD; i++) { LookupTable[i] = (int)(3999/2 * sin(2*M_PI/NUM_POINTS_PER_PERIOD*i)+ 3999/2); } // init Timer3 for Sinusoid Interrupt // Assume Prescalar is 2 // Need to verify that SYS_FREQ / sineFreq / numPoints < 65534 OpenTimer3( T3_ON | T3_PS_1_1 | T3_SOURCE_INT, periodValue); mT3SetIntPriority( 7); // set Timer3 Interrupt Priority mT3ClearIntFlag(); // clear interrupt flag mT3IntEnable( 1); // enable timer3 interrupts // this will store the string for the LCD char LCDbuffer[33]; while(1) { // Timer 3 ISR takes care of the code } CloseOC1(); }//end main // interrput code void __ISR( _TIMER_3_VECTOR, ipl7) T3Interrupt( void) { if (++timeStep >= NUM_POINTS_PER_PERIOD) { timeStep = 0; } SetDCOC1PWM(LookupTable[timeStep]); SetDCOC2PWM(LookupTable[timeStep]); // clear interrupt flag and exit mT3ClearIntFlag(); } // T3 Interrupt