diff --git a/application.c b/application.c index 9f702f9..823503e 100644 --- a/application.c +++ b/application.c @@ -1128,54 +1128,55 @@ static void command_cplug(void* argument) // check if plug is present in socket by checking if the GND pins are interconnected bool gnd_any = false; bool gnd_all = true; - uint8_t gnd; + bool gnd; gnd = usb_cables_test_pins(&usb_pins[connector->pins[1]], &usb_pins[connector->pins[12]]); // A1- A12 GND - gnd_any |= (gnd >= 0x11); - gnd_all &= (gnd >= 0x11); + gnd_any |= gnd; + gnd_all &= gnd; gnd = usb_cables_test_pins(&usb_pins[connector->pins[1]], &usb_pins[connector->pins[12 + 1]]); // A1- B1 GND - gnd_any |= (gnd >= 0x11); - gnd_all &= (gnd >= 0x11); + gnd_any |= gnd; + gnd_all &= gnd; gnd = usb_cables_test_pins(&usb_pins[connector->pins[1]], &usb_pins[connector->pins[12 + 12]]); // A1- B12 GND - gnd_any |= (gnd >= 0x11); - gnd_all &= (gnd >= 0x11); + gnd_any |= gnd; + gnd_all &= gnd; gnd = usb_cables_test_pins(&usb_pins[connector->pins[12]], &usb_pins[connector->pins[12 + 1]]); // A12 - B1 GND - gnd_any |= (gnd >= 0x11); - gnd_all &= (gnd >= 0x11); + gnd_any |= gnd; + gnd_all &= gnd; gnd = usb_cables_test_pins(&usb_pins[connector->pins[12]], &usb_pins[connector->pins[12 + 12]]); // A12 - B12 GND - gnd_any |= (gnd >= 0x11); - gnd_all &= (gnd >= 0x11); + gnd_any |= gnd; + gnd_all &= gnd; gnd = usb_cables_test_pins(&usb_pins[connector->pins[12 + 1]], &usb_pins[connector->pins[12 + 12]]); // B1- B12 GND - gnd_any |= (gnd >= 0x11); - gnd_all &= (gnd >= 0x11); + gnd_any |= gnd; + gnd_all &= gnd; printf("- %s GND pins interconnected\n", gnd_all ? "all" : (gnd_any ? "some" : "no")); // check if plug is present in socket by checking if the VBUS pins are interconnected bool vbus_any = false; bool vbus_all = true; - uint8_t vbus; + bool vbus; vbus = usb_cables_test_pins(&usb_pins[connector->pins[4]], &usb_pins[connector->pins[9]]); // A4- A9 VBUS - vbus_any |= (vbus >= 0x44); - vbus_all &= (vbus >= 0x44); + vbus_any |= vbus; + vbus_all &= vbus; vbus = usb_cables_test_pins(&usb_pins[connector->pins[4]], &usb_pins[connector->pins[12 + 4]]); // A4- B4 VBUS - vbus_any |= (vbus >= 0x44); - vbus_all &= (vbus >= 0x44); + vbus_any |= vbus; + vbus_all &= vbus; vbus = usb_cables_test_pins(&usb_pins[connector->pins[4]], &usb_pins[connector->pins[12 + 9]]); // A4- B9 VBUS - vbus_any |= (vbus >= 0x44); - vbus_all &= (vbus >= 0x44); + vbus_any |= vbus; + vbus_all &= vbus; vbus = usb_cables_test_pins(&usb_pins[connector->pins[9]], &usb_pins[connector->pins[12 + 4]]); // A9 - B4 VBUS - vbus_any |= (vbus >= 0x44); - vbus_all &= (vbus >= 0x44); + vbus_any |= vbus; + vbus_all &= vbus; vbus = usb_cables_test_pins(&usb_pins[connector->pins[9]], &usb_pins[connector->pins[12 + 9]]); // A9 - B9 VBUS - vbus_any |= (vbus >= 0x44); - vbus_all &= (vbus >= 0x44); + vbus_any |= vbus; + vbus_all &= vbus; vbus = usb_cables_test_pins(&usb_pins[connector->pins[12 + 4]], &usb_pins[connector->pins[12 + 9]]); // B4- B9 VBUS - vbus_any |= (vbus >= 0x44); - vbus_all &= (vbus >= 0x44); + vbus_any |= vbus; + vbus_all &= vbus; printf("- %s VBUS pins interconnected\n", vbus_all ? "all" : (vbus_any ? "some" : "no")); // check if it is a powered cable (VCONN is connected to ground by Ra), or connected to sink - uint8_t cc1 = usb_cables_test_pins(&usb_pins[connector->pins[5]], &usb_pins[connector->pins[12 + 1]]); // A5 CC1 - A1 GND - printf("- CC1 %sconnected to GND (%02x)\n", (cc1 >= 0x21) ? "" : "not ", cc1); - uint8_t cc2 = usb_cables_test_pins(&usb_pins[connector->pins[12 + 5]], &usb_pins[connector->pins[12 + 1]]); // B5 CC1 - A1 GND - printf("- CC2 %sconnected to GND (%02x)\n", (cc2 >= 0x21) ? "" : "not ", cc2); + bool cc1 = usb_cables_test_pins(&usb_pins[connector->pins[5]], &usb_pins[connector->pins[12 + 1]]); // A5 CC1 - A1 GND + printf("- CC1 %sconnected to GND\n", cc1 ? "" : "not "); + bool cc2 = usb_cables_test_pins(&usb_pins[connector->pins[12 + 5]], &usb_pins[connector->pins[12 + 1]]); // B5 CC1 - A1 GND + printf("- CC2 %sconnected to GND\n", cc2 ? "" : "not "); +/* if (cc1 >= 0x41) { puts("- this is a powered cable (CC1 is VCONN)\n"); } else if (cc1 >= 0x21) { @@ -1191,12 +1192,13 @@ static void command_cplug(void* argument) } else { puts("- there is no plug/cable\n"); } +*/ // check if it should be connected to a source cc1 = usb_cables_test_pins(&usb_pins[connector->pins[5]], &usb_pins[connector->pins[4]]); // A5 CC1 - A4 VBUS - printf("- CC1 %sconnected to VBUS (%02x)\n", (cc1 >= 0x22) ? "" : "not ", cc1); + printf("- CC1 %sconnected to VBUS\n", cc1 ? "" : "not "); cc2 = usb_cables_test_pins(&usb_pins[connector->pins[12 + 5]], &usb_pins[connector->pins[4]]); // B5 CC1 - A1 VBUS - printf("- CC2 %sconnected to VBUS (%02x)\n", (cc2 >= 0x22) ? "" : "not ", cc2); - if (cc1 >= 0x22 || cc2 >= 0x22) { + printf("- CC2 %sconnected to VBUS\n", cc2 ? "" : "not "); + if (cc1 || cc2) { puts("- this cable is to be connected to a source on the other end (A plug)\n"); } } @@ -1208,15 +1210,15 @@ static void command_cplug(void* argument) return; } puts("cable interconnection\n"); - uint8_t conn; + bool conn; conn = usb_cables_test_pins(&usb_pins[c1->pins[5]], &usb_pins[c2->pins[5]]); // A5 CC1 - A5 CC1 - printf("- CC1 %sconnected to CC1 (%02x)\n", (conn >= 0x22) ? "" : "not ", conn); + printf("- CC1 %sconnected to CC1\n", conn ? "" : "not "); conn = usb_cables_test_pins(&usb_pins[c1->pins[5]], &usb_pins[c2->pins[12 + 5]]); // A5 CC1 - B5 CC2 - printf("- CC1 %sconnected to CC2 (%02x)\n", (conn >= 0x22) ? "" : "not ", conn); + printf("- CC1 %sconnected to CC2\n", conn ? "" : "not "); conn = usb_cables_test_pins(&usb_pins[c1->pins[12 + 5]], &usb_pins[c2->pins[5]]); // B5 CC2 - A5 CC1 - printf("- CC2 %sconnected to CC1 (%02x)\n", (conn >= 0x22) ? "" : "not ", conn); + printf("- CC2 %sconnected to CC1\n", conn ? "" : "not "); conn = usb_cables_test_pins(&usb_pins[c1->pins[12 + 5]], &usb_pins[c2->pins[12 + 5]]); // B5 CC2 - B5 CC2 - printf("- CC2 %sconnected to CC2 (%02x)\n", (conn >= 0x22) ? "" : "not ", conn); + printf("- CC2 %sconnected to CC2\n", conn ? "" : "not "); } // ==================== @@ -1602,7 +1604,7 @@ void main(void) uint16_t cable_message_i = 0; // the message index of the last cable message to be displayed uint32_t cable_message_t = last_connect_time; // the time stamp of the last message update - + interactive = true; while (true) { // infinite loop iwdg_reset(); // kick the dog if (user_input_available) { // user input is available diff --git a/usb_cables.c b/usb_cables.c index a661b63..5d2628f 100644 --- a/usb_cables.c +++ b/usb_cables.c @@ -4994,78 +4994,159 @@ const struct usb_connector_t* usb_cables_get_connector(uint8_t pin) return NULL; } -uint8_t usb_cables_test_pins(const struct usb_pin_t* pin1, const struct usb_pin_t* pin2) +bool usb_cables_test_pins(const struct usb_pin_t* pin1, const struct usb_pin_t* pin2) { - cm3_assert(pin1 && pin2); + cm3_assert(pin1); + cm3_assert(pin2); if (pin1->port == pin2->port && pin1->pin == pin2->pin) { // it's the same pin - return 0xff; // all connections will work + return true; // all connections will work } - uint8_t connection = 0; // the connection result to return - for (int8_t mode = 3; mode >= 0; mode--) { // test all connection types (from most to least accurate) + /** how the connection is tested */ + enum connection_test_e { + /** the output side is driven using pull/push, the input is pulled up/down + * @note this will detect direct connections, when nothing is could be powered + * @note there is no false positive, just false negatives when resistors are in line + */ + CONNECTION_TEST_DRIVE_PULL, + /** the output side is driven using pull/push, the input is floating + * @note this can also detect connections with inline resistors, since no pull-up/down will form a voltage divider + * @note prone to noise since one side is floating + */ + CONNECTION_TEST_DRIVE_FLOAT, + /** the output side is driven using pull up/down, the input is floating + * @note this is similar to CONNECTION_TEST_DRIVE_FLOAT method, but meant for ground pins, which should not be driven. + * @note since only a weak pull-up/down resistor is used, inline resistor will lead to voltage divider and possible false negatives + */ + CONNECTION_TEST_PULL_FLOAT, + /** number of connections test */ + CONNECTION_TEST_COUNT, + }; + + // the connection details + struct usb_connection_t _connection = { + .tx_pull_float = false, + .tx_drive_float = false, + .tx_drive_pull = false, + .rx_pull_float = false, + .rx_drive_float = false, + .rx_drive_pull = false, + }; + + for (enum connection_test_e connection_test = 0; connection_test < CONNECTION_TEST_COUNT; connection_test++) { // test all connection types (from most to least accurate) for (uint8_t direction = 0; direction < 2; direction++) { // test both directions - uint32_t from_port = (0 == direction ? pin2->port : pin1->port); - uint16_t from_pin = (0 == direction ? pin2->pin : pin1->pin); - uint32_t to_port = (0 == direction ? pin1->port : pin2->port); - uint16_t to_pin = (0 == direction ? pin1->pin : pin2->pin); + const struct usb_pin_t* pin_from = (0 == direction ? pin1 : pin2); + const struct usb_pin_t* pin_to = (0 == direction ? pin2 : pin1); + const uint32_t from_port = pin_from->port; + const uint16_t from_pin = pin_from->pin; + const uint32_t to_port = pin_to->port; + const uint16_t to_pin = pin_to->pin; // don't drive a ground or shield pin high (shield might be connected to ground) - if (((0 == direction && (USB_PIN_TYPE_GROUND == pin2->type || USB_PIN_TYPE_SHIELD == pin2->type)) || \ - (1 == direction && (USB_PIN_TYPE_GROUND == pin1->type || USB_PIN_TYPE_SHIELD == pin1->type))) && \ - (USB_PIN_CONNECTION_DRIVE_FLOAT == (1 << mode) || USB_PIN_CONNECTION_DRIVE_PULL == (1 << mode))) { + if ((USB_PIN_TYPE_GROUND == pin_from->type || USB_PIN_TYPE_SHIELD == pin_from->type) && \ + (CONNECTION_TEST_DRIVE_PULL == connection_test || CONNECTION_TEST_DRIVE_FLOAT == connection_test)) { continue; } - // set pin mode - switch ((1 << mode)) { - case USB_PIN_CONNECTION_DRIVE_FLOAT: - gpio_set_mode(from_port, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, from_pin); - gpio_set_mode(to_port, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, to_pin); - break; - case USB_PIN_CONNECTION_PULL_FLOAT: + // set pin mode for sending pin + switch (connection_test) { + case CONNECTION_TEST_PULL_FLOAT: gpio_set_mode(from_port, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, from_pin); - gpio_set_mode(to_port, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, to_pin); break; - case USB_PIN_CONNECTION_DRIVE_PULL: + case CONNECTION_TEST_DRIVE_PULL: + case CONNECTION_TEST_DRIVE_FLOAT: gpio_set_mode(from_port, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, from_pin); - gpio_set_mode(to_port, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, to_pin); break; default: + continue; // unknown mode break; } // test pattern + // note: the pattern length, and pauses between toggles are purely found out through testing + // cross talk, noise, and capacitance are a pain to correctly get rid off + // the result is not perfect, but often checking both directions eliminates false positives bool connected = true; - for (uint8_t pattern = 0; pattern < 8 && connected; pattern++) { - if (0 == pattern % 2) { - gpio_clear(from_port, from_pin); - gpio_set(to_port, to_pin); - sleep_us(25); // wait for GPIO/line to settle - if (gpio_get(to_port, to_pin)) { - connected = false; - } + for (uint8_t pattern = 0; pattern < 6 && connected; pattern++) { // the pattern is just a sequence on high/low + // set alternatively high and low on one pin, and check level on the other pin + gpio_set_mode(to_port, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, to_pin); // first pull to remove capacitance + if (0 == pattern % 2) { // test if high level is detected + gpio_clear(to_port, to_pin); // pull down + gpio_set(from_port, from_pin); // set high + } else { // test if low level is detected + gpio_set(to_port, to_pin); // pull up + gpio_clear(from_port, from_pin); // set low + } + if (USB_PIN_TYPE_POWER == pin_from->type) { + sleep_us(1000); // wait to cancel capacitance (10 nF, as used on VBUS in C plug) } else { - gpio_set(from_port, from_pin); - gpio_clear(to_port, to_pin); - sleep_us(25); // wait for GPIO/line to settle - if (0 == gpio_get(to_port, to_pin)) { - connected = false; - } + sleep_us(10); // wait to settle + } + switch (connection_test) { // get to float (capacitance should be canceled) + case CONNECTION_TEST_DRIVE_FLOAT: + case CONNECTION_TEST_PULL_FLOAT: + gpio_set_mode(to_port, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, to_pin); + break; + default: + break; + } + sleep_us(10); // wait for signal to settle + if ((0 == pattern % 2) && (0 == gpio_get(to_port, to_pin))) { // high level not detected + connected = false; + } else if ((pattern % 2) && gpio_get(to_port, to_pin)) { // low level not detected + connected = false; } } - if (connected) { - connection |= (1 << (mode + direction * 4)); + // test connection result + if (0 == direction) { + switch (connection_test) { + case CONNECTION_TEST_PULL_FLOAT: + _connection.tx_pull_float = connected; + break; + case CONNECTION_TEST_DRIVE_FLOAT: + _connection.tx_drive_float = connected; + break; + case CONNECTION_TEST_DRIVE_PULL: + _connection.tx_drive_pull = connected; + break; + default: + break; + } + } else { + switch (connection_test) { + case CONNECTION_TEST_PULL_FLOAT: + _connection.rx_pull_float = connected; + break; + case CONNECTION_TEST_DRIVE_FLOAT: + _connection.rx_drive_float = connected; + break; + case CONNECTION_TEST_DRIVE_PULL: + _connection.rx_drive_pull = connected; + break; + default: + break; + } } - } - if ((connection & (1 << (mode + 0))) && (connection & (1 << (mode + 4)))) { // more accurate connection showed, no need to continue with least accurate test - break; - } - } + } // direction + } // connection_test // put back to floating gpio_set_mode(pin1->port, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, pin1->pin); gpio_set_mode(pin2->port, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, pin2->pin); - return connection; + // summarize connection + bool connected = true; + if (USB_PIN_TYPE_GROUND == pin1->type || USB_PIN_TYPE_SHIELD == pin1->type) { + connected &= _connection.tx_pull_float; + } else { // not a ground pin + connected &= _connection.tx_drive_float | _connection.tx_drive_pull; + } + if (USB_PIN_TYPE_GROUND == pin2->type || USB_PIN_TYPE_SHIELD == pin2->type) { + connected &= _connection.rx_pull_float; + } else { // not a ground pin + connected &= _connection.rx_drive_float | _connection.rx_drive_pull; + } + + return connected; } bool usb_cables_test_ground(const struct usb_connector_t** connectors, uint8_t connectors_nb, bool* connected) @@ -5236,8 +5317,8 @@ bool usb_cables_test_cable(const struct usb_cable_t* cable, uint8_t* defined, ui } } - uint8_t connection = usb_cables_test_pins(usb_pin_from, usb_pin_to); - if (connection >= 0x22) { + const bool connection = usb_cables_test_pins(usb_pin_from, usb_pin_to); + if (connection) { if (pair_defined) { _defined++; } else if (pair_optional) { @@ -5292,7 +5373,7 @@ uint8_t** usb_cables_test_connections(const struct usb_connector_t** connectors, continue; } for (uint8_t pin_from_i = 0; pin_from_i < connector_from->pins_nb; pin_from_i++) { // test from every pin - uint8_t pin_from = connector_from->pins[pin_from_i]; // get pin + const uint8_t pin_from = connector_from->pins[pin_from_i]; // get pin if (pin_from >= LENGTH(usb_pins)) { // check pin continue; } @@ -5309,7 +5390,7 @@ uint8_t** usb_cables_test_connections(const struct usb_connector_t** connectors, continue; } for (uint8_t pin_to_i = 0; pin_to_i < connector_to->pins_nb; pin_to_i++) { // test to every pin - uint8_t pin_to = connector_to->pins[pin_to_i]; // get pin + const uint8_t pin_to = connector_to->pins[pin_to_i]; // get pin const struct usb_pin_t* usb_pin_to = &usb_pins[pin_to]; // get to pin if (pin_to >= LENGTH(usb_pins)) { // check pin continue; @@ -5335,8 +5416,8 @@ uint8_t** usb_cables_test_connections(const struct usb_connector_t** connectors, } // test connection - uint8_t connection = usb_cables_test_pins(usb_pin_from, usb_pin_to); - if (connection >= 0x22) { + const bool connected = usb_cables_test_pins(usb_pin_from, usb_pin_to); + if (connected) { if (UINT16_MAX == (*connections_nb)) { // we already found the maximum of connections return (uint8_t**)connections; } diff --git a/usb_cables.h b/usb_cables.h index bfce318..7326024 100644 --- a/usb_cables.h +++ b/usb_cables.h @@ -22,21 +22,49 @@ enum usb_pin_type_e { USB_PIN_TYPE_UNDEFINED, /*< the default type, undefined */ USB_PIN_TYPE_GROUND, /*< ground (including ground drain) */ - USB_PIN_TYPE_POWER, /*< power (VBUS) */ + USB_PIN_TYPE_POWER, /*< power (VBUS, VCONN) */ USB_PIN_TYPE_SHIELD, /*< shield */ USB_PIN_TYPE_DIFFERENTIAL, /*< differential data pairs (D+/-, SSTX+/-, SSRX+/-) */ USB_PIN_TYPE_IDENTIFICATION, /*< pin to identify type (often connected to power or ground through resistor) */ USB_PIN_TYPE_OTHER, /*< any other type */ }; -/** the type of connection between two pins - * @note to be used as bit mask - * @note ordered from least most most reliable/accurate - */ -enum usb_pin_connection_e { - USB_PIN_CONNECTION_DRIVE_FLOAT = (1 << 0), /*< the output side is driven using pull/push, the input is floating */ - USB_PIN_CONNECTION_PULL_FLOAT = (1 << 1), /*< the output side is driven using pull up/down, the input is floating */ - USB_PIN_CONNECTION_DRIVE_PULL = (1 << 2), /*< the output side is driven using pull/push, the input is pulled up/down */ +/** the type of connection between two pins */ +struct usb_connection_t { + /** pin1 is driven using pull up/down, pin2 is floating, set if a pattern set on pin1 is received on pin2 + * @note this method is similar to tx_drive_float, but meant for ground pins, which should not be driven. + * @note prone to false negative when there is an inline resistor (which forms a voltage divider) + * @note this might not be set (e.g. tested) if tx_drive_float or tx_drive_pull are set + */ + bool tx_pull_float; + /** pin1 is driven using push/pull, pin2 is floating, set if a pattern set on pin1 is received on pin2 + * @note this can also detect connections with inline resistors, since no pull-up/down will form a voltage divider + * @note prone to false positive since pin2 is floating + * @note this might not be set (e.g. tested) tx_drive_pull is set + */ + bool tx_drive_float; + /** pin1 is driven using push/pull, pin2 is pulled up/down, set if a pattern set on pin1 is received on pin1 + * @note this will detect direct connections, when nothing is could be powered + * @note prone to false negative when there is an inline resistor (which forms a voltage divider) + */ + bool tx_drive_pull; + /** pin2 is driven using pull up/down, pin1 is floating, set if a pattern set on pin2 is received on pin1 + * @note this method is similar to tx_drive_float, but meant for ground pins, which should not be driven. + * @note prone to false negative when there is an inline resistor (which forms a voltage divider) + * @note this might not be set (e.g. tested) if tx_drive_float or tx_drive_pull are set + */ + bool rx_pull_float; + /** pin2 is driven using push/pull, pin1 is floating, set if a pattern set on pin2 is received on pin1 + * @note this can also detect connections with inline resistors, since no pull-up/down will form a voltage divider + * @note prone to false positive since pin1 is floating + * @note this might not be set (e.g. tested) tx_drive_pull is set + */ + bool rx_drive_float; + /** pin2 is driven using push/pull, pin1 is pulled up/down, set if a pattern set on pin2 is received on pin1 + * @note this will detect direct connections, when nothing is could be powered + * @note prone to false negative when there is an inline resistor (which forms a voltage divider) + */ + bool rx_drive_pull; }; /** USB pin definition */ @@ -196,10 +224,9 @@ const struct usb_connector_t* usb_cables_get_connector(uint8_t pin); /** test if two pins are connected * @param[in] pin1 first pin * @param[in] pin2 second pin - * @return first nibble corresponds to the connection types (usb_pin_connection_e) from pin1 to pin2, second nibble from pin2 to pin1 - * @note setting both levels (high, low) in both directions (pin1 to pin2 and pin2 to pin1) are tested, except for ground pins + * @return if the two pins are connected */ -uint8_t usb_cables_test_pins(const struct usb_pin_t* pin1, const struct usb_pin_t* pin2); +bool usb_cables_test_pins(const struct usb_pin_t* pin1, const struct usb_pin_t* pin2); /** test connectors for connections between ground pins of the connectors * @param[in] connectors connectors to test * @param[in] connectors_nb numbers of connectors