2018-06-15 09:47:22 +02:00
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*
*/
/** BusVoodoo generic UART mode (code)
* @ note this only contains the common UART methods and should be supplied with mode specific methods and information
* @ file busvoodoo_uart_generic . c
* @ author King Kévin < kingkevin @ cuvoodoo . info >
* @ date 2018
*/
/* standard libraries */
# include <stdint.h> // standard integer types
# include <stdlib.h> // standard utilities
# include <string.h> // string utilities
/* STM32 (including CM3) libraries */
2018-07-11 08:21:41 +02:00
# include <libopencm3/cm3/nvic.h> // interrupt utilities
2018-06-15 09:47:22 +02:00
# 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
2018-07-11 08:21:41 +02:00
# include <libopencm3/stm32/timer.h> // timer library
2018-06-15 09:47:22 +02:00
/* own libraries */
# include "global.h" // board definitions
# include "print.h" // printing utilities
2018-07-11 08:21:41 +02:00
# include "interrupt.h" // user interrupt table
2018-06-15 09:47:22 +02:00
# include "menu.h" // menu definitions
# include "usart_enhanced.h" // utilities for USART enhancements
# include "busvoodoo_global.h" // BusVoodoo definitions
# include "busvoodoo_uart_generic.h" // own definitions
/** the USART mode specific information */
static const struct busvoodoo_uart_generic_specific_t * busvoodoo_uart_generic_specific = NULL ;
/** mode setup stage */
static enum busvoodoo_uart_generic_setting_t {
BUSVOODOO_UART_SETTING_NONE ,
BUSVOODOO_UART_SETTING_BAUDRATE ,
BUSVOODOO_UART_SETTING_DATABITS ,
BUSVOODOO_UART_SETTING_PARITY ,
BUSVOODOO_UART_SETTING_STOPBITS ,
BUSVOODOO_UART_SETTING_HWFLOWCTL ,
BUSVOODOO_UART_SETTING_DRIVE ,
BUSVOODOO_UART_SETTING_DONE ,
} busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_NONE ; /**< current mode setup stage */
/** UART baud rate (in bps) */
static uint32_t busvoodoo_uart_generic_baudrate = 115200 ;
/** UART data bits */
static uint8_t busvoodoo_uart_generic_databits = 8 ;
/** UART parity setting */
static enum usart_enhanced_parity_t busvoodoo_uart_generic_parity = USART_ENHANCED_PARITY_NONE ;
/** UART stop bits setting */
static uint32_t busvoodoo_uart_generic_stopbits = USART_STOPBITS_1 ;
/** UART hardware flow control setting (true = with hardware flow control, false = without hardware flow control */
static bool busvoodoo_uart_generic_hwflowctl = false ;
/** pin drive mode (true = push-pull, false = open-drain) */
static bool busvoodoo_uart_generic_drive = true ;
/** if embedded pull-up resistors are used */
static bool busvoodoo_uart_generic_pullup = false ;
2018-07-11 08:21:41 +02:00
/** 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
2018-06-15 09:47:22 +02:00
bool busvoodoo_uart_generic_configure ( const struct busvoodoo_uart_generic_specific_t * conf )
{
busvoodoo_uart_generic_specific = NULL ; // reset specific information
if ( NULL = = conf ) {
return false ;
}
if ( ! conf - > usart | | ! conf - > usart_rcc | | ! conf - > usart_rst ) {
return false ;
}
if ( ! conf - > tx_rcc | | ! conf - > rx_rcc ) {
return false ;
}
if ( conf - > hwflowctl & & ( ! conf - > rts_rcc | | ! conf - > cts_rcc ) ) {
return false ;
}
2018-07-11 08:21:41 +02:00
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 ;
}
2018-06-15 09:47:22 +02:00
busvoodoo_uart_generic_specific = conf ;
return true ;
}
bool busvoodoo_uart_generic_setup ( char * * prefix , const char * line )
{
if ( NULL = = busvoodoo_uart_generic_specific ) { // there is nothing to configure
return true ;
}
bool complete = false ; // is the setup complete
if ( NULL = = line ) { // first call
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_NONE ; // re-start configuration
}
switch ( busvoodoo_uart_generic_setting ) {
case BUSVOODOO_UART_SETTING_NONE :
snprintf ( busvoodoo_global_string , LENGTH ( busvoodoo_global_string ) , " baud rate in bps (1200-2000000) [%u] " , busvoodoo_uart_generic_baudrate ) ;
* prefix = busvoodoo_global_string ; // ask for baud rate
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_BAUDRATE ;
break ;
case BUSVOODOO_UART_SETTING_BAUDRATE :
if ( NULL = = line | | 0 = = strlen ( line ) ) { // use default setting
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_DATABITS ; // go to next setting
} else { // setting provided
uint32_t baudrate = atoi ( line ) ; // parse setting
if ( baudrate > 0 & & baudrate < = 2000000 ) { // check setting
busvoodoo_uart_generic_baudrate = baudrate ; // remember setting
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_DATABITS ; // go to next setting
}
}
if ( BUSVOODOO_UART_SETTING_DATABITS = = busvoodoo_uart_generic_setting ) { // if next setting
snprintf ( busvoodoo_global_string , LENGTH ( busvoodoo_global_string ) , " data bits (5-8) [%u] " , busvoodoo_uart_generic_databits ) ; // prepare next setting
* prefix = busvoodoo_global_string ; // display next setting
}
break ;
case BUSVOODOO_UART_SETTING_DATABITS :
if ( NULL = = line | | 0 = = strlen ( line ) ) { // use default setting
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_PARITY ; // go to next setting
} else if ( 1 = = strlen ( line ) ) { // setting provided
uint8_t databits = atoi ( line ) ; // parse setting
if ( databits > = 5 & & databits < = 8 ) { // check setting
busvoodoo_uart_generic_databits = databits ; // remember setting
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_PARITY ; // go to next setting
}
}
if ( BUSVOODOO_UART_SETTING_PARITY = = busvoodoo_uart_generic_setting ) { // if next setting
printf ( " 1) none \n " ) ;
printf ( " 2) even \n " ) ;
printf ( " 3) odd \n " ) ;
printf ( " 4) mark \n " ) ;
printf ( " 5) space \n " ) ;
snprintf ( busvoodoo_global_string , LENGTH ( busvoodoo_global_string ) , " parity (1,2,3,4,5) [%u] " , busvoodoo_uart_generic_parity + 1 ) ; // prepare next setting
* prefix = busvoodoo_global_string ; // display next setting
}
break ;
case BUSVOODOO_UART_SETTING_PARITY :
if ( NULL = = line | | 0 = = strlen ( line ) ) { // use default setting
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
2018-07-11 08:21:41 +02:00
if ( parity > 0 & & parity < 6 ) { // check setting
2018-06-15 09:47:22 +02:00
busvoodoo_uart_generic_parity = parity - 1 ;
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_STOPBITS ; // go to next setting
}
}
if ( BUSVOODOO_UART_SETTING_STOPBITS = = busvoodoo_uart_generic_setting ) { // if next setting
printf ( " 1) 0.5 \n " ) ;
printf ( " 2) 1 \n " ) ;
printf ( " 3) 1.5 \n " ) ;
printf ( " 4) 2 \n " ) ;
snprintf ( busvoodoo_global_string , LENGTH ( busvoodoo_global_string ) , " stop bits (1,2,3,4) [%c] " , USART_STOPBITS_0_5 = = busvoodoo_uart_generic_stopbits ? ' 1 ' : ( USART_STOPBITS_1 = = busvoodoo_uart_generic_stopbits ? ' 2 ' : ( USART_STOPBITS_1_5 = = busvoodoo_uart_generic_stopbits ? ' 3 ' : ' 4 ' ) ) ) ; // prepare next setting
* prefix = busvoodoo_global_string ; // display next setting
}
break ;
case BUSVOODOO_UART_SETTING_STOPBITS :
if ( NULL = = line | | 0 = = strlen ( line ) ) { // use default setting
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_HWFLOWCTL ; // go to next setting
} else if ( 1 = = strlen ( line ) ) { // setting provided
if ( ' 1 ' = = line [ 0 ] ) { // 0.5 stop bits
busvoodoo_uart_generic_stopbits = USART_STOPBITS_0_5 ; // remember setting
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_HWFLOWCTL ; // go to next setting
} else if ( ' 2 ' = = line [ 0 ] ) { // 1 stop bits
busvoodoo_uart_generic_stopbits = USART_STOPBITS_1 ; // remember setting
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_HWFLOWCTL ; // go to next setting
} else if ( ' 3 ' = = line [ 0 ] ) { // 1.5 stop bits
busvoodoo_uart_generic_stopbits = USART_STOPBITS_1_5 ; // remember setting
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_HWFLOWCTL ; // go to next setting
} else if ( ' 4 ' = = line [ 0 ] ) { // 2 stop bits
busvoodoo_uart_generic_stopbits = USART_STOPBITS_2 ; // remember setting
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_HWFLOWCTL ; // go to next setting
}
}
if ( BUSVOODOO_UART_SETTING_HWFLOWCTL = = busvoodoo_uart_generic_setting ) { // if next setting
if ( ! busvoodoo_uart_generic_specific - > hwflowctl ) { // hardware flow control is not supported
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_DRIVE ; // go to next setting
goto setting_drive ; // actually go to next setting
}
printf ( " 1) no flow control \n " ) ;
printf ( " 2) RTS/CTS hardware flow control \n " ) ;
snprintf ( busvoodoo_global_string , LENGTH ( busvoodoo_global_string ) , " flow control (1,2) [%c] " , busvoodoo_uart_generic_hwflowctl ? ' 2 ' : ' 1 ' ) ; // prepare next setting
* prefix = busvoodoo_global_string ; // display next setting
}
break ;
case BUSVOODOO_UART_SETTING_HWFLOWCTL :
if ( NULL = = line | | 0 = = strlen ( line ) ) { // use default setting
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_DRIVE ; // go to next setting
} else if ( 1 = = strlen ( line ) ) { // setting provided
if ( ' 1 ' = = line [ 0 ] | | ' 2 ' = = line [ 0 ] ) { // setting provided
busvoodoo_uart_generic_hwflowctl = ( ' 2 ' = = line [ 0 ] ) ; // remember setting
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_DRIVE ; // go to next setting
}
}
setting_drive :
if ( BUSVOODOO_UART_SETTING_DRIVE = = busvoodoo_uart_generic_setting ) { // if next setting
if ( ! busvoodoo_uart_generic_specific - > multidrive ) {
busvoodoo_uart_generic_drive = true ; // only push-pull driving mode is supported
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_DONE ; // go to next setting
goto setting_done ; // actually go to next setting
}
printf ( " 1) push-pull (3.3V) \n " ) ;
printf ( " 2) open-drain, with embedded pull-up resistors (2kO) \n " ) ;
printf ( " 3) open-drain, with external pull-up resistors \n " ) ;
snprintf ( busvoodoo_global_string , LENGTH ( busvoodoo_global_string ) , " drive mode (1,2,3) [%c] " , busvoodoo_uart_generic_drive ? ' 1 ' : ( busvoodoo_uart_generic_pullup ? ' 2 ' : ' 3 ' ) ) ; // show drive mode
* prefix = busvoodoo_global_string ; // display next setting
}
break ;
case BUSVOODOO_UART_SETTING_DRIVE :
if ( NULL = = line | | 0 = = strlen ( line ) ) { // use default setting
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_DONE ; // go to next setting
} else if ( 1 = = strlen ( line ) ) { // setting provided
uint8_t drive = atoi ( line ) ; // parse setting
if ( 1 = = drive | | 2 = = drive | | 3 = = drive ) { // check setting
busvoodoo_uart_generic_drive = ( 1 = = drive ) ; // remember setting
busvoodoo_uart_generic_pullup = ( 2 = = drive ) ; // remember setting
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_DONE ; // go to next setting
}
}
setting_done :
if ( BUSVOODOO_UART_SETTING_DONE = = busvoodoo_uart_generic_setting ) { // we have all settings, configure UART
rcc_periph_clock_enable ( RCC_AFIO ) ; // enable clock for USART alternate function
rcc_periph_clock_enable ( busvoodoo_uart_generic_specific - > usart_rcc ) ; // enable clock for USART peripheral
rcc_periph_reset_pulse ( busvoodoo_uart_generic_specific - > usart_rst ) ; // reset USART peripheral
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
if ( busvoodoo_uart_generic_specific - > hwflowctl & & busvoodoo_uart_generic_hwflowctl ) {
usart_set_flow_control ( busvoodoo_uart_generic_specific - > usart , USART_FLOWCONTROL_RTS_CTS ) ; // set RTS/CTS flow control
} else {
usart_set_flow_control ( busvoodoo_uart_generic_specific - > usart , USART_FLOWCONTROL_NONE ) ; // set no flow control
}
usart_set_mode ( busvoodoo_uart_generic_specific - > usart , USART_MODE_TX_RX ) ; // full-duplex communication
rcc_periph_clock_enable ( busvoodoo_uart_generic_specific - > tx_rcc ) ; // enable clock for USART GPIO peripheral
rcc_periph_clock_enable ( busvoodoo_uart_generic_specific - > rx_rcc ) ; // enable clock for USART GPIO peripheral
if ( busvoodoo_uart_generic_specific - > hwflowctl & & busvoodoo_uart_generic_hwflowctl ) {
rcc_periph_clock_enable ( busvoodoo_uart_generic_specific - > rts_rcc ) ; // enable clock for USART GPIO peripheral
rcc_periph_clock_enable ( busvoodoo_uart_generic_specific - > cts_rcc ) ; // enable clock for USART GPIO peripheral
}
if ( busvoodoo_uart_generic_drive ) { // use push-pull drive mode
gpio_set_mode ( busvoodoo_uart_generic_specific - > tx_port , GPIO_MODE_OUTPUT_10_MHZ , GPIO_CNF_OUTPUT_ALTFN_PUSHPULL , busvoodoo_uart_generic_specific - > tx_pin ) ; // setup GPIO pin USART transmit
gpio_set ( busvoodoo_uart_generic_specific - > rx_port , busvoodoo_uart_generic_specific - > rx_pin ) ; // pull up to avoid noise when not connected
gpio_set_mode ( busvoodoo_uart_generic_specific - > rx_port , GPIO_MODE_INPUT , GPIO_CNF_INPUT_PULL_UPDOWN , busvoodoo_uart_generic_specific - > rx_pin ) ; // setup GPIO pin USART receive
if ( busvoodoo_uart_generic_specific - > hwflowctl & & busvoodoo_uart_generic_hwflowctl ) { // use open drain drive mode
gpio_set_mode ( busvoodoo_uart_generic_specific - > rts_port , GPIO_MODE_OUTPUT_10_MHZ , GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN , busvoodoo_uart_generic_specific - > rts_pin ) ; // setup GPIO pin USART transmit
gpio_set ( busvoodoo_uart_generic_specific - > cts_port , busvoodoo_uart_generic_specific - > cts_pin ) ; // pull up to block transmission unless requested
gpio_set_mode ( busvoodoo_uart_generic_specific - > cts_port , GPIO_MODE_INPUT , GPIO_CNF_INPUT_PULL_UPDOWN , busvoodoo_uart_generic_specific - > cts_pin ) ; // setup GPIO pin USART receive
}
} else {
gpio_set_mode ( busvoodoo_uart_generic_specific - > tx_port , GPIO_MODE_OUTPUT_10_MHZ , GPIO_CNF_OUTPUT_ALTFN_PUSHPULL , busvoodoo_uart_generic_specific - > tx_pin ) ; // setup GPIO pin USART transmit
gpio_set_mode ( busvoodoo_uart_generic_specific - > rx_port , GPIO_MODE_INPUT , GPIO_CNF_INPUT_FLOAT , busvoodoo_uart_generic_specific - > rx_pin ) ; // setup GPIO pin USART receive
if ( busvoodoo_uart_generic_specific - > hwflowctl & & busvoodoo_uart_generic_hwflowctl ) {
gpio_set_mode ( busvoodoo_uart_generic_specific - > rts_port , GPIO_MODE_OUTPUT_10_MHZ , GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN , busvoodoo_uart_generic_specific - > rts_pin ) ; // setup GPIO pin USART transmit
gpio_set_mode ( busvoodoo_uart_generic_specific - > cts_port , GPIO_MODE_INPUT , GPIO_CNF_INPUT_FLOAT , busvoodoo_uart_generic_specific - > cts_pin ) ; // setup GPIO pin USART receive
}
}
if ( ! busvoodoo_uart_generic_drive & & busvoodoo_uart_generic_pullup ) { // enable embedded pull-ups if used
busvoodoo_embedded_pullup ( true ) ; // set embedded pull-ups
printf ( " use LV to set pull-up voltage \n " ) ;
}
usart_enable ( busvoodoo_uart_generic_specific - > usart ) ; // enable USART
2018-07-11 08:21:41 +02:00
// setup timer to measure RX edge timing for baud rate guessing
if ( busvoodoo_uart_generic_specific - > timer ) {
}
2018-06-15 09:47:22 +02:00
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
}
break ;
default : // unknown case
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_NONE ; // restart settings next time
break ;
}
return complete ;
}
void busvoodoo_uart_generic_exit ( void )
{
if ( NULL = = busvoodoo_uart_generic_specific ) {
return ;
}
usart_disable ( busvoodoo_uart_generic_specific - > usart ) ; // disable USART
rcc_periph_clock_disable ( busvoodoo_uart_generic_specific - > usart_rcc ) ; // disable domain clock
gpio_set_mode ( busvoodoo_uart_generic_specific - > tx_port , GPIO_MODE_INPUT , GPIO_CNF_INPUT_FLOAT , busvoodoo_uart_generic_specific - > tx_pin ) ; // set pin back to floating input
gpio_set_mode ( busvoodoo_uart_generic_specific - > rx_port , GPIO_MODE_INPUT , GPIO_CNF_INPUT_FLOAT , busvoodoo_uart_generic_specific - > rx_pin ) ; // set pin back to floating input
if ( busvoodoo_uart_generic_specific - > hwflowctl ) {
gpio_set_mode ( busvoodoo_uart_generic_specific - > rts_port , GPIO_MODE_INPUT , GPIO_CNF_INPUT_FLOAT , busvoodoo_uart_generic_specific - > rts_pin ) ; // set pin back to floating input
2018-06-17 12:09:50 +02:00
gpio_set_mode ( busvoodoo_uart_generic_specific - > cts_port , GPIO_MODE_INPUT , GPIO_CNF_INPUT_FLOAT , busvoodoo_uart_generic_specific - > cts_pin ) ; // set pin back to floating input
2018-06-15 09:47:22 +02:00
}
if ( busvoodoo_uart_generic_specific - > multidrive ) {
busvoodoo_embedded_pullup ( false ) ; // disable embedded pull-ups
}
busvoodoo_uart_generic_specific = NULL ; // remove specific information
}
/** write to UART
* @ param [ in ] value value to write
*/
static void busvoodoo_uart_generic_write ( uint8_t value )
{
if ( NULL = = busvoodoo_uart_generic_specific ) {
return ;
}
if ( busvoodoo_uart_generic_specific - > tx_pre ) {
( * busvoodoo_uart_generic_specific - > tx_pre ) ( ) ;
}
while ( ( 0 = = ( ( USART_SR ( busvoodoo_uart_generic_specific - > usart ) ) & USART_SR_TXE ) & & ! user_input_available ) ) ; // wait for transmit buffer to be empty (or user to interrupt)
if ( ( USART_SR ( busvoodoo_uart_generic_specific - > usart ) ) & USART_SR_TXE ) { // we can send data
// send data
busvoodoo_led_blue_pulse ( BUSVOODOO_LED_PULSE ) ; // pulse blue LED to show transmission
usart_enhanced_send ( busvoodoo_uart_generic_specific - > usart , value ) ; // transmit data
// display data send
printf ( " write: '%c'/0x%02x \n " , value , value ) ;
}
2018-06-17 12:09:50 +02:00
while ( ( 0 = = ( ( USART_SR ( busvoodoo_uart_generic_specific - > usart ) ) & USART_SR_TC ) & & ! user_input_available ) ) ; // wait for transfer to be complete
2018-06-16 11:05:48 +02:00
if ( user_input_available ) { // user interrupted flow
user_input_get ( ) ; // discard user input
}
2018-06-15 09:47:22 +02:00
if ( busvoodoo_uart_generic_specific - > tx_post ) {
( * busvoodoo_uart_generic_specific - > tx_post ) ( ) ;
}
}
/** read from UART
*/
static void busvoodoo_uart_generic_read ( void )
{
if ( NULL = = busvoodoo_uart_generic_specific ) {
return ;
}
if ( busvoodoo_uart_generic_specific - > rx_pre ) {
( * busvoodoo_uart_generic_specific - > rx_pre ) ( ) ;
}
printf ( " read: " ) ;
while ( ! ( USART_SR ( busvoodoo_uart_generic_specific - > usart ) & USART_SR_RXNE ) & & ! user_input_available ) ; // wait for incoming data to be available (or user input to exit)
if ( ( USART_SR ( busvoodoo_uart_generic_specific - > usart ) & USART_SR_RXNE ) ) { // verify if data has been received
busvoodoo_led_blue_pulse ( BUSVOODOO_LED_PULSE ) ; // enable blue LED to show reception
// get the errors
bool error_noise = ( 0 ! = ( USART_SR ( busvoodoo_uart_generic_specific - > usart ) & USART_SR_NE ) ) ; // read noise error flag
bool error_framing = ( 0 ! = ( USART_SR ( busvoodoo_uart_generic_specific - > usart ) & USART_SR_FE ) ) ; // read frame error flag
uint8_t input = usart_enhanced_recv ( busvoodoo_uart_generic_specific - > usart ) ; // read received character (also clears the error flags)
// display data
printf ( " '%c'/0x%02x " , input , input ) ;
// display errors
printf ( " ( " ) ;
if ( error_noise ) {
printf ( " noise " ) ;
} else if ( error_framing ) {
printf ( " framing " ) ;
} else if ( usart_enhanced_parity_error ( busvoodoo_uart_generic_specific - > usart ) ) {
printf ( " parity " ) ;
} else {
printf ( " no " ) ;
}
printf ( " error) " ) ;
}
printf ( " \n " ) ;
2018-06-16 11:05:48 +02:00
if ( user_input_available ) { // user interrupted flow
user_input_get ( ) ; // discard user input
}
2018-06-15 09:47:22 +02:00
if ( busvoodoo_uart_generic_specific - > rx_post ) {
( * busvoodoo_uart_generic_specific - > rx_post ) ( ) ;
}
}
/** perform UART action
* @ param [ in ] action action to perform
* @ param [ in ] repetition how many times to perform the action
* @ param [ in ] perform the action ( true ) or just check it ( false )
* @ return true if the action has been performed , false if it is malformed
*/
static bool busvoodoo_uart_generic_action ( const char * action , uint32_t repetition , bool perform )
{
uint32_t length = strlen ( action ) ; // remember length since it will be used a number of times
if ( NULL = = action | | 0 = = length ) { // there is nothing to do
return true ;
}
if ( 1 = = length & & ' r ' = = action [ 0 ] ) { // read data
if ( ! perform ) {
return true ;
}
for ( uint32_t i = 0 ; i < repetition ; i + + ) {
busvoodoo_uart_generic_read ( ) ; // read from UART
}
} else if ( 1 = = length & & ' u ' = = action [ 0 ] ) { // sleep us
if ( ! perform ) {
return true ;
}
printf ( " wait for %u us \n " , repetition ) ;
sleep_us ( repetition ) ; // sleep
} else if ( 1 = = length & & ' m ' = = action [ 0 ] ) { // sleep ms
if ( ! perform ) {
return true ;
}
printf ( " wait for %u ms \n " , repetition ) ;
sleep_ms ( repetition ) ; // sleep
} else if ( ' 0 ' = = action [ 0 ] ) { // send digit
if ( 1 = = length ) { // just send 0
if ( ! perform ) {
return true ;
}
for ( uint32_t i = 0 ; i < repetition ; i + + ) {
busvoodoo_uart_generic_write ( 0 ) ; // write to UART
}
} else if ( ' x ' = = action [ 1 ] | | ' b ' = = action [ 1 ] ) { // send hex/binary
return busvoodoo_uart_generic_action ( action + 1 , repetition , perform ) ; // just retry without leading 0
} else if ( action [ 1 ] > = ' 0 ' & & action [ 1 ] < = ' 9 ' ) { // send decimal
return busvoodoo_uart_generic_action ( action + 1 , repetition , perform ) ; // just retry without leading 0
} else { // malformed action
return false ;
}
} else if ( ' x ' = = action [ 0 ] & & length > 1 ) { // send hexadecimal value
for ( uint32_t i = 1 ; i < length ; i + + ) { // check string
if ( ! ( ( action [ i ] > = ' 0 ' & & action [ i ] < = ' 9 ' ) | | ( action [ i ] > = ' a ' & & action [ i ] < = ' f ' ) | | ( action [ i ] > = ' A ' & & action [ i ] < = ' F ' ) ) ) { // check for hexadecimal character
return false ; // not an hexadecimal string
}
}
if ( ! perform ) {
return true ;
}
uint32_t value = strtol ( & action [ 1 ] , NULL , 16 ) ; // get hex value
for ( uint32_t i = 0 ; i < repetition ; i + + ) {
busvoodoo_uart_generic_write ( value ) ; // write to SPI
}
} else if ( ' b ' = = action [ 0 ] & & length > 1 ) { // send binary value
for ( uint32_t i = 1 ; i < length ; i + + ) { // check string
if ( action [ i ] < ' 0 ' | | action [ i ] > ' 1 ' ) { // check for binary character
return false ; // not a binary string
}
}
if ( ! perform ) {
return true ;
}
uint32_t value = strtol ( & action [ 1 ] , NULL , 2 ) ; // get binary value
for ( uint32_t i = 0 ; i < repetition ; i + + ) {
busvoodoo_uart_generic_write ( value ) ; // write to SPI
}
} else if ( action [ 0 ] > = ' 1 ' & & action [ 0 ] < = ' 9 ' ) { // send decimal value
for ( uint32_t i = 1 ; i < length ; i + + ) { // check string
if ( action [ i ] < ' 0 ' | | action [ i ] > ' 9 ' ) { // check for decimal character
return false ; // not a decimal string
}
}
if ( ! perform ) {
return true ;
}
uint32_t value = strtol ( & action [ 0 ] , NULL , 10 ) ; // get decimal value
for ( uint32_t i = 0 ; i < repetition ; i + + ) {
busvoodoo_uart_generic_write ( value ) ; // write to SPI
}
} else if ( length > = 2 & & ( ' " ' = = action [ 0 ] | | ' \' ' = = action [ 0 ] ) & & ( action [ length - 1 ] = = action [ 0 ] ) ) { // send ASCII character
if ( ! perform ) {
return true ;
}
for ( uint32_t r = 0 ; r < repetition ; r + + ) {
for ( uint32_t i = 1 ; i < length - 1 ; i + + ) { // go through string
busvoodoo_uart_generic_write ( action [ i ] ) ; // write to SPI
}
}
} else { // malformed action
return false ;
}
return true ; // all went well
}
// command handlers
/** command to perform actions
* @ param [ in ] argument actions to perform
*/
static void busvoodoo_uart_generic_command_actions ( void * argument )
{
if ( NULL = = argument | | 0 = = strlen ( argument ) ) {
printf ( " available actions (separated by space or ,): \n " ) ;
printf ( " 0 \t write decimal value \n " ) ;
printf ( " 0x0 \t write hexadecimal value \n " ) ;
printf ( " 0b0 \t write binary value \n " ) ;
printf ( " \" a \" /'a' \t write ASCII characters \n " ) ;
printf ( " r \t read value \n " ) ;
printf ( " u/m \t wait 1 us/ms \n " ) ;
printf ( " :n \t repeat action n times \n " ) ;
return ;
}
// copy argument since it will be modified
char * copy = calloc ( strlen ( argument ) + 1 , sizeof ( char ) ) ;
if ( ! copy ) {
while ( true ) ;
}
strncpy ( copy , argument , strlen ( argument ) + 1 ) ;
// verify and perform actions
if ( ! busvoodoo_global_actions ( copy , false , & busvoodoo_uart_generic_action ) ) { // verify actions
printf ( " malformed action(s) \n " ) ;
} else { // action are ok
printf ( " press any key to exit \n " ) ;
busvoodoo_global_actions ( argument , true , & busvoodoo_uart_generic_action ) ; // perform action
if ( user_input_available ) { // user interrupted flow
user_input_get ( ) ; // discard user input
}
}
free ( copy ) ; // release memory
}
/** command to transmit a string
* @ param [ in ] argument string to transmit ( CR + LF when none provided )
*/
static void busvoodoo_uart_generic_command_transmit ( void * argument )
{
if ( NULL = = busvoodoo_uart_generic_specific ) {
return ;
}
if ( busvoodoo_uart_generic_specific - > tx_pre ) {
( * busvoodoo_uart_generic_specific - > tx_pre ) ( ) ;
}
if ( NULL = = argument | | 0 = = strlen ( argument ) ) { // nothing to transmit
argument = " \r \n " ; // transmit CR+LF
}
printf ( " press any key to exit \n " ) ;
for ( uint16_t i = 0 ; ( ( char * ) ( argument ) ) [ i ] & & ! user_input_available ; i + + ) {
while ( ( 0 = = ( USART_SR ( busvoodoo_uart_generic_specific - > usart ) & USART_SR_TXE ) & & ! user_input_available ) ) ; // wait for transmit buffer to be empty
if ( USART_SR ( busvoodoo_uart_generic_specific - > usart ) & USART_SR_TXE ) { // we can send a character
printf ( " %c " , ( ( char * ) ( argument ) ) [ i ] ) ; // echo character to transmit
busvoodoo_led_blue_pulse ( BUSVOODOO_LED_PULSE ) ; // pulse blue LED to show transmission
usart_enhanced_send ( busvoodoo_uart_generic_specific - > usart , ( ( char * ) ( argument ) ) [ i ] ) ; // transmit character
}
}
2018-06-17 12:09:50 +02:00
while ( ( 0 = = ( ( USART_SR ( busvoodoo_uart_generic_specific - > usart ) ) & USART_SR_TC ) & & ! user_input_available ) ) ; // wait for transfer to be complete
2018-06-15 09:47:22 +02:00
if ( user_input_available ) { // user interrupted flow
user_input_get ( ) ; // discard user input
}
if ( strcmp ( argument , " \r \n " ) ) {
printf ( " \n " ) ;
}
if ( busvoodoo_uart_generic_specific - > tx_post ) {
( * busvoodoo_uart_generic_specific - > tx_post ) ( ) ;
}
}
/** command to receive data
* @ param [ in ] argument in which format to display
*/
static void busvoodoo_uart_generic_command_receive ( void * argument )
{
bool display_hex = false ; // display in hex
bool display_bin = false ; // display in bin
if ( NULL ! = argument & & strlen ( argument ) > 0 ) {
if ( 0 = = strcmp ( argument , " h " ) | | 0 = = strcmp ( argument , " hex " ) ) { // user wants hexadecimal display
display_hex = true ; // remember to display in hexadecimal
} else if ( 0 = = strcmp ( argument , " b " ) | | 0 = = strcmp ( argument , " bin " ) ) { // user wants binary display
display_bin = true ; // remember to display in binary
} else {
printf ( " malformed argument \n " ) ;
return ;
}
}
if ( NULL = = busvoodoo_uart_generic_specific ) {
return ;
}
if ( busvoodoo_uart_generic_specific - > rx_pre ) {
( * busvoodoo_uart_generic_specific - > rx_pre ) ( ) ;
}
printf ( " press any key to exit \n " ) ;
while ( ! user_input_available ) { // check for user input to exit
if ( ( USART_SR ( busvoodoo_uart_generic_specific - > usart ) & USART_SR_RXNE ) ) { // verify if data has been received
uint8_t input = usart_enhanced_recv ( busvoodoo_uart_generic_specific - > usart ) ; // receive character
busvoodoo_led_blue_pulse ( BUSVOODOO_LED_PULSE ) ; // enable blue LED to show reception
if ( display_hex ) { // display data in hex
printf ( " %02x " , input ) ;
} else if ( display_bin ) { // display data in binary
printf ( " %08b " , input ) ;
} else { // display in ASCII
printf ( " %c " , input ) ; // print received character
}
}
}
user_input_get ( ) ; // discard user input
printf ( " \n " ) ; // get to next line
if ( busvoodoo_uart_generic_specific - > rx_post ) {
( * busvoodoo_uart_generic_specific - > rx_post ) ( ) ;
}
}
/** command to transmit and receive data
* @ param [ in ] argument no argument required
*/
static void busvoodoo_uart_generic_command_transceive ( void * argument )
{
( void ) argument ; // we won't use the argument
if ( NULL = = busvoodoo_uart_generic_specific ) {
return ;
}
if ( busvoodoo_uart_generic_specific - > rx_pre ) {
( * busvoodoo_uart_generic_specific - > rx_pre ) ( ) ;
}
printf ( " press 5 times escape to exit \n " ) ;
char last_c = 0 ; // last user character received
uint8_t esc_count = 0 ; // number of times escape has press received
while ( true ) { // check for escape sequence
if ( user_input_available ) { // check if user wants to transmit something
char c = user_input_get ( ) ; // get user input
if ( 0x1b = = c ) { // user pressed escape
if ( 0x1b ! = last_c ) { // this is the first escape press
esc_count = 0 ;
}
esc_count + + ; // increment escape count
}
last_c = c ; // remember key press
if ( esc_count < 5 ) { // check for escape sequence
if ( busvoodoo_uart_generic_specific - > rx_post ) {
( * busvoodoo_uart_generic_specific - > rx_post ) ( ) ;
}
if ( busvoodoo_uart_generic_specific - > tx_pre ) {
( * busvoodoo_uart_generic_specific - > tx_pre ) ( ) ;
}
while ( ( 0 = = ( USART_SR ( busvoodoo_uart_generic_specific - > usart ) & USART_SR_TXE ) & & ! user_input_available ) ) ; // wait for transmit buffer to be empty
if ( USART_SR ( busvoodoo_uart_generic_specific - > usart ) & USART_SR_TXE ) { // we can send a character
usart_enhanced_send ( busvoodoo_uart_generic_specific - > usart , c ) ; // send user character
busvoodoo_led_blue_pulse ( BUSVOODOO_LED_PULSE ) ; // enable blue LED to show transmission
}
2018-06-17 12:09:50 +02:00
while ( ( 0 = = ( ( USART_SR ( busvoodoo_uart_generic_specific - > usart ) ) & USART_SR_TC ) & & ! user_input_available ) ) ; // wait for transfer to be complete
2018-06-15 09:47:22 +02:00
if ( busvoodoo_uart_generic_specific - > tx_post ) {
( * busvoodoo_uart_generic_specific - > tx_post ) ( ) ;
}
if ( busvoodoo_uart_generic_specific - > rx_pre ) {
( * busvoodoo_uart_generic_specific - > rx_pre ) ( ) ;
}
} else { // user wants to exit
break ; // exit infinite loop
}
}
if ( ( USART_SR ( busvoodoo_uart_generic_specific - > usart ) & USART_SR_RXNE ) ) { // verify if data has been received
char input = usart_enhanced_recv ( busvoodoo_uart_generic_specific - > usart ) ; // receive character
busvoodoo_led_blue_pulse ( BUSVOODOO_LED_PULSE ) ; // enable blue LED to show reception
printf ( " %c " , input ) ; // print received character
}
}
printf ( " \n " ) ; // get to next line
if ( busvoodoo_uart_generic_specific - > rx_post ) {
( * busvoodoo_uart_generic_specific - > rx_post ) ( ) ;
}
}
/** command to verify incoming transmission for error
* @ param [ in ] argument argument not required
*/
static void busvoodoo_uart_generic_command_error ( void * argument )
{
( void ) argument ; // argument not used
printf ( " press any key to exit \n " ) ;
while ( ! user_input_available ) { // wait until user interrupt
busvoodoo_uart_generic_read ( ) ; // read incoming data (this also checks for errors
}
user_input_get ( ) ; // discard user input
}
2018-07-11 08:21:41 +02:00
/** 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 " ) ;
2018-07-13 18:30:45 +02:00
2018-07-11 08:21:41 +02:00
// 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
2018-07-13 18:30:45 +02:00
2018-07-11 08:21:41 +02:00
// 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
2018-07-13 18:30:45 +02:00
// switch on RX
if ( busvoodoo_uart_generic_specific - > rx_pre ) {
( * busvoodoo_uart_generic_specific - > rx_pre ) ( ) ;
}
2018-07-11 08:21:41 +02:00
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 ( " \n new baud rate: %u bps \n " , uart_baudrate ) ; // show measurement frequency
} else {
printf ( " \n detected %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 ( " \n restarting 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 ( " \n new 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 " ) ;
2018-07-13 18:30:45 +02:00
// switch off RX
if ( busvoodoo_uart_generic_specific - > rx_post ) {
( * busvoodoo_uart_generic_specific - > rx_post ) ( ) ;
}
2018-07-11 08:21:41 +02:00
// 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
}
2018-06-15 09:47:22 +02:00
const struct menu_command_t busvoodoo_uart_generic_commands [ busvoodoo_uart_generic_commands_nb ] = {
{
. shortcut = ' a ' ,
. name = " action " ,
. command_description = " perform protocol actions " ,
. argument = MENU_ARGUMENT_STRING ,
. argument_description = " [actions] " ,
. command_handler = & busvoodoo_uart_generic_command_actions ,
} ,
{
. shortcut = ' r ' ,
. name = " receive " ,
. command_description = " show incoming data [in hexadecimal or binary] " ,
. argument = MENU_ARGUMENT_STRING ,
. argument_description = " [hex|bin] " ,
. command_handler = & busvoodoo_uart_generic_command_receive ,
} ,
{
. shortcut = ' t ' ,
. name = " transmit " ,
. command_description = " transmit ASCII text (empty for CR+LF) " ,
. argument = MENU_ARGUMENT_STRING ,
. argument_description = " [text] " ,
. command_handler = & busvoodoo_uart_generic_command_transmit ,
} ,
{
. shortcut = ' x ' ,
. name = " transceive " ,
. command_description = " transmit and receive data " ,
. argument = MENU_ARGUMENT_NONE ,
. argument_description = NULL ,
. command_handler = & busvoodoo_uart_generic_command_transceive ,
} ,
{
. shortcut = ' e ' ,
. name = " error " ,
. command_description = " verify incoming transmission for errors " ,
. argument = MENU_ARGUMENT_NONE ,
. argument_description = NULL ,
. command_handler = & busvoodoo_uart_generic_command_error ,
} ,
2018-07-11 08:21:41 +02:00
{
. 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 ,
} ,
2018-06-15 09:47:22 +02:00
} ;
2018-07-11 08:21:41 +02:00