Merge pull request #264 from lancaster-university/pwm-patch

Pwm patch + AnalogOut updates
This commit is contained in:
Joe Finney 2017-02-28 09:50:33 +00:00 committed by GitHub
commit 96549fb657
5 changed files with 61 additions and 232 deletions

View file

@ -29,15 +29,8 @@ DEALINGS IN THE SOFTWARE.
#ifndef MICROBIT_DYNAMIC_PWM_H
#define MICROBIT_DYNAMIC_PWM_H
#define NO_PWMS 3
#define MICROBIT_DEFAULT_PWM_PERIOD 20000
enum PwmPersistence
{
PWM_PERSISTENCE_TRANSIENT = 1,
PWM_PERSISTENCE_PERSISTENT = 2,
};
/**
* Class definition for DynamicPwm.
*
@ -47,13 +40,10 @@ enum PwmPersistence
class DynamicPwm : public PwmOut
{
private:
static DynamicPwm* pwms[NO_PWMS];
static uint8_t lastUsed;
static uint16_t sharedPeriod;
uint8_t flags;
static uint32_t period;
float lastValue;
public:
/**
* An internal constructor used when allocating a new DynamicPwm instance.
@ -63,40 +53,7 @@ class DynamicPwm : public PwmOut
* @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:
/**
* Redirects the pwm channel to point at a different pin.
*
* @param pin the desired pin to output a PWM wave.
*
* @code
* DynamicPwm* pwm = DynamicPwm::allocate(PinName n);
* pwm->redirect(p0); // pwm is now produced on p0
* @endcode
*/
void redirect(PinName pin);
/**
* Creates a new DynamicPwm instance, or reuses an existing instance that
* has a persistence level of PWM_PERSISTENCE_TRANSIENT.
*
* @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.)
* or PWM_PERSISTENCE_TRANSIENT (can be replaced at any point if a channel is required.)
*
* @return a pointer to the first available free pwm channel - or the first one that can be reallocated. If
* no channels are available, NULL is returned.
*
* @code
* DynamicPwm* pwm = DynamicPwm::allocate(PinName n);
* @endcode
*/
static DynamicPwm* allocate(PinName pin, PwmPersistence persistence = PWM_PERSISTENCE_TRANSIENT);
DynamicPwm(PinName pin);
/**
* Frees this DynamicPwm instance for reuse.
@ -123,7 +80,7 @@ class DynamicPwm : public PwmOut
int write(float value);
/**
* Retreives the PinName associated with this DynamicPwm instance.
* Retrieves the PinName associated with this DynamicPwm instance.
*
* @code
* DynamicPwm* pwm = DynamicPwm::allocate(PinName n);
@ -138,7 +95,7 @@ class DynamicPwm : public PwmOut
PinName getPinName();
/**
* Retreives the last value that has been written to this DynamicPwm instance.
* Retrieves the last value that has been written to this DynamicPwm instance.
* in the range 0 - 1023 inclusive.
*
* @code
@ -152,7 +109,7 @@ class DynamicPwm : public PwmOut
int getValue();
/**
* Retreives the current period in use by the entire PWM module in microseconds.
* Retrieves the current period in use by the entire PWM module in microseconds.
*
* Example:
* @code
@ -160,7 +117,7 @@ class DynamicPwm : public PwmOut
* pwm->getPeriod();
* @endcode
*/
int getPeriodUs();
uint32_t getPeriodUs();
/**
* Retreives the current period in use by the entire PWM module in milliseconds.
@ -174,7 +131,7 @@ class DynamicPwm : public PwmOut
* pwm->getPeriod();
* @endcode
*/
int getPeriod();
uint32_t getPeriod();
/**
* Sets the period used by the WHOLE PWM module.
@ -193,7 +150,7 @@ class DynamicPwm : public PwmOut
*
* @note Any changes to the period will AFFECT ALL CHANNELS.
*/
int setPeriodUs(int period);
int setPeriodUs(uint32_t period);
/**
* Sets the period used by the WHOLE PWM module. Any changes to the period will AFFECT ALL CHANNELS.
@ -210,7 +167,9 @@ class DynamicPwm : public PwmOut
* pwm->setPeriod(20);
* @endcode
*/
int setPeriod(int period);
int setPeriod(uint32_t period);
~DynamicPwm();
};
#endif

View file

@ -80,9 +80,13 @@ DEALINGS IN THE SOFTWARE.
* Used to determine the capabilities of each Pin as some can only be digital, or can be both digital and analogue.
*/
enum PinCapability{
PIN_CAPABILITY_DIGITAL = 0x01,
PIN_CAPABILITY_ANALOG = 0x02,
PIN_CAPABILITY_AD = PIN_CAPABILITY_DIGITAL | PIN_CAPABILITY_ANALOG,
PIN_CAPABILITY_DIGITAL_IN = 0x01,
PIN_CAPABILITY_DIGITAL_OUT = 0x02,
PIN_CAPABILITY_DIGITAL = PIN_CAPABILITY_DIGITAL_IN | PIN_CAPABILITY_DIGITAL_OUT,
PIN_CAPABILITY_ANALOG_IN = 0x04,
PIN_CAPABILITY_ANALOG_OUT = 0x08,
PIN_CAPABILITY_ANALOG = PIN_CAPABILITY_ANALOG_IN | PIN_CAPABILITY_ANALOG_OUT,
PIN_CAPABILITY_STANDARD = PIN_CAPABILITY_DIGITAL | PIN_CAPABILITY_ANALOG_OUT,
PIN_CAPABILITY_ALL = PIN_CAPABILITY_DIGITAL | PIN_CAPABILITY_ANALOG
};

View file

@ -35,51 +35,7 @@ DEALINGS IN THE SOFTWARE.
#include "MicroBitPin.h"
#include "ErrorNo.h"
DynamicPwm* DynamicPwm::pwms[NO_PWMS] = { NULL, NULL, 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.
*
* @param pin The desired pin to begin a PWM wave.
*
* @param oldPin The pin to stop running a PWM wave.
*
* @param channel_number The GPIOTE channel being used to drive this PWM channel
*
* TODO: Merge into mbed, at a later date.
*/
void gpiote_reinit(PinName pin, PinName oldPin, uint8_t channel_number)
{
// Connect GPIO input buffers and configure PWM_OUTPUT_PIN_NUMBER as an output.
NRF_GPIO->PIN_CNF[pin] = (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)
| (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos)
| (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos)
| (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
| (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos);
NRF_GPIO->OUTCLR = (1 << oldPin);
NRF_GPIO->OUTCLR = (1 << pin);
/* Finally configure the channel as the caller expects. If OUTINIT works, the channel is configured properly.
If it does not, the channel output inheritance sets the proper level. */
NRF_GPIOTE->CONFIG[channel_number] = (GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos) |
((uint32_t)pin << GPIOTE_CONFIG_PSEL_Pos) |
((uint32_t)GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos) |
((uint32_t)GPIOTE_CONFIG_OUTINIT_Low << GPIOTE_CONFIG_OUTINIT_Pos); // ((uint32_t)GPIOTE_CONFIG_OUTINIT_High <<
// GPIOTE_CONFIG_OUTINIT_Pos);//
/* Three NOPs are required to make sure configuration is written before setting tasks or getting events */
__NOP();
__NOP();
__NOP();
NRF_TIMER2->CC[channel_number] = 0;
}
uint32_t DynamicPwm::period = MICROBIT_DEFAULT_PWM_PERIOD;
/**
* An internal constructor used when allocating a new DynamicPwm instance.
@ -89,82 +45,8 @@ void gpiote_reinit(PinName pin, PinName oldPin, uint8_t channel_number)
* @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::DynamicPwm(PinName pin, PwmPersistence persistence) : PwmOut(pin)
DynamicPwm::DynamicPwm(PinName pin) : PwmOut(pin)
{
this->flags = persistence;
}
/**
* Redirects the pwm channel to point at a different pin.
*
* @param pin the desired pin to output a PWM wave.
*
* @code
* DynamicPwm* pwm = DynamicPwm::allocate(PinName n);
* pwm->redirect(p0); // pwm is now produced on p0
* @endcode
*/
void DynamicPwm::redirect(PinName pin)
{
gpiote_reinit(pin, _pwm.pin, (uint8_t)_pwm.pwm);
this->_pwm.pin = pin;
}
/**
* Creates a new DynamicPwm instance, or reuses an existing instance that
* has a persistence level of PWM_PERSISTENCE_TRANSIENT.
*
* @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.)
* or PWM_PERSISTENCE_TRANSIENT (can be replaced at any point if a channel is required.)
*
* @return a pointer to the first available free pwm channel - or the first one that can be reallocated. If
* no channels are available, NULL is returned.
*
* @code
* DynamicPwm* pwm = DynamicPwm::allocate(PinName n);
* @endcode
*/
DynamicPwm* DynamicPwm::allocate(PinName pin, PwmPersistence persistence)
{
//try to find a blank spot first
for(int i = 0; i < NO_PWMS; i++)
{
if(pwms[i] == NULL)
{
lastUsed = i;
pwms[i] = new DynamicPwm(pin, persistence);
return pwms[i];
}
}
//no blank spot.. try to find a transient PWM
int channelIterator = (lastUsed + 1 > NO_PWMS - 1) ? 0 : lastUsed + 1;
while(channelIterator != lastUsed)
{
if(pwms[channelIterator]->flags & PWM_PERSISTENCE_TRANSIENT)
{
lastUsed = channelIterator;
pwms[channelIterator]->flags = persistence;
pwms[channelIterator]->redirect(pin);
return pwms[channelIterator];
}
channelIterator = (channelIterator + 1 > NO_PWMS - 1) ? 0 : channelIterator + 1;
}
//if we haven't found a free one, we must try to allocate the last used...
if(pwms[lastUsed]->flags & PWM_PERSISTENCE_TRANSIENT)
{
pwms[lastUsed]->flags = persistence;
pwms[lastUsed]->redirect(pin);
return pwms[lastUsed];
}
//well if we have no transient channels - we can't give any away! :( return null
return (DynamicPwm*)NULL;
}
/**
@ -175,20 +57,10 @@ DynamicPwm* DynamicPwm::allocate(PinName pin, PwmPersistence persistence)
* pwm->release();
* @endcode
*/
void DynamicPwm::release()
DynamicPwm::~DynamicPwm()
{
//free the pwm instance.
NRF_GPIOTE->CONFIG[(uint8_t) _pwm.pwm] = 0;
pwmout_free(&_pwm);
this->flags = PWM_PERSISTENCE_TRANSIENT;
//set the pointer to this object to null...
for(int i =0; i < NO_PWMS; i++)
if(pwms[i] == this)
{
delete pwms[i];
pwms[i] = NULL;
}
}
/**
@ -215,7 +87,7 @@ int DynamicPwm::write(float value){
}
/**
* Retreives the PinName associated with this DynamicPwm instance.
* Retrieves the PinName associated with this DynamicPwm instance.
*
* @code
* DynamicPwm* pwm = DynamicPwm::allocate(PinName n);
@ -233,7 +105,7 @@ PinName DynamicPwm::getPinName()
}
/**
* Retreives the last value that has been written to this DynamicPwm instance.
* Retrieves the last value that has been written to this DynamicPwm instance.
* in the range 0 - 1023 inclusive.
*
* @code
@ -250,7 +122,7 @@ int DynamicPwm::getValue()
}
/**
* Retreives the current period in use by the entire PWM module in microseconds.
* Retrieves the current period in use by the entire PWM module in microseconds.
*
* Example:
* @code
@ -258,13 +130,13 @@ int DynamicPwm::getValue()
* pwm->getPeriod();
* @endcode
*/
int DynamicPwm::getPeriodUs()
uint32_t DynamicPwm::getPeriodUs()
{
return sharedPeriod;
return this->period;
}
/**
* Retreives the current period in use by the entire PWM module in milliseconds.
* Retrieves the current period in use by the entire PWM module in milliseconds.
*
* Example:
* @code
@ -275,7 +147,7 @@ int DynamicPwm::getPeriodUs()
* pwm->getPeriod();
* @endcode
*/
int DynamicPwm::getPeriod()
uint32_t DynamicPwm::getPeriod()
{
return getPeriodUs() / 1000;
}
@ -297,15 +169,12 @@ int DynamicPwm::getPeriod()
*
* @note Any changes to the period will AFFECT ALL CHANNELS.
*/
int DynamicPwm::setPeriodUs(int period)
int DynamicPwm::setPeriodUs(uint32_t 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;
this->period = period;
return MICROBIT_OK;
}
@ -325,7 +194,7 @@ int DynamicPwm::setPeriodUs(int period)
* pwm->setPeriod(20);
* @endcode
*/
int DynamicPwm::setPeriod(int period)
int DynamicPwm::setPeriod(uint32_t period)
{
return setPeriodUs(period * 1000);
}

View file

@ -47,24 +47,24 @@ MicroBitIO::MicroBitIO(int ID_P0, int ID_P1, int ID_P2,
int ID_P12,int ID_P13,int ID_P14,
int ID_P15,int ID_P16,int ID_P19,
int ID_P20) :
P0 (ID_P0, MICROBIT_PIN_P0, PIN_CAPABILITY_ALL), //P0 is the left most pad (ANALOG/DIGITAL/TOUCH)
P1 (ID_P1, MICROBIT_PIN_P1, PIN_CAPABILITY_ALL), //P1 is the middle pad (ANALOG/DIGITAL/TOUCH)
P2 (ID_P2, MICROBIT_PIN_P2, PIN_CAPABILITY_ALL), //P2 is the right most pad (ANALOG/DIGITAL/TOUCH)
P3 (ID_P3, MICROBIT_PIN_P3, PIN_CAPABILITY_AD), //COL1 (ANALOG/DIGITAL)
P4 (ID_P4, MICROBIT_PIN_P4, PIN_CAPABILITY_AD), //COL2 (ANALOG/DIGITAL)
P5 (ID_P5, MICROBIT_PIN_P5, PIN_CAPABILITY_DIGITAL), //BTN_A
P6 (ID_P6, MICROBIT_PIN_P6, PIN_CAPABILITY_DIGITAL), //ROW2
P7 (ID_P7, MICROBIT_PIN_P7, PIN_CAPABILITY_DIGITAL), //ROW1
P8 (ID_P8, MICROBIT_PIN_P8, PIN_CAPABILITY_DIGITAL), //PIN 18
P9 (ID_P9, MICROBIT_PIN_P9, PIN_CAPABILITY_DIGITAL), //ROW3
P10(ID_P10,MICROBIT_PIN_P10,PIN_CAPABILITY_AD), //COL3 (ANALOG/DIGITAL)
P11(ID_P11,MICROBIT_PIN_P11,PIN_CAPABILITY_DIGITAL), //BTN_B
P12(ID_P12,MICROBIT_PIN_P12,PIN_CAPABILITY_DIGITAL), //PIN 20
P13(ID_P13,MICROBIT_PIN_P13,PIN_CAPABILITY_DIGITAL), //SCK
P14(ID_P14,MICROBIT_PIN_P14,PIN_CAPABILITY_DIGITAL), //MISO
P15(ID_P15,MICROBIT_PIN_P15,PIN_CAPABILITY_DIGITAL), //MOSI
P16(ID_P16,MICROBIT_PIN_P16,PIN_CAPABILITY_DIGITAL), //PIN 16
P19(ID_P19,MICROBIT_PIN_P19,PIN_CAPABILITY_DIGITAL), //SCL
P20(ID_P20,MICROBIT_PIN_P20,PIN_CAPABILITY_DIGITAL) //SDA
P0 (ID_P0, MICROBIT_PIN_P0, PIN_CAPABILITY_ALL), //P0 is the left most pad (ANALOG/DIGITAL/TOUCH)
P1 (ID_P1, MICROBIT_PIN_P1, PIN_CAPABILITY_ALL), //P1 is the middle pad (ANALOG/DIGITAL/TOUCH)
P2 (ID_P2, MICROBIT_PIN_P2, PIN_CAPABILITY_ALL), //P2 is the right most pad (ANALOG/DIGITAL/TOUCH)
P3 (ID_P3, MICROBIT_PIN_P3, PIN_CAPABILITY_ALL), //COL1 (ANALOG/DIGITAL)
P4 (ID_P4, MICROBIT_PIN_P4, PIN_CAPABILITY_ALL), //COL2 (ANALOG/DIGITAL)
P5 (ID_P5, MICROBIT_PIN_P5, PIN_CAPABILITY_STANDARD), //BTN_A
P6 (ID_P6, MICROBIT_PIN_P6, PIN_CAPABILITY_STANDARD), //ROW2
P7 (ID_P7, MICROBIT_PIN_P7, PIN_CAPABILITY_STANDARD), //ROW1
P8 (ID_P8, MICROBIT_PIN_P8, PIN_CAPABILITY_STANDARD), //PIN 18
P9 (ID_P9, MICROBIT_PIN_P9, PIN_CAPABILITY_STANDARD), //ROW3
P10(ID_P10,MICROBIT_PIN_P10,PIN_CAPABILITY_ALL), //COL3 (ANALOG/DIGITAL)
P11(ID_P11,MICROBIT_PIN_P11,PIN_CAPABILITY_STANDARD), //BTN_B
P12(ID_P12,MICROBIT_PIN_P12,PIN_CAPABILITY_STANDARD), //PIN 20
P13(ID_P13,MICROBIT_PIN_P13,PIN_CAPABILITY_STANDARD), //SCK
P14(ID_P14,MICROBIT_PIN_P14,PIN_CAPABILITY_STANDARD), //MISO
P15(ID_P15,MICROBIT_PIN_P15,PIN_CAPABILITY_STANDARD), //MOSI
P16(ID_P16,MICROBIT_PIN_P16,PIN_CAPABILITY_STANDARD), //PIN 16
P19(ID_P19,MICROBIT_PIN_P19,PIN_CAPABILITY_STANDARD), //SCL
P20(ID_P20,MICROBIT_PIN_P20,PIN_CAPABILITY_STANDARD) //SDA
{
}

View file

@ -87,10 +87,7 @@ void MicroBitPin::disconnect()
}
if (status & IO_STATUS_ANALOG_OUT)
{
if(((DynamicPwm *)pin)->getPinName() == name)
((DynamicPwm *)pin)->release();
}
delete ((DynamicPwm *)pin);
if (status & IO_STATUS_TOUCH_IN)
delete ((MicroBitButton *)pin);
@ -118,7 +115,7 @@ void MicroBitPin::disconnect()
int MicroBitPin::setDigitalValue(int value)
{
// Check if this pin has a digital mode...
if(!(PIN_CAPABILITY_DIGITAL & capability))
if(!(PIN_CAPABILITY_DIGITAL_OUT & capability))
return MICROBIT_NOT_SUPPORTED;
// Ensure we have a valid value.
@ -153,7 +150,7 @@ int MicroBitPin::setDigitalValue(int value)
int MicroBitPin::getDigitalValue()
{
//check if this pin has a digital mode...
if(!(PIN_CAPABILITY_DIGITAL & capability))
if(!(PIN_CAPABILITY_DIGITAL_IN & capability))
return MICROBIT_NOT_SUPPORTED;
// Move into a Digital input state if necessary.
@ -194,7 +191,7 @@ int MicroBitPin::obtainAnalogChannel()
// Move into an analogue input state if necessary, if we are no longer the focus of a DynamicPWM instance, allocate ourselves again!
if (!(status & IO_STATUS_ANALOG_OUT) || !(((DynamicPwm *)pin)->getPinName() == name)){
disconnect();
pin = (void *)DynamicPwm::allocate(name);
pin = (void *)new DynamicPwm(name);
status |= IO_STATUS_ANALOG_OUT;
}
@ -212,7 +209,7 @@ int MicroBitPin::obtainAnalogChannel()
int MicroBitPin::setAnalogValue(int value)
{
//check if this pin has an analogue mode...
if(!(PIN_CAPABILITY_ANALOG & capability))
if(!(PIN_CAPABILITY_ANALOG_OUT & capability))
return MICROBIT_NOT_SUPPORTED;
//sanitise the level value
@ -248,7 +245,7 @@ int MicroBitPin::setAnalogValue(int value)
int MicroBitPin::setServoValue(int value, int range, int center)
{
//check if this pin has an analogue mode...
if(!(PIN_CAPABILITY_ANALOG & capability))
if(!(PIN_CAPABILITY_ANALOG_OUT & capability))
return MICROBIT_NOT_SUPPORTED;
//sanitise the servo level
@ -284,7 +281,7 @@ int MicroBitPin::setServoValue(int value, int range, int center)
int MicroBitPin::getAnalogValue()
{
//check if this pin has an analogue mode...
if(!(PIN_CAPABILITY_ANALOG & capability))
if(!(PIN_CAPABILITY_ANALOG_IN & capability))
return MICROBIT_NOT_SUPPORTED;
// Move into an analogue input state if necessary.
@ -362,7 +359,7 @@ int MicroBitPin::isAnalog()
int MicroBitPin::isTouched()
{
//check if this pin has a touch mode...
if(!(PIN_CAPABILITY_DIGITAL & capability))
if(!(PIN_CAPABILITY_DIGITAL_IN & capability))
return MICROBIT_NOT_SUPPORTED;
// Move into a touch input state if necessary.
@ -387,7 +384,7 @@ int MicroBitPin::isTouched()
int MicroBitPin::setServoPulseUs(int pulseWidth)
{
//check if this pin has an analogue mode...
if(!(PIN_CAPABILITY_ANALOG & capability))
if(!(PIN_CAPABILITY_ANALOG_OUT & capability))
return MICROBIT_NOT_SUPPORTED;
//sanitise the pulse width