5 years, 6 months ago.

generating square wave using digital pins

Hi all, I'm trying to get 4 dynamic outputs from the digital pins in k64f, something like PWM wave(square wave). but when I try to use PWM function I don't get the desired output exactly as I define period and pulse width, for instance when I set period as 4 seconds and pulse width as 2 seconds I expect to see the LED is on for 2 seconds and again off for 2 seconds but instead what I observe is LED blinking so fast and not in 2 second pulse width. I have been told I should design my own timer to achieve my purpose and using PWM is not feasible for me, but I'm not quite sure how to that. I would really appreciate if anyone could help

1 Answer

5 years, 6 months ago.

Since you want to run several pins at once the best way is to define a class that will drive one pin at the required rate using timers. This class inherits digitalOut so you can do everything with it that you could do with a normal digital out but this adds extra functions to it.

Once this is defined you can then create as many copies of that class as needed.

First the header file for the class, this defines what it can do but not how it does it.

slowPWM,h

#ifndef __slowPWM.h__
#define __slowPWM.h__ 

#include "mbed.h"
class slowPWM : public DigitalOut
{
public:

// constructor without setting anything going.
    slowPWM( const PinName pin);

// constructor that also starts things running
    slowPWM( float period, float highTime, const PinName pin );

// set the period
    void setPeriod(float period);

// set the on time per cycle
    void setHighTime(float highTime);

// stop things. If output is high it will still turn low at the correct time
    void stop();

// start things
    void start();

private:
// internal functions and variables not visible to the outside world

    void onTurnOff(void);
    void onCycleStart(void);

    Ticker cycleTimer;
    Timeout offTimer;

    float _timeOn;
    float _repeatTime;
};
#endif

You then create a .cpp file that tells the compiler how to actually do the things in the header.

slowPWM.cpp

#include "slowPWM.h"

slowPWM::slowPWM(const PinName pin ):DigitalOut(pin)
{
}

slowPWM::slowPWM( float period, float highTime, const PinName pin ):DigitalOut(pin)
{
    setPeriod(period);
    setHighTime(highTime);
    start();
}

void slowPWM::setPeriod(float period)
{
    _repeatTime = period;
    if (_repeatTime <= 0)   // check it's not 0 or negative
        _repeatTime = 1;
    setHighTime(_timeOn);  // perform sanity check on high time
}

void slowPWM::setHighTime(float highTime)
{
    _timeOn = highTime;
    if (_timeOn >= _repeatTime)  // check it's not more than the cycle time.
        _timeOn = _repeatTime/2; // set to 50% if invalid 
}

void slowPWM::stop()
{
    cycleTimer.detach();
}

// start things.
void slowPWM::start()
{
    cycleTimer.attach(callback(this,&slowPWM::onCycleStart),_repeatTime);
    onCycleStart();
}

void slowPWM::onTurnOff(void)
{
    write(0);
}

void slowPWM::onCycleStart(void)
{
    offTimer.attach(callback(this,&slowPWM::onTurnOff),_timeOn);
    write(1);
}

Finally you use your new class in your main application.

main.cpp

#include "mbed.h"

// include the header
#include "slowPWM.h"

// create the instances of the class
slowPWM MyTimer1(LED1);
slowPWM MyTimer2(LED2);
slowPWM MyTimer3(LED3);

main()
{
// set the times and get things running
    MyTimer1.setPeriod(4);
    MyTimer1.setHighTime(2);
    MyTimer1.start();

    MyTimer2.setPeriod(2);
    MyTimer2.setHighTime(1.5);
    MyTimer2.start();

    MyTimer3.setPeriod(3.8);
    MyTimer3.setHighTime(1.5);
    MyTimer3.start();

// now sit back and watch the flashing lights....
    while(true) {
        wait(1);
    }
}

Accepted Answer

I've pushed this out as a library to make life easier. It's not had any testing but it's simple enough that the risk of bugs is fairly low. https://os.mbed.com/users/AndyA/code/SlowPWM/

posted by Andy A 18 Jun 2019

thank you, Andy, for your answer. I tried to use the library you introduced but I get <Error: Cannot open source input file "slowPWM.h": No such file or directory in "SlowPWM/SlowPWM.cpp", Line: 1, Col: 22> error. I searched online for some methods to fix it but didn't work, do you know how can I fix this error? thank you

posted by si nj 18 Jun 2019

Sorry, the error was that I was stupid. In order to be more consistent with mbed standards I changed it from being called slowPWM in the code above to being called SlowPWM. But then I didn't change it everywhere and didn't check it after the change.

If you do an update it should be fixed now.

posted by Andy A 18 Jun 2019

thank you so much, Andy, it was a big help.

posted by si nj 18 Jun 2019

Beautiful code Andy My only nit is that it seems that the constructor

// constructor that also starts things running
    slowPWM( float period, float highTime, const PinName pin );

feels a little bit out of order since the pin name is last. Perhaps this order?

// constructor that also starts things running
    slowPWM( const PinName pin, float period, float highTime );

BB

posted by Bill Bellis 20 Jun 2019

just came up with the question, maybe you can help me, Andy. Is it possible to get a constant output from the slow PWM? ; cause in simple PWM function the constant output can be reached from setting the pulse width as zero or putting duty cycle equal to period. But in slow PWM this wouldn't work since it uses the timer. Can you think of a way to reach the constant output from slow PWM?

posted by si nj 21 Jun 2019

@Bill Bellis Good point. I've changed that and also made the other parameters const since they are (no real impact on user code).

@si nj Yes. Since it inherits DigitalOut you could just do MyTimer1.stop(); MyTimer1 = 1; to set the pin permanently high.

I've also just added special case handling to the setDutyCycle and setHighTime functions so that setting either of them to 0 or setting a duty cycle of 1 or a high time >= the period stops the timers and sets the output pin. Setting it to anything else starts the timers again.

posted by Andy A 21 Jun 2019