FastArduino  v1.8
C++ library to build fast but small Arduino/AVR projects
mcp23008.h
Go to the documentation of this file.
1 // Copyright 2016-2021 Jean-Francois Poilpret
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
16 
21 #ifndef MCP23008_H
22 #define MCP23008_H
23 
24 #include "mcp230xx.h"
25 #include "../i2c_device.h"
26 
28 {
37  template<typename MANAGER>
38  class MCP23008 : public i2c::I2CDevice<MANAGER>
39  {
40  private:
42  template<typename T> using PROXY = typename PARENT::template PROXY<T>;
43  template<typename OUT, typename IN> using FUTURE = typename PARENT::template FUTURE<OUT, IN>;
44 
45  // Forward declarations needed by compiler
46  class WriteRegisterFuture;
47  struct Write3Registers;
48  class ReadRegisterFuture;
49 
50  static constexpr uint8_t compute_address(uint8_t address)
51  {
52  return uint8_t((uint8_t(BASE_ADDRESS) | uint8_t(address & 0x07U)) << 1);
53  }
54 
55  public:
63  MCP23008(MANAGER& manager, uint8_t address)
64  : PARENT{manager, compute_address(address), i2c::I2C_FAST, true} {}
65 
66  // Asynchronous API
67  //==================
79  class BeginFuture : public WriteRegisterFuture
80  {
81  public:
83  explicit BeginFuture(InterruptPolarity interrupt_polarity = InterruptPolarity::ACTIVE_HIGH)
84  : WriteRegisterFuture{IOCON, build_IOCON(interrupt_polarity == InterruptPolarity::ACTIVE_HIGH)} {}
85  BeginFuture(BeginFuture&&) = default;
86  BeginFuture& operator=(BeginFuture&&) = default;
88  };
89 
107  int begin(PROXY<BeginFuture> future)
108  {
109  return this->launch_commands(future, {write_stop()});
110  }
111 
129  class ConfigureGPIOFuture : public FUTURE<void, Write3Registers>
130  {
131  using PARENT = FUTURE<void, Write3Registers>;
132  public:
134  ConfigureGPIOFuture(uint8_t direction, uint8_t pullup = 0, uint8_t polarity = 0)
135  : PARENT{Write3Registers{IODIR, direction, IPOL, polarity, GPPU, pullup}} {}
137  ConfigureGPIOFuture& operator=(ConfigureGPIOFuture&&) = default;
139  };
140 
158  int configure_gpio(PROXY<ConfigureGPIOFuture> future)
159  {
160  return this->launch_commands(future, {write_stop(2), write_stop(2), write_stop(2)});
161  }
162 
181  class ConfigureInterruptsFuture : public FUTURE<void, Write3Registers>
182  {
183  using PARENT = FUTURE<void, Write3Registers>;
184  public:
186  ConfigureInterruptsFuture(uint8_t int_pins, uint8_t ref = 0, uint8_t compare_ref = 0)
187  : PARENT{Write3Registers{GPINTEN, int_pins, DEFVAL, ref, INTCON, compare_ref}} {}
191  };
192 
210  int configure_interrupts(PROXY<ConfigureInterruptsFuture> future)
211  {
212  return this->launch_commands(future, {write_stop(2), write_stop(2), write_stop(2)});
213  }
214 
226  class SetValuesFuture : public WriteRegisterFuture
227  {
228  public:
230  explicit SetValuesFuture(uint8_t value) : WriteRegisterFuture{GPIO, value} {}
231  SetValuesFuture(SetValuesFuture&&) = default;
232  SetValuesFuture& operator=(SetValuesFuture&&) = default;
234  };
235 
253  int values(PROXY<SetValuesFuture> future)
254  {
255  return this->launch_commands(future, {write_stop()});
256  }
257 
266  class GetValuesFuture : public ReadRegisterFuture
267  {
268  public:
270  GetValuesFuture() : ReadRegisterFuture{GPIO} {}
271  GetValuesFuture(GetValuesFuture&&) = default;
272  GetValuesFuture& operator=(GetValuesFuture&&) = default;
274  };
275 
293  int values(PROXY<GetValuesFuture> future)
294  {
295  return this->launch_commands(future, {this->write(), this->read()});
296  }
297 
307  class InterruptFlagsFuture : public ReadRegisterFuture
308  {
309  public:
311  InterruptFlagsFuture() : ReadRegisterFuture{INTF} {}
313  InterruptFlagsFuture& operator=(InterruptFlagsFuture&&) = default;
315  };
316 
335  int interrupt_flags(PROXY<InterruptFlagsFuture> future)
336  {
337  return this->launch_commands(future, {this->write(), this->read()});
338  }
339 
349  class CapturedValuesFuture : public ReadRegisterFuture
350  {
351  public:
353  CapturedValuesFuture() : ReadRegisterFuture{INTCAP} {}
355  CapturedValuesFuture& operator=(CapturedValuesFuture&&) = default;
357  };
358 
379  int captured_values(PROXY<CapturedValuesFuture> future)
380  {
381  return this->launch_commands(future, {this->write(), this->read()});
382  }
383 
384  // Synchronous API
385  //=================
398  {
399  BeginFuture future{interrupt_polarity};
400  if (begin(PARENT::make_proxy(future)) != 0) return false;
401  return (future.await() == future::FutureStatus::READY);
402  }
403 
421  bool configure_gpio(uint8_t direction, uint8_t pullup = 0, uint8_t polarity = 0)
422  {
423  ConfigureGPIOFuture future{direction, pullup, polarity};
424  if (configure_gpio(PARENT::make_proxy(future)) != 0) return false;
425  return (future.await() == future::FutureStatus::READY);
426  }
427 
445  bool configure_interrupts(uint8_t int_pins, uint8_t ref = 0, uint8_t compare_ref = 0)
446  {
447  ConfigureInterruptsFuture future{int_pins, ref, compare_ref};
448  if (configure_interrupts(PARENT::make_proxy(future)) != 0) return false;
449  return (future.await() == future::FutureStatus::READY);
450  }
451 
463  bool values(uint8_t value)
464  {
465  SetValuesFuture future{value};
466  if (values(PARENT::make_proxy(future)) != 0) return false;
467  return (future.await() == future::FutureStatus::READY);
468  }
469 
479  uint8_t values()
480  {
482  if (values(PARENT::make_proxy(future)) != 0) return 0;
483  uint8_t value;
484  if (future.get(value))
485  return value;
486  else
487  return 0;
488  }
489 
500  uint8_t interrupt_flags()
501  {
503  if (interrupt_flags(PARENT::make_proxy(future)) != 0) return 0;
504  uint8_t value;
505  if (future.get(value))
506  return value;
507  else
508  return 0;
509  }
510 
523  uint8_t captured_values()
524  {
526  if (captured_values(PARENT::make_proxy(future)) != 0) return 0;
527  uint8_t value;
528  if (future.get(value))
529  return value;
530  else
531  return 0;
532  }
533 
534  private:
535  // Base address of the device (actual address can be in 0x20-0x27)
536  static constexpr const uint8_t BASE_ADDRESS = 0x20;
537 
538  // All registers addresses
539  static constexpr const uint8_t IODIR = 0x00;
540  static constexpr const uint8_t IPOL = 0x01;
541 
542  static constexpr const uint8_t GPINTEN = 0x02;
543  static constexpr const uint8_t DEFVAL = 0x03;
544  static constexpr const uint8_t INTCON = 0x04;
545 
546  static constexpr const uint8_t IOCON = 0x05;
547 
548  static constexpr const uint8_t GPPU = 0x06;
549 
550  static constexpr const uint8_t INTF = 0x07;
551  static constexpr const uint8_t INTCAP = 0x08;
552 
553  static constexpr const uint8_t GPIO = 0x09;
554  static constexpr const uint8_t OLAT = 0x0A;
555 
556  // IOCON bits (not all are used in this implementation)
557  static constexpr const uint8_t IOCON_SEQOP = bits::BV8(5);
558  static constexpr const uint8_t IOCON_DISSLW = bits::BV8(4);
559  static constexpr const uint8_t IOCON_HAEN = bits::BV8(3);
560  static constexpr const uint8_t IOCON_ODR = bits::BV8(2);
561  static constexpr const uint8_t IOCON_INTPOL = bits::BV8(1);
562 
563  i2c::I2CLightCommand write_stop(uint8_t byte_count = 0) const
564  {
565  return this->write(byte_count, false, true);
566  }
567 
568  struct Write3Registers
569  {
570  Write3Registers(uint8_t address1, uint8_t value1,
571  uint8_t address2, uint8_t value2,
572  uint8_t address3, uint8_t value3)
573  : address1_{address1}, value1_{value1},
574  address2_{address2}, value2_{value2},
575  address3_{address3}, value3_{value3} {}
576 
577  uint8_t address1_;
578  uint8_t value1_;
579  uint8_t address2_;
580  uint8_t value2_;
581  uint8_t address3_;
582  uint8_t value3_;
583  };
584 
585  struct WriteRegister
586  {
587  WriteRegister(uint8_t address, uint8_t value) : address_{address}, value_{value} {}
588 
589  uint8_t address_;
590  uint8_t value_;
591  };
592 
593  class WriteRegisterFuture : public FUTURE<void, WriteRegister>
594  {
595  using PARENT = FUTURE<void, WriteRegister>;
596  protected:
597  WriteRegisterFuture(uint8_t address, uint8_t value) : PARENT{WriteRegister{address, value}} {}
598  WriteRegisterFuture(WriteRegisterFuture&&) = default;
599  WriteRegisterFuture& operator=(WriteRegisterFuture&&) = default;
600  };
601 
602  class ReadRegisterFuture : public FUTURE<uint8_t, uint8_t>
603  {
604  using PARENT = FUTURE<uint8_t, uint8_t>;
605  protected:
606  explicit ReadRegisterFuture(uint8_t address) : PARENT{address} {}
607  ReadRegisterFuture(ReadRegisterFuture&&) = default;
608  ReadRegisterFuture& operator=(ReadRegisterFuture&&) = default;
609  };
610 
611  static constexpr uint8_t build_IOCON(bool int_polarity)
612  {
613  return (int_polarity ? IOCON_INTPOL : 0);
614  }
615  };
616 }
617 
618 #endif /* MCP23008_H */
619 
devices::mcp230xx::MCP23008::SetValuesFuture
Create a future to be used by asynchronous method values(SetValuesFuture&).
Definition: mcp23008.h:227
i2c::I2CDevice< MANAGER >::read
static constexpr I2CLightCommand read(uint8_t read_count=0, bool finish_future=false, bool stop=false)
Build a read I2CLightCommand that can be later pushed to the I2C Manager for proper handling.
Definition: i2c_device.h:183
future
Contains the API around Future implementation.
Definition: future.cpp:18
i2c::I2CDevice
Base class for all I2C devices.
Definition: i2c_device.h:86
devices::mcp230xx::MCP23008::interrupt_flags
int interrupt_flags(PROXY< InterruptFlagsFuture > future)
Get the pins that generated the latest interrupt on the port of the MCP23008 chip.
Definition: mcp23008.h:335
i2c::I2CDevice< MANAGER >::write
static constexpr I2CLightCommand write(uint8_t write_count=0, bool finish_future=false, bool stop=false)
Build a write I2CLightCommand that can be later pushed to the I2C Manager for proper handling.
Definition: i2c_device.h:212
devices::mcp230xx::MCP23008::GetValuesFuture
Create a future to be used by asynchronous method values(GetValuesFuture&).
Definition: mcp23008.h:267
devices::mcp230xx::MCP23008::values
uint8_t values()
Get levels of pins on the port of this MCP23008 chip.
Definition: mcp23008.h:479
future::FutureStatus::READY
@ READY
The status of a Future once its output value has been fully set by a provider.
i2c::I2CDevice< MANAGER >::MANAGER
MANAGER MANAGER
the type of I2C Manager that can handle this device.
Definition: i2c_device.h:89
devices::mcp230xx::MCP23008::configure_gpio
bool configure_gpio(uint8_t direction, uint8_t pullup=0, uint8_t polarity=0)
Configure GPIO on the port of this MCP23008 chip.
Definition: mcp23008.h:421
bits::BV8
static constexpr uint8_t BV8(uint8_t bit)
Create a uint8_t bitmask for the given bit number.
Definition: bits.h:41
i2c::I2CDevice< MANAGER >::make_proxy
static PROXY< T > make_proxy(const T &target)
Create a PROXY from target.
Definition: i2c_device.h:418
devices::mcp230xx::MCP23008::values
int values(PROXY< GetValuesFuture > future)
Get levels of pins on the port of this MCP23008 chip.
Definition: mcp23008.h:293
devices::mcp230xx::MCP23008::CapturedValuesFuture
Create a future to be used by asynchronous method captured_values(CapturedValuesFuture&).
Definition: mcp23008.h:350
devices::mcp230xx::MCP23008::ConfigureInterruptsFuture
Create a future to be used by asynchronous method configure_interrupts(ConfigureInterruptsFuture&).
Definition: mcp23008.h:182
devices::mcp230xx::MCP23008::ConfigureGPIOFuture
Create a future to be used by asynchronous method configure_gpio(ConfigureGPIOFuture&).
Definition: mcp23008.h:130
devices::mcp230xx::MCP23008::captured_values
uint8_t captured_values()
Get captured levels, at the time an interrupt was triggered, of pins on the port of this MCP23008 chi...
Definition: mcp23008.h:523
devices::mcp230xx::MCP23008::interrupt_flags
uint8_t interrupt_flags()
Get the pins that generated the latest interrupt on the port of the MCP23008 chip.
Definition: mcp23008.h:500
i2c::I2CDevice< MANAGER >::launch_commands
int launch_commands(PROXY< ABSTRACT_FUTURE > proxy, utils::range< I2CLightCommand > commands)
Launch execution (asynchronously or synchronously, depending on MANAGER) of a chain of I2CLightComman...
Definition: i2c_device.h:255
devices::mcp230xx::MCP23008::configure_gpio
int configure_gpio(PROXY< ConfigureGPIOFuture > future)
Configure GPIO on the port of this MCP23008 chip.
Definition: mcp23008.h:158
devices::mcp230xx::MCP23008::begin
int begin(PROXY< BeginFuture > future)
Initialize the chip before operation.
Definition: mcp23008.h:107
devices::mcp230xx::MCP23008::values
bool values(uint8_t value)
Set output levels of output pins on the port of this MCP23008 chip.
Definition: mcp23008.h:463
devices::mcp230xx::MCP23008::values
int values(PROXY< SetValuesFuture > future)
Set output levels of output pins on the port of this MCP23008 chip.
Definition: mcp23008.h:253
devices::mcp230xx::MCP23008::BeginFuture
Create a future to be used by asynchronous method begin(BeginFuture&).
Definition: mcp23008.h:80
devices::mcp230xx::MCP23008::configure_interrupts
bool configure_interrupts(uint8_t int_pins, uint8_t ref=0, uint8_t compare_ref=0)
Configure interrupts on the port of this MCP23008 chip.
Definition: mcp23008.h:445
mcp230xx.h
API to handle the MCP23008/23017 chips (8 & 16-Bit I/O Expanders with I2C interface).
devices::mcp230xx::MCP23008::InterruptFlagsFuture
Create a future to be used by asynchronous method interrupt_flags(InterruptFlagsFuture&).
Definition: mcp23008.h:308
devices::mcp230xx::MCP23008::captured_values
int captured_values(PROXY< CapturedValuesFuture > future)
Get captured levels, at the time an interrupt was triggered, of pins on the port of this MCP23008 chi...
Definition: mcp23008.h:379
devices::mcp230xx::MCP23008::configure_interrupts
int configure_interrupts(PROXY< ConfigureInterruptsFuture > future)
Configure interrupts on the port of this MCP23008 chip.
Definition: mcp23008.h:210
devices::mcp230xx::InterruptPolarity
InterruptPolarity
The polarity of the MCP23008/MCP23017 INT pins.
Definition: mcp230xx.h:44
devices::mcp230xx::InterruptPolarity::ACTIVE_LOW
@ ACTIVE_LOW
The INT pins shall be active low, ie they are high by default, and changed to low when an interrupt i...
devices::mcp230xx::MCP23008::begin
bool begin(InterruptPolarity interrupt_polarity=InterruptPolarity::ACTIVE_HIGH)
Initialize the chip before operation.
Definition: mcp23008.h:397
devices::mcp230xx::MCP23008::MCP23008
MCP23008(MANAGER &manager, uint8_t address)
Create a new device driver for an MCP23008 chip.
Definition: mcp23008.h:63
devices::mcp230xx::MCP23008
I2C device driver for Microchip MCP23008 support.
Definition: mcp23008.h:39
i2c::I2CLightCommand
Light atomic I2C command as prepared by an I2C device.
Definition: i2c_handler_common.h:176
devices::mcp230xx
Defines the API for MCP23008/MCP23017 chips support.
Definition: mcp23008.h:28
i2c::I2C_FAST
static constexpr Mode I2C_FAST
Constant determining that best supported I2C mode for an I2CDevice is FAST (400kHz).
Definition: i2c_device.h:49