mirror of
https://github.com/esp8266/Arduino.git
synced 2025-04-24 08:45:10 +03:00
* Simple i/f to turn on inverted logic on UART0. * Refactor invert from HardwareSerial to uart * Final refactoring of invert bits into config bitmap. * Overload instead of default arg for subclassing. * Prevent unwanted effects if setting invert on other than UART0 - only that has these flags defined and documented.
1004 lines
25 KiB
C++
1004 lines
25 KiB
C++
/*
|
|
uart.cpp - esp8266 UART HAL
|
|
|
|
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
|
|
This file is part of the esp8266 core for Arduino environment.
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
This library 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
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*/
|
|
|
|
|
|
/**
|
|
* UART GPIOs
|
|
*
|
|
* UART0 TX: 1 or 2
|
|
* UART0 RX: 3
|
|
*
|
|
* UART0 SWAP TX: 15
|
|
* UART0 SWAP RX: 13
|
|
*
|
|
*
|
|
* UART1 TX: 7 (NC) or 2
|
|
* UART1 RX: 8 (NC)
|
|
*
|
|
* UART1 SWAP TX: 11 (NC)
|
|
* UART1 SWAP RX: 6 (NC)
|
|
*
|
|
* NC = Not Connected to Module Pads --> No Access
|
|
*
|
|
*/
|
|
#include "Arduino.h"
|
|
#include <pgmspace.h>
|
|
#include "gdb_hooks.h"
|
|
#include "uart.h"
|
|
#include "esp8266_peri.h"
|
|
#include "user_interface.h"
|
|
#include "uart_register.h"
|
|
|
|
#define MODE2WIDTH(mode) (((mode%16)>>2)+5)
|
|
#define MODE2STOP(mode) (((mode)>>5)+1)
|
|
#define MODE2PARITY(mode) (mode%4)
|
|
|
|
/*
|
|
Some general architecture for GDB integration with the UART to enable
|
|
serial debugging.
|
|
|
|
UART1 is transmit only and can never be used by GDB.
|
|
|
|
When gdbstub_has_uart_isr_control() (only true in the case GDB is enabled),
|
|
UART0 needs to be under the control of the GDB stub for enable/disable/irq
|
|
(but speed, parity, etc. still alllowable). Basically, GDB needs to make
|
|
sure that UART0 is never really disabled.
|
|
|
|
GDB sets up UART0 with a fifo and a 2-character timeout during init. This
|
|
is required to ensure that GDBStub can check every character coming in, even
|
|
if it is not read by the user app or if the commands don't hit the FIFO
|
|
interrupt level. It checks every character that comes in, and if GDB isn't
|
|
active just passes them to the user RAM FIFO unless it's a Ctrl-C (0x03).
|
|
|
|
GDBStub doesn't care about the struct uart_*, and allocating it or freeing
|
|
it has no effect (so you can do Serial.end() and free the uart...but as
|
|
mentioned above even if you Serial.end, the actual UART0 HW will still be
|
|
kept running to enable GDB to do commands.
|
|
*/
|
|
|
|
extern "C" {
|
|
|
|
static int s_uart_debug_nr = UART0;
|
|
|
|
struct uart_rx_buffer_
|
|
{
|
|
size_t size;
|
|
size_t rpos;
|
|
size_t wpos;
|
|
uint8_t * buffer;
|
|
};
|
|
|
|
struct uart_
|
|
{
|
|
int uart_nr;
|
|
int baud_rate;
|
|
bool rx_enabled;
|
|
bool tx_enabled;
|
|
bool rx_overrun;
|
|
bool rx_error;
|
|
uint8_t rx_pin;
|
|
uint8_t tx_pin;
|
|
struct uart_rx_buffer_ * rx_buffer;
|
|
};
|
|
|
|
|
|
/*
|
|
In the context of the naming conventions in this file, "_unsafe" means two things:
|
|
1. the input arguments are not checked. It is up to the caller to check argument sanity.
|
|
2. The function body is not interrupt-safe, i.e.: the isr could fire anywhen during the
|
|
body execution, leading to corruption of the data shared between the body and the isr
|
|
(parts of the rx_buffer).
|
|
|
|
The unsafe versions of the functions are private to this TU. There are "safe" versions that
|
|
wrap the unsafe ones with disabling/enabling of the uart interrupt for safe public use.
|
|
*/
|
|
|
|
|
|
|
|
// called by ISR
|
|
inline size_t ICACHE_RAM_ATTR
|
|
uart_rx_fifo_available(const int uart_nr)
|
|
{
|
|
return (USS(uart_nr) >> USRXC) & 0xFF;
|
|
}
|
|
|
|
|
|
/**********************************************************/
|
|
/************ UNSAFE FUNCTIONS ****************************/
|
|
/**********************************************************/
|
|
inline size_t
|
|
uart_rx_buffer_available_unsafe(const struct uart_rx_buffer_ * rx_buffer)
|
|
{
|
|
if(rx_buffer->wpos < rx_buffer->rpos)
|
|
return (rx_buffer->wpos + rx_buffer->size) - rx_buffer->rpos;
|
|
|
|
return rx_buffer->wpos - rx_buffer->rpos;
|
|
}
|
|
|
|
inline size_t
|
|
uart_rx_available_unsafe(uart_t* uart)
|
|
{
|
|
return uart_rx_buffer_available_unsafe(uart->rx_buffer) + uart_rx_fifo_available(uart->uart_nr);
|
|
}
|
|
|
|
//#define UART_DISCARD_NEWEST
|
|
|
|
// Copy all the rx fifo bytes that fit into the rx buffer
|
|
// called by ISR
|
|
inline void ICACHE_RAM_ATTR
|
|
uart_rx_copy_fifo_to_buffer_unsafe(uart_t* uart)
|
|
{
|
|
struct uart_rx_buffer_ *rx_buffer = uart->rx_buffer;
|
|
|
|
while(uart_rx_fifo_available(uart->uart_nr))
|
|
{
|
|
size_t nextPos = (rx_buffer->wpos + 1) % rx_buffer->size;
|
|
if(nextPos == rx_buffer->rpos)
|
|
{
|
|
if (!uart->rx_overrun)
|
|
{
|
|
uart->rx_overrun = true;
|
|
//os_printf_plus(overrun_str);
|
|
}
|
|
|
|
// a choice has to be made here,
|
|
// do we discard newest or oldest data?
|
|
#ifdef UART_DISCARD_NEWEST
|
|
// discard newest data
|
|
// Stop copying if rx buffer is full
|
|
USF(uart->uart_nr);
|
|
break;
|
|
#else
|
|
// discard oldest data
|
|
if (++rx_buffer->rpos == rx_buffer->size)
|
|
rx_buffer->rpos = 0;
|
|
#endif
|
|
}
|
|
uint8_t data = USF(uart->uart_nr);
|
|
rx_buffer->buffer[rx_buffer->wpos] = data;
|
|
rx_buffer->wpos = nextPos;
|
|
}
|
|
}
|
|
|
|
inline int
|
|
uart_peek_char_unsafe(uart_t* uart)
|
|
{
|
|
if (!uart_rx_available_unsafe(uart))
|
|
return -1;
|
|
|
|
//without the following if statement and body, there is a good chance of a fifo overrun
|
|
if (uart_rx_buffer_available_unsafe(uart->rx_buffer) == 0)
|
|
// hw fifo can't be peeked, data need to be copied to sw
|
|
uart_rx_copy_fifo_to_buffer_unsafe(uart);
|
|
|
|
return uart->rx_buffer->buffer[uart->rx_buffer->rpos];
|
|
}
|
|
|
|
// taking data straight from hw fifo: loopback-test BW jumps by 19%
|
|
inline int
|
|
uart_read_char_unsafe(uart_t* uart)
|
|
{
|
|
if (uart_rx_buffer_available_unsafe(uart->rx_buffer))
|
|
{
|
|
// take oldest sw data
|
|
int ret = uart->rx_buffer->buffer[uart->rx_buffer->rpos];
|
|
uart->rx_buffer->rpos = (uart->rx_buffer->rpos + 1) % uart->rx_buffer->size;
|
|
return ret;
|
|
}
|
|
// unavailable
|
|
return -1;
|
|
}
|
|
|
|
uint8_t
|
|
uart_get_bit_length(const int uart_nr)
|
|
{
|
|
// return bit length from uart mode, +1 for the start bit which is always there.
|
|
return MODE2WIDTH(USC0(uart_nr)) + MODE2PARITY(USC0(uart_nr)) + MODE2STOP(USC0(uart_nr)) + 1;
|
|
}
|
|
|
|
size_t
|
|
uart_rx_available(uart_t* uart)
|
|
{
|
|
if(uart == NULL || !uart->rx_enabled)
|
|
return 0;
|
|
|
|
ETS_UART_INTR_DISABLE();
|
|
int uartrxbufferavailable = uart_rx_buffer_available_unsafe(uart->rx_buffer);
|
|
ETS_UART_INTR_ENABLE();
|
|
|
|
return uartrxbufferavailable + uart_rx_fifo_available(uart->uart_nr);
|
|
}
|
|
|
|
int
|
|
uart_peek_char(uart_t* uart)
|
|
{
|
|
if(uart == NULL || !uart->rx_enabled)
|
|
return -1;
|
|
|
|
ETS_UART_INTR_DISABLE(); //access to rx_buffer can be interrupted by the isr (similar to a critical section), so disable interrupts here
|
|
int ret = uart_peek_char_unsafe(uart);
|
|
ETS_UART_INTR_ENABLE();
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
uart_read_char(uart_t* uart)
|
|
{
|
|
uint8_t ret;
|
|
return uart_read(uart, (char*)&ret, 1)? ret: -1;
|
|
}
|
|
|
|
// loopback-test BW jumps by 190%
|
|
size_t
|
|
uart_read(uart_t* uart, char* userbuffer, size_t usersize)
|
|
{
|
|
if(uart == NULL || !uart->rx_enabled)
|
|
return 0;
|
|
|
|
size_t ret = 0;
|
|
ETS_UART_INTR_DISABLE();
|
|
|
|
while (ret < usersize && uart_rx_available_unsafe(uart))
|
|
{
|
|
if (!uart_rx_buffer_available_unsafe(uart->rx_buffer))
|
|
{
|
|
// no more data in sw buffer, take them from hw fifo
|
|
while (ret < usersize && uart_rx_fifo_available(uart->uart_nr))
|
|
userbuffer[ret++] = USF(uart->uart_nr);
|
|
|
|
// no more sw/hw data available
|
|
break;
|
|
}
|
|
|
|
// pour sw buffer to user's buffer
|
|
// get largest linear length from sw buffer
|
|
size_t chunk = uart->rx_buffer->rpos < uart->rx_buffer->wpos?
|
|
uart->rx_buffer->wpos - uart->rx_buffer->rpos:
|
|
uart->rx_buffer->size - uart->rx_buffer->rpos;
|
|
if (ret + chunk > usersize)
|
|
chunk = usersize - ret;
|
|
memcpy(userbuffer + ret, uart->rx_buffer->buffer + uart->rx_buffer->rpos, chunk);
|
|
uart->rx_buffer->rpos = (uart->rx_buffer->rpos + chunk) % uart->rx_buffer->size;
|
|
ret += chunk;
|
|
}
|
|
|
|
ETS_UART_INTR_ENABLE();
|
|
return ret;
|
|
}
|
|
|
|
// When GDB is running, this is called one byte at a time to stuff the user FIFO
|
|
// instead of the uart_isr...uart_rx_copy_fifo_to_buffer_unsafe()
|
|
// Since we've already read the bytes from the FIFO, can't use that
|
|
// function directly and need to implement it bytewise here
|
|
static void ICACHE_RAM_ATTR uart_isr_handle_data(void* arg, uint8_t data)
|
|
{
|
|
uart_t* uart = (uart_t*)arg;
|
|
if(uart == NULL || !uart->rx_enabled) {
|
|
return;
|
|
}
|
|
|
|
// Copy all the rx fifo bytes that fit into the rx buffer
|
|
// called by ISR
|
|
struct uart_rx_buffer_ *rx_buffer = uart->rx_buffer;
|
|
|
|
size_t nextPos = (rx_buffer->wpos + 1) % rx_buffer->size;
|
|
if(nextPos == rx_buffer->rpos)
|
|
{
|
|
uart->rx_overrun = true;
|
|
//os_printf_plus(overrun_str);
|
|
|
|
// a choice has to be made here,
|
|
// do we discard newest or oldest data?
|
|
#ifdef UART_DISCARD_NEWEST
|
|
// discard newest data
|
|
// Stop copying if rx buffer is full
|
|
return;
|
|
#else
|
|
// discard oldest data
|
|
if (++rx_buffer->rpos == rx_buffer->size)
|
|
rx_buffer->rpos = 0;
|
|
#endif
|
|
}
|
|
rx_buffer->buffer[rx_buffer->wpos] = data;
|
|
rx_buffer->wpos = nextPos;
|
|
|
|
// Check the UART flags and note hardware overflow/etc.
|
|
uint32_t usis = USIS(uart->uart_nr);
|
|
|
|
if(usis & (1 << UIOF))
|
|
uart->rx_overrun = true;
|
|
|
|
if (usis & ((1 << UIFR) | (1 << UIPE) | (1 << UITO)))
|
|
uart->rx_error = true;
|
|
|
|
USIC(uart->uart_nr) = usis;
|
|
}
|
|
|
|
size_t
|
|
uart_resize_rx_buffer(uart_t* uart, size_t new_size)
|
|
{
|
|
if(uart == NULL || !uart->rx_enabled)
|
|
return 0;
|
|
|
|
if(uart->rx_buffer->size == new_size)
|
|
return uart->rx_buffer->size;
|
|
|
|
uint8_t * new_buf = (uint8_t*)malloc(new_size);
|
|
if(!new_buf)
|
|
return uart->rx_buffer->size;
|
|
|
|
size_t new_wpos = 0;
|
|
ETS_UART_INTR_DISABLE();
|
|
while(uart_rx_available_unsafe(uart) && new_wpos < new_size)
|
|
new_buf[new_wpos++] = uart_read_char_unsafe(uart); //if uart_rx_available_unsafe() returns non-0, uart_read_char_unsafe() can't return -1
|
|
if (new_wpos == new_size)
|
|
new_wpos = 0;
|
|
|
|
uint8_t * old_buf = uart->rx_buffer->buffer;
|
|
uart->rx_buffer->rpos = 0;
|
|
uart->rx_buffer->wpos = new_wpos;
|
|
uart->rx_buffer->size = new_size;
|
|
uart->rx_buffer->buffer = new_buf;
|
|
ETS_UART_INTR_ENABLE();
|
|
free(old_buf);
|
|
return uart->rx_buffer->size;
|
|
}
|
|
|
|
size_t
|
|
uart_get_rx_buffer_size(uart_t* uart)
|
|
{
|
|
return uart && uart->rx_enabled? uart->rx_buffer->size: 0;
|
|
}
|
|
|
|
// The default ISR handler called when GDB is not enabled
|
|
void ICACHE_RAM_ATTR
|
|
uart_isr(void * arg)
|
|
{
|
|
uart_t* uart = (uart_t*)arg;
|
|
uint32_t usis = USIS(uart->uart_nr);
|
|
|
|
if(uart == NULL || !uart->rx_enabled)
|
|
{
|
|
USIC(uart->uart_nr) = usis;
|
|
ETS_UART_INTR_DISABLE();
|
|
return;
|
|
}
|
|
|
|
if(usis & (1 << UIFF))
|
|
uart_rx_copy_fifo_to_buffer_unsafe(uart);
|
|
|
|
if(usis & (1 << UIOF))
|
|
{
|
|
uart->rx_overrun = true;
|
|
//os_printf_plus(overrun_str);
|
|
}
|
|
|
|
if (usis & ((1 << UIFR) | (1 << UIPE) | (1 << UITO)))
|
|
uart->rx_error = true;
|
|
|
|
USIC(uart->uart_nr) = usis;
|
|
}
|
|
|
|
static void
|
|
uart_start_isr(uart_t* uart)
|
|
{
|
|
if(uart == NULL || !uart->rx_enabled)
|
|
return;
|
|
|
|
if(gdbstub_has_uart_isr_control()) {
|
|
gdbstub_set_uart_isr_callback(uart_isr_handle_data, (void *)uart);
|
|
return;
|
|
}
|
|
|
|
// UCFFT value is when the RX fifo full interrupt triggers. A value of 1
|
|
// triggers the IRS very often. A value of 127 would not leave much time
|
|
// for ISR to clear fifo before the next byte is dropped. So pick a value
|
|
// in the middle.
|
|
// update: loopback test @ 3Mbauds/8n1 (=2343Kibits/s):
|
|
// - 4..120 give > 2300Kibits/s
|
|
// - 1, 2, 3 are below
|
|
// was 100, use 16 to stay away from overrun
|
|
#define INTRIGG 16
|
|
|
|
//was:USC1(uart->uart_nr) = (INTRIGG << UCFFT) | (0x02 << UCTOT) | (1 <<UCTOE);
|
|
USC1(uart->uart_nr) = (INTRIGG << UCFFT);
|
|
USIC(uart->uart_nr) = 0xffff;
|
|
//was: USIE(uart->uart_nr) = (1 << UIFF) | (1 << UIFR) | (1 << UITO);
|
|
// UIFF: rx fifo full
|
|
// UIOF: rx fifo overflow (=overrun)
|
|
// UIFR: frame error
|
|
// UIPE: parity error
|
|
// UITO: rx fifo timeout
|
|
USIE(uart->uart_nr) = (1 << UIFF) | (1 << UIOF) | (1 << UIFR) | (1 << UIPE) | (1 << UITO);
|
|
ETS_UART_INTR_ATTACH(uart_isr, (void *)uart);
|
|
ETS_UART_INTR_ENABLE();
|
|
}
|
|
|
|
static void
|
|
uart_stop_isr(uart_t* uart)
|
|
{
|
|
if(uart == NULL || !uart->rx_enabled)
|
|
return;
|
|
|
|
if(gdbstub_has_uart_isr_control()) {
|
|
gdbstub_set_uart_isr_callback(NULL, NULL);
|
|
return;
|
|
}
|
|
|
|
ETS_UART_INTR_DISABLE();
|
|
USC1(uart->uart_nr) = 0;
|
|
USIC(uart->uart_nr) = 0xffff;
|
|
USIE(uart->uart_nr) = 0;
|
|
ETS_UART_INTR_ATTACH(NULL, NULL);
|
|
}
|
|
|
|
/*
|
|
Reference for uart_tx_fifo_available() and uart_tx_fifo_full():
|
|
-Espressif Techinical Reference doc, chapter 11.3.7
|
|
-tools/sdk/uart_register.h
|
|
-cores/esp8266/esp8266_peri.h
|
|
*/
|
|
inline size_t
|
|
uart_tx_fifo_available(const int uart_nr)
|
|
{
|
|
return (USS(uart_nr) >> USTXC) & 0xff;
|
|
}
|
|
|
|
inline bool
|
|
uart_tx_fifo_full(const int uart_nr)
|
|
{
|
|
return uart_tx_fifo_available(uart_nr) >= 0x7f;
|
|
}
|
|
|
|
|
|
static void
|
|
uart_do_write_char(const int uart_nr, char c)
|
|
{
|
|
while(uart_tx_fifo_full(uart_nr));
|
|
|
|
USF(uart_nr) = c;
|
|
}
|
|
|
|
size_t
|
|
uart_write_char(uart_t* uart, char c)
|
|
{
|
|
if(uart == NULL || !uart->tx_enabled)
|
|
return 0;
|
|
|
|
if(gdbstub_has_uart_isr_control() && uart->uart_nr == UART0) {
|
|
gdbstub_write_char(c);
|
|
return 1;
|
|
}
|
|
uart_do_write_char(uart->uart_nr, c);
|
|
return 1;
|
|
}
|
|
|
|
size_t
|
|
uart_write(uart_t* uart, const char* buf, size_t size)
|
|
{
|
|
if(uart == NULL || !uart->tx_enabled)
|
|
return 0;
|
|
|
|
if(gdbstub_has_uart_isr_control() && uart->uart_nr == UART0) {
|
|
gdbstub_write(buf, size);
|
|
return 0;
|
|
}
|
|
|
|
size_t ret = size;
|
|
const int uart_nr = uart->uart_nr;
|
|
while (size--)
|
|
uart_do_write_char(uart_nr, pgm_read_byte(buf++));
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
size_t
|
|
uart_tx_free(uart_t* uart)
|
|
{
|
|
if(uart == NULL || !uart->tx_enabled)
|
|
return 0;
|
|
|
|
return UART_TX_FIFO_SIZE - uart_tx_fifo_available(uart->uart_nr);
|
|
}
|
|
|
|
void
|
|
uart_wait_tx_empty(uart_t* uart)
|
|
{
|
|
if(uart == NULL || !uart->tx_enabled)
|
|
return;
|
|
|
|
while(uart_tx_fifo_available(uart->uart_nr) > 0)
|
|
delay(0);
|
|
|
|
}
|
|
|
|
void
|
|
uart_flush(uart_t* uart)
|
|
{
|
|
if(uart == NULL)
|
|
return;
|
|
|
|
uint32_t tmp = 0x00000000;
|
|
if(uart->rx_enabled)
|
|
{
|
|
tmp |= (1 << UCRXRST);
|
|
ETS_UART_INTR_DISABLE();
|
|
uart->rx_buffer->rpos = 0;
|
|
uart->rx_buffer->wpos = 0;
|
|
ETS_UART_INTR_ENABLE();
|
|
}
|
|
|
|
if(uart->tx_enabled)
|
|
tmp |= (1 << UCTXRST);
|
|
|
|
if(!gdbstub_has_uart_isr_control() || uart->uart_nr != UART0) {
|
|
USC0(uart->uart_nr) |= (tmp);
|
|
USC0(uart->uart_nr) &= ~(tmp);
|
|
}
|
|
}
|
|
|
|
void
|
|
uart_set_baudrate(uart_t* uart, int baud_rate)
|
|
{
|
|
if(uart == NULL)
|
|
return;
|
|
|
|
uart->baud_rate = baud_rate;
|
|
USD(uart->uart_nr) = (ESP8266_CLOCK / uart->baud_rate);
|
|
}
|
|
|
|
int
|
|
uart_get_baudrate(uart_t* uart)
|
|
{
|
|
if(uart == NULL)
|
|
return 0;
|
|
|
|
return uart->baud_rate;
|
|
}
|
|
|
|
uart_t*
|
|
uart_init(int uart_nr, int baudrate, int config, int mode, int tx_pin, size_t rx_size, bool invert)
|
|
{
|
|
uart_t* uart = (uart_t*) malloc(sizeof(uart_t));
|
|
if(uart == NULL)
|
|
return NULL;
|
|
|
|
uart->uart_nr = uart_nr;
|
|
uart->rx_overrun = false;
|
|
uart->rx_error = false;
|
|
|
|
switch(uart->uart_nr)
|
|
{
|
|
case UART0:
|
|
ETS_UART_INTR_DISABLE();
|
|
if(!gdbstub_has_uart_isr_control()) {
|
|
ETS_UART_INTR_ATTACH(NULL, NULL);
|
|
}
|
|
uart->rx_enabled = (mode != UART_TX_ONLY);
|
|
uart->tx_enabled = (mode != UART_RX_ONLY);
|
|
uart->rx_pin = (uart->rx_enabled)?3:255;
|
|
if(uart->rx_enabled)
|
|
{
|
|
struct uart_rx_buffer_ * rx_buffer = (struct uart_rx_buffer_ *)malloc(sizeof(struct uart_rx_buffer_));
|
|
if(rx_buffer == NULL)
|
|
{
|
|
free(uart);
|
|
return NULL;
|
|
}
|
|
rx_buffer->size = rx_size;//var this
|
|
rx_buffer->rpos = 0;
|
|
rx_buffer->wpos = 0;
|
|
rx_buffer->buffer = (uint8_t *)malloc(rx_buffer->size);
|
|
if(rx_buffer->buffer == NULL)
|
|
{
|
|
free(rx_buffer);
|
|
free(uart);
|
|
return NULL;
|
|
}
|
|
uart->rx_buffer = rx_buffer;
|
|
pinMode(uart->rx_pin, SPECIAL);
|
|
}
|
|
if(uart->tx_enabled)
|
|
{
|
|
if (tx_pin == 2)
|
|
{
|
|
uart->tx_pin = 2;
|
|
pinMode(uart->tx_pin, FUNCTION_4);
|
|
}
|
|
else
|
|
{
|
|
uart->tx_pin = 1;
|
|
pinMode(uart->tx_pin, FUNCTION_0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uart->tx_pin = 255;
|
|
}
|
|
IOSWAP &= ~(1 << IOSWAPU0);
|
|
break;
|
|
|
|
case UART1:
|
|
// Note: uart_interrupt_handler does not support RX on UART 1.
|
|
uart->rx_enabled = false;
|
|
uart->tx_enabled = (mode != UART_RX_ONLY);
|
|
uart->rx_pin = 255;
|
|
uart->tx_pin = (uart->tx_enabled)?2:255; // GPIO7 as TX not possible! See GPIO pins used by UART
|
|
if(uart->tx_enabled)
|
|
pinMode(uart->tx_pin, SPECIAL);
|
|
|
|
break;
|
|
|
|
case UART_NO:
|
|
default:
|
|
// big fail!
|
|
free(uart);
|
|
return NULL;
|
|
}
|
|
|
|
uart_set_baudrate(uart, baudrate);
|
|
if(uart->uart_nr == UART0 && invert)
|
|
{
|
|
config |= BIT(UCDTRI) | BIT(UCRTSI) | BIT(UCTXI) | BIT(UCDSRI) | BIT(UCCTSI) | BIT(UCRXI);
|
|
}
|
|
USC0(uart->uart_nr) = config;
|
|
|
|
if(!gdbstub_has_uart_isr_control() || uart->uart_nr != UART0) {
|
|
uart_flush(uart);
|
|
USC1(uart->uart_nr) = 0;
|
|
USIC(uart->uart_nr) = 0xffff;
|
|
USIE(uart->uart_nr) = 0;
|
|
}
|
|
if(uart->uart_nr == UART0) {
|
|
if(uart->rx_enabled) {
|
|
uart_start_isr(uart);
|
|
}
|
|
if(gdbstub_has_uart_isr_control()) {
|
|
ETS_UART_INTR_ENABLE(); // Undo the disable in the switch() above
|
|
}
|
|
}
|
|
|
|
return uart;
|
|
}
|
|
|
|
void
|
|
uart_uninit(uart_t* uart)
|
|
{
|
|
if(uart == NULL)
|
|
return;
|
|
|
|
uart_stop_isr(uart);
|
|
|
|
if(uart->tx_enabled && (!gdbstub_has_uart_isr_control() || uart->uart_nr != UART0)) {
|
|
switch(uart->tx_pin)
|
|
{
|
|
case 1:
|
|
pinMode(1, INPUT);
|
|
break;
|
|
case 2:
|
|
pinMode(2, INPUT);
|
|
break;
|
|
case 15:
|
|
pinMode(15, INPUT);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(uart->rx_enabled) {
|
|
free(uart->rx_buffer->buffer);
|
|
free(uart->rx_buffer);
|
|
if(!gdbstub_has_uart_isr_control()) {
|
|
switch(uart->rx_pin)
|
|
{
|
|
case 3:
|
|
pinMode(3, INPUT);
|
|
break;
|
|
case 13:
|
|
pinMode(13, INPUT);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
free(uart);
|
|
}
|
|
|
|
void
|
|
uart_swap(uart_t* uart, int tx_pin)
|
|
{
|
|
if(uart == NULL)
|
|
return;
|
|
|
|
switch(uart->uart_nr)
|
|
{
|
|
case UART0:
|
|
if(((uart->tx_pin == 1 || uart->tx_pin == 2) && uart->tx_enabled) || (uart->rx_pin == 3 && uart->rx_enabled))
|
|
{
|
|
if(uart->tx_enabled) //TX
|
|
{
|
|
pinMode(uart->tx_pin, INPUT);
|
|
uart->tx_pin = 15;
|
|
}
|
|
if(uart->rx_enabled) //RX
|
|
{
|
|
pinMode(uart->rx_pin, INPUT);
|
|
uart->rx_pin = 13;
|
|
}
|
|
if(uart->tx_enabled)
|
|
pinMode(uart->tx_pin, FUNCTION_4); //TX
|
|
|
|
if(uart->rx_enabled)
|
|
pinMode(uart->rx_pin, FUNCTION_4); //RX
|
|
|
|
IOSWAP |= (1 << IOSWAPU0);
|
|
}
|
|
else
|
|
{
|
|
if(uart->tx_enabled) //TX
|
|
{
|
|
pinMode(uart->tx_pin, INPUT);
|
|
uart->tx_pin = (tx_pin == 2)?2:1;
|
|
}
|
|
if(uart->rx_enabled) //RX
|
|
{
|
|
pinMode(uart->rx_pin, INPUT);
|
|
uart->rx_pin = 3;
|
|
}
|
|
if(uart->tx_enabled)
|
|
pinMode(uart->tx_pin, (tx_pin == 2)?FUNCTION_4:SPECIAL); //TX
|
|
|
|
if(uart->rx_enabled)
|
|
pinMode(3, SPECIAL); //RX
|
|
|
|
IOSWAP &= ~(1 << IOSWAPU0);
|
|
}
|
|
break;
|
|
case UART1:
|
|
// Currently no swap possible! See GPIO pins used by UART
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
uart_set_tx(uart_t* uart, int tx_pin)
|
|
{
|
|
if(uart == NULL)
|
|
return;
|
|
|
|
switch(uart->uart_nr)
|
|
{
|
|
case UART0:
|
|
if(uart->tx_enabled)
|
|
{
|
|
if (uart->tx_pin == 1 && tx_pin == 2)
|
|
{
|
|
pinMode(uart->tx_pin, INPUT);
|
|
uart->tx_pin = 2;
|
|
pinMode(uart->tx_pin, FUNCTION_4);
|
|
}
|
|
else if (uart->tx_pin == 2 && tx_pin != 2)
|
|
{
|
|
pinMode(uart->tx_pin, INPUT);
|
|
uart->tx_pin = 1;
|
|
pinMode(uart->tx_pin, SPECIAL);
|
|
}
|
|
}
|
|
|
|
break;
|
|
case UART1:
|
|
// GPIO7 as TX not possible! See GPIO pins used by UART
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
uart_set_pins(uart_t* uart, int tx, int rx)
|
|
{
|
|
if(uart == NULL)
|
|
return;
|
|
|
|
if(uart->uart_nr == UART0) // Only UART0 allows pin changes
|
|
{
|
|
if(uart->tx_enabled && uart->tx_pin != tx)
|
|
{
|
|
if( rx == 13 && tx == 15)
|
|
{
|
|
uart_swap(uart, 15);
|
|
}
|
|
else if (rx == 3 && (tx == 1 || tx == 2))
|
|
{
|
|
if (uart->rx_pin != rx)
|
|
uart_swap(uart, tx);
|
|
else
|
|
uart_set_tx(uart, tx);
|
|
}
|
|
}
|
|
if(uart->rx_enabled && uart->rx_pin != rx && rx == 13 && tx == 15)
|
|
uart_swap(uart, 15);
|
|
}
|
|
}
|
|
|
|
|
|
bool
|
|
uart_tx_enabled(uart_t* uart)
|
|
{
|
|
if(uart == NULL)
|
|
return false;
|
|
|
|
return uart->tx_enabled;
|
|
}
|
|
|
|
bool
|
|
uart_rx_enabled(uart_t* uart)
|
|
{
|
|
if(uart == NULL)
|
|
return false;
|
|
|
|
return uart->rx_enabled;
|
|
}
|
|
|
|
bool
|
|
uart_has_overrun (uart_t* uart)
|
|
{
|
|
if (uart == NULL || !uart->rx_overrun)
|
|
return false;
|
|
|
|
// clear flag
|
|
uart->rx_overrun = false;
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
uart_has_rx_error (uart_t* uart)
|
|
{
|
|
if (uart == NULL || !uart->rx_error)
|
|
return false;
|
|
|
|
// clear flag
|
|
uart->rx_error = false;
|
|
return true;
|
|
}
|
|
|
|
static void
|
|
uart_ignore_char(char c)
|
|
{
|
|
(void) c;
|
|
}
|
|
|
|
inline void
|
|
uart_write_char_delay(const int uart_nr, char c)
|
|
{
|
|
while(uart_tx_fifo_full(uart_nr))
|
|
delay(0);
|
|
|
|
USF(uart_nr) = c;
|
|
|
|
}
|
|
|
|
static void
|
|
uart0_write_char(char c)
|
|
{
|
|
uart_write_char_delay(0, c);
|
|
}
|
|
|
|
static void
|
|
uart1_write_char(char c)
|
|
{
|
|
uart_write_char_delay(1, c);
|
|
}
|
|
|
|
void
|
|
uart_set_debug(int uart_nr)
|
|
{
|
|
s_uart_debug_nr = uart_nr;
|
|
fp_putc_t func = NULL;
|
|
switch(s_uart_debug_nr)
|
|
{
|
|
case UART0:
|
|
func = &uart0_write_char;
|
|
// This selects the UART for ROM ets_putc which is used by
|
|
// ::printf, ets_printf_P in core_esp_postmortem.cpp and others.
|
|
// Has a side effect of clearing RX FIFO for UART0
|
|
uart_buff_switch(0);
|
|
break;
|
|
case UART1:
|
|
func = &uart1_write_char;
|
|
uart_buff_switch(1);
|
|
break;
|
|
case UART_NO:
|
|
default:
|
|
func = &uart_ignore_char;
|
|
// There is no disable option for ets_putc,
|
|
// we switch to UART0 for disable case.
|
|
uart_buff_switch(0);
|
|
break;
|
|
}
|
|
if(gdbstub_has_putc1_control()) {
|
|
gdbstub_set_putc1_callback(func);
|
|
} else {
|
|
if (uart_nr == UART0 || uart_nr == UART1) {
|
|
system_set_os_print(1);
|
|
} else {
|
|
system_set_os_print(0);
|
|
}
|
|
ets_install_putc1(func);
|
|
}
|
|
}
|
|
|
|
int
|
|
uart_get_debug()
|
|
{
|
|
return s_uart_debug_nr;
|
|
}
|
|
|
|
/*
|
|
To start detection of baud rate with the UART the UART_AUTOBAUD_EN bit needs to be cleared and set. The ROM function uart_baudrate_detect() does this only once, so on a next call the UartDev.rcv_state is not equal to BAUD_RATE_DET. Instead of poking around in the UartDev struct with unknown effect, the UART_AUTOBAUD_EN bit is directly triggered by the function uart_detect_baudrate().
|
|
*/
|
|
void
|
|
uart_start_detect_baudrate(int uart_nr)
|
|
{
|
|
USA(uart_nr) &= ~(UART_GLITCH_FILT << UART_GLITCH_FILT_S | UART_AUTOBAUD_EN);
|
|
USA(uart_nr) = 0x08 << UART_GLITCH_FILT_S | UART_AUTOBAUD_EN;
|
|
}
|
|
|
|
int
|
|
uart_detect_baudrate(int uart_nr)
|
|
{
|
|
static bool doTrigger = true;
|
|
|
|
if (doTrigger)
|
|
{
|
|
uart_start_detect_baudrate(uart_nr);
|
|
doTrigger = false;
|
|
}
|
|
|
|
int32_t divisor = uart_baudrate_detect(uart_nr, 1);
|
|
if (!divisor) {
|
|
return 0;
|
|
}
|
|
|
|
doTrigger = true; // Initialize for a next round
|
|
int32_t baudrate = UART_CLK_FREQ / divisor;
|
|
|
|
static const int 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];
|
|
}
|
|
|
|
};
|