/* * 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 . * ---------------------------------------------------------------------------- * Heavily modified and enhanced by Thorsten von Eicken in 2015 */ #include "esp8266.h" #include "task.h" #include "uart.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) { // Set the empty threshold to 0 and enable the buffer empty interrupt 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= 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