BV UART: add serial configuration auto-detection command

This commit is contained in:
King Kévin 2018-07-11 08:21:41 +02:00
parent 8dd5b89944
commit 1cb08356ec
2 changed files with 380 additions and 3 deletions

View File

@ -24,13 +24,16 @@
#include <string.h> // string utilities
/* STM32 (including CM3) libraries */
#include <libopencm3/cm3/nvic.h> // interrupt utilities
#include <libopencm3/stm32/gpio.h> // general purpose input output library
#include <libopencm3/stm32/rcc.h> // real-time control clock library
#include <libopencm3/stm32/usart.h> // USART utilities
#include <libopencm3/stm32/timer.h> // timer library
/* own libraries */
#include "global.h" // board definitions
#include "print.h" // printing utilities
#include "interrupt.h" // user interrupt table
#include "menu.h" // menu definitions
#include "usart_enhanced.h" // utilities for USART enhancements
#include "busvoodoo_global.h" // BusVoodoo definitions
@ -64,6 +67,12 @@ static bool busvoodoo_uart_generic_drive = true;
/** if embedded pull-up resistors are used */
static bool busvoodoo_uart_generic_pullup = false;
/** set if the timer ISR should be set in the interrupt table instead of the vector table
* @note the vector table is faster, but doesn't allow to change the ISR
* @warning for the BusVoodoo UART (using USART3) and RS-232/RS-485 (using USART2) the RX is on TIM2_CH4 (remapped for USART3)
*/
#define BUSVOODOO_UART_TIMER_USE_INTERRUPT_TABLE false
bool busvoodoo_uart_generic_configure(const struct busvoodoo_uart_generic_specific_t* conf)
{
busvoodoo_uart_generic_specific = NULL; // reset specific information
@ -79,6 +88,9 @@ bool busvoodoo_uart_generic_configure(const struct busvoodoo_uart_generic_specif
if (conf->hwflowctl && (!conf->rts_rcc || !conf->cts_rcc)) {
return false;
}
if (conf->timer && (!conf->timer_rcc || !conf->timer_port_rcc || !(NVIC_TIM2_IRQ==conf->timer_nvic_irq || NVIC_TIM3_IRQ==conf->timer_nvic_irq || NVIC_TIM4_IRQ==conf->timer_nvic_irq || NVIC_TIM5_IRQ==conf->timer_nvic_irq))) {
return false;
}
busvoodoo_uart_generic_specific = conf;
return true;
}
@ -138,7 +150,7 @@ bool busvoodoo_uart_generic_setup(char** prefix, const char* line)
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_STOPBITS; // go to next setting
} else if (1==strlen(line)) { // setting provided
uint8_t parity = atoi(line); // parse setting
if (parity>0 && parity<6) { // check settin
if (parity>0 && parity<6) { // check setting
busvoodoo_uart_generic_parity = parity-1;
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_STOPBITS; // go to next setting
}
@ -257,6 +269,9 @@ setting_done:
printf("use LV to set pull-up voltage\n");
}
usart_enable(busvoodoo_uart_generic_specific->usart); // enable USART
// setup timer to measure RX edge timing for baud rate guessing
if (busvoodoo_uart_generic_specific->timer) {
}
busvoodoo_led_blue_off(); // disable blue LED because there is no activity
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_NONE; // restart settings next time
complete = true; // configuration is complete
@ -459,7 +474,6 @@ static bool busvoodoo_uart_generic_action(const char* action, uint32_t repetitio
return true; // all went well
}
// command handlers
/** command to perform actions
@ -650,6 +664,345 @@ static void busvoodoo_uart_generic_command_error(void* argument)
user_input_get(); // discard user input
}
/** the possible properties of a UART configuration (to be updated with every character) */
struct uart_configuration_t {
uint8_t databits; /**< data word size in bits */
bool databits_matching; /**< if the data is still matching the data bits */
bool parity_even; /**< if the date is still matching the additional even parity bit */
bool parity_odd; /**< if the date is still matching the additional odd parity bit */
bool parity_mark; /**< if the date is still matching the additional mark parity bit */
bool parity_space; /**< if the date is still matching the additional space parity bit */
uint8_t parity_possibilities; /**< the number to still matching parity possibilities (number of parity_* at true) */
};
/** reset all matching values of UART configuration
* @param[out] configuration UART configuration to reset
*/
static void uart_configuration_reset(struct uart_configuration_t* configuration)
{
configuration->databits_matching = true;
configuration->parity_even = true;
configuration->parity_odd = true;
configuration->parity_mark = true;
configuration->parity_space = true;
configuration->parity_possibilities = 4;
}
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 */
#if defined(BUSVOODOO_UART_TIMER_USE_INTERRUPT_TABLE) && BUSVOODOO_UART_TIMER_USE_INTERRUPT_TABLE
static void timer_isr(void)
#else
void TIM_ISR(2)(void)
#endif
{
busvoodoo_led_blue_pulse(BUSVOODOO_LED_PULSE);
static uint32_t pulse = UINT32_MAX; // measured pulse duration (MAX is an invalid values)
if (timer_get_flag(busvoodoo_uart_generic_specific->timer, TIM_SR_UIF)) { // overflow update event happened
timer_clear_flag(busvoodoo_uart_generic_specific->timer, TIM_SR_UIF); // clear flag
if (pulse>(UINT32_MAX-0x10000)) { // we can't measure longer pulser (and baud rate < 0.017 bps make no sense)
pulse = UINT32_MAX; // invalidate measured pulse
} else {
pulse += 0x10000; // account for the 16-bit timer limit
}
}
if (timer_get_flag(busvoodoo_uart_generic_specific->timer, busvoodoo_uart_generic_specific->timer_sr_ccof)) { // capture overflow occurred
timer_clear_flag(busvoodoo_uart_generic_specific->timer, busvoodoo_uart_generic_specific->timer_sr_ccof); // clear flag
pulse = UINT32_MAX; // invalidate measured pulse
}
if (timer_get_flag(busvoodoo_uart_generic_specific->timer, busvoodoo_uart_generic_specific->timer_sr_ccif)) {
uint16_t edge = *busvoodoo_uart_generic_specific->timer_ccr; // 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;
pulse_flag = true;
}
}
pulse = edge; // replace with current edge time
}
}
/** command to auto-detect incoming UART configuration (baud rate, data bits, parity)
* @param[in] argument argument not required
* @remark uses timer input capture for the baud rate, and guess checking for the data bits and parity
*/
static void busvoodoo_uart_generic_command_detect(void* argument)
{
(void)argument; // argument not used
if (!busvoodoo_uart_generic_specific->timer) {
printf("baud rate detection not possible (no timer available)\n");
return;
}
printf("the more traffic is incoming, the better the detection\n");
printf("press any key to exit\n");
// setup USART to receive character
uint8_t uart_databits = 8; // start with 8 bits since this is the most common case (i.e. no additional parity bit is used)
usart_set_baudrate(busvoodoo_uart_generic_specific->usart, 1200); // configure UART to pre-selected baud rate
usart_set_databits(busvoodoo_uart_generic_specific->usart, uart_databits); // configure UART to pre-selected data-bits
usart_set_stopbits(busvoodoo_uart_generic_specific->usart, USART_STOPBITS_1); // 1 stop-bits also complies to 2 stop-bits
usart_set_parity(busvoodoo_uart_generic_specific->usart, USART_PARITY_NONE); // get the raw data since we will do the parity check ourselves
// setup timer to generate/measure signal timing
if ((busvoodoo_uart_generic_specific->timer_port != busvoodoo_uart_generic_specific->rx_port) || (busvoodoo_uart_generic_specific->timer_pin != busvoodoo_uart_generic_specific->rx_pin)) {
rcc_periph_clock_enable(busvoodoo_uart_generic_specific->timer_port_rcc); // enable clock for GPIO peripheral
gpio_set_mode(busvoodoo_uart_generic_specific->timer_port, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, busvoodoo_uart_generic_specific->timer_pin); // switch pin to input to measure the target transmit baud rate
}
rcc_periph_clock_enable(busvoodoo_uart_generic_specific->timer_rcc); // enable clock for timer peripheral
timer_reset(busvoodoo_uart_generic_specific->timer); // reset timer state
timer_disable_counter(busvoodoo_uart_generic_specific->timer); // disable timer to configure it
timer_set_mode(busvoodoo_uart_generic_specific->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(busvoodoo_uart_generic_specific->timer, 1-1); // don't use prescale so to get the most precise measurement
timer_ic_set_input(busvoodoo_uart_generic_specific->timer, busvoodoo_uart_generic_specific->timer_ic, busvoodoo_uart_generic_specific->timer_ic_in_ti); // configure the input capture ICx to use the right channel TIn
timer_ic_set_filter(busvoodoo_uart_generic_specific->timer, busvoodoo_uart_generic_specific->timer_ic, TIM_IC_OFF); // use no filter input to keep precise timing
/* ideally we would trigger on any edge, allowing to measure the bit width (on 010 or 101 bit pattern) and calculate the correct baud rate.
* sadly the STM32 F1 family timer peripheral does not supporting triggering on both edges at the same time (the F0 family can).
* it is possible to simply update the polarity setting to capture the other edge, but this update seems to slow even in a ISR to capture > 1 MHz frequencies (=500 bps).
* thus we will start the trigger on the start bit, and wait for the next falling edge.
* this way we don't need to change the parity and have twice the time to measure the baud rate.
* to calculate the baud rate we just have to divided the frequency by two.
* the correct baud rate on the following pattern (HLHL), and since the start bit is the first HL pattern we just have to wait for data starting with 10 (LSb first)
*/
timer_ic_set_polarity(busvoodoo_uart_generic_specific->timer, busvoodoo_uart_generic_specific->timer_ic, TIM_IC_FALLING); // capture on falling end to trigger on the start bit
timer_ic_set_prescaler(busvoodoo_uart_generic_specific->timer, busvoodoo_uart_generic_specific->timer_ic, TIM_IC_PSC_OFF); // don't use any prescaler since we want to capture every pulse
timer_ic_enable(busvoodoo_uart_generic_specific->timer, busvoodoo_uart_generic_specific->timer_ic); // enable capture interrupt
timer_clear_flag(busvoodoo_uart_generic_specific->timer, busvoodoo_uart_generic_specific->timer_sr_ccif); // clear input compare flag
timer_enable_irq(busvoodoo_uart_generic_specific->timer, busvoodoo_uart_generic_specific->timer_dier_ccie); // enable capture interrupt
timer_update_on_overflow(busvoodoo_uart_generic_specific->timer); // only use counter overflow as UEV source (use overflow to measure longer times)
timer_clear_flag(busvoodoo_uart_generic_specific->timer, TIM_SR_UIF); // clear overflow flag
timer_enable_irq(busvoodoo_uart_generic_specific->timer, TIM_DIER_UIE); // enable update interrupt for timer
#if defined(BUSVOODOO_UART_TIMER_USE_INTERRUPT_TABLE) && BUSVOODOO_UART_TIMER_USE_INTERRUPT_TABLE
/** backup of timer ISR before replacing it */
void (*isr_backup)(void) = interrupt_table[busvoodoo_uart_generic_specific->timer_nvic_irq]; // backup current timer ISR before replacing it
interrupt_table[busvoodoo_uart_generic_specific->timer_nvic_irq] = &timer_isr; // replace current timer ISR with our to be able to measure the edge timing
bool irq_backup = nvic_get_irq_enabled(busvoodoo_uart_generic_specific->timer_nvic_irq); // backup enable IRQ setting
#endif
nvic_enable_irq(busvoodoo_uart_generic_specific->timer_nvic_irq); // catch interrupts for this timer
pulse_duration = UINT32_MAX; // reset pulse duration
timer_enable_counter(busvoodoo_uart_generic_specific->timer); // enable timer
bool reset_state = true; // flag to know if we need to reset the states
uint8_t rx_errors; // number of UART receive errors received
bool wait_for_idle = false; // flag to wait for an IDLE frame
/** the possible UART configurations
* @note since the first valid configuration will be chosen, order in decreasing probability of being valid and decreasing probability or getting invalidated */
struct uart_configuration_t uart_configurations[] = {
{ .databits = 5 },
{ .databits = 6 },
{ .databits = 8 },
{ .databits = 7 },
};
uint8_t uart_configuration_valid = LENGTH(uart_configurations); // current best valid UART configuration index
char uart_configuration_parity = '?'; // current best valid UART parity
const uint32_t baudrates[] = { 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, 576000, 921600 }; // list of standard baud rates, to match with measured frequency
uint32_t uart_baudrate = 0; // fastest found baud rate
while (!user_input_available) {
if (reset_state) { // reset the configuration
rx_errors = 0;
for (uint8_t i=0; i<LENGTH(uart_configurations); i++) {
uart_configuration_reset(&uart_configurations[i]);
}
usart_recv(busvoodoo_uart_generic_specific->usart); // clear input buffer and allow flag to be set
usart_enable(busvoodoo_uart_generic_specific->usart); // ensure UART is enabled
reset_state = false;
}
if (pulse_flag) { // new pulse duration has been measured
pulse_flag = false; // clear flag
printf("u");
uint32_t baudrate = rcc_ahb_frequency/(pulse_duration/2); // calculate baud rate based on measured timing
if (baudrate>uart_baudrate+100) { // new higher baud rate detected
uart_baudrate = baudrate; // save new baud rate
if (uart_baudrate>=1200) { // ensure minimum hardware supported baud rate is respected
// search for closest standard baud rate
uint32_t standard_baudrate = 0;
for (uint8_t i=0; i<LENGTH(baudrates); i++) {
if (uart_baudrate>=baudrates[i]*0.9 && uart_baudrate<=baudrates[i]*1.1) { // measured baud rate matches standard baud rate within factor
standard_baudrate = baudrates[i]; // remember matching baud rate
break; // stop searching for matching baud rate
}
}
if (standard_baudrate) { // matching standard baud rate found
uart_baudrate = standard_baudrate; // save matching baud rate
}
usart_disable(busvoodoo_uart_generic_specific->usart); // disable UART before reconfiguring
usart_set_baudrate(busvoodoo_uart_generic_specific->usart, uart_baudrate); // set new baud rate
reset_state = true; // reset the states since we set a new baud rate
printf("\nnew baud rate: %u bps\n", uart_baudrate); // show measurement frequency
} else {
printf("\ndetected %u bps baud rate is lower than minimum supported 1200 bps\n", baudrate);
}
}
}
if (USART_SR(busvoodoo_uart_generic_specific->usart) & (USART_SR_NE|USART_SR_FE)) { // error on UART received
usart_recv(busvoodoo_uart_generic_specific->usart); // clear input buffer and flags
rx_errors++; // increment number of errors
if (rx_errors>=5) { // the format seems wrong
// the threshold must be high enough so the UART peripheral has enough opportunities to synchronize to the start bit (just after and idle frame)
// two high probable frame error causes:
// - when set to 9 data-bits with high speed 8 data-bits traffic incoming: the next start bit comes right after the stop bit of and 8-bit frame, which is interpreted as faulty 9 data-bits frame stop bit
// - when set to 8 data-bits with 9 data-bits (8+1 parity) traffic incoming: the low parity bit is interpreted as faulty stop-bit
uart_databits = ((8==uart_databits) ? 9 : 8); // switch between 8 and 9-bit packets
usart_disable(busvoodoo_uart_generic_specific->usart); // disable UART before reconfiguring
usart_set_databits(busvoodoo_uart_generic_specific->usart, uart_databits); // set new data width
reset_state = true;
pulse_duration = UINT32_MAX; // also reset the baud rate
uart_baudrate = 0; // also reset the baud rate
rx_errors = 0; // reset error counter
printf("\nrestarting guessing because too detected error\n");
} else {
wait_for_idle = true; // wait form an IDLE frame so to better sync to the next start bit
}
}
if (wait_for_idle) {
/* we have to check the IDLE flag in the main loop instead of just looping over the flag because a hardware fault could prevent it from being set.
* from the "STM32F10xxC/D/E silicon limitations" errata, section 2.12.2 "Idle frame is not detected if receiver clock speed is deviated": If the USART receives an idle frame followed by a character, and the clock of the transmitter device is faster than the USART receiver clock, the USART receive signal falls too early when receiving the character start bit, with the result that the idle frame is not detected (IDLE flag is not set).
* there this no workaround be it will be fixed when changing the baud rate
*/
if (USART_SR(busvoodoo_uart_generic_specific->usart) | USART_SR_IDLE) {
wait_for_idle = false;
}
if (USART_SR(busvoodoo_uart_generic_specific->usart) | USART_SR_RXNE) {
USART_DR(busvoodoo_uart_generic_specific->usart); // empty receive buffer so the IDLE flag can retrigger
}
}
if (USART_SR(busvoodoo_uart_generic_specific->usart) & USART_SR_RXNE) { // data received
uint16_t usart_data = usart_recv(busvoodoo_uart_generic_specific->usart); // save received data (also clears flag)
if (0 == uart_baudrate) { // we did not find any valid baud rate yet
continue;
}
uint16_t usart_data_padded = ((8 == uart_databits) ? usart_data | 0xff00 : usart_data | 0xfe00); // pad with 1 (stop bit/idle state) for better word size detection
uint16_t usart_data_relevant = usart_data & ~(0xffff << uart_configurations[uart_configuration_valid].databits); // get only the data bits
// verify parity and word size
for (uint8_t i = 0; i < LENGTH(uart_configurations); i++) {
// skip check if we already know the word size is wrong
if (!uart_configurations[i].databits_matching) {
continue;
}
// do parity checks
if (uart_configurations[i].parity_even) {
uart_configurations[i].parity_even &= usart_enhanced_even_parity_lut[usart_data_relevant];
}
if (uart_configurations[i].parity_odd) {
uart_configurations[i].parity_odd &= !usart_enhanced_even_parity_lut[usart_data_relevant];
}
if (uart_configurations[i].parity_mark) {
uart_configurations[i].parity_mark &= (usart_data_padded & (1 << uart_configurations[i].databits));
}
if (uart_configurations[i].parity_space) {
uart_configurations[i].parity_space &= !(usart_data_padded & (1 << uart_configurations[i].databits));
}
// update parity count
uart_configurations[i].parity_possibilities = 0;
if (uart_configurations[i].parity_even) {
uart_configurations[i].parity_possibilities++;
}
if (uart_configurations[i].parity_odd) {
uart_configurations[i].parity_possibilities++;
}
if (uart_configurations[i].parity_mark) {
uart_configurations[i].parity_possibilities++;
}
if (uart_configurations[i].parity_space) {
uart_configurations[i].parity_possibilities++;
}
// verify word size
uint16_t databits_mask = (0xffff << (uart_configurations[i].databits + ((0 == uart_configurations[i].parity_possibilities) ? 0 : 1))); // mask for bits which should not be cleared
if (~usart_data_padded & databits_mask) { // see if bit outside the word size are cleared
uart_configurations[i].databits_matching = false;
}
}
bool no_valid_configuration = true;
uint8_t new_valid_configuration = LENGTH(uart_configurations);
char parity = '?';
for (uint8_t i=0; i<LENGTH(uart_configurations); i++) {
// skip check the word size is wrong
if (!uart_configurations[i].databits_matching) {
continue;
}
no_valid_configuration = false;
if (uart_configurations[i].parity_possibilities > 1) { // parity is not yet clear
continue;
} else if (uart_configurations[i].parity_even) {
parity = 'E';
} else if (uart_configurations[i].parity_odd) {
parity = 'O';
} else if (uart_configurations[i].parity_mark) {
parity = 'M';
} else if (uart_configurations[i].parity_space) {
parity = 'S';
} else if (0==uart_configurations[i].parity_possibilities) {
parity = 'N';
}
new_valid_configuration = i;
break; // stop searching since we found a configuration
}
if (no_valid_configuration) {
reset_state = true; // reset the configurations
pulse_duration = UINT32_MAX; // also reset the baud rate
uart_baudrate = 0; // also reset the baud rate
} else if (new_valid_configuration < LENGTH(uart_configurations) && '?' != parity && (new_valid_configuration != uart_configuration_valid || parity != uart_configuration_parity)) { // we found a new valid configuration
uart_configuration_valid = new_valid_configuration;
uart_configuration_parity = parity;
printf("\nnew UART configuration found: %u %u%c1\n", uart_baudrate, uart_configurations[uart_configuration_valid].databits, uart_configuration_parity);
}
// print received data if a configuration has been found
if (uart_configuration_valid < LENGTH(uart_configurations)) { // valid configuration existing
if (uart_configurations[uart_configuration_valid].databits >= 7 && usart_data_relevant < 0x80) { // this is probably valid ASCII data
printf("%c", usart_data_relevant);
} else {
printf("0x%02x ", usart_data_relevant);
}
} else {
printf("0b%09b\n", usart_data_relevant);
}
}
}
user_input_get(); // clear input used to interrupt previous loop
printf("\n");
// stop timer
timer_disable_counter(busvoodoo_uart_generic_specific->timer); // disable timer
#if defined(BUSVOODOO_UART_TIMER_USE_INTERRUPT_TABLE) && BUSVOODOO_UART_TIMER_USE_INTERRUPT_TABLE
interrupt_table[busvoodoo_uart_generic_specific->timer_nvic_irq] = isr_backup; // restore ISR
if (!irq_backup) {
nvic_disable_irq(busvoodoo_uart_generic_specific->timer_nvic_irq); // disable IRQ
}
#else
nvic_disable_irq(busvoodoo_uart_generic_specific->timer_nvic_irq); // disable IRQ
#endif
timer_reset(busvoodoo_uart_generic_specific->timer); // reset timer
rcc_periph_clock_disable(busvoodoo_uart_generic_specific->timer_rcc); // disable clock for timer peripheral
// no need to disable pin since it's already a floating input it is not Rx
if (uart_configuration_valid < LENGTH(uart_configurations)) {
printf("press y to use configuration found: %u %u%c1\n", uart_baudrate, uart_configurations[uart_configuration_valid].databits, uart_configuration_parity);
while (!user_input_available); // wait until user input
if ('y' == user_input_get()) { // user want to use found configuration
busvoodoo_uart_generic_baudrate = uart_baudrate;
busvoodoo_uart_generic_databits = uart_configurations[uart_configuration_valid].databits;
if (uart_configurations[uart_configuration_valid].parity_even) {
busvoodoo_uart_generic_parity = USART_ENHANCED_PARITY_EVEN;
} else if (uart_configurations[uart_configuration_valid].parity_odd) {
busvoodoo_uart_generic_parity = USART_ENHANCED_PARITY_ODD;
} else if (uart_configurations[uart_configuration_valid].parity_mark) {
busvoodoo_uart_generic_parity = USART_ENHANCED_PARITY_MARK;
} else if (uart_configurations[uart_configuration_valid].parity_space) {
busvoodoo_uart_generic_parity = USART_ENHANCED_PARITY_SPACE;
} else {
busvoodoo_uart_generic_parity = USART_ENHANCED_PARITY_NONE;
}
busvoodoo_uart_generic_stopbits = USART_STOPBITS_1;
}
}
// reconfigure USART
usart_set_baudrate(busvoodoo_uart_generic_specific->usart, busvoodoo_uart_generic_baudrate); // set baud rate
usart_enhanced_config(busvoodoo_uart_generic_specific->usart, busvoodoo_uart_generic_databits, busvoodoo_uart_generic_parity); // use enhanced USART to configure the USART peripherals, supporting more data-bits and parity configurations
usart_set_stopbits(busvoodoo_uart_generic_specific->usart, busvoodoo_uart_generic_stopbits); // set stop bits
}
const struct menu_command_t busvoodoo_uart_generic_commands[busvoodoo_uart_generic_commands_nb] = {
{
.shortcut = 'a',
@ -691,4 +1044,13 @@ const struct menu_command_t busvoodoo_uart_generic_commands[busvoodoo_uart_gener
.argument_description = NULL,
.command_handler = &busvoodoo_uart_generic_command_error,
},
{
.shortcut = 'd',
.name = "detect",
.command_description = "auto-detect serial configuration (baud rate, data bits, parity)",
.argument = MENU_ARGUMENT_NONE,
.argument_description = NULL,
.command_handler = &busvoodoo_uart_generic_command_detect,
},
};

View File

@ -18,6 +18,7 @@
* @author King Kévin <kingkevin@cuvoodoo.info>
* @date 2018
*/
#include <libopencm3/stm32/timer.h> // timer library
/** UART specific methods that will be called by the generic methods */
struct busvoodoo_uart_generic_specific_t {
@ -35,6 +36,7 @@ struct busvoodoo_uart_generic_specific_t {
uint32_t rx_rcc; /**< GPIO RCC address of receive pin */
void (*rx_pre)(void); /**< method to be called before receiving data */
void (*rx_post)(void); /**< method to be called after receiving data */
/* hardware flow control configuration */
bool hwflowctl; /**< if RTC/CTS hardware flow control is supported */
uint32_t rts_port; /**< GPIO port address of request to send pin */
uint32_t rts_pin; /**< GPIO pin address of request to send pin */
@ -42,6 +44,19 @@ struct busvoodoo_uart_generic_specific_t {
uint32_t cts_port; /**< GPIO port address of clear to send pin */
uint32_t cts_pin; /**< GPIO pin address of clear to send pin */
uint32_t cts_rcc; /**< GPIO RCC address of clear to send pin */
/* timer input channel capture to measure Rx edge timing */
uint32_t timer; /**< timer peripheral */
uint32_t timer_rcc; /**< timer RCC address */
uint32_t timer_port; /**< port of timer capture channel */
uint32_t timer_port_rcc; /**< port RCC of timer capture channel */
uint32_t timer_pin; /**< pin of timer capture channel */
enum tim_ic_id timer_ic; /**< timer input capture channel */
enum tim_ic_input timer_ic_in_ti; /**< timer input capture channel TIn */
uint32_t timer_sr_ccif; /**< timer channel capture interrupt flag */
uint32_t timer_sr_ccof; /**< timer channel capture overrun flag */
volatile uint32_t* timer_ccr; /**< timer channel capture register */
uint32_t timer_dier_ccie; /**< timer channel capture interrupt enable */
uint32_t timer_nvic_irq; /**< timer IRQ */
};
/** provide the generic USART with mode specific information
@ -61,6 +76,6 @@ void busvoodoo_uart_generic_exit(void);
/** number of commands supported by the generic UART mode
* @warning this variable must be constant, thus be adjusted by hand corresponding to the actual content
*/
#define busvoodoo_uart_generic_commands_nb 5
#define busvoodoo_uart_generic_commands_nb 6
/** commands supported by the generic UART mode */
extern const struct menu_command_t busvoodoo_uart_generic_commands[busvoodoo_uart_generic_commands_nb];