You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
301 lines
11 KiB
301 lines
11 KiB
#include <Arduino.h>
|
|
#include <Wire.h>
|
|
#include "settings.h"
|
|
#include "Port.h"
|
|
#include "Log.h"
|
|
|
|
// Infos:
|
|
// PCA9555 has 16 channels that are subdivided into 2 ports with 8 channels each.
|
|
// Every channels is represented by a bit.
|
|
// Examples for ESPuino-configuration:
|
|
// 100 => port 0 channel/bit 0
|
|
// 107 => port 0 channel/bit 7
|
|
// 108 => port 1 channel/bit 0
|
|
// 115 => port 1 channel/bit 7
|
|
|
|
#ifdef PORT_EXPANDER_ENABLE
|
|
extern TwoWire i2cBusTwo;
|
|
|
|
uint8_t Port_ExpanderPortsInputChannelStatus[2];
|
|
static uint8_t Port_ExpanderPortsOutputChannelStatus[2] = {255, 255}; // Stores current configuration of output-channels locally
|
|
void Port_ExpanderHandler(void);
|
|
uint8_t Port_ChannelToBit(const uint8_t _channel);
|
|
void Port_WriteInitMaskForOutputChannels(void);
|
|
void Port_Test(void);
|
|
|
|
#if (PE_INTERRUPT_PIN >= 0 && PE_INTERRUPT_PIN <= 39)
|
|
#define PE_INTERRUPT_PIN_ENABLE
|
|
void IRAM_ATTR PORT_ExpanderISR(void);
|
|
bool Port_AllowReadFromPortExpander = false;
|
|
bool Port_AllowInitReadFromPortExpander = true;
|
|
#endif
|
|
#endif
|
|
|
|
void Port_Init(void) {
|
|
#ifdef PORT_EXPANDER_ENABLE
|
|
Port_Test();
|
|
Port_WriteInitMaskForOutputChannels();
|
|
#endif
|
|
|
|
#ifdef PE_INTERRUPT_PIN_ENABLE
|
|
pinMode(PE_INTERRUPT_PIN, INPUT_PULLUP);
|
|
attachInterrupt(PE_INTERRUPT_PIN, PORT_ExpanderISR, FALLING);
|
|
Log_Println(portExpanderInterruptEnabled, LOGLEVEL_NOTICE);
|
|
#endif
|
|
|
|
// If automatic HP-detection is not used...
|
|
#ifndef HEADPHONE_ADJUST_ENABLE
|
|
#ifdef GPIO_PA_EN
|
|
Port_Write(GPIO_PA_EN, true, true); // ...but it's necessary to enable loudspeaker amp...
|
|
#endif
|
|
#ifdef GPIO_HP_EN
|
|
Port_Write(GPIO_HP_EN, true, true); // ...or headphones-amp
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
void Port_Cyclic(void) {
|
|
#ifdef PORT_EXPANDER_ENABLE
|
|
Port_ExpanderHandler();
|
|
#endif
|
|
}
|
|
|
|
// Wrapper: reads from GPIOs (via digitalRead()) or from port-expander (if enabled)
|
|
// Behaviour like digitalRead(): returns true if not pressed and false if pressed
|
|
bool Port_Read(const uint8_t _channel) {
|
|
switch (_channel) {
|
|
case 0 ... 39: // GPIO
|
|
return digitalRead(_channel);
|
|
|
|
#ifdef PORT_EXPANDER_ENABLE
|
|
case 100 ... 107: // Port-expander (port 0)
|
|
return (Port_ExpanderPortsInputChannelStatus[0] & (1 << (_channel - 100))); // Remove offset 100 (return false if pressed)
|
|
|
|
case 108 ... 115: // Port-expander (port 1)
|
|
return (Port_ExpanderPortsInputChannelStatus[1] & (1 << (_channel - 108))); // Remove offset 100 + 8 (return false if pressed)
|
|
|
|
#endif
|
|
|
|
default: // Everything else (doesn't make sense at all) isn't supposed to be pressed
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Wrapper-function to reverse detection of connected headphones.
|
|
// Normally headphones are supposed to be plugged in if a given GPIO/channel is LOW/false.
|
|
bool Port_Detect_Mode_HP(bool _state) {
|
|
#ifndef DETECT_HP_ON_HIGH
|
|
return _state;
|
|
#else
|
|
return !_state;
|
|
#endif
|
|
}
|
|
|
|
// Configures OUTPUT-mode for GPIOs (non port-expander)
|
|
// Output-mode for port-channels is done via Port_WriteInitMaskForOutputChannels()
|
|
void Port_Write(const uint8_t _channel, const bool _newState, const bool _initGpio) {
|
|
if (_initGpio) {
|
|
switch (_channel) {
|
|
case 0 ... 39: { // GPIO
|
|
pinMode(_channel, OUTPUT);
|
|
Port_Write(_channel, _newState);
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
Port_Write(_channel, _newState);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Wrapper: writes to GPIOs (via digitalWrite()) or to port-expander (if enabled)
|
|
void Port_Write(const uint8_t _channel, const bool _newState) {
|
|
switch (_channel) {
|
|
case 0 ... 39: { // GPIO
|
|
digitalWrite(_channel, _newState);
|
|
break;
|
|
}
|
|
|
|
#ifdef PORT_EXPANDER_ENABLE
|
|
case 100 ... 115: {
|
|
uint8_t portOffset = 0;
|
|
if (_channel >= 108 && _channel <= 115) {
|
|
portOffset = 1;
|
|
}
|
|
|
|
uint8_t oldPortBitmask = Port_ExpanderPortsOutputChannelStatus[portOffset];
|
|
uint8_t newPortBitmask;
|
|
|
|
i2cBusTwo.beginTransmission(expanderI2cAddress);
|
|
i2cBusTwo.write(0x02); // Pointer to output configuration-register
|
|
if (_newState) {
|
|
newPortBitmask = (oldPortBitmask | (1 << Port_ChannelToBit(_channel)));
|
|
Port_ExpanderPortsOutputChannelStatus[portOffset] = newPortBitmask; // Write back new status
|
|
} else {
|
|
newPortBitmask = (oldPortBitmask & ~(1 << Port_ChannelToBit(_channel)));
|
|
Port_ExpanderPortsOutputChannelStatus[portOffset] = newPortBitmask; // Write back new status
|
|
}
|
|
i2cBusTwo.write(Port_ExpanderPortsOutputChannelStatus[0]);
|
|
i2cBusTwo.write(Port_ExpanderPortsOutputChannelStatus[1]);
|
|
i2cBusTwo.endTransmission();
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
default: {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef PORT_EXPANDER_ENABLE
|
|
// Translates digitalWrite-style "GPIO" to bit
|
|
uint8_t Port_ChannelToBit(const uint8_t _channel) {
|
|
switch (_channel) {
|
|
case 100:
|
|
case 108:
|
|
return 0;
|
|
break;
|
|
case 101:
|
|
case 109:
|
|
return 1;
|
|
break;
|
|
case 102:
|
|
case 110:
|
|
return 2;
|
|
break;
|
|
case 103:
|
|
case 111:
|
|
return 3;
|
|
break;
|
|
case 104:
|
|
case 112:
|
|
return 4;
|
|
break;
|
|
case 105:
|
|
case 113:
|
|
return 5;
|
|
break;
|
|
case 106:
|
|
case 114:
|
|
return 6;
|
|
break;
|
|
case 107:
|
|
case 115:
|
|
return 7;
|
|
break;
|
|
|
|
default:
|
|
return 255; // not valid!
|
|
}
|
|
}
|
|
|
|
// Writes initial port-configuration (I/O) for port-expander PCA9555
|
|
// If no output-channel is necessary, nothing has to be configured as all channels are in input-mode as per default (255)
|
|
// So every bit representing an output-channel needs to be set to 0.
|
|
void Port_WriteInitMaskForOutputChannels(void) {
|
|
const uint8_t portBaseValueBitMask = 255;
|
|
const uint8_t portsToWrite = 2;
|
|
uint8_t OutputBitMaskAsPerPort[portsToWrite] = { portBaseValueBitMask, portBaseValueBitMask }; // 255 => all channels set to input; [0]: port0, [1]: port1
|
|
|
|
#ifdef GPIO_PA_EN
|
|
if (GPIO_PA_EN >= 100 && GPIO_PA_EN <= 107) {
|
|
// Bits of channels to be configured as input are 1 by default.
|
|
// So in order to change I/O-direction to output we need to set those bits to 0.
|
|
OutputBitMaskAsPerPort[0] &= ~(1 << Port_ChannelToBit(GPIO_PA_EN));
|
|
//Serial.printf("PA LO: %u\n", OutputBitMaskAsPerPort[0]);
|
|
} else if (GPIO_PA_EN >= 108 && GPIO_PA_EN <= 115) {
|
|
OutputBitMaskAsPerPort[1] &= ~(1 << Port_ChannelToBit(GPIO_PA_EN));
|
|
//Serial.printf("PA HI: %u\n", OutputBitMaskAsPerPort[1]);
|
|
}
|
|
#endif
|
|
|
|
#ifdef GPIO_HP_EN
|
|
if (GPIO_HP_EN >= 100 && GPIO_HP_EN <= 107) {
|
|
OutputBitMaskAsPerPort[0] &= ~(1 << Port_ChannelToBit(GPIO_HP_EN));
|
|
//Serial.printf("HP LO: %u\n", OutputBitMaskAsPerPort[0]);
|
|
} else if (GPIO_HP_EN >= 108 && GPIO_HP_EN <= 115) {
|
|
OutputBitMaskAsPerPort[1] &= ~(1 << Port_ChannelToBit(GPIO_HP_EN));
|
|
//Serial.printf("HP HI: %u\n", OutputBitMaskAsPerPort[1]);
|
|
}
|
|
#endif
|
|
|
|
// Only change port-config if necessary (at least bitmask changed from base-default for one port)
|
|
if ((OutputBitMaskAsPerPort[0] != portBaseValueBitMask) || (OutputBitMaskAsPerPort[1] != portBaseValueBitMask)) {
|
|
i2cBusTwo.beginTransmission(expanderI2cAddress);
|
|
i2cBusTwo.write(0x06);
|
|
for (uint8_t i=0; i<portsToWrite; i++) {
|
|
i2cBusTwo.write(OutputBitMaskAsPerPort[i]);
|
|
//Serial.printf("Register %u - Mask: %u\n", 0x06+i, OutputBitMaskAsPerPort[i]);
|
|
}
|
|
i2cBusTwo.endTransmission();
|
|
|
|
i2cBusTwo.beginTransmission(expanderI2cAddress);
|
|
i2cBusTwo.write(0x02); // Pointer to configuration of output-channels
|
|
i2cBusTwo.write(0x00); // Set all output-channels (port0) to low as per default (channels configured as input aren't affected by this)
|
|
i2cBusTwo.write(0x00); // Set all output-channels (port1) to low as per default (channels configured as input aren't affected by this)
|
|
i2cBusTwo.endTransmission();
|
|
}
|
|
}
|
|
|
|
// Reads input from port-expander and writes output into global array
|
|
// Datasheet: https://www.nxp.com/docs/en/data-sheet/PCA9555.pdf
|
|
void Port_ExpanderHandler(void) {
|
|
// If interrupt-handling is active, only ready port-expander's register if interrupt was fired
|
|
#ifdef PE_INTERRUPT_PIN_ENABLE
|
|
if (!Port_AllowReadFromPortExpander && !Port_AllowInitReadFromPortExpander) {
|
|
//Serial.println("Interrupt false!");
|
|
return;
|
|
} else if (Port_AllowInitReadFromPortExpander) {
|
|
Port_AllowInitReadFromPortExpander = false;
|
|
} else if (Port_AllowReadFromPortExpander || Port_AllowInitReadFromPortExpander) {
|
|
//Serial.println("Interrupt true!");
|
|
Port_AllowReadFromPortExpander = false;
|
|
}
|
|
#endif
|
|
|
|
i2cBusTwo.beginTransmission(expanderI2cAddress);
|
|
for (uint8_t i = 0; i < 2; i++) {
|
|
i2cBusTwo.write(0x00 + i); // Pointer to input-register...
|
|
i2cBusTwo.endTransmission();
|
|
i2cBusTwo.requestFrom(expanderI2cAddress, 1u); // ...and read its byte
|
|
|
|
if (i2cBusTwo.available()) {
|
|
Port_ExpanderPortsInputChannelStatus[i] = i2cBusTwo.read();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Make sure ports are read finally at shutdown in order to clear any active IRQs that could cause re-wakeup immediately
|
|
void Port_Exit(void) {
|
|
i2cBusTwo.beginTransmission(expanderI2cAddress);
|
|
for (uint8_t i = 0; i < 2; i++) {
|
|
i2cBusTwo.write(0x00 + i); // Pointer to input-register...
|
|
i2cBusTwo.endTransmission();
|
|
i2cBusTwo.requestFrom(expanderI2cAddress, 1u); // ...and read its byte
|
|
|
|
if (i2cBusTwo.available()) {
|
|
Port_ExpanderPortsInputChannelStatus[i] = i2cBusTwo.read();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Tests if port-expander can be detected at address configured
|
|
void Port_Test(void) {
|
|
i2cBusTwo.beginTransmission(expanderI2cAddress);
|
|
i2cBusTwo.write(0x02);
|
|
if (!i2cBusTwo.endTransmission()) {
|
|
Log_Println(portExpanderFound, LOGLEVEL_NOTICE);
|
|
} else {
|
|
Log_Println(portExpanderNotFound, LOGLEVEL_ERROR);
|
|
}
|
|
}
|
|
|
|
#ifdef PE_INTERRUPT_PIN_ENABLE
|
|
void IRAM_ATTR PORT_ExpanderISR(void) {
|
|
Port_AllowReadFromPortExpander = true;
|
|
}
|
|
#endif
|
|
#endif
|