Skip to content

Commit c91d164

Browse files
committed
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 298e590 commit c91d164

File tree

2 files changed

+108
-12
lines changed

2 files changed

+108
-12
lines changed

lib/pbio/drv/i2c/i2c_ev3.c

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

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

108174
static pbio_os_process_t ev3_i2c_init_process;

lib/pbio/include/pbdrv/i2c.h

Lines changed: 39 additions & 9 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,16 +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);
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);
4365

4466
// Delete above and add read/write functions here. See I2C driver for examples, also protothreads.
4567

@@ -50,7 +72,15 @@ static inline pbio_error_t pbdrv_i2c_get_instance(uint8_t id, pbdrv_i2c_dev_t **
5072
return PBIO_ERROR_NOT_SUPPORTED;
5173
}
5274

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

0 commit comments

Comments
 (0)