363 lines
8.8 KiB
C
363 lines
8.8 KiB
C
// 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"
|
|
|
|
#define ETS_UART_INUM 5
|
|
#define ETS_UART2_INUM ETS_UART_INUM
|
|
|
|
#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_INUM(u) ((u==0)?ETS_UART0_INUM:( (u==1)?ETS_UART1_INUM:( (u==2)?ETS_UART2_INUM: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;
|
|
xSemaphoreHandle lock;
|
|
uint8_t num;
|
|
xQueueHandle queue;
|
|
};
|
|
|
|
#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},
|
|
{(volatile uart_dev_t *)(DR_REG_UART1_BASE), NULL, 1, NULL},
|
|
{(volatile uart_dev_t *)(DR_REG_UART2_BASE), NULL, 2, NULL}
|
|
};
|
|
|
|
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];
|
|
uart->dev->int_clr.rxfifo_full = 1;
|
|
uart->dev->int_clr.frm_err = 1;
|
|
uart->dev->int_clr.rxfifo_tout = 1;
|
|
while(uart->dev->status.rxfifo_cnt) {
|
|
c = uart->dev->fifo.rw_byte;
|
|
if(uart->queue != NULL && !xQueueIsQueueFullFromISR(uart->queue)) {
|
|
xQueueSendFromISR(uart->queue, &c, &xHigherPriorityTaskWoken);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (xHigherPriorityTaskWoken) {
|
|
portYIELD_FROM_ISR();
|
|
}
|
|
}
|
|
|
|
void uartEnableGlobalInterrupt()
|
|
{
|
|
xt_set_interrupt_handler(ETS_UART_INUM, _uart_isr, NULL);
|
|
ESP_INTR_ENABLE(ETS_UART_INUM);
|
|
}
|
|
|
|
void uartDisableGlobalInterrupt()
|
|
{
|
|
ESP_INTR_DISABLE(ETS_UART_INUM);
|
|
xt_set_interrupt_handler(ETS_UART_INUM, NULL, NULL);
|
|
}
|
|
|
|
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;
|
|
|
|
intr_matrix_set(xPortGetCoreID(), UART_INTR_SOURCE(uart->num), ETS_UART_INUM);
|
|
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;
|
|
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);
|
|
uartEnableGlobalInterrupt();
|
|
}
|
|
|
|
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(uart->lock == NULL) {
|
|
uart->lock = xSemaphoreCreateMutex();
|
|
if(uart->lock == NULL) {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if(queueLen && uart->queue == NULL) {
|
|
uart->queue = xQueueCreate(queueLen, sizeof(uint8_t)); //initialize the queue
|
|
if(uart->queue == NULL) {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
uartFlush(uart);
|
|
uartSetBaudRate(uart, baudrate);
|
|
UART_MUTEX_LOCK();
|
|
uart->dev->conf0.val = config;
|
|
UART_MUTEX_UNLOCK();
|
|
|
|
if(rxPin != -1) {
|
|
uartAttachRx(uart, rxPin, inverted);
|
|
}
|
|
|
|
if(txPin != -1) {
|
|
uartAttachTx(uart, txPin, inverted);
|
|
}
|
|
|
|
return uart;
|
|
}
|
|
|
|
void uartEnd(uart_t* uart)
|
|
{
|
|
if(uart == NULL) {
|
|
return;
|
|
}
|
|
|
|
UART_MUTEX_LOCK();
|
|
if(uart->queue != NULL) {
|
|
vQueueDelete(uart->queue);
|
|
}
|
|
|
|
uartDetachRx(uart);
|
|
uartDetachTx(uart);
|
|
|
|
uart->dev->conf0.val = 0;
|
|
UART_MUTEX_UNLOCK();
|
|
}
|
|
|
|
uint32_t uartAvailable(uart_t* uart)
|
|
{
|
|
if(uart == NULL || uart->queue == NULL) {
|
|
return 0;
|
|
}
|
|
return uxQueueMessagesWaiting(uart->queue);
|
|
}
|
|
|
|
uint8_t uartRead(uart_t* uart)
|
|
{
|
|
if(uart == NULL || uart->queue == NULL) {
|
|
return 0;
|
|
}
|
|
uint8_t c;
|
|
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(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(len && uart->dev->status.txfifo_cnt < 0x7F) {
|
|
uart->dev->fifo.rw_byte = *data++;
|
|
len--;
|
|
}
|
|
}
|
|
UART_MUTEX_UNLOCK();
|
|
}
|
|
|
|
void uartFlush(uart_t* uart)
|
|
{
|
|
if(uart == NULL) {
|
|
return;
|
|
}
|
|
|
|
UART_MUTEX_LOCK();
|
|
while(uart->dev->status.txfifo_cnt);
|
|
|
|
uart->dev->conf0.txfifo_rst = 1;
|
|
uart->dev->conf0.txfifo_rst = 0;
|
|
|
|
uart->dev->conf0.rxfifo_rst = 1;
|
|
uart->dev->conf0.rxfifo_rst = 0;
|
|
UART_MUTEX_UNLOCK();
|
|
}
|
|
|
|
void uartSetBaudRate(uart_t* uart, uint32_t baud_rate)
|
|
{
|
|
if(uart == NULL) {
|
|
return;
|
|
}
|
|
UART_MUTEX_LOCK();
|
|
uint32_t clk_div = ((UART_CLK_FREQ<<4)/baud_rate);
|
|
uart->dev->clk_div.div_int = clk_div>>4 ;
|
|
uart->dev->clk_div.div_frag = clk_div & 0xf;
|
|
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);
|
|
return ((UART_CLK_FREQ<<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 uartSetDebug(uart_t* uart)
|
|
{
|
|
if(uart == NULL || uart->num > 2) {
|
|
s_uart_debug_nr = -1;
|
|
ets_install_putc1(NULL);
|
|
return;
|
|
}
|
|
if(s_uart_debug_nr == uart->num) {
|
|
return;
|
|
}
|
|
s_uart_debug_nr = uart->num;
|
|
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;
|
|
}
|
|
}
|
|
|
|
int uartGetDebug()
|
|
{
|
|
return s_uart_debug_nr;
|
|
}
|
|
|
|
|
|
|