element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • About Us
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
Safe and Sound
  • Challenges & Projects
  • Design Challenges
  • Safe and Sound
  • More
  • Cancel
Safe and Sound
Blog MSP432 and TI-RTOS: PID Library Part 2 - Real World Example
  • Blog
  • Forum
  • Documents
  • Polls
  • Files
  • Events
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: Jan Cumps
  • Date Created: 25 Jan 2017 7:09 PM Date Created
  • Views 2251 views
  • Likes 8 likes
  • Comments 8 comments
  • message
  • control_theory
  • semaphore
  • msp432
  • synchronisation
  • pid
  • safe&sound
  • ti-rtos
  • launchpad
  • ti_rt
  • communication
Related
Recommended

MSP432 and TI-RTOS: PID Library Part 2 - Real World Example

Jan Cumps
Jan Cumps
25 Jan 2017

A real hardware test of the port of the very good Arduino PID library to  MSP432 LaunchPad and TI-RTOS.

 

image

PID is a control algorithm. It tries to keep the output of a device at a desired level by controlling its input.

Part 1 describes the port from Arduino C++ to C.

This part, we generate a DC voltage with the LaunchPad, sample that,  and keep its level under control with the PID library.

 

I'll also introduce a few advanced RTOS principles:

  • task synchronisation
  • inter-task communication

 

What you need:

  • MSP432 LaunchPad
  • 1 micro-USB cable
  • Code Composer Studio
  • TI-RTOS for MSP43X
  • three 10K resistors and a  4µ7 capacitor (or whatever spare parts you happen to find on your desk)
  • a solder iron or a breadboard with a few wires
  • a voltmeter or oscilloscope (both optional)

 

What are we Trying?

It's Control Theort time.

We're making a device that's able to manage to keep a voltage at a certain level.

We'll have three parts:

  • PID engine from the previous blog manages the voltage. It uses two other components to do that.
  • PWM module generates a block signal. An RC filter converts that to DC. Our PID engine can modify the duty cycle (and as a result the DC level).
  • ADC module samples the DC and hands that data over to PID

The PID will constantly play with the PWM module until the DC matches the requested level.

 

RC Filter

image

Our MSP432 doesn't have a DAC module. We can't generate an analog value straight away.

It does have a PWM module however. That generates a block wave. We can control the frequency and the duty cycle.

The higher the duty cycle, the more energy our PWM signal holds.

We can use an RC filter to turn that energy into a DC signal. The DC signal's voltage is proportional to the duty cycle of your PWM signal (not to its frequency).

 

image

On the scope trace here you can see the yellow PWM signal that's generated by the MSP432. That goes into our RC filter.

The duty cycle of that signal is just a little above 86%.

The blue trace is the DC output of that filter. It's almost 2V.

If the duty cycle of the PWM is set below 86%, the DC signal will decrease. It's at max when the duty cycle is 100%, 0V when the duty cycle is 0%.

 

 

 

image

 

The RC filter is 3 components. You have a 5K resistor and a 4µ7 capacitor that filter the PWM into DC.

The 10K resistor in parallel with the capacitor keeps the voltage to the output below the ADC reference (together with the 5K resistor it acts as a 2/3 voltage divider). It's also a bleeder resistor that discharges the capacitor when you disconnect the filter from its input for some reason.

You can make the 5K from two parallel 10K resistors. That keeps the BOM down to a single very common value.

These values aren't critical at all. Anything that's in the ballpark of what I'm showing here will work.

You can also change the PWM frequency if you have different components lying around.

 

Connect the input of the RC filter to the output of your LaunchPad's PWM signal. We will use P2.6.

Wire the ground of the filter to one of the many ground pins on the LaunchPad.

The output needs to go to P5.5,

You can check in the PWM and ADC setup sections later in this blog how and why these pins are configured and used.

 

The TI-RTOS FirmWare - Synchronise with Mailboxes and Events

 

The CCS project is attached to this post. You can us it to check out some neat RTOS techniques: Messaging, Events and Synchronisation.

