Skip to content

Commit 40207d9

Browse files
authored
Merge pull request #264 from adafruit/add-i2c-tiny-usb
Add i2c-tiny-usb adapter example
2 parents 3d2167c + 3e4e0d0 commit 40207d9

File tree

7 files changed

+426
-3
lines changed

7 files changed

+426
-3
lines changed

.codespell/ignore-words.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
synopsys
22
sie
33
inout
4+
busses

.github/workflows/githubci.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ jobs:
1818
- name: Checkout code
1919
uses: actions/checkout@v3
2020

21-
- name: Run codespell
22-
uses: codespell-project/actions-codespell@master
21+
- name: Run pre-commit
22+
uses: pre-commit/action@v3.0.0
2323

2424
- name: Checkout adafruit/ci-arduino
2525
uses: actions/checkout@v3
@@ -50,7 +50,6 @@ jobs:
5050
- 'feather_esp32s3'
5151
# ESP32S2
5252
- 'feather_esp32s2'
53-
- 'metroesp32s2'
5453
# nRF52
5554
- 'cpb'
5655
- 'nrf52840'

.pre-commit-config.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# SPDX-FileCopyrightText: 2020 Diego Elio Pettenò
2+
#
3+
# SPDX-License-Identifier: Unlicense
4+
5+
repos:
6+
- repo: https://github.com/codespell-project/codespell
7+
rev: v2.2.4
8+
hooks:
9+
- id: codespell
10+
#args: [-w]
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
/*
2+
* The MIT License (MIT)
3+
*
4+
* Copyright (c) 2023 Ha Thach (tinyusb.org) for Adafruit Industries
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
* THE SOFTWARE.
23+
*/
24+
25+
/*
26+
* Reference:
27+
* - https://github.com/torvalds/linux/blob/master/drivers/i2c/busses/i2c-tiny-usb.c
28+
* - https://github.com/harbaum/I2C-Tiny-USB
29+
*
30+
* Requirement:
31+
* - Install i2c-tools with
32+
* sudo apt install i2c-tools
33+
*
34+
* How to test example:
35+
* - Compile and flash this sketch on your board with an i2c device, it should enumerated as
36+
* ID 1c40:0534 EZPrototypes i2c-tiny-usb interface
37+
*
38+
* - Run "i2cdetect -l" to find our bus ID e.g
39+
* i2c-8 i2c i2c-tiny-usb at bus 003 device 039 I2C adapter
40+
*
41+
* - Run "i2cdetect -y 8" to scan for on-board device (8 is the above bus ID)
42+
* 0 1 2 3 4 5 6 7 8 9 a b c d e f
43+
00: -- -- -- -- -- -- -- --
44+
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
45+
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
46+
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
47+
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
48+
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
49+
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50+
70: -- -- -- -- -- -- -- 77
51+
52+
- You can then interact with sensor using following commands:
53+
i2cget i2cset i2cdump i2ctransfer or using any driver/tools that work on i2c device.
54+
*/
55+
56+
#include "Adafruit_USBD_I2C.h"
57+
58+
Adafruit_USBD_I2C::Adafruit_USBD_I2C(TwoWire* wire) {
59+
_wire = wire;
60+
_buf = NULL;
61+
_bufsize = 0; // not used to verify length yet
62+
_state = I2C_STATUS_IDLE;
63+
_functionality = 0x8eff0001; // check out _functionality_* defines
64+
setStringDescriptor("I2C Interface");
65+
}
66+
67+
uint16_t Adafruit_USBD_I2C::getInterfaceDescriptor(uint8_t itfnum, uint8_t* buf, uint16_t bufsize) {
68+
uint8_t desc[] = { TUD_VENDOR_DESCRIPTOR(itfnum, 0, 0x00, 0x80, 64) };
69+
uint16_t const len = sizeof(desc);
70+
if (buf) {
71+
if (bufsize < len) {
72+
return 0;
73+
}
74+
memcpy(buf, desc, len);
75+
}
76+
return len;
77+
}
78+
79+
bool Adafruit_USBD_I2C::begin(uint8_t* buffer, size_t bufsize) {
80+
_buf = buffer;
81+
_bufsize = (uint16_t) bufsize;
82+
83+
if (!_wire || !_buf || !_bufsize) return false;
84+
85+
// needed to identify as a device for i2c_tiny_usb (EZPrototypes VID/PID)
86+
TinyUSBDevice.setID(0x1c40, 0x0534);
87+
if (!TinyUSBDevice.addInterface(*this)) return false;
88+
89+
_wire->begin();
90+
return true;
91+
}
92+
93+
uint16_t Adafruit_USBD_I2C::i2c_read(uint8_t addr, uint8_t* buf, uint16_t len, bool stop_bit)
94+
{
95+
uint16_t const rd_count = (uint16_t) _wire->requestFrom(addr, len, stop_bit);
96+
97+
_state = (len && !rd_count) ? I2C_STATUS_NAK : I2C_STATUS_ACK;
98+
99+
// Serial.printf("I2C Read: addr = 0x%02X, len = %u, rd_count %u bytes, status = %u\r\n", addr, len, rd_count, i2c_state);
100+
101+
for(uint16_t i = 0; i <rd_count; i++)
102+
{
103+
buf[i] = (uint8_t) _wire->read();
104+
}
105+
106+
return rd_count;
107+
}
108+
109+
uint16_t Adafruit_USBD_I2C::i2c_write(uint8_t addr, uint8_t const* buf, uint16_t len, bool stop_bit)
110+
{
111+
_wire->beginTransmission(addr);
112+
uint16_t wr_count = (uint16_t) _wire->write(buf, len);
113+
uint8_t const sts = _wire->endTransmission(stop_bit);
114+
115+
_state = (sts == 0) ? I2C_STATUS_ACK : I2C_STATUS_NAK;
116+
117+
// Serial.printf("I2C Write: addr = 0x%02X, len = %u, wr_count = %u, status = %u\r\n", addr, len, wr_count, i2c_state);
118+
119+
return wr_count;
120+
}
121+
122+
bool Adafruit_USBD_I2C::handleControlTransfer(uint8_t rhport, uint8_t stage, tusb_control_request_t const* request) {
123+
uint8_t const cmd = request->bRequest;
124+
125+
if ( stage == CONTROL_STAGE_SETUP )
126+
{
127+
switch ( cmd )
128+
{
129+
case CMD_ECHO:
130+
// echo
131+
return tud_control_xfer(rhport, request, (void*) &request->wValue, sizeof(request->wValue));
132+
133+
case CMD_GET_FUNC:
134+
// capabilities
135+
return tud_control_xfer(rhport, request, (void*) &_functionality, sizeof(_functionality));
136+
137+
case CMD_SET_DELAY:
138+
if ( request->wValue == 0 )
139+
{
140+
_wire->setClock(115200);
141+
}
142+
else
143+
{
144+
int baudrate = 1000000 / request->wValue;
145+
if ( baudrate > 400000 ) baudrate = 400000;
146+
_wire->setClock(baudrate);
147+
}
148+
return tud_control_status(rhport, request);
149+
150+
case CMD_GET_STATUS:
151+
return tud_control_xfer(rhport, request, (void*) &_state, sizeof(_state));
152+
153+
case CMD_I2C_IO:
154+
case CMD_I2C_IO | CMD_I2C_IO_BEGIN:
155+
case CMD_I2C_IO | CMD_I2C_IO_END:
156+
case CMD_I2C_IO | CMD_I2C_IO_BEGIN | CMD_I2C_IO_END:
157+
{
158+
uint8_t const addr = (uint8_t) request->wIndex;
159+
// uint16_t const flags = request->wValue;
160+
uint16_t const len = tu_min16(request->wLength, _bufsize);
161+
bool const stop_bit = (cmd & CMD_I2C_IO_END) ? true : false;
162+
163+
if (request->bmRequestType_bit.direction == TUSB_DIR_OUT)
164+
{
165+
if (len == 0)
166+
{
167+
// zero write: do it here since there will be no data stage for len = 0
168+
i2c_write(addr, _buf, len, stop_bit);
169+
}
170+
return tud_control_xfer(rhport, request, _buf, len);
171+
}else
172+
{
173+
uint16_t const rd_count = i2c_read(addr, _buf, len, stop_bit);
174+
return tud_control_xfer(rhport, request, rd_count ? _buf : NULL, rd_count);
175+
}
176+
}
177+
break;
178+
179+
default: return true;
180+
}
181+
}
182+
else if ( stage == CONTROL_STAGE_DATA )
183+
{
184+
switch ( cmd )
185+
{
186+
case CMD_I2C_IO:
187+
case CMD_I2C_IO | CMD_I2C_IO_BEGIN:
188+
case CMD_I2C_IO | CMD_I2C_IO_END:
189+
case CMD_I2C_IO | CMD_I2C_IO_BEGIN | CMD_I2C_IO_END:
190+
if (request->bmRequestType_bit.direction == TUSB_DIR_OUT)
191+
{
192+
uint8_t const addr = (uint8_t) request->wIndex;
193+
// uint16_t const flags = request->wValue;
194+
uint16_t const len = tu_min16(request->wLength, _bufsize);
195+
bool const stop_bit = (cmd & CMD_I2C_IO_END) ? true : false;
196+
197+
i2c_write(addr, _buf, len, stop_bit);
198+
}
199+
return true;
200+
201+
default: return true;
202+
}
203+
}
204+
else
205+
{
206+
// CONTROL_STAGE_STATUS
207+
return true;
208+
}
209+
}
210+
211+
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
* The MIT License (MIT)
3+
*
4+
* Copyright (c) 2023 Ha Thach (tinyusb.org) for Adafruit Industries
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
* THE SOFTWARE.
23+
*/
24+
25+
#ifndef ADAFRUIT_USBD_I2C_H_
26+
#define ADAFRUIT_USBD_I2C_H_
27+
28+
#include "Adafruit_TinyUSB.h"
29+
#include "Wire.h"
30+
31+
/* commands from USB, must e.g. match command ids in kernel driver */
32+
#define CMD_ECHO 0
33+
#define CMD_GET_FUNC 1
34+
#define CMD_SET_DELAY 2
35+
#define CMD_GET_STATUS 3
36+
37+
#define CMD_I2C_IO 4
38+
#define CMD_I2C_IO_BEGIN (1<<0) // flag for I2C_IO
39+
#define CMD_I2C_IO_END (1<<1) // flag for I2C_IO
40+
41+
/* linux kernel flags */
42+
#define I2C_M_TEN 0x10 /* we have a ten bit chip address */
43+
#define I2C_M_RD 0x01
44+
#define I2C_M_NOSTART 0x4000
45+
#define I2C_M_REV_DIR_ADDR 0x2000
46+
#define I2C_M_IGNORE_NAK 0x1000
47+
#define I2C_M_NO_RD_ACK 0x0800
48+
49+
/* To determine what functionality is present */
50+
#define I2C_FUNC_I2C 0x00000001
51+
#define I2C_FUNC_10BIT_ADDR 0x00000002 /* required for I2C_M_TEN */
52+
#define I2C_FUNC_PROTOCOL_MANGLING 0x00000004 /* required for I2C_M_IGNORE_NAK etc. */
53+
#define I2C_FUNC_SMBUS_PEC 0x00000008
54+
#define I2C_FUNC_NOSTART 0x00000010 /* required for I2C_M_NOSTART */
55+
#define I2C_FUNC_SLAVE 0x00000020
56+
#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL 0x00008000 /* SMBus 2.0 or later */
57+
#define I2C_FUNC_SMBUS_QUICK 0x00010000
58+
#define I2C_FUNC_SMBUS_READ_BYTE 0x00020000
59+
#define I2C_FUNC_SMBUS_WRITE_BYTE 0x00040000
60+
#define I2C_FUNC_SMBUS_READ_BYTE_DATA 0x00080000
61+
#define I2C_FUNC_SMBUS_WRITE_BYTE_DATA 0x00100000
62+
#define I2C_FUNC_SMBUS_READ_WORD_DATA 0x00200000
63+
#define I2C_FUNC_SMBUS_WRITE_WORD_DATA 0x00400000
64+
#define I2C_FUNC_SMBUS_PROC_CALL 0x00800000
65+
#define I2C_FUNC_SMBUS_READ_BLOCK_DATA 0x01000000 /* required for I2C_M_RECV_LEN */
66+
#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000
67+
#define I2C_FUNC_SMBUS_READ_I2C_BLOCK 0x04000000 /* I2C-like block xfer */
68+
#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK 0x08000000 /* w/ 1-byte reg. addr. */
69+
#define I2C_FUNC_SMBUS_HOST_NOTIFY 0x10000000 /* SMBus 2.0 or later */
70+
71+
#define I2C_FUNC_SMBUS_BYTE (I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE)
72+
#define I2C_FUNC_SMBUS_BYTE_DATA (I2C_FUNC_SMBUS_READ_BYTE_DATA | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)
73+
#define I2C_FUNC_SMBUS_WORD_DATA (I2C_FUNC_SMBUS_READ_WORD_DATA | I2C_FUNC_SMBUS_WRITE_WORD_DATA)
74+
#define I2C_FUNC_SMBUS_BLOCK_DATA (I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_BLOCK_DATA)
75+
#define I2C_FUNC_SMBUS_I2C_BLOCK (I2C_FUNC_SMBUS_READ_I2C_BLOCK | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)
76+
77+
#define I2C_FUNC_SMBUS_EMUL (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | \
78+
I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_WRITE_BLOCK_DATA | \
79+
I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_PEC)
80+
81+
/* if I2C_M_RECV_LEN is also supported */
82+
#define I2C_FUNC_SMBUS_EMUL_ALL (I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL)
83+
84+
#define I2C_STATUS_IDLE 0
85+
#define I2C_STATUS_ACK 1
86+
#define I2C_STATUS_NAK 2
87+
88+
class Adafruit_USBD_I2C : public Adafruit_USBD_Interface {
89+
public:
90+
Adafruit_USBD_I2C(TwoWire* wire);
91+
uint16_t getInterfaceDescriptor(uint8_t itfnum, uint8_t* buf, uint16_t bufsize);
92+
bool begin(uint8_t* buffer, size_t bufsize);
93+
94+
bool handleControlTransfer(uint8_t rhport, uint8_t stage, tusb_control_request_t const* request);
95+
96+
private:
97+
TwoWire* _wire;
98+
uint8_t _state;
99+
uint32_t _functionality;
100+
uint8_t* _buf;
101+
uint16_t _bufsize;
102+
103+
uint16_t i2c_write(uint8_t addr, uint8_t const* buf, uint16_t len, bool stop_bit);
104+
uint16_t i2c_read(uint8_t addr, uint8_t* buf, uint16_t len, bool stop_bit);
105+
};
106+
107+
#endif

0 commit comments

Comments
 (0)