Skip to content

Commit

Permalink
Overhaul the pin handling (not backwards compatible)
Browse files Browse the repository at this point in the history
This makes a number of changes:
 - Split INPUT mode into INPUT_DIGITAL and INPUT_ANALOG. This allows
   using analog pins as digital pins as well. It requires explicitely
   choosing either of these modes, except when using pin.makeinput,
   which autoselects based on the pin type.

 - Rename and renumber the pin mode constants. This makes things a bit
   more consistent and by removing the negative values we allow using
   the upper bits of the mode as flags or other values.

 - Generalize the "pin mode" value into a "pin config" value, which
   contains the pin mode, combined with other values. Right now, this
   is only a flag for wether the pullup is enabled (which was previously
   part of the mode), but in the future flags like "reports enabled" or
   values like the analog reference to use can be included.

   This removes the pin.setmode command and replaces it with pin.config,
   which works similar. It accepts the following modes: disabled,
   disconnected, input_digital, input_analog, output_digital,
   output_pwm. Additionally, the value flag_pullup can be added to
   the input modes to enable pullups (defaulting to disabled). e.g.:

       pin.config("d8", output_digital)
       pin.config("a0", input_digital + flag_pullup)

 - When setting a pin to output_digital, it now gets written to LOW by
   default. Previously, the value would be unchanged, which meant that
   if the pullup was previously enabled, it would be HIGH, otherwise it
   would be LOW. When using pin.config, the value can be overridden
   (which is useful if you must go from input-with-pullup to output-high
   without going through output-low for some reason).

 - pin.makeinput no longer takes input or input_pullup as the second
   argument, but instead expects a 0 or 1 value to disable/enable
   pullups. Leaving it out makes the default depend on the pin type (see
   next item).

 - When setting an analog pin to input using pin.makeinput, it no longer
   defaults to having the pullup enabled. Since the pullup is connected
   to 3.3V, that's too high for the ADC, and if the voltage sensed comes
   from a voltage divider, having the pullup enabled will even influence
   the values read.

 - PinConfig and Mode values are now wrapped in structs, so you can do
   things like config.mode().input() to see if an input mode was
   selected. The constants for them are no also in their own scope, so
   you write things like PinConfig::Mode::DISCONNECTED, which is more
   clear than before. It seems like this would be a good usecase to use
   C++11 enum classes to add scoping and type safety, but you cannot add
   methods to enum classes. So this uses a struct with methods and an
   old-style enum, which fixes the scoping but is a bit less typesafe.

   This struct-wrapping does not add any storage overhead and minimal
   to no code size overhead.

This does not properly update the reports yet. They are somewhat
updated, but analog pins still only generate analog reports now and the
pin modes are replaced by the new pin configs directly, which have
different values. There should probably be a single mode report and a
single value report, instead of splitting through analog and digital,
but that's for a later commit.
  • Loading branch information
matthijskooijman committed Jan 19, 2015
1 parent 760c94f commit f837f41
Show file tree
Hide file tree
Showing 3 changed files with 247 additions and 172 deletions.
189 changes: 101 additions & 88 deletions src/Scout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -320,9 +320,9 @@ void PinoccioScout::saveState() {
for (int i=0; i<NUM_DIGITAL_PINS; i++) {
PinState *state = &pinStates[i];
if (isPinReserved(i)) {
state->mode = PINMODE_RESERVED;
state->config = PinConfig::Mode::RESERVED;
} else {
state->mode = PINMODE_UNSET;
state->config = PinConfig::Mode::UNSET;
}
}

Expand All @@ -347,61 +347,77 @@ int8_t PinoccioScout::getRegisterPinMode(uint8_t pin) {
}
}