There are three tasks.

  • PID task manages everything. It's scheduled to run at a given interval by the TI-RTOS.
    It asks samples from the ADC module and tells the PWM task to change its duty cycle.
  • The PWM task sleeps until it gets a command from the PID task to change the duty cycle.
  • The ADC task sleeps until it gets a nodge from PID to give the sampled DC value.

 

Message and Event

We're using the Mailbox functionality to exchange the data between the two tasks. Mailbox has synchronisation built in.

You can let a task wait until it's either told to read a message or write one.

The PID task that's scheduled by the RTOS is the one that tells the other tasks to do their work.

 

 

The PWM task sleeps (idle, no cpu time consumed) and waits for a read event.

image

 

    while (1) {
        /* wait for mailbox to be posted by writer() */
        if (Mailbox_pend(mbPWM, &msg, BIOS_WAIT_FOREVER)) {
            PWM_setDuty(pwm1, msg.pwm);
        }
    }

 

When the PID task decides that the PWM duty cycle needs to change (because the output isn't at the right level), it sends a message.

 

             if (pidCompute()) {
                 pMsg.pwm = trunc(Output);
                 /* enqueue message */
                 Mailbox_post(mbPWM, &pMsg, 10);

 

The PWM task that was idle all the time wakes up (magic of RTOS - it sends a read event if you define that), reads the message and sets PWM to the what was requested in that message.

Then the PWM goes idle until it receives a new message.

 

The ADC task sleeps, unless there's a place on the message box (and there's only one place available) to write data.

image

 

    while (1) {
        /* Blocking mode conversion */
        ADC_convert(adc0, &adcValue0);
        msg.adc = adcValue0;
        /* enqueue message */
        /* wait for mailbox to be read by reader() */
        Mailbox_post(mbADC, &msg, BIOS_WAIT_FOREVER);
    }

 

The PID task will make a place available in that mailbox by reading the available message.

If there's no sample available it doesn't wait. We want the PID task to never block. It should run at a steady pace.

 

         if (! Mailbox_pend(mbADC, &aMsg, BIOS_NO_WAIT) == 0) { // no sample
             Input = aMsg.adc; // todo set to same base as output
            // send to PID library and change PWM when needed

            // ...

         }

 

A full cycle of the PID task is:

 

     while (1) {
         Task_sleep(((UInt)arg0));

         if (! Mailbox_pend(mbADC, &aMsg, BIOS_NO_WAIT) == 0) { // no sample
             Input = aMsg.adc; // todo set to same base as output
             // some calculations that you can check in the source code are removed
             if (pidCompute()) {
                 pMsg.pwm = trunc(Output);
                 /* enqueue message */
                 Mailbox_post(mbPWM, &pMsg, 10);
             }
         }
    }

 

The messages are a very simple structure that we define ourselves:

 

typedef struct MsgPWM {
    uint32_t pwm;
} MsgPWM;

typedef struct MsgADC {
    uint32_t adc;
} MsgADC;

 

Their size is important, because the Mailbox mechanism makes a byte per byte copy of them when sending and receiving.

You can see in the screenprints of the Mailbox setups above that I enter a char size of 4. That's because the structure of the message contains one 32 bit value. That's 4 chars.

 

 

The PWM Module Setup

 

By default, the MSP432 LaunchPad configures two PWM pins. They both go to one of the LEDs on the board.

We route it to pin P2.6. Check the PWM post of this series for the details (read it if you don't understand what the code snippet below is doing)

You change that in the file MSP_EXP432P401R.c.

 

const PWMTimerMSP432_HWAttrsV1 pwmTimerMSP432HWAttrs[MSP_EXP432P401R_PWMCOUNT] = {
    {
        .timerBaseAddr = TIMER_A1_BASE,
        .clockSource = TIMER_A_CLOCKSOURCE_SMCLK,
        .compareRegister = TIMER_A_CAPTURECOMPARE_REGISTER_1,
        .gpioPort = GPIO_PORT_P2,
        .gpioPinIndex = GPIO_PIN6,
        .pwmMode = GPIO_PRIMARY_MODULE_FUNCTION
    },

// ...

    /* Use Port Map on Port2 get Timer outputs on pins with LEDs (2.1, 2.2) */
    const uint8_t portMap [] = {
        PM_NONE, PM_NONE, PM_TA1CCR2A, PM_NONE,
        PM_NONE, PM_NONE,     PM_TA1CCR1A,     PM_NONE
    };

 

In our PWM task's initialisation section, we arm PWM:

 

Void fnTaskPWM(UArg arg0, UArg arg1)
{
    PWM_Handle pwm1;
    PWM_Params params;
    uint32_t   pwmCounts = PWM_PERIOD;


    MsgPWM msg;

    PWM_Params_init(&params);
    params.dutyUnits = PWM_DUTY_COUNTS;
    params.dutyValue = 0;
    params.periodUnits = PWM_PERIOD_COUNTS;
    params.periodValue = pwmCounts;
    pwm1 = PWM_open(Board_PWM0, &params);
    if (pwm1 == NULL) {
        System_abort("Board_PWM0 did not open");
    }
    PWM_start(pwm1);

    while (1) {
// the wile loop is described above, in the TI-RTOS section

 

This task isn't scheduled to run at regular times in RTOS. It doesn't use the Task_sleep() function like the PID task.

That's because the Message Box schedules it.

 

image

 

The ADC Module Setup

 

Here, we're doing the same as in the ADC blog of this series. Our ADC input is pin 5.5, with a reference level of 2.5V, 14-bit.

 

const ADCMSP432_HWAttrs adcMSP432HWAttrs[MSP_EXP432P401R_ADCCOUNT] = {
    {
        .channel = ADC_INPUT_A0,
        .gpioPort = GPIO_PORT_P5,
        .gpioPin = GPIO_PIN5,
        .gpioMode = GPIO_TERTIARY_MODULE_FUNCTION,
        .refVoltage = REF_A_VREF2_5V,
        .resolution = ADC_14BIT
    },
    // ...

 

The initialisation:

 

Void fnTaskADC(UArg arg0, UArg arg1)
{
    ADC_Handle   adc0;
    ADC_Params   params;
    uint16_t adcValue0;
    MsgADC msg;
    ADC_Params_init(&params);
    adc0 = ADC_open(Board_ADC0, &params);

    while (1) {
    // the wile loop is described above, in the TI-RTOS section

 

 

The RTOS configuration for the task is identical to that op the PWM job.

 

The PID Loop

 

Finally the PID initialisation:

Void fnTaskPID(UArg arg0, UArg arg1)
{
    MsgADC      aMsg;
    MsgPWM      pMsg;
    //Define Variables we'll be connecting to
    double Setpoint, Input, Output;
    //Specify the links and initial tuning parameters
    double Kp= 1; // one duty cycle count
    double Ki=4, Kd=0; // todo make these good values for our strategy
    Input = 0.0;
    //initialize the variables we're linked to
     Setpoint = PID_BASE;
     Setpoint *= 0.25; // relative to the ADC reference value (2V5)
     pidInit(&Input, &Output, &Setpoint, Kp, Ki, Kd, PID_DIRECT);
     pidSetOutputLimits(0, PID_BASE);
     pidSetSampleTime(arg0);
     //turn the PID on
     pidSetMode(PID_AUTOMATIC);

     while (1) {
    // the wile loop is described above, in the TI-RTOS section

 

 

image

 

You can hook up a voltmeter over the output of the RC filter and measure the output level.

If you want to check out what's happening with a scope, the interesting signals are the filters input (PWM signal) and output (DC with ripple)

 

Check out the project code. It's attached to this blog.

Here are some things you can change in the code and see what's happening:

 

  • define another set value by changing the multiplier. 0 = 0%, 1 = 100%

     Setpoint *= 0.25; // relative to the ADC reference value (2V5)

 

  • change the PID parameters

    double Kp= 1; // one duty cycle count
    double Ki=0, Kd=0; // todo make these good values for our strategy

 

  • change the control loop frequency. Argument 0 contains the sleep time in microseconds.

image

  • look at the order of events. Is the sample taken at the right time?

 

 

Enjoy!

 

 

TI-RTOS Series
MSP432 and TI-RTOS: Getting Started Pt. 1 - Set Up and 1st RTOS Task
MSP432 and TI-RTOS: Getting Started Pt. 2 - Add an ADC Sample Task
MSP432 and TI-RTOS: Getting Started Pt. 3 - USB with Minimal CPU Use
MSP432 and TI-RTOS: PWM
MSP432 and TI-RTOS: I2C Configuration for Sensors BoosterPack
MSP432 and TI-RTOS: another I2C example - talk to a DAC
MSP432 and TI-RTOS: Sharp LCD BoosterPack
MSP432 and TI-RTOS: PID Library Part 1 - Intro
MSP432 and TI-RTOS: PID Library Part 2 - Real World Example
Attachments:
MSP432_PID_TIRTOS.zip
  • Sign in to reply

Top Comments

  • jc2048
    jc2048 over 8 years ago +1
    "The 10K resistor in parallel with the capacitor is there to have a discharge path." I didn't understand that bit. Why do you need a discharge path (other than the 5K resistor)?
  • Jan Cumps
    Jan Cumps over 8 years ago in reply to jc2048 +1
    That's 'for personal use ' as a makeshift load and to get back to 0 when unplugging the PWM source cable. edit: also keeps the voltage under the ADC reference of 2V5
  • Jan Cumps
    Jan Cumps over 8 years ago in reply to jc2048 +1
    I've reworded that part in the blog, John. When re-reading I saw that my explanation was confusing.
  • DAB
    DAB over 8 years ago

    Great post Jan.

     

    Nice review of filter theory too.

     

    DAB

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • Jan Cumps
    Jan Cumps over 8 years ago in reply to jc2048

    Here we go.

     

     

    image

     

    image

    I kept the same resistors as in your design, capacitors are half of your value (100n instead of 180n) which moves the frequency out to 20kHz .

    Diagrams:

    image

     

    image

    (I used 2nd order CR filter Design tools )

     

    The PID manages to keep up - I've stretched it to the level that it had to supply 95% duty cycle.

    image

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • jc2048
    jc2048 over 8 years ago in reply to Jan Cumps

    184.

     

    18 with 4 zeroes for the value in pF.

     

    If you want to quickly try it, two 100n in parallel to replace each 180n would do. (Or use a single 100n and adjust the resistor values.)

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • Jan Cumps
    Jan Cumps over 8 years ago in reply to jc2048

    I'll try it out, John <digs into box with random passive components and realises that he forgot how to read the capacitor values on ceramics??/>.

    I deliberately made a simple filter to give the PID library something to do. I'll check how it behaves with smoother more nervous filtering.

    - I'll also have to solder things up. I Can't get rid of the coupled switching noise with a breadboard.

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • jc2048
    jc2048 over 8 years ago

    Would it be alright if I make an observation here? You'd do much better with a second-order filter (it only adds two more components).

     

    Here's your filter circuit

     

    image

     

    this is the frequency response

     

    image

     

    you've had to position the -3db point at 10Hz to get a response that's down by 60db at the PWM frequency in order to make the ripple reasonable - here's the ripple

     

    image

     

    but with the -3db point so low the transient response is poor

     

    image

     

    Here's the circuit of a second-order filter. I've chosen the values so that the -60dB point is roughly similiar, so we should get about the same ripple

     

    image

     

    here's the frequency response

     

    image

     

    the ripple is similar

     

    image

     

    but now look at the transient performance - it's about ten times better. Much more agile and responsive - though it does mean your loop tuning will be more challenging to control it.

     

    image

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
>
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube