/* Speeduino - Simple engine management for the Arduino Mega 2560 platform Copyright (C) Josh Stewart A full copy of the license may be found in the projects root directory */ /** @file * Custom Programmable I/O. * The config related to Programmable I/O is found on configPage13 (of type @ref config13). */ #include #include "globals.h" #include "utilities.h" #include "decoders.h" #include "comms.h" #include "logger.h" #include "scheduler.h" #include "scheduledIO.h" #include "speeduino.h" uint8_t ioDelay[sizeof(configPage13.outputPin)]; uint8_t ioOutDelay[sizeof(configPage13.outputPin)]; uint8_t pinIsValid = 0; uint8_t currentRuleStatus = 0; /** Translate between the pin list that appears in TS and the actual pin numbers. For the **digital IO**, this will simply return the same number as the rawPin value as those are mapped directly. For **analog pins**, it will translate them into the correct internal pin number. * @param rawPin - High level pin number * @return Translated / usable pin number */ byte pinTranslate(byte rawPin) { byte outputPin = rawPin; if(rawPin > BOARD_MAX_DIGITAL_PINS) { outputPin = A8 + (outputPin - BOARD_MAX_DIGITAL_PINS - 1); } return outputPin; } /** Translate a pin number (0 - 22) to the relevant Ax (analog) pin reference. * This is required as some ARM chips do not have all analog pins in order (EG pin A15 != A14 + 1). * */ byte pinTranslateAnalog(byte rawPin) { byte outputPin = rawPin; switch(rawPin) { case 0: outputPin = A0; break; case 1: outputPin = A1; break; case 2: outputPin = A2; break; case 3: outputPin = A3; break; case 4: outputPin = A4; break; case 5: outputPin = A5; break; case 6: outputPin = A6; break; case 7: outputPin = A7; break; case 8: outputPin = A8; break; case 9: outputPin = A9; break; case 10: outputPin = A10; break; case 11: outputPin = A11; break; case 12: outputPin = A12; break; case 13: outputPin = A13; break; #if BOARD_MAX_ADC_PINS >= 14 case 14: outputPin = A14; break; #endif #if BOARD_MAX_ADC_PINS >= 15 case 15: outputPin = A15; break; #endif #if BOARD_MAX_ADC_PINS >= 16 case 16: outputPin = A16; break; #endif #if BOARD_MAX_ADC_PINS >= 17 case 17: outputPin = A17; break; #endif #if BOARD_MAX_ADC_PINS >= 18 case 18: outputPin = A18; break; #endif #if BOARD_MAX_ADC_PINS >= 19 case 19: outputPin = A19; break; #endif #if BOARD_MAX_ADC_PINS >= 20 case 20: outputPin = A20; break; #endif #if BOARD_MAX_ADC_PINS >= 21 case 21: outputPin = A21; break; #endif #if BOARD_MAX_ADC_PINS >= 22 case 22: outputPin = A22; break; #endif } return outputPin; } void setResetControlPinState(void) { BIT_CLEAR(currentStatus.status3, BIT_STATUS3_RESET_PREVENT); /* Setup reset control initial state */ switch (resetControl) { case RESET_CONTROL_PREVENT_WHEN_RUNNING: /* Set the reset control pin LOW and change it to HIGH later when we get sync. */ digitalWrite(pinResetControl, LOW); BIT_CLEAR(currentStatus.status3, BIT_STATUS3_RESET_PREVENT); break; case RESET_CONTROL_PREVENT_ALWAYS: /* Set the reset control pin HIGH and never touch it again. */ digitalWrite(pinResetControl, HIGH); BIT_SET(currentStatus.status3, BIT_STATUS3_RESET_PREVENT); break; case RESET_CONTROL_SERIAL_COMMAND: /* Set the reset control pin HIGH. There currently isn't any practical difference between this and PREVENT_ALWAYS but it doesn't hurt anything to have them separate. */ digitalWrite(pinResetControl, HIGH); BIT_CLEAR(currentStatus.status3, BIT_STATUS3_RESET_PREVENT); break; default: // Do nothing - keep MISRA happy break; } } //********************************************************************************************************************************************************************************* void initialiseProgrammableIO(void) { uint8_t outputPin; for (uint8_t y = 0; y < sizeof(configPage13.outputPin); y++) { ioDelay[y] = 0; ioOutDelay[y] = 0; outputPin = configPage13.outputPin[y]; if (outputPin > 0) { if ( outputPin >= 128 ) //Cascate rule usage { BIT_WRITE(currentStatus.outputsStatus, y, BIT_CHECK(configPage13.outputInverted, y)); BIT_SET(pinIsValid, y); } else if ( !pinIsUsed(outputPin) ) { pinMode(outputPin, OUTPUT); digitalWrite(outputPin, BIT_CHECK(configPage13.outputInverted, y)); BIT_WRITE(currentStatus.outputsStatus, y, BIT_CHECK(configPage13.outputInverted, y)); BIT_SET(pinIsValid, y); } else { BIT_CLEAR(pinIsValid, y); } } } } /** Check all (8) programmable I/O:s and carry out action on output pin as needed. * Compare 2 (16 bit) vars in a way configured by @ref cmpOperation (see also @ref config13.operation). * Use ProgrammableIOGetData() to get 2 vars to compare. * Skip all programmable I/O:s where output pin is set 0 (meaning: not programmed). */ void checkProgrammableIO(void) { int16_t data, data2; uint8_t dataRequested; bool firstCheck, secondCheck; for (uint8_t y = 0; y < sizeof(configPage13.outputPin); y++) { firstCheck = false; secondCheck = false; if ( BIT_CHECK(pinIsValid, y) ) //if outputPin == 0 it is disabled { dataRequested = configPage13.firstDataIn[y]; if ( dataRequested > 239U ) //Somehow using 239 uses 9 bytes of RAM, why?? { dataRequested -= REUSE_RULES; if ( dataRequested <= sizeof(configPage13.outputPin) ) { data = BIT_CHECK(currentRuleStatus, dataRequested); } else { data = 0; } } else { data = ProgrammableIOGetData(dataRequested); } data2 = configPage13.firstTarget[y]; if ( (configPage13.operation[y].firstCompType == COMPARATOR_EQUAL) && (data == data2) ) { firstCheck = true; } else if ( (configPage13.operation[y].firstCompType == COMPARATOR_NOT_EQUAL) && (data != data2) ) { firstCheck = true; } else if ( (configPage13.operation[y].firstCompType == COMPARATOR_GREATER) && (data > data2) ) { firstCheck = true; } else if ( (configPage13.operation[y].firstCompType == COMPARATOR_GREATER_EQUAL) && (data >= data2) ) { firstCheck = true; } else if ( (configPage13.operation[y].firstCompType == COMPARATOR_LESS) && (data < data2) ) { firstCheck = true; } else if ( (configPage13.operation[y].firstCompType == COMPARATOR_LESS_EQUAL) && (data <= data2) ) { firstCheck = true; } else if ( (configPage13.operation[y].firstCompType == COMPARATOR_AND) && ((data & data2) != 0) ) { firstCheck = true; } else if ( (configPage13.operation[y].firstCompType == COMPARATOR_XOR) && ((data ^ data2) != 0) ) { firstCheck = true; } if (configPage13.operation[y].bitwise != BITWISE_DISABLED) { dataRequested = configPage13.secondDataIn[y]; if ( dataRequested <= (REUSE_RULES + sizeof(configPage13.outputPin)) ) //Failsafe check { if ( dataRequested > 239U ) //Somehow using 239 uses 9 bytes of RAM, why?? { dataRequested -= REUSE_RULES; data = BIT_CHECK(currentRuleStatus, dataRequested); } else { data = ProgrammableIOGetData(dataRequested); } data2 = configPage13.secondTarget[y]; if ( (configPage13.operation[y].secondCompType == COMPARATOR_EQUAL) && (data == data2) ) { secondCheck = true; } else if ( (configPage13.operation[y].secondCompType == COMPARATOR_NOT_EQUAL) && (data != data2) ) { secondCheck = true; } else if ( (configPage13.operation[y].secondCompType == COMPARATOR_GREATER) && (data > data2) ) { secondCheck = true; } else if ( (configPage13.operation[y].secondCompType == COMPARATOR_GREATER_EQUAL) && (data >= data2) ) { secondCheck = true; } else if ( (configPage13.operation[y].secondCompType == COMPARATOR_LESS) && (data < data2) ) { secondCheck = true; } else if ( (configPage13.operation[y].secondCompType == COMPARATOR_LESS_EQUAL) && (data <= data2) ) { secondCheck = true; } else if ( (configPage13.operation[y].secondCompType == COMPARATOR_AND) && ((data & data2) != 0) ) { secondCheck = true; } else if ( (configPage13.operation[y].secondCompType == COMPARATOR_XOR) && ((data ^ data2) != 0) ) { secondCheck = true; } if (configPage13.operation[y].bitwise == BITWISE_AND) { firstCheck &= secondCheck; } if (configPage13.operation[y].bitwise == BITWISE_OR) { firstCheck |= secondCheck; } if (configPage13.operation[y].bitwise == BITWISE_XOR) { firstCheck ^= secondCheck; } } } //If the limiting time is active(>0) and using maximum time if (BIT_CHECK(configPage13.kindOfLimiting, y)) { if(firstCheck) { if ((configPage13.outputTimeLimit[y] != 0) && (ioOutDelay[y] >= configPage13.outputTimeLimit[y])) { firstCheck = false; } //Time has counted, disable the output } else { //Released before Maximum time, set delay to maximum to flip the output next if(BIT_CHECK(currentStatus.outputsStatus, y)) { ioOutDelay[y] = configPage13.outputTimeLimit[y]; } else { ioOutDelay[y] = 0; } //Reset the counter for next time } } if ( (firstCheck == true) && (configPage13.outputDelay[y] < UINT8_MAX) ) { if (ioDelay[y] >= configPage13.outputDelay[y]) { bool bitStatus = BIT_CHECK(configPage13.outputInverted, y) ^ firstCheck; if (BIT_CHECK(currentStatus.outputsStatus, y) && (ioOutDelay[y] < configPage13.outputTimeLimit[y])) { ioOutDelay[y]++; } if (configPage13.outputPin[y] < 128) { digitalWrite(configPage13.outputPin[y], bitStatus); } else { BIT_WRITE(currentRuleStatus, y, bitStatus); } BIT_WRITE(currentStatus.outputsStatus, y, bitStatus); } else { ioDelay[y]++; } } else { if (ioOutDelay[y] >= configPage13.outputTimeLimit[y]) { bool bitStatus = BIT_CHECK(configPage13.outputInverted, y) ^ firstCheck; if (configPage13.outputPin[y] < 128) { digitalWrite(configPage13.outputPin[y], bitStatus); } else { BIT_WRITE(currentRuleStatus, y, bitStatus); } BIT_WRITE(currentStatus.outputsStatus, y, bitStatus); if(!BIT_CHECK(configPage13.kindOfLimiting, y)) { ioOutDelay[y] = 0; } } else { ioOutDelay[y]++; } ioDelay[y] = 0; } } } } /** Get single I/O data var (from currentStatus) for comparison. * @param index - Field index/number (?) * @return 16 bit (int) result */ int16_t ProgrammableIOGetData(uint16_t index) { int16_t result; if ( index < LOG_ENTRY_SIZE ) { if(is2ByteEntry(index)) { result = word(getTSLogEntry(index+1), getTSLogEntry(index)); } else { result = getTSLogEntry(index); } //Special cases for temperatures if( (index == 6) || (index == 7) ) { result -= CALIBRATION_TEMPERATURE_OFFSET; } } else if ( index == 239U ) { result = (int16_t)max((uint32_t)runSecsX10, (uint32_t)32768); } //STM32 used std lib else { result = -1; } //Index is bigger than fullStatus array return result; }