int8_t PinoccioScout::getPinMode(uint8_t pin) {
PinConfig PinoccioScout::getPinConfig(uint8_t pin) {
if (pin < NUM_DIGITAL_PINS)
return pinStates[pin].mode;
return PINMODE_UNSET;
return pinStates[pin].config;
return PinConfig::Mode::UNSET;
}

void PinoccioScout::makeUnsetDisconnected() {
for (int i=0; i<NUM_DIGITAL_PINS; i++) {
if (getPinMode(i) == PINMODE_UNSET)
setMode(i, PINMODE_DISCONNECTED);
if (getPinConfig(i).mode() == PinConfig::Mode::UNSET)
setPinConfig(i, PinConfig::Mode::DISCONNECTED);
}
}

bool PinoccioScout::setMode(uint8_t pin, int8_t mode) {
if (isPinReserved(pin)) {
bool PinoccioScout::setPinConfig(uint8_t pin, PinConfig config, uint8_t outvalue) {
// Cannot change pins marked as reserved
if (getPinConfig(pin).mode() == PinConfig::Mode::RESERVED)
return false;

// Pullup flag is only valid for input modes
if ((config & PinConfig::Flag::PULLUP)
&& config.mode() != PinConfig::Mode::INPUT_DIGITAL
&& config.mode() != PinConfig::Mode::INPUT_ANALOG)
return false;

if (config.mode() == PinConfig::Mode::OUTPUT_PWM && !digitalPinHasPWM(pin))
return false;
}

// pre-set initial values for mode change
int value;
switch (mode) {
case PINMODE_DISABLED:
case PINMODE_INPUT:
if (config.mode() == PinConfig::Mode::INPUT_ANALOG && !isAnalogPin(pin))
return false;

uint16_t value;
switch(config.mode()) {
case PinConfig::Mode::INPUT_DIGITAL:
pinMode(pin, INPUT);
// Writing non-zero to an input pin enables the pullup
digitalWrite(pin, config & PinConfig::Flag::PULLUP);
value = digitalRead(pin);
break;
// On disconnected (floating) pins, enable a pullup. Without the
// pullup, the input logic might switch between high and low all the
// time, causing excessive power usage.
case PINMODE_DISCONNECTED:
case PINMODE_INPUT_PULLUP:
pinMode(pin, INPUT_PULLUP);
case PinConfig::Mode::INPUT_ANALOG:
pinMode(pin, INPUT);
// Writing non-zero to an input pin enables the pullup
digitalWrite(pin, config & PinConfig::Flag::PULLUP);
value = analogRead(pin);
break;
case PINMODE_OUTPUT:
case PINMODE_PWM:
case PinConfig::Mode::OUTPUT_DIGITAL:
value = outvalue;
pinMode(pin, OUTPUT);
digitalWrite(pin, outvalue);
break;
default:
return false;
}

switch(mode) {
case PINMODE_DISABLED:
case PinConfig::Mode::OUTPUT_PWM:
value = outvalue;
pinMode(pin, OUTPUT);
analogWrite(pin, value);
case PinConfig::Mode::DISABLED:
// INPUT is effectively high-impedance
value = 0;
pinMode(pin, INPUT);
break;
case PINMODE_PWM:
case PinConfig::Mode::DISCONNECTED:
// On disconnected (floating) pins, enable a pullup. Without the
// pullup, the input logic might switch between high and low all
// the time, causing excessive power usage.
value = 0;
pinMode(pin, INPUT_PULLUP);
break;
case PINMODE_INPUT:
case PINMODE_OUTPUT:
case PINMODE_INPUT_PULLUP:
value = pinRead(pin);
break;
case PinConfig::Mode::UNSET:
default:
return false;
}

updatePinState(pin, value, mode);
updatePinState(pin, value, config);

return true;
}
Expand All @@ -418,44 +434,41 @@ bool PinoccioScout::isPWMPin(uint8_t pin) {
return digitalPinHasPWM(pin);
}

bool PinoccioScout::isInputPin(uint8_t pin) {
return (getPinMode(pin) == PINMODE_INPUT || getPinMode(pin) == PINMODE_INPUT_PULLUP) ? true : false;
}
bool PinoccioScout::pinWrite(uint8_t pin, uint8_t value) {
PinConfig config = getPinConfig(pin);
switch(config.mode()) {
case PinConfig::Mode::OUTPUT_DIGITAL:
digitalWrite(pin, value);
break;

bool PinoccioScout::isOutputPin(uint8_t pin) {
return (getPinMode(pin) == PINMODE_OUTPUT || getPinMode(pin) == PINMODE_PWM) ? true : false;
}
case PinConfig::Mode::OUTPUT_PWM:
analogWrite(pin, value);
break;

bool PinoccioScout::pinWrite(uint8_t pin, uint8_t value) {
if (isPinReserved(pin) || !isOutputPin(pin)) {
return false;
default:
return false;
}

if (getPinMode(pin) == PINMODE_PWM)
analogWrite(pin, value);
else
digitalWrite(pin, value);

updatePinState(pin, value, getPinMode(pin));
updatePinState(pin, value, config);

return true;
}

uint16_t PinoccioScout::pinRead(uint8_t pin) {
switch(getPinMode(pin)) {
case PINMODE_PWM:
return pinStates[pin].value;
PinConfig config = getPinConfig(pin);
switch(config.mode()) {
case PinConfig::Mode::INPUT_ANALOG:
return analogRead(pin - A0);

case PINMODE_INPUT:
case PINMODE_INPUT_PULLUP:
if (isAnalogPin(pin))
return analogRead(pin - A0);
else
return digitalRead(pin);
case PinConfig::Mode::INPUT_DIGITAL:
return digitalRead(pin);

case PINMODE_OUTPUT:
case PinConfig::Mode::OUTPUT_DIGITAL:
return digitalRead(pin);

case PinConfig::Mode::OUTPUT_PWM:
return pinStates[pin].value;

default:
return 0;
}
Expand All @@ -477,24 +490,24 @@ const __FlashStringHelper* PinoccioScout::getNameForPin(uint8_t pin) {
return NULL;
}

const __FlashStringHelper* PinoccioScout::getNameForPinMode(int8_t mode) {
const __FlashStringHelper* PinoccioScout::getNameForPinMode(PinConfig::Mode mode) {
switch(mode) {
case (PINMODE_DISCONNECTED):
case (PinConfig::Mode::DISCONNECTED):
return F("disconnected");
case (PINMODE_UNSET):
case (PinConfig::Mode::UNSET):
return F("unset");
case (PINMODE_RESERVED):
return F("reserved");
case (PINMODE_DISABLED):
case (PinConfig::Mode::DISABLED):
return F("disabled");
case (PINMODE_INPUT):
return F("input");
case (PINMODE_OUTPUT):
return F("output");
case (PINMODE_INPUT_PULLUP):
return F("input_pullup");
case (PINMODE_PWM):
return F("pwm");
case (PinConfig::Mode::RESERVED):
return F("reserved");
case (PinConfig::Mode::INPUT_DIGITAL):
return F("input_digital");
case (PinConfig::Mode::INPUT_ANALOG):
return F("input_analog");
case (PinConfig::Mode::OUTPUT_DIGITAL):
return F("output_digital");
case (PinConfig::Mode::OUTPUT_PWM):
return F("output_pwm");
default:
return NULL;
}
Expand Down Expand Up @@ -523,11 +536,11 @@ bool PinoccioScout::isPinReserved(uint8_t pin) {
}
}

bool PinoccioScout::updatePinState(uint8_t pin, int16_t val, int8_t mode) {
bool PinoccioScout::updatePinState(uint8_t pin, int16_t val, PinConfig config) {
PinState *state = &pinStates[pin];
if (state->value != val || state->mode != mode) {
if (state->value != val || state->config != config) {
state->value = val;
state->mode = mode;
state->config = config;

if (isDigitalPin(pin) && digitalPinEventHandler != 0) {
if (eventVerboseOutput) {
Expand All @@ -536,10 +549,10 @@ bool PinoccioScout::updatePinState(uint8_t pin, int16_t val, int8_t mode) {
Serial.print(F(","));
Serial.print(val);
Serial.print(F(","));
Serial.print(mode);
Serial.print(config);
Serial.println(F(")"));
}
digitalPinEventHandler(pin, val, mode);
digitalPinEventHandler(pin, val, config);
}

if (isAnalogPin(pin) && analogPinEventHandler != 0) {
Expand All @@ -549,10 +562,10 @@ bool PinoccioScout::updatePinState(uint8_t pin, int16_t val, int8_t mode) {
Serial.print(F(","));
Serial.print(val);
Serial.print(F(","));
Serial.print(mode);
Serial.print(config);
Serial.println(F(")"));
}
analogPinEventHandler(pin - A0, val, mode);
analogPinEventHandler(pin - A0, val, config);
}

return true;
Expand All @@ -563,10 +576,10 @@ bool PinoccioScout::updatePinState(uint8_t pin, int16_t val, int8_t mode) {
static void scoutDigitalStateChangeTimerHandler(SYS_Timer_t *timer) {
if (Scout.digitalPinEventHandler != 0) {
for (int i=2; i<9; i++) {
int mode = Scout.getPinMode(i);
if (mode >= 0) {
PinConfig config = Scout.getPinConfig(i);
if (config.mode().active()) {
int value = Scout.pinRead(i);
Scout.updatePinState(i, value, mode);
Scout.updatePinState(i, value, config);
}
}
}
Expand All @@ -575,10 +588,10 @@ static void scoutDigitalStateChangeTimerHandler(SYS_Timer_t *timer) {
static void scoutAnalogStateChangeTimerHandler(SYS_Timer_t *timer) {
if (Scout.analogPinEventHandler != 0) {
for (int i=0; i<NUM_ANALOG_INPUTS; i++) {
int mode = Scout.getPinMode(i+A0);
if (mode >= 0) {
PinConfig config = Scout.getPinConfig(i+A0);
if (config.mode().active()) {
int value = Scout.pinRead(i+A0);
Scout.updatePinState(i+A0, value, mode);
Scout.updatePinState(i+A0, value, config);
}
}
}
Expand Down
Loading

0 comments on commit f837f41

Please sign in to comment.