diff --git a/README.md b/README.md index f061d50..1da484c 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ summary this tools allow to identify if channels (e.g. lines) are inputs or outputs. up to 16 channels can be identified at once. -it also allows to monitor the channel activity. +it also allows to monitor the channel activity, identify UART TX and RX signals. usage ----- @@ -51,6 +51,11 @@ the configuration is displayed as it finds a better match. it will also show the decoded data. this mainly works for ASCII based communication (e.g. human readable debug logs), and might not give correct results for binary communication. +to find the corresponding UART RX pin (e.g. once UART TX pin has been identified using the `uart_auto` command), use the `uart_rx` commands. +provide as argument the channel for the UART TX pin, and the UART baud rate and frame settings (e.g. 115200 8N1). +data will be transmitted on the channels to probe and if we received the echo back on the TX, we fond the RX. +this detection method only works if the target echoes back the data (e.g. on login screens). + you can also reset the target board if you connected to target reset pin to the SWJ finder. you can select of to drive the reset pin (OD for open-drain, PP for push-pull) and active level (H for high, L for low) using the `reset [ODL|ODH|PPL|PPH]` command. to assert or release the reset, us the `reset 1` or `reset 0` commands. diff --git a/application.c b/application.c index 52f6478..c383241 100644 --- a/application.c +++ b/application.c @@ -1005,6 +1005,202 @@ static void command_uart_autodetect(void* argument) rcc_periph_clock_disable(RCC_USART(UART_ID)); // disable clock for USART peripheral } +/** find the RX UART pin + * @param TX channel, baud rate, data bits parity and stop bits + */ +static void command_uart_probe(void* argument) +{ + char* line = (char*)argument; // get line of arguments + if (NULL == argument || 0 == strlen(line)) { // ensure argument is provided + goto command_uart_probe_error; + return; + } + + // get channel + const char* delimiter = " "; // words are separated by spaces + char* word = strtok(line, delimiter); // get first word + if (!word) { + goto command_uart_probe_error; + return; + } + const int32_t channel = strtol(word, NULL, 10); // get signed integer + if ((channel < 0) || (channel >= CHANNEL_NUMBERS)) { // verify argument + printf("channel %d out of range (0-%u)\n", channel, CHANNEL_NUMBERS - 1); + return; + } + + // get baud rate + word = strtok(NULL, delimiter); // get second word + if (!word) { + goto command_uart_probe_error; + return; + } + const int32_t baudrate = strtol(word, NULL, 10); // get signed integer + if ((baudrate < 1200) || (baudrate >= 2000000)) { // verify argument + printf("baud rate %d out of range (1200-2000000 bps)\n", baudrate); + return; + } + + // get frame format (data bits, parity, stop bits + word = strtok(NULL, delimiter); // get third word + if (!word || 3 != strlen(word)) { + printf("unknown frame format %s\n", word); + goto command_uart_probe_error; + return; + } + // data bits + if (word[0] < '5' || word[0] > '8') { + printf("unknown data bits %c\n", word[0]); + goto command_uart_probe_error; + return; + } + const uint8_t databits = word[0] - '0'; + // parity + enum usart_enhanced_parity_t parity; + switch(word[1]) { + case 'N': + case 'n': + parity = USART_ENHANCED_PARITY_NONE; + break; + case 'E': + case 'e': + parity = USART_ENHANCED_PARITY_EVEN; + break; + case 'O': + case 'o': + parity = USART_ENHANCED_PARITY_ODD; + break; + case 'M': + case 'm': + parity = USART_ENHANCED_PARITY_MARK; + break; + case 'S': + case 's': + parity = USART_ENHANCED_PARITY_SPACE; + break; + default: + printf("unknown parity %c\n", word[1]); + goto command_uart_probe_error; + return; + } + // stop bits + if (word[2] < '1' || word[2] > '2') { + printf("unknown stop bits %c\n", word[2]); + goto command_uart_probe_error; + return; + } + const uint8_t stopbits = word[2] - '0'; + + // verify target voltage is OK + const float* voltages = measure_voltages(); // get target voltage + if (voltages[1] < 1.5) { + puts("target voltage too low: "); + print_fpu(voltages[1], 2); + puts(" < 1.5V\n"); + return; + } else { + puts("target voltage: "); + print_fpu(voltages[1], 2); + puts("V\n"); + } + + // configure UART + rcc_periph_clock_enable(GPIO_RCC(UART_TX)); // enable clock for USART TX pin port peripheral + gpio_mode_setup(GPIO_PORT(UART_TX), GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN(UART_TX)); // use pin for UART data (normally output, but in half duplex it can become input) + gpio_set_af(GPIO_PORT(UART_TX), UART_AF, GPIO_PIN(UART_TX)); // set alternate function to UART + gpio_clear(GPIO_PORT(SHIFT_EN_PIN), GPIO_PIN(SHIFT_EN_PIN)); // connect target voltage to level shifters pull-up + rcc_periph_clock_enable(RCC_USART(UART_ID)); // enable USART peripheral + rcc_periph_reset_pulse(RST_USART(FREQUENCY_TIMER)); // reset USART peripheral + usart_set_baudrate(USART(UART_ID), baudrate); // configure UART to slowest baud rate + if (2 == stopbits) { + usart_set_stopbits(USART(UART_ID), USART_STOPBITS_2); // ensure we have 2 stop bits + } else { + usart_set_stopbits(USART(UART_ID), USART_STOPBITS_1); // 1 stop-bits also complies to 2 stop-bits when receiving + } + usart_enhanced_config(USART(UART_ID), databits, parity); // set data bits and parity using enhanced library to support more modes + usart_set_mode(USART(UART_ID), USART_MODE_TX_RX); // we will send and receive data + USART_CR3(USART(UART_ID)) |= USART_CR3_HDSEL; // we will use the half-duplex mode to use the TX pin as RX + usart_enable(USART(UART_ID)); // ensure UART is enabled + + // setup timer to measure 0.1 seconds + rcc_periph_clock_enable(RCC_TIM(MONITOR_TIMER)); // enable clock for timer peripheral + rcc_periph_reset_pulse(RST_TIM(MONITOR_TIMER)); // reset timer state + timer_disable_counter(TIM(MONITOR_TIMER)); // disable timer to configure it + timer_set_mode(TIM(MONITOR_TIMER), TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); // set timer mode, use undivided timer clock, edge alignment (simple count), and count up + timer_set_prescaler(TIM(MONITOR_TIMER), (rcc_ahb_frequency / 2000) - 1); // generate half millisecond ticks (prescaler is not large enough for milliseconds) + timer_set_period(TIM(MONITOR_TIMER), 200 - 1); // set period to 0.1 seconds + timer_clear_flag(TIM(MONITOR_TIMER), TIM_SR_UIF); // clear update (overflow) flag + timer_update_on_overflow(TIM(MONITOR_TIMER)); // only use counter overflow as UEV source (use overflow as start time or timeout) + timer_one_shot_mode(TIM(MONITOR_TIMER)); // use timer one to count down once + timer_enable_counter(TIM(MONITOR_TIMER)); // start timer have the setting take effect (else the first oneshot is too short) + + // probe each channel + const char cs[] = {' ', '0', 'X'}; // characters to send + bool found = false; // to remember if we found an RX channel + printf("probing for UART RX on CH%02u-CH%02u\n", channel_start, channel_stop); + while (!timer_get_flag(TIM(MONITOR_TIMER), TIM_SR_UIF)); // wait for oneshot to complete + timer_disable_counter(TIM(MONITOR_TIMER)); // ensure the timer if off (should be in one shot) + for (uint8_t ch = channel_start; ch <= channel_stop; ch++) { // go through each channel (it might also be the one we are sending from) + putc('.'); + uint8_t success = 0; // count how often the characters has been echoed back + for (uint8_t i = 0; i < LENGTH(cs); i++) { // send each character + if (wakeup_flag || second_flag) { + iwdg_reset(); // kick the dog + wakeup_flag = false; // clear flag + second_flag = false; // clear flag + } + mux_select(ch); // select channel to transmit + USART_DR(USART(UART_ID)); // empty receive buffer + usart_get_flag(USART(UART_ID), USART_SR_TC); // read flag to clear it by writing to DR + usart_enhanced_send(USART(UART_ID), cs[i]); // send probing character (also clears TC flag) + while (!usart_get_flag(USART(UART_ID), USART_SR_TC)); // wait until transmission completed + mux_select(channel); // select channel to receive + while (!usart_get_flag(USART(UART_ID), USART_SR_RXNE)); // wait for own echo because we are in half duplex + USART_DR(USART(UART_ID)); // empty receive buffer + timer_clear_flag(TIM(MONITOR_TIMER), TIM_SR_UIF); // clear timer flag + timer_set_counter(TIM(MONITOR_TIMER), 0); // restart counter + timer_enable_counter(TIM(MONITOR_TIMER)); // start timer + while (!timer_get_flag(TIM(MONITOR_TIMER), TIM_SR_UIF)); // wait until 0.1 second has passed (it could be optimised by checking if data has been received) + timer_disable_counter(TIM(MONITOR_TIMER)); // ensure the timer if off (should be in one shot) + timer_clear_flag(TIM(MONITOR_TIMER), TIM_SR_UIF); // clear timer flag + if (usart_get_flag(USART(UART_ID), USART_SR_RXNE) && cs[i] == usart_enhanced_recv(USART(UART_ID))) { // data has been echoed back + success++; // remember data has been echoed back + } + } + if (success > 1) { + printf("\nUART RX found on CH%02u\n", ch); // show found channel to user + found = true; // remember we found a channel + } + } + if (!found) { + puts("\nUART RX not found"); + } + putc('\n'); + + // clean up + gpio_set(GPIO_PORT(SHIFT_EN_PIN), GPIO_PIN(SHIFT_EN_PIN)); // remove power from level shifters pull-up + gpio_mode_setup(GPIO_PORT(UART_RX), GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN(UART_RX)); // put pin back to safe floating mode + mux_select(-1); // disable multiplexer + timer_disable_counter(TIM(MONITOR_TIMER)); // disable timer + rcc_periph_reset_pulse(RST_TIM(MONITOR_TIMER)); // reset timer state + rcc_periph_clock_disable(RCC_TIM(MONITOR_TIMER)); // disable clock for timer peripheral + timer_disable_counter(TIM(FREQUENCY_TIMER)); // disable timer + nvic_disable_irq(NVIC_TIM_IRQ(FREQUENCY_TIMER)); // catch interrupts for this timer + timer_disable_irq(TIM(FREQUENCY_TIMER), TIM_DIER_UIE); // disable update interrupt for timer + timer_disable_irq(TIM(FREQUENCY_TIMER), TIM_DIER_CCIE(FREQUENCY_CHANNEL)); // disable capture interrupt + timer_ic_disable(TIM(FREQUENCY_TIMER), TIM_IC(FREQUENCY_CHANNEL)); // disable capture interrupt + rcc_periph_reset_pulse(RST_TIM(FREQUENCY_TIMER)); // reset timer state + rcc_periph_clock_disable(RCC_TIM(FREQUENCY_TIMER)); // disable clock for timer peripheral + + return; +command_uart_probe_error: + puts("arguments: TX_channel baudrate DatabitsParityStopbits\n"); + printf("TX_channel: 0-%u\n", CHANNEL_NUMBERS - 1); + puts("baudrate: 1200-2000000 (bps)\n"); + puts("DatabitsParityStopbits: 5-8 databits, N=none/E=even/O=odd/M=mark/S=space, 1-2:stopbits\n"); + puts("example: 01 115200 8N1\n"); +} + /** set first channel of range to scan * @param[in] argument optional pointer to first channel number */ @@ -1207,6 +1403,14 @@ static const struct menu_command_t menu_commands[] = { .argument_description = "CH", .command_handler = &command_uart_autodetect, }, + { + .shortcut = 'u', + .name = "uart_rx", + .command_description = "find UART RX pin", + .argument = MENU_ARGUMENT_STRING, + .argument_description = "TX_CH baudrate DatabitsParityStopbits", + .command_handler = &command_uart_probe, + }, { .shortcut = 'c', .name = "start",