diff --git a/README.md b/README.md index 35d7567..7df1292 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/application.c b/application.c index 87b1b16..ef594cc 100644 --- a/application.c +++ b/application.c @@ -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