|
|
|
/*
|
|
|
|
* File : uart.c
|
|
|
|
* This file is part of Espressif's AT+ command set program.
|
|
|
|
* Copyright (C) 2013 - 2016, Espressif Systems
|
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of version 3 of the GNU General Public License as
|
|
|
|
* published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
* Heavily modified and enhanced by Thorsten von Eicken in 2015
|
|
|
|
*/
|
|
|
|
#define USE_US_TIMER
|
|
|
|
#include "esp8266.h"
|
|
|
|
#include "task.h"
|
|
|
|
#include "uart.h"
|
|
|
|
#include <osapi.h>
|
|
|
|
|
|
|
|
#ifdef UART_DBG
|
|
|
|
#define DBG_UART(format, ...) os_printf(format, ## __VA_ARGS__)
|
|
|
|
#else
|
|
|
|
#define DBG_UART(format, ...) do { } while(0)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
LOCAL uint8_t uart_recvTaskNum;
|
|
|
|
LOCAL int8_t uart0_tx_enable_pin;
|
|
|
|
|
|
|
|
// UartDev is defined and initialized in rom code.
|
|
|
|
extern UartDevice UartDev;
|
|
|
|
#define MAX_CB 4
|
|
|
|
static UartRecv_cb uart_recv_cb[4];
|
|
|
|
|
|
|
|
static void uart0_rx_intr_handler(void *para);
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
* FunctionName : set_tx_enable_pin
|
|
|
|
* Description : Set which pin to use for RS-485 TX_ENABLE
|
|
|
|
* Parameters : pin, the pin to use
|
|
|
|
* Returns : NONE
|
|
|
|
*******************************************************************************/
|
|
|
|
void ICACHE_FLASH_ATTR
|
|
|
|
uart0_set_tx_enable_pin(int8_t pin) {
|
|
|
|
uart0_tx_enable_pin = pin;
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
* FunctionName : tx_enable
|
|
|
|
* Description : Internal used function
|
|
|
|
* Set the TX_ENABLE line for RS-485 communications
|
|
|
|
* Parameters : state, true if the TX_ENABLE line should be asserted high
|
|
|
|
* Returns : NONE
|
|
|
|
*******************************************************************************/
|
|
|
|
static void ICACHE_FLASH_ATTR
|
|
|
|
tx_enable(bool state)
|
|
|
|
{
|
|
|
|
if (uart0_tx_enable_pin >= 0) {
|
|
|
|
#ifdef SERBR_DBG
|
|
|
|
os_printf("TX_ENABLE gpio%d state=%d\n", uart0_tx_enable_pin, (int)state);
|
|
|
|
#endif
|
|
|
|
GPIO_OUTPUT_SET(uart0_tx_enable_pin, (state) ? 1 : 0);
|
|
|
|
}
|
|
|
|
#ifdef SERBR_DBG
|
|
|
|
else { os_printf("TX Enable: no pin\n"); }
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
* FunctionName : tx_completed_interrupt
|
|
|
|
* Description : Internal used function
|
|
|
|
* Set the TX enable line low, after the UART has completed tranmission
|
|
|
|
* Parameters : unused unused
|
|
|
|
* Returns : NONE
|
|
|
|
*******************************************************************************/
|
|
|
|
static void ICACHE_FLASH_ATTR
|
|
|
|
tx_completed_interrupt(void *arg)
|
|
|
|
{
|
|
|
|
tx_enable(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
* FunctionName : uart_config
|
|
|
|
* Description : Internal used function
|
|
|
|
* UART0 used for data TX/RX, RX buffer size is 0x100, interrupt enabled
|
|
|
|
* UART1 just used for debug output
|
|
|
|
* Parameters : uart_no, use UART0 or UART1 defined ahead
|
|
|
|
* Returns : NONE
|
|
|
|
*******************************************************************************/
|
|
|
|
static void ICACHE_FLASH_ATTR
|
|
|
|
uart_config(uint8 uart_no)
|
|
|
|
{
|
|
|
|
if (uart_no == UART1) {
|
|
|
|
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_U1TXD_BK);
|
|
|
|
PIN_PULLUP_DIS(PERIPHS_IO_MUX_GPIO2_U);
|
|
|
|
} else {
|
|
|
|
/* rcv_buff size is 0x100 */
|
|
|
|
ETS_UART_INTR_ATTACH(uart0_rx_intr_handler, &(UartDev.rcv_buff));
|
|
|
|
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD);
|
|
|
|
PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, 0); // FUNC_U0RXD==0
|
|
|
|
//PIN_PULLUP_DIS (PERIPHS_IO_MUX_U0TXD_U); now done in serbridgeInitPins
|
|
|
|
//PIN_PULLUP_DIS (PERIPHS_IO_MUX_U0RXD_U);
|
|
|
|
}
|
|
|
|
|
|
|
|
uart_div_modify(uart_no, UART_CLK_FREQ / UartDev.baut_rate);
|
|
|
|
|
|
|
|
if (uart_no == UART1) //UART 1 always 8 N 1
|
|
|
|
WRITE_PERI_REG(UART_CONF0(uart_no),
|
|
|
|
CALC_UARTMODE(EIGHT_BITS, NONE_BITS, ONE_STOP_BIT));
|
|
|
|
else
|
|
|
|
WRITE_PERI_REG(UART_CONF0(uart_no),
|
|
|
|
CALC_UARTMODE(UartDev.data_bits, UartDev.parity, UartDev.stop_bits));
|
|
|
|
|
|
|
|
//clear rx and tx fifo,not ready
|
|
|
|
SET_PERI_REG_MASK(UART_CONF0(uart_no), UART_RXFIFO_RST | UART_TXFIFO_RST);
|
|
|
|
CLEAR_PERI_REG_MASK(UART_CONF0(uart_no), UART_RXFIFO_RST | UART_TXFIFO_RST);
|
|
|
|
|
|
|
|
if (uart_no == UART0) {
|
|
|
|
// Configure RX interrupt conditions as follows: trigger rx-full when there are 80 characters
|
|
|
|
// in the buffer, trigger rx-timeout when the fifo is non-empty and nothing further has been
|
|
|
|
// received for 4 character periods.
|
|
|
|
// Set the hardware flow-control to trigger when the FIFO holds 100 characters, although
|
|
|
|
// we don't really expect the signals to actually be wired up to anything. It doesn't hurt
|
|
|
|
// to set the threshold here...
|
|
|
|
// We do not enable framing error interrupts 'cause they tend to cause an interrupt avalanche
|
|
|
|
// and instead just poll for them when we get a std RX interrupt.
|
|
|
|
|
|
|
|
uint32_t tx_empty_bits = 0;
|
|
|
|
if (uart0_tx_enable_pin >= 0) {
|
|
|
|
tx_empty_bits = (0 & UART_TXFIFO_EMPTY_THRHD) << UART_TXFIFO_EMPTY_THRHD_S | UART_TXFIFO_EMPTY_INT_ENA;
|
|
|
|
}
|
|
|
|
WRITE_PERI_REG(UART_CONF1(uart_no),
|
|
|
|
((80 & UART_RXFIFO_FULL_THRHD) << UART_RXFIFO_FULL_THRHD_S) |
|
|
|
|
((100 & UART_RX_FLOW_THRHD) << UART_RX_FLOW_THRHD_S) |
|
|
|
|
UART_RX_FLOW_EN |
|
|
|
|
(4 & UART_RX_TOUT_THRHD) << UART_RX_TOUT_THRHD_S |
|
|
|
|
UART_RX_TOUT_EN) |
|
|
|
|
tx_empty_bits
|
|
|
|
;
|
|
|
|
SET_PERI_REG_MASK(UART_INT_ENA(uart_no), UART_RXFIFO_FULL_INT_ENA | UART_RXFIFO_TOUT_INT_ENA);
|
|
|
|
} else {
|
|
|
|
WRITE_PERI_REG(UART_CONF1(uart_no),
|
|
|
|
((UartDev.rcv_buff.TrigLvl & UART_RXFIFO_FULL_THRHD) << UART_RXFIFO_FULL_THRHD_S));
|
|
|
|
}
|
|
|
|
|
|
|
|
//clear all interrupt
|
|
|
|
WRITE_PERI_REG(UART_INT_CLR(uart_no), 0xffff);
|
|
|
|
}
|
|
|
|
|
|
|
|
os_timer_t uart_tx_enable_timer;
|
|
|
|
bool uart_tx_enable_timer_inited = false;
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
* FunctionName : uart1_tx_one_char
|
|
|
|
* Description : Internal used function
|
|
|
|
* Use uart1 interface to transfer one char
|
|
|
|
* Parameters : uint8 TxChar - character to tx
|
|
|
|
* Returns : OK
|
|
|
|
*******************************************************************************/
|
|
|
|
STATUS
|
|
|
|
uart_tx_one_char(uint8 uart, uint8 c)
|
|
|
|
{
|
|
|
|
//Wait until there is room in the FIFO
|
|
|
|
while (((READ_PERI_REG(UART_STATUS(uart))>>UART_TXFIFO_CNT_S)&UART_TXFIFO_CNT)>=100) ;
|
|
|
|
//Send the character
|
|
|
|
if (UART0 == uart) {
|
|
|
|
if (uart_tx_enable_timer_inited) {
|
|
|
|
// If a tx_completed_interrupt has already been scheduled, cancel it before it fires during our transmission
|
|
|
|
os_timer_disarm(&uart_tx_enable_timer);
|
|
|
|
} else {
|
|
|
|
os_timer_setfn(&uart_tx_enable_timer, tx_completed_interrupt, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
tx_enable(true);
|
|
|
|
}
|
|
|
|
WRITE_PERI_REG(UART_FIFO(uart), c);
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
* FunctionName : uart1_write_char
|
|
|
|
* Description : Internal used function
|
|
|
|
* Do some special deal while tx char is '\r' or '\n'
|
|
|
|
* Parameters : char c - character to tx
|
|
|
|
* Returns : NONE
|
|
|
|
*******************************************************************************/
|
|
|
|
void ICACHE_FLASH_ATTR
|
|
|
|
uart1_write_char(char c)
|
|
|
|
{
|
|
|
|
//if (c == '\n') uart_tx_one_char(UART1, '\r');
|
|
|
|
uart_tx_one_char(UART1, c);
|
|
|
|
}
|
|
|
|
void ICACHE_FLASH_ATTR
|
|
|
|
uart0_write_char(char c)
|
|
|
|
{
|
|
|
|
//if (c == '\n') uart_tx_one_char(UART0, '\r');
|
|
|
|
uart_tx_one_char(UART0, c);
|
|
|
|
}
|
|
|
|
/******************************************************************************
|
|
|
|
* FunctionName : uart0_tx_buffer
|
|
|
|
* Description : use uart0 to transfer buffer
|
|
|
|
* Parameters : uint8 *buf - point to send buffer
|
|
|
|
* uint16 len - buffer len
|
|
|
|
* Returns :
|
|
|
|
*******************************************************************************/
|
|
|
|
void ICACHE_FLASH_ATTR
|
|
|
|
uart0_tx_buffer(char *buf, uint16 len)
|
|
|
|
{
|
|
|
|
uint16 i;
|
|
|
|
|
|
|
|
for (i = 0; i < len; i++)
|
|
|
|
{
|
|
|
|
uart_tx_one_char(UART0, buf[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
* FunctionName : uart0_sendStr
|
|
|
|
* Description : use uart0 to transfer buffer
|
|
|
|
* Parameters : uint8 *buf - point to send buffer
|
|
|
|
* uint16 len - buffer len
|
|
|
|
* Returns :
|
|
|
|
*******************************************************************************/
|
|
|
|
void ICACHE_FLASH_ATTR
|
|
|
|
uart0_sendStr(const char *str)
|
|
|
|
{
|
|
|
|
while(*str)
|
|
|
|
{
|
|
|
|
uart_tx_one_char(UART0, *str++);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32 last_frm_err; // time in us when last framing error message was printed
|
|
|
|
static int uart0_baud_rate = 0; // The baud rate for uart0
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
* FunctionName : uart0_rx_intr_handler
|
|
|
|
* Description : Internal used function
|
|
|
|
* UART0 interrupt handler, add self handle code inside
|
|
|
|
* Parameters : void *para - point to ETS_UART_INTR_ATTACH's arg
|
|
|
|
* Returns : NONE
|
|
|
|
*******************************************************************************/
|
|
|
|
static void // must not use ICACHE_FLASH_ATTR !
|
|
|
|
uart0_rx_intr_handler(void *para)
|
|
|
|
{
|
|
|
|
// we assume that uart1 has interrupts disabled (it uses the same interrupt vector)
|
|
|
|
uint8 uart_no = UART0;
|
|
|
|
const uint32 one_sec = 1000000; // one second in usecs
|
|
|
|
|
|
|
|
// we end up largely ignoring framing errors and we just print a warning every second max
|
|
|
|
if (READ_PERI_REG(UART_INT_RAW(uart_no)) & UART_FRM_ERR_INT_RAW) {
|
|
|
|
uint32 now = system_get_time();
|
|
|
|
if (last_frm_err == 0 || (now - last_frm_err) > one_sec) {
|
|
|
|
os_printf("UART framing error (bad baud rate?)\n");
|
|
|
|
last_frm_err = now;
|
|
|
|
}
|
|
|
|
// clear rx fifo (apparently this is not optional at this point)
|
|
|
|
SET_PERI_REG_MASK(UART_CONF0(uart_no), UART_RXFIFO_RST);
|
|
|
|
CLEAR_PERI_REG_MASK(UART_CONF0(uart_no), UART_RXFIFO_RST);
|
|
|
|
// reset framing error
|
|
|
|
WRITE_PERI_REG(UART_INT_CLR(UART0), UART_FRM_ERR_INT_CLR);
|
|
|
|
// once framing errors are gone for 10 secs we forget about having seen them
|
|
|
|
} else if (last_frm_err != 0 && (system_get_time() - last_frm_err) > 10*one_sec) {
|
|
|
|
last_frm_err = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (UART_RXFIFO_FULL_INT_ST == (READ_PERI_REG(UART_INT_ST(uart_no)) & UART_RXFIFO_FULL_INT_ST)
|
|
|
|
|| UART_RXFIFO_TOUT_INT_ST == (READ_PERI_REG(UART_INT_ST(uart_no)) & UART_RXFIFO_TOUT_INT_ST))
|
|
|
|
{
|
|
|
|
//DBG_UART("stat:%02X",*(uint8 *)UART_INT_ENA(uart_no));
|
|
|
|
ETS_UART_INTR_DISABLE();
|
|
|
|
post_usr_task(uart_recvTaskNum, 0);
|
|
|
|
} else if (UART_TXFIFO_EMPTY_INT_ST == (READ_PERI_REG(UART_INT_ST(uart_no)) & UART_TXFIFO_EMPTY_INT_ST)) {
|
|
|
|
// TX Queue is empty, disable the TX_ENABLE line once the transmission is complete
|
|
|
|
if (0 != uart0_baud_rate) {
|
|
|
|
int tx_char_time = 8 * 1000000 / uart0_baud_rate; // assumes 8 bits per character
|
|
|
|
os_timer_arm_us(&uart_tx_enable_timer, tx_char_time, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
* FunctionName : uart_recvTask
|
|
|
|
* Description : system task triggered on receive interrupt, empties FIFO and calls callbacks
|
|
|
|
*******************************************************************************/
|
|
|
|
static void ICACHE_FLASH_ATTR
|
|
|
|
uart_recvTask(os_event_t *events)
|
|
|
|
{
|
|
|
|
while (READ_PERI_REG(UART_STATUS(UART0)) & (UART_RXFIFO_CNT << UART_RXFIFO_CNT_S)) {
|
|
|
|
//WRITE_PERI_REG(0X60000914, 0x73); //WTD // commented out by TvE
|
|
|
|
|
|
|
|
// read a buffer-full from the uart
|
|
|
|
uint16 length = 0;
|
|
|
|
char buf[128];
|
|
|
|
while ((READ_PERI_REG(UART_STATUS(UART0)) & (UART_RXFIFO_CNT << UART_RXFIFO_CNT_S)) &&
|
|
|
|
(length < 128)) {
|
|
|
|
buf[length++] = READ_PERI_REG(UART_FIFO(UART0)) & 0xFF;
|
|
|
|
}
|
|
|
|
//DBG_UART("%d ix %d\n", system_get_time(), length);
|
|
|
|
|
|
|
|
for (int i=0; i<MAX_CB; i++) {
|
|
|
|
if (uart_recv_cb[i] != NULL) (uart_recv_cb[i])(buf, length);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
WRITE_PERI_REG(UART_INT_CLR(UART0), UART_RXFIFO_FULL_INT_CLR|UART_RXFIFO_TOUT_INT_CLR);
|
|
|
|
ETS_UART_INTR_ENABLE();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Turn UART interrupts off and poll for nchars or until timeout hits
|
|
|
|
uint16_t ICACHE_FLASH_ATTR
|
|
|
|
uart0_rx_poll(char *buff, uint16_t nchars, uint32_t timeout_us) {
|
|
|
|
ETS_UART_INTR_DISABLE();
|
|
|
|
uint16_t got = 0;
|
|
|
|
uint32_t start = system_get_time(); // time in us
|
|
|
|
while (system_get_time()-start < timeout_us) {
|
|
|
|
while (READ_PERI_REG(UART_STATUS(UART0)) & (UART_RXFIFO_CNT << UART_RXFIFO_CNT_S)) {
|
|
|
|
buff[got++] = READ_PERI_REG(UART_FIFO(UART0)) & 0xFF;
|
|
|
|
if (got == nchars) goto done;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
done:
|
|
|
|
ETS_UART_INTR_ENABLE();
|
|
|
|
return got;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ICACHE_FLASH_ATTR
|
|
|
|
uart0_baud(int rate) {
|
|
|
|
uart0_baud_rate = rate;
|
|
|
|
os_printf("UART %d baud\n", rate);
|
|
|
|
uart_div_modify(UART0, UART_CLK_FREQ / rate);
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
* FunctionName : uart_init
|
|
|
|
* Description : user interface for init uart
|
|
|
|
* Parameters : UartBautRate uart0_br - uart0 bautrate
|
|
|
|
* uart0TxEnablePin
|
|
|
|
* UartBautRate uart1_br - uart1 bautrate
|
|
|
|
* Returns : NONE
|
|
|
|
*******************************************************************************/
|
|
|
|
void ICACHE_FLASH_ATTR
|
|
|
|
uart_init(UartBautRate uart0_br, int8_t uart0TxEnablePin, UartBautRate uart1_br)
|
|
|
|
{
|
|
|
|
if (uart0TxEnablePin >= 0) {
|
|
|
|
uart0_set_tx_enable_pin(uart0TxEnablePin);
|
|
|
|
}
|
|
|
|
|
|
|
|
// rom use 74880 baut_rate, here reinitialize
|
|
|
|
uart0_baud_rate = (int)uart0_br;
|
|
|
|
UartDev.baut_rate = uart0_br;
|
|
|
|
uart_config(UART0);
|
|
|
|
UartDev.baut_rate = uart1_br;
|
|
|
|
uart_config(UART1);
|
|
|
|
for (int i=0; i<4; i++) uart_tx_one_char(UART1, '\n');
|
|
|
|
for (int i=0; i<4; i++) uart_tx_one_char(UART0, '\n');
|
|
|
|
ETS_UART_INTR_ENABLE();
|
|
|
|
|
|
|
|
// install uart1 putc callback
|
|
|
|
os_install_putc1((void *)uart0_write_char);
|
|
|
|
|
|
|
|
uart_recvTaskNum = register_usr_task(uart_recvTask);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ICACHE_FLASH_ATTR
|
|
|
|
uart_add_recv_cb(UartRecv_cb cb) {
|
|
|
|
for (int i=0; i<MAX_CB; i++) {
|
|
|
|
if (uart_recv_cb[i] == NULL) {
|
|
|
|
uart_recv_cb[i] = cb;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
os_printf("UART: max cb count exceeded\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
void ICACHE_FLASH_ATTR
|
|
|
|
uart_reattach()
|
|
|
|
{
|
|
|
|
uart_init(BIT_RATE_74880, -1, BIT_RATE_74880);
|
|
|
|
// ETS_UART_INTR_ATTACH(uart_rx_intr_handler_ssc, &(UartDev.rcv_buff));
|
|
|
|
// ETS_UART_INTR_ENABLE();
|
|
|
|
}
|