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
2020-06-24 11:49:09 +02:00
# include <string.h> // memory utilisites
2016-10-23 17:42:55 +02:00
/* 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 )
* @ {
*/
2020-06-24 11:49:09 +02:00
# define UART_SOFT_RX0_GPIO PB14 /**< pin for receive signal for UART port 0 */
//#define UART_SOFT_RX1_GPIO PA0 /**< pin for receive signal for UART port 1 */
//#define UART_SOFT_RX2_GPIO PA0 /**< pin for receive signal for UART port 2 */
//#define UART_SOFT_RX3_GPIO PA0 /**< pin for receive signal for UART port 3 */
# define UART_SOFT_TX0_GPIO PB13 /**< pin for transmit signal for UART port 0 */
//#define UART_SOFT_TX1_GPIO PA0 /**< pin for transmit signal for UART port 1 */
//#define UART_SOFT_TX2_GPIO PA0 /**< pin for transmit signal for UART port 2 */
//#define UART_SOFT_TX3_GPIO PA0 /**< pin for transmit signal for UART port 3 */
2016-10-23 17:42:55 +02:00
/** @} */
2020-06-24 11:49:09 +02:00
# if defined(UART_SOFT_RX3_GPIO) || defined(UART_SOFT_TX3_GPIO)
# define UART_NB 4 /*< number of UART ports */
# elif defined(UART_SOFT_RX2_GPIO) || defined(UART_SOFT_TX2_GPIO)
# define UART_NB 3 /*< number of UART ports */
# elif defined(UART_SOFT_RX1_GPIO) || defined(UART_SOFT_TX1_GPIO)
# define UART_NB 2 /*< number of UART ports */
# elif defined(UART_SOFT_RX0_GPIO) || defined(UART_SOFT_TX0_GPIO)
# define UART_NB 1 /*< number of UART ports */
# else
# define UART_NB 0 /*< number of UART ports */
# endif
/** buffer size for receive and transmit buffers (must be a power of 2) */
2016-10-23 17:42:55 +02:00
# define UART_SOFT_BUFFER 128
2020-06-24 11:49:09 +02:00
2016-10-23 17:42:55 +02:00
/** 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 */
2020-06-24 11:49:09 +02:00
uint32_t baudrate ; /**< UART receive baud rate (in timer ticks, not bps) */
2016-10-23 17:42:55 +02:00
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 */
} ;
/** 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 */
2020-06-24 11:49:09 +02:00
uint32_t baudrate ; /**< UART receive baud rate (in timer ticks, not bps) */
2016-10-23 17:42:55 +02:00
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 */
} ;
2020-06-24 11:49:09 +02:00
static struct soft_uart_rx_state uart_soft_rx_states [ UART_NB ] ; /**< states of UART receive ports */
static struct soft_uart_tx_state uart_soft_tx_states [ UART_NB ] ; /**< states of UART transmit ports */
2016-10-23 17:42:55 +02:00
volatile bool uart_soft_received [ 4 ] = { false , false , false , false } ;
/** @defgroup uart_soft_timer timer used to sample UART signals
* @ {
*/
2020-06-24 11:49:09 +02:00
# if defined(UART_SOFT_RX0_GPIO) || defined(UART_SOFT_RX1_GPIO) || defined(UART_SOFT_RX2_GPIO) || defined(UART_SOFT_RX3_GPIO)
2016-10-23 17:42:55 +02:00
# define UART_SOFT_RX_TIMER 3 /**< timer peripheral for receive signals */
# endif
2020-06-24 11:49:09 +02:00
# if defined(UART_SOFT_TX0_GPIO) || defined(UART_SOFT_TX1_GPIO) || defined(UART_SOFT_TX2_GPIO) || defined(UART_SOFT_TX3_GPIO)
2016-10-23 17:42:55 +02:00
# 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
2020-06-24 11:49:09 +02:00
bool uart_soft_setup ( const uint32_t * rx_baudrates , const uint32_t * tx_baudrates )
2016-10-23 17:42:55 +02:00
{
// save UART receive definition
2020-06-24 11:49:09 +02:00
memset ( & uart_soft_rx_states , 0 , sizeof ( uart_soft_rx_states ) ) ; // initialize receiver configuration array
# if defined(UART_SOFT_RX0_GPIO)
uart_soft_rx_states [ 0 ] . port = GPIO_PORT ( UART_SOFT_RX0_GPIO ) ; // save receive port
uart_soft_rx_states [ 0 ] . pin = GPIO_PIN ( UART_SOFT_RX0_GPIO ) ; // save receive pin
uart_soft_rx_states [ 0 ] . rcc = GPIO_RCC ( UART_SOFT_RX0_GPIO ) ; // save receive port peripheral clock
uart_soft_rx_states [ 0 ] . exti = GPIO_EXTI ( UART_SOFT_RX0_GPIO ) ; // save receive external interrupt
uart_soft_rx_states [ 0 ] . irq = GPIO_NVIC_EXTI_IRQ ( UART_SOFT_RX0_GPIO ) ; // save receive interrupt request
# endif
# if defined(UART_SOFT_RX1_GPIO)
uart_soft_rx_states [ 1 ] . port = GPIO_PORT ( UART_SOFT_RX1_GPIO ) ; // save receive port
uart_soft_rx_states [ 1 ] . pin = GPIO_PIN ( UART_SOFT_RX1_GPIO ) ; // save receive pin
uart_soft_rx_states [ 1 ] . rcc = GPIO_RCC ( UART_SOFT_RX1_GPIO ) ; // save receive port peripheral clock
uart_soft_rx_states [ 1 ] . exti = GPIO_EXTI ( UART_SOFT_RX1_GPIO ) ; // save receive external interrupt
uart_soft_rx_states [ 1 ] . irq = GPIO_NVIC_EXTI_IRQ ( UART_SOFT_RX1_GPIO ) ; // save receive interrupt request
# endif
# if defined(UART_SOFT_RX2_GPIO)
uart_soft_rx_states [ 2 ] . port = GPIO_PORT ( UART_SOFT_RX2_GPIO ) ; // save receive port
uart_soft_rx_states [ 2 ] . pin = GPIO_PIN ( UART_SOFT_RX2_GPIO ) ; // save receive pin
uart_soft_rx_states [ 2 ] . rcc = GPIO_RCC ( UART_SOFT_RX2_GPIO ) ; // save receive port peripheral clock
uart_soft_rx_states [ 2 ] . exti = GPIO_EXTI ( UART_SOFT_RX2_GPIO ) ; // save receive external interrupt
uart_soft_rx_states [ 2 ] . irq = GPIO_NVIC_EXTI_IRQ ( UART_SOFT_RX2_GPIO ) ; // save receive interrupt request
# endif
# if defined(UART_SOFT_RX3_GPIO)
uart_soft_rx_states [ 3 ] . port = GPIO_PORT ( UART_SOFT_RX3_GPIO ) ; // save receive port
uart_soft_rx_states [ 3 ] . pin = GPIO_PIN ( UART_SOFT_RX3_GPIO ) ; // save receive pin
uart_soft_rx_states [ 3 ] . rcc = GPIO_RCC ( UART_SOFT_RX3_GPIO ) ; // save receive port peripheral clock
uart_soft_rx_states [ 3 ] . exti = GPIO_EXTI ( UART_SOFT_RX3_GPIO ) ; // save receive external interrupt
uart_soft_rx_states [ 3 ] . irq = GPIO_NVIC_EXTI_IRQ ( UART_SOFT_RX3_GPIO ) ; // save receive interrupt request
2016-10-23 17:42:55 +02:00
# endif
// setup UART receive GPIO
2020-06-24 11:49:09 +02:00
for ( uint8_t rx = 0 ; rx < UART_NB ; rx + + ) {
if ( 0 = = uart_soft_rx_states [ rx ] . port ) { // verify is receive UART is defined
2016-10-23 17:42:55 +02:00
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 ;
}
2020-06-24 11:49:09 +02:00
uart_soft_rx_states [ rx ] . baudrate = rcc_ahb_frequency / rx_baudrates [ rx ] - 1 ; // save baud rate
rcc_periph_clock_enable ( uart_soft_rx_states [ rx ] . rcc ) ; // enable clock for GPIO peripheral
gpio_set ( uart_soft_rx_states [ rx ] . port , uart_soft_rx_states [ rx ] . pin ) ; // pull up to avoid noise when not connected
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
2016-10-23 17:42:55 +02:00
rcc_periph_clock_enable ( RCC_AFIO ) ; // enable alternate function clock for external interrupt
2020-06-24 11:49:09 +02: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
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
2016-10-23 17:42:55 +02:00
}
// save UART transmit definition
2020-06-24 11:49:09 +02:00
memset ( & uart_soft_tx_states , 0 , sizeof ( uart_soft_tx_states ) ) ; // initialize transmitter configuration array
# if defined(UART_SOFT_TX0_GPIO)
uart_soft_tx_states [ 0 ] . port = GPIO_PORT ( UART_SOFT_TX0_GPIO ) ; // save receive port
uart_soft_tx_states [ 0 ] . pin = GPIO_PIN ( UART_SOFT_TX0_GPIO ) ; // save receive pin
uart_soft_tx_states [ 0 ] . rcc = GPIO_RCC ( UART_SOFT_TX0_GPIO ) ; // save receive port peripheral clock
# endif
# if defined(UART_SOFT_TX1_GPIO)
uart_soft_tx_states [ 1 ] . port = GPIO_PORT ( UART_SOFT_TX1_GPIO ) ; // save receive port
uart_soft_tx_states [ 1 ] . pin = GPIO_PIN ( UART_SOFT_TX1_GPIO ) ; // save receive pin
uart_soft_tx_states [ 1 ] . rcc = GPIO_RCC ( UART_SOFT_TX1_GPIO ) ; // save receive port peripheral clock
# endif
# if defined(UART_SOFT_TX2_GPIO)
uart_soft_tx_states [ 2 ] . port = GPIO_PORT ( UART_SOFT_TX2_GPIO ) ; // save receive port
uart_soft_tx_states [ 2 ] . pin = GPIO_PIN ( UART_SOFT_TX2_GPIO ) ; // save receive pin
uart_soft_tx_states [ 2 ] . rcc = GPIO_RCC ( UART_SOFT_TX2_GPIO ) ; // save receive port peripheral clock
# endif
# if defined(UART_SOFT_TX3_GPIO)
uart_soft_tx_states [ 3 ] . port = GPIO_PORT ( UART_SOFT_TX3_GPIO ) ; // save receive port
uart_soft_tx_states [ 3 ] . pin = GPIO_PIN ( UART_SOFT_TX3_GPIO ) ; // save receive pin
uart_soft_tx_states [ 3 ] . rcc = GPIO_RCC ( UART_SOFT_TX3_GPIO ) ; // save receive port peripheral clock
2016-10-23 17:42:55 +02:00
# endif
2019-03-26 19:27:40 +01:00
2016-10-23 17:42:55 +02:00
// setup UART transmit GPIO
2020-06-24 11:49:09 +02:00
for ( uint8_t tx = 0 ; tx < UART_NB ; tx + + ) {
if ( 0 = = uart_soft_tx_states [ tx ] . port ) { // verify is transmit UART is defined
2016-10-23 17:42:55 +02:00
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 ;
}
2020-06-24 11:49:09 +02:00
uart_soft_tx_states [ tx ] . baudrate = rcc_ahb_frequency / tx_baudrates [ tx ] - 1 ; // 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
2016-10-23 17:42:55 +02:00
}
2020-06-24 11:49:09 +02:00
rcc_periph_clock_enable ( GPIO_RCC ( PB12 ) ) ;
gpio_set_mode ( GPIO_PORT ( PB12 ) , GPIO_MODE_OUTPUT_2_MHZ , GPIO_CNF_OUTPUT_PUSHPULL , GPIO_PIN ( PB12 ) ) ;
2016-10-23 17:42:55 +02:00
// 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-06-24 11:49:09 +02:00
if ( uart > = UART_NB | | 0 = = uart_soft_rx_states [ uart ] . port ) { // ensure receive UART port is defined
return 0 ;
2016-10-23 17:42:55 +02:00
}
2020-06-24 11:49:09 +02:00
if ( 0 = = uart_soft_rx_states [ uart ] . buffer_used ) { // there is no data in the buffer
return 0 ;
2016-10-23 17:42:55 +02:00
}
2020-06-24 11:49:09 +02:00
nvic_disable_irq ( uart_soft_rx_states [ uart ] . irq ) ; // disable interrupt to protect the state of the buffer (interrupt can still get pending)
uint8_t to_return = uart_soft_rx_states [ uart ] . buffer [ uart_soft_rx_states [ uart ] . buffer_i ] ; // get the next available character
uart_soft_rx_states [ uart ] . buffer_i = ( uart_soft_rx_states [ uart ] . buffer_i + 1 ) & ( LENGTH ( uart_soft_rx_states [ uart ] . buffer ) - 1 ) ; // update used buffer
uart_soft_rx_states [ uart ] . buffer_used - - ; // update used buffer
nvic_enable_irq ( uart_soft_rx_states [ uart ] . irq ) ; // re-enable interrupt since critical part is finished
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
return to_return ;
}
2020-06-24 11:49:09 +02:00
/** timer interrupt service routine to generate parse receive signal */
2016-10-23 17:42:55 +02:00
void TIM_ISR ( UART_SOFT_RX_TIMER ) ( void )
{
2020-06-24 11:49:09 +02:00
for ( uint8_t rx = 0 ; rx < UART_NB ; rx + + ) {
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 ( 0 = = uart_soft_rx_states [ rx ] . port ) { // verify if RX exists
2016-10-23 17:42:55 +02:00
continue ; // skip if receive port is not defined it
}
2020-06-24 11:49:09 +02:00
gpio_toggle ( GPIO_PORT ( PB12 ) , GPIO_PIN ( PB12 ) ) ;
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 ) ) + uart_soft_rx_states [ rx ] . baudrate ) ; // set timer to next bit
uart_soft_rx_states [ rx ] . bit + + ; // wait for next bit
2016-10-23 17:42:55 +02:00
} else { // last bit received
2020-06-24 11:49:09 +02: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 ) - 1 ) ; // drop oldest byte
uart_soft_rx_states [ rx ] . buffer_used - - ; // update buffer usage
2016-10-23 17:42:55 +02:00
}
2020-06-24 11:49:09 +02: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 ) - 1 ) ] = uart_soft_rx_states [ rx ] . byte ; // put byte in buffer
uart_soft_rx_states [ rx ] . buffer_used + + ; // update used buffer
uart_soft_received [ rx ] = true ; // notify user data is available
timer_disable_irq ( TIM ( UART_SOFT_RX_TIMER ) , timer_interrupt [ rx ] ) ; // stop interrupting
uart_soft_rx_states [ rx ] . bit = 0 ; // next bit should be first bit of next byte
2016-10-23 17:42:55 +02:00
}
}
}
}
# endif
# if defined(UART_SOFT_TX_TIMER)
void uart_soft_flush ( uint8_t uart )
{
2020-06-24 11:49:09 +02:00
if ( uart > = UART_NB | | 0 = = uart_soft_tx_states [ uart ] . port ) { // ensure transmit UART port is defined
2016-10-23 17:42:55 +02:00
return ; // return
}
2020-06-24 11:49:09 +02:00
while ( uart_soft_tx_states [ uart ] . buffer_used ) { // idle until buffer is empty
2016-10-23 17:42:55 +02:00
__WFI ( ) ; // sleep until interrupt
}
2020-06-24 11:49:09 +02:00
while ( uart_soft_tx_states [ uart ] . transmit ) { // idle until transmission is complete
2016-10-23 17:42:55 +02:00
__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 ) {
2020-06-24 11:49:09 +02:00
if ( uart > = UART_NB | | 0 = = uart_soft_tx_states [ uart ] . port ) { // ensure transmit UART port is defined
2016-10-23 17:42:55 +02:00
return ; // UART transmit port not defined
}
2020-06-24 11:49:09 +02:00
if ( uart_soft_tx_states [ uart ] . transmit ) { // already transmitting
2016-10-23 17:42:55 +02:00
return ; // transmission is already ongoing
}
2020-06-24 11:49:09 +02:00
if ( ! uart_soft_tx_states [ uart ] . buffer_used ) { // no buffered data to transmit
2016-10-23 17:42:55 +02:00
return ; // nothing to transmit
}
2020-06-24 11:49:09 +02:00
timer_disable_irq ( TIM ( UART_SOFT_TX_TIMER ) , timer_interrupt [ uart ] ) ; // disable interrupt to protect the state of the buffer (interrupt can still get pending)
uart_soft_tx_states [ uart ] . byte = uart_soft_tx_states [ uart ] . buffer [ uart_soft_tx_states [ uart ] . buffer_i ] ; // get byte
uart_soft_tx_states [ uart ] . buffer_i = ( uart_soft_tx_states [ uart ] . buffer_i + 1 ) & ( LENGTH ( uart_soft_tx_states [ uart ] . buffer ) - 1 ) ; // update index
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
timer_set_oc_value ( TIM ( UART_SOFT_TX_TIMER ) , timer_oc [ uart ] , timer_get_counter ( TIM ( UART_SOFT_TX_TIMER ) ) + 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
2020-06-24 11:49:09 +02:00
timer_enable_irq ( TIM ( UART_SOFT_TX_TIMER ) , timer_interrupt [ uart ] ) ; // enable timer IRQ for TX for this UART
2016-10-23 17:42:55 +02:00
}
void uart_soft_putbyte_nonblocking ( uint8_t uart , uint8_t byte )
{
2020-06-24 11:49:09 +02:00
if ( uart > = UART_NB | | 0 = = uart_soft_tx_states [ uart ] . port ) { // 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-06-24 11:49:09 +02:00
while ( uart_soft_tx_states [ uart ] . buffer_used > = LENGTH ( uart_soft_tx_states [ uart ] . buffer ) ) ; // wait until there is place in the buffer
timer_disable_irq ( TIM ( UART_SOFT_TX_TIMER ) , timer_interrupt [ uart ] ) ; // disable interrupt to protect the state of the buffer (interrupt can still get pending)
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 ) - 1 ) ] = byte ; // save byte to be transmitted
uart_soft_tx_states [ uart ] . buffer_used + + ; // update used buffer
timer_enable_irq ( TIM ( UART_SOFT_TX_TIMER ) , timer_interrupt [ uart ] ) ; // re-enable interrupts to resume transmission
2016-10-23 17:42:55 +02:00
uart_soft_transmit ( uart ) ; // start transmission
}
void uart_soft_putbyte_blocking ( uint8_t uart , uint8_t byte )
{
2020-06-24 11:49:09 +02:00
if ( uart > = UART_NB | | 0 = = uart_soft_tx_states [ uart ] . port ) { // ensure transmit UART port is defined
return ; // return
}
2016-10-23 17:42:55 +02:00
uart_soft_putbyte_nonblocking ( uart , byte ) ; // put byte in queue
uart_soft_flush ( uart ) ; // wait for all byte to be transmitted
}
2020-06-24 11:49:09 +02:00
/** timer interrupt service routine to transmit data */
2016-10-23 17:42:55 +02:00
void TIM_ISR ( UART_SOFT_TX_TIMER ) ( void )
{
2020-06-24 11:49:09 +02:00
for ( uint8_t tx = 0 ; tx < UART_NB ; tx + + ) {
2020-02-17 14:07:20 +01:00
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
2020-06-24 11:49:09 +02:00
if ( 0 = = uart_soft_tx_states [ tx ] . port ) { // verify if transmit is defined
2016-10-23 17:42:55 +02:00
continue ; // skip if transmit port is not defined it
}
2020-06-24 11:49:09 +02: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
gpio_set ( uart_soft_tx_states [ tx ] . port , uart_soft_tx_states [ tx ] . pin ) ; // set output to high
2016-10-23 17:42:55 +02:00
} else { // bit to transmit is a 0
2020-06-24 11:49:09 +02:00
gpio_clear ( uart_soft_tx_states [ tx ] . port , uart_soft_tx_states [ tx ] . pin ) ; // set output to low
2016-10-23 17:42:55 +02:00
}
2020-06-24 11:49:09 +02:00
timer_set_oc_value ( TIM ( UART_SOFT_TX_TIMER ) , timer_oc [ tx ] , timer_get_counter ( TIM ( UART_SOFT_TX_TIMER ) ) + uart_soft_tx_states [ tx ] . baudrate ) ; // wait for the next frame bit
uart_soft_tx_states [ tx ] . bit + + ; // go to next bit
} else if ( 8 = = uart_soft_tx_states [ tx ] . bit ) { // transmit stop bit
gpio_set ( uart_soft_tx_states [ tx ] . port , uart_soft_tx_states [ tx ] . pin ) ; // go idle high
timer_set_oc_value ( TIM ( UART_SOFT_TX_TIMER ) , timer_oc [ tx ] , timer_get_counter ( TIM ( UART_SOFT_TX_TIMER ) ) + uart_soft_tx_states [ tx ] . baudrate ) ; // wait for 1 stop bit
uart_soft_tx_states [ tx ] . bit + + ; // go to next bit
2016-10-23 17:42:55 +02:00
} else { // UART frame is complete
timer_disable_irq ( TIM ( UART_SOFT_TX_TIMER ) , timer_interrupt [ tx ] ) ; // enable timer IRQ for TX for this UART
2020-06-24 11:49:09 +02:00
uart_soft_tx_states [ tx ] . transmit = false ; // transmission finished
2016-10-23 17:42:55 +02:00
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-06-24 11:49:09 +02:00
for ( uint8_t rx = 0 ; rx < UART_NB ; rx + + ) {
if ( 0 = = uart_soft_rx_states [ rx ] . port ) { // verify if receive port is not configured
2016-10-23 17:42:55 +02:00
continue ; // skip if receive port is not defined it
}
2020-06-24 11:49:09 +02:00
const uint16_t state_new = gpio_get ( uart_soft_rx_states [ rx ] . port , uart_soft_rx_states [ rx ] . pin ) ; // get new state
if ( uart_soft_rx_states [ rx ] . state ! = state_new ) { // only do something if state changed
uart_soft_rx_states [ rx ] . state = state_new ; // save new state
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 ) ) + ( uart_soft_rx_states [ rx ] . baudrate ) * 3 / 2 ) ; // 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
2020-06-24 11:49:09 +02:00
uart_soft_rx_states [ rx ] . byte = 0 ; // reset byte value
uart_soft_rx_states [ rx ] . bit = 1 ; // wait for first bit
2016-10-23 17:42:55 +02:00
}
} else { // data bit detected
2020-06-24 11:49:09 +02:00
timer_set_oc_value ( TIM ( UART_SOFT_RX_TIMER ) , timer_oc [ rx ] , timer_get_counter ( TIM ( UART_SOFT_RX_TIMER ) ) + ( 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 */
2020-06-24 11:49:09 +02:00
# if (defined(UART_SOFT_RX0_GPIO) && NVIC_EXTI0_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX0_GPIO)) || (defined(UART_SOFT_RX1_GPIO) && NVIC_EXTI0_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX1_GPIO)) || (defined(UART_SOFT_RX2_GPIO) && NVIC_EXTI0_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX2_GPIO)) || (defined(UART_SOFT_RX3_GPIO) && NVIC_EXTI0_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX3_GPIO))
2016-10-23 17:42:55 +02:00
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
2020-06-24 11:49:09 +02:00
# if (defined(UART_SOFT_RX0_GPIO) && NVIC_EXTI1_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX0_GPIO)) || (defined(UART_SOFT_RX1_GPIO) && NVIC_EXTI1_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX1_GPIO)) || (defined(UART_SOFT_RX2_GPIO) && NVIC_EXTI1_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX2_GPIO)) || (defined(UART_SOFT_RX3_GPIO) && NVIC_EXTI1_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX3_GPIO))
2016-10-23 17:42:55 +02:00
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
2020-06-24 11:49:09 +02:00
# if (defined(UART_SOFT_RX0_GPIO) && NVIC_EXTI2_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX0_GPIO)) || (defined(UART_SOFT_RX1_GPIO) && NVIC_EXTI2_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX1_GPIO)) || (defined(UART_SOFT_RX2_GPIO) && NVIC_EXTI2_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX2_GPIO)) || (defined(UART_SOFT_RX3_GPIO) && NVIC_EXTI2_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX3_GPIO))
2016-10-23 17:42:55 +02:00
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
2020-06-24 11:49:09 +02:00
# if (defined(UART_SOFT_RX0_GPIO) && NVIC_EXTI3_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX0_GPIO)) || (defined(UART_SOFT_RX1_GPIO) && NVIC_EXTI3_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX1_GPIO)) || (defined(UART_SOFT_RX2_GPIO) && NVIC_EXTI3_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX2_GPIO)) || (defined(UART_SOFT_RX3_GPIO) && NVIC_EXTI3_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX3_GPIO))
2016-10-23 17:42:55 +02:00
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
2020-06-24 11:49:09 +02:00
# if (defined(UART_SOFT_RX0_GPIO) && NVIC_EXTI4_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX0_GPIO)) || (defined(UART_SOFT_RX1_GPIO) && NVIC_EXTI4_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX1_GPIO)) || (defined(UART_SOFT_RX2_GPIO) && NVIC_EXTI4_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX2_GPIO)) || (defined(UART_SOFT_RX3_GPIO) && NVIC_EXTI4_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX3_GPIO))
2016-10-23 17:42:55 +02:00
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
2020-06-24 11:49:09 +02:00
# if (defined(UART_SOFT_RX0_GPIO) && NVIC_EXTI9_5_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX0_GPIO)) || (defined(UART_SOFT_RX1_GPIO) && NVIC_EXTI9_5_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX1_GPIO)) || (defined(UART_SOFT_RX2_GPIO) && NVIC_EXTI9_5_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX2_GPIO)) || (defined(UART_SOFT_RX3_GPIO) && NVIC_EXTI9_5_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX3_GPIO))
2016-10-23 17:42:55 +02:00
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
2020-06-24 11:49:09 +02:00
# if (defined(UART_SOFT_RX0_GPIO) && NVIC_EXTI15_10_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX0_GPIO)) || (defined(UART_SOFT_RX1_GPIO) && NVIC_EXTI15_10_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX1_GPIO)) || (defined(UART_SOFT_RX2_GPIO) && NVIC_EXTI15_10_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX2_GPIO)) || (defined(UART_SOFT_RX3_GPIO) && NVIC_EXTI15_10_IRQ == GPIO_NVIC_EXTI_IRQ(UART_SOFT_RX3_GPIO))
2016-10-23 17:42:55 +02:00
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