/* MCP23S08 MODULE Copyright (C) 2020 by Eddi De Pieri Adapted from https://github.com/kmpelectronics/Arduino Copyright (C) 2016 Plamen Kovandjiev & Dimitar Antonov (ref. https://github.com/kmpelectronics/Arduino/blob/master/ProDinoWiFiEsp/src/PRODINoESP8266/src/KMPDinoWiFiESP.cpp) */ #include "espurna.h" #if MCP23S08_SUPPORT #include "mcp23s08.h" #include "mcp23s08_pin.h" #include #include // TODO: check if this needed for SPI operation #define MCP23S08_CS_PIN 15 // Known commands #define READ_CMD 0x41 #define WRITE_CMD 0x40 // Registers #define IODIR 0x00 #define IPOL 0x01 #define GPINTEN 0x02 #define DEFVAL 0x03 #define INTCON 0x04 #define IOCON 0x05 #define GPPU 0x06 #define INTF 0x07 #define INTCAP 0x08 #define GPIO 0x09 #define OLAT 0x0A static uint8_t _mcp23s08TxData[16] __attribute__((aligned(4))); static uint8_t _mcp23s08RxData[16] __attribute__((aligned(4))); namespace { class GpioMcp23s08 : public GpioBase { public: constexpr static size_t Pins { 8ul }; using Pin = McpGpioPin; using Mask = std::bitset; const char* id() const { return "mcp23s08"; } size_t pins() const { return Pins; } bool lock(unsigned char index) const override { return _lock[index]; } void lock(unsigned char index, bool value) override { _lock.set(index, value); } bool valid(unsigned char index) const override { return (index < Pins); } BasePinPtr pin(unsigned char index) override { return std::make_unique(index); } private: Mask _lock; }; } // namespace void MCP23S08Setup() { DEBUG_MSG_P(PSTR("[MCP23S08] Initialize SPI bus\n")); // Expander SPI settings SPI.begin(); SPI.setHwCs(true); SPI.setFrequency(1000000); SPI.setDataMode(SPI_MODE0); pinMode(MCP23S08_CS_PIN, OUTPUT); digitalWrite(MCP23S08_CS_PIN, HIGH); } /** * @brief Set a expander MCP23S08 the pin direction. * * @param pinNumber Pin number for set. * @param mode direction mode. 0 - INPUT, 1 - OUTPUT. * * @return void */ void MCP23S08SetDirection(uint8_t pinNumber, uint8_t mode) { uint8_t registerData = MCP23S08ReadRegister(IODIR); if (INPUT == mode) { registerData |= (1 << pinNumber); } else { registerData &= ~(1 << pinNumber); } MCP23S08WriteRegister(IODIR, registerData); } /** * @brief Read an expander MCP23S08 a register. * * @param address A register address. * * @return The data from the register. */ uint8_t MCP23S08ReadRegister(uint8_t address) { _mcp23s08TxData[0] = READ_CMD; _mcp23s08TxData[1] = address; digitalWrite(MCP23S08_CS_PIN, LOW); SPI.transferBytes(_mcp23s08TxData, _mcp23s08RxData, 3); digitalWrite(MCP23S08_CS_PIN, HIGH); return _mcp23s08RxData[2]; } /** * @brief Write data in expander MCP23S08 register. * * @param address A register address. * @param data A byte for write. * * @return void. */ void MCP23S08WriteRegister(uint8_t address, uint8_t data) { _mcp23s08TxData[0] = WRITE_CMD; _mcp23s08TxData[1] = address; _mcp23s08TxData[2] = data; digitalWrite(MCP23S08_CS_PIN, LOW); SPI.transferBytes(_mcp23s08TxData, _mcp23s08RxData, 3); digitalWrite(MCP23S08_CS_PIN, HIGH); } /** * @brief Set expander MCP23S08 pin state. * * @param pinNumber The number of pin to be set. * @param state The pin state, true - 1, false - 0. * * @return void */ void MCP23S08SetPin(uint8_t pinNumber, bool state) { uint8_t registerData = MCP23S08ReadRegister(OLAT); if (state) { registerData |= (1 << pinNumber); } else { registerData &= ~(1 << pinNumber); } MCP23S08WriteRegister(OLAT, registerData); } /** * @brief Get MCP23S08 pin state. * * @param pinNumber The number of pin to get. * * @return State true - 1, false - 0. */ bool MCP23S08GetPin(uint8_t pinNumber) { uint8_t registerData = MCP23S08ReadRegister(GPIO); return registerData & (1 << pinNumber); } /** * @brief Ensure pin number is valid. * * @param pinNumber The number of pin to be get. * * @return State true - 1, false - 0. */ bool mcpGpioValid(unsigned char gpio) { return gpio < McpGpioPins; } GpioBase& mcp23s08Gpio() { static GpioMcp23s08 gpio; return gpio; } #endif // MCP23S08_SUPPORT