application: add command to probe for UART RX pin

This commit is contained in:
King Kévin 2021-05-10 12:33:55 +02:00
parent 3ba5b95061
commit c98e82fedf
2 changed files with 210 additions and 1 deletions

View File

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

View File

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