application: add frequency measurement in monitor single mode

This commit is contained in:
King Kévin 2021-05-07 12:16:24 +02:00
parent 97818981fb
commit 16cb734f6f
2 changed files with 75 additions and 4 deletions

View File

@ -42,6 +42,7 @@ to stop monitoring, press any key.
to monitor the activity of a single channel, use the `monitor_single` command.
provide as argument the channel you want to monitor.
this works like the `monitor` command, but has the advantage to detect high voltages down to 1.5V (ideal for 1.8V logic).
it will also show the (maximum) frequency of the signal (useful to clock and baud rate measurement).
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.

View File

@ -75,7 +75,15 @@ static uint8_t channel_start = 0; /**< first signal of range to probe */
static uint8_t channel_stop = CHANNEL_NUMBERS - 1; /**< last signal of range to probe */
/** timer ID for timer to measure activity timing */
#define MONITOR_TIMER 2
#define MONITOR_TIMER 3
/** timer to measure frequency and baud rate
* @note PA3/USART2_RX/TIM2_CH4 (PA2/USART2_TX/TIM2_CH3 could also be used)
* @{
*/
#define FREQUENCY_TIMER 2 /**< timer peripheral ID */
#define FREQUENCY_CHANNEL 4 /**< timer channel to capture edges */
#define FREQUENCY_AF GPIO_AF1 /**< alternate function for UART pin to use as timer channel */
/** @} */
/** UART peripheral for signal reading (TX and RX are connected together)
* @{
@ -525,6 +533,34 @@ static void command_monitor(void* argument)
rcc_periph_clock_disable(RCC_TIM(MONITOR_TIMER)); // disable clock for timer peripheral
}
volatile bool pulse_flag = false; /**< set when a small pulse time is detected */
volatile uint32_t pulse_duration = UINT32_MAX; /**< smallest pulse duration measured */
/** timer ISR to measure edge timing */
void TIM_ISR(FREQUENCY_TIMER)(void)
{
static uint32_t pulse = UINT32_MAX; // measured pulse duration (MAX is an invalid value)
if (timer_get_flag(TIM(FREQUENCY_TIMER), TIM_SR_UIF)) { // overflow update event happened
timer_clear_flag(TIM(FREQUENCY_TIMER), TIM_SR_UIF); // clear flag
pulse = UINT32_MAX; // timer 2 is already a 32-bit timer, no need to count the overflow
}
if (timer_get_flag(TIM(FREQUENCY_TIMER), TIM_SR_CCOF(FREQUENCY_CHANNEL))) { // capture overflow occurred
timer_clear_flag(TIM(FREQUENCY_TIMER), TIM_SR_CCOF(FREQUENCY_CHANNEL)); // clear flag
pulse = UINT32_MAX; // invalidate measured pulse
}
if (timer_get_flag(TIM(FREQUENCY_TIMER), TIM_SR_CCIF(FREQUENCY_CHANNEL))) {
uint16_t edge = TIM_CCR(FREQUENCY_TIMER, FREQUENCY_CHANNEL); // retrieve captured value (clears flag)
if (UINT32_MAX != pulse) { // only calculate pulse if previous edge is valid
pulse = ((pulse & 0xffff0000) + edge) - (pulse & 0xffff); // calculate pulse duration
if (pulse < pulse_duration) { // save new pulse duration if smaller
pulse_duration = pulse; // save measurement for user
pulse_flag = true; // notify user we got a measurement
}
}
pulse = edge; // replace with current edge time
}
}
/** monitor single channel for activity
* @param[in] argument channel number
*/
@ -558,7 +594,8 @@ static void command_monitor_single(void* argument)
// select channel
rcc_periph_clock_enable(GPIO_RCC(UART_RX)); // enable clock for USART RX pin port peripheral
gpio_mode_setup(GPIO_PORT(UART_RX), GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN(UART_RX)); // use as input (it is pulled up by level shifter)
gpio_mode_setup(GPIO_PORT(UART_RX), GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN(UART_RX)); // use as input for the timer (it is pulled up by level shifter)
gpio_set_af(GPIO_PORT(UART_RX), FREQUENCY_AF, GPIO_PIN(UART_RX)); // set alternate function to timer channel
mux_select(channel); // select channel
gpio_clear(GPIO_PORT(SHIFT_EN_PIN), GPIO_PIN(SHIFT_EN_PIN)); // connect target voltage to level shifters pull-up
@ -566,9 +603,29 @@ static void command_monitor_single(void* argument)
printf("CH%02u is pulled to target voltage by 10 kOhm\n", channel);
puts("high = 1.5-5.5V, 'X' shows multiple changes\n");
puts("press any key to stop monitoring\n");
puts("time (s) CH\n");
puts("time (s) CH freq. (Hz)\n");
// setup timer to measure milliseconds
// setup timer to measure frequency
rcc_periph_clock_enable(RCC_TIM(FREQUENCY_TIMER)); // enable clock for timer peripheral
rcc_periph_reset_pulse(RST_TIM(FREQUENCY_TIMER)); // reset timer state
timer_disable_counter(TIM(FREQUENCY_TIMER)); // disable timer to configure it
timer_set_mode(TIM(FREQUENCY_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(FREQUENCY_TIMER), 0); // don't use prescale so to get the most precise measurement
timer_ic_set_input(TIM(FREQUENCY_TIMER), TIM_IC(FREQUENCY_CHANNEL), TIM_IC_IN_TI(FREQUENCY_CHANNEL)); // configure the input capture ICx to use the right channel TIn
timer_ic_set_filter(TIM(FREQUENCY_TIMER), TIM_IC(FREQUENCY_CHANNEL), TIM_IC_OFF); // use no filter input to keep precise timing
timer_ic_set_polarity(TIM(FREQUENCY_TIMER), TIM_IC(FREQUENCY_CHANNEL), TIM_IC_FALLING); // capture on falling edge
timer_ic_set_prescaler(TIM(FREQUENCY_TIMER), TIM_IC(FREQUENCY_CHANNEL), TIM_IC_PSC_OFF); // don't use any prescaler since we want to capture every pulse
timer_ic_enable(TIM(FREQUENCY_TIMER), TIM_IC(FREQUENCY_CHANNEL)); // enable capture interrupt
timer_clear_flag(TIM(FREQUENCY_TIMER), TIM_SR_CCIF(FREQUENCY_CHANNEL)); // clear input compare flag
timer_enable_irq(TIM(FREQUENCY_TIMER), TIM_DIER_CCIE(FREQUENCY_CHANNEL)); // enable capture interrupt
timer_update_on_overflow(TIM(FREQUENCY_TIMER)); // only use counter overflow as UEV source (use overflow to measure longer times)
timer_clear_flag(TIM(FREQUENCY_TIMER), TIM_SR_UIF); // clear overflow flag
timer_enable_irq(TIM(FREQUENCY_TIMER), TIM_DIER_UIE); // enable update interrupt for timer
nvic_enable_irq(NVIC_TIM_IRQ(FREQUENCY_TIMER)); // catch interrupts for this timer
pulse_duration = UINT32_MAX; // reset pulse duration
timer_enable_counter(TIM(FREQUENCY_TIMER)); // enable timer
// 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
@ -607,6 +664,12 @@ static void command_monitor_single(void* argument)
} else {
putc('0');
}
if (pulse_flag) { // frequency measurement worked
const uint32_t freq = rcc_ahb_frequency / pulse_duration;
printf(" %u", freq);
pulse_duration = UINT32_MAX; // clear measurement
pulse_flag = false; // clear flag
}
puts("\n");
channel_changes = 0; // clear changes
}
@ -626,6 +689,13 @@ static void command_monitor_single(void* argument)
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
}
/** set first channel of range to scan