Skip to content

Commit 6a01b3a

Browse files
committed
add i2c-tiny-usb adapter example
1 parent a65044b commit 6a01b3a

File tree

2 files changed

+303
-0
lines changed

2 files changed

+303
-0
lines changed
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
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 I2C_USB_INTERFACE_H_
26+
#define I2C_USB_INTERFACE_H_
27+
28+
#include "Adafruit_TinyUSB.h"
29+
30+
/* commands from USB, must e.g. match command ids in kernel driver */
31+
#define CMD_ECHO 0
32+
#define CMD_GET_FUNC 1
33+
#define CMD_SET_DELAY 2
34+
#define CMD_GET_STATUS 3
35+
36+
#define CMD_I2C_IO 4
37+
#define CMD_I2C_IO_BEGIN (1<<0) // flag fo I2C_IO
38+
#define CMD_I2C_IO_END (1<<1) // flag fo I2C_IO
39+
40+
/* linux kernel flags */
41+
#define I2C_M_TEN 0x10 /* we have a ten bit chip address */
42+
#define I2C_M_RD 0x01
43+
#define I2C_M_NOSTART 0x4000
44+
#define I2C_M_REV_DIR_ADDR 0x2000
45+
#define I2C_M_IGNORE_NAK 0x1000
46+
#define I2C_M_NO_RD_ACK 0x0800
47+
48+
/* To determine what functionality is present */
49+
#define I2C_FUNC_I2C 0x00000001
50+
#define I2C_FUNC_10BIT_ADDR 0x00000002 /* required for I2C_M_TEN */
51+
#define I2C_FUNC_PROTOCOL_MANGLING 0x00000004 /* required for I2C_M_IGNORE_NAK etc. */
52+
#define I2C_FUNC_SMBUS_PEC 0x00000008
53+
#define I2C_FUNC_NOSTART 0x00000010 /* required for I2C_M_NOSTART */
54+
#define I2C_FUNC_SLAVE 0x00000020
55+
#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL 0x00008000 /* SMBus 2.0 or later */
56+
#define I2C_FUNC_SMBUS_QUICK 0x00010000
57+
#define I2C_FUNC_SMBUS_READ_BYTE 0x00020000
58+
#define I2C_FUNC_SMBUS_WRITE_BYTE 0x00040000
59+
#define I2C_FUNC_SMBUS_READ_BYTE_DATA 0x00080000
60+
#define I2C_FUNC_SMBUS_WRITE_BYTE_DATA 0x00100000
61+
#define I2C_FUNC_SMBUS_READ_WORD_DATA 0x00200000
62+
#define I2C_FUNC_SMBUS_WRITE_WORD_DATA 0x00400000
63+
#define I2C_FUNC_SMBUS_PROC_CALL 0x00800000
64+
#define I2C_FUNC_SMBUS_READ_BLOCK_DATA 0x01000000 /* required for I2C_M_RECV_LEN */
65+
#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000
66+
#define I2C_FUNC_SMBUS_READ_I2C_BLOCK 0x04000000 /* I2C-like block xfer */
67+
#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK 0x08000000 /* w/ 1-byte reg. addr. */
68+
#define I2C_FUNC_SMBUS_HOST_NOTIFY 0x10000000 /* SMBus 2.0 or later */
69+
70+
#define I2C_FUNC_SMBUS_BYTE (I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE)
71+
#define I2C_FUNC_SMBUS_BYTE_DATA (I2C_FUNC_SMBUS_READ_BYTE_DATA | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)
72+
#define I2C_FUNC_SMBUS_WORD_DATA (I2C_FUNC_SMBUS_READ_WORD_DATA | I2C_FUNC_SMBUS_WRITE_WORD_DATA)
73+
#define I2C_FUNC_SMBUS_BLOCK_DATA (I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_BLOCK_DATA)
74+
#define I2C_FUNC_SMBUS_I2C_BLOCK (I2C_FUNC_SMBUS_READ_I2C_BLOCK | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)
75+
76+
#define I2C_FUNC_SMBUS_EMUL (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | \
77+
I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_WRITE_BLOCK_DATA | \
78+
I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_PEC)
79+
80+
/* if I2C_M_RECV_LEN is also supported */
81+
#define I2C_FUNC_SMBUS_EMUL_ALL (I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL)
82+
83+
class I2C_USB_Interface : public Adafruit_USBD_Interface {
84+
public:
85+
I2C_USB_Interface(void) {
86+
setStringDescriptor("I2C Interface");
87+
}
88+
89+
uint16_t getInterfaceDescriptor(uint8_t itfnum, uint8_t* buf, uint16_t bufsize) {
90+
uint8_t desc[] = { TUD_VENDOR_DESCRIPTOR(itfnum, 0, 0x00, 0x80, 64) };
91+
uint16_t const len = sizeof(desc);
92+
if (buf) {
93+
if (bufsize < len) {
94+
return 0;
95+
}
96+
memcpy(buf, desc, len);
97+
}
98+
return len;
99+
}
100+
101+
bool begin(void) {
102+
return TinyUSBDevice.addInterface(*this);
103+
}
104+
};
105+
106+
#endif
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
/*********************************************************************
2+
Adafruit invests time and resources providing this open source code,
3+
please support Adafruit and open-source hardware by purchasing
4+
products from Adafruit!
5+
6+
MIT license, check LICENSE for more information
7+
Copyright (c) 2019 Ha Thach for Adafruit Industries
8+
All text above, and the splash screen below must be included in
9+
any redistribution
10+
*********************************************************************/
11+
12+
#include <Wire.h>
13+
#include "Adafruit_TinyUSB.h"
14+
15+
#include "../i2c_tiny_usb_adapter/I2C_USB_Interface.h"
16+
17+
/* This sketch demonstrates how to use tinyusb vendor interface to implement
18+
* i2c-tiny-usb adapter to use with Linux
19+
*
20+
* Reference:
21+
* - https://github.com/torvalds/linux/blob/master/drivers/i2c/busses/i2c-tiny-usb.c
22+
* - https://github.com/harbaum/I2C-Tiny-USB
23+
*
24+
* Requirement:
25+
* - Install i2c-tools with
26+
* sudo apt install i2c-tools
27+
*
28+
* How to test example:
29+
* - Compile and flash this sketch on your board with an i2c device, it should enumerated as
30+
* ID 1c40:0534 EZPrototypes i2c-tiny-usb interface
31+
*
32+
* - Run "i2cdetect -l" to find our bus ID e.g
33+
* i2c-8 i2c i2c-tiny-usb at bus 003 device 039 I2C adapter
34+
*
35+
* - Run "i2cdetect -y 8" to scan for on-board device (8 is the above bus ID)
36+
* 0 1 2 3 4 5 6 7 8 9 a b c d e f
37+
00: -- -- -- -- -- -- -- --
38+
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
39+
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40+
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
41+
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
42+
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
43+
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
44+
70: -- -- -- -- -- -- -- 77
45+
46+
- You can then interact with sensor using following commands:
47+
i2cget i2cset i2cdump i2ctransfer or using any driver/tools that work on i2c device.
48+
*/
49+
50+
#define STATUS_IDLE 0
51+
#define STATUS_ACK 1
52+
#define STATUS_NAK 2
53+
54+
I2C_USB_Interface i2c_usb;
55+
56+
// check out I2C_FUNC_* defines
57+
static uint32_t i2c_func = 0x8eff0001;
58+
static uint8_t i2c_state = STATUS_IDLE;
59+
static uint8_t i2c_buf[800];
60+
61+
#define MyWire Wire
62+
//#define MyWire Wire1
63+
64+
void setup() {
65+
// needed to identify as a device for i2c_tiny_usb (EZPrototypes VID/PID)
66+
TinyUSBDevice.setID(0x1c40, 0x0534);
67+
i2c_usb.begin();
68+
MyWire.begin();
69+
}
70+
71+
void loop() {
72+
}
73+
74+
uint16_t i2c_read(uint8_t addr, uint8_t* buf, uint16_t len, bool stop_bit)
75+
{
76+
uint16_t const rd_count = (uint16_t) MyWire.requestFrom(addr, len, stop_bit);
77+
78+
i2c_state = (len && !rd_count) ? STATUS_NAK : STATUS_ACK;
79+
80+
// Serial.printf("I2C Read: addr = 0x%02X, len = %u, rd_count %u bytes, status = %u\r\n", addr, len, rd_count, i2c_state);
81+
82+
for(uint16_t i = 0; i <rd_count; i++)
83+
{
84+
buf[i] = (uint8_t) MyWire.read();
85+
}
86+
87+
return rd_count;
88+
}
89+
90+
uint16_t i2c_write(uint8_t addr, uint8_t const* buf, uint16_t len, bool stop_bit)
91+
{
92+
MyWire.beginTransmission(addr);
93+
uint16_t wr_count = (uint16_t) MyWire.write(buf, len);
94+
uint8_t const sts = MyWire.endTransmission(stop_bit);
95+
96+
i2c_state = (sts == 0) ? STATUS_ACK : STATUS_NAK;
97+
98+
// Serial.printf("I2C Write: addr = 0x%02X, len = %u, wr_count = %u, status = %u\r\n", addr, len, wr_count, i2c_state);
99+
100+
return wr_count;
101+
}
102+
103+
bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const* request)
104+
{
105+
if ( request->bmRequestType_bit.type == TUSB_REQ_TYPE_VENDOR )
106+
{
107+
uint8_t const cmd = request->bRequest;
108+
109+
if ( stage == CONTROL_STAGE_SETUP )
110+
{
111+
switch ( cmd )
112+
{
113+
case CMD_ECHO:
114+
// echo
115+
return tud_control_xfer(rhport, request, (void*) &request->wValue, sizeof(request->wValue));
116+
117+
case CMD_GET_FUNC:
118+
// capabilities
119+
return tud_control_xfer(rhport, request, (void*) &i2c_func, sizeof(i2c_func));
120+
121+
case CMD_SET_DELAY:
122+
if ( request->wValue == 0 )
123+
{
124+
MyWire.setClock(115200);
125+
}
126+
else
127+
{
128+
int baudrate = 1000000 / request->wValue;
129+
if ( baudrate > 400000 ) baudrate = 400000;
130+
MyWire.setClock(baudrate);
131+
}
132+
return tud_control_status(rhport, request);
133+
134+
case CMD_GET_STATUS:
135+
return tud_control_xfer(rhport, request, (void*) &i2c_state, sizeof(i2c_state));
136+
137+
case CMD_I2C_IO:
138+
case CMD_I2C_IO | CMD_I2C_IO_BEGIN:
139+
case CMD_I2C_IO | CMD_I2C_IO_END:
140+
case CMD_I2C_IO | CMD_I2C_IO_BEGIN | CMD_I2C_IO_END:
141+
{
142+
uint8_t const addr = (uint8_t) request->wIndex;
143+
uint16_t const flags = request->wValue;
144+
uint16_t const len = request->wLength;
145+
bool const stop_bit = (cmd & CMD_I2C_IO_END) ? true : false;
146+
147+
if (request->bmRequestType_bit.direction == TUSB_DIR_OUT)
148+
{
149+
if (len == 0)
150+
{
151+
// zero write: do it here since there will be no data stage for len = 0
152+
i2c_write(addr, i2c_buf, len, stop_bit);
153+
}
154+
return tud_control_xfer(rhport, request, i2c_buf, len);
155+
}else
156+
{
157+
uint16_t const rd_count = i2c_read(addr, i2c_buf, len, stop_bit);
158+
return tud_control_xfer(rhport, request, rd_count ? i2c_buf : NULL, rd_count);
159+
}
160+
}
161+
break;
162+
163+
default: return true;
164+
}
165+
}
166+
else if ( stage == CONTROL_STAGE_DATA )
167+
{
168+
switch ( cmd )
169+
{
170+
case CMD_I2C_IO:
171+
case CMD_I2C_IO | CMD_I2C_IO_BEGIN:
172+
case CMD_I2C_IO | CMD_I2C_IO_END:
173+
case CMD_I2C_IO | CMD_I2C_IO_BEGIN | CMD_I2C_IO_END:
174+
if (request->bmRequestType_bit.direction == TUSB_DIR_OUT)
175+
{
176+
uint8_t const addr = (uint8_t) request->wIndex;
177+
uint16_t const flags = request->wValue;
178+
uint16_t const len = request->wLength;
179+
bool const stop_bit = (cmd & CMD_I2C_IO_END) ? true : false;
180+
181+
i2c_write(addr, i2c_buf, len, stop_bit);
182+
}
183+
return true;
184+
185+
default: return true;
186+
}
187+
}
188+
else
189+
{
190+
// CONTROL_STAGE_STATUS
191+
return true;
192+
}
193+
}
194+
195+
return false;
196+
}
197+

0 commit comments

Comments
 (0)