microbit: DynamicPwm fixes and additions

This commit introduces an initial draft fix
for a bug where it would take two "setPeriods" for the
PWM class to update to the expected value.

This commit also adds three new methods to a DynamicPwm instance:

	* write() -  which is a lightweight wrapper around PwmOut write,
	  so that the value can be trapped. The trapping of the value
	  is necessary for the bug fix.
	* getPeriod() - which returns the value of a static variable,
	  containing the current period shared across the pwm bus.
	* getValue() - which returns a scaled value in the range 0-1024
	  representing the current duty cycle.
This commit is contained in:
James Devine 2016-01-09 19:45:41 +00:00
parent 500f851991
commit 32c40cdc29
2 changed files with 186 additions and 21 deletions

View file

@ -4,6 +4,7 @@
#define MICROBIT_DYNAMIC_PWM_H
#define NO_PWMS 3
#define MICROBIT_DEFAULT_PWM_PERIOD 20000
enum PwmPersistence
{
@ -14,27 +15,30 @@ enum PwmPersistence
/**
* Class definition for DynamicPwm.
*
* This class addresses a few issues found in the underlying libraries.
* This class addresses a few issues found in the underlying libraries.
* This provides the ability for a neat, clean swap between PWM channels.
*/
class DynamicPwm : public PwmOut
{
private:
private:
static DynamicPwm* pwms[NO_PWMS];
static uint8_t lastUsed;
static uint16_t sharedPeriod;
uint8_t flags;
float lastValue;
/**
* An internal constructor used when allocating a new DynamicPwm representation
* @param pin the name of the pin for the pwm to target
* @param persistance the level of persistence for this pin PWM_PERSISTENCE_PERSISTENT (can not be replaced until freed, should only be used for system services really.)
* @param persistance the level of persistence for this pin PWM_PERSISTENCE_PERSISTENT (can not be replaced until freed, should only be used for system services really.)
* or PWM_PERSISTENCE_TRANSIENT (can be replaced at any point if a channel is required.)
*/
DynamicPwm(PinName pin, PwmPersistence persistence = PWM_PERSISTENCE_TRANSIENT);
public:
public:
/**
* Redirects the pwm channel to point at a different pin.
* @param pin the new pin to direct PWM at.
@ -46,12 +50,12 @@ class DynamicPwm : public PwmOut
* @endcode
*/
void redirect(PinName pin);
/**
* Retrieves a pointer to the first available free pwm channel - or the first one that can be reallocated.
* @param pin the name of the pin for the pwm to target
* @param persistance the level of persistence for this pin PWM_PERSISTENCE_PERSISTENT (can not be replaced until freed, should only be used for system services really.)
* @param persistance the level of persistence for this pin PWM_PERSISTENCE_PERSISTENT (can not be replaced until freed, should only be used for system services really.)
* or PWM_PERSISTENCE_TRANSIENT (can be replaced at any point if a channel is required.)
* @param period the frequency of the pwm channel in us.
*
@ -61,7 +65,7 @@ class DynamicPwm : public PwmOut
* @endcode
*/
static DynamicPwm* allocate(PinName pin, PwmPersistence persistence = PWM_PERSISTENCE_TRANSIENT);
/**
* Frees this DynamicPwm instance if the pointer is valid.
*
@ -72,31 +76,95 @@ class DynamicPwm : public PwmOut
* @endcode
*/
void release();
/**
* A lightweight wrapper around the super class' write in order to capture the value
*
* @param value the duty cycle percentage in floating point format.
*
* @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER if value is out of range
*
* Example:
* @code
* DynamicPwm* pwm = DynamicPwm::allocate();
* pwm->write(0.5);
* @endcode
*/
int write(float value);
/**
* Retreives the pin name associated with this DynamicPwm instance.
*
* Example:
* @code
* DynamicPwm* pwm = DynamicPwm::allocate(PinName n);
* pwm->getPinName(); // equal to n
* pwm->getPinName();
* @endcode
*/
PinName getPinName();
/**
* Retreives the last value that has been written to this pwm channel.
*
* Example:
* @code
* DynamicPwm* pwm = DynamicPwm::allocate(PinName n);
* pwm->getPeriod();
* @endcode
*/
int getValue();
/**
* Retreives the current period in use by the entire PWM module in microseconds.
*
* Example:
* @code
* DynamicPwm* pwm = DynamicPwm::allocate(PinName n);
* pwm->getPeriod();
* @endcode
*/
int getPeriodUs();
/**
* Retreives the current period in use by the entire PWM module in milliseconds.
*
* Example:
* @code
* DynamicPwm* pwm = DynamicPwm::allocate(PinName n);
* pwm->getPeriod();
* @endcode
*/
int getPeriod();
/**
* Sets the period used by the WHOLE PWM module. Any changes to the period will AFFECT ALL CHANNELS.
*
* @param period the desired period in microseconds.
*
* @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER if value is out of range
*
* Example:
* @code
* DynamicPwm* pwm = DynamicPwm::allocate(PinName n);
* pwm->setPeriodUs(1000); // period now is 1ms
* @endcode
*
* @note The display uses the pwm module, if you change this value the display may flicker.
*/
void setPeriodUs(int period);
int setPeriodUs(int period);
/**
* Sets the period used by the WHOLE PWM module. Any changes to the period will AFFECT ALL CHANNELS.
*
* @param period the desired period in milliseconds.
*
* @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER if value is out of range
*
* Example:
* @code
* DynamicPwm* pwm = DynamicPwm::allocate(PinName n);
* pwm->setPeriod(1); // period now is 1ms
* @endcode
*/
int setPeriod(int period);
};
#endif

View file

@ -4,6 +4,8 @@ DynamicPwm* DynamicPwm::pwms[NO_PWMS] = { NULL };
uint8_t DynamicPwm::lastUsed = NO_PWMS+1; //set it to out of range i.e. 4 so we know it hasn't been used yet.
uint16_t DynamicPwm::sharedPeriod = 0; //set the shared period to an unknown state
/**
* Reassigns an already operational PWM channel to the given pin
* #HACK #BODGE # YUCK #MBED_SHOULD_DO_THIS
@ -146,6 +148,30 @@ void DynamicPwm::release()
}
}
/**
* A lightweight wrapper around the super class' write in order to capture the value
*
* @param value the duty cycle percentage in floating point format.
*
* @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER if value is out of range
*
* Example:
* @code
* DynamicPwm* pwm = DynamicPwm::allocate();
* pwm->write(0.5);
* @endcode
*/
int DynamicPwm::write(float value){
if(value < 0)
return MICROBIT_INVALID_PARAMETER;
PwmOut::write(value);
lastValue = value;
return MICROBIT_OK;
}
/**
* Retreives the pin name associated with this DynamicPwm instance.
*
@ -160,18 +186,89 @@ PinName DynamicPwm::getPinName()
return _pwm.pin;
}
/**
* Retreives the last value that has been written to this DynamicPwm instance.
* The value is in the range 0-1024
*
* Example:
* @code
* DynamicPwm* pwm = DynamicPwm::allocate(PinName n);
* pwm->getValue();
* @endcode
*/
int DynamicPwm::getValue()
{
return (float)lastValue * float(MICROBIT_PIN_MAX_OUTPUT);
}
/**
* Retreives the current period in use by the entire PWM module
*
* Example:
* @code
* DynamicPwm* pwm = DynamicPwm::allocate(PinName n);
* pwm->getPeriod();
* @endcode
*/
int DynamicPwm::getPeriodUs()
{
return sharedPeriod;
}
/**
* Retreives the current period in use by the entire PWM module
*
* Example:
* @code
* DynamicPwm* pwm = DynamicPwm::allocate(PinName n);
* pwm->getPeriod();
* @endcode
*/
int DynamicPwm::getPeriod()
{
return getPeriodUs() / 1000;
}
/**
* Sets the period used by the WHOLE PWM module. Any changes to the period will AFFECT ALL CHANNELS.
*
* @param period the desired period in microseconds.
*
* @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER if value is out of range
*
* Example:
* @code
* DynamicPwm* pwm = DynamicPwm::allocate(PinName n);
* pwm->setPeriodUs(1000); // period now is 1ms
* @endcode
*
* @note The display uses the pwm module, if you change this value the display may flicker.
*/
void DynamicPwm::setPeriodUs(int period)
int DynamicPwm::setPeriodUs(int period)
{
if(period < 0)
return MICROBIT_INVALID_PARAMETER;
//#HACK this forces mbed to update the pulse width calculation.
period_us(period);
write(lastValue);
sharedPeriod = period;
return MICROBIT_OK;
}
/**
* Sets the period used by the WHOLE PWM module. Any changes to the period will AFFECT ALL CHANNELS.
*
* @param period the desired period in microseconds.
*
* @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER if value is out of range
*
* Example:
* @code
* DynamicPwm* pwm = DynamicPwm::allocate(PinName n);
* pwm->setPeriod(1); // period now is 1ms
* @endcode
*/
int DynamicPwm::setPeriod(int period)
{
return setPeriodUs(period * 1000);
}