arduino-audio-driver
Loading...
Searching...
No Matches
API_I2C.h
Go to the documentation of this file.
1#pragma once
2#include <stdint.h>
3#include "DriverCommon.h"
4
5// Platform-specific includes (must be at file scope, before namespace)
6#if defined(ARDUINO) && !AUDIO_DRIVER_FORCE_IDF
7# include <Wire.h>
8# include <assert.h>
9# include <stdio.h>
10#elif defined(ESP32_CMAKE)
11# include <assert.h>
12# include <string.h>
13# include "driver/i2c_master.h"
14#elif defined(__zephyr__)
15# include <assert.h>
16# include <string.h>
17# include <zephyr/drivers/i2c.h>
18# include <zephyr/kernel.h>
19#endif
20
21namespace audio_driver {
22
23struct I2CConfig {
25 uint32_t frequency;
26 int port;
28// Zephyr manages the pins in the devicetree!
29#if !defined(__zephyr__)
32#endif
33};
34
35#ifndef ARDUINO
36struct TwoWire {};
37#endif
38
39// ---- Arduino I2C implementation ----
40#if defined(ARDUINO) && !AUDIO_DRIVER_FORCE_IDF
41
42inline error_t i2c_bus_create(struct I2CConfig *config) {
43 AD_LOGI("i2c_bus_create");
44 assert(config != nullptr);
45 I2CConfig &pins = *config;
46 TwoWire *p_wire = (TwoWire *)pins.p_wire;
47 assert(p_wire != nullptr);
48
49 if (pins.scl == -1 || pins.sda == -1) {
50 AD_LOGI("setting up I2C w/o pins");
51 p_wire->begin();
52 } else {
53#if defined(ESP32)
54 AD_LOGI("setting up I2C scl: %d, sda: %d", pins.scl, pins.sda);
55 p_wire->begin(pins.sda, pins.scl);
56#elif defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_ARCH_STM32)
57 AD_LOGI("setting up I2C scl: %d, sda: %d", pins.scl, pins.sda);
58 p_wire->setSCL(pins.scl);
59 p_wire->setSDA(pins.sda);
60 p_wire->begin();
61#else
62 AD_LOGW("setting up I2C w/o pins");
63 p_wire->begin();
64#endif
65 if (pins.frequency > 0) {
66 AD_LOGI("Setting i2c clock: %u", pins.frequency);
67 p_wire->setClock(pins.frequency);
68 }
69 }
70 return RESULT_OK;
71}
72
73inline void i2c_bus_delete(i2c_bus_handle_t bus) {
74 TwoWire *p_wire = (TwoWire *)bus;
75 assert(p_wire != nullptr);
76#if !defined(ESP8266) && FORCE_WIRE_CLOSE
77 p_wire->end();
78#endif
79}
80
81// Renders up to 8 bytes as a contiguous hex string (e.g. {0x00,0x2D} -> "002D")
82// for logging - reg/data can be 1 or more bytes, and printf has no portable
83// way to log a runtime-length byte array, so build the string first.
84inline const char *ad_log_hex(char *out, size_t out_size, const uint8_t *bytes,
85 int len) {
86 size_t pos = 0;
87 for (int i = 0; i < len && pos + 2 < out_size; i++) {
88 pos += snprintf(out + pos, out_size - pos, "%02X", bytes[i]);
89 }
90 out[pos] = 0;
91 return out;
92}
93
94inline error_t i2c_bus_write_bytes(i2c_bus_handle_t bus, int addr, uint8_t *reg,
95 int reglen, uint8_t *data, int datalen) {
96 char reg_hex[8];
97 char data_hex[24];
98 AD_LOGD("i2c_bus_write_bytes: addr=0x%X reg=0x%s data=0x%s", addr,
99 ad_log_hex(reg_hex, sizeof(reg_hex), reg, reglen),
100 ad_log_hex(data_hex, sizeof(data_hex), data, datalen));
101 TwoWire *p_wire = (TwoWire *)bus;
102 assert(p_wire != nullptr);
103 int result = RESULT_OK;
104 p_wire->beginTransmission(addr);
105 if (reglen > 0) p_wire->write(reg, reglen);
106 if (datalen > 0) p_wire->write(data, datalen);
107 int rc = p_wire->endTransmission(I2C_END);
108 if (rc != 0) {
109 AD_LOGE("->p_wire->endTransmission: %d", rc);
110 result = RESULT_FAIL;
111 }
112 return result;
113}
114
115inline error_t i2c_bus_read_bytes(i2c_bus_handle_t bus, int addr, uint8_t *reg,
116 int reglen, uint8_t *outdata, int datalen, bool stopBit=true) {
117 char reg_hex[8];
118 AD_LOGD("i2c_bus_read_bytes: addr=0x%X reg=0x%s", addr,
119 ad_log_hex(reg_hex, sizeof(reg_hex), reg, reglen));
120 TwoWire *p_wire = (TwoWire *)bus;
121 assert(p_wire != nullptr);
122
123 memset(outdata, 0, datalen);
124 int result = RESULT_OK;
125
126 p_wire->beginTransmission(addr);
127 p_wire->write(reg, reglen);
128 int rc = p_wire->endTransmission(stopBit);
129 if (rc != 0) {
130 AD_LOGE("->p_wire->endTransmission: %d", rc);
131 }
132
133 uint8_t result_len = p_wire->requestFrom(addr, datalen, (int)I2C_END);
134 if (result_len > 0) {
135 result_len = p_wire->readBytes(outdata, datalen);
136 char data_hex[24];
137 AD_LOGD("i2c_bus_read_bytes: addr=0x%X reg=0x%s -> data=0x%s", addr,
138 ad_log_hex(reg_hex, sizeof(reg_hex), reg, reglen),
139 ad_log_hex(data_hex, sizeof(data_hex), outdata, datalen));
140 } else {
141 AD_LOGE("->p_wire->requestFrom %d->%d", datalen, result_len);
142 result = RESULT_FAIL;
143 }
144 return result;
145}
146
147// ---- Espressif IDF I2C implementation ----
148#elif defined(ESP32_CMAKE)
149
150static I2CConfig *i2c_configs[5];
151
152static inline I2CConfig *get_config(i2c_bus_handle_t hdl) {
153 for (int j = 0; j < 5; j++) {
154 if (i2c_configs[j]->p_wire == hdl) {
155 return i2c_configs[j];
156 }
157 }
158 return nullptr;
159}
160
161inline error_t i2c_bus_create(struct I2CConfig *config) {
162 I2CConfig &pins = *config;
163 i2c_port_t i2c_slave_port = (i2c_port_t)pins.port;
164 i2c_configs[pins.port] = config;
165
166 AD_LOGI("i2c_bus_create port: %d scl: %d, sda: %d address: 0x%x", pins.port,
167 pins.scl, pins.sda, pins.address);
168
169 i2c_master_bus_config_t i2c_mst_config = {};
170 i2c_mst_config.clk_source = I2C_CLK_SRC_DEFAULT;
171 i2c_mst_config.i2c_port = i2c_slave_port;
172 i2c_mst_config.scl_io_num = (gpio_num_t)pins.scl;
173 i2c_mst_config.sda_io_num = (gpio_num_t)pins.sda;
174 i2c_mst_config.glitch_ignore_cnt = 7;
175 i2c_mst_config.flags.enable_internal_pullup = true;
176
177 i2c_master_bus_handle_t bus_handle;
178 if (i2c_new_master_bus(&i2c_mst_config, &bus_handle) != ESP_OK) {
179 AD_LOGE("i2c_new_master_bus");
180 return ESP_FAIL;
181 }
182
183 if (i2c_master_probe(bus_handle, pins.address, -1) != RESULT_OK) {
184 AD_LOGE("Address check failed: scanning addresses:");
185 for (int j = 0; j < 127; j++) {
186 auto rc = i2c_master_probe(bus_handle, j, -1);
187 AD_LOGE("- address: 0x%x -> %d", j, rc);
188 }
189 return ESP_FAIL;
190 }
191
192 pins.p_wire = bus_handle;
193 return ESP_OK;
194}
195
196inline void i2c_bus_delete(i2c_bus_handle_t bus) {
197 if (i2c_del_master_bus((i2c_master_bus_handle_t)bus) != ESP_OK) {
198 AD_LOGI("i2c_bus_delete");
199 }
200}
201
202inline error_t i2c_bus_write_bytes(i2c_bus_handle_t bus, int addr, uint8_t *reg,
203 int reglen, uint8_t *data, int datalen) {
204 AD_LOGD("i2c_bus_write_bytes address: 0x%x", addr);
205 i2c_master_bus_handle_t bus_handle = (i2c_master_bus_handle_t)bus;
206
207 I2CConfig *cfg = get_config(bus);
208 assert(cfg != nullptr);
209 assert(cfg->address == addr);
210
211 i2c_device_config_t dev_cfg = {};
212 dev_cfg.dev_addr_length = I2C_ADDR_BIT_LEN_7;
213 dev_cfg.device_address = cfg->address;
214 dev_cfg.scl_speed_hz = cfg->frequency;
215
216 i2c_master_dev_handle_t dev_handle;
217 if (i2c_master_bus_add_device(bus_handle, &dev_cfg, &dev_handle) != ESP_OK) {
218 AD_LOGE("i2c_new_master_bus");
219 return ESP_FAIL;
220 }
221
222 int total_len = reglen + datalen;
223 uint8_t total_data[total_len];
224 memcpy(total_data, reg, reglen);
225 memcpy(total_data + reglen, data, datalen);
226
227 esp_err_t ret = ESP_OK;
228 ret |= i2c_master_transmit(dev_handle, total_data, total_len, -1);
229 if (ret == ESP_OK) {
230 ret = i2c_master_bus_wait_all_done(bus_handle, -1);
231 if (ret != ESP_OK) AD_LOGE("i2c_master_bus_wait_all_done");
232 } else {
233 AD_LOGE("i2c_master_transmit");
234 }
235
236 if (i2c_master_bus_rm_device(dev_handle) != ESP_OK) {
237 AD_LOGE("i2c_master_bus_rm_device");
238 }
239
240 if (ret != ESP_OK) {
241 AD_LOGE("i2c_bus_write_bytes");
242 }
243 return ret;
244}
245
246inline error_t i2c_bus_read_bytes(i2c_bus_handle_t bus, int addr, uint8_t *reg,
247 int reglen, uint8_t *read_buffer, int read_size) {
248 AD_LOGD("i2c_bus_read_bytes address: 0x%x", addr);
249 i2c_master_bus_handle_t bus_handle = (i2c_master_bus_handle_t)bus;
250
251 I2CConfig *cfg = get_config(bus);
252 assert(cfg != nullptr);
253 assert(cfg->address == addr);
254
255 i2c_device_config_t dev_cfg = {};
256 dev_cfg.dev_addr_length = I2C_ADDR_BIT_LEN_7;
257 dev_cfg.device_address = cfg->address;
258 dev_cfg.scl_speed_hz = cfg->frequency;
259
260 i2c_master_dev_handle_t dev_handle;
261 if (i2c_master_bus_add_device(bus_handle, &dev_cfg, &dev_handle) != ESP_OK) {
262 AD_LOGE("i2c_master_bus_add_device");
263 return ESP_FAIL;
264 }
265
266 int write_size = reglen;
267 uint8_t write_buffer[write_size];
268 memcpy(write_buffer, reg, reglen);
269
270 esp_err_t ret = ESP_OK;
271 ret = i2c_master_transmit_receive(dev_handle, write_buffer, write_size,
272 read_buffer, read_size, -1);
273 ret = i2c_master_bus_wait_all_done(bus_handle, -1);
274 if (ret != ESP_OK) {
275 AD_LOGE("i2c_bus_read_bytes");
276 }
277 if (i2c_master_bus_rm_device(dev_handle) != ESP_OK) {
278 AD_LOGI("i2c_master_bus_rm_device");
279 }
280
281 return ret;
282}
283
284// ---- Zephyr I2C implementation ----
285#elif defined(__zephyr__)
286
287inline error_t i2c_bus_create(struct I2CConfig *config) {
288 AD_LOGI("i2c_bus_create");
289 assert(config != NULL);
290
291 struct device *dev = (struct device *)config->p_wire;
292 assert(dev != NULL);
293
294 if (!device_is_ready(dev)) {
295 AD_LOGE("I2C device not ready");
296 return RESULT_FAIL;
297 }
298
299 if (config->frequency > 0) {
300 uint32_t i2c_cfg = I2C_MODE_CONTROLLER;
301
302 if (config->frequency <= 100000) {
303 i2c_cfg |= I2C_SPEED_SET(I2C_SPEED_STANDARD);
304 } else if (config->frequency <= 400000) {
305 i2c_cfg |= I2C_SPEED_SET(I2C_SPEED_FAST);
306 } else if (config->frequency <= 1000000) {
307 i2c_cfg |= I2C_SPEED_SET(I2C_SPEED_FAST_PLUS);
308 } else {
309 i2c_cfg |= I2C_SPEED_SET(I2C_SPEED_HIGH);
310 }
311
312 int rc = i2c_configure(dev, i2c_cfg);
313 if (rc != 0) {
314 AD_LOGE("Failed to configure I2C: %d", rc);
315 return RESULT_FAIL;
316 }
317 AD_LOGI("Setting I2C clock: %u Hz", config->frequency);
318 }
319
320 return RESULT_OK;
321}
322
323inline void i2c_bus_delete(i2c_bus_handle_t bus) {
324 (void)bus;
325}
326
327inline error_t i2c_bus_write_bytes(i2c_bus_handle_t bus, int addr, uint8_t *reg,
328 int reglen, uint8_t *data, int datalen) {
329 AD_LOGD("i2c_bus_write_bytes: addr=0x%X reglen=%d datalen=%d reg=0x%02X",
330 addr, reglen, datalen, reglen > 0 ? reg[0] : 0);
331
332 struct device *dev = (struct device *)bus;
333 assert(dev != NULL);
334
335 struct i2c_msg msgs[2];
336 uint8_t num_msgs = 0;
337
338 if (reglen > 0) {
339 msgs[num_msgs].buf = reg;
340 msgs[num_msgs].len = reglen;
341 msgs[num_msgs].flags = I2C_MSG_WRITE;
342 num_msgs++;
343 }
344
345 if (datalen > 0) {
346 msgs[num_msgs].buf = data;
347 msgs[num_msgs].len = datalen;
348 msgs[num_msgs].flags = I2C_MSG_WRITE;
349 num_msgs++;
350 }
351
352 if (num_msgs > 0) {
353 msgs[num_msgs - 1].flags |= I2C_MSG_STOP;
354 }
355
356 int rc = i2c_transfer(dev, msgs, num_msgs, (uint16_t)addr);
357 if (rc != 0) {
358 AD_LOGE("i2c_transfer (write) failed: %d", rc);
359 return RESULT_FAIL;
360 }
361 return RESULT_OK;
362}
363
364inline error_t i2c_bus_read_bytes(i2c_bus_handle_t bus, int addr, uint8_t *reg,
365 int reglen, uint8_t *outdata, int datalen) {
366 AD_LOGD("i2c_bus_read_bytes: addr=0x%X reglen=%d datalen=%d reg=0x%02X", addr,
367 reglen, datalen, reglen > 0 ? reg[0] : 0);
368
369 struct device *dev = (struct device *)bus;
370 assert(reglen == 1);
371 assert(dev != NULL);
372
373 memset(outdata, 0, datalen);
374
375 struct i2c_msg msgs[2] = {
376 {
377 .buf = reg,
378 .len = (uint32_t)reglen,
379 .flags = I2C_MSG_WRITE,
380 },
381 {
382 .buf = outdata,
383 .len = (uint32_t)datalen,
384 .flags = I2C_MSG_READ | I2C_MSG_STOP,
385 },
386 };
387
388 int rc = i2c_transfer(dev, msgs, 2, (uint16_t)addr);
389 if (rc != 0) {
390 AD_LOGE("i2c_transfer (read) failed: %d", rc);
391 return RESULT_FAIL;
392 }
393 return RESULT_OK;
394}
395
396#endif // platform selection
397
398} // namespace audio_driver
#define RESULT_OK
Definition DriverCommon.h:74
#define I2C_END
Definition DriverCommon.h:83
#define RESULT_FAIL
Definition DriverCommon.h:75
#define AD_LOGD(...)
Definition Logger.h:12
#define AD_LOGI(...)
Definition Logger.h:13
#define AD_LOGE(...)
Definition Logger.h:15
#define AD_LOGW(...)
Definition Logger.h:14
Namespace for audio driver components.
Definition AudioBoard.h:5
int16_t GpioPin
Definition DriverCommon.h:153
int error_t
Definition DriverCommon.h:150
void * i2c_bus_handle_t
Definition DriverCommon.h:155
Definition API_I2C.h:23
GpioPin scl
Definition API_I2C.h:30
int address
Definition API_I2C.h:27
i2c_bus_handle_t p_wire
Definition API_I2C.h:24
int port
Definition API_I2C.h:26
uint32_t frequency
Definition API_I2C.h:25
GpioPin sda
Definition API_I2C.h:31
Definition API_I2C.h:36