2017-08-27 22:01:36 -07:00
|
|
|
/*
|
|
|
|
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
|
|
|
|
*/
|
2021-06-21 22:30:52 -07:00
|
|
|
/** @file
|
|
|
|
* Custom Programmable I/O.
|
|
|
|
* The config related to Programmable I/O is found on configPage13 (of type @ref config13).
|
|
|
|
*/
|
2018-08-15 00:44:30 -07:00
|
|
|
#include <avr/pgmspace.h>
|
2018-07-19 00:35:35 -07:00
|
|
|
#include "globals.h"
|
2020-08-22 03:53:42 -07:00
|
|
|
#include "utilities.h"
|
2018-08-13 19:46:39 -07:00
|
|
|
#include "decoders.h"
|
2019-03-17 04:14:29 -07:00
|
|
|
#include "comms.h"
|
2021-10-03 06:09:53 -07:00
|
|
|
#include "logger.h"
|
2022-04-17 21:12:57 -07:00
|
|
|
#include "scheduler.h"
|
|
|
|
#include "scheduledIO.h"
|
|
|
|
#include "speeduino.h"
|
2019-03-17 04:14:29 -07:00
|
|
|
|
2021-07-18 16:11:04 -07:00
|
|
|
uint8_t ioDelay[sizeof(configPage13.outputPin)];
|
|
|
|
uint8_t ioOutDelay[sizeof(configPage13.outputPin)];
|
2021-04-20 21:36:27 -07:00
|
|
|
uint8_t pinIsValid = 0;
|
2021-07-18 16:11:04 -07:00
|
|
|
uint8_t currentRuleStatus = 0;
|
2021-04-20 21:36:27 -07:00
|
|
|
|
2017-08-27 22:01:36 -07:00
|
|
|
|
2021-06-21 22:30:52 -07:00
|
|
|
/** 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
|
|
|
|
*/
|
2017-09-30 07:07:36 -07:00
|
|
|
byte pinTranslate(byte rawPin)
|
|
|
|
{
|
|
|
|
byte outputPin = rawPin;
|
2021-02-01 15:00:04 -08:00
|
|
|
if(rawPin > BOARD_MAX_DIGITAL_PINS) { outputPin = A8 + (outputPin - BOARD_MAX_DIGITAL_PINS - 1); }
|
2017-09-30 07:07:36 -07:00
|
|
|
|
|
|
|
return outputPin;
|
|
|
|
}
|
2021-06-21 22:30:52 -07:00
|
|
|
/** 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).
|
|
|
|
* */
|
2021-03-18 17:59:21 -07:00
|
|
|
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;
|
2021-04-20 22:06:59 -07:00
|
|
|
#if BOARD_MAX_ADC_PINS >= 14
|
|
|
|
case 14: outputPin = A14; break;
|
|
|
|
#endif
|
|
|
|
#if BOARD_MAX_ADC_PINS >= 15
|
|
|
|
case 15: outputPin = A15; break;
|
|
|
|
#endif
|
2021-03-18 17:59:21 -07:00
|
|
|
#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;
|
|
|
|
}
|
2017-09-30 07:07:36 -07:00
|
|
|
|
2018-05-14 21:05:45 -07:00
|
|
|
|
2022-11-05 15:43:29 -07:00
|
|
|
void setResetControlPinState(void)
|
2018-01-07 14:22:18 -08:00
|
|
|
{
|
|
|
|
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;
|
2023-11-27 15:13:00 -08:00
|
|
|
default:
|
|
|
|
// Do nothing - keep MISRA happy
|
|
|
|
break;
|
2018-01-07 14:22:18 -08:00
|
|
|
}
|
|
|
|
}
|
2019-03-17 04:14:29 -07:00
|
|
|
|
2020-08-11 21:21:36 -07:00
|
|
|
//*********************************************************************************************************************************************************************************
|
2022-11-05 15:43:29 -07:00
|
|
|
void initialiseProgrammableIO(void)
|
2020-08-11 21:21:36 -07:00
|
|
|
{
|
2021-07-18 16:11:04 -07:00
|
|
|
uint8_t outputPin;
|
2020-08-11 21:21:36 -07:00
|
|
|
for (uint8_t y = 0; y < sizeof(configPage13.outputPin); y++)
|
|
|
|
{
|
2021-07-18 16:11:04 -07:00
|
|
|
ioDelay[y] = 0;
|
|
|
|
ioOutDelay[y] = 0;
|
|
|
|
outputPin = configPage13.outputPin[y];
|
|
|
|
if (outputPin > 0)
|
2020-08-11 21:21:36 -07:00
|
|
|
{
|
2021-07-18 16:11:04 -07:00
|
|
|
if ( outputPin >= 128 ) //Cascate rule usage
|
2020-12-01 13:59:44 -08:00
|
|
|
{
|
2021-07-18 16:11:04 -07:00
|
|
|
BIT_WRITE(currentStatus.outputsStatus, y, BIT_CHECK(configPage13.outputInverted, y));
|
2020-12-01 13:59:44 -08:00
|
|
|
BIT_SET(pinIsValid, y);
|
|
|
|
}
|
2021-07-18 16:11:04 -07:00
|
|
|
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); }
|
2020-08-11 21:21:36 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-06-21 22:30:52 -07:00
|
|
|
/** Check all (8) programmable I/O:s and carry out action on output pin as needed.
|
2021-10-20 14:03:29 -07:00
|
|
|
* Compare 2 (16 bit) vars in a way configured by @ref cmpOperation (see also @ref config13.operation).
|
2021-06-21 22:30:52 -07:00
|
|
|
* Use ProgrammableIOGetData() to get 2 vars to compare.
|
|
|
|
* Skip all programmable I/O:s where output pin is set 0 (meaning: not programmed).
|
|
|
|
*/
|
2022-11-05 15:43:29 -07:00
|
|
|
void checkProgrammableIO(void)
|
2020-08-11 21:21:36 -07:00
|
|
|
{
|
|
|
|
int16_t data, data2;
|
2021-07-18 16:11:04 -07:00
|
|
|
uint8_t dataRequested;
|
2020-08-11 21:21:36 -07:00
|
|
|
bool firstCheck, secondCheck;
|
|
|
|
|
|
|
|
for (uint8_t y = 0; y < sizeof(configPage13.outputPin); y++)
|
|
|
|
{
|
|
|
|
firstCheck = false;
|
|
|
|
secondCheck = false;
|
2020-12-01 13:59:44 -08:00
|
|
|
if ( BIT_CHECK(pinIsValid, y) ) //if outputPin == 0 it is disabled
|
2021-07-18 16:11:04 -07:00
|
|
|
{
|
|
|
|
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); }
|
2020-08-11 21:21:36 -07:00
|
|
|
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; }
|
2021-07-18 16:11:04 -07:00
|
|
|
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; }
|
2020-08-11 21:21:36 -07:00
|
|
|
|
|
|
|
if (configPage13.operation[y].bitwise != BITWISE_DISABLED)
|
|
|
|
{
|
2021-07-18 16:11:04 -07:00
|
|
|
dataRequested = configPage13.secondDataIn[y];
|
|
|
|
if ( dataRequested <= (REUSE_RULES + sizeof(configPage13.outputPin)) ) //Failsafe check
|
2020-08-11 21:21:36 -07:00
|
|
|
{
|
2021-07-18 16:11:04 -07:00
|
|
|
if ( dataRequested > 239U ) //Somehow using 239 uses 9 bytes of RAM, why??
|
|
|
|
{
|
|
|
|
dataRequested -= REUSE_RULES;
|
|
|
|
data = BIT_CHECK(currentRuleStatus, dataRequested);
|
|
|
|
}
|
|
|
|
else { data = ProgrammableIOGetData(dataRequested); }
|
2020-08-11 21:21:36 -07:00
|
|
|
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; }
|
2021-07-18 16:11:04 -07:00
|
|
|
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; }
|
2020-08-11 21:21:36 -07:00
|
|
|
|
|
|
|
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; }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-18 16:11:04 -07:00
|
|
|
//If the limiting time is active(>0) and using maximum time
|
|
|
|
if (BIT_CHECK(configPage13.kindOfLimiting, y))
|
|
|
|
{
|
|
|
|
if(firstCheck)
|
|
|
|
{
|
2021-07-29 04:00:43 -07:00
|
|
|
if ((configPage13.outputTimeLimit[y] != 0) && (ioOutDelay[y] >= configPage13.outputTimeLimit[y])) { firstCheck = false; } //Time has counted, disable the output
|
2021-07-18 16:11:04 -07:00
|
|
|
}
|
|
|
|
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] < 255) )
|
2020-08-11 21:21:36 -07:00
|
|
|
{
|
2021-07-18 16:11:04 -07:00
|
|
|
if (ioDelay[y] >= configPage13.outputDelay[y])
|
2020-08-11 21:21:36 -07:00
|
|
|
{
|
2021-07-18 16:11:04 -07:00
|
|
|
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);
|
2020-08-11 21:21:36 -07:00
|
|
|
}
|
|
|
|
else { ioDelay[y]++; }
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-07-18 16:11:04 -07:00
|
|
|
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;
|
2020-08-11 21:21:36 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-06-21 22:30:52 -07:00
|
|
|
/** Get single I/O data var (from currentStatus) for comparison.
|
|
|
|
* @param index - Field index/number (?)
|
|
|
|
* @return 16 bit (int) result
|
|
|
|
*/
|
2020-08-11 21:21:36 -07:00
|
|
|
int16_t ProgrammableIOGetData(uint16_t index)
|
|
|
|
{
|
|
|
|
int16_t result;
|
|
|
|
if ( index < LOG_ENTRY_SIZE )
|
|
|
|
{
|
2022-01-04 21:41:12 -08:00
|
|
|
if(is2ByteEntry(index)) { result = word(getTSLogEntry(index+1), getTSLogEntry(index)); }
|
|
|
|
else { result = getTSLogEntry(index); }
|
2021-02-01 18:14:47 -08:00
|
|
|
|
2020-08-19 23:58:44 -07:00
|
|
|
//Special cases for temperatures
|
|
|
|
if( (index == 6) || (index == 7) ) { result -= CALIBRATION_TEMPERATURE_OFFSET; }
|
2020-08-11 21:21:36 -07:00
|
|
|
}
|
2021-07-18 16:11:04 -07:00
|
|
|
else if ( index == 239U ) { result = (int16_t)max((uint32_t)runSecsX10, (uint32_t)32768); } //STM32 used std lib
|
2020-08-11 21:21:36 -07:00
|
|
|
else { result = -1; } //Index is bigger than fullStatus array
|
|
|
|
return result;
|
2021-06-21 22:30:52 -07:00
|
|
|
}
|