microbit-dal/source/MicroBitLightSensor.cpp

132 lines
4 KiB
C++
Raw Normal View History

/**
* Class definition for MicroBitLightSensor.
*
* This is an object that interleaves light sensing with uBit.display.
*/
#include "MicroBit.h"
/**
* After the startSensing method has been called, this method will be called
* MICROBIT_LIGHT_SENSOR_AN_SET_TIME after.
*
* It will then read from the currently selected channel using the AnalogIn
* that was configured in the startSensing method.
*/
void MicroBitLightSensor::analogReady()
{
this->results[chan] = this->sensePin->read_u16();
analogDisable();
DigitalOut((PinName)(MICROBIT_DISPLAY_COLUMN_START + chan)).write(1);
chan++;
chan = chan % MICROBIT_LIGHT_SENSOR_CHAN_NUM;
}
/**
* Forcibly disables the AnalogIn, otherwise it will remain in possession
* of the GPIO channel it is using, meaning that the display will not be
* able to use a channel (COL).
*
* This is required as per PAN 3, details of which can be found here:
*
* https://www.nordicsemi.com/eng/nordic/download_resource/24634/5/88440387
*/
void MicroBitLightSensor::analogDisable()
{
NRF_ADC->ENABLE = ADC_ENABLE_ENABLE_Disabled;
NRF_ADC->CONFIG = (ADC_CONFIG_RES_8bit << ADC_CONFIG_RES_Pos) |
(ADC_CONFIG_INPSEL_SupplyTwoThirdsPrescaling << ADC_CONFIG_INPSEL_Pos) |
(ADC_CONFIG_REFSEL_VBG << ADC_CONFIG_REFSEL_Pos) |
(ADC_CONFIG_PSEL_Disabled << ADC_CONFIG_PSEL_Pos) |
(ADC_CONFIG_EXTREFSEL_None << ADC_CONFIG_EXTREFSEL_Pos);
}
/**
* The method that is invoked by sending MICROBIT_DISPLAY_EVT_LIGHT_SENSE
* using the id MICROBIT_ID_DISPLAY.
*
* If you want to manually trigger this method, you should use the event bus.
*
* @note This is currently too churny, and allocates a lot of stuff on the stack
* however, this makes this chunk of code platform agnostic in mbed land.
*/
void MicroBitLightSensor::startSensing(MicroBitEvent)
{
for(int rowCount = 0; rowCount < MICROBIT_DISPLAY_ROW_COUNT; rowCount++)
DigitalOut((PinName)(MICROBIT_DISPLAY_ROW_START + rowCount)).write(0);
PinName currentPin = (PinName)(MICROBIT_DISPLAY_COLUMN_START + chan);
DigitalOut(currentPin).write(1);
DigitalIn(currentPin, PullNone).~DigitalIn();
if(this->sensePin != NULL)
delete this->sensePin;
this->sensePin = new AnalogIn(currentPin);
analogTrigger.attach_us(this, &MicroBitLightSensor::analogReady, MICROBIT_LIGHT_SENSOR_AN_SET_TIME);
}
/**
* Constructor.
* Create a representation of the light sensor
*/
MicroBitLightSensor::MicroBitLightSensor() : analogTrigger()
{
this->chan = 0;
uBit.MessageBus.listen(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_LIGHT_SENSE, this, &MicroBitLightSensor::startSensing, MESSAGE_BUS_LISTENER_IMMEDIATE);
this->sensePin = NULL;
}
/**
* This method returns a summed average of the three sections of the display.
*
* A section is defined as:
* ___________________
* | 1 | | 2 | | 3 |
* |___|___|___|___|___|
* | | | | | |
* |___|___|___|___|___|
* | 2 | | 3 | | 1 |
* |___|___|___|___|___|
* | | | | | |
* |___|___|___|___|___|
* | 3 | | 1 | | 2 |
* |___|___|___|___|___|
*
* Where each number represents a different section on the 5 x 5 matrix display.
*
* @note currently values are inverted to how one would think:
* - Lower is brighter
* - Higher is darker
* TODO: Normalise the returned values into an SI unit!
*/
int MicroBitLightSensor::read()
{
int sum = 0;
for(int i = 0; i < MICROBIT_LIGHT_SENSOR_CHAN_NUM; i++)
sum += results[i];
return sum/MICROBIT_LIGHT_SENSOR_CHAN_NUM;
}
/**
* The destructor restores the default Display Mode and tick speed, and also
* removes the listener from the MessageBus.
*/
MicroBitLightSensor::~MicroBitLightSensor()
{
uBit.MessageBus.ignore(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_EVT_LIGHT_SENSE, this, &MicroBitLightSensor::startSensing);
}