/
i2c.h
218 lines (196 loc) · 8.88 KB
/
i2c.h
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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
#pragma once
#include "daisy_core.h"
namespace daisy
{
/** A handle for interacting with an I2C peripheral. This is a dumb
* gateway that internally points to one of the four I2C peripherals
* after it was initialised. It can then be copied and passed around.
* Use an I2CHandle like this:
*
* // setup the configuration
* I2CHandle::Config i2c_conf;
* i2c_conf.periph = I2CHandle::Config::Peripheral::I2C_1;
* i2c_conf.speed = I2CHandle::Config::Speed::I2C_400KHZ;
* i2c_conf.mode = I2CHandle::Config::Mode::Master;
* i2c_conf.pin_config.scl = {DSY_GPIOB, 8};
* i2c_conf.pin_config.sda = {DSY_GPIOB, 9};
* // initialise the peripheral
* I2CHandle i2c;
* i2c.Init(i2c_conf);
* // now i2c points to the corresponding peripheral and can be used.
* i2c.TransmitBlocking( ... );
*/
class I2CHandle
{
public:
/** Contains settings for initialising an I2C interface. */
struct Config
{
/** Specifies whether the interface will operate in master or slave mode. */
enum class Mode
{
I2C_MASTER,
I2C_SLAVE,
};
/** Specifices the internal peripheral to use (these are mapped to different pins on the hardware). */
enum class Peripheral
{
I2C_1 = 0, /**< & */
I2C_2, /**< & */
I2C_3, /**< & */
I2C_4, /**< & */
};
/** Rate at which the clock/data will be sent/received. The device being used will have maximum speeds.
* 1MHZ Mode is currently 886kHz
*/
enum class Speed
{
I2C_100KHZ, /**< & */
I2C_400KHZ, /**< & */
I2C_1MHZ, /**< & */
};
Peripheral periph; /**< & */
struct
{
dsy_gpio_pin scl; /**< & */
dsy_gpio_pin sda; /**< & */
} pin_config; /**< & */
Speed speed; /**< & */
Mode mode; /**< & */
// 0x10 is chosen as a default to avoid conflicts with reserved addresses
uint8_t address = 0x10; /**< & */
};
/** Return values for I2C functions. */
enum class Result
{
OK, /**< & */
ERR /**< & */
};
enum class Direction
{
TRANSMIT, /**< & */
RECEIVE, /**< & */
};
I2CHandle() : pimpl_(nullptr) {}
I2CHandle(const I2CHandle& other) = default;
I2CHandle& operator=(const I2CHandle& other) = default;
/** Initializes an I2C peripheral. */
Result Init(const Config& config);
/** Returns the current config. */
const Config& GetConfig() const;
/** Transmits data and blocks until the transmission is complete.
* Use this for smaller transmissions of a few bytes.
*
* \param address The slave device address. Unused in slave mode.
* \param data A pointer to the data to be sent.
* \param size The size of the data to be sent, in bytes.
* \param timeout A timeout.
*/
Result TransmitBlocking(uint16_t address,
uint8_t* data,
uint16_t size,
uint32_t timeout);
/** Receives data and blocks until the reception is complete.
* Use this for smaller transmissions of a few bytes.
*
* \param address The slave device address. Unused in slave mode.
* \param data A pointer to the data to be received.
* \param size The size of the data to be received, in bytes.
* \param timeout A timeout.
*/
Result ReceiveBlocking(uint16_t address,
uint8_t* data,
uint16_t size,
uint32_t timeout);
/** A callback to be executed when a dma transfer is complete. */
typedef void (*CallbackFunctionPtr)(void* context, Result result);
/** Transmits data with a DMA and returns immediately. Use this for larger transmissions.
* The pointer to data must be located in the D2 memory domain by adding the
* `DMA_BUFFER_MEM_SECTION` attribute like this:
* uint8_t DMA_BUFFER_MEM_SECTION my_buffer[100];
* If that is not possible for some reason, you MUST clear the cachelines spanning the size of
* the buffer, before initiating the dma transfer by calling
* `dsy_dma_clear_cache_for_buffer(buffer, size);`
*
* A single DMA is shared across I2C1, I2C2 and I2C3. I2C4 has no DMA support (yet).
* If the DMA is busy with another transfer, the job will be queued and executed later.
* If there is a job waiting to be executed for this I2C peripheral, this function
* will block until the queue is free and the job can be queued.
*
* \param address The slave device address. Unused in slave mode.
* \param data A pointer to the data to be sent.
* \param size The size of the data to be sent, in bytes.
* \param callback A callback to execute when the transfer finishes, or NULL.
* \param callback_context A pointer that will be passed back to you in the callback.
*/
Result TransmitDma(uint16_t address,
uint8_t* data,
uint16_t size,
CallbackFunctionPtr callback,
void* callback_context);
/** Receives data with a DMA and returns immediately. Use this for larger transmissions.
* The pointer to data must be located in the D2 memory domain by adding the
* `DMA_BUFFER_MEM_SECTION` attribute like this:
* uint8_t DMA_BUFFER_MEM_SECTION my_buffer[100];
* If that is not possible for some reason, you MUST clear the cachelines spanning the size of
* the buffer, before initiating the dma transfer by calling
* `dsy_dma_clear_cache_for_buffer(buffer, size);`
*
* A single DMA is shared across I2C, I2C2 and I2C3. I2C4 has no DMA support (yet).
* If the DMA is busy with another transfer, the job will be queued and executed later.
* If there is a job waiting to be executed for this I2C peripheral, this function
* will block until the queue is free and the job can be queued.
*
* \param address The slave device address. Unused in slave mode.
* \param data A pointer to the data buffer.
* \param size The size of the data to be received, in bytes.
* \param callback A callback to execute when the transfer finishes, or NULL.
* \param callback_context A pointer that will be passed back to you in the callback.
*/
Result ReceiveDma(uint16_t address,
uint8_t* data,
uint16_t size,
CallbackFunctionPtr callback,
void* callback_context);
/** Reads an amount of data from a specific memory address.
* This method will return an error if the I2C peripheral is in slave mode.
*
* \param address The slave device address.
* \param mem_address Pointer to data containing the address to read from device.
* \param mem_address_size Size of the memory address in bytes.
* \param data Pointer to buffer that will be filled with contents at mem_address
* \param data_size Size of the data to be read in bytes.
* \param timeout The timeout in milliseconds before returning without communication
*/
Result ReadDataAtAddress(uint16_t address,
uint16_t mem_address,
uint16_t mem_address_size,
uint8_t* data,
uint16_t data_size,
uint32_t timeout);
/** Writes an amount of data from a specific memory address.
* This method will return an error if the I2C peripheral is in slave mode.
*
* \param address The slave device address.
* \param mem_address Pointer to data containing the address to write to device.
* \param mem_address_size Size of the memory address in bytes.
* \param data Pointer to buffer that will be written to the mem_address
* \param data_size Size of the data to be written in bytes.
* \param timeout The timeout in milliseconds before returning without communication
*/
Result WriteDataAtAddress(uint16_t address,
uint16_t mem_address,
uint16_t mem_address_size,
uint8_t* data,
uint16_t data_size,
uint32_t timeout);
class Impl; /**< & */
private:
Impl* pimpl_;
};
extern "C"
{
/** internal. Used for global init. */
void dsy_i2c_global_init();
};
} // namespace daisy