This repository has been archived by the owner on Nov 11, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 99
/
u_port_i2c.h
432 lines (402 loc) · 20.4 KB
/
u_port_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
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
/*
* Copyright 2019-2024 u-blox
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _U_PORT_I2C_H_
#define _U_PORT_I2C_H_
/* Only header files representing a direct and unavoidable
* dependency between the API of this module and the API
* of another module should be included here; otherwise
* please keep #includes to your .c files. */
/** \addtogroup __port
* @{
*/
/** @file
* @brief Porting layer for I2C access functions. These functions
* are thread-safe. Note that these functions are currently only
* used to talk to u-blox GNSS modules and that reflects the extent
* to which they are tested; should you decide to use them to talk
* with other I2C devices then it may be worth expanding the testing
* also.
*/
#ifdef __cplusplus
extern "C" {
#endif
/* ----------------------------------------------------------------
* COMPILE-TIME MACROS
* -------------------------------------------------------------- */
#ifndef U_PORT_I2C_CLOCK_FREQUENCY_HERTZ
/** The default I2C clock frequency in Hertz.
*/
# define U_PORT_I2C_CLOCK_FREQUENCY_HERTZ 100000
#endif
#ifndef U_PORT_I2C_TIMEOUT_MILLISECONDS
/** The default I2C timeout in milliseconds, noting that this value
* is per-byte, i.e. it is very short.
*/
# define U_PORT_I2C_TIMEOUT_MILLISECONDS 10
#endif
/* ----------------------------------------------------------------
* TYPES
* -------------------------------------------------------------- */
/* ----------------------------------------------------------------
* FUNCTIONS
* -------------------------------------------------------------- */
/** Initialise I2C handling. If I2C has already been initialised
* this function will return success without doing anything.
*
* @return zero on success else negative error code.
*/
int32_t uPortI2cInit();
/** Shutdown I2C handling; any open I2C instances will be closed.
*/
void uPortI2cDeinit();
/** Open an I2C instance. If an I2C instance has already
* been opened on the given I2C HW block this function returns
* an error. Note that the pin numbers are those of the MCU:
* if you are using an MCU inside a u-blox module the IO pin
* numbering for the module is likely different to that from
* the MCU: check the data sheet for the module to determine
* the mapping.
*
* IMPORTANT: some platforms, specifically Zephyr (used on NRF53),
* do not permit I2C pin choices to be made at link-time, only at
* compile time. For such platforms the pins passed in here MUST
* be -1 (otherwise an error will be returned) and you MUST check
* the README.md for that platform to find out how the pins
* are chosen.
*
* @param i2c the I2C HW block to use.
* @param pinSda the data pin, a positive integer or -1 if
* the pin choice has already been determined
* at compile time or is irrelevant (for
* example Zephyr and Linux).
* @param pinSdc the clock pin, a positive integer or -1
* if the pin choice has already been
* determined at compile time or is irrelevant
* (for example Zephyr and Linux).
* @param controller set to true for an I2C controller; this is for
* forwards-compatibility only, it must currently
* always be set to true since target/peripheral/
* slave mode is not supported.
* @return an I2C handle else negative error code.
*/
int32_t uPortI2cOpen(int32_t i2c, int32_t pinSda, int32_t pinSdc,
bool controller);
/** This is like uPortI2cOpen() but it does NOT modify any of the
* platform HW; use this if you have ALREADY opened/configured the I2C
* port and you simply want to allow the port API to access it.
*
* @param i2c the I2C HW block to adopt.
* @param controller set to true for an I2C controller; this is for
* forwards-compatibility only, it must currently
* always be set to true since target/peripheral/
* slave mode is not supported.
* @return an I2C handle else negative error code.
*/
int32_t uPortI2cAdopt(int32_t i2c, bool controller);
/** Close an I2C instance; if the I2C interface was adopted rather
* than opened this will only free memory etc., it will do nothing
* to the I2C HW.
*
* @param handle the handle of the I2C instance to close.
*/
void uPortI2cClose(int32_t handle);
/** Close an I2C instance and attempt to recover the I2C bus; useful
* if a slave has stopped working in a bad way, pulling SDA low.
* WHATEVER THE RETURN VALUE of this function, you must ALWAYS call
* uPortI2cOpen() once more to continue using I2C; even if bus
* recovery is not supported on a given platform (e.g. ESP-IDF
* performs bus recovery when it encounters an error, there is no
* explicit function to do so), provided you have given a valid
* handle the I2C instance WILL have been closed. Note that this
* function will not recover all situations and it is not always
* possible for this function to determine that it has succeeded;
* it is best for you to do that by addressing a peripheral that
* you know works. Ultimately the only reliable I2C bus recovery
* method is out-of-band, i.e. wire the reset pins of your I2C
* devices together and hang them off a GPIO pin of this MCU that
* you can reset them all with. If that is not possible you
* might also consider the advice here:
* https://www.analog.com/media/en/technical-documentation/application-notes/54305147357414an686_0.pdf.
*
* Note that if the I2C interface was adopted rather than
* opened this will return #U_ERROR_COMMON_NOT_SUPPORTED.
*
* @param handle the handle of the I2C instance.
* @return zero on success else negative error code.
*/
int32_t uPortI2cCloseRecoverBus(int32_t handle);
/** Set the I2C clock frequency. If this is not called
* #U_PORT_I2C_CLOCK_FREQUENCY_HERTZ will be used. Note that
* the I2C specification generally permits only certain
* frequencies (e.g. 100 kHz, 400 kHz, 1 MHz, 3.4 MHz and
* 5 MHz) and which frequencies will work depends on the
* host chipset and the peripheral on the I2C bus being
* addressed. On some platforms (e.g. ESP-IDF) setting the
* clock requires the I2C instance to be taken down and brought
* back up again, hence if this function returns an error the
* I2C instance should be closed and re-opened to ensure
* that all is good.
*
* Note that if the I2C interface was adopted rather than
* opened this will return #U_ERROR_COMMON_NOT_SUPPORTED.
* Note for STM32F4 platform: there is an erratta:
* https://www.st.com/resource/en/errata_sheet/es0206-stm32f427437-and-stm32f429439-line-limitations-stmicroelectronics.pdf
* ...which suggests that using a 100 kHz clock might not
* work in some circumstances, hence you may wish to switch
* to 400 kHz.
*
* @param handle the handle of the I2C instance.
* @param clockHertz the clock frequency in Hertz.
* @return zero on success else negative error code.
*/
int32_t uPortI2cSetClock(int32_t handle, int32_t clockHertz);
/** Get the I2C clock frequency. Note that if the I2C interface
* was adopted rather than opened this will return
* #U_ERROR_COMMON_NOT_SUPPORTED.
*
* @param handle the handle of the I2C instance.
* @return the clock frequency in Hertz, else negative
* error code.
*/
int32_t uPortI2cGetClock(int32_t handle);
/** Set the timeout for an I2C instance; this timeout is PER BYTE,
* i.e. it is very short. Not all platforms support setting the
* I2C timeout through an API (e.g. Zephyr doesn't). If this
* function is not called, #U_PORT_I2C_TIMEOUT_MILLISECONDS will
* be used. It is best to call this once after opening the I2C
* instance since setting the timeout may reset the I2C HW.
*
* Note: on some platforms (e.g. ESP32X3 series) the timeout ends
* up being a power of two of the I2C clock source; in these cases
* the closest value that is greater than or equal to the
* requested value will be set.
*
* Note that on some platforms, if the I2C interface was adopted
* rather than opened, this will return #U_ERROR_COMMON_NOT_SUPPORTED.
*
* @param handle the handle of the I2C instance.
* @param timeoutMs the timeout in milliseconds.
* @return zero on success else negative error code.
*/
int32_t uPortI2cSetTimeout(int32_t handle, int32_t timeoutMs);
/** Get the timeout for an I2C instance. Not all platforms support
* getting the I2C timeout through an API (e.g. Zephyr doesn't).
*
* Note that on some platforms, if the I2C interface was adopted
* rather than opened, this will return #U_ERROR_COMMON_NOT_SUPPORTED.
*
* @param handle the handle of the I2C instance.
* @return the timeout in milliseconds, else negative
* error code.
*/
int32_t uPortI2cGetTimeout(int32_t handle);
/** Set the maximum segment size for an I2C transfer in both
* directions; this should be used only on chipsets where the HW
* interface is limited (e.g. nRF52832, which has a maximum DMA
* size of 256 for I2C, STM32 on Zephyr which has a similar (though
* not DMA related) limitation and STM32U5 on STM32Cube): any
* transfers above this size will be segmented into N transfers of
* no more than this size. If this is not called no segmentation will
* be applied. Where this is not supported a weakly-linked function
* will return #U_ERROR_COMMON_NOT_SUPPORTED.
*
* Note to GNSS users: the receive length of each segment of
* data from the GNSS device is already limited by
* #U_GNSS_MSG_TEMPORARY_BUFFER_LENGTH_BYTES. To avoid any
* inefficiencies you may wish to make sure that matches
* the maxSegmentSize you use here.
*
* @param handle the handle of the I2C instance.
* @param maxSegmentSize the maximum segment size; use zero to
* indicate no limit (the default).
* @return zero on success, else negative error code.
*/
int32_t uPortI2cSetMaxSegmentSize(int32_t handle, size_t maxSegmentSize);
/** Get the maximum segment size for an I2C transfer in both
* directions. If this is not called no segmentation will be
* applied.
*
* @param handle the handle of the I2C instance.
* @return the maximum segment size (zero if no segmentation
* is applied).
*/
int32_t uPortI2cGetMaxSegmentSize(int32_t handle);
/** Send and/or receive over the I2C interface as a controller.
* Note that the NRF52 and NRF53 chips require all buffers to
* be in RAM.
*
* Note that the uPortI2cSetTimeout() (or the equivalent set by
* a platform at compile-time) applies for the whole of this
* transaction, i.e. the peripheral must begin responding within
* that time; if you wish to allow the peripheral longer to respond
* you should take control of the time allowed yourself by calling
* uPortI2cControllerExchange() twice, once with only a send buffer
* and again with only a receive buffer.
*
* @param handle the handle of the I2C instance.
* @param address the I2C address to send to; only the lower
* 7 bits are used unless the platform supports
* 10-bit addressing. Note that the NRF5 SDK,
* and hence Zephyr on NRF52/53 (which uses the NRF5
* SDK under the hood) does not support 10-bit
* addressing and, in any case, we've not yet found
* a device that supports 10-bit addressing to test
* against.
* @param[in] pSend a pointer to the data to send, use NULL
* if only receive is required; setting this and
* pReceive to NULL will return success only if a
* device with the given address is present on the I2C
* bus; however note that the NRFX drivers used on nRF52
* and nRF53 by NRF-SDK and Zephyr don't support
* sending only the address, data must follow.
* @param bytesToSend the number of bytes to send, must be zero if pSend
* is NULL.
* @param[out] pReceive a pointer to a buffer in which to store received
* data; use NULL if only send is required.
* @param bytesToReceive the size of buffer pointed to by pReceive, must
* be zero if pReceive is NULL.
* @param noInterveningStop if true then no stop is sent between the send
* and the receive; this is useful for devices such
* as EEPROMs or, in certain situations, u-blox GNSS
* modules, which allow writing of a memory address
* byte or bytes, followed by no stop bit; the data
* from that memory address may then be received
* into pReceive. This is sometimes called using a
* "repeated start bit". Note that if pReceive
* is NULL, depending on the platform, this _may_
* be ignored and a stop bit added in any case;
* e.g. the STM32 drivers within Zephyr will do this;
* they require a stop bit at the end of an I2C
* transaction.
* @return if pReceive is not NULL the number of bytes
* received or negative error code; if pReceive is
* NULL then zero on success else negative error code.
* Note that the underlying platform drivers often
* do not report the number of bytes received and
* hence the return value may just be either an
* error code or bytesToReceive copied back to you.
*/
int32_t uPortI2cControllerExchange(int32_t handle, uint16_t address,
const char *pSend, size_t bytesToSend,
char *pReceive, size_t bytesToReceive,
bool noInterveningStop);
/** \deprecated this function is deprecated, please use
* uPortI2cControllerExchange() instead; if you are making your own
* I2C port, please implement uPortI2cControllerExchange() and
* not this function.
*
* Send and/or receive over the I2C interface as a controller.
* Note that the NRF52 and NRF53 chips require all buffers to
* be in RAM.
*
* Note that the uPortI2cSetTimeout() (or the equivalent set
* by a platform at compile-time) applies for the whole of this
* transaction, i.e. the peripheral must begin responding within
* that time; if you wish to allow the peripheral longer to respond
* you should take control of the time allowed yourself by calling
* uPortI2cControllerSend() and then, after the appropriate time,
* this function with only the receive buffer set.
*
* @param handle the handle of the I2C instance.
* @param address the I2C address to send to; only the lower
* 7 bits are used unless the platform supports
* 10-bit addressing. Note that the NRF5 SDK,
* and hence Zephyr on NRF52/53 (which uses the NRF5
* SDK under the hood) does not support 10-bit
* addressing and, in any case, we've not yet found
* a device that supports 10-bit addressing to test
* against.
* @param[in] pSend a pointer to the data to send, use NULL
* if only receive is required. This function
* will do nothing, and return success, if both
* pSend and pReceive are NULL; if you want to do
* a "scan" for valid addresses, use
* uPortI2cControllerSend() with a NULL pSend,
* though note that not all platforms support this.
* @param bytesToSend the number of bytes to send, must be zero if pSend
* is NULL.
* @param[out] pReceive a pointer to a buffer in which to store received
* data; use NULL if only send is required.
* @param bytesToReceive the size of buffer pointed to by pReceive, must
* be zero if pReceive is NULL.
* @return if pReceive is not NULL the number of bytes
* received or negative error code; if pReceive is
* NULL then zero on success else negative error code.
* Note that the underlying platform drivers often
* do not report the number of bytes received and
* hence the return value may just be either an
* error code or bytesToReceive copied back to you.
*/
int32_t uPortI2cControllerSendReceive(int32_t handle, uint16_t address,
const char *pSend, size_t bytesToSend,
char *pReceive, size_t bytesToReceive);
/** \deprecated this function is deprecated, please use
* uPortI2cControllerExchange() instead; if you are making your own
* I2C port, please implement uPortI2cControllerExchange() and
* not this function.
*
* Perform just a send over the I2C interface as a controller, with the
* option of omitting the stop marker on the end.
* Note that the NRF52 and NRF53 chips require the buffer to be in RAM.
*
* @param handle the handle of the I2C instance.
* @param address the I2C address to send to; only the lower
* 7 bits are used unless the platform supports
* 10-bit addressing. Note that the NRF5 SDK,
* and hence Zephyr on NRF52/53 (which uses the NRF5
* SDK under the hood) does not support 10-bit
* addressing and, in any case, we've not yet found
* a device that supports 10-bit addressing to test
* against.
* @param[in] pSend a pointer to the data to send; setting this to
* NULL will return success only if a device with
* the given address is present on the I2C bus;
* however note that the NRFX drivers used on nRF52
* and nRF53 by NRF-SDK and Zephyr don't support
* sending only the address, data must follow.
* @param bytesToSend the number of bytes to send; must be zero if
* pSend is NULL.
* @param noStop if true then no stop is sent at the end of the
* transmission; this is useful for devices such
* as EEPROMs or, in certain situations, u-blox GNSS
* modules, which allow writing of a memory address
* byte or bytes, followed by no stop bit; the data
* from that memory address may then be received
* e.g. by calling uPortI2cControllerSendReceive()
* with a receive buffer only. This is sometimes
* called using a "repeated start bit", because
* there is no stop bit between the start bit
* sent by this function and that sent by
* uPortI2cControllerSendReceive().
* @return zero on success else negative error code.
*/
int32_t uPortI2cControllerSend(int32_t handle, uint16_t address,
const char *pSend, size_t bytesToSend,
bool noStop);
/** Get the number of I2C interfaces currently open; this may be used
* as a basic check for heap monitoring.
*
* You do not need to implement this function: where it is not
* implemented a #U_WEAK implementation provided in u_port_resource.c will
* return zero.
*/
int32_t uPortI2cResourceAllocCount();
#ifdef __cplusplus
}
#endif
/** @}*/
#endif // _U_PORT_I2C_H_
// End of file