YAHAL
Yet Another Hardware Abstraction Library
Loading...
Searching...
No Matches
mcp23s17_drv.cpp
1/*
2 * mcp23s17_drv.cpp
3 *
4 * Created on: 28.02.2016
5 * Author: Andreas Terstegge
6 */
7
8#include <cassert>
9#include "mcp23s17_drv.h"
10
11namespace MCP23S17 {
12// Chip register addresses
13// -----------------------
14// IODIR_REG : I/O direction: 0 = output, 1 = input
15// IPOL_REG : Input Polarity: 0 = normal, 1 = inverted
16// GPINTEN_REG : interrupt enable: 0 = diabled, 1 = enabled
17// DEFVAL_REG : Default Value (for interrupt generation)
18// INTCON_REG : interrupt control: 0 = int on change, 1 = int on !default
19// IOCON_REG : I/O Expander configuration (see datasheet)
20// GPPU_REG : GPIO Pull-Up resistor: 0 = disabled, 1 = enabled
21// INTF_REG : interrupt flag: 0 = no interrupt pending, 1 = int pending
22// INTCAP_REG : interrupt captured value: State of port when int occured
23// GPIO_REG : GPIO Port: Read/Write value to port
24// OLAT_REG : Output latch: (possible) state of GPIO output pins
25 static const uint8_t IODIR_REG = 0x00;
26// static const uint8_t IPOL_REG = 0x02;
27 static const uint8_t GPINTEN_REG = 0x04;
28 static const uint8_t DEFVAL_REG = 0x06;
29 static const uint8_t INTCON_REG = 0x08;
30 static const uint8_t IOCON_REG = 0x0A;
31 static const uint8_t GPPU_REG = 0x0C;
32 static const uint8_t INTF_REG = 0x0E;
33 static const uint8_t INTCAP_REG = 0x10;
34 static const uint8_t GPIO_REG = 0x12;
35 static const uint8_t OLAT_REG = 0x14;
36}
37
38mcp23s17_drv::mcp23s17_drv(spi_interface &hw, uint8_t spi_addr)
39 : _spi(hw), _spi_addr(spi_addr) {
40 // Read current register values for caching
41 _iodir = readRegister(MCP23S17::IODIR_REG);
42 _gppu = readRegister(MCP23S17::GPPU_REG);
43 _olat = readRegister(MCP23S17::OLAT_REG);
44 _gpinten = readRegister(MCP23S17::GPINTEN_REG);
45 _defval = readRegister(MCP23S17::DEFVAL_REG);
46 _intcon = readRegister(MCP23S17::INTCON_REG);
47 // bank = 0, int mirror, enable sequential operation,
48 // enable HW addr lines, interrupt active high
49 writeRegister(MCP23S17::IOCON_REG, 0x4a4a);
50}
51
52void mcp23s17_drv::gpioMode(uint16_t mode) {
53 uint16_t mask = (1 << (_port * 8 + _pin));
54 uint16_t iodir_old = _iodir;
55 uint16_t gppu_old = _gppu;
56 uint16_t olat_old = _olat;
57
58 switch (mode & ~GPIO::INIT_HIGH & ~GPIO::INIT_LOW) {
59 case GPIO::INPUT: {
60 _iodir |= mask;
61 _gppu &= ~mask;
62 break;
63 }
64 case GPIO::INPUT | GPIO::PULLUP: {
65 _iodir |= mask;
66 _gppu |= mask;
67 break;
68 }
69 case GPIO::OUTPUT: {
70 _iodir &= ~mask;
71 break;
72 }
73 default: {
74 assert(false);
75 }
76 }
77
78 if (mode & GPIO::INIT_HIGH) _olat |= mask;
79 if (mode & GPIO::INIT_LOW) _olat &= ~mask;
80
81 // write back register values if necessary
82 if (iodir_old != _iodir)
83 writeRegister(MCP23S17::IODIR_REG, _iodir);
84 if (gppu_old != _gppu)
85 writeRegister(MCP23S17::GPPU_REG, _gppu);
86 if (olat_old != _olat)
87 writeRegister(MCP23S17::OLAT_REG, _olat);
88}
89
90bool mcp23s17_drv::gpioRead() {
91 uint16_t mask = (1 << (_port * 8 + _pin));
92 return readRegister(MCP23S17::GPIO_REG) & mask ? true : false;
93}
94
95void mcp23s17_drv::gpioWrite(bool value) {
96 uint16_t mask = (1 << (_port * 8 + _pin));
97 uint16_t olat_old = _olat;
98 if (value) {
99 _olat |= mask;
100 } else {
101 _olat &= ~mask;
102 }
103 if (olat_old != _olat)
104 writeRegister(MCP23S17::OLAT_REG, _olat);
105}
106
107void mcp23s17_drv::attachInterrupt(void (*handler)(uint16_t gpio),
108 uint16_t mode) {
109 uint16_t mask = (1 << (_port * 8 + _pin));
110 uint16_t defvalold = _defval;
111 uint16_t intconold = _intcon;
112
113 switch (mode) {
114 case GPIO::RISING:
115 case GPIO::FALLING:
116 case GPIO::RISING | GPIO::FALLING: {
117 _intcon &= ~mask;
118 break;
119 }
120 case GPIO::LEVEL_HIGH: {
121 _defval &= ~mask; // default value is 0
122 _intcon |= mask; // compare with defval
123 break;
124 }
125 case GPIO::LEVEL_LOW: {
126 _defval |= mask; // default value is 1
127 _intcon |= mask; // compare with defval
128 break;
129 }
130 }
131
132 // store handler data
133 intHandler[_port][_pin] = handler;
134 intMode[_port][_pin] = mode;
135 // write back register values if necessary
136 if (defvalold != _defval)
137 writeRegister(MCP23S17::DEFVAL_REG, _defval);
138 if (intconold != _intcon)
139 writeRegister(MCP23S17::INTCON_REG, _intcon);
140 // finally enable the interrupt
141 enableInterrupt();
142}
143
144void mcp23s17_drv::detachInterrupt() {
145 disableInterrupt();
146 intHandler[_port][_pin] = 0;
147 intMode[_port][_pin] = 0;
148}
149
150void mcp23s17_drv::enableInterrupt() {
151 uint16_t mask = (1 << (_port * 8 + _pin));
152 uint16_t gpintenold = _gpinten;
153 _gpinten |= mask;
154 if (gpintenold != _gpinten)
155 writeRegister(MCP23S17::GPINTEN_REG, _gpinten);
156}
157
158void mcp23s17_drv::disableInterrupt() {
159 uint16_t mask = (1 << (_port * 8 + _pin));
160 uint16_t gpintenold = _gpinten;
161 _gpinten &= ~mask;
162 if (gpintenold != _gpinten)
163 writeRegister(MCP23S17::GPINTEN_REG, _gpinten);
164}
165
166void mcp23s17_drv::handleInterrupt() {
167 // read interrupt status and clear interrupt
168 uint16_t intFlags = readRegister(MCP23S17::INTF_REG);
169 uint16_t intValue = readRegister(MCP23S17::INTCAP_REG);
170 uint16_t mask = 0x0001;
171 for (uint8_t i = 0; i < 16; i++) {
172 if (intFlags & mask) {
173 uint8_t port = i / 8;
174 uint8_t pin = i % 8;
175 switch (intMode[port][pin]) {
176 case GPIO::RISING:
177 if ((intValue & mask) != 0)
178 intHandler[port][pin](PORT_PIN(port, pin));
179 break;
180 case GPIO::FALLING:
181 if ((intValue & mask) == 0)
182 intHandler[port][pin](PORT_PIN(port, pin));
183 break;
184 default:
185 intHandler[port][pin](PORT_PIN(port, pin));
186 }
187 }
188 mask <<= 1;
189 }
190}
191
192
193uint16_t mcp23s17_drv::digitalReadPort(uint16_t mask) {
194 return readRegister(MCP23S17::GPIO_REG) & mask;
195}
196
197void mcp23s17_drv::digitalWritePort(uint16_t value, uint16_t mask) {
198 uint16_t olat_old = _olat;
199 _olat &= ~mask;
200 value &= mask;
201 _olat |= value;
202 if (olat_old != _olat)
203 writeRegister(MCP23S17::OLAT_REG, _olat);
204}
205
206uint16_t mcp23s17_drv::readRegister(uint8_t reg) {
207 unsigned char txbuf[4];
208 unsigned char rxbuf[4];
209
210 txbuf[0] = 0x41 | (_spi_addr << 1);
211 txbuf[1] = reg;
212 txbuf[2] = 0; // dummy
213 txbuf[3] = 0; // dummy
214
215 _spi.spiTxRx(txbuf, rxbuf, 4);
216 return (rxbuf[3] << 8) | rxbuf[2];
217}
218
219void mcp23s17_drv::writeRegister(uint8_t reg, uint16_t value) {
220 unsigned char txbuf[4];
221 unsigned char rxbuf[4];
222
223 txbuf[0] = 0x40 | (_spi_addr << 1);
224 txbuf[1] = reg;
225 txbuf[2] = value & 0xff;
226 txbuf[3] = value >> 8;
227
228 _spi.spiTxRx(txbuf, rxbuf, 4);
229}
230