text: // Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
  //
  // Licensed under the Apache License, Version 2.0 (the "License");
  // you may not use this file except in compliance with the License.
  // You may obtain a copy of the License at
  
  //     http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing, software
  // distributed under the License is distributed on an "AS IS" BASIS,
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  // See the License for the specific language governing permissions and
  // limitations under the License.
  
  #include "esp32-hal-uart.h"
  #include "esp32-hal.h"
  #include "freertos/FreeRTOS.h"
  #include "freertos/task.h"
  #include "freertos/queue.h"
  #include "freertos/semphr.h"
  #include "rom/ets_sys.h"
  #include "esp_attr.h"
  #include "esp_intr.h"
  #include "rom/uart.h"
  #include "soc/uart_reg.h"
  #include "soc/uart_struct.h"
  #include "soc/io_mux_reg.h"
  #include "soc/gpio_sig_map.h"
  #include "soc/dport_reg.h"
  #include "soc/rtc.h"
  #include "esp_intr_alloc.h"
  
  //begin mod   ****************************************************
  //            USE_SLIP_FOR_BREAK_DETECT causes ISR to use SLIP encoding for bytes
  //            sent to queue when a uart's break detect interrupt is enabled.
  //            A detected break is encoded as SLIP_END.
  #define USE_SLIP_FOR_BREAK_DETECT 1
  
  //Special Byte Definitions for SLIP Encoding
  #define SLIP_END      0xC0
  #define SLIP_ESC      0xDB
  #define SLIP_ESC_ESC  0xDC
  #define SLIP_ESC_END  0xDD
  //end mod   ****************************************************
  
  #define UART_REG_BASE(u)    ((u==0)?DR_REG_UART_BASE:(      (u==1)?DR_REG_UART1_BASE:(    (u==2)?DR_REG_UART2_BASE:0)))
  #define UART_RXD_IDX(u)     ((u==0)?U0RXD_IN_IDX:(          (u==1)?U1RXD_IN_IDX:(         (u==2)?U2RXD_IN_IDX:0)))
  #define UART_TXD_IDX(u)     ((u==0)?U0TXD_OUT_IDX:(         (u==1)?U1TXD_OUT_IDX:(        (u==2)?U2TXD_OUT_IDX:0)))
  #define UART_INTR_SOURCE(u) ((u==0)?ETS_UART0_INTR_SOURCE:( (u==1)?ETS_UART1_INTR_SOURCE:((u==2)?ETS_UART2_INTR_SOURCE:0)))
  
  static int s_uart_debug_nr = 0;
  
  struct uart_struct_t {
      uart_dev_t * dev;
  #if !CONFIG_DISABLE_HAL_LOCKS
      xSemaphoreHandle lock;
  #endif
      uint8_t num;
      xQueueHandle queue;
      intr_handle_t intr_handle;
  };
  
  #if CONFIG_DISABLE_HAL_LOCKS
  #define UART_MUTEX_LOCK()
  #define UART_MUTEX_UNLOCK()
  
  static uart_t _uart_bus_array[3] = {
      {(volatile uart_dev_t *)(DR_REG_UART_BASE), 0, NULL, NULL},
      {(volatile uart_dev_t *)(DR_REG_UART1_BASE), 1, NULL, NULL},
      {(volatile uart_dev_t *)(DR_REG_UART2_BASE), 2, NULL, NULL}
  };
  #else
  #define UART_MUTEX_LOCK()    do {} while (xSemaphoreTake(uart->lock, portMAX_DELAY) != pdPASS)
  #define UART_MUTEX_UNLOCK()  xSemaphoreGive(uart->lock)
  
  static uart_t _uart_bus_array[3] = {
      {(volatile uart_dev_t *)(DR_REG_UART_BASE), NULL, 0, NULL, NULL},
      {(volatile uart_dev_t *)(DR_REG_UART1_BASE), NULL, 1, NULL, NULL},
      {(volatile uart_dev_t *)(DR_REG_UART2_BASE), NULL, 2, NULL, NULL}
  };
  #endif
  
  static void uart_on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb);
  
  static void IRAM_ATTR _uart_isr(void *arg)
  {
      uint8_t i, c;
      BaseType_t xHigherPriorityTaskWoken;
      uart_t* uart;
  
      for(i=0;i<3;i++){
          uart = &_uart_bus_array[i];
          if(uart->intr_handle == NULL){
              continue;
          }
          uart->dev->int_clr.rxfifo_full = 1;
          uart->dev->int_clr.frm_err = 1;
          uart->dev->int_clr.rxfifo_tout = 1;
          
  //begin mod ****************************************************        
  #if USE_SLIP_FOR_BREAK_DETECT
  
  		// if break detect interrupt is enabled, use SLIP encoding 
          if ( uart->dev->int_ena.brk_det ) {
          	while(uart->dev->status.rxfifo_cnt) {
  				c = uart->dev->fifo.rw_byte;
  		
  				// encode incoming byte using SLIP
  				// SLIP is simple and uses a special byte SLIP_END to mark the end
  				// of a packet.  If the special byte appears as data, it is escaped
  				// as follows:
  				// if byte == SLIP_END replace with SLIP_ESC followed by SLIP_ESC_END
  				if ( c == SLIP_END ) {
  					c = SLIP_ESC;
  					if(uart->queue != NULL && !xQueueIsQueueFullFromISR(uart->queue)) {
  						xQueueSendFromISR(uart->queue, &c, &xHigherPriorityTaskWoken);
  					}
  					c = SLIP_ESC_END;
  					if(uart->queue != NULL && !xQueueIsQueueFullFromISR(uart->queue)) {
  						xQueueSendFromISR(uart->queue, &c, &xHigherPriorityTaskWoken);
  					}
  				// if byte == SLIP_ESC replace with SLIP_ESC followed by SLIP_ESC_ESC
  				} else if ( c == SLIP_ESC ) {
  					c = SLIP_ESC;
  					if(uart->queue != NULL && !xQueueIsQueueFullFromISR(uart->queue)) {
  						xQueueSendFromISR(uart->queue, &c, &xHigherPriorityTaskWoken);
  					}
  					c = SLIP_ESC_ESC;
  					if(uart->queue != NULL && !xQueueIsQueueFullFromISR(uart->queue)) {
  						xQueueSendFromISR(uart->queue, &c, &xHigherPriorityTaskWoken);
  					}
  				//otherwise just queue the byte
  				} else {
  					if(uart->queue != NULL && !xQueueIsQueueFullFromISR(uart->queue)) {
  						xQueueSendFromISR(uart->queue, &c, &xHigherPriorityTaskWoken);
  					}
  				}
  			}
          	// send SLIP END when break is detected to mark end of packet
  			if (uart->dev->int_st.brk_det) {
  				c = SLIP_END;
  				if(uart->queue != NULL && !xQueueIsQueueFullFromISR(uart->queue)) {
  					xQueueSendFromISR(uart->queue, &c, &xHigherPriorityTaskWoken);
  					uart->dev->int_clr.brk_det = 1;
  				}
  				
  				//uart->dev->int_clr.brk_det = 1;
  			}
          } else {
          	// else break detect not enabled do it like not modified
  #endif
  //end mod ****************************************************       
          
          
          // unmodified sending to queue
          while(uart->dev->status.rxfifo_cnt || (uart->dev->mem_rx_status.wr_addr != uart->dev->mem_rx_status.rd_addr)) {
              c = uart->dev->fifo.rw_byte;
              if(uart->queue != NULL && !xQueueIsQueueFullFromISR(uart->queue)) {
                  xQueueSendFromISR(uart->queue, &c, &xHigherPriorityTaskWoken);
              }
          }
  //begin mod ****************************************************  
  #if USE_SLIP_FOR_BREAK_DETECT
  		}
  #endif
  //end mod ****************************************************  
      }
      
  
      if (xHigherPriorityTaskWoken) {
          portYIELD_FROM_ISR();
      }
  }
  
  void uartEnableInterrupt(uart_t* uart)
  {
      UART_MUTEX_LOCK();
      uart->dev->conf1.rxfifo_full_thrhd = 112;
      uart->dev->conf1.rx_tout_thrhd = 2;
      uart->dev->conf1.rx_tout_en = 1;
      uart->dev->int_ena.rxfifo_full = 1;
      uart->dev->int_ena.frm_err = 1;
      uart->dev->int_ena.rxfifo_tout = 1;
      uart->dev->int_clr.val = 0xffffffff;
  
      esp_intr_alloc(UART_INTR_SOURCE(uart->num), (int)ESP_INTR_FLAG_IRAM, _uart_isr, NULL, &uart->intr_handle);
      UART_MUTEX_UNLOCK();
  }
  
  void uartDisableInterrupt(uart_t* uart)
  {
      UART_MUTEX_LOCK();
      uart->dev->conf1.val = 0;
      uart->dev->int_ena.val = 0;
      uart->dev->int_clr.val = 0xffffffff;
  
      esp_intr_free(uart->intr_handle);
      uart->intr_handle = NULL;
  
      UART_MUTEX_UNLOCK();
  }
  
  void uartDetachRx(uart_t* uart)
  {
      if(uart == NULL) {
          return;
      }
      pinMatrixInDetach(UART_RXD_IDX(uart->num), false, false);
      uartDisableInterrupt(uart);
  }
  
  void uartDetachTx(uart_t* uart)
  {
      if(uart == NULL) {
          return;
      }
      pinMatrixOutDetach(UART_TXD_IDX(uart->num), false, false);
  }
  
  void uartAttachRx(uart_t* uart, uint8_t rxPin, bool inverted)
  {
      if(uart == NULL || rxPin > 39) {
          return;
      }
      pinMode(rxPin, INPUT);
      pinMatrixInAttach(rxPin, UART_RXD_IDX(uart->num), inverted);
      uartEnableInterrupt(uart);
  }
  
  void uartAttachTx(uart_t* uart, uint8_t txPin, bool inverted)
  {
      if(uart == NULL || txPin > 39) {
          return;
      }
      pinMode(txPin, OUTPUT);
      pinMatrixOutAttach(txPin, UART_TXD_IDX(uart->num), inverted, false);
  }
  
  uart_t* uartBegin(uint8_t uart_nr, uint32_t baudrate, uint32_t config, int8_t rxPin, int8_t txPin, uint16_t queueLen, bool inverted)
  {
      if(uart_nr > 2) {
          return NULL;
      }
  
      if(rxPin == -1 && txPin == -1) {
          return NULL;
      }
  
      uart_t* uart = &_uart_bus_array[uart_nr];
  
  #if !CONFIG_DISABLE_HAL_LOCKS
      if(uart->lock == NULL) {
          uart->lock = xSemaphoreCreateMutex();
          if(uart->lock == NULL) {
              return NULL;
          }
      }
  #endif
  
      if(queueLen && uart->queue == NULL) {
          uart->queue = xQueueCreate(queueLen, sizeof(uint8_t)); //initialize the queue
          if(uart->queue == NULL) {
              return NULL;
          }
      }
      if(uart_nr == 1){
          DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_UART1_CLK_EN);
          DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_UART1_RST);
      } else if(uart_nr == 2){
          DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_UART2_CLK_EN);
          DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_UART2_RST);
      } else {
          DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_UART_CLK_EN);
          DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_UART_RST);
      }
      uartFlush(uart);
      uartSetBaudRate(uart, baudrate);
      UART_MUTEX_LOCK();
      uart->dev->conf0.val = config;
      #define TWO_STOP_BITS_CONF 0x3
      #define ONE_STOP_BITS_CONF 0x1
  
      if ( uart->dev->conf0.stop_bit_num == TWO_STOP_BITS_CONF) {
          uart->dev->conf0.stop_bit_num = ONE_STOP_BITS_CONF;
          uart->dev->rs485_conf.dl1_en = 1;
      }
  
      // tx_idle_num : idle interval after tx FIFO is empty(unit: the time it takes to send one bit under current baudrate)
      // Setting it to 0 prevents line idle time/delays when sending messages with small intervals
      uart->dev->idle_conf.tx_idle_num = 0;  //
  
      UART_MUTEX_UNLOCK();
  
      if(rxPin != -1) {
          uartAttachRx(uart, rxPin, inverted);
      }
  
      if(txPin != -1) {
          uartAttachTx(uart, txPin, inverted);
      }
      addApbChangeCallback(uart, uart_on_apb_change);
      return uart;
  }
  
  void uartEnd(uart_t* uart)
  {
      if(uart == NULL) {
          return;
      }
      removeApbChangeCallback(uart, uart_on_apb_change);
  
      UART_MUTEX_LOCK();
      if(uart->queue != NULL) {
          vQueueDelete(uart->queue);
          uart->queue = NULL;
      }
  
      uart->dev->conf0.val = 0;
  
      UART_MUTEX_UNLOCK();
  
      uartDetachRx(uart);
      uartDetachTx(uart);
  }
  
  size_t uartResizeRxBuffer(uart_t * uart, size_t new_size) {
      if(uart == NULL) {
          return 0;
      }
  
      UART_MUTEX_LOCK();
      if(uart->queue != NULL) {
          vQueueDelete(uart->queue);
          uart->queue = xQueueCreate(new_size, sizeof(uint8_t));
          if(uart->queue == NULL) {
              return NULL;
          }
      }
      UART_MUTEX_UNLOCK();
  
      return new_size;
  }
  
  uint32_t uartAvailable(uart_t* uart)
  {
      if(uart == NULL || uart->queue == NULL) {
          return 0;
      }
      return (uxQueueMessagesWaiting(uart->queue) + uart->dev->status.rxfifo_cnt) ;
  }
  
  uint32_t uartAvailableForWrite(uart_t* uart)
  {
      if(uart == NULL) {
          return 0;
      }
      return 0x7f - uart->dev->status.txfifo_cnt;
  }
  
  void uartRxFifoToQueue(uart_t* uart)
  {
  	uint8_t c;
      UART_MUTEX_LOCK();
      while(uart->dev->status.rxfifo_cnt || (uart->dev->mem_rx_status.wr_addr != uart->dev->mem_rx_status.rd_addr)) {
          c = uart->dev->fifo.rw_byte;
          xQueueSend(uart->queue, &c, 0);
      }
      UART_MUTEX_UNLOCK();
  }
  
  uint8_t uartRead(uart_t* uart)
  {
      if(uart == NULL || uart->queue == NULL) {
          return 0;
      }
      uint8_t c;
      if ((uxQueueMessagesWaiting(uart->queue) == 0) && (uart->dev->status.rxfifo_cnt > 0))
      {
      	uartRxFifoToQueue(uart);
      }
      if(xQueueReceive(uart->queue, &c, 0)) {
          return c;
      }
      return 0;
  }
  
  uint8_t uartPeek(uart_t* uart)
  {
      if(uart == NULL || uart->queue == NULL) {
          return 0;
      }
      uint8_t c;
      if ((uxQueueMessagesWaiting(uart->queue) == 0) && (uart->dev->status.rxfifo_cnt > 0))
      {
      	uartRxFifoToQueue(uart);
      }
      if(xQueuePeek(uart->queue, &c, 0)) {
          return c;
      }
      return 0;
  }
  
  void uartWrite(uart_t* uart, uint8_t c)
  {
      if(uart == NULL) {
          return;
      }
      UART_MUTEX_LOCK();
      while(uart->dev->status.txfifo_cnt == 0x7F);
      uart->dev->fifo.rw_byte = c;
      UART_MUTEX_UNLOCK();
  }
  
  void uartWriteBuf(uart_t* uart, const uint8_t * data, size_t len)
  {
      if(uart == NULL) {
          return;
      }
      UART_MUTEX_LOCK();
      while(len) {
          while(uart->dev->status.txfifo_cnt == 0x7F);
          uart->dev->fifo.rw_byte = *data++;
          len--;
      }
      UART_MUTEX_UNLOCK();
  }
  
  void uartFlush(uart_t* uart)
  {
      uartFlushTxOnly(uart,false);
  }
  
  void uartFlushTxOnly(uart_t* uart, bool txOnly)
  {
      if(uart == NULL) {
          return;
      }
  
      UART_MUTEX_LOCK();
      while(uart->dev->status.txfifo_cnt || uart->dev->status.st_utx_out);
      
      if( !txOnly ){
          //Due to hardware issue, we can not use fifo_rst to reset uart fifo.
          //See description about UART_TXFIFO_RST and UART_RXFIFO_RST in <<esp32_technical_reference_manual>> v2.6 or later.
  
          // we read the data out and make `fifo_len == 0 && rd_addr == wr_addr`.
          while(uart->dev->status.rxfifo_cnt != 0 || (uart->dev->mem_rx_status.wr_addr != uart->dev->mem_rx_status.rd_addr)) {
              READ_PERI_REG(UART_FIFO_REG(uart->num));
          }
  
          xQueueReset(uart->queue);
      }
      
      UART_MUTEX_UNLOCK();
  }
  
  void uartSetBaudRate(uart_t* uart, uint32_t baud_rate)
  {
      if(uart == NULL) {
          return;
      }
      UART_MUTEX_LOCK();
      uint32_t clk_div = ((getApbFrequency()<<4)/baud_rate);
      uart->dev->clk_div.div_int = clk_div>>4 ;
      uart->dev->clk_div.div_frag = clk_div & 0xf;
      UART_MUTEX_UNLOCK();
  }
  
  static void uart_on_apb_change(void * arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb)
  {
      uart_t* uart = (uart_t*)arg;
      if(ev_type == APB_BEFORE_CHANGE){
          UART_MUTEX_LOCK();
          //disabple interrupt
          uart->dev->int_ena.val = 0;
          uart->dev->int_clr.val = 0xffffffff;
          // read RX fifo
          uint8_t c;
     //     BaseType_t xHigherPriorityTaskWoken;
          while(uart->dev->status.rxfifo_cnt != 0 || (uart->dev->mem_rx_status.wr_addr != uart->dev->mem_rx_status.rd_addr)) {
              c = uart->dev->fifo.rw_byte;
              if(uart->queue != NULL ) {
                  xQueueSend(uart->queue, &c, 1); //&xHigherPriorityTaskWoken);
              }
          }
          UART_MUTEX_UNLOCK();
   
          // wait TX empty
          while(uart->dev->status.txfifo_cnt || uart->dev->status.st_utx_out);
      } else {
          //todo:
          // set baudrate
          UART_MUTEX_LOCK();
          uint32_t clk_div = (uart->dev->clk_div.div_int << 4) | (uart->dev->clk_div.div_frag & 0x0F);
          uint32_t baud_rate = ((old_apb<<4)/clk_div);
          clk_div = ((new_apb<<4)/baud_rate);
          uart->dev->clk_div.div_int = clk_div>>4 ;
          uart->dev->clk_div.div_frag = clk_div & 0xf;
          //enable interrupts
          uart->dev->int_ena.rxfifo_full = 1;
          uart->dev->int_ena.frm_err = 1;
          uart->dev->int_ena.rxfifo_tout = 1;
          uart->dev->int_clr.val = 0xffffffff;
          UART_MUTEX_UNLOCK();
      }
  }
  
  uint32_t uartGetBaudRate(uart_t* uart)
  {
      if(uart == NULL) {
          return 0;
      }
  
      uint32_t clk_div = (uart->dev->clk_div.div_int << 4) | (uart->dev->clk_div.div_frag & 0x0F);
      if(!clk_div) {
          return 0;
      }
  
      return ((getApbFrequency()<<4)/clk_div);
  }
  
  static void IRAM_ATTR uart0_write_char(char c)
  {
      while(((ESP_REG(0x01C+DR_REG_UART_BASE) >> UART_TXFIFO_CNT_S) & 0x7F) == 0x7F);
      ESP_REG(DR_REG_UART_BASE) = c;
  }
  
  static void IRAM_ATTR uart1_write_char(char c)
  {
      while(((ESP_REG(0x01C+DR_REG_UART1_BASE) >> UART_TXFIFO_CNT_S) & 0x7F) == 0x7F);
      ESP_REG(DR_REG_UART1_BASE) = c;
  }
  
  static void IRAM_ATTR uart2_write_char(char c)
  {
      while(((ESP_REG(0x01C+DR_REG_UART2_BASE) >> UART_TXFIFO_CNT_S) & 0x7F) == 0x7F);
      ESP_REG(DR_REG_UART2_BASE) = c;
  }
  
  void uart_install_putc()
  {
      switch(s_uart_debug_nr) {
      case 0:
          ets_install_putc1((void (*)(char)) &uart0_write_char);
          break;
      case 1:
          ets_install_putc1((void (*)(char)) &uart1_write_char);
          break;
      case 2:
          ets_install_putc1((void (*)(char)) &uart2_write_char);
          break;
      default:
          ets_install_putc1(NULL);
          break;
      }
  }
  
  void uartSetDebug(uart_t* uart)
  {
      if(uart == NULL || uart->num > 2) {
          s_uart_debug_nr = -1;
          //ets_install_putc1(NULL);
          //return;
      } else
      if(s_uart_debug_nr == uart->num) {
          return;
      } else
      s_uart_debug_nr = uart->num;
      uart_install_putc();
  }
  
  int uartGetDebug()
  {
      return s_uart_debug_nr;
  }
  
  int log_printf(const char *format, ...)
  {
      if(s_uart_debug_nr < 0){
          return 0;
      }
      static char loc_buf[64];
      char * temp = loc_buf;
      int len;
      va_list arg;
      va_list copy;
      va_start(arg, format);
      va_copy(copy, arg);
      len = vsnprintf(NULL, 0, format, arg);
      va_end(copy);
      if(len >= sizeof(loc_buf)){
          temp = (char*)malloc(len+1);
          if(temp == NULL) {
              return 0;
          }
      }
      vsnprintf(temp, len+1, format, arg);
  #if !CONFIG_DISABLE_HAL_LOCKS
      if(_uart_bus_array[s_uart_debug_nr].lock){
          xSemaphoreTake(_uart_bus_array[s_uart_debug_nr].lock, portMAX_DELAY);
          ets_printf("%s", temp);
          xSemaphoreGive(_uart_bus_array[s_uart_debug_nr].lock);
      } else {
          ets_printf("%s", temp);
      }
  #else
      ets_printf("%s", temp);
  #endif
      va_end(arg);
      if(len >= sizeof(loc_buf)){
          free(temp);
      }
      return len;
  }
  
  /*
   * if enough pulses are detected return the minimum high pulse duration + minimum low pulse duration divided by two. 
   * This equals one bit period. If flag is true the function return inmediately, otherwise it waits for enough pulses.
   */
  unsigned long uartBaudrateDetect(uart_t *uart, bool flg)
  {
      while(uart->dev->rxd_cnt.edge_cnt < 30) { // UART_PULSE_NUM(uart_num)
          if(flg) return 0;
          ets_delay_us(1000);
      }
  
      UART_MUTEX_LOCK();
      unsigned long ret = ((uart->dev->lowpulse.min_cnt + uart->dev->highpulse.min_cnt) >> 1) + 12;
      UART_MUTEX_UNLOCK();
  
      return ret;
  }
  
  /*
   * To start detection of baud rate with the uart the auto_baud.en bit needs to be cleared and set. The bit period is 
   * detected calling uartBadrateDetect(). The raw baudrate is computed using the UART_CLK_FREQ. The raw baudrate is 
   * rounded to the closed real baudrate.
  */
  void uartStartDetectBaudrate(uart_t *uart) {
    if(!uart) return;
  
    uart->dev->auto_baud.glitch_filt = 0x08;
    uart->dev->auto_baud.en = 0;
    uart->dev->auto_baud.en = 1;
  }
  
  unsigned long
  uartDetectBaudrate(uart_t *uart)
  {
      static bool uartStateDetectingBaudrate = false;
  
      if(!uartStateDetectingBaudrate) {
          uart->dev->auto_baud.glitch_filt = 0x08;
          uart->dev->auto_baud.en = 0;
          uart->dev->auto_baud.en = 1;
          uartStateDetectingBaudrate = true;
      }
  
      unsigned long divisor = uartBaudrateDetect(uart, true);
      if (!divisor) {
          return 0;
      }
  
      uart->dev->auto_baud.en = 0;
      uartStateDetectingBaudrate = false; // Initialize for the next round
  
      unsigned long baudrate = getApbFrequency() / divisor;
  
      static const unsigned long default_rates[] = {300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 74880, 115200, 230400, 256000, 460800, 921600, 1843200, 3686400};
  
      size_t i;
      for (i = 1; i < sizeof(default_rates) / sizeof(default_rates[0]) - 1; i++)	// find the nearest real baudrate
      {
          if (baudrate <= default_rates[i])
          {
              if (baudrate - default_rates[i - 1] < default_rates[i] - baudrate) {
                  i--;
              }
              break;
          }
      }
  
      return default_rates[i];
  }
  
  /*
   * Returns the status of the RX state machine, if the value is non-zero the state machine is active.
   */
  bool uartRxActive(uart_t* uart) {
      return uart->dev->status.st_urx_out != 0;
  }
  