2020-02-17 14:07:20 +01:00
/** library to control up to 4 independent receive and transmit software UART ports
* @ file
2016-10-23 17:42:55 +02:00
* @ author King Kévin < kingkevin @ cuvoodoo . info >
2020-06-06 14:35:55 +02:00
* @ copyright SPDX - License - Identifier : GPL - 3.0 - or - later
2020-02-17 14:07:20 +01:00
* @ date 2016 - 2020
2016-10-23 17:42:55 +02:00
* @ note peripherals used : GPIO @ ref uart_soft_gpio , timer @ ref uart_soft_timer
*/
/* standard libraries */
# include <stdint.h> // standard integer types
# include <stdlib.h> // general utilities
/* STM32 (including CM3) libraries */
# include <libopencm3/stm32/rcc.h> // real-time control clock library
# include <libopencm3/stm32/gpio.h> // general purpose input output library
# include <libopencm3/stm32/timer.h> // timer library
# include <libopencm3/cm3/nvic.h> // interrupt handler
# include <libopencm3/stm32/exti.h> // external interrupt defines
# include <libopencmsis/core_cm3.h> // Cortex M3 utilities
# include "uart_soft.h" // software UART library API
# include "global.h" // common methods
/** @defgroup uart_soft_gpio GPIO used for the software 4 UART ports
* @ note comment if unused
* @ warning only one port must be used per line ( pin number )
* @ {
*/
# define UART_SOFT_RX_PORT0 B /**< port for receive signal for UART port 0 */
# define UART_SOFT_RX_PIN0 9 /**< pin for receive signal for UART port 0 */
//#define UART_SOFT_RX_PORT1 A /**< port for receive signal for UART port 0 */
//#define UART_SOFT_RX_PIN1 0 /**< pin for receive signal for UART port 0 */
//#define UART_SOFT_RX_PORT2 A /**< port for receive signal for UART port 0 */
//#define UART_SOFT_RX_PIN2 0 /**< pin for receive signal for UART port 0 */
//#define UART_SOFT_RX_PORT3 A /**< port for receive signal for UART port 0 */
//#define UART_SOFT_RX_PIN3 0 /**< pin for receive signal for UART port 0 */
# define UART_SOFT_TX_PORT0 B /**< port for transmit signal for UART port 0 */
# define UART_SOFT_TX_PIN0 8 /**< pin for transmit signal for UART port 0 */
//#define UART_SOFT_TX_PORT1 A /**< port for transmit signal for UART port 0 */
//#define UART_SOFT_TX_PIN1 0 /**< pin for transmit signal for UART port 0 */
//#define UART_SOFT_TX_PORT2 A /**< port for transmit signal for UART port 0 */
//#define UART_SOFT_TX_PIN2 0 /**< pin for transmit signal for UART port 0 */
//#define UART_SOFT_TX_PORT3 A /**< port for transmit signal for UART port 0 */
//#define UART_SOFT_TX_PIN3 0 /**< pin for transmit signal for UART port 0 */
/** @} */
/** buffer size for receive and transmit buffers */
# define UART_SOFT_BUFFER 128
/** UART receive state definition */
struct soft_uart_rx_state {
uint32_t port ; /**< UART receive port */
uint16_t pin ; /**< UART receive pin */
uint32_t rcc ; /**< UART receive port peripheral clock */
uint32_t exti ; /**< UART receive external interrupt */
uint32_t irq ; /**< UART receive interrupt request */
uint32_t baudrate ; /**< UART receive baud rate */
volatile uint16_t state ; /**< GPIO state for receive pin */
volatile uint8_t bit ; /**< next UART frame bit to receive */
volatile uint8_t byte ; /**< byte being received */
volatile uint8_t buffer [ UART_SOFT_BUFFER ] ; /**< receive buffer */
volatile uint8_t buffer_i ; /**< index of current data to be read out */
volatile uint8_t buffer_used ; /**< how much data is available */
volatile bool lock ; /**< put lock when changing buffer_i or buffer_used */
volatile uint8_t buffer_byte ; /**< to temporary store byte while locked */
volatile bool buffer_byte_used ; /**< signal a byte has been stored in temporary buffer */
} ;
/** UART transmit state definition */
struct soft_uart_tx_state {
uint32_t port ; /**< UART receive port */
uint16_t pin ; /**< UART receive pin */
uint32_t rcc ; /**< UART receive port peripheral clock */
uint32_t baudrate ; /**< UART receive baud rate */
volatile uint8_t bit ; /**< next UART frame bit to transmit */
volatile uint8_t byte ; /**< byte being transmitted */
volatile uint8_t buffer [ UART_SOFT_BUFFER ] ; /**< receive buffer */
volatile uint8_t buffer_i ; /**< index of current data to be read out */
volatile uint8_t buffer_used ; /**< how much data is available */
volatile bool transmit ; /**< flag to know it transmission is ongoing */
} ;
static struct soft_uart_rx_state * uart_soft_rx_states [ 4 ] = { NULL } ; /**< states of UART receive ports (up to 4) */
static struct soft_uart_tx_state * uart_soft_tx_states [ 4 ] = { NULL } ; /**< states of UART transmit ports (up to 4) */
volatile bool uart_soft_received [ 4 ] = { false , false , false , false } ;
/** @defgroup uart_soft_timer timer used to sample UART signals
* @ {
*/
# if (defined(UART_SOFT_RX_PORT0) && defined(UART_SOFT_RX_PIN0)) || (defined(UART_SOFT_RX_PORT1) && defined(UART_SOFT_RX_PIN1)) || (defined(UART_SOFT_RX_PORT2) && defined(UART_SOFT_RX_PIN2)) || (defined(UART_SOFT_RX_PORT3) && defined(UART_SOFT_RX_PIN0))
# define UART_SOFT_RX_TIMER 3 /**< timer peripheral for receive signals */
# endif
# if (defined(UART_SOFT_TX_PORT0) && defined(UART_SOFT_TX_PIN0)) || (defined(UART_SOFT_TX_PORT1) && defined(UART_SOFT_TX_PIN1)) || (defined(UART_SOFT_TX_PORT2) && defined(UART_SOFT_TX_PIN2)) || (defined(UART_SOFT_TX_PORT3) && defined(UART_SOFT_TX_PIN0))
# define UART_SOFT_TX_TIMER 4 /**< timer peripheral for transmit signals */
# endif
/** @} */
2020-02-17 14:07:20 +01:00
static const uint32_t timer_flags [ 4 ] = { TIM_SR_CC1IF , TIM_SR_CC2IF , TIM_SR_CC3IF , TIM_SR_CC4IF } ; /**< the interrupt flags for the compare units */
static const uint32_t timer_interrupt [ 4 ] = { TIM_DIER_CC1IE , TIM_DIER_CC2IE , TIM_DIER_CC3IE , TIM_DIER_CC4IE } ; /**< the interrupt enable for the compare units */
static const enum tim_oc_id timer_oc [ 4 ] = { TIM_OC1 , TIM_OC2 , TIM_OC3 , TIM_OC4 } ; /**< the output compares for the compare units */
2016-10-23 17:42:55 +02:00
bool uart_soft_setup ( uint32_t * rx_baudrates , uint32_t * tx_baudrates )
{
( void ) rx_baudrates ; // ensure compile does no complain even if no receive port is used
( void ) tx_baudrates ; // ensure compile does no complain even if no transmit port is used
// save UART receive definition
# if defined(UART_SOFT_RX_PORT0) && defined(UART_SOFT_RX_PIN0)
2020-02-17 14:07:20 +01:00
uart_soft_rx_states [ 0 ] = calloc ( 1 , sizeof ( struct soft_uart_rx_state ) ) ; // create state definition
2016-10-23 17:42:55 +02:00
uart_soft_rx_states [ 0 ] - > port = GPIO ( UART_SOFT_RX_PORT0 ) ; // save receive port
uart_soft_rx_states [ 0 ] - > pin = GPIO ( UART_SOFT_RX_PIN0 ) ; // save receive pin
uart_soft_rx_states [ 0 ] - > rcc = RCC_GPIO ( UART_SOFT_RX_PORT0 ) ; // save receive port peripheral clock
uart_soft_rx_states [ 0 ] - > exti = EXTI ( UART_SOFT_RX_PIN0 ) ; // save receive external interrupt
uart_soft_rx_states [ 0 ] - > irq = NVIC_EXTI_IRQ ( UART_SOFT_RX_PIN0 ) ; // save receive interrupt request
# endif
// setup UART receive GPIO
2020-02-17 14:07:20 +01:00
for ( uint8_t rx = 0 ; rx < 4 ; rx + + ) {
2016-10-23 17:42:55 +02:00
if ( ! uart_soft_rx_states [ rx ] ) { // verify is receive UART is defined
continue ; // skip configuration if not defined
}
2020-02-17 14:07:20 +01:00
if ( ! rx_baudrates | | 0 = = rx_baudrates [ rx ] ) { // verify if receive baud rate has been defined
2016-10-23 17:42:55 +02:00
return false ;
}
uart_soft_rx_states [ rx ] - > baudrate = rx_baudrates [ rx ] ; // save baud rate
rcc_periph_clock_enable ( uart_soft_rx_states [ rx ] - > rcc ) ; // enable clock for GPIO peripheral
gpio_set_mode ( uart_soft_rx_states [ rx ] - > port , GPIO_MODE_INPUT , GPIO_CNF_INPUT_PULL_UPDOWN , uart_soft_rx_states [ rx ] - > pin ) ; // setup GPIO pin UART receive
gpio_set ( uart_soft_rx_states [ rx ] - > port , uart_soft_rx_states [ rx ] - > pin ) ; // pull up to avoid noise when not connected
rcc_periph_clock_enable ( RCC_AFIO ) ; // enable alternate function clock for external interrupt
2019-03-26 19:27:40 +01:00
exti_select_source ( uart_soft_rx_states [ rx ] - > exti , uart_soft_rx_states [ rx ] - > port ) ; // mask external interrupt of this pin only for this port
2016-10-23 17:42:55 +02:00
exti_enable_request ( uart_soft_rx_states [ rx ] - > exti ) ; // enable external interrupt
exti_set_trigger ( uart_soft_rx_states [ rx ] - > exti , EXTI_TRIGGER_BOTH ) ; // trigger when button is pressed
nvic_enable_irq ( uart_soft_rx_states [ rx ] - > irq ) ; // enable interrupt
uart_soft_rx_states [ rx ] - > state = gpio_get ( uart_soft_rx_states [ rx ] - > port , uart_soft_rx_states [ rx ] - > pin ) ; // save state of GPIO
uart_soft_rx_states [ rx ] - > bit = 0 ; // reset bits received
}
// save UART transmit definition
2019-03-26 19:27:40 +01:00
# if defined(UART_SOFT_TX_PORT0) && defined(UART_SOFT_TX_PIN0)
2020-02-17 14:07:20 +01:00
uart_soft_tx_states [ 0 ] = calloc ( 1 , sizeof ( struct soft_uart_tx_state ) ) ; // create state definition
2016-10-23 17:42:55 +02:00
uart_soft_tx_states [ 0 ] - > port = GPIO ( UART_SOFT_TX_PORT0 ) ; // save receive port
uart_soft_tx_states [ 0 ] - > pin = GPIO ( UART_SOFT_TX_PIN0 ) ; // save receive pin
uart_soft_tx_states [ 0 ] - > rcc = RCC_GPIO ( UART_SOFT_TX_PORT0 ) ; // save receive port peripheral clock
# endif
2019-03-26 19:27:40 +01:00
2016-10-23 17:42:55 +02:00
// setup UART transmit GPIO
2020-02-17 14:07:20 +01:00
for ( uint8_t tx = 0 ; tx < 4 ; tx + + ) {
2016-10-23 17:42:55 +02:00
if ( ! uart_soft_tx_states [ tx ] ) { // verify is transmit UART is defined
continue ; // skip configuration if not defined
}
2020-02-17 14:07:20 +01:00
if ( ! tx_baudrates | | 0 = = tx_baudrates [ tx ] ) { // verify if transmit baud rate has been defined
2016-10-23 17:42:55 +02:00
return false ;
}
uart_soft_tx_states [ tx ] - > baudrate = tx_baudrates [ tx ] ; // save baud rate
rcc_periph_clock_enable ( uart_soft_tx_states [ tx ] - > rcc ) ; // enable clock for GPIO peripheral
gpio_set_mode ( uart_soft_tx_states [ tx ] - > port , GPIO_MODE_OUTPUT_2_MHZ , GPIO_CNF_OUTPUT_PUSHPULL , uart_soft_tx_states [ tx ] - > pin ) ; // setup GPIO UART transmit pin
gpio_set ( uart_soft_tx_states [ tx ] - > port , uart_soft_tx_states [ tx ] - > pin ) ; // idle high
}
// setup timer
# if defined(UART_SOFT_RX_TIMER)
rcc_periph_clock_enable ( RCC_TIM ( UART_SOFT_RX_TIMER ) ) ; // enable clock for timer peripheral
2020-02-17 13:59:49 +01:00
rcc_periph_reset_pulse ( RST_TIM ( UART_SOFT_RX_TIMER ) ) ; // reset timer state
2016-10-23 17:42:55 +02:00
timer_set_mode ( TIM ( UART_SOFT_RX_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 ( UART_SOFT_RX_TIMER ) , 0 ) ; // prescaler to be able to sample 2400-115200 bps (72MHz/2^16=1099<2400bps)
nvic_enable_irq ( NVIC_TIM_IRQ ( UART_SOFT_RX_TIMER ) ) ; // allow interrupt for timer
timer_enable_counter ( TIM ( UART_SOFT_RX_TIMER ) ) ; // start timer to generate interrupts for the receive pins
# endif
# if defined(UART_SOFT_TX_TIMER)
rcc_periph_clock_enable ( RCC_TIM ( UART_SOFT_TX_TIMER ) ) ; // enable clock for timer peripheral
2020-02-17 13:59:49 +01:00
rcc_periph_reset_pulse ( RST_TIM ( UART_SOFT_TX_TIMER ) ) ; // reset timer state
2016-10-23 17:42:55 +02:00
timer_set_mode ( TIM ( UART_SOFT_TX_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 ( UART_SOFT_TX_TIMER ) , 0 ) ; // prescaler to be able to output 2400-115200 bps (72MHz/2^16=1099<2400bps)
nvic_enable_irq ( NVIC_TIM_IRQ ( UART_SOFT_TX_TIMER ) ) ; // allow interrupt for timer
timer_enable_counter ( TIM ( UART_SOFT_TX_TIMER ) ) ; // start timer to generate interrupts for the transmit pins
# endif
return true ; // setup completed
}
# if defined(UART_SOFT_RX_TIMER)
uint8_t uart_soft_getbyte ( uint8_t uart )
{
2020-02-17 14:07:20 +01:00
if ( uart > = 4 | | ! uart_soft_rx_states [ uart ] ) { // ensure receive UART port is defined
2016-10-23 17:42:55 +02:00
return 0 ; // return
}
while ( ! uart_soft_rx_states [ uart ] - > buffer_used ) { // idle until data is available
__WFI ( ) ; // sleep until interrupt
}
uart_soft_rx_states [ uart ] - > lock = true ; // set lock
uint8_t to_return = uart_soft_rx_states [ uart ] - > buffer [ uart_soft_rx_states [ uart ] - > buffer_i ] ; // get the next available character
2020-02-17 14:07:20 +01:00
uart_soft_rx_states [ uart ] - > buffer_i = ( uart_soft_rx_states [ uart ] - > buffer_i + 1 ) % LENGTH ( uart_soft_rx_states [ uart ] - > buffer ) ; // update used buffer
2016-10-23 17:42:55 +02:00
uart_soft_rx_states [ uart ] - > buffer_used - - ; // update used buffer
uart_soft_rx_states [ uart ] - > lock = false ; // free lock
if ( uart_soft_rx_states [ uart ] - > buffer_byte_used ) { // temporary byte has been stored
2020-02-17 14:07:20 +01:00
uart_soft_rx_states [ uart ] - > buffer [ ( uart_soft_rx_states [ uart ] - > buffer_i + uart_soft_rx_states [ uart ] - > buffer_used ) % LENGTH ( uart_soft_rx_states [ uart ] - > buffer ) ] = uart_soft_rx_states [ uart ] - > buffer_byte ; // put byte in buffer
2016-10-23 17:42:55 +02:00
uart_soft_rx_states [ uart ] - > buffer_used + + ; // update used buffer
uart_soft_rx_states [ uart ] - > buffer_byte_used = false ; // buffer byte is now in buffer
}
2020-02-17 14:07:20 +01:00
uart_soft_received [ uart ] = ( 0 ! = uart_soft_rx_states [ uart ] - > buffer_used ) ; // notify user if data is available
2016-10-23 17:42:55 +02:00
uart_soft_rx_states [ uart ] - > lock = false ; // free lock
return to_return ;
}
/** timer interrupt service routine to generate UART transmit signals */
void TIM_ISR ( UART_SOFT_RX_TIMER ) ( void )
{
2020-02-17 14:07:20 +01:00
for ( uint8_t rx = 0 ; rx < 4 ; rx + + ) {
2016-10-23 17:42:55 +02:00
if ( timer_interrupt_source ( TIM ( UART_SOFT_RX_TIMER ) , timer_flags [ rx ] ) ) { // got a match on compare for receive pin
timer_clear_flag ( TIM ( UART_SOFT_RX_TIMER ) , timer_flags [ rx ] ) ; // clear flag
if ( ! uart_soft_rx_states [ rx ] ) { // verify if RX exists
continue ; // skip if receive port is not defined it
}
2020-02-17 14:07:20 +01:00
uart_soft_rx_states [ rx ] - > byte + = ( 0 = = ( gpio_get ( uart_soft_rx_states [ rx ] - > port , uart_soft_rx_states [ rx ] - > pin ) ? 0 : 1 ) < < ( uart_soft_rx_states [ rx ] - > bit - 1 ) ) ; // save bit value
if ( uart_soft_rx_states [ rx ] - > bit < 8 ) { // not the last bit received
timer_set_oc_value ( TIM ( UART_SOFT_RX_TIMER ) , timer_oc [ rx ] , timer_get_counter ( TIM ( UART_SOFT_RX_TIMER ) ) + rcc_ahb_frequency / uart_soft_rx_states [ rx ] - > baudrate ) ; // set timer to next bit
2016-10-23 17:42:55 +02:00
uart_soft_rx_states [ rx ] - > bit + + ; // wait for next bit
} else { // last bit received
if ( uart_soft_rx_states [ rx ] - > lock ) { // someone is already reading data
uart_soft_rx_states [ rx ] - > buffer_byte = uart_soft_rx_states [ rx ] - > byte ; // save byte
uart_soft_rx_states [ rx ] - > buffer_byte_used = true ; // notify reader there is a temporary byte
} else { // buffer can be updated
2020-02-17 14:07:20 +01:00
if ( uart_soft_rx_states [ rx ] - > buffer_used > = LENGTH ( uart_soft_rx_states [ rx ] - > buffer ) ) { // buffer is full
uart_soft_rx_states [ rx ] - > buffer_i = ( uart_soft_rx_states [ rx ] - > buffer_i + 1 ) % LENGTH ( uart_soft_rx_states [ rx ] - > buffer ) ; // drop oldest byte
2016-10-23 17:42:55 +02:00
uart_soft_rx_states [ rx ] - > buffer_used - - ; // update buffer usage
}
2020-02-17 14:07:20 +01:00
uart_soft_rx_states [ rx ] - > buffer [ ( uart_soft_rx_states [ rx ] - > buffer_i + uart_soft_rx_states [ rx ] - > buffer_used ) % LENGTH ( uart_soft_rx_states [ rx ] - > buffer ) ] = uart_soft_rx_states [ rx ] - > byte ; // put byte in buffer
2016-10-23 17:42:55 +02:00
uart_soft_rx_states [ rx ] - > buffer_used + + ; // update used buffer
uart_soft_received [ rx ] = true ; // notify user data is available
}
2020-02-17 14:07:20 +01:00
timer_disable_irq ( TIM ( UART_SOFT_RX_TIMER ) , timer_interrupt [ rx ] ) ; // stop_interrupting
2016-10-23 17:42:55 +02:00
uart_soft_rx_states [ rx ] - > bit = 0 ; // next bit should be first bit of next byte
}
}
}
}
# endif
# if defined(UART_SOFT_TX_TIMER)
void uart_soft_flush ( uint8_t uart )
{
2020-02-17 14:07:20 +01:00
if ( uart > = 4 | | ! uart_soft_tx_states [ uart ] ) { // ensure transmit UART port is defined
2016-10-23 17:42:55 +02:00
return ; // return
}
while ( uart_soft_tx_states [ uart ] - > buffer_used ) { // idle until buffer is empty
__WFI ( ) ; // sleep until interrupt
}
while ( uart_soft_tx_states [ uart ] - > transmit ) { // idle until transmission is complete
__WFI ( ) ; // sleep until interrupt
}
}
/** start transmitting a byte from the buffer
* @ param [ in ] uart UART port used for transmission
*/
static void uart_soft_transmit ( uint8_t uart ) {
if ( uart > = 4 | | ! uart_soft_tx_states [ uart ] ) { // ensure transmit UART port is defined
return ; // UART transmit port not defined
}
if ( uart_soft_tx_states [ uart ] - > transmit ) { // already transmitting
return ; // transmission is already ongoing
}
if ( ! uart_soft_tx_states [ uart ] - > buffer_used ) { // no buffered data to transmit
return ; // nothing to transmit
}
uart_soft_tx_states [ uart ] - > byte = uart_soft_tx_states [ uart ] - > buffer [ uart_soft_tx_states [ uart ] - > buffer_i ] ; // get byte
2020-02-17 14:07:20 +01:00
uart_soft_tx_states [ uart ] - > buffer_i = ( uart_soft_tx_states [ uart ] - > buffer_i + 1 ) % LENGTH ( uart_soft_tx_states [ uart ] - > buffer ) ; // update index
2016-10-23 17:42:55 +02:00
uart_soft_tx_states [ uart ] - > buffer_used - - ; // update used buffer
uart_soft_tx_states [ uart ] - > bit = 0 ; // LSb is transmitted first
uart_soft_tx_states [ uart ] - > transmit = true ; // start transmission
gpio_clear ( uart_soft_tx_states [ uart ] - > port , uart_soft_tx_states [ uart ] - > pin ) ; // output start bit
2020-02-17 14:07:20 +01:00
timer_set_oc_value ( TIM ( UART_SOFT_TX_TIMER ) , timer_oc [ uart ] , timer_get_counter ( TIM ( UART_SOFT_TX_TIMER ) ) + ( rcc_ahb_frequency / uart_soft_tx_states [ uart ] - > baudrate ) ) ; // set timer to output UART frame 1 (data bit 0) in 1 bit
2016-10-23 17:42:55 +02:00
timer_clear_flag ( TIM ( UART_SOFT_TX_TIMER ) , timer_flags [ uart ] ) ; // clear flag before enabling interrupt
timer_enable_irq ( TIM ( UART_SOFT_TX_TIMER ) , timer_interrupt [ uart ] ) ; // enable timer IRQ for TX for this UART
}
void uart_soft_putbyte_nonblocking ( uint8_t uart , uint8_t byte )
{
2020-02-17 14:07:20 +01:00
if ( uart > = 4 | | ! uart_soft_tx_states [ uart ] ) { // ensure transmit UART port is defined
2016-10-23 17:42:55 +02:00
return ; // return
2019-03-26 19:27:40 +01:00
}
2020-02-17 14:07:20 +01:00
while ( uart_soft_tx_states [ uart ] - > buffer_used > = LENGTH ( uart_soft_tx_states [ uart ] - > buffer ) ) { // idle until there is place in the buffer
2016-10-23 17:42:55 +02:00
__WFI ( ) ; // sleep until something happened
}
2020-02-17 14:07:20 +01:00
uart_soft_tx_states [ uart ] - > buffer [ ( uart_soft_tx_states [ uart ] - > buffer_i + uart_soft_tx_states [ uart ] - > buffer_used ) % LENGTH ( uart_soft_tx_states [ uart ] - > buffer ) ] = byte ; // save byte to be transmitted
2016-10-23 17:42:55 +02:00
uart_soft_tx_states [ uart ] - > buffer_used + + ; // update used buffer
uart_soft_transmit ( uart ) ; // start transmission
}
void uart_soft_putbyte_blocking ( uint8_t uart , uint8_t byte )
{
uart_soft_putbyte_nonblocking ( uart , byte ) ; // put byte in queue
uart_soft_flush ( uart ) ; // wait for all byte to be transmitted
}
/** timer interrupt service routine to sample UART receive signals */
void TIM_ISR ( UART_SOFT_TX_TIMER ) ( void )
{
2020-02-17 14:07:20 +01:00
for ( uint8_t tx = 0 ; tx < 4 ; tx + + ) {
if ( timer_interrupt_source ( TIM ( UART_SOFT_TX_TIMER ) , timer_flags [ tx ] ) ) { // got a match on compare for transmit pin
timer_clear_flag ( TIM ( UART_SOFT_TX_TIMER ) , timer_flags [ tx ] ) ; // clear flag
2016-10-23 17:42:55 +02:00
if ( ! uart_soft_tx_states [ tx ] ) { // verify if transmit is defined
continue ; // skip if transmit port is not defined it
}
2020-02-17 14:07:20 +01:00
if ( uart_soft_tx_states [ tx ] - > bit < 8 ) { // there is a data bit to transmit
if ( ( uart_soft_tx_states [ tx ] - > byte > > uart_soft_tx_states [ tx ] - > bit ) & 0x01 ) { // bit to transmit is a 1
2016-10-23 17:42:55 +02:00
gpio_set ( uart_soft_tx_states [ tx ] - > port , uart_soft_tx_states [ tx ] - > pin ) ; // set output to high
} else { // bit to transmit is a 0
gpio_clear ( uart_soft_tx_states [ tx ] - > port , uart_soft_tx_states [ tx ] - > pin ) ; // set output to low
}
2020-02-17 14:07:20 +01:00
timer_set_oc_value ( TIM ( UART_SOFT_TX_TIMER ) , timer_oc [ tx ] , timer_get_counter ( TIM ( UART_SOFT_TX_TIMER ) ) + ( rcc_ahb_frequency / uart_soft_tx_states [ tx ] - > baudrate ) ) ; // wait for the next frame bit
2016-10-23 17:42:55 +02:00
uart_soft_tx_states [ tx ] - > bit + + ; // go to next bit
2020-02-17 14:07:20 +01:00
} else if ( 8 = = uart_soft_tx_states [ tx ] - > bit ) { // transmit stop bit
2016-10-23 17:42:55 +02:00
gpio_set ( uart_soft_tx_states [ tx ] - > port , uart_soft_tx_states [ tx ] - > pin ) ; // go idle high
2020-02-17 14:07:20 +01:00
timer_set_oc_value ( TIM ( UART_SOFT_TX_TIMER ) , timer_oc [ tx ] , timer_get_counter ( TIM ( UART_SOFT_TX_TIMER ) ) + ( rcc_ahb_frequency / uart_soft_tx_states [ tx ] - > baudrate ) ) ; // wait for 1 stop bit
2016-10-23 17:42:55 +02:00
uart_soft_tx_states [ tx ] - > bit + + ; // go to next bit
} else { // UART frame is complete
timer_disable_irq ( TIM ( UART_SOFT_TX_TIMER ) , timer_interrupt [ tx ] ) ; // enable timer IRQ for TX for this UART
uart_soft_tx_states [ tx ] - > transmit = false ; // transmission finished
uart_soft_transmit ( tx ) ; // start next transmission (if there is)
}
} // compare match
} // go through UARTs
}
# endif
/** central function handling receive signal activity */
static void uart_soft_receive_activity ( void )
{
2020-02-17 14:07:20 +01:00
for ( uint8_t rx = 0 ; rx < 4 ; rx + + ) {
2016-10-23 17:42:55 +02:00
if ( ! uart_soft_rx_states [ rx ] ) { // verify if receive port is not configured
continue ; // skip if receive port is not defined it
}
if ( uart_soft_rx_states [ rx ] - > state ! = gpio_get ( uart_soft_rx_states [ rx ] - > port , uart_soft_rx_states [ rx ] - > pin ) ) { // only do something if state changed
uart_soft_rx_states [ rx ] - > state = gpio_get ( uart_soft_rx_states [ rx ] - > port , uart_soft_rx_states [ rx ] - > pin ) ; // save new state
2020-02-17 14:07:20 +01:00
if ( 0 = = uart_soft_rx_states [ rx ] - > bit ) { // start bit edge detected
if ( 0 = = uart_soft_rx_states [ rx ] - > state ) { // start bit has to be low
timer_set_oc_value ( TIM ( UART_SOFT_RX_TIMER ) , timer_oc [ rx ] , timer_get_counter ( TIM ( UART_SOFT_RX_TIMER ) ) + ( rcc_ahb_frequency / uart_soft_rx_states [ rx ] - > baudrate ) * 1.5 ) ; // set timer to sample data bit 0 in 1.5 bits
2016-10-23 17:42:55 +02:00
timer_clear_flag ( TIM ( UART_SOFT_RX_TIMER ) , timer_flags [ rx ] ) ; // clear flag before enabling interrupt
timer_enable_irq ( TIM ( UART_SOFT_RX_TIMER ) , timer_interrupt [ rx ] ) ; // enable timer IRQ for RX for this UART
uart_soft_rx_states [ rx ] - > byte = 0 ; // reset byte value
uart_soft_rx_states [ rx ] - > bit + + ; // wait for first bit
}
} else { // data bit detected
2020-02-17 14:07:20 +01:00
timer_set_oc_value ( TIM ( UART_SOFT_RX_TIMER ) , timer_oc [ rx ] , timer_get_counter ( TIM ( UART_SOFT_RX_TIMER ) ) + ( rcc_ahb_frequency / uart_soft_rx_states [ rx ] - > baudrate ) / 2 ) ; // resync timer to half a bit (good for drifting transmission, bad if the line is noisy)
2016-10-23 17:42:55 +02:00
}
}
}
}
/** GPIO interrupt service routine to detect UART receive activity */
# if (defined(UART_SOFT_RX_PORT0) && defined(UART_SOFT_RX_PIN0) && UART_SOFT_RX_PIN0==0) || (defined(UART_SOFT_RX_PORT1) && defined(UART_SOFT_RX_PIN1) && UART_SOFT_RX_PIN1==0) || (defined(UART_SOFT_RX_PORT2) && defined(UART_SOFT_RX_PIN2) && UART_SOFT_RX_PIN2==0) || (defined(UART_SOFT_RX_PORT3) && defined(UART_SOFT_RX_PIN3) && UART_SOFT_RX_PIN3==0)
void exti0_isr ( void )
{
exti_reset_request ( EXTI0 ) ; // clear interrupt flag for pin triggers this ISR (pin state will be checked independently)
uart_soft_receive_activity ( ) ; // check which GPIO changed
}
# endif
# if (defined(UART_SOFT_RX_PORT0) && defined(UART_SOFT_RX_PIN0) && UART_SOFT_RX_PIN0==1) || (defined(UART_SOFT_RX_PORT1) && defined(UART_SOFT_RX_PIN1) && UART_SOFT_RX_PIN1==1) || (defined(UART_SOFT_RX_PORT2) && defined(UART_SOFT_RX_PIN2) && UART_SOFT_RX_PIN2==1) || (defined(UART_SOFT_RX_PORT3) && defined(UART_SOFT_RX_PIN3) && UART_SOFT_RX_PIN3==1)
void exti1_isr ( void )
{
exti_reset_request ( EXTI1 ) ; // clear interrupt flag for pin triggers this ISR (pin state will be checked independently)
uart_soft_receive_activity ( ) ; // check which GPIO changed
}
# endif
# if (defined(UART_SOFT_RX_PORT0) && defined(UART_SOFT_RX_PIN0) && UART_SOFT_RX_PIN0==2) || (defined(UART_SOFT_RX_PORT1) && defined(UART_SOFT_RX_PIN1) && UART_SOFT_RX_PIN1==2) || (defined(UART_SOFT_RX_PORT2) && defined(UART_SOFT_RX_PIN2) && UART_SOFT_RX_PIN2==2) || (defined(UART_SOFT_RX_PORT3) && defined(UART_SOFT_RX_PIN3) && UART_SOFT_RX_PIN3==2)
void exti2_isr ( void )
{
exti_reset_request ( EXTI2 ) ; // clear interrupt flag for pin triggers this ISR (pin state will be checked independently)
uart_soft_receive_activity ( ) ; // check which GPIO changed
}
# endif
# if (defined(UART_SOFT_RX_PORT0) && defined(UART_SOFT_RX_PIN0) && UART_SOFT_RX_PIN0==3) || (defined(UART_SOFT_RX_PORT1) && defined(UART_SOFT_RX_PIN1) && UART_SOFT_RX_PIN1==3) || (defined(UART_SOFT_RX_PORT2) && defined(UART_SOFT_RX_PIN2) && UART_SOFT_RX_PIN2==3) || (defined(UART_SOFT_RX_PORT3) && defined(UART_SOFT_RX_PIN3) && UART_SOFT_RX_PIN3==3)
void exti3_isr ( void )
{
exti_reset_request ( EXTI3 ) ; // clear interrupt flag for pin triggers this ISR (pin state will be checked independently)
uart_soft_receive_activity ( ) ; // check which GPIO changed
}
# endif
# if (defined(UART_SOFT_RX_PORT0) && defined(UART_SOFT_RX_PIN0) && UART_SOFT_RX_PIN0==4) || (defined(UART_SOFT_RX_PORT1) && defined(UART_SOFT_RX_PIN1) && UART_SOFT_RX_PIN1==4) || (defined(UART_SOFT_RX_PORT2) && defined(UART_SOFT_RX_PIN2) && UART_SOFT_RX_PIN2==4) || (defined(UART_SOFT_RX_PORT3) && defined(UART_SOFT_RX_PIN3) && UART_SOFT_RX_PIN3==4)
void exti4_isr ( void )
{
exti_reset_request ( EXTI4 ) ; // clear interrupt flag for pin triggers this ISR (pin state will be checked independently)
uart_soft_receive_activity ( ) ; // check which GPIO changed
}
# endif
# if (defined(UART_SOFT_RX_PORT0) && defined(UART_SOFT_RX_PIN0) && (UART_SOFT_RX_PIN0==5 || UART_SOFT_RX_PIN0==6 || UART_SOFT_RX_PIN0==7 || UART_SOFT_RX_PIN0==8 || UART_SOFT_RX_PIN0==9)) || (defined(UART_SOFT_RX_PORT1) && defined(UART_SOFT_RX_PIN1) && (UART_SOFT_RX_PIN1==5 || UART_SOFT_RX_PIN1==6 || UART_SOFT_RX_PIN1==7 || UART_SOFT_RX_PIN1==8 || UART_SOFT_RX_PIN1==9)) || (defined(UART_SOFT_RX_PORT2) && defined(UART_SOFT_RX_PIN2) && (UART_SOFT_RX_PIN2==5 || UART_SOFT_RX_PIN2==6 || UART_SOFT_RX_PIN2==7 || UART_SOFT_RX_PIN2==8 || UART_SOFT_RX_PIN2==9)) || (defined(UART_SOFT_RX_PORT3) && defined(UART_SOFT_RX_PIN3) && (UART_SOFT_RX_PIN3==5 || UART_SOFT_RX_PIN3==6 || UART_SOFT_RX_PIN3==7 || UART_SOFT_RX_PIN3==8 || UART_SOFT_RX_PIN3==9))
void exti9_5_isr ( void )
{
2020-02-17 14:07:20 +01:00
exti_reset_request ( EXTI5 | EXTI6 | EXTI7 | EXTI8 | EXTI9 ) ; // clear interrupt flag for pin triggers this ISR (pin state will be checked independently)
2016-10-23 17:42:55 +02:00
uart_soft_receive_activity ( ) ; // check which GPIO changed
}
# endif
# if (defined(UART_SOFT_RX_PORT0) && defined(UART_SOFT_RX_PIN0) && (UART_SOFT_RX_PIN0==10 || UART_SOFT_RX_PIN0==11 || UART_SOFT_RX_PIN0==12 || UART_SOFT_RX_PIN0==13 || UART_SOFT_RX_PIN0==14 || UART_SOFT_RX_PIN0==15)) || (defined(UART_SOFT_RX_PORT1) && defined(UART_SOFT_RX_PIN1) && (UART_SOFT_RX_PIN1==10 || UART_SOFT_RX_PIN1==11 || UART_SOFT_RX_PIN1==12 || UART_SOFT_RX_PIN1==13 || UART_SOFT_RX_PIN1==14 || UART_SOFT_RX_PIN1==15)) || (defined(UART_SOFT_RX_PORT2) && defined(UART_SOFT_RX_PIN2) && (UART_SOFT_RX_PIN2==10 || UART_SOFT_RX_PIN2==11 || UART_SOFT_RX_PIN2==12 || UART_SOFT_RX_PIN2==13 || UART_SOFT_RX_PIN2==14 || UART_SOFT_RX_PIN2==15)) || (defined(UART_SOFT_RX_PORT3) && defined(UART_SOFT_RX_PIN3) && (UART_SOFT_RX_PIN3==10 || UART_SOFT_RX_PIN3==11 || UART_SOFT_RX_PIN3==12 || UART_SOFT_RX_PIN3==13 || UART_SOFT_RX_PIN3==14 || UART_SOFT_RX_PIN3==15))
void exti15_10_isr ( void )
{
2020-02-17 14:07:20 +01:00
exti_reset_request ( EXTI10 | EXTI11 | EXTI12 | EXTI13 | EXTI14 | EXTI15 ) ; // clear interrupt flag for pin triggers this ISR (pin state will be checked independently)
2016-10-23 17:42:55 +02:00
uart_soft_receive_activity ( ) ; // check which GPIO changed
}
# endif