summaryrefslogtreecommitdiff
path: root/src/drivers/gpio-expander.cpp
blob: 8b2e527e79c1abdc9317d6d083b5202f13f07a57 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#include "gpio-expander.hpp"

#include "i2c.hpp"

#include <cstdint>

namespace drivers {

GpioExpander::GpioExpander() {
  ports_ = pack(kPortADefault, kPortBDefault);
  // Read and write initial values on initialisation so that we do not have a
  // strange partially-initialised state.
  // TODO: log or abort if these error; it's really bad!
  Write();
  Read();
}

GpioExpander::~GpioExpander() {}

void GpioExpander::with(std::function<void(GpioExpander&)> f) {
  f(*this);
  Write();
}

esp_err_t GpioExpander::Write() {
  i2c_cmd_handle_t handle = i2c_cmd_link_create();
  if (handle == NULL) {
    return ESP_ERR_NO_MEM;
  }

  std::pair<uint8_t, uint8_t> ports_ab = unpack(ports());

  I2CTransaction transaction;
  transaction.start()
      .write_addr(kPca8575Address, I2C_MASTER_WRITE)
      .write_ack(ports_ab.first, ports_ab.second)
      .stop();

  return transaction.Execute();
}

esp_err_t GpioExpander::Read() {
  uint8_t input_a, input_b;

  I2CTransaction transaction;
  transaction.start()
      .write_addr(kPca8575Address, I2C_MASTER_READ)
      .read(&input_a, I2C_MASTER_ACK)
      .read(&input_b, I2C_MASTER_LAST_NACK)
      .stop();

  esp_err_t ret = transaction.Execute();
  inputs_ = pack(input_a, input_b);
  return ret;
}

void GpioExpander::set_pin(ChipSelect cs, bool value) {
  set_pin((Pin)cs, value);
}

void GpioExpander::set_pin(Pin pin, bool value) {
  if (value) {
    ports_ |= (1 << pin);
  } else {
    ports_ &= ~(1 << pin);
  }
}

bool GpioExpander::get_input(Pin pin) const {
  return (inputs_ & (1 << pin)) > 0;
}

GpioExpander::SpiLock GpioExpander::AcquireSpiBus(ChipSelect cs) {
  // TODO: also spi_device_acquire_bus?
  return SpiLock(*this, cs);
}

GpioExpander::SpiLock::SpiLock(GpioExpander& gpio, ChipSelect cs)
    : lock_(gpio.cs_mutex_), gpio_(gpio), cs_(cs) {
  gpio_.with([&](auto& expander) { expander.set_pin(cs_, 0); });
}

GpioExpander::SpiLock::~SpiLock() {
  gpio_.with([&](auto& expander) { expander.set_pin(cs_, 1); });
}

}  // namespace drivers