i2c-tiny-usb works!

This commit is contained in:
Triss 2021-06-20 23:58:26 +02:00
parent ff45c13498
commit df797cbeb4
9 changed files with 121 additions and 96 deletions

View File

@ -33,8 +33,8 @@ if(FAMILY STREQUAL "rp2040")
family_initialize_project(PROJECT ${CMAKE_CURRENT_LIST_DIR}) # calls pico_sdk_init()
#pico_sdk_init()
pico_set_program_name(PROJECT "${PROJECT}")
pico_set_program_version(PROJECT "0.1")
#pico_set_program_name(${PROJECT} "${PROJECT}")
#pico_set_program_version(${PROJECT} "0.1")
add_executable(${PROJECT})

View File

@ -46,7 +46,7 @@ These microcontrollers support the following protocols:
| MCU | SWD | JTAG | UART | SPI (flashrom) | I2C | Other stuff |
|:------ |:---:|:----:|:----:|:-------------- |:--- |:--------------- |
| RP2040 | X | X | X | X | WIP | Planned |
| RP2040 | X | X | X | X | X | Planned |
| STM32F072B Discovery | X | | | | | |
The [original repository](https://github.com/majbthrd/DapperMime/) (Dapper
@ -164,10 +164,10 @@ libco is licensed under the [ISC license](https://opensource.org/licenses/ISC)
separate mode that temporarily disables all other IO protocols
- [x] UART with CTS/RTS flow control
- [x] Needs configurable stuff as well, as some UART interfaces won't use this.
- [ ] Second UART port for when stdio UART is disabled?
- [x] Debug interface to send printf stuff directly to USB, instead of having
to use the UART interface as a loopback thing.
- [ ] I2C support by emulating the I2C Tiny USB
- [ ] Second UART port for when stdio UART is disabled?
- [x] I2C support by emulating the I2C Tiny USB
- [ ] Expose RP2040-internal temperature ADC on I2C-over-USB bus?
- ~~Does SMBus stuff need special treatment here?~~ ~~No.~~ Actually, some
parts do, but, laziness.

View File

@ -7,6 +7,10 @@
/*#define DBOARD_HAS_SERPROG*/
/*#define DBOARD_HAS_TINYI2C*/
#define CFG_TUD_CDC 0
#define CFG_TUD_HID 0
#define CFG_TUD_VENDOR 0
#define INFO_BOARDNAME "unknown"
#endif

View File

@ -377,33 +377,40 @@ uint32_t i2ctu_set_freq(uint32_t freq, uint32_t us) {
}
// TODO: FIX START AND STOP COND HANDLING. MAYBE. BUG IN vnd_i2ctinyusb.c MORE SERIOUS
// ALSO TODO: i2cex routines seem to mess with the I2C bus when a timeout (=> abort) happens?
enum itu_status i2ctu_write(enum ki2c_flags flags, enum itu_command startstopflags,
uint16_t addr, const uint8_t* buf, size_t len) {
bool nostop = !(startstopflags & ITU_CMD_I2C_IO_END);
printf("nostop=%c ", nostop?'t':'f');
if (len == 0) {
// do a read, that's less hazardous
uint8_t stuff = 0;
int rv = i2cex_read_timeout_us(PINOUT_I2C_DEV, addr, false, &stuff, 1,
!(startstopflags & ITU_CMD_I2C_IO_END), 1000*1000);
int rv = i2c_read_timeout_us(PINOUT_I2C_DEV, addr, /*false,*/ &stuff, 1,
nostop, 1000*1000);
if (rv < 0) return ITU_STATUS_ADDR_NAK;
return ITU_STATUS_ADDR_ACK;
} else {
int rv = i2cex_write_timeout_us(PINOUT_I2C_DEV, addr, false, buf, len,
!(startstopflags & ITU_CMD_I2C_IO_END), 1000*1000);
int rv = i2c_write_timeout_us(PINOUT_I2C_DEV, addr, /*false,*/ buf, len,
nostop, 1000*1000);
if (rv < 0 || (size_t)rv < len) return ITU_STATUS_ADDR_NAK;
return ITU_STATUS_ADDR_ACK;
}
}
enum itu_status i2ctu_read(enum ki2c_flags flags, enum itu_command startstopflags,
uint16_t addr, uint8_t* buf, size_t len) {
bool nostop = !(startstopflags & ITU_CMD_I2C_IO_END);
printf("nostop=%c ", nostop?'t':'f');
if (len == 0) {
uint8_t stuff = 0;
int rv = i2cex_read_timeout_us(PINOUT_I2C_DEV, addr, false, &stuff, 1,
!(startstopflags & ITU_CMD_I2C_IO_END), 1000*1000);
int rv = i2c_read_timeout_us(PINOUT_I2C_DEV, addr, /*false,*/ &stuff, 1,
nostop, 1000*1000);
if (rv < 0) return ITU_STATUS_ADDR_NAK;
return ITU_STATUS_ADDR_ACK;
} else {
int rv = i2cex_read_timeout_us(PINOUT_I2C_DEV, addr, false, buf, len,
!(startstopflags & ITU_CMD_I2C_IO_END), 1000*1000);
int rv = i2c_read_timeout_us(PINOUT_I2C_DEV, addr, /*false,*/ buf, len,
nostop, 1000*1000);
printf("p le rv=%d buf=%02x ", rv, buf[0]);
if (rv < 0 || (size_t)rv < len) return ITU_STATUS_ADDR_NAK;
return ITU_STATUS_ADDR_ACK;

View File

@ -7,15 +7,32 @@
#define DBOARD_HAS_SERPROG
#define DBOARD_HAS_I2C
#define HID_N_CMSISDAP 0
#define CDC_N_UART 0
#define CDC_N_SERPROG 1
#define VND_N_I2CTINYUSB 0
enum {
HID_N_CMSISDAP = 0,
HID_N__NITF
};
enum {
CDC_N_UART = 0,
CDC_N_SERPROG,
#ifdef USE_USBCDC_FOR_STDIO
#define CDC_N_STDIO 2
CDC_N_STDIO,
#endif
CDC_N__NITF
};
enum {
VND_N__NITF = 0
};
#define CFG_TUD_HID 1
#ifdef USE_USBCDC_FOR_STDIO
#define CFG_TUD_CDC 3
#else
#define CFG_TUD_CDC 2
#endif
#define CFG_TUD_VENDOR 0
/*#define USB_VID 0x2e8a*/ /* Raspberry Pi */
#define USB_VID 0xcafe /* TinyUSB */
/*#define USB_VID 0x1209*/ /* Generic */

View File

@ -7,6 +7,22 @@
/*#define DBOARD_HAS_SERPROG*/
/*#define DBOARD_HAS_TINYI2C*/
enum {
HID_N_CMSISDAP = 0,
HID_N__NITF
};
enum {
CDC_N__NITF
};
enum {
VND_N__NITF = 0
};
#define CFG_TUD_HID 1
#define CFG_TUD_CDC 0
#define CFG_TUD_VENDOR 0
#define USB_VID 0xcafe /* TinyUSB */
/*#define USB_VID 0x1209*/ /* Generic */
/*#define USB_VID 0x1d50*/ /* OpenMoko */

View File

@ -84,11 +84,12 @@ enum ki2c_funcs {
I2C_FUNC_SMBUS_BLOCK_PROC_CALL),
};
__attribute__((__packed__))
struct itu_cmd {
uint8_t cmd;
uint16_t flags;
uint16_t addr;
uint16_t len;
uint8_t cmd;
};
#ifdef DBOARD_HAS_I2C

View File

@ -30,6 +30,8 @@
extern "C" {
#endif
#include "protocfg.h"
//--------------------------------------------------------------------
// COMMON CONFIGURATION
//--------------------------------------------------------------------
@ -98,17 +100,10 @@ extern "C" {
#endif
//------------- CLASS -------------//
#ifdef USE_USBCDC_FOR_STDIO
#define CFG_TUD_CDC 3
#else
#define CFG_TUD_CDC 2
#endif
#define CFG_TUD_MSC 0
#define CFG_TUD_HID 1
#define CFG_TUD_MIDI 0
#define CFG_TUD_VENDOR 0
#define CFG_TUD_NET 0
// see also: bsp/<family>/protocfg.h
#define CFG_TUD_HID_EP_BUFSIZE 64

View File

@ -20,6 +20,9 @@ static uint8_t itf_num;
static enum itu_status status;
static struct itu_cmd curcmd;
static uint8_t rxbuf[128];
static uint8_t txbuf[128];
static void iub_init(void) {
status = ITU_STATUS_IDLE;
memset(&curcmd, 0, sizeof curcmd);
@ -51,41 +54,32 @@ static uint16_t iub_open(uint8_t rhport, tusb_desc_interface_t const* itf_desc,
}
static bool iub_ctl_req(uint8_t rhport, uint8_t stage, tusb_control_request_t const* req) {
// if (stage == CONTROL_STAGE_DATA && req->bmRequestType_bit.type == TUSB_REQ_TYPE_VENDOR) {
// // TODO: should URB_CONTROL out data be read in this stage????
// // FIXME: cond ^ has false-positives!
// // FIXME: handle 0-byte writes in SETUP stage
// // FIXME: use curcmd var for eliminating false-positives
// // FIXME: other stuff???
// if (req->bRequest >= ITU_CMD_I2C_IO && req->bRequest <= ITU_CMD_I2C_IO_BEGINEND
// /*&& curcmd.cmd == req->bRequest && curcmd.flags == req->wValue
// && curcmd.addr == req->wIndex && curcmd.len == req->wLength*/) {
// uint8_t buf[req->wLength];
// bool rv = tud_control_xfer(rhport, req, buf, req->wLength);
// printf("write addr=%04hx len=%04hx ", req->wIndex, req->wLength);
// if (rv) {
// printf("data=%02x %02x...\n", buf[0], buf[1]);
// status = i2ctu_write(req->wValue, req->bRequest & ITU_CMD_I2C_IO_DIR_MASK,
// req->wIndex, buf, sizeof buf);
// } else {
// printf("no data :/\n");
// status = ITU_STATUS_ADDR_NAK;
// }
// return rv;
// } else {
// //printf("I2C-Tiny-USB: bad command in DATA stage\n");
// //return false;
// }
// return true;
// }
static char* stages[]={"SETUP","DATA","ACK"};
static char* types[]={"STD","CLS","VND","INV"};
if (stage != CONTROL_STAGE_SETUP) return true;
/*printf("ctl req stage=%s rt=%s, wIndex=%04x, bReq=%02x, wValue=%04x wLength=%04x\n",
stages[stage], types[req->bmRequestType_bit.type],
req->wIndex, req->bRequest, req->wValue, req->wLength);*/
/*printf("ctl req rhport=%02x, stage=%02x, wIndex=%04x, bReq=%02x, wValue=%04x\n",
rhport, stage,
req->wIndex, req->bRequest, req->wValue);*/
if (req->bmRequestType_bit.type != TUSB_REQ_TYPE_VENDOR) return true;
if (req->bmRequestType_bit.type == TUSB_REQ_TYPE_VENDOR) {
if (stage == CONTROL_STAGE_DATA) {
struct itu_cmd cmd = curcmd;
if (req->bRequest >= ITU_CMD_I2C_IO && req->bRequest <= ITU_CMD_I2C_IO_BEGINEND
&& cmd.cmd == req->bRequest && cmd.flags == req->wValue
&& cmd.addr == req->wIndex && cmd.len == req->wLength) {
printf("WDATA a=%04hx l=%04hx ", cmd.addr, cmd.len);
printf("data=%02x %02x...\n", rxbuf[0], rxbuf[1]);
status = i2ctu_write(cmd.flags, cmd.cmd & ITU_CMD_I2C_IO_DIR_MASK,
cmd.addr, rxbuf, cmd.len > sizeof rxbuf ? sizeof rxbuf : cmd.len);
// cancel curcmd
curcmd.cmd = 0xff;
}
return true;
} else if (stage == CONTROL_STAGE_SETUP) {
switch (req->bRequest) {
case ITU_CMD_ECHO: { // flags to be echoed back, addr unused, len=2
if (req->wLength != 2) return false; // bad length -> let's stall
@ -100,12 +94,11 @@ static bool iub_ctl_req(uint8_t rhport, uint8_t stage, tusb_control_request_t co
if (req->wLength != 4) return false;
const uint32_t func = i2ctu_get_func();
uint8_t rv[4];
rv[0]=func&0xff;
rv[1]=(func>>8)&0xff;
rv[2]=(func>>16)&0xff;
rv[3]=(func>>24)&0xff;
return tud_control_xfer(rhport, req, rv, sizeof rv);
txbuf[0]=func&0xff;
txbuf[1]=(func>>8)&0xff;
txbuf[2]=(func>>16)&0xff;
txbuf[3]=(func>>24)&0xff;
return tud_control_xfer(rhport, req, txbuf, 4);
}
break;
case ITU_CMD_SET_DELAY: { // flags=delay, addr unused, len=0
@ -132,50 +125,42 @@ static bool iub_ctl_req(uint8_t rhport, uint8_t stage, tusb_control_request_t co
case ITU_CMD_I2C_IO_BEGIN: // addr: I2C address
case ITU_CMD_I2C_IO_END: // len: transfer size
case ITU_CMD_I2C_IO_BEGINEND: { // (transfer dir is in flags)
/*struct itu_cmd cmd;
cmd.cmd = req->bRequest;
struct itu_cmd cmd;
cmd.flags = req->wValue;
cmd.addr = req->wIndex;
cmd.len = req->wLength;
curcmd = cmd;*/
cmd.cmd = req->bRequest;
// TODO: what's the max value of wLength? does this need
// to be handled separately in the data stage as well?
// will the entire thing be read into one big chunk, or
// does it also get split up into buffers of eg. 64 bytes?
uint8_t buf[req->wLength];
//printf("flags=%04x\n", req->wValue);
if (req->wValue & I2C_M_RD) { // read from I2C device
printf("read addr=%04hx len=%04hx ", req->wIndex, req->wLength);
status = i2ctu_read(req->wValue, req->bRequest & ITU_CMD_I2C_IO_DIR_MASK,
req->wIndex, buf, sizeof buf);
printf("data=%02x %02x...\n", buf[0], buf[1]);
return tud_control_xfer(rhport, req, buf, req->wLength);
if (cmd.flags & I2C_M_RD) { // read from I2C device
printf("read addr=%04hx len=%04hx ", cmd.addr, cmd.len);
status = i2ctu_read(cmd.flags, cmd.cmd & ITU_CMD_I2C_IO_DIR_MASK,
cmd.addr, txbuf, cmd.len);
printf("data=%02x %02x...\n", txbuf[0], txbuf[1]);
return tud_control_xfer(rhport, req, txbuf,
cmd.len > sizeof txbuf ? sizeof txbuf : cmd.len);
} else { // write
return true; // handled in DATA stage
/*// FIXME: THIS NO WORKY! STUFF IN BUFFER IS NONSENSE
bool rv = tud_control_xfer(rhport, req, buf, req->wLength);
if (rv) {
printf("data=%02x %02x...\n", buf[0], buf[1]);
status = i2ctu_write(req->wValue, req->bRequest & ITU_CMD_I2C_IO_DIR_MASK,
req->wIndex, buf, sizeof buf);
printf("write addr=%04hx len=%04hx ", cmd.addr, cmd.len);
if (cmd.len == 0) { // address probe -> do this here
uint8_t bleh = 0;
status = i2ctu_write(cmd.flags, cmd.cmd & ITU_CMD_I2C_IO_DIR_MASK,
cmd.addr, &bleh, 0);
printf("probe -> %d\n", status);
return tud_control_status(rhport, req);
} else {
printf("no data :/\n");
status = ITU_STATUS_ADDR_NAK;
// handled in DATA stage!
curcmd = cmd;
bool rv = tud_control_xfer(rhport, req, rxbuf,
cmd.len > sizeof rxbuf ? sizeof rxbuf : cmd.len);
return rv;
}
return rv;*/
}
}
break;
default:
printf("I2C-Tiny-USB: unknown command %02x\n", req->bRequest);
return false; // unknown!
return false;
}
} else {
printf("I2C-Tiny-USB: bad request type %02x\n", req->bmRequestType);
return false; // not a vendor command? no clue what to do with it!
}
} else return true; // other stage...
}
// never actually called fsr