Tutorial 2: PWM controller
PWM tutorial files (for use with this tutorial and with Exp. 5)
Inverter files (for use with Exp. 4)
This tutorial is an introduction to the sample PV system software framework. It illustrates how to access analog to digital converter (ADC) data, control the PWM output, and use the DAC to provide a measureable voltage related to the PWM. It also provides a review of some of the rules of C programming syntax.
The final program measures the voltage produced by a potentiometer voltage divider between 0 and 2.5V and translates that level to a duty cycle. The duty cycle is limited to a range of 10-90%. Also, an analog voltage is produced by a DAC output with a voltage in the range 0-1 V corresponding to the duty cycle.
The MSP430F169 has 12-Bit ADC with an input range of 0-2.5V determined by the MSP's onboard voltage reference. Being a 12-Bit converter there are 4096 discrete measureable values between 0 and 2.5V. IMPORTANT!!! The absolute maximum voltage that can be applied to an ADC input, without causing damage, is 3.6V! You will be making voltage measurements with the ADC using a potentiometer and the dev board's +3.3V supply. There are a number of pins labelled as 3.3V_# on the dual row header.
- Construct a simple voltage divider with a potentiometer to provide voltage from 0-3.3V.
- With a jumperwire, connect the voltage output of your resistor divider to ADC0 (P6.2) on the dev board.
Download the project files and extract them to a working directory. Start IAR and open the project. The project contains five files:
- main.c contains the system startup routine and the main system loop. This is where control routines will go.
- periph.c contains configuration subroutines for all used peripheral devices as well as some routines for starting and stopping peripherals.
- periph.h simply provides function prototypes for the externally callable functions in periph.c.
- config.h contains system and board constants and some typedefs.
- msp430x16x.h is TI's provided include file and contains #define'd register names and constants for the MSP430. All of the names match those in the family datasheet.
Theory of operation:
The software framework is intended to let you program a digital control loop without having to first learn the specifics of working with the MSP430. The basic framework samples 4 ADC channels, manages PWM generation, and generates a sinusoidal 60Hz reference autonomously, in the background.
At startup, an initial call to systemInit() configures and starts all system peripherals. Execution then falls into an infinite while(1) loop. All user designed control algorithms should be executed within this loop. This strategy prevents any potential priority level conflicts between software interrupts. A simple control algorithm would:
- Read ADC values from the array Adc_Data
- Calculate a PWM duty cycle
- Update the global variable D0 with the new PWM duty cycle
The ADC and DAC operate in the background using the MSP430's DMA controller. The CPU is commanded by the DMA controller to move data to and from the DAC and ADC and memory. Placing the control code within the infinite while(1) loop results in a "best effort" control configuration where control parameters, such as the PWM, are updated only as fast as the system is capable. PWM duty cycles are updated in the background at the end of each PWM period by reading the variables D0 and D1. A second DAC channel is also available to produce an analog output signal.
Using the ADC (Updated 2/22/08)
Open main.c for editing from the file browser on the left. The main() routine should look like the excerpt below and do nothing except initialize the system.
The four ADC channels used by the sample framework are sampled synchronously with the PWM. Each switching period a single ADC channel is measured. To avoid excessive switching noise, the samples are made during the last 2us of each switching period before the next rising edge. At the end of each sequence of 4 samples the DMA controller copies the data from the ADC data registers into a four element array called Adc_Data. It takes four switching periods before valid ADC data is available in the array. The array values are cleared to 0 at startup.
Make the following changes to the code:
Since the DMA access to the Adc_Data array is not synchronized with the code execution within the while loop you should use a temporary variable to store your working values. This way measured voltage does not mysteriously change from 1V to 1.1V halfway through a series of calculations. Note that this only becomes an issue if the amount of time spent working on the ADC value takes more time than the ADC sample period.
Remember to add variable declarations for your variables. If you have been working in Matlab this is easy to forget! The data type uint16 is an unsigned 16-Bit integer.
Generating PWM (Updated 2/22/08)
We now need to use the measured value of ADC0 to calculate a duty cycle.
On the MSP430, PWM signals are generated by switching a timer output high at the beginning of every timer period and then switching low when a predetermined value is reached. Timer B is used for PWM generation and operates at 8MHz. To switch at 100kHz, Timer B counts to 80 and resets. This constrains the number of possible duty cycles to integer fractions of 80.
Therefore we should divide the 4096 values of the ADC into 80 increments. We can do this by adding a constant. A #define is a preprocessor constant whose value will be substituted everywhere its name appears before compilation. It does not need an assignment '=' or ';'.
The variable D0 is a global variable that is periodically read to update the signal PWM0 output. This excerpt of code also does two simple tests to limit the output duty cycle between 10% and 90%.
Compiling and downloading the code you could now control the duty cycle of PWM0 using the potentiometer.
Remember that all mathematical operations such as divide are integer operations. The result will be rounded.
In periph.c you can change the value of the register TBCCR0 under the TIMERB settings in order to set your desired switching frequency. The value of this register is simply the multiple of your desired frequency that will result in 8e6 (8 MHz) which is the speed of the on-board crystal. If you want to learn more about what these registers and their values mean, you are encouraged to consult the MSP430 Family User's Guide
For Experiment 5, you can configure Timer B to output signals for both the inverter modified sine wave and the buck converter gate drive. This requires reconfiguring Timer B to drive pins 4.2 through 4.6.
Using the DAC (Updated 2/22/08)
Using the DAC to display an analog value corresponding to the duty cycle requires the addition of two more lines. First we add another #define to scale the DAC output to run from 0-1V. We write to DAC channel 1 (DAC0 is also available if startSine() is not called) by writing directly to the variable DAC1 (an alias for the DAC data register DAC12_1DAT). The final line in the while loop toggles the LED. By measuring the frequency of P6.0 you can see directly how fast the control loop is operating.
The final code should look like:
IAR provides a few useful debugging tools you can use in conjunction with breakpoints, notably the ability to monitor variable values. To access this, open the Watch window (after downloading your code to the board) by clicking View->Watch. To add a variable to the watch, left-click in the column labelled Expression and type the variable name. Arrays will be displayed with a + symbol that you may click and that expands to show the value of each element within the array. Watch values are updated whenever program execution is halted by an event such as a breakpoint.
Try this now with the Adc_Data array. Note that you should let the program run for a short time before inserting a breakpoint within the while loop so that four complete PWM periods can expire allowing the ADC to complete its sequence of conversions. If you don't the Adc_Data array will appear to be empty.
Screenshot of the final waveform output. 70.9% duty cycle corresponds to 0.7027V output on the DAC.
Screenshot using persist on the oscilloscope to show noise on the ADC lines without any filtering.
Same screenshot as above after the addition of a single 1nF capacitor.
Modifying the Framework (Updated 2/22/08):
While working on your own project you may want to modify some system settings. We have attempted to comment the code thoroughly enough that most changes require a minimum of research. Most of the important system variables you may want to modify are marked with // EDIT: comments describing what must be done. A brief list of what you might modify is given here:
- Switching frequency is set in timerInit() by the TBCCR0 variable.
- ADC sample rate and channel count is set in adcInit(). See the family datasheet for these settings.
- ADC sample data storage location can be changed to something other than Adc_Data should you need a sample history by editing the settings in dmaInit(). To create a circular buffer you will have to use a DMA interrupt service routine. dacDmaIsr() is provided