/** * \file cimdithal.cpp * \brief implementation of the CimditHAL class * \author GrumpyDeveloper (Sascha Nitsch) * \copyright 2022 Sascha Nitsch * Licensed under MIT license * */ // our defines #include "defines.h" // library includes #include // own includes #include "cimdithal.h" uint32_t CimditHAL::m_millis = 0; CimditHAL::CimditHAL() { m_nextKeyScan = 0; m_nextAnalogRead = 0; m_keyRow = 0; m_analogNum = 0; m_analogChanged = 0; for (uint8_t i = 0; i < 8; ++i) { m_currentButtons[i] = 0xff; m_buttonsChanged[i] = 0; } m_rotChanged = 0; } void CimditHAL::begin() { pinMode(ROT_INT, INPUT); pinMode(ANALOG_MUX0, OUTPUT); pinMode(ANALOG_MUX1, OUTPUT); pinMode(ANALOG_MUX2, OUTPUT); pinMode(ANALOG_MUX3, OUTPUT); m_keyMatrix.begin_I2C(0x20); m_rotEncoders.begin_I2C(0x21); // combine the inputs to a single interrupt pin m_rotEncoders.setupInterrupts(true, false, HIGH); for (uint8_t i = 0; i < 8; ++i) { // set key matrix row to output m_keyMatrix.pinMode(i, OUTPUT); // set encoders to input and setup interrupt to fire on change m_rotEncoders.pinMode(i, INPUT_PULLUP); m_rotEncoders.setupInterruptPin(i, CHANGE); m_currentButtons[i] = 0xff; } for (uint8_t i = 8; i < 16; ++i) { // set key matrix rows to input m_keyMatrix.pinMode(i, INPUT_PULLUP); // set encoders to input and setup interrupt to fire on change m_rotEncoders.pinMode(i, INPUT_PULLUP); m_rotEncoders.setupInterruptPin(i, CHANGE); } m_keyMatrix.writeGPIO(0xff, 0); // read in encoder values uint16_t rot = m_rotEncoders.readGPIOAB(); for (uint8_t i = 0; i < 8; ++i) { m_rotaryEncoders[i].update((rot & 1) != 0, (rot & 2) != 0); rot >>= 2; } // read in key matrix values for (uint8_t i = 0 ; i < 8; ++i) { m_keyMatrix.writeGPIO(0xff ^ (1 << i), 0); delay(10); m_currentButtons[i] = m_keyMatrix.readGPIO(1); } // read in analog values for (uint8_t i = 0 ; i < 16; ++i) { digitalWrite(ANALOG_MUX3, i & 8); digitalWrite(ANALOG_MUX2, i & 4); digitalWrite(ANALOG_MUX1, i & 2); digitalWrite(ANALOG_MUX0, i & 1); delay(10); // give the muxer a chance m_currentAnalogValues[i] = analogRead(ANALOG_IN); } m_millis = millis(); } void CimditHAL::readFromHardware(bool interrupt) { bool rotChanged = interrupt; // if true, we return after doing the rotary steps while (interrupt || digitalRead(ROT_INT)) { rotChanged = true; interrupt = false; uint16_t rot = m_rotEncoders.readGPIOAB(); for (uint8_t i = 0; i < 8; ++i) { m_rotChanged |= m_rotaryEncoders[i].update((rot & 1) != 0, (rot & 2) != 0) << i; rot = rot >> 2; } } // quick return if rotary encoder changed if (rotChanged) return; m_millis = millis(); // uint32_t rollover every 92 days if (m_millis < ROLLOVER_INTERVAL) { m_nextAnalogRead = 0; m_nextKeyScan = 0; } if (m_millis > m_nextAnalogRead) { // only scan analog every ANALOG_INTERVAL ms m_nextAnalogRead = m_millis + ANALOG_INTERVAL; // save old state int16_t lastAnalogValue = m_currentAnalogValues[m_analogNum]; // scan analog value m_currentAnalogValues[m_analogNum] = analogRead(ANALOG_IN); #ifdef ANALOG_LOW_PASS_FACTOR m_currentAnalogValues[m_analogNum] = lastAnalogValue - (ANALOG_LOW_PASS_FACTOR * (lastAnalogValue - m_currentAnalogValues[m_analogNum])); #endif // set changed bits if (lastAnalogValue != m_currentAnalogValues[m_analogNum]) { //if (abs(lastAnalogValue - m_currentAnalogValues[m_analogNum]) > 1) { m_analogChanged |= 1 << m_analogNum; } // set mux value for next round, wrap to 0 at 16 m_analogNum = (m_analogNum + 1) & 15; // set output digitalWrite(ANALOG_MUX3, m_analogNum & 8); digitalWrite(ANALOG_MUX2, m_analogNum & 4); digitalWrite(ANALOG_MUX1, m_analogNum & 2); digitalWrite(ANALOG_MUX0, m_analogNum & 1); } // scan key matrix if (m_millis > m_nextKeyScan) { // only scan analog every KEYSCAN_INTERVAL ms m_nextKeyScan = m_millis + KEYSCAN_INTERVAL; // save old state uint8_t lastButtons = m_currentButtons[m_keyRow]; // read column bits m_currentButtons[m_keyRow] = m_keyMatrix.readGPIO(1); // set changed bits m_buttonsChanged[m_keyRow] |= lastButtons ^ m_currentButtons[m_keyRow]; // set row bit wrap 8 to 0 m_keyRow = (m_keyRow + 1) & 7; m_keyMatrix.writeGPIO(0xff ^ (1 << m_keyRow), 0); } } uint8_t CimditHAL::rotaryChanged() { uint8_t ret = m_rotChanged; m_rotChanged = 0; return ret; } uint16_t CimditHAL::analogChanged() { uint16_t ret = m_analogChanged; m_analogChanged = 0; return ret; } uint64_t CimditHAL::buttonsChanged() { uint64_t ret = 0; for (int8_t i = 7; i >= 0; --i) { ret = (ret << 8) + m_buttonsChanged[i]; m_buttonsChanged[i] = 0; } return ret; } int8_t CimditHAL::getEncoder(uint8_t num) { return m_rotaryEncoders[num].getDelta(); } uint16_t CimditHAL::getAnalog(uint8_t num) const { return m_currentAnalogValues[num]; } bool CimditHAL::getButton(uint8_t num) const { return !((m_currentButtons[num >> 3] >> (num & 7)) & 1); }