292 строки
7.8 KiB
C++
292 строки
7.8 KiB
C++
#include "pxt.h"
|
|
|
|
enum class PulseValue {
|
|
//% block=high
|
|
High = DEVICE_PIN_EVT_PULSE_HI,
|
|
//% block=low
|
|
Low = DEVICE_PIN_EVT_PULSE_LO
|
|
};
|
|
|
|
enum class PinEvent {
|
|
//% block="pulse high"
|
|
PulseHigh = DEVICE_PIN_EVT_PULSE_HI,
|
|
//% block="pulse low"
|
|
PulseLow = DEVICE_PIN_EVT_PULSE_LO,
|
|
//% block="rise"
|
|
Rise = DEVICE_PIN_EVT_RISE,
|
|
//% block="fall"
|
|
Fall = DEVICE_PIN_EVT_FALL,
|
|
};
|
|
|
|
enum class PinPullMode {
|
|
//% block="down"
|
|
PullDown = 0,
|
|
//% block="up"
|
|
PullUp = 1,
|
|
//% block="none"
|
|
PullNone = 2
|
|
};
|
|
|
|
namespace DigitalInOutPinMethods {
|
|
/**
|
|
* Read a pin or connector as either 0 or 1
|
|
* @param name pin to read from
|
|
*/
|
|
//% help=pins/digital-read weight=61
|
|
//% blockId=device_get_digital_pin block="digital read|pin %name" blockGap=8
|
|
//% blockNamespace=pins
|
|
//% name.fieldEditor="gridpicker"
|
|
//% name.fieldOptions.width=220
|
|
//% name.fieldOptions.columns=4
|
|
bool digitalRead(DigitalInOutPin name) {
|
|
return PINOP(getDigitalValue()) != 0;
|
|
}
|
|
|
|
/**
|
|
* Set a pin or connector value to either 0 or 1.
|
|
* @param name pin to write to
|
|
* @param value value to set on the pin
|
|
*/
|
|
//% help=pins/digital-write weight=60
|
|
//% blockId=device_set_digital_pin block="digital write|pin %name|to %value=toggleHighLow"
|
|
//% blockNamespace=pins
|
|
//% name.fieldEditor="gridpicker"
|
|
//% name.fieldOptions.width=220
|
|
//% name.fieldOptions.columns=4
|
|
void digitalWrite(DigitalInOutPin name, bool value) {
|
|
PINOP(setDigitalValue(value));
|
|
}
|
|
|
|
/**
|
|
* Make this pin a digital input, and create events where the timestamp is the duration
|
|
* that this pin was either ``high`` or ``low``.
|
|
*/
|
|
//% help=pins/on-pulsed weight=16 blockGap=8
|
|
//% blockId=pins_on_pulsed block="on|pin %pin|pulsed %pulse"
|
|
//% blockNamespace=pins
|
|
//% pin.fieldEditor="gridpicker"
|
|
//% pin.fieldOptions.width=220
|
|
//% pin.fieldOptions.columns=4
|
|
//% deprecated=1 hidden=1
|
|
void onPulsed(DigitalInOutPin pin, PulseValue pulse, Action body) {
|
|
pin->eventOn(DEVICE_PIN_EVENT_ON_PULSE);
|
|
registerWithDal(pin->id, (int)pulse, body);
|
|
}
|
|
|
|
/**
|
|
* Register code to run when a pin event occurs.
|
|
*/
|
|
//% help=pins/on-event weight=20 blockGap=8
|
|
//% blockId=pinsonevent block="on|pin %pin|%event"
|
|
//% blockNamespace=pins
|
|
//% pin.fieldEditor="gridpicker"
|
|
//% pin.fieldOptions.width=220
|
|
//% pin.fieldOptions.columns=4
|
|
void onEvent(DigitalInOutPin pin, PinEvent event, Action body) {
|
|
switch(event) {
|
|
case PinEvent::PulseHigh:
|
|
case PinEvent::PulseLow:
|
|
pin->eventOn(DEVICE_PIN_EVENT_ON_PULSE);
|
|
registerWithDal(pin->id, (int)event, body);
|
|
break;
|
|
case PinEvent::Rise:
|
|
case PinEvent::Fall:
|
|
pin->eventOn(DEVICE_PIN_EVENT_ON_EDGE);
|
|
registerWithDal(pin->id, (int)event, body);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the duration of a pulse in microseconds
|
|
* @param name the pin which measures the pulse
|
|
* @param value the value of the pulse (default high)
|
|
* @param maximum duration in micro-seconds
|
|
*/
|
|
//% blockId="pins_pulse_in" block="pulse in (µs)|pin %name|pulsed %high||timeout %maxDuration (µs)"
|
|
//% weight=18 blockGap=8
|
|
//% help="pins/pulse-in"
|
|
//% blockNamespace=pins
|
|
//% pin.fieldEditor="gridpicker"
|
|
//% pin.fieldOptions.width=220
|
|
//% pin.fieldOptions.columns=4
|
|
int pulseIn(DigitalInOutPin pin, PulseValue value, int maxDuration = 2000000) {
|
|
int pulse = PulseValue::High == value ? 1 : 0;
|
|
uint64_t tick = system_timer_current_time_us();
|
|
uint64_t maxd = (uint64_t)maxDuration;
|
|
while (pin->getDigitalValue() != pulse) {
|
|
if (system_timer_current_time_us() - tick > maxd)
|
|
return 0;
|
|
}
|
|
|
|
uint64_t start = system_timer_current_time_us();
|
|
while (pin->getDigitalValue() == pulse) {
|
|
if (system_timer_current_time_us() - tick > maxd)
|
|
return 0;
|
|
}
|
|
uint64_t end = system_timer_current_time_us();
|
|
return end - start;
|
|
}
|
|
|
|
/**
|
|
* Set the pull direction of this pin.
|
|
* @param name pin to set the pull mode on
|
|
* @param pull one of the mbed pull configurations: PullUp, PullDown, PullNone
|
|
*/
|
|
//% help=pins/set-pull weight=17 blockGap=8
|
|
//% blockId=device_set_pull block="set pull|pin %pin|to %pull"
|
|
//% blockNamespace=pins
|
|
//% name.fieldEditor="gridpicker"
|
|
//% name.fieldOptions.width=220
|
|
//% name.fieldOptions.columns=4
|
|
void setPull(DigitalInOutPin name, PinPullMode pull) {
|
|
PullMode m = pull == PinPullMode::PullDown ? PullMode::Down : pull == PinPullMode::PullUp
|
|
? PullMode::Up
|
|
: PullMode::None;
|
|
PINOP(setPull(m));
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef PXT_CODAL
|
|
|
|
namespace pxt {
|
|
|
|
static void waitABit() {
|
|
// for (int i = 0; i < 10; ++i)
|
|
// asm volatile("nop");
|
|
}
|
|
|
|
class ButtonMultiplexer : public CodalComponent {
|
|
public:
|
|
Pin &latch;
|
|
Pin &clock;
|
|
Pin &data;
|
|
uint32_t state;
|
|
uint32_t invMask;
|
|
uint16_t buttonIdPerBit[8];
|
|
bool enabled;
|
|
|
|
ButtonMultiplexer(uint16_t id)
|
|
: latch(*LOOKUP_PIN(BTNMX_LATCH)), clock(*LOOKUP_PIN(BTNMX_CLOCK)),
|
|
data(*LOOKUP_PIN(BTNMX_DATA)) {
|
|
this->id = id;
|
|
this->status |= DEVICE_COMPONENT_STATUS_SYSTEM_TICK;
|
|
|
|
state = 0;
|
|
invMask = 0;
|
|
enabled = true;
|
|
|
|
memset(buttonIdPerBit, 0, sizeof(buttonIdPerBit));
|
|
|
|
data.getDigitalValue(PullMode::Down);
|
|
latch.setDigitalValue(1);
|
|
clock.setDigitalValue(1);
|
|
}
|
|
|
|
void disable() {
|
|
data.getDigitalValue(PullMode::None);
|
|
latch.getDigitalValue(PullMode::None);
|
|
clock.getDigitalValue(PullMode::None);
|
|
enabled = false;
|
|
}
|
|
|
|
bool isButtonPressed(int id) {
|
|
for (int i = 0; i < 8; ++i) {
|
|
if (buttonIdPerBit[i] == id)
|
|
return (state & (1 << i)) != 0;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
uint32_t readBits(int bits) {
|
|
latch.setDigitalValue(0);
|
|
waitABit();
|
|
latch.setDigitalValue(1);
|
|
waitABit();
|
|
|
|
uint32_t state = 0;
|
|
for (int i = 0; i < bits; i++) {
|
|
state <<= 1;
|
|
if (data.getDigitalValue(PullMode::Down))
|
|
state |= 1;
|
|
|
|
clock.setDigitalValue(0);
|
|
waitABit();
|
|
clock.setDigitalValue(1);
|
|
waitABit();
|
|
}
|
|
|
|
return state;
|
|
}
|
|
|
|
virtual void periodicCallback() override {
|
|
if (!enabled)
|
|
return;
|
|
|
|
uint32_t newState = readBits(8);
|
|
newState ^= invMask;
|
|
if (newState == state)
|
|
return;
|
|
|
|
for (int i = 0; i < 8; ++i) {
|
|
uint32_t mask = 1 << i;
|
|
if (!buttonIdPerBit[i])
|
|
continue;
|
|
int ev = 0;
|
|
if (!(state & mask) && (newState & mask))
|
|
ev = PXT_INTERNAL_KEY_DOWN;
|
|
else if ((state & mask) && !(newState & mask))
|
|
ev = PXT_INTERNAL_KEY_UP;
|
|
if (ev) {
|
|
Event(ev, buttonIdPerBit[i]);
|
|
Event(ev, 0); // any key
|
|
}
|
|
}
|
|
|
|
state = newState;
|
|
}
|
|
};
|
|
|
|
static ButtonMultiplexer *btnMultiplexer;
|
|
ButtonMultiplexer *getMultiplexer() {
|
|
if (!btnMultiplexer)
|
|
btnMultiplexer = new ButtonMultiplexer(DEVICE_ID_FIRST_BUTTON);
|
|
return btnMultiplexer;
|
|
}
|
|
|
|
int registerMultiplexedButton(int pin, int buttonId) {
|
|
if (1050 <= pin && pin < 1058) {
|
|
pin -= 50;
|
|
getMultiplexer()->invMask |= 1 << (pin - 1000);
|
|
}
|
|
if (1000 <= pin && pin < 1008) {
|
|
getMultiplexer()->buttonIdPerBit[pin - 1000] = buttonId;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int multiplexedButtonIsPressed(int btnId) {
|
|
if (btnMultiplexer)
|
|
return btnMultiplexer->isButtonPressed(btnId) ? 512 : 0;
|
|
return 0;
|
|
}
|
|
|
|
//% expose
|
|
uint32_t readButtonMultiplexer(int bits) {
|
|
if (!LOOKUP_PIN(BTNMX_CLOCK))
|
|
return 0;
|
|
return getMultiplexer()->readBits(bits);
|
|
}
|
|
|
|
void disableButtonMultiplexer() {
|
|
if (LOOKUP_PIN(BTNMX_CLOCK)) {
|
|
getMultiplexer()->disable();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|