Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
MCP23017Gpio.hxx
Go to the documentation of this file.
1
36#ifndef _FREERTOS_DRIVERS_COMMON_MCP23017GPIO_HXX_
37#define _FREERTOS_DRIVERS_COMMON_MCP23017GPIO_HXX_
38
39#include <fcntl.h>
40#include <unistd.h>
41#include "i2c.h"
42#include "i2c-dev.h"
43
44#include "executor/Executor.hxx"
45
46class MCP23017 : private Atomic
47{
48public:
49 // 50 Hz polling
50 static constexpr long long POLLING_DELAY = MSEC_TO_NSEC(20);
51
56 MCP23017(ExecutorBase *executor, bool a2, bool a1, bool a0)
57 : executor_(executor)
58 {
59 unsigned num = 0;
60 if (a2)
61 {
62 num |= 0b100;
63 }
64 if (a1)
65 {
66 num |= 0b010;
67 }
68 if (a0)
69 {
70 num |= 0b001;
71 }
73 }
74
77 void init(const char *i2c_path)
78 {
79 int fd = ::open(i2c_path, O_RDWR);
80 HASSERT(fd >= 0);
81 init(fd);
82 }
83
86 void init(int i2c_fd)
87 {
88 fd_ = i2c_fd;
89
90 dir_[0] = dir_[1] = 0xff;
91 dirty_ = DIRTY_DIR;
92 update_out();
93 update_in();
94
95 timer_.start(POLLING_DELAY);
96 }
97
98 enum
99 {
100 PORTA = 0,
101 PORTB = 1,
102 };
103
104private:
106
107 friend class MCP23017Gpio;
108
109 enum Registers
110 {
111 IODIRA = 0x0,
112 IODIRB = 0x1,
113 IPOLA = 0x2,
114 IPOLB = 0x3,
115 GPINTENA = 0x4,
116 GPINTENB = 0x5,
117 DEFVALA = 0x6,
118 DEFVALB = 0x7,
119 INTCONA = 0x8,
120 INTCONB = 0x9,
121 IOCON = 0xA,
122 GPPUA = 0xC,
123 GPPUB = 0xD,
124 INTFA = 0xE,
125 INTFB = 0xF,
126 INTCAPA = 0x10,
127 INTCAPB = 0x11,
128 RGPIOA = 0x12,
129 RGPIOB = 0x13,
130 OLATA = 0x14,
131 OLATB = 0x15
132 };
133
134 enum
135 {
137 BASE_ADDRESS = 0b0100000,
138
139 DIRTY_DIR = 1,
140 DIRTY_LAT = 2,
141 };
142
145 {
146 bool push_dir = false;
147 bool push_lat = false;
148 uint8_t d[2];
149 uint8_t l[2];
150 {
151 AtomicHolder h(this);
152 d[0] = dir_[0];
153 d[1] = dir_[1];
154 l[0] = lat_[0];
155 l[1] = lat_[1];
156 if (dirty_ & DIRTY_DIR)
157 {
158 dirty_ &= ~DIRTY_DIR;
159 push_dir = true;
160 }
161 if (dirty_ & DIRTY_LAT)
162 {
163 dirty_ &= ~DIRTY_LAT;
164 push_lat = true;
165 }
166 }
167 if (push_dir)
168 {
169 register_write(IODIRA, d, 2);
170 }
171 if (push_lat)
172 {
173 register_write(OLATA, l, 2);
174 }
175 }
176
179 {
180 register_read(RGPIOA, gpio_, 2);
181 }
182
188 void register_write(uint8_t reg, const uint8_t *data, uint16_t len)
189 {
190 uint8_t dat[3];
191 dat[0] = reg;
192 dat[1] = data[0];
193 if (len > 1)
194 {
195 dat[2] = data[1];
196 }
197 HASSERT(len <= 2);
198 struct i2c_msg msgs[] = {{.addr = i2cAddress_,
199 .flags = 0,
200 .len = (uint16_t)(len + 1),
201 .buf = dat}};
202
203 struct i2c_rdwr_ioctl_data ioctl_data = {
204 .msgs = msgs, .nmsgs = ARRAYSIZE(msgs)};
205
206 ::ioctl(fd_, I2C_RDWR, &ioctl_data);
207 }
208
214 void register_read(uint8_t reg, uint8_t *data, uint16_t len)
215 {
216 struct i2c_msg msgs[] = {
217 {.addr = i2cAddress_, .flags = 0, .len = 1, .buf = &reg},
218 {.addr = i2cAddress_, .flags = I2C_M_RD, .len = len, .buf = data}};
219
220 struct i2c_rdwr_ioctl_data ioctl_data = {
221 .msgs = msgs, .nmsgs = ARRAYSIZE(msgs)};
222
223 ::ioctl(fd_, I2C_RDWR, &ioctl_data);
224 }
225
228 class RefreshTimer : public ::Timer
229 {
230 public:
231 RefreshTimer(MCP23017 *parent)
232 : ::Timer(parent->executor_->active_timers())
233 , parent_(parent)
234 {
235 }
236
237 long long timeout() override
238 {
239 parent_->update_out();
240 parent_->update_in();
241 return RESTART;
242 };
243
244 private:
245 MCP23017 *parent_;
246 };
247
253 int fd_;
254
256 uint8_t dir_[2];
258 uint8_t lat_[2];
260 uint8_t gpio_[2];
261
263 uint8_t i2cAddress_;
264
266 uint8_t dirty_ = 0;
267};
268
269class MCP23017Gpio : public Gpio
270{
271public:
272 constexpr MCP23017Gpio(MCP23017 *const parent, unsigned port, unsigned pin)
273 : parent_(parent)
274 , port_(port)
275 , pinBit_(1 << pin)
276 {
277 }
278
279 void set() const override
280 {
281 write(VHIGH);
282 }
283
284 void clr() const override
285 {
286 write(VLOW);
287 }
288
289 void write(Value new_state) const override
290 {
291 AtomicHolder h(parent_);
292 bool old_state = !!(parent_->lat_[port_] & pinBit_);
293 if (new_state != old_state)
294 {
295 if (new_state)
296 {
297 parent_->lat_[port_] |= pinBit_;
298 }
299 else
300 {
301 parent_->lat_[port_] &= ~pinBit_;
302 }
303 parent_->dirty_ |= parent_->DIRTY_LAT;
304 }
305 }
306
307 Value read() const override
308 {
309 if (parent_->dir_[port_] & pinBit_)
310 {
311 // input. Use gpio_.
312 return (parent_->gpio_[port_] & pinBit_) ? VHIGH : VLOW;
313 }
314 else
315 {
316 // output. Use lat_.
317 return (parent_->lat_[port_] & pinBit_) ? VHIGH : VLOW;
318 }
319 }
320
321 void set_direction(Direction dir) const override
322 {
323 AtomicHolder h(parent_);
324 uint8_t desired = (dir == Direction::DOUTPUT) ? 0 : pinBit_;
325 if (desired != ((parent_->dir_[port_] & pinBit_)))
326 {
327 parent_->dir_[port_] =
328 (parent_->dir_[port_] & (~pinBit_)) | desired;
329 parent_->dirty_ |= parent_->DIRTY_DIR;
330 }
331 }
332
333 Direction direction() const override
334 {
335 if (parent_->dir_[port_] & pinBit_)
336 {
337 return Direction::DINPUT;
338 }
339 else
340 {
341 return Direction::DOUTPUT;
342 }
343 }
344
345private:
347
348 MCP23017 *const parent_;
350 const uint8_t port_;
352 const uint8_t pinBit_;
353};
354
355#endif // _FREERTOS_DRIVERS_COMMON_MCP23017GPIO_HXX_
int ioctl(int fd, unsigned long int key,...)
Request and ioctl transaction.
Definition Fileio.cxx:452
See OSMutexLock in os/OS.hxx.
Definition Atomic.hxx:153
Lightweight locking class for protecting small critical sections.
Definition Atomic.hxx:130
This class implements an execution of tasks pulled off an input queue.
Definition Executor.hxx:64
ActiveTimers * active_timers()
Definition Executor.hxx:144
OS-independent abstraction for GPIO.
Definition Gpio.hxx:43
Value
Defines the options for GPIO level.
Definition Gpio.hxx:62
Direction
Defines the options for GPIO direction.
Definition Gpio.hxx:73
Direction direction() const override
Gets the GPIO direction.
const uint8_t pinBit_
one bit that denotes the output pin.
void clr() const override
Clears the GPIO output pin to low.
Value read() const override
Retrieves the current Value of a GPIO input pin.
void set() const override
Sets the GPIO output pin to high.
void write(Value new_state) const override
Writes a GPIO output pin (set or clear to a specific state).
const uint8_t port_
0 or 1 for portA/B
void set_direction(Direction dir) const override
Sets the GPIO direction.
This timer runs in the parent executor and upon every timeout it executes the update / polling of the...
long long timeout() override
Clients of timer should override this function.
uint8_t lat_[2]
Shadow of the latch registers.
int fd_
I2C port file descriptor.
uint8_t gpio_[2]
Shadow of the input registers.
void update_in()
Updates input registers.
MCP23017(ExecutorBase *executor, bool a2, bool a1, bool a0)
Constructor.
void update_out()
Updates the output direction and latch registers if any of it is dirty.
@ BASE_ADDRESS
I2C address of the first device.
uint8_t dir_[2]
Shadow of the direction registers.
void register_read(uint8_t reg, uint8_t *data, uint16_t len)
Reads one or more (sequential) registers in the MCP23017.
uint8_t i2cAddress_
Address of this particular device on the I2C port.
uint8_t dirty_
Bit mask of registers that need updating.
ExecutorBase * executor_
Executor. We will be blocking this for the I2C IO.
void init(int i2c_fd)
Initializes the device.
RefreshTimer timer_
Timer instance to schedule work on the executor.
void init(const char *i2c_path)
Initializes the device.
void register_write(uint8_t reg, const uint8_t *data, uint16_t len)
Writes one or more (sequential) registers in the MCP23017.
A timer that can schedule itself to run on an executor at specified times in the future.
Definition Timer.hxx:134
@ RESTART
Restart the timer with existing period.
Definition Timer.hxx:162
void start(long long period=-1)
Starts a timer.
Definition Timer.hxx:185
#define ARRAYSIZE(a)
Returns the number of elements in a statically defined array (of static size)
Definition macros.h:185
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
Definition macros.h:138
#define DISALLOW_COPY_AND_ASSIGN(TypeName)
Removes default copy-constructor and assignment added by C++.
Definition macros.h:171
#define MSEC_TO_NSEC(_msec)
Convert a millisecond value to a nanosecond value.
Definition os.h:268