usb_cables: improve pin test and make it more simpler

This commit is contained in:
King Kévin 2020-01-08 16:26:25 +01:00
parent 7dd50762b8
commit e84a481669
3 changed files with 211 additions and 101 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -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