Skip to content

Commit 90fdd42

Browse files
authored
feat: Breakout I2C Implementation (#47)
Splits out I2C only functionality Creates I2C Interface Unit-tests and test harness Extends mocking capability Adds test run for `WIRE_HAS_MOCK`
1 parent 2d5176c commit 90fdd42

15 files changed

+2480
-561
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
.vscode/
22
.DS_Store
3+
*.gch
34
*.out
45

56
Doxyfile*

src/NoteI2c.hpp

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#ifndef NOTE_I2C_HPP
2+
#define NOTE_I2C_HPP
3+
4+
#include <stddef.h>
5+
#include <stdint.h>
6+
7+
#include "Notecard.h"
8+
9+
class NoteI2c {
10+
public:
11+
/**************************************************************************/
12+
/*!
13+
@brief Type used to abstract specific hardware implementation types.
14+
*/
15+
/**************************************************************************/
16+
typedef void * bus_t;
17+
18+
virtual ~NoteI2c(void) {}
19+
20+
/**************************************************************************/
21+
/*!
22+
@brief Receives an amount of data from the Notecard in blocking mode.
23+
@param[in] device_address
24+
The I2C address.
25+
@param[out] buffer
26+
A buffer to hold the data read from the I2C controller.
27+
@param[in] requested_byte_count
28+
The number of bytes requested.
29+
@param[out] available
30+
The number of bytes available for subsequent calls to receive().
31+
@returns A string with an error, or `nullptr` if the receive was
32+
successful.
33+
*/
34+
/**************************************************************************/
35+
virtual const char * receive(uint16_t device_address, uint8_t * buffer, uint16_t size, uint32_t * available) = 0;
36+
37+
/**************************************************************************/
38+
/*!
39+
@brief Resets the I2C port. Required by note-c.
40+
@return `true`.
41+
*/
42+
/**************************************************************************/
43+
virtual bool reset(uint16_t device_address) = 0;
44+
45+
/**************************************************************************/
46+
/*!
47+
@brief Transmits an amount of data from the host in blocking mode.
48+
@param[in] device_address
49+
The I2C address.
50+
@param[in] buffer
51+
The data to transmit over I2C. The caller should have shifted
52+
it right so that the low bit is NOT the read/write bit.
53+
@param[in] size
54+
The number of bytes to transmit.
55+
@returns A string with an error, or `nullptr` if the transmission was
56+
successful.
57+
*/
58+
/**************************************************************************/
59+
virtual const char * transmit(uint16_t device_address, uint8_t * buffer, uint16_t size) = 0;
60+
61+
/**************************************************************************/
62+
/*!
63+
@brief Size of the header for Serial-Over-I2C requests.
64+
65+
@details The request made to the low-level I2C controller should be
66+
for REQUEST_HEADER_SIZE + the `size` parameter supplied to the
67+
`receive` method.
68+
69+
@see NoteI2c::receive
70+
*/
71+
/**************************************************************************/
72+
static const size_t REQUEST_HEADER_SIZE = 2;
73+
};
74+
75+
/******************************************************************************/
76+
/*!
77+
@brief Helper function to abstract, create and maintain a single instance
78+
of the NoteI2c interface implementation, as required by the underlying
79+
`note-c` library.
80+
@param[in] i2c_bus Pointer to the hardware specific I2C bus implementation.
81+
*/
82+
/******************************************************************************/
83+
NoteI2c * make_note_i2c (
84+
NoteI2c::bus_t i2c_bus
85+
);
86+
87+
#endif // NOTE_I2C_HPP

src/NoteI2c_Arduino.cpp

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
#include "NoteI2c_Arduino.hpp"
2+
3+
#if defined(NOTE_LOWMEM)
4+
static const char *i2cerr = "i2c {io}";
5+
#endif
6+
7+
NoteI2c *
8+
make_note_i2c (
9+
NoteI2c::bus_t i2c_bus_
10+
) {
11+
static NoteI2c * note_i2c = nullptr;
12+
if (note_i2c) {
13+
delete note_i2c;
14+
}
15+
note_i2c = new NoteI2c_Arduino(*reinterpret_cast<TwoWire *>(i2c_bus_));
16+
return note_i2c;
17+
}
18+
19+
NoteI2c_Arduino::NoteI2c_Arduino
20+
(
21+
TwoWire & i2c_bus_
22+
) :
23+
_i2cPort(i2c_bus_)
24+
{
25+
_i2cPort.begin();
26+
}
27+
28+
const char *
29+
NoteI2c_Arduino::receive (
30+
uint16_t device_address_,
31+
uint8_t * buffer_,
32+
uint16_t requested_byte_count_,
33+
uint32_t * available_
34+
) {
35+
const char *result = nullptr;
36+
uint8_t transmission_error = 0;
37+
38+
// Request response data from Notecard
39+
for (size_t i = 0 ; i < 3 ; ++i) {
40+
_i2cPort.beginTransmission(static_cast<uint8_t>(device_address_));
41+
_i2cPort.write(0);
42+
_i2cPort.write(static_cast<uint8_t>(requested_byte_count_));
43+
transmission_error = _i2cPort.endTransmission();
44+
45+
// Break out of loop on success
46+
if (!transmission_error) {
47+
break;
48+
}
49+
50+
switch (transmission_error) {
51+
case 1:
52+
result = ERRSTR("i2c: data too long to fit in transmit buffer {io}",i2cerr);
53+
break;
54+
case 2:
55+
result = ERRSTR("i2c: received NACK on transmit of address {io}",i2cerr);
56+
break;
57+
case 3:
58+
result = ERRSTR("i2c: received NACK on transmit of data {io}",i2cerr);
59+
break;
60+
case 4:
61+
result = ERRSTR("i2c: unknown error on TwoWire::endTransmission() {io}",i2cerr);
62+
break;
63+
case 5:
64+
result = ERRSTR("i2c: timeout {io}",i2cerr);
65+
break;
66+
default:
67+
result = ERRSTR("i2c: unknown error encounter during I2C transmission {io}",i2cerr);
68+
}
69+
}
70+
71+
// Read and cache response from Notecard
72+
if (!transmission_error) {
73+
const int request_length = requested_byte_count_ + NoteI2c::REQUEST_HEADER_SIZE;
74+
const int response_length = _i2cPort.requestFrom((int)device_address_, request_length);
75+
if (!response_length) {
76+
result = ERRSTR("serial-over-i2c: no response to read request {io}",i2cerr);
77+
} else if (response_length != request_length) {
78+
result = ERRSTR("serial-over-i2c: unexpected raw byte count {io}",i2cerr);
79+
} else {
80+
// Update available with remaining bytes
81+
*available_ = _i2cPort.read();
82+
// Ensure protocol response length matches size request
83+
if (requested_byte_count_ != static_cast<uint8_t>(_i2cPort.read())) {
84+
result = ERRSTR("serial-over-i2c: unexpected protocol byte count {io}",i2cerr);
85+
} else {
86+
for (size_t i = 0 ; i < requested_byte_count_ ; ++i) {
87+
//TODO: Perf test against indexed buffer writes
88+
*buffer_++ = _i2cPort.read();
89+
}
90+
}
91+
}
92+
}
93+
94+
return result;
95+
}
96+
97+
bool
98+
NoteI2c_Arduino::reset (
99+
uint16_t device_address_
100+
) {
101+
(void)device_address_;
102+
#if WIRE_HAS_END
103+
_i2cPort.end();
104+
#endif
105+
_i2cPort.begin();
106+
return true;
107+
}
108+
109+
const char *
110+
NoteI2c_Arduino::transmit (
111+
uint16_t device_address_,
112+
uint8_t * buffer_,
113+
uint16_t size_
114+
) {
115+
const char * result = nullptr;
116+
uint8_t transmission_error = 0;
117+
118+
_i2cPort.beginTransmission(static_cast<uint8_t>(device_address_));
119+
_i2cPort.write(static_cast<uint8_t>(size_));
120+
_i2cPort.write(buffer_, size_);
121+
transmission_error = _i2cPort.endTransmission();
122+
123+
if (transmission_error) {
124+
switch (transmission_error) {
125+
case 1:
126+
result = ERRSTR("i2c: data too long to fit in transmit buffer {io}",i2cerr);
127+
break;
128+
case 2:
129+
result = ERRSTR("i2c: received NACK on transmit of address {io}",i2cerr);
130+
break;
131+
case 3:
132+
result = ERRSTR("i2c: received NACK on transmit of data {io}",i2cerr);
133+
break;
134+
case 4:
135+
result = ERRSTR("i2c: unknown error on TwoWire::endTransmission() {io}",i2cerr);
136+
break;
137+
case 5:
138+
result = ERRSTR("i2c: timeout {io}",i2cerr);
139+
break;
140+
default:
141+
result = ERRSTR("i2c: unknown error encounter during I2C transmission {io}",i2cerr);
142+
}
143+
}
144+
145+
return result;
146+
}

src/NoteI2c_Arduino.hpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#ifndef NOTE_I2C_ARDUINO_HPP
2+
#define NOTE_I2C_ARDUINO_HPP
3+
4+
#include "NoteI2c.hpp"
5+
6+
#ifndef MOCK
7+
#include <Wire.h>
8+
#else
9+
#include "mock/mock-arduino.hpp"
10+
#include "mock/mock-parameters.hpp"
11+
#endif
12+
13+
class NoteI2c_Arduino final : public NoteI2c {
14+
public:
15+
NoteI2c_Arduino(TwoWire & i2c_bus);
16+
const char * receive(uint16_t device_address, uint8_t * buffer, uint16_t requested_byte_count, uint32_t * available) override;
17+
bool reset(uint16_t device_address) override;
18+
const char * transmit(uint16_t device_address, uint8_t * buffer, uint16_t size) override;
19+
20+
private:
21+
TwoWire & _i2cPort;
22+
};
23+
24+
#endif // NOTE_I2C_ARDUINO_HPP

src/NoteSerial_Arduino.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "NoteSerial_Arduino.hpp"
22

3-
NoteSerial * make_note_serial (
3+
NoteSerial *
4+
make_note_serial (
45
NoteSerial::channel_t serial_channel_,
56
size_t baud_rate_
67
)

0 commit comments

Comments
 (0)