Skip to content

Commit 5173fd4

Browse files
ArcaneNibbledlech
authored andcommitted
pbio/drv/i2c/i2c_ev3.c: Implement I2C operation
This implements performing an I2C operation asynchronously. It does not hook this up to any higher-level functionality.
1 parent 1446d47 commit 5173fd4

File tree

2 files changed

+108
-14
lines changed

2 files changed

+108
-14
lines changed

lib/pbio/drv/i2c/i2c_ev3.c

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,75 @@ static void pbdrv_i2c_irq_3(void) {
9999
pbio_os_request_poll();
100100
}
101101

102-
pbio_error_t pbdrv_i2c_placeholder_operation(pbdrv_i2c_dev_t *i2c_dev, const char *operation) {
103-
debug_pr("I2C placeholder operation %s\n", operation);
104-
return PBIO_SUCCESS;
102+
pbio_error_t pbdrv_i2c_write_then_read(
103+
pbio_os_state_t *state,
104+
pbdrv_i2c_dev_t *i2c_dev,
105+
uint8_t dev_addr,
106+
const uint8_t *wdata,
107+
size_t wlen,
108+
uint8_t *rdata,
109+
size_t rlen,
110+
bool nxt_quirk) {
111+
112+
PBIO_OS_ASYNC_BEGIN(state);
113+
114+
if (wlen && !wdata) {
115+
return PBIO_ERROR_INVALID_ARG;
116+
}
117+
if (rlen && !rdata) {
118+
return PBIO_ERROR_INVALID_ARG;
119+
}
120+
if (wlen > 0xff || rlen > 0xff) {
121+
return PBIO_ERROR_INVALID_ARG;
122+
}
123+
124+
if (i2c_dev->is_busy) {
125+
return PBIO_ERROR_BUSY;
126+
}
127+
128+
// Prepare TX data
129+
if (wlen) {
130+
memcpy(i2c_dev->buffer, wdata, wlen);
131+
}
132+
i2c_dev->is_busy = true;
133+
pbdrv_cache_prepare_before_dma(i2c_dev->buffer, PRU_I2C_MAX_BYTES_PER_TXN);
134+
135+
// Kick off transfer
136+
pbdrv_rproc_ev3_pru1_shared_ram.i2c[i2c_dev->pru_i2c_idx].flags = PBDRV_RPROC_EV3_PRU1_I2C_PACK_FLAGS(
137+
dev_addr,
138+
rlen,
139+
wlen,
140+
PBDRV_RPROC_EV3_PRU1_I2C_CMD_START | (nxt_quirk ? PBDRV_RPROC_EV3_PRU1_I2C_CMD_NXT_QUIRK : 0)
141+
);
142+
143+
// Wait for transfer to finish
144+
PBIO_OS_AWAIT_WHILE(state, i2c_dev->is_busy);
145+
146+
uint32_t flags = pbdrv_rproc_ev3_pru1_shared_ram.i2c[i2c_dev->pru_i2c_idx].flags;
147+
debug_pr("i2c %d done flags %08x\r\n", i2c_dev->pru_i2c_idx, flags);
148+
if (!(flags & PBDRV_RPROC_EV3_PRU1_I2C_STAT_DONE)) {
149+
debug_pr("i2c %d not actually done???\r\n", i2c_dev->pru_i2c_idx);
150+
return PBIO_ERROR_FAILED;
151+
}
152+
switch (flags & PBDRV_RPROC_EV3_PRU1_I2C_STAT_MASK) {
153+
case PBDRV_RPROC_EV3_PRU1_I2C_STAT_OK:
154+
break;
155+
case PBDRV_RPROC_EV3_PRU1_I2C_STAT_TIMEOUT:
156+
return PBIO_ERROR_TIMEDOUT;
157+
case PBDRV_RPROC_EV3_PRU1_I2C_STAT_NAK:
158+
return PBIO_ERROR_IO;
159+
default:
160+
debug_pr("i2c %d unknown error occurred???\r\n", i2c_dev->pru_i2c_idx);
161+
return PBIO_ERROR_FAILED;
162+
}
163+
164+
// If we got here, there's no error. Copy RX data.
165+
pbdrv_cache_prepare_after_dma(i2c_dev->buffer, PRU_I2C_MAX_BYTES_PER_TXN);
166+
if (rlen) {
167+
memcpy(rdata, &i2c_dev->buffer[wlen], rlen);
168+
}
169+
170+
PBIO_OS_ASYNC_END(PBIO_SUCCESS);
105171
}
106172

107173
static pbio_os_process_t ev3_i2c_init_process;

lib/pbio/include/pbdrv/i2c.h

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
#include <pbdrv/config.h>
1515
#include <pbio/error.h>
16+
#include <pbio/os.h>
1617
#include <pbio/port.h>
1718
#include <contiki.h>
1819

@@ -30,18 +31,37 @@ typedef struct _pbdrv_i2c_dev_t pbdrv_i2c_dev_t;
3031
pbio_error_t pbdrv_i2c_get_instance(uint8_t id, pbdrv_i2c_dev_t **i2c_dev);
3132

3233
/**
33-
* Does an I2C operation. To be replaced.
34+
* Does an I2C operation. To be integrated with higher-level code.
3435
*
35-
* Doesn't do anything yet. This is a placeholder so someone knowledgable with
36-
* EV3 I2C has a starting point without setting up all the boilerplate.
37-
*
38-
* @param [in] i2c_dev The I2C device.
39-
* @param [in] operation Dummy parameter.
40-
* @return ::PBIO_SUCCESS on success.
36+
* @param [in] state Protothread state for async operation.
37+
* @param [in] i2c_dev The I2C device.
38+
* @param [in] dev_addr I2C device address (unshifted).
39+
* @param [in] wdata Data to be sent to the device.
40+
* Can be null if \p wlen is 0.
41+
* @param [in] wlen Length of \p wdata.
42+
* If this is 0 and \p rlen is also 0,
43+
* an empty "ping" will be sent, consisting of
44+
* only a device address (with R/nW = 0).
45+
* If this is 0 but \p rlen is not 0,
46+
* a read transaction will be sent.
47+
* If this is not 0 and \p rlen is not 0,
48+
* a write will be sent followed by a read in
49+
* a single transaction.
50+
* @param [out] rdata Buffer for data read from the device.
51+
* Can be null if \p rlen is 0.
52+
* @param [in] rlen Size of \p rdata.
53+
* @param [in] nxt_quirk Whether to use NXT I2C transaction quirk.
54+
* @return ::PBIO_SUCCESS on success.
4155
*/
42-
pbio_error_t pbdrv_i2c_placeholder_operation(pbdrv_i2c_dev_t *i2c_dev, const char *operation);
43-
44-
// Delete above and add read/write functions here. See I2C driver for examples, also protothreads.
56+
pbio_error_t pbdrv_i2c_write_then_read(
57+
pbio_os_state_t *state,
58+
pbdrv_i2c_dev_t *i2c_dev,
59+
uint8_t dev_addr,
60+
const uint8_t *wdata,
61+
size_t wlen,
62+
uint8_t *rdata,
63+
size_t rlen,
64+
bool nxt_quirk);
4565

4666
#else // PBDRV_CONFIG_I2C
4767

@@ -50,7 +70,15 @@ static inline pbio_error_t pbdrv_i2c_get_instance(uint8_t id, pbdrv_i2c_dev_t **
5070
return PBIO_ERROR_NOT_SUPPORTED;
5171
}
5272

53-
static inline pbio_error_t pbdrv_i2c_placeholder_operation(pbdrv_i2c_dev_t *i2c_dev, const char *operation) {
73+
static inline pbio_error_t pbdrv_i2c_write_then_read(
74+
pbio_os_state_t *state,
75+
pbdrv_i2c_dev_t *i2c_dev,
76+
uint8_t dev_addr,
77+
const uint8_t *wdata,
78+
size_t wlen,
79+
uint8_t *rdata,
80+
size_t rlen,
81+
bool nxt_quirk) {
5482
return PBIO_ERROR_NOT_SUPPORTED;
5583
}
5684

0 commit comments

Comments
 (0)