microbit: added Servo API to MicroBitPin

* Added a number of #defines to MicroBitPin.h which ultimately lead to retabulation of the #defines

* Added two new instance methods:

	- setServoValue() which sets the default servo period (20ms) and accepts a value in the range
			  0 - 180, which maps to a duty cycle between 5% and 10% respectively. This
			  mapping is also configurable.

	- setServoPulseUs() which accepts a pulse width in microseconds, and maps it onto the PWM
			    channel.
This commit is contained in:
James Devine 2016-01-12 20:30:53 +00:00
parent 2650aaa569
commit 983e9e1ad3
2 changed files with 147 additions and 38 deletions

View file

@ -3,36 +3,40 @@
#include "mbed.h"
#include "MicroBitComponent.h"
// Status Field flags...
#define IO_STATUS_DIGITAL_IN 0x01 // Pin is configured as a digital input, with no pull up.
#define IO_STATUS_DIGITAL_OUT 0x02 // Pin is configured as a digital output
#define IO_STATUS_ANALOG_IN 0x04 // Pin is Analog in
#define IO_STATUS_ANALOG_OUT 0x08 // Pin is Analog out
#define IO_STATUS_TOUCH_IN 0x10 // Pin is a makey-makey style touch sensor
#define IO_STATUS_EVENTBUS_ENABLED 0x80 // Pin is will generate events on change
// Status Field flags...
#define IO_STATUS_DIGITAL_IN 0x01 // Pin is configured as a digital input, with no pull up.
#define IO_STATUS_DIGITAL_OUT 0x02 // Pin is configured as a digital output
#define IO_STATUS_ANALOG_IN 0x04 // Pin is Analog in
#define IO_STATUS_ANALOG_OUT 0x08 // Pin is Analog out
#define IO_STATUS_TOUCH_IN 0x10 // Pin is a makey-makey style touch sensor
#define IO_STATUS_EVENTBUS_ENABLED 0x80 // Pin is will generate events on change
//#defines for each edge connector pin
#define MICROBIT_PIN_P0 P0_3 //P0 is the left most pad (ANALOG/DIGITAL) used to be P0_3 on green board
#define MICROBIT_PIN_P1 P0_2 //P1 is the middle pad (ANALOG/DIGITAL)
#define MICROBIT_PIN_P2 P0_1 //P2 is the right most pad (ANALOG/DIGITAL) used to be P0_1 on green board
#define MICROBIT_PIN_P3 P0_4 //COL1 (ANALOG/DIGITAL)
#define MICROBIT_PIN_P4 P0_17 //BTN_A
#define MICROBIT_PIN_P5 P0_5 //COL2 (ANALOG/DIGITAL)
#define MICROBIT_PIN_P6 P0_12 //COL9
#define MICROBIT_PIN_P7 P0_11 //COL8
#define MICROBIT_PIN_P8 P0_18 //PIN 18
#define MICROBIT_PIN_P9 P0_10 //COL7
#define MICROBIT_PIN_P10 P0_6 //COL3 (ANALOG/DIGITAL)
#define MICROBIT_PIN_P11 P0_26 //BTN_B
#define MICROBIT_PIN_P12 P0_20 //PIN 20
#define MICROBIT_PIN_P13 P0_23 //SCK
#define MICROBIT_PIN_P14 P0_22 //MISO
#define MICROBIT_PIN_P15 P0_21 //MOSI
#define MICROBIT_PIN_P16 P0_16 //PIN 16
#define MICROBIT_PIN_P19 P0_0 //SCL
#define MICROBIT_PIN_P20 P0_30 //SDA
#define MICROBIT_PIN_P0 P0_3 //P0 is the left most pad (ANALOG/DIGITAL) used to be P0_3 on green board
#define MICROBIT_PIN_P1 P0_2 //P1 is the middle pad (ANALOG/DIGITAL)
#define MICROBIT_PIN_P2 P0_1 //P2 is the right most pad (ANALOG/DIGITAL) used to be P0_1 on green board
#define MICROBIT_PIN_P3 P0_4 //COL1 (ANALOG/DIGITAL)
#define MICROBIT_PIN_P4 P0_17 //BTN_A
#define MICROBIT_PIN_P5 P0_5 //COL2 (ANALOG/DIGITAL)
#define MICROBIT_PIN_P6 P0_12 //COL9
#define MICROBIT_PIN_P7 P0_11 //COL8
#define MICROBIT_PIN_P8 P0_18 //PIN 18
#define MICROBIT_PIN_P9 P0_10 //COL7
#define MICROBIT_PIN_P10 P0_6 //COL3 (ANALOG/DIGITAL)
#define MICROBIT_PIN_P11 P0_26 //BTN_B
#define MICROBIT_PIN_P12 P0_20 //PIN 20
#define MICROBIT_PIN_P13 P0_23 //SCK
#define MICROBIT_PIN_P14 P0_22 //MISO
#define MICROBIT_PIN_P15 P0_21 //MOSI
#define MICROBIT_PIN_P16 P0_16 //PIN 16
#define MICROBIT_PIN_P19 P0_0 //SCL
#define MICROBIT_PIN_P20 P0_30 //SDA
#define MICROBIT_PIN_MAX_OUTPUT 1023
#define MICROBIT_PIN_MAX_OUTPUT 1023
#define MICROBIT_PIN_MAX_SERVO_RANGE 180
#define MICROBIT_PIN_DEFAULT_SERVO_RANGE 1000
#define MICROBIT_PIN_DEFAULT_SERVO_CENTER MICROBIT_PIN_DEFAULT_SERVO_RANGE + MICROBIT_PIN_DEFAULT_SERVO_RANGE/2
/**
@ -69,6 +73,12 @@ class MicroBitPin : public MicroBitComponent
*/
void disconnect();
/**
* Performs a check to ensure that the current Pin is in control of a
* DynamicPwm instance, and if it's not, allocates a new DynamicPwm instance.
*/
int obtainAnalogChannel();
public:
PinName name; // mBed pin name of this pin.
@ -120,6 +130,21 @@ class MicroBitPin : public MicroBitComponent
*/
int setAnalogValue(int value);
/**
* Configures this IO pin as an analog/pwm output if it isn't already, configures the period to be 20ms,
* and sets the duty cycle between 0.05 and 0.1 (i.e. 5% or 10%) based on the value given to this method.
*
* A value of 180 sets the duty cycle to be 10%, and a value of 0 sets the duty cycle to be 5% by default.
*
* This range can be modified to fine tune, and also tolerate different servos.
*
* @param value the level to set on the output pin, in the range 0 - 180
* @param range which gives the span of possible values the i.e. lower and upper bounds center ± range/2 (Defaults to: MICROBIT_PIN_DEFAULT_SERVO_RANGE)
* @param center the center point from which to calculate the lower and upper bounds (Defaults to: MICROBIT_PIN_DEFAULT_SERVO_CENTER)
* @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER if value is out of range, or MICROBIT_NOT_SUPPORTED
* if the given pin does not have analog capability.
*/
int setServoValue(int value, int range = MICROBIT_PIN_DEFAULT_SERVO_RANGE, int center = MICROBIT_PIN_DEFAULT_SERVO_CENTER);
/**
* Configures this IO pin as an analogue input (if necessary and possible).
@ -172,6 +197,16 @@ class MicroBitPin : public MicroBitComponent
*/
int isTouched();
/**
* Configures this IO pin as an analog/pwm output if it isn't already, configures the period to be 20ms,
* and sets the pulse width, based on the value it is given
*
* @param pulseWidth the desired pulse width in microseconds.
* @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER if value is out of range, or MICROBIT_NOT_SUPPORTED
* if the given pin does not have analog capability.
*/
int setServoPulseUs(int pulseWidth);
/**
* Configures the PWM period of the analog output to the given value.
*

View file

@ -120,9 +120,21 @@ int MicroBitPin::getDigitalValue()
return ((DigitalIn *)pin)->read();
}
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);
status |= IO_STATUS_ANALOG_OUT;
}
return MICROBIT_OK;
}
/**
* Configures this IO pin as an analog/pwm output, and change the output value to the given level.
* @param value the level to set on the output pin, in the range 0 - 1024
* @param value the level to set on the output pin, in the range 0 - 1023
* @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER if value is out of range, or MICROBIT_NOT_SUPPORTED
* if the given pin does not have analog capability.
*/
@ -132,26 +144,57 @@ int MicroBitPin::setAnalogValue(int value)
if(!(PIN_CAPABILITY_ANALOG & capability))
return MICROBIT_NOT_SUPPORTED;
//sanitise the brightness level
//sanitise the level value
if(value < 0 || value > MICROBIT_PIN_MAX_OUTPUT)
return MICROBIT_INVALID_PARAMETER;
float level = (float)value / float(MICROBIT_PIN_MAX_OUTPUT);
// 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);
status |= IO_STATUS_ANALOG_OUT;
}
//perform a write with an extra check! :)
if(((DynamicPwm *)pin)->getPinName() == name)
//obtain use of the DynamicPwm instance, if it has changed / configure if we do not have one
if(obtainAnalogChannel() == MICROBIT_OK)
return ((DynamicPwm *)pin)->write(level);
return MICROBIT_OK;
}
/**
* Configures this IO pin as an analog/pwm output if it isn't already, configures the period to be 20ms,
* and sets the duty cycle between 0.05 and 0.1 (i.e. 5% or 10%) based on the value given to this method.
*
* A value of 180 sets the duty cycle to be 10%, and a value of 0 sets the duty cycle to be 5% by default.
*
* This range can be modified to fine tune, and also tolerate different servos.
*
* @param value the level to set on the output pin, in the range 0 - 180
* @param range which gives the span of possible values the i.e. lower and upper bounds center ± range/2 (Defaults to: MICROBIT_PIN_DEFAULT_SERVO_RANGE)
* @param center the center point from which to calculate the lower and upper bounds (Defaults to: MICROBIT_PIN_DEFAULT_SERVO_CENTER)
* @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER if value is out of range, or MICROBIT_NOT_SUPPORTED
* if the given pin does not have analog capability.
*/
int MicroBitPin::setServoValue(int value, int range, int center)
{
//check if this pin has an analogue mode...
if(!(PIN_CAPABILITY_ANALOG & capability))
return MICROBIT_NOT_SUPPORTED;
//sanitise the servo level
if(value < 0 || range < 1 || center < 1)
return MICROBIT_INVALID_PARAMETER;
//clip - just in case
if(value > MICROBIT_PIN_MAX_SERVO_RANGE)
value = MICROBIT_PIN_MAX_SERVO_RANGE;
//calculate the lower bound based on the midpoint
int lower = (center - (range / 2)) * 1000;
value = value * 1000;
//add the percentage of the range based on the value between 0 and 180
int scaled = lower + (range * (value / MICROBIT_PIN_MAX_SERVO_RANGE));
return setServoPulseUs(scaled / 1000);
}
/**
* Configures this IO pin as an analogue input (if necessary and possible).
@ -245,6 +288,37 @@ int MicroBitPin::isTouched()
return ((MicroBitButton *)pin)->isPressed();
}
/**
* Configures this IO pin as an analog/pwm output if it isn't already, configures the period to be 20ms,
* and sets the pulse width, based on the value it is given
*
* @param pulseWidth the desired pulse width in microseconds.
* @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER if value is out of range, or MICROBIT_NOT_SUPPORTED
* if the given pin does not have analog capability.
*/
int MicroBitPin::setServoPulseUs(int pulseWidth)
{
//check if this pin has an analogue mode...
if(!(PIN_CAPABILITY_ANALOG & capability))
return MICROBIT_NOT_SUPPORTED;
//sanitise the pulse width
if(pulseWidth < 0)
return MICROBIT_INVALID_PARAMETER;
//Check we still have the control over the DynamicPwm instance
if(obtainAnalogChannel() == MICROBIT_OK)
{
//check if the period is set to 20ms
if(((DynamicPwm *)pin)->getPeriodUs() != MICROBIT_DEFAULT_PWM_PERIOD)
((DynamicPwm *)pin)->setPeriodUs(MICROBIT_DEFAULT_PWM_PERIOD);
((DynamicPwm *)pin)->pulsewidth_us(pulseWidth);
}
return MICROBIT_OK;
}
/**
* Configures the PWM period of the analog output to the given value.
*