Tag Archives: Oscilloscope

C2000 Programming Model Guide Tutorial

C2000 Programming Model Guide


In this C2000 Programming Model Guide, the two basic approaches to programming on the C2000 will be over viewed using the C2000 Launchpad.  Some code examples will be shown, as well as a code execution speed test comparing the two methods.  All the code is written in Code Composer Studio (CCS) v5.5, with the latest version of ControlSuite released at the time of this article being posted.

TI provides all the documentation required for programming the C2000, which can be found here.

The Texas Instruments ControlSuite can be downloaded free of charge and includes support for two programming methods: Direct Register Access Model and Software Driver Model. There is also a third method, using Assembly language but that will not be covered here. Both of these models can be used independently or a combination of both can be used. Each model has advantages and disadvantages, which are summarised below.

Advantages Disadvantages
Direct Register Access Model Smaller code footprint, Faster code execution Statements are obscure, A detailed knowledge of each register is required
Software Driver Model Larger code footprint, Generally slower execution time Code is much more over viewable and understandable

Direct Register Access Model

The direct register access model writes values directly to the individual peripherals registers, if you have programmed the MSP430G Launchpad it uses a similar method.  All of the peripheral registers are defined in the corresponding header file contained in f2802x0_header/i/include, the image below shows this in CCS.

C2000 Programming Model Guide Direct Register Access f2802x_headers

The individual header files define the location of each register with respect to other registers within a peripheral, as well as the bit fields within each register.  This is all implemented using structures, the header files only define the structures they do not declare them.  A C source file is used to declare the structures with the physical memory of the device F2802x_GlobalVariablesDefs.c.  To use the direct register access model in an application, the file DSP28x_Project.h must be included in each source file were the register accesses are made.  An example of a direct register access statement can be found in the code snippet below, this particular call clears the ADC interrupt flag for ADC interrupt 1.

Software Driver Model

The software driver model uses an API provided by the peripheral driver library, which is used by applications to control the peripherals.  Before a peripheral can be used, the driver header file needs to be included and a handle to that peripheral initialised.  The code snippet below shows an example of this, illustrating the header file for the PWM peripheral, then the initialisation and finally two API function examples.

Writing An Application Using Both Models

To write an application that includes both the direct register access model and the software driver model, TI recommends the following:

  • Link driverlib.lib into your application (see below image)
  • Include DSP28x_Project.h in files you wish to use the direct register access model
  • Add /controlsuite/device_support/f2802x0/version/ to your projects include path (see below image)
  • Include driver header files from f2802x0_common/include in any source file that makes calls to that driver

C2000 Programming Model Guide Software Driver Model f2802x_common

Simple Code Execution Time Test Using An Oscilloscope

A simple test was devised to illustrate the main differences between these two models, this test is also useful for testing control and timing applications.  It has been used in a previous tutorial to access code execution time, which can be found here.

The C2000 clock is set to 60MHz for this test.  The code uses a timer which generates an interrupt every 100uS, when this interrupt is generated GPIO pin 19 is set high, the interrupt is then cleared.  There is also a continuous while loop running in the main program function, this has a statement/function which sets the GPIO pin 19 low.  So the sequence of events is as follows:

Every 100uS the interrupt handler is called -> GPIO pin 19 is set high -> The interrupt flag is cleared -> The program returns to the while loop and GPIO pin 19 is taken low

There is also one more piece of additional code added, a variable called control.  This variable is quite important as in between the interrupt calls, the code inside the while loop will still be executed.  Therefore if the GPIO pin low statement/function is partly way through executing when the interrupt is called, it will then return to this point and cause multiple timing traces on the oscilloscope.  This is due to the fact that there is an indeterminate time when the interrupt is called and what code is being executed at that time.  So using the if statement inside the while loop which uses the control variable, reduces these unknowns and ensures the code is far more predictable.


The code snippet below shows the code used in the main function, this code also demonstrates the equivalent direct register access statement to the software driver function for the GPIO pin 19 operation.

The next four images show screen captures from the oscilloscope, for the direct register access model and the software driver model.  It should be noted there is still a small variation in the timing as multiple traces are visible, hence two images are shown for each model allowing the variation to be calculated.

Direct Register Access lowest Pulse Width

C2000 Programming Model Guide Tutorial Direct Registry Access lowest

Direct Register Access Highest Pulse Width

C2000 Programming Model Guide Tutorial Direct Registry Access highest

Software Driver Lowest Pulse Width

C2000 Programming Model Guide Tutorial TI API command lowest

Software Driver Highest Pulse Width

C2000 Programming Model Guide Tutorial TI API command highest

What these oscilloscope traces show is the direct register access model clearly executes at a faster rate, the lowest and highest execution times are listed below for each model.

Direct Register Access Model:

  • Lowest = 2.92uS which equates to 176 Clock Cycles
  • Highest = 3.4uS which equates to 204 Clock Cycles

Software Driver Model:

  • Lowest = 6.64uS which equates to 399 Clock Cycles
  • Highest = 7.08uS which equates to 425 Clock Cycles

How the models are used is dependant on the application:  (1) If code size and execution time is not an issue it’s perfectly ok to use the software driver model.  (2) If code size is not an issue but certain aspects where speed of execution and timing are critical, then using both methods could be applicable.  (3) If code size and execution time are both critical, then the direct register access model is probably the best choice (Assembly language would be even better).

I take great care when writing all the tutorials and articles, ensuring all the code is fully tested to avoid issues for my readers.  All this takes time and a great deal of work, so please support the site by using the Adfly links etc.  If you have found this useful or have any problems implementing, please feel free to leave a comment and I will do my best to help.

C2000 Solar MPPT tutorial

C2000 Solar MPPT tutorial Pt/3


In this third part of the C2000 Solar MPPT Tutorial, the software will be looked at in greater detail.  This will entail a look at the Perturb & Observe algorithm, ADC code and timing code to ensure everything operates in a controlled manner.

The software is based around a Perturb and Observe (P&O) algorithm, the P&O algorithm falls under the category of a hill climbing algorithm.  Hill climbing algorithms are named so due to the algorithm taking steps over sampled data to reach a desired value, in the case of the P&O this takes steps towards the MPP by increasing or decreasing the duty cycle.  There are other hill climbing algorithms such as dP/dV Feedback Control and Incremental Conductance, I intend to revisit these and write code at a later date, but for now will focus on the P&O.  Some further information on MPPT algorithms can also be found here.

Perturb & Observe Algorithm

The P&O algorithm is a relatively simple algorithm, as such it has a few drawbacks:

  • The algorithm can be confused and track in the wrong direction, this can occur under fast changing irradiance conditions, the severity of this confusion depends on the P&O setup i.e. step size and update frequency.
  • The algorithm oscillates around the set point showing characteristics of an on/off controller.  More on this can be found on a previous tutorial I wrote regarding PID control, which can be found here.

The flowchart below shows the P&O algorithm used for this project

C2000 Solar MPPT Tutorial Perturb and Observe Algorithm Flow Chart

As can be seen from the flowchart the algorithm is fairly easy to follow, turning this into C code is also relatively easy, the final C code P&O algorithm can be seen below inside the function Adj_PWM().

So lets now run through the code briefly starting with line 3, this basically assigns the value in the counter compare A register to variable PWM_Temp.  PWM_Temp could simply be assigned to a temporary global variable, but I chose to get it straight from the register in this case.  Lines 5 to 24 form the main body of the algorithm, looking back at the flowchart the first two steps “Sample” and “Calculate” are carried out elsewhere in the ADC section of the code.  Lines 5 to 14 are illustrated by the right hand branch of the flowchart and lines 15 to 24 are illustrated by the left hand branch of the flowchart.

You have some simple if and else statements that determine which direction the algorithm takes, which is dependant on the sampled ADC data.  The result of these steps will either increase or decrease the PWM duty cycle, this increase or decrease determines the step size and in this case that value is 2.

The next block of code from lines 26 to 31 are used to prevent the duty cycle from reaching too large, and too small a value.  This was used during tuning, but also serves to provide some boundaries for the PWM, for example the duty cycle for the half bridge MOSFET drivers cannot exceed 99%, or the boost function will not operate correctly.

Lines 32 and 33 are used to update the duty cycle to the counter compare A registers for PWM1 and PWM2, both are the same duty cycle but PWM2 is 180o out of phase with PWM1.  Line 35 then assign the latest calculated solar panel voltage IP_Volt to the variable Old_IP_Volt and line 36 assign the latest calculated solar panel power New_PW_In to the variable Old_PW_In, both these variables are then used when the Adj_PWM() function is called again.

In order to help visualise the two PWM signals, the below image shows an oscilloscope trace with PWM1 in yellow and PWM2 in blue, both are set to 50% duty cycle and PWM2 is out of phase by 180o with PWM1.

C2000 Solar MPPT Tutorial 50% Duty 180 Phase

ADC Code

The next piece of code to be looked at is the ADC, I am not going to show the set-up code for the ADC or the PWM that triggers the ADC SOC, but will just show the code relating to the sampling and calculation.  However I intend to write a tutorial on each peripheral inside the C2000 with code examples, when time allows.  The ADC sampling is triggered by the PWM on every first event, therefore the sampling rate is 15ksps.  The final ADC sampling code can be seen below inside the function Data_Update()

So starting with lines 3 to 6 these are the local variables used for the function, the two floats are used to store the ADC values and the two integers are used to determine how many samples in the for loop.  The float in line 4 is a float array with four arrays, now the same result could be achieved with four separate floats.  I have left it as a float array for now, but if four floats were used the code should be optimised, by using 64 samples the following division of 128 (lines 23 to 26) could be substituted with a right bit wise shift of 7.

Lines 8 to 21 consist of the for loop, this uses the integer i as a counter and numberOfsamples as the count value.  Inside the for loop shown on line 10 this statement will wait until the next PWM trigger is received, which then initiates the ADC SOC channel number 0, once ADC channel 0 is finished it initiates channel 1 and so on and so forth.  The samples are saved in each of the channel numbers registers, then using the += addition assignment operator are added to the sum_of_ADC_samples_Array[n].  So an accumulated value is built up of the total samples every time the for loop is executed.  In addition there are 8 ADC channels being sampled, channel 0 to 7, but only 4 samples are accumulated so ADC channel 0 and 4 are added to sum_of_ADC_samples_Array[0] and channel 1 and 5 are added to sum_of_ADC_samples_Array[1] and so on and so forth.  When the ADC channel sampling sequence has finished, the trigger flag for the SOC sequence is cleared (line 20) and the loop waits for the next trigger event from the PWM.  Once i reaches 64 the loop is exited, each of the of the sum_of_ADC_samples_Array[n] now have 128 accumulated sample values in.

Lines 23 to 26 divide the sum_of_ADC_samples_Array[n] by 128 and assign the value to ADC_An floats.  Lines 28 to 31 convert the new floats to real world voltages read on the GPIO. Lines 33 to 36 then use constant values calculated from the electronic component values in the circuitry, to convert the floats to actual voltages and currents sampled in the circuit.  Lines 38 to 40 simply convert the input voltage and current to an input power and the output voltage and current to an output power.


Timing Code

The timing code is quite critical as it determines the update frequency of the MPPT, it must also ensure the code does not overrun and cause unpredictable behaviour.  The internal timer module was used, Timer 0 was set-up to trigger an interrupt every 100mS or 10Hz. The interrupt code is shown below.

When the interrupt is called an integer called SysTick is set to one, then the interrupt flag is cleared allowing the interrupt request to be executed and exited quickly.

Inside the main function there is a continuous while loop, the following code is run inside this loop.

Every time the interrupt sets the integer called SysTick to one, it allows the functions Data_Update() and Adj_PWM() to be executed, once these functions have completed SysTick is set to zero.  There are some additional lines of code on line 5 and line 10, these are used for testing and allowing the code execution time to be displayed on an oscilloscope.  The code on line 5 switches GPIO pin 19 high, then the code on line 10 switches GPIO pin 19 back to low, so a square wave pulse is produced and the pulse width gives an indication of the code execution time of the functions Data_Update() and Adj_PWM(). The following images show captures from an oscilloscope.

C2000 solar MPPT PWM and MPPT update frequency

This first image shows the 15kHz PWM being displayed on channel 1, the individual wave pulses are not visible as the time base is set to display channel 2.  The blue trace shown on channel 2 can be seen to have a frequency of 10Hz, with a pulse width of 4.4mS, so the functions Data_Update() and Adj_PWM() take 4.4mS to execute.  Putting this into context there should be 128 ADC samples captured, taken from 64 triggers of the PWM signal, therefore 64*66.67uS (one 15kHz cycle) = 4.27mS.  A single ADC sample and conversion takes around 650nS, if we multiply that by the 8 samples, a conversion is being completed every 8*650nS = 5.2uS (it will be faster than this due to ADC pipelining effects).  It can be clearly seen that there is plenty of room for more oversampling if required, as the 5.2uS sample and conversion time easily fits inside the 66.67uS window of the PWM trigger.  There is also a small amount of code overhead being added artificially by toggling GPIO pin 19, which is not significant but something to be aware of.

C2000 solar MPPT PWM and MPPT update frequency zoomed

The second image has a smaller time base setting (100uS) effectively zooming in, which allows the individual pulses from the 15kHz PWM to be visible.  So going back to the step size of 2 shown in the Adj_PWM() function, this can be put into context when the maximum duty cycle value as a variable, for 100% duty cycle equals 1000.  Therefore with a PWM update frequency of 10Hz and a maximum step size of 2, this equates to a maximum duty cycle change of 2% per second.

I captured some video which shows 3 variables being graphed in Code Composer Studio, these variables are PV Power, PV Volts and the Duty Cycle.  The video also demonstrates the MPPT in action under simulated fast changing cloud conditions, as well as some natural cloud.

There will be one final part to this series of tutorials this will cover some of the set-up and testing, and will also include a link to the full C code for the project.

I take great care when writing all the tutorials and articles, ensuring all the code is fully tested to avoid issues for my readers.  All this takes time and a great deal of work, so please support the site by using the Adfly links etc.  If you have found this useful or have any problems implementing, please feel free to leave a comment and I will do my best to help.