Merge branch 'driver_merge_tmp/merge_uart' into 'master'

Add UART driver

1. add uart.h and uart.c
2. add ESP_ERR_TIMEOUT in esp_err.h
3. add UART AHB FIFO address in uart_reg.h
4. modify xRingbufferSendFromISR return value in ringbuffer.c
5. add #include "soc/gpio_sig_map.h" in gpio.h

See merge request !146
This commit is contained in:
Jeroen Domburg 2016-11-10 18:11:21 +08:00
commit eb067ce908
9 changed files with 2174 additions and 353 deletions

View File

@ -18,34 +18,13 @@
#include "freertos/xtensa_api.h"
#include "driver/gpio.h"
#include "soc/soc.h"
#include "esp_log.h"
//TODO: move debug options to menuconfig
#define GPIO_DBG_ENABLE (0)
#define GPIO_WARNING_ENABLE (0)
#define GPIO_ERROR_ENABLE (0)
#define GPIO_INFO_ENABLE (0)
//DBG INFOR
#if GPIO_INFO_ENABLE
#define GPIO_INFO ets_printf
#else
#define GPIO_INFO(...)
#endif
#if GPIO_WARNING_ENABLE
#define GPIO_WARNING(format,...) do{\
ets_printf("[waring][%s#%u]",__FUNCTION__,__LINE__);\
ets_printf(format,##__VA_ARGS__);\
}while(0)
#else
#define GPIO_WARNING(...)
#endif
#if GPIO_ERROR_ENABLE
#define GPIO_ERROR(format,...) do{\
ets_printf("[error][%s#%u]",__FUNCTION__,__LINE__);\
ets_printf(format,##__VA_ARGS__);\
}while(0)
#else
#define GPIO_ERROR(...)
#endif
static const char* GPIO_TAG = "GPIO";
#define GPIO_CHECK(a, str, ret_val) if (!(a)) { \
ESP_LOGE(GPIO_TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
return (ret_val); \
}
const uint32_t GPIO_PIN_MUX_REG[GPIO_PIN_COUNT] = {
GPIO_PIN_REG_0,
@ -90,33 +69,17 @@ const uint32_t GPIO_PIN_MUX_REG[GPIO_PIN_COUNT] = {
GPIO_PIN_REG_39
};
static int is_valid_gpio(int gpio_num)
{
if(gpio_num >= GPIO_PIN_COUNT || GPIO_PIN_MUX_REG[gpio_num] == 0) {
GPIO_ERROR("GPIO io_num=%d does not exist\n",gpio_num);
return 0;
}
return 1;
}
esp_err_t gpio_set_intr_type(gpio_num_t gpio_num, gpio_int_type_t intr_type)
{
if(!is_valid_gpio(gpio_num)) {
return ESP_ERR_INVALID_ARG;
}
if(intr_type >= GPIO_INTR_MAX) {
GPIO_ERROR("Unknown GPIO intr:%u\n",intr_type);
return ESP_ERR_INVALID_ARG;
}
GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG);
GPIO_CHECK(intr_type < GPIO_INTR_MAX, "GPIO interrupt type error", ESP_ERR_INVALID_ARG);
GPIO.pin[gpio_num].int_type = intr_type;
return ESP_OK;
}
esp_err_t gpio_intr_enable(gpio_num_t gpio_num)
{
if(!is_valid_gpio(gpio_num)) {
return ESP_ERR_INVALID_ARG;
}
GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG);
if(xPortGetCoreID() == 0) {
GPIO.pin[gpio_num].int_ena = GPIO_PRO_CPU_INTR_ENA; //enable pro cpu intr
} else {
@ -127,18 +90,14 @@ esp_err_t gpio_intr_enable(gpio_num_t gpio_num)
esp_err_t gpio_intr_disable(gpio_num_t gpio_num)
{
if(!is_valid_gpio(gpio_num)) {
return ESP_ERR_INVALID_ARG;
}
GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG);
GPIO.pin[gpio_num].int_ena = 0; //disable GPIO intr
return ESP_OK;
}
static esp_err_t gpio_output_disable(gpio_num_t gpio_num)
{
if(!is_valid_gpio(gpio_num)) {
return ESP_ERR_INVALID_ARG;
}
GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG);
if(gpio_num < 32) {
GPIO.enable_w1tc = (0x1 << gpio_num);
} else {
@ -149,13 +108,7 @@ static esp_err_t gpio_output_disable(gpio_num_t gpio_num)
static esp_err_t gpio_output_enable(gpio_num_t gpio_num)
{
if(gpio_num >= 34) {
GPIO_ERROR("io_num=%d can only be input\n",gpio_num);
return ESP_ERR_INVALID_ARG;
}
if(!is_valid_gpio(gpio_num)) {
return ESP_ERR_INVALID_ARG;
}
GPIO_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "GPIO output gpio_num error", ESP_ERR_INVALID_ARG);
if(gpio_num < 32) {
GPIO.enable_w1ts = (0x1 << gpio_num);
} else {
@ -166,9 +119,7 @@ static esp_err_t gpio_output_enable(gpio_num_t gpio_num)
esp_err_t gpio_set_level(gpio_num_t gpio_num, uint32_t level)
{
if(!GPIO_IS_VALID_GPIO(gpio_num)) {
return ESP_ERR_INVALID_ARG;
}
GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG);
if(level) {
if(gpio_num < 32) {
GPIO.out_w1ts = (1 << gpio_num);
@ -196,9 +147,8 @@ int gpio_get_level(gpio_num_t gpio_num)
esp_err_t gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull)
{
if(!is_valid_gpio(gpio_num)) {
return ESP_ERR_INVALID_ARG;
}
GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG);
GPIO_CHECK(pull <= GPIO_FLOATING, "GPIO pull mode error", ESP_ERR_INVALID_ARG);
esp_err_t ret = ESP_OK;
switch(pull) {
case GPIO_PULLUP_ONLY:
@ -218,7 +168,7 @@ esp_err_t gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull)
PIN_PULLDWN_DIS(GPIO_PIN_MUX_REG[gpio_num]);
break;
default:
GPIO_ERROR("Unknown pull up/down mode,gpio_num=%u,pull=%u\n",gpio_num,pull);
ESP_LOGE(GPIO_TAG, "Unknown pull up/down mode,gpio_num=%u,pull=%u",gpio_num,pull);
ret = ESP_ERR_INVALID_ARG;
break;
}
@ -227,11 +177,9 @@ esp_err_t gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull)
esp_err_t gpio_set_direction(gpio_num_t gpio_num, gpio_mode_t mode)
{
if(!is_valid_gpio(gpio_num)) {
return ESP_ERR_INVALID_ARG;
}
GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG);
if(gpio_num >= 34 && (mode & (GPIO_MODE_DEF_OUTPUT))) {
GPIO_ERROR("io_num=%d can only be input\n",gpio_num);
ESP_LOGE(GPIO_TAG, "io_num=%d can only be input",gpio_num);
return ESP_ERR_INVALID_ARG;
}
esp_err_t ret = ESP_OK;
@ -266,54 +214,56 @@ esp_err_t gpio_config(gpio_config_t *pGPIOConfig)
uint64_t gpio_pin_mask = (pGPIOConfig->pin_bit_mask);
uint32_t io_reg = 0;
uint32_t io_num = 0;
uint64_t bit_valid = 0;
uint8_t input_en = 0;
uint8_t output_en = 0;
uint8_t od_en = 0;
uint8_t pu_en = 0;
uint8_t pd_en = 0;
if(pGPIOConfig->pin_bit_mask == 0 || pGPIOConfig->pin_bit_mask >= (((uint64_t) 1) << GPIO_PIN_COUNT)) {
GPIO_ERROR("GPIO_PIN mask error \n");
ESP_LOGE(GPIO_TAG, "GPIO_PIN mask error ");
return ESP_ERR_INVALID_ARG;
}
if((pGPIOConfig->mode) & (GPIO_MODE_DEF_OUTPUT)) {
//GPIO 34/35/36/37/38/39 can only be used as input mode;
if((gpio_pin_mask & ( GPIO_SEL_34 | GPIO_SEL_35 | GPIO_SEL_36 | GPIO_SEL_37 | GPIO_SEL_38 | GPIO_SEL_39))) {
GPIO_ERROR("GPIO34-39 can only be used as input mode\n");
ESP_LOGE(GPIO_TAG, "GPIO34-39 can only be used as input mode");
return ESP_ERR_INVALID_ARG;
}
}
do {
io_reg = GPIO_PIN_MUX_REG[io_num];
if(((gpio_pin_mask >> io_num) & BIT(0)) && io_reg) {
GPIO_INFO("Gpio%02d |Mode:",io_num);
if((pGPIOConfig->mode) & GPIO_MODE_DEF_INPUT) {
GPIO_INFO("INPUT ");
input_en = 1;
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[io_num]);
} else {
PIN_INPUT_DISABLE(GPIO_PIN_MUX_REG[io_num]);
}
if((pGPIOConfig->mode) & GPIO_MODE_DEF_OD) {
GPIO_INFO("OD ");
od_en = 1;
GPIO.pin[io_num].pad_driver = 1; /*0x01 Open-drain */
} else {
GPIO.pin[io_num].pad_driver = 0; /*0x00 Normal gpio output */
}
if((pGPIOConfig->mode) & GPIO_MODE_DEF_OUTPUT) {
GPIO_INFO("OUTPUT ");
output_en = 1;
gpio_output_enable(io_num);
} else {
gpio_output_disable(io_num);
}
GPIO_INFO("|");
if(pGPIOConfig->pull_up_en) {
GPIO_INFO("PU ");
pu_en = 1;
PIN_PULLUP_EN(io_reg);
} else {
PIN_PULLUP_DIS(io_reg);
}
if(pGPIOConfig->pull_down_en) {
GPIO_INFO("PD ");
pd_en = 1;
PIN_PULLDWN_EN(io_reg);
} else {
PIN_PULLDWN_DIS(io_reg);
}
GPIO_INFO("Intr:%d |\n",pGPIOConfig->intr_type);
ESP_LOGI(GPIO_TAG, "GPIO[%d]| InputEn: %d| OutputEn: %d| OpenDrain: %d| Pullup: %d| Pulldown: %d| Intr:%d ", io_num, input_en, output_en, od_en, pu_en, pd_en, pGPIOConfig->intr_type);
gpio_set_intr_type(io_num, pGPIOConfig->intr_type);
if(pGPIOConfig->intr_type) {
gpio_intr_enable(io_num);
@ -321,8 +271,6 @@ esp_err_t gpio_config(gpio_config_t *pGPIOConfig)
gpio_intr_disable(io_num);
}
PIN_FUNC_SELECT(io_reg, PIN_FUNC_GPIO); /*function number 2 is GPIO_FUNC for each pin */
} else if(bit_valid && (io_reg == 0)) {
GPIO_WARNING("io_num=%d does not exist\n",io_num);
}
io_num++;
} while(io_num < GPIO_PIN_COUNT);
@ -331,9 +279,7 @@ esp_err_t gpio_config(gpio_config_t *pGPIOConfig)
esp_err_t gpio_isr_register(uint32_t gpio_intr_num, void (*fn)(void*), void * arg)
{
if(fn == NULL) {
return ESP_ERR_INVALID_ARG;
}
GPIO_CHECK(fn, "GPIO ISR null", ESP_ERR_INVALID_ARG);
ESP_INTR_DISABLE(gpio_intr_num);
intr_matrix_set(xPortGetCoreID(), ETS_GPIO_INTR_SOURCE, gpio_intr_num);
xt_set_interrupt_handler(gpio_intr_num, fn, arg);
@ -344,15 +290,13 @@ esp_err_t gpio_isr_register(uint32_t gpio_intr_num, void (*fn)(void*), void * ar
/*only level interrupt can be used for wake-up function*/
esp_err_t gpio_wakeup_enable(gpio_num_t gpio_num, gpio_int_type_t intr_type)
{
if(!is_valid_gpio(gpio_num)) {
return ESP_ERR_INVALID_ARG;
}
GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG);
esp_err_t ret = ESP_OK;
if((intr_type == GPIO_INTR_LOW_LEVEL) || (intr_type == GPIO_INTR_HIGH_LEVEL)) {
GPIO.pin[gpio_num].int_type = intr_type;
GPIO.pin[gpio_num].wakeup_enable = 0x1;
} else {
GPIO_ERROR("GPIO wakeup only support Level mode,but edge mode set. gpio_num:%u\n",gpio_num);
ESP_LOGE(GPIO_TAG, "GPIO wakeup only support Level mode,but edge mode set. gpio_num:%u",gpio_num);
ret = ESP_ERR_INVALID_ARG;
}
return ret;
@ -360,9 +304,7 @@ esp_err_t gpio_wakeup_enable(gpio_num_t gpio_num, gpio_int_type_t intr_type)
esp_err_t gpio_wakeup_disable(gpio_num_t gpio_num)
{
if(!is_valid_gpio(gpio_num)) {
return ESP_ERR_INVALID_ARG;
}
GPIO_CHECK(GPIO_IS_VALID_GPIO(gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG);
GPIO.pin[gpio_num].wakeup_enable = 0;
return ESP_OK;
}

View File

@ -20,6 +20,7 @@
#include "soc/gpio_struct.h"
#include "soc/rtc_io_reg.h"
#include "soc/io_mux_reg.h"
#include "soc/gpio_sig_map.h"
#include "rom/gpio.h"
#include "esp_attr.h"

View File

@ -0,0 +1,763 @@
// 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.
#ifndef _DRIVER_UART_H_
#define _DRIVER_UART_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "soc/uart_reg.h"
#include "soc/uart_struct.h"
#include "esp_err.h"
#include "driver/periph_ctrl.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/xtensa_api.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/ringbuf.h"
#include <esp_types.h>
#define UART_FIFO_LEN (128) /*< Length of the hardware FIFO buffers */
#define UART_INTR_MASK 0x1ff
#define UART_LINE_INV_MASK (0x3f << 19)
#define UART_BITRATE_MAX 5000000
#define UART_PIN_NO_CHANGE (-1)
#define UART_INVERSE_DISABLE (0x0) /*!< Disable UART signal inverse*/
#define UART_INVERSE_RXD (UART_RXD_INV_M) /*!< UART RXD input inverse*/
#define UART_INVERSE_CTS (UART_CTS_INV_M) /*!< UART CTS input inverse*/
#define UART_INVERSE_TXD (UART_TXD_INV_M) /*!< UART TXD output inverse*/
#define UART_INVERSE_RTS (UART_RTS_INV_M) /*!< UART RTS output inverse*/
typedef enum {
UART_DATA_5_BITS = 0x0, /*!< word length: 5bits*/
UART_DATA_6_BITS = 0x1, /*!< word length: 6bits*/
UART_DATA_7_BITS = 0x2, /*!< word length: 7bits*/
UART_DATA_8_BITS = 0x3, /*!< word length: 8bits*/
UART_DATA_BITS_MAX = 0X4,
} uart_word_length_t;
typedef enum {
UART_STOP_BITS_1 = 0x1, /*!< stop bit: 1bit*/
UART_STOP_BITS_1_5 = 0x2, /*!< stop bit: 1.5bits*/
UART_STOP_BITS_2 = 0x3, /*!< stop bit: 2bits*/
UART_STOP_BITS_MAX = 0x4,
} uart_stop_bits_t;
typedef enum {
UART_NUM_0 = 0x0, /*!< UART base address 0x3ff40000*/
UART_NUM_1 = 0x1, /*!< UART base address 0x3ff50000*/
UART_NUM_2 = 0x2, /*!< UART base address 0x3ff6E000*/
UART_NUM_MAX,
} uart_port_t;
typedef enum {
UART_PARITY_DISABLE = 0x0, /*!< Disable UART parity*/
UART_PARITY_EVEN = 0x10, /*!< Enable UART even parity*/
UART_PARITY_ODD = 0x11 /*!< Enable UART odd parity*/
} uart_parity_t;
typedef enum {
UART_HW_FLOWCTRL_DISABLE = 0x0, /*!< disable hardware flow control*/
UART_HW_FLOWCTRL_RTS = 0x1, /*!< enable RX hardware flow control (rts)*/
UART_HW_FLOWCTRL_CTS = 0x2, /*!< enable TX hardware flow control (cts)*/
UART_HW_FLOWCTRL_CTS_RTS = 0x3, /*!< enable hardware flow control*/
UART_HW_FLOWCTRL_MAX = 0x4,
} uart_hw_flowcontrol_t;
typedef struct {
int baud_rate; /*!< UART baudrate*/
uart_word_length_t data_bits; /*!< UART byte size*/
uart_parity_t parity; /*!< UART parity mode*/
uart_stop_bits_t stop_bits; /*!< UART stop bits*/
uart_hw_flowcontrol_t flow_ctrl; /*!< UART HW flow control mode(cts/rts)*/
uint8_t rx_flow_ctrl_thresh ; /*!< UART HW RTS threshold*/
} uart_config_t;
typedef struct {
uint32_t intr_enable_mask; /*!< UART interrupt enable mask, choose from UART_XXXX_INT_ENA_M under UART_INT_ENA_REG(i), connect with bit-or operator*/
uint8_t rx_timeout_thresh; /*!< UART timeout interrupt threshold(unit: time of sending one byte)*/
uint8_t txfifo_empty_intr_thresh; /*!< UART TX empty interrupt threshold.*/
uint8_t rxfifo_full_thresh; /*!< UART RX full interrupt threshold.*/
} uart_intr_config_t;
typedef enum {
UART_DATA, /*!< UART data event*/
UART_BREAK, /*!< UART break event*/
UART_BUFFER_FULL, /*!< UART RX buffer full event*/
UART_FIFO_OVF, /*!< UART FIFO overflow event*/
UART_FRAME_ERR, /*!< UART RX frame error event*/
UART_PARITY_ERR, /*!< UART RX parity event*/
UART_DATA_BREAK, /*!< UART TX data and break event*/
UART_EVENT_MAX, /*!< UART event max index*/
} uart_event_type_t;
typedef struct {
uart_event_type_t type; /*!< UART event type */
size_t size; /*!< UART data size for UART_DATA event*/
} uart_event_t;
/**
* @brief Set UART data bits.
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param data_bit UART data bits
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_set_word_length(uart_port_t uart_num, uart_word_length_t data_bit);
/**
* @brief Get UART data bits.
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @return
* - ESP_FAIL Parameter error
* - ESP_OK Success, result will be put in (*data_bit)
*/
esp_err_t uart_get_word_length(uart_port_t uart_num, uart_word_length_t* data_bit);
/**
* @brief Set UART stop bits.
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param bit_num UART stop bits
*
* @return
* - ESP_OK Success
* - ESP_FAIL Fail
*/
esp_err_t uart_set_stop_bits(uart_port_t uart_no, uart_stop_bits_t bit_num);
/**
* @brief Set UART stop bits.
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @return
* - ESP_FAIL Parameter error
* - ESP_OK Success, result will be put in (*stop_bit)
*/
esp_err_t uart_get_stop_bits(uart_port_t uart_num, uart_stop_bits_t* stop_bit);
/**
* @brief Set UART parity.
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param parity_mode the enum of uart parity configuration
*
* @return
* - ESP_FAIL Parameter error
* - ESP_OK Success
*/
esp_err_t uart_set_parity(uart_port_t uart_no, uart_parity_t parity_mode);
/**
* @brief Get UART parity mode.
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @return
* - ESP_FAIL Parameter error
* - ESP_OK Success, result will be put in (*parity_mode)
*
*/
esp_err_t uart_get_parity(uart_port_t uart_num, uart_parity_t* parity_mode);
/**
* @brief Set UART baud rate.
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param baud_rate UART baud-rate.
*
* @return
* - ESP_FAIL Parameter error
* - ESP_OK Success
*/
esp_err_t uart_set_baudrate(uart_port_t uart_no, uint32_t baud_rate);
/**
* @brief Get UART bit-rate.
*
* @param uart_no: UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @return
* - ESP_FAIL Parameter error
* - ESP_OK Success, result will be put in (*baudrate)
*
*/
esp_err_t uart_get_baudrate(uart_port_t uart_num, uint32_t* baudrate);
/**
* @brief Set UART line inverse mode
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param inverse_mask Choose the wires that need to be inversed.
*
* (inverse_mask should be chosen from UART_INVERSE_RXD/UART_INVERSE_TXD/UART_INVERSE_RTS/UART_INVERSE_CTS, combine with OR-OPERATION)
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_set_line_inverse(uart_port_t uart_no, uint32_t inverse_mask);
/**
* @brief Set hardware flow control.
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param flow_ctrl Hardware flow control mode
*
* @param rx_thresh Threshold of Hardware RX flow control(0 ~ UART_FIFO_LEN)
*
* Only when UART_HW_FLOWCTRL_RTS is set , will the rx_thresh value be set.
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_set_hw_flow_ctrl(uart_port_t uart_no, uart_hw_flowcontrol_t flow_ctrl, uint8_t rx_thresh);
/**
* @brief Get hardware flow control mode
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @return
* - ESP_FAIL Parameter error
* - ESP_OK Success, result will be put in (*flow_ctrl)
*/
esp_err_t uart_get_hw_flow_ctrl(uart_port_t uart_num, uart_hw_flowcontrol_t* flow_ctrl);
/**
* @brief Clear UART interrupt status
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param clr_mask Bit mask of the status that to be cleared.
*
* (enable_mask should be chosen from the fields of register UART_INT_CLR_REG)
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_clear_intr_status(uart_port_t uart_num, uint32_t clr_mask);
/**
* @brief Set UART interrupt enable
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param enable_mask Bit mask of the enable bits.
*
* (enable_mask should be chosen from the fields of register UART_INT_ENA_REG)
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_enable_intr_mask(uart_port_t uart_num, uint32_t enable_mask);
/**
* @brief Clear UART interrupt enable bits
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param disable_mask Bit mask of the disable bits.
*
* (disable_mask should be chosen from the fields of register UART_INT_ENA_REG)
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_disable_intr_mask(uart_port_t uart_num, uint32_t disable_mask);
/**
* @brief Enable UART RX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT)
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_enable_rx_intr(uart_port_t uart_num);
/**
* @brief Disable UART RX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT)
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_disable_rx_intr(uart_port_t uart_num);
/**
* @brief Disable UART TX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT)
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_disable_tx_intr(uart_port_t uart_num);
/**
* @brief Enable UART TX interrupt(RX_FULL & RX_TIMEOUT INTERRUPT)
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param enable 1: enable; 0: disable
*
* @param thresh Threshold of TX interrupt, 0 ~ UART_FIFO_LEN
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_enable_tx_intr(uart_port_t uart_num, int enable, int thresh);
/**
* @brief register UART interrupt handler(ISR).
* @note
* UART ISR handler will be attached to the same CPU core that this function is running on.
* Users should know that which CPU is running and then pick a INUM that is not used by system.
* We can find the information of INUM and interrupt level in soc.h.
*
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param uart_intr_num UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details
*
* @param fn Interrupt handler function.
* @attention
* The ISR handler function MUST be defined with attribution of "IRAM_ATTR" for now.
* @param arg parameter for handler function
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_isr_register(uart_port_t uart_num, uint8_t uart_intr_num, void (*fn)(void*), void * arg);
/**
* @brief Set UART pin number
*
* @note
* Internal signal can be output to multiple GPIO pads
* Only one GPIO pad can connect with input signal
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param tx_io_num UART TX pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin.
*
* @param rx_io_num UART RX pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin.
*
* @param rts_io_num UART RTS pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin.
*
* @param cts_io_num UART CTS pin GPIO number, if set to UART_PIN_NO_CHANGE, use the current pin.
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int rts_io_num, int cts_io_num);
/**
* @brief UART set RTS level (before inverse)
* UART rx hardware flow control should not be set.
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param level 1: RTS output low(active); 0: RTS output high(block)
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_set_rts(uart_port_t uart_num, int level);
/**
* @brief UART set DTR level (before inverse)
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param level 1: DTR output low; 0: DTR output high
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_set_dtr(uart_port_t uart_num, int level);
/**
* @brief UART parameter configure
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param uart_config UART parameter settings
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_param_config(uart_port_t uart_num, const uart_config_t *uart_config);
/**
* @brief UART interrupt configure
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param intr_conf UART interrupt settings
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_intr_config(uart_port_t uart_num, const uart_intr_config_t *intr_conf);
/**
* @brief Install UART driver.
*
* UART ISR handler will be attached to the same CPU core that this function is running on.
* Users should know that which CPU is running and then pick a INUM that is not used by system.
* We can find the information of INUM and interrupt level in soc.h.
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param rx_buffer_size UART RX ring buffer size
*
* @param tx_buffer_size UART TX ring buffer size.
*
* If set to zero, driver will not use TX buffer, TX function will block task until all data have been sent out..
*
* @param queue_size UART event queue size/depth.
*
* @param uart_intr_num UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details
*
* @param uart_queue UART event queue handle, if set NULL, driver will not use an event queue.
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, int uart_intr_num, void* uart_queue);
/**
* @brief Uninstall UART driver.
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_driver_delete(uart_port_t uart_num);
/**
* @brief Wait UART TX FIFO empty
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param ticks_to_wait Timeout, count in RTOS ticks
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
* - ESP_ERR_TIMEOUT Timeout
*/
esp_err_t uart_wait_tx_done(uart_port_t uart_num, TickType_t ticks_to_wait);
/**
* @brief Send data to the UART port from a given buffer and length,
* This function will not wait for the space in TX FIFO, just fill the TX FIFO and return when the FIFO is full.
* @note
* This function should only be used when UART TX buffer is not enabled.
*
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param buffer data buffer address
*
* @param len data length to send
*
* @return
* - (-1) Parameter error
* - OTHERS(>=0) The number of data that pushed to the TX FIFO
*/
int uart_tx_chars(uart_port_t uart_no, const char* buffer, uint32_t len);
/**
* @brief Send data to the UART port from a given buffer and length,
*
* If parameter tx_buffer_size is set to zero:
* This function will not return until all the data have been sent out, or at least pushed into TX FIFO.
*
* Otherwise, if tx_buffer_size > 0, this function will return after copying all the data to tx ringbuffer,
* then, UART ISR will move data from ring buffer to TX FIFO gradually.
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param src data buffer address
*
* @param size data length to send
*
* @return
* - (-1) Parameter error
* - OTHERS(>=0) The number of data that pushed to the TX FIFO
*/
int uart_write_bytes(uart_port_t uart_num, const char* src, size_t size);
/**
* @brief Send data to the UART port from a given buffer and length,
*
* If parameter tx_buffer_size is set to zero:
* This function will not return until all the data and the break signal have been sent out.
* After all data send out, send a break signal.
*
* Otherwise, if tx_buffer_size > 0, this function will return after copying all the data to tx ringbuffer,
* then, UART ISR will move data from ring buffer to TX FIFO gradually.
* After all data send out, send a break signal.
*
*
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param src data buffer address
*
* @param size data length to send
*
* @param brk_len break signal length (unit: one bit's time@current_baudrate)
*
* @return
* - (-1) Parameter error
* - OTHERS(>=0) The number of data that pushed to the TX FIFO
*/
int uart_write_bytes_with_break(uart_port_t uart_num, const char* src, size_t size, int brk_len);
/**
* @brief UART read bytes from UART buffer
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @param buf pointer to the buffer.
*
* @param length data length
*
* @param ticks_to_wait sTimeout, count in RTOS ticks
*
*
* @return
* - (-1) Error
* - Others return a char data from uart fifo.
*/
int uart_read_bytes(uart_port_t uart_num, uint8_t* buf, uint32_t length, TickType_t ticks_to_wait);
/**
* @brief UART ring buffer flush
*
* @param uart_no UART_NUM_0, UART_NUM_1 or UART_NUM_2
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t uart_flush(uart_port_t uart_num);
/***************************EXAMPLE**********************************
*
*
* ----------------EXAMPLE OF UART SETTING ---------------------
* @code{c}
* //1. Setup UART
* #include "freertos/queue.h"
* #define UART_INTR_NUM 17 //choose one interrupt number from soc.h
* //a. Set UART parameter
* int uart_num = 0; //uart port number
* uart_config_t uart_config = {
* .baud_rate = UART_BITRATE_115200, //baudrate
* .data_bits = UART_DATA_8_BITS, //data bit mode
* .parity = UART_PARITY_DISABLE, //parity mode
* .stop_bits = UART_STOP_BITS_1, //stop bit mode
* .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, //hardware flow control(cts/rts)
* .rx_flow_ctrl_thresh = 120, //flow control threshold
* };
* uart_param_config(uart_num, &uart_config);
* //b1. Setup UART driver(with UART queue)
* QueueHandle_t uart_queue;
* //parameters here are just an example, tx buffer size is 2048
* uart_driver_install(uart_num, 1024 * 2, 1024 * 2, 10, UART_INTR_NUM, &uart_queue);
* //b2. Setup UART driver(without UART queue)
* //parameters here are just an example, tx buffer size is 0
* uart_driver_install(uart_num, 1024 * 2, 0, 10, UART_INTR_NUM, NULL);
*@endcode
*-----------------------------------------------------------------------------*
* @code{c}
* //2. Set UART pin
* //set UART pin, not needed if use default pins.
* uart_set_pin(uart_num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, 15, 13);
* @endcode
*-----------------------------------------------------------------------------*
* @code{c}
* //3. Read data from UART.
* uint8_t data[128];
* int length = 0;
* length = uart_read_bytes(uart_num, data, sizeof(data), 100);
* @endcode
*-----------------------------------------------------------------------------*
* @code{c}
* //4. Write data to UART.
* char* test_str = "This is a test string.\n"
* uart_write_bytes(uart_num, (const char*)test_str, strlen(test_str));
* @endcode
*-----------------------------------------------------------------------------*
* @code{c}
* //5. Write data to UART, end with a break signal.
* uart_write_bytes_with_break(0, "test break\n",strlen("test break\n"), 100);
* @endcode
*-----------------------------------------------------------------------------*
* @code{c}
* //6. an example of echo test with hardware flow control on UART1
* void uart_loop_back_test()
* {
* int uart_num = 1;
* uart_config_t uart_config = {
* .baud_rate = 115200,
* .data_bits = UART_DATA_8_BITS,
* .parity = UART_PARITY_DISABLE,
* .stop_bits = UART_STOP_BITS_1,
* .flow_ctrl = UART_HW_FLOWCTRL_CTS_RTS,
* .rx_flow_ctrl_thresh = 122,
* };
* //Configure UART1 parameters
* uart_param_config(uart_num, &uart_config);
* //Set UART1 pins(TX: IO16, RX: IO17, RTS: IO18, CTS: IO19)
* uart_set_pin(uart_num, 16, 17, 18, 19);
* //Install UART driver( We don't need an event queue here)
* uart_driver_install(uart_num, 1024 * 2, 1024*4, 10, 17, NULL, RINGBUF_TYPE_BYTEBUF);
* uint8_t data[1000];
* while(1) {
* //Read data from UART
* int len = uart_read_bytes(uart_num, data, sizeof(data), 10);
* //Write data back to UART
* uart_write_bytes(uart_num, (const char*)data, len);
* }
* }
* @endcode
*-----------------------------------------------------------------------------*
* @code{c}
* //7. An example of using UART event queue on UART0.
* #include "freertos/queue.h"
* //A queue to handle UART event.
* QueueHandle_t uart0_queue;
* static const char *TAG = "uart_example";
* void uart_task(void *pvParameters)
* {
* int uart_num = (int)pvParameters;
* uart_event_t event;
* uint8_t dtmp[1000];
* for(;;) {
* //Waiting for UART event.
* if(xQueueReceive(uart0_queue, (void * )&event, (portTickType)portMAX_DELAY)) {
* ESP_LOGI(TAG, "uart[%d] event:", uart_num);
* switch(event.type) {
* memset(dtmp, 0, sizeof(dtmp));
* //Event of UART receving data
* case UART_DATA:
* ESP_LOGI(TAG,"data, len: %d", event.size);
* int len = uart_read_bytes(uart_num, dtmp, event.size, 10);
* ESP_LOGI(TAG, "uart read: %d", len);
uart_write_bytes(uart_num, (const char*)dtmp, len);
* break;
* //Event of HW FIFO overflow detected
* case UART_FIFO_OVF:
* ESP_LOGI(TAG, "hw fifo overflow\n");
* break;
* //Event of UART ring buffer full
* case UART_BUFFER_FULL:
* ESP_LOGI(TAG, "ring buffer full\n");
* break;
* //Event of UART RX break detected
* case UART_BREAK:
* ESP_LOGI(TAG, "uart rx break\n");
* break;
* //Event of UART parity check error
* case UART_PARITY_ERR:
* ESP_LOGI(TAG, "uart parity error\n");
* break;
* //Event of UART frame error
* case UART_FRAME_ERR:
* ESP_LOGI(TAG, "uart frame error\n");
* break;
* //Others
* default:
* ESP_LOGI(TAG, "uart event type: %d\n", event.type);
* break;
* }
* }
* }
* vTaskDelete(NULL);
* }
*
* void uart_queue_test()
* {
* int uart_num = 0;
* uart_config_t uart_config = {
* .baud_rate = 115200,
* .data_bits = UART_DATA_8_BITS,
* .parity = UART_PARITY_DISABLE,
* .stop_bits = UART_STOP_BITS_1,
* .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
* .rx_flow_ctrl_thresh = 122,
* };
* //Set UART parameters
* uart_param_config(uart_num, &uart_config);
* //Set UART pins,(-1: default pin, no change.)
* uart_set_pin(uart_num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, 15, 13);
* //Set UART log level
* esp_log_level_set(TAG, ESP_LOG_INFO);
* //Install UART driver, and get the queue.
* uart_driver_install(uart_num, 1024 * 2, 1024*4, 10, 17, &uart0_queue, RINGBUF_TYPE_BYTEBUF);
* //Create a task to handler UART event from ISR
* xTaskCreate(uart_task, "uTask", 2048*8, (void*)uart_num, 10, NULL);
* }
* @endcode
*
***************************END OF EXAMPLE**********************************/
#ifdef __cplusplus
}
#endif
#endif /*_DRIVER_UART_H_*/

View File

@ -18,86 +18,19 @@
#include "freertos/xtensa_api.h"
#include "soc/gpio_sig_map.h"
#include "driver/ledc.h"
#include "esp_log.h"
//TODO: to use APIs in esp_log.h.
#define LEDC_DBG_WARING_ENABLE (0)
#define LEDC_DBG_ERROR_ENABLE (0)
#define LEDC_INFO_ENABLE (0)
#define LEDC_DBG_ENABLE (0)
//DBG INFOR
#if LEDC_DBG_ENABLE
#define LEDC_DBG(format,...) do{\
ets_printf("[dbg][%s#%u]",__FUNCTION__,__LINE__);\
ets_printf(format,##__VA_ARGS__);\
}while(0)
#else
#define LEDC_DBG(...)
#endif
#if LEDC_INFO_ENABLE
#define LEDC_INFO(format,...) do{\
ets_printf("[info][%s#%u]",__FUNCTION__,__LINE__);\
ets_printf(format,##__VA_ARGS__);\
}while(0)
#else
#define LEDC_INFO(...)
#endif
#if LEDC_DBG_WARING_ENABLE
#define LEDC_WARING(format,...) do{\
ets_printf("[waring][%s#%u]",__FUNCTION__,__LINE__);\
ets_printf(format,##__VA_ARGS__);\
}while(0)
#else
#define LEDC_WARING(...)
#endif
#if LEDC_DBG_ERROR_ENABLE
#define LEDC_ERROR(format,...) do{\
ets_printf("[error][%s#%u]",__FUNCTION__,__LINE__);\
ets_printf(format,##__VA_ARGS__);\
}while(0)
#else
#define LEDC_ERROR(...)
#endif
static const char* LEDC_TAG = "LEDC";
static portMUX_TYPE ledc_spinlock = portMUX_INITIALIZER_UNLOCKED;
static bool ledc_is_valid_channel(uint32_t channel)
{
if(channel > LEDC_CHANNEL_7) {
LEDC_ERROR("LEDC CHANNEL ERR: %d\n",channel);
return false;
}
return true;
}
static bool ledc_is_valid_mode(uint32_t mode)
{
if(mode >= LEDC_SPEED_MODE_MAX) {
LEDC_ERROR("LEDC MODE ERR: %d\n",mode);
return false;
}
return true;
}
static bool ledc_is_valid_timer(int timer)
{
if(timer > LEDC_TIMER_3) {
LEDC_ERROR("LEDC TIMER ERR: %d\n", timer);
return false;
}
return true;
}
#define LEDC_CHECK(a, str, ret_val) if (!(a)) { \
ESP_LOGE(LEDC_TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
return (ret_val); \
}
esp_err_t ledc_timer_set(ledc_mode_t speed_mode, ledc_timer_t timer_sel, uint32_t div_num, uint32_t bit_num, ledc_clk_src_t clk_src)
{
if(!ledc_is_valid_mode(speed_mode)) {
return ESP_ERR_INVALID_ARG;
}
if(!ledc_is_valid_timer(timer_sel)) {
return ESP_ERR_INVALID_ARG;
}
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG);
LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG);
portENTER_CRITICAL(&ledc_spinlock);
LEDC.timer_group[speed_mode].timer[timer_sel].conf.div_num = div_num;
LEDC.timer_group[speed_mode].timer[timer_sel].conf.tick_sel = clk_src;
@ -125,12 +58,8 @@ static esp_err_t ledc_duty_config(ledc_mode_t speed_mode, uint32_t channel_num,
esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, uint32_t channel, uint32_t timer_idx)
{
if(!ledc_is_valid_mode(speed_mode)) {
return ESP_ERR_INVALID_ARG;
}
if(!ledc_is_valid_timer(timer_idx)) {
return ESP_ERR_INVALID_ARG;
}
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG);
LEDC_CHECK(timer_idx <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG);
portENTER_CRITICAL(&ledc_spinlock);
LEDC.channel_group[speed_mode].channel[channel].conf0.timer_sel = timer_idx;
portEXIT_CRITICAL(&ledc_spinlock);
@ -139,12 +68,8 @@ esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, uint32_t channel, uint
esp_err_t ledc_timer_rst(ledc_mode_t speed_mode, uint32_t timer_sel)
{
if(!ledc_is_valid_mode(speed_mode)) {
return ESP_ERR_INVALID_ARG;
}
if(!ledc_is_valid_timer(timer_sel)) {
return ESP_ERR_INVALID_ARG;
}
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG);
LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG);
portENTER_CRITICAL(&ledc_spinlock);
LEDC.timer_group[speed_mode].timer[timer_sel].conf.rst = 1;
LEDC.timer_group[speed_mode].timer[timer_sel].conf.rst = 0;
@ -154,12 +79,8 @@ esp_err_t ledc_timer_rst(ledc_mode_t speed_mode, uint32_t timer_sel)
esp_err_t ledc_timer_pause(ledc_mode_t speed_mode, uint32_t timer_sel)
{
if(!ledc_is_valid_mode(speed_mode)) {
return ESP_ERR_INVALID_ARG;
}
if(!ledc_is_valid_timer(timer_sel)) {
return ESP_ERR_INVALID_ARG;
}
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG);
LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG);
portENTER_CRITICAL(&ledc_spinlock);
LEDC.timer_group[speed_mode].timer[timer_sel].conf.pause = 1;
portEXIT_CRITICAL(&ledc_spinlock);
@ -168,12 +89,8 @@ esp_err_t ledc_timer_pause(ledc_mode_t speed_mode, uint32_t timer_sel)
esp_err_t ledc_timer_resume(ledc_mode_t speed_mode, uint32_t timer_sel)
{
if(!ledc_is_valid_mode(speed_mode)) {
return ESP_ERR_INVALID_ARG;
}
if(!ledc_is_valid_timer(timer_sel)) {
return ESP_ERR_INVALID_ARG;
}
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG);
LEDC_CHECK(timer_sel <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG);
portENTER_CRITICAL(&ledc_spinlock);
LEDC.timer_group[speed_mode].timer[timer_sel].conf.pause = 0;
portEXIT_CRITICAL(&ledc_spinlock);
@ -182,9 +99,7 @@ esp_err_t ledc_timer_resume(ledc_mode_t speed_mode, uint32_t timer_sel)
static esp_err_t ledc_enable_intr_type(ledc_mode_t speed_mode, uint32_t channel, ledc_intr_type_t type)
{
if(!ledc_is_valid_mode(speed_mode)) {
return ESP_ERR_INVALID_ARG;
}
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG);
uint32_t value;
uint32_t intr_type = type;
portENTER_CRITICAL(&ledc_spinlock);
@ -200,9 +115,7 @@ static esp_err_t ledc_enable_intr_type(ledc_mode_t speed_mode, uint32_t channel,
esp_err_t ledc_isr_register(uint32_t ledc_intr_num, void (*fn)(void*), void * arg)
{
if(fn == NULL) {
return ESP_ERR_INVALID_ARG;
}
LEDC_CHECK(fn, "ledc isr null", ESP_ERR_INVALID_ARG);
portENTER_CRITICAL(&ledc_spinlock);
ESP_INTR_DISABLE(ledc_intr_num);
intr_matrix_set(xPortGetCoreID(), ETS_LEDC_INTR_SOURCE, ledc_intr_num);
@ -218,16 +131,13 @@ esp_err_t ledc_timer_config(ledc_timer_config_t* timer_conf)
int bit_num = timer_conf->bit_num;
int timer_num = timer_conf->timer_num;
int speed_mode = timer_conf->speed_mode;
if(!ledc_is_valid_mode(speed_mode)) {
return ESP_ERR_INVALID_ARG;
}
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG);
if(freq_hz == 0 || bit_num == 0 || bit_num > LEDC_TIMER_15_BIT) {
LEDC_ERROR("freq_hz=%u bit_num=%u\n", freq_hz, bit_num);
ESP_LOGE(LEDC_TAG, "freq_hz=%u bit_num=%u", freq_hz, bit_num);
return ESP_ERR_INVALID_ARG;
}
if(timer_num > LEDC_TIMER_3) {
LEDC_ERROR("Time Select %u\n", timer_num);
ESP_LOGE(LEDC_TAG, "Time Select %u", timer_num);
return ESP_ERR_INVALID_ARG;
}
esp_err_t ret = ESP_OK;
@ -239,7 +149,7 @@ esp_err_t ledc_timer_config(ledc_timer_config_t* timer_conf)
/*Selet the reference tick*/
div_param = ((uint64_t) LEDC_REF_CLK_HZ << 8) / freq_hz / precision;
if(div_param <= 256 || div_param > LEDC_DIV_NUM_HSTIMER0_V) {
LEDC_ERROR("div param err,div_param=%u\n", div_param);
ESP_LOGE(LEDC_TAG, "div param err,div_param=%u", (uint32_t)div_param);
ret = ESP_FAIL;
}
timer_clk_src = LEDC_REF_TICK;
@ -254,6 +164,21 @@ esp_err_t ledc_timer_config(ledc_timer_config_t* timer_conf)
return ret;
}
esp_err_t ledc_set_pin(int gpio_num, ledc_mode_t speed_mode, ledc_channel_t ledc_channel)
{
LEDC_CHECK(ledc_channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG);
LEDC_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "ledc GPIO output number error", ESP_ERR_INVALID_ARG);
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG);
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[gpio_num], PIN_FUNC_GPIO);
gpio_set_direction(gpio_num, GPIO_MODE_OUTPUT);
if(speed_mode == LEDC_HIGH_SPEED_MODE) {
gpio_matrix_out(gpio_num, LEDC_HS_SIG_OUT0_IDX + ledc_channel, 0, 0);
} else {
}
return ESP_OK;
}
esp_err_t ledc_channel_config(ledc_channel_config_t* ledc_conf)
{
uint32_t speed_mode = ledc_conf->speed_mode;
@ -262,21 +187,10 @@ esp_err_t ledc_channel_config(ledc_channel_config_t* ledc_conf)
uint32_t timer_select = ledc_conf->timer_sel;
uint32_t intr_type = ledc_conf->intr_type;
uint32_t duty = ledc_conf->duty;
if(!ledc_is_valid_channel(ledc_channel)) {
return ESP_ERR_INVALID_ARG;
}
if(!ledc_is_valid_mode(speed_mode)) {
return ESP_ERR_INVALID_ARG;
}
if(!GPIO_IS_VALID_OUTPUT_GPIO(gpio_num)) {
LEDC_ERROR("GPIO number error: IO%d\n ", gpio_num);
return ESP_ERR_INVALID_ARG;
}
if(timer_select > LEDC_TIMER_3) {
LEDC_ERROR("Time Select %u\n", timer_select);
return ESP_ERR_INVALID_ARG;
}
LEDC_CHECK(ledc_channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG);
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG);
LEDC_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), "ledc GPIO output number error", ESP_ERR_INVALID_ARG);
LEDC_CHECK(timer_select <= LEDC_TIMER_3, "ledc timer error", ESP_ERR_INVALID_ARG);
esp_err_t ret = ESP_OK;
/*set channel parameters*/
/* channel parameters decide how the waveform looks like in one period*/
@ -288,7 +202,7 @@ esp_err_t ledc_channel_config(ledc_channel_config_t* ledc_conf)
ledc_bind_channel_timer(speed_mode, ledc_channel, timer_select);
/*set interrupt type*/
ledc_enable_intr_type(speed_mode, ledc_channel, intr_type);
LEDC_INFO("LEDC_PWM CHANNEL %1u|GPIO %02u|Duty %04u|Time %01u\n",
ESP_LOGI(LEDC_TAG, "LEDC_PWM CHANNEL %1u|GPIO %02u|Duty %04u|Time %01u",
ledc_channel, gpio_num, duty, timer_select
);
/*set LEDC signal in gpio matrix*/
@ -300,12 +214,8 @@ esp_err_t ledc_channel_config(ledc_channel_config_t* ledc_conf)
esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel)
{
if(!ledc_is_valid_mode(speed_mode)) {
return ESP_ERR_INVALID_ARG;
}
if(!ledc_is_valid_channel(channel)) {
return ESP_ERR_INVALID_ARG;
}
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG);
LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG);
portENTER_CRITICAL(&ledc_spinlock);
LEDC.channel_group[speed_mode].channel[channel].conf0.sig_out_en = 1;
LEDC.channel_group[speed_mode].channel[channel].conf1.duty_start = 1;
@ -315,12 +225,8 @@ esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel)
esp_err_t ledc_stop(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t idle_level)
{
if(!ledc_is_valid_mode(speed_mode)) {
return ESP_ERR_INVALID_ARG;
}
if(!ledc_is_valid_channel(channel)) {
return ESP_ERR_INVALID_ARG;
}
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG);
LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG);
portENTER_CRITICAL(&ledc_spinlock);
LEDC.channel_group[speed_mode].channel[channel].conf0.idle_lv = idle_level & 0x1;
LEDC.channel_group[speed_mode].channel[channel].conf0.sig_out_en = 0;
@ -331,18 +237,11 @@ esp_err_t ledc_stop(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t idl
esp_err_t ledc_set_fade(ledc_mode_t speed_mode, uint32_t channel, uint32_t duty, ledc_duty_direction_t fade_direction,
uint32_t step_num, uint32_t duty_cyle_num, uint32_t duty_scale)
{
if(!ledc_is_valid_mode(speed_mode)) {
return ESP_ERR_INVALID_ARG;
}
if(!ledc_is_valid_channel(channel)) {
return ESP_ERR_INVALID_ARG;
}
if(fade_direction > LEDC_DUTY_DIR_INCREASE) {
LEDC_ERROR("Duty direction err\n");
return ESP_ERR_INVALID_ARG;
}
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG);
LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG);
LEDC_CHECK(fade_direction <= LEDC_DUTY_DIR_INCREASE, "ledc fade direction error", ESP_ERR_INVALID_ARG);
if(step_num > LEDC_DUTY_NUM_HSCH0_V || duty_cyle_num > LEDC_DUTY_CYCLE_HSCH0_V || duty_scale > LEDC_DUTY_SCALE_HSCH0_V) {
LEDC_ERROR("step_num=%u duty_cyle_num=%u duty_scale=%u\n", step_num, duty_cyle_num, duty_scale);
ESP_LOGE(LEDC_TAG, "step_num=%u duty_cyle_num=%u duty_scale=%u", step_num, duty_cyle_num, duty_scale);
return ESP_ERR_INVALID_ARG;
}
ledc_duty_config(speed_mode,
@ -359,12 +258,8 @@ esp_err_t ledc_set_fade(ledc_mode_t speed_mode, uint32_t channel, uint32_t duty,
esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t duty)
{
if(!ledc_is_valid_mode(speed_mode)) {
return ESP_ERR_INVALID_ARG;
}
if(!ledc_is_valid_channel(channel)) {
return ESP_ERR_INVALID_ARG;
}
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG);
LEDC_CHECK(channel <= LEDC_CHANNEL_7, "ledc channel error", ESP_ERR_INVALID_ARG);
ledc_duty_config(speed_mode,
channel, //uint32_t chan_num,
0, //uint32_t hpoint_val,
@ -379,18 +274,14 @@ esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t
int ledc_get_duty(ledc_mode_t speed_mode, ledc_channel_t channel)
{
if(!ledc_is_valid_mode(speed_mode)) {
return -1;
}
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", (-1));
uint32_t duty = (LEDC.channel_group[speed_mode].channel[channel].duty_rd.duty_read >> 4);
return duty;
}
esp_err_t ledc_set_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num, uint32_t freq_hz)
{
if(!ledc_is_valid_mode(speed_mode)) {
return ESP_ERR_INVALID_ARG;
}
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", ESP_ERR_INVALID_ARG);
portENTER_CRITICAL(&ledc_spinlock);
esp_err_t ret = ESP_OK;
uint32_t div_num = 0;
@ -403,7 +294,7 @@ esp_err_t ledc_set_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num, uint32_t
div_num = ((uint64_t) LEDC_REF_CLK_HZ << 8) / freq_hz / precision;
}
if(div_num <= 256 || div_num > LEDC_DIV_NUM_HSTIMER0) {
LEDC_ERROR("div param err,div_param=%u\n", div_num);
ESP_LOGE(LEDC_TAG, "div param err,div_param=%u", div_num);
ret = ESP_FAIL;
}
LEDC.timer_group[speed_mode].timer[timer_num].conf.div_num = div_num;
@ -413,9 +304,7 @@ esp_err_t ledc_set_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num, uint32_t
uint32_t ledc_get_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num)
{
if(!ledc_is_valid_mode(speed_mode)) {
return 0;
}
LEDC_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "ledc mode error", (0));
portENTER_CRITICAL(&ledc_spinlock);
uint32_t freq = 0;
uint32_t timer_source_clk = LEDC.timer_group[speed_mode].timer[timer_num].conf.tick_sel;

1008
components/driver/uart.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -34,6 +34,8 @@ typedef int32_t esp_err_t;
#define ESP_ERR_INVALID_SIZE 0x104
#define ESP_ERR_NOT_FOUND 0x105
#define ESP_ERR_NOT_SUPPORTED 0x106
#define ESP_ERR_TIMEOUT 0x107
#define ESP_ERR_WIFI_BASE 0x3000 /*!< Starting number of WiFi error codes */

View File

@ -18,8 +18,10 @@
#include "soc.h"
#define REG_UART_BASE( i ) (DR_REG_UART_BASE + (i) * 0x10000 + ( i > 1 ? 0xe000 : 0 ) )
#define REG_UART_AHB_BASE(i) (0x60000000 + (i) * 0x10000 + ( i > 1 ? 0xe000 : 0 ) )
#define UART_FIFO_AHB_REG(i) (REG_UART_AHB_BASE(i) + 0x0)
#define UART_FIFO_REG(i) (REG_UART_BASE(i) + 0x0)
/* UART_RXFIFO_RD_BYTE : RO ;bitpos:[7:0] ;default: 8'b0 ; */
/*description: This register stores one byte data read by rx fifo.*/
#define UART_RXFIFO_RD_BYTE 0x000000FF

View File

@ -18,22 +18,34 @@ to this bit of memory will block.
The requirement for items to be contiguous is slightly problematic when the only way to place
the next item would involve a wraparound from the end to the beginning of the ringbuffer. This can
be solved in two ways:
- allow_split_items = pdTRUE: The insertion code will split the item in two items; one which fits
be solved (or not) in a few ways:
- type = RINGBUF_TYPE_ALLOWSPLIT: The insertion code will split the item in two items; one which fits
in the space left at the end of the ringbuffer, one that contains the remaining data which is placed
in the beginning. Two xRingbufferReceive calls will be needed to retrieve the data.
- allow_split_items = pdFALSE: The insertion code will leave the room at the end of the ringbuffer
- type = RINGBUF_TYPE_NOSPLIT: The insertion code will leave the room at the end of the ringbuffer
unused and instead will put the entire item at the start of the ringbuffer, as soon as there is
enough free space.
- type = RINGBUF_TYPE_BYTEBUF: This is your conventional byte-based ringbuffer. It does have no
overhead, but it has no item contiguousness either: a read will just give you the entire written
buffer space, or the space up to the end of the buffer, and writes can be broken up in any way
possible. Note that this type cannot do a 2nd read before returning the memory of the 1st.
The maximum size of an item will be affected by this decision. When split items are allowed, it's
acceptable to push items of (buffer_size)-16 bytes into the buffer. When it's not allowed, the
maximum size is (buffer_size/2)-8 bytes.
maximum size is (buffer_size/2)-8 bytes. The bytebuf can fill the entire buffer with data, it has
no overhead.
*/
//An opaque handle for a ringbuff object.
typedef void * RingbufHandle_t;
//The various types of buffer
typedef enum {
RINGBUF_TYPE_NOSPLIT = 0,
RINGBUF_TYPE_ALLOWSPLIT,
RINGBUF_TYPE_BYTEBUF
} ringbuf_type_t;
/**
* @brief Create a ring buffer
@ -45,7 +57,7 @@ typedef void * RingbufHandle_t;
*
* @return A RingbufHandle_t handle to the created ringbuffer, or NULL in case of error.
*/
RingbufHandle_t xRingbufferCreate(size_t buf_length, BaseType_t allow_split_items);
RingbufHandle_t xRingbufferCreate(size_t buf_length, ringbuf_type_t type);
/**
@ -120,6 +132,34 @@ void *xRingbufferReceive(RingbufHandle_t ringbuf, size_t *item_size, TickType_t
void *xRingbufferReceiveFromISR(RingbufHandle_t ringbuf, size_t *item_size);
/**
* @brief Retrieve bytes from a ByteBuf type of ring buffer, specifying the maximum amount of bytes
* to return
*
* @param ringbuf - Ring buffer to retrieve the item from
* @param item_size - Pointer to a variable to which the size of the retrieved item will be written.
* @param xTicksToWait - Ticks to wait for items in the ringbuffer.
*
* @return Pointer to the retrieved item on success; *item_size filled with the length of the
* item. NULL on timeout, *item_size is untouched in that case.
*/
void *xRingbufferReceiveUpTo(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait, size_t wanted_size);
/**
* @brief Retrieve bytes from a ByteBuf type of ring buffer, specifying the maximum amount of bytes
* to return. Call this from an ISR.
*
* @param ringbuf - Ring buffer to retrieve the item from
* @param item_size - Pointer to a variable to which the size of the retrieved item will be written.
*
* @return Pointer to the retrieved item on success; *item_size filled with the length of the
* item. NULL when the ringbuffer is empty, *item_size is untouched in that case.
*/
void *xRingbufferReceiveUpToFromISR(RingbufHandle_t ringbuf, size_t *item_size, size_t wanted_size);
/**
* @brief Return a previously-retrieved item to the ringbuffer
*

View File

@ -18,6 +18,7 @@
#include "freertos/queue.h"
#include "freertos/xtensa_api.h"
#include "freertos/ringbuf.h"
#include "esp_attr.h"
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
@ -25,6 +26,7 @@
typedef enum {
flag_allowsplit = 1,
flag_bytebuf = 2,
} rbflag_t;
typedef enum {
@ -33,8 +35,10 @@ typedef enum {
} itemflag_t;
typedef struct ringbuf_t ringbuf_t;
//The ringbuffer structure
typedef struct {
struct ringbuf_t {
SemaphoreHandle_t free_space_sem; //Binary semaphore, wakes up writing threads when there's more free space
SemaphoreHandle_t items_buffered_sem; //Binary semaphore, indicates there are new packets in the circular buffer. See remark.
size_t size; //Size of the data storage
@ -44,7 +48,12 @@ typedef struct {
uint8_t *data; //Data storage
portMUX_TYPE mux; //Spinlock for actual data/ptr/struct modification
rbflag_t flags;
} ringbuf_t;
size_t maxItemSize;
//The following keep function pointers to hold different implementations for ringbuffer management.
BaseType_t (*copyItemToRingbufImpl)(ringbuf_t *rb, uint8_t *buffer, size_t buffer_size);
uint8_t *(*getItemFromRingbufImpl)(ringbuf_t *rb, size_t *length, int wanted_length);
void (*returnItemToRingbufImpl)(ringbuf_t *rb, void *item);
};
@ -73,14 +82,16 @@ static int ringbufferFreeMem(ringbuf_t *rb)
return free_size-1;
}
//Copies a single item to the ring buffer. Assumes there is space in the ringbuffer and
//Copies a single item to the ring buffer; refuses to split items. Assumes there is space in the ringbuffer and
//the ringbuffer is locked. Increases write_ptr to the next item. Returns pdTRUE on
//success, pdFALSE if it can't make the item fit and the calling routine needs to retry
//later or fail.
//This function by itself is not threadsafe, always call from within a muxed section.
static BaseType_t copyItemToRingbuf(ringbuf_t *rb, uint8_t *buffer, size_t buffer_size)
static BaseType_t copyItemToRingbufNoSplit(ringbuf_t *rb, uint8_t *buffer, size_t buffer_size)
{
size_t rbuffer_size=(buffer_size+3)&~3; //Payload length, rounded to next 32-bit value
size_t rbuffer_size;
rbuffer_size=(buffer_size+3)&~3; //Payload length, rounded to next 32-bit value
configASSERT(((int)rb->write_ptr&3)==0); //write_ptr needs to be 32-bit aligned
configASSERT(rb->write_ptr-(rb->data+rb->size) >= sizeof(buf_entry_hdr_t)); //need to have at least the size
//of a header to the end of the ringbuff
@ -88,65 +99,28 @@ static BaseType_t copyItemToRingbuf(ringbuf_t *rb, uint8_t *buffer, size_t buffe
//See if we have enough contiguous space to write the buffer.
if (rem_len < rbuffer_size + sizeof(buf_entry_hdr_t)) {
//The buffer can't be contiguously written to the ringbuffer, but needs special handling. Do
//that depending on how the ringbuffer is configured.
//The code here is also expected to check if the buffer, mangled in whatever way is implemented,
//will still fit, and return pdFALSE if that is not the case.
if (rb->flags & flag_allowsplit) {
//Buffer plus header is not going to fit in the room from wr_pos to the end of the
//ringbuffer... we need to split the write in two.
//First, see if this will fit at all.
if (ringbufferFreeMem(rb) < (sizeof(buf_entry_hdr_t)*2)+rbuffer_size) {
//Will not fit.
return pdFALSE;
}
//Because the code at the end of the function makes sure we always have
//room for a header, this should never assert.
configASSERT(rem_len>=sizeof(buf_entry_hdr_t));
//Okay, it should fit. Write everything.
//First, place bit of buffer that does fit. Write header first...
buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->write_ptr;
hdr->flags=0;
hdr->len=rem_len-sizeof(buf_entry_hdr_t);
rb->write_ptr+=sizeof(buf_entry_hdr_t);
rem_len-=sizeof(buf_entry_hdr_t);
if (rem_len!=0) {
//..then write the data bit that fits.
memcpy(rb->write_ptr, buffer, rem_len);
//Update vars so the code later on will write the rest of the data.
buffer+=rem_len;
rbuffer_size-=rem_len;
buffer_size-=rem_len;
} else {
//Huh, only the header fit. Mark as dummy so the receive function doesn't receive
//an useless zero-byte packet.
hdr->flags|=iflag_dummydata;
}
rb->write_ptr=rb->data;
} else {
//Buffer plus header is not going to fit in the room from wr_pos to the end of the
//ringbuffer... but we're not allowed to split the buffer. We need to fill the
//rest of the ringbuffer with a dummy item so we can place the data at the _start_ of
//the ringbuffer..
//First, find out if we actually have enough space at the start of the ringbuffer to
//make this work (Again, we need 4 bytes extra because otherwise read_ptr==free_ptr)
if (rb->free_ptr-rb->data < rbuffer_size+sizeof(buf_entry_hdr_t)+4) {
//Will not fit.
return pdFALSE;
}
//If the read buffer hasn't wrapped around yet, there's no way this will work either.
if (rb->free_ptr > rb->write_ptr) {
//No luck.
return pdFALSE;
}
//Okay, it will fit. Mark the rest of the ringbuffer space with a dummy packet.
buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->write_ptr;
hdr->flags=iflag_dummydata;
//Reset the write pointer to the start of the ringbuffer so the code later on can
//happily write the data.
rb->write_ptr=rb->data;
//Buffer plus header is not going to fit in the room from wr_pos to the end of the
//ringbuffer... but we're not allowed to split the buffer. We need to fill the
//rest of the ringbuffer with a dummy item so we can place the data at the _start_ of
//the ringbuffer..
//First, find out if we actually have enough space at the start of the ringbuffer to
//make this work (Again, we need 4 bytes extra because otherwise read_ptr==free_ptr)
if (rb->free_ptr-rb->data < rbuffer_size+sizeof(buf_entry_hdr_t)+4) {
//Will not fit.
return pdFALSE;
}
//If the read buffer hasn't wrapped around yet, there's no way this will work either.
if (rb->free_ptr > rb->write_ptr) {
//No luck.
return pdFALSE;
}
//Okay, it will fit. Mark the rest of the ringbuffer space with a dummy packet.
buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->write_ptr;
hdr->flags=iflag_dummydata;
//Reset the write pointer to the start of the ringbuffer so the code later on can
//happily write the data.
rb->write_ptr=rb->data;
} else {
//No special handling needed. Checking if it's gonna fit probably still is a good idea.
if (ringbufferFreeMem(rb) < sizeof(buf_entry_hdr_t)+rbuffer_size) {
@ -174,9 +148,117 @@ static BaseType_t copyItemToRingbuf(ringbuf_t *rb, uint8_t *buffer, size_t buffe
return pdTRUE;
}
//Copies a single item to the ring buffer; allows split items. Assumes there is space in the ringbuffer and
//the ringbuffer is locked. Increases write_ptr to the next item. Returns pdTRUE on
//success, pdFALSE if it can't make the item fit and the calling routine needs to retry
//later or fail.
//This function by itself is not threadsafe, always call from within a muxed section.
static BaseType_t copyItemToRingbufAllowSplit(ringbuf_t *rb, uint8_t *buffer, size_t buffer_size)
{
size_t rbuffer_size;
rbuffer_size=(buffer_size+3)&~3; //Payload length, rounded to next 32-bit value
configASSERT(((int)rb->write_ptr&3)==0); //write_ptr needs to be 32-bit aligned
configASSERT(rb->write_ptr-(rb->data+rb->size) >= sizeof(buf_entry_hdr_t)); //need to have at least the size
//of a header to the end of the ringbuff
size_t rem_len=(rb->data + rb->size) - rb->write_ptr; //length remaining until end of ringbuffer
//See if we have enough contiguous space to write the buffer.
if (rem_len < rbuffer_size + sizeof(buf_entry_hdr_t)) {
//The buffer can't be contiguously written to the ringbuffer, but needs special handling. Do
//that depending on how the ringbuffer is configured.
//The code here is also expected to check if the buffer, mangled in whatever way is implemented,
//will still fit, and return pdFALSE if that is not the case.
//Buffer plus header is not going to fit in the room from wr_pos to the end of the
//ringbuffer... we need to split the write in two.
//First, see if this will fit at all.
if (ringbufferFreeMem(rb) < (sizeof(buf_entry_hdr_t)*2)+rbuffer_size) {
//Will not fit.
return pdFALSE;
}
//Because the code at the end of the function makes sure we always have
//room for a header, this should never assert.
configASSERT(rem_len>=sizeof(buf_entry_hdr_t));
//Okay, it should fit. Write everything.
//First, place bit of buffer that does fit. Write header first...
buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->write_ptr;
hdr->flags=0;
hdr->len=rem_len-sizeof(buf_entry_hdr_t);
rb->write_ptr+=sizeof(buf_entry_hdr_t);
rem_len-=sizeof(buf_entry_hdr_t);
if (rem_len!=0) {
//..then write the data bit that fits.
memcpy(rb->write_ptr, buffer, rem_len);
//Update vars so the code later on will write the rest of the data.
buffer+=rem_len;
rbuffer_size-=rem_len;
buffer_size-=rem_len;
} else {
//Huh, only the header fit. Mark as dummy so the receive function doesn't receive
//an useless zero-byte packet.
hdr->flags|=iflag_dummydata;
}
rb->write_ptr=rb->data;
} else {
//No special handling needed. Checking if it's gonna fit probably still is a good idea.
if (ringbufferFreeMem(rb) < sizeof(buf_entry_hdr_t)+rbuffer_size) {
//Buffer is not going to fit, period.
return pdFALSE;
}
}
//If we are here, the buffer is guaranteed to fit in the space starting at the write pointer.
buf_entry_hdr_t *hdr=(buf_entry_hdr_t *)rb->write_ptr;
hdr->len=buffer_size;
hdr->flags=0;
rb->write_ptr+=sizeof(buf_entry_hdr_t);
memcpy(rb->write_ptr, buffer, buffer_size);
rb->write_ptr+=rbuffer_size;
//The buffer will wrap around if we don't have room for a header anymore.
if ((rb->data+rb->size)-rb->write_ptr < sizeof(buf_entry_hdr_t)) {
//'Forward' the write buffer until we are at the start of the ringbuffer.
//The read pointer will always be at the start of a full header, which cannot
//exist at the point of the current write pointer, so there's no chance of overtaking
//that.
rb->write_ptr=rb->data;
}
return pdTRUE;
}
//Copies a bunch of daya to the ring bytebuffer. Assumes there is space in the ringbuffer and
//the ringbuffer is locked. Increases write_ptr to the next item. Returns pdTRUE on
//success, pdFALSE if it can't make the item fit and the calling routine needs to retry
//later or fail.
//This function by itself is not threadsafe, always call from within a muxed section.
static BaseType_t copyItemToRingbufByteBuf(ringbuf_t *rb, uint8_t *buffer, size_t buffer_size)
{
size_t rem_len=(rb->data + rb->size) - rb->write_ptr; //length remaining until end of ringbuffer
//See if we have enough contiguous space to write the buffer.
if (rem_len < buffer_size) {
//...Nope. Write the data bit that fits.
memcpy(rb->write_ptr, buffer, rem_len);
//Update vars so the code later on will write the rest of the data.
buffer+=rem_len;
buffer_size-=rem_len;
rb->write_ptr=rb->data;
}
//If we are here, the buffer is guaranteed to fit in the space starting at the write pointer.
memcpy(rb->write_ptr, buffer, buffer_size);
rb->write_ptr+=buffer_size;
//The buffer will wrap around if we're at the end.
if ((rb->data+rb->size)==rb->write_ptr) {
rb->write_ptr=rb->data;
}
return pdTRUE;
}
//Retrieves a pointer to the data of the next item, or NULL if this is not possible.
//This function by itself is not threadsafe, always call from within a muxed section.
static uint8_t *getItemFromRingbuf(ringbuf_t *rb, size_t *length)
//Because we always return one item, this function ignores the wanted_length variable.
static uint8_t *getItemFromRingbufDefault(ringbuf_t *rb, size_t *length, int wanted_length)
{
uint8_t *ret;
configASSERT(((int)rb->read_ptr&3)==0);
@ -210,10 +292,48 @@ static uint8_t *getItemFromRingbuf(ringbuf_t *rb, size_t *length)
return ret;
}
//Retrieves a pointer to the data in the buffer, or NULL if this is not possible.
//This function by itself is not threadsafe, always call from within a muxed section.
//This function honours the wanted_length and will never return more data than this.
static uint8_t *getItemFromRingbufByteBuf(ringbuf_t *rb, size_t *length, int wanted_length)
{
uint8_t *ret;
if (rb->read_ptr != rb->free_ptr) {
//This type of ringbuff does not support multiple outstanding buffers.
return NULL;
}
if (rb->read_ptr == rb->write_ptr) {
//No data available.
return NULL;
}
ret=rb->read_ptr;
if (rb->read_ptr > rb->write_ptr) {
//Available data wraps around. Give data until the end of the buffer.
*length=rb->size-(rb->read_ptr - rb->data);
if (wanted_length != 0 && *length > wanted_length) {
*length=wanted_length;
rb->read_ptr+=wanted_length;
} else {
rb->read_ptr=rb->data;
}
} else {
//Return data up to write pointer.
*length=rb->write_ptr -rb->read_ptr;
if (wanted_length != 0 && *length > wanted_length) {
*length=wanted_length;
rb->read_ptr+=wanted_length;
} else {
rb->read_ptr=rb->write_ptr;
}
}
return ret;
}
//Returns an item to the ringbuffer. Will mark the item as free, and will see if the free pointer
//can be increase.
//This function by itself is not threadsafe, always call from within a muxed section.
static void returnItemToRingbuf(ringbuf_t *rb, void *item) {
static void returnItemToRingbufDefault(ringbuf_t *rb, void *item) {
uint8_t *data=(uint8_t*)item;
configASSERT(((int)rb->free_ptr&3)==0);
configASSERT(data >= rb->data);
@ -243,12 +363,26 @@ static void returnItemToRingbuf(ringbuf_t *rb, void *item) {
if ((rb->data+rb->size)-rb->free_ptr < sizeof(buf_entry_hdr_t)) {
rb->free_ptr=rb->data;
}
//The free_ptr can not exceed read_ptr, otherwise write_ptr might overwrite read_ptr.
//Read_ptr can not set to rb->data with free_ptr, otherwise write_ptr might wrap around to rb->data.
if(rb->free_ptr == rb->read_ptr) break;
//Next header
hdr=(buf_entry_hdr_t *)rb->free_ptr;
}
}
//Returns an item to the ringbuffer. Will mark the item as free, and will see if the free pointer
//can be increase.
//This function by itself is not threadsafe, always call from within a muxed section.
static void returnItemToRingbufBytebuf(ringbuf_t *rb, void *item) {
uint8_t *data=(uint8_t*)item;
configASSERT(data >= rb->data);
configASSERT(data < rb->data+rb->size);
//Free the read memory.
rb->free_ptr=rb->read_ptr;
}
void xRingbufferPrintInfo(RingbufHandle_t ringbuf)
{
ringbuf_t *rb=(ringbuf_t *)ringbuf;
@ -259,7 +393,7 @@ void xRingbufferPrintInfo(RingbufHandle_t ringbuf)
RingbufHandle_t xRingbufferCreate(size_t buf_length, BaseType_t allow_split_items)
RingbufHandle_t xRingbufferCreate(size_t buf_length, ringbuf_type_t type)
{
ringbuf_t *rb = malloc(sizeof(ringbuf_t));
if (rb==NULL) goto err;
@ -273,9 +407,35 @@ RingbufHandle_t xRingbufferCreate(size_t buf_length, BaseType_t allow_split_item
rb->free_space_sem = xSemaphoreCreateBinary();
rb->items_buffered_sem = xSemaphoreCreateBinary();
rb->flags=0;
if (allow_split_items) rb->flags|=flag_allowsplit;
if (type==RINGBUF_TYPE_ALLOWSPLIT) {
rb->flags|=flag_allowsplit;
rb->copyItemToRingbufImpl=copyItemToRingbufAllowSplit;
rb->getItemFromRingbufImpl=getItemFromRingbufDefault;
rb->returnItemToRingbufImpl=returnItemToRingbufDefault;
//Calculate max item size. Worst case, we need to split an item into two, which means two headers of overhead.
rb->maxItemSize=rb->size-(sizeof(buf_entry_hdr_t)*2)-4;
} else if (type==RINGBUF_TYPE_BYTEBUF) {
rb->flags|=flag_bytebuf;
rb->copyItemToRingbufImpl=copyItemToRingbufByteBuf;
rb->getItemFromRingbufImpl=getItemFromRingbufByteBuf;
rb->returnItemToRingbufImpl=returnItemToRingbufBytebuf;
//Calculate max item size. We have no headers and can split anywhere -> size is total size minus one.
rb->maxItemSize=rb->size-1;
} else if (type==RINGBUF_TYPE_NOSPLIT) {
rb->copyItemToRingbufImpl=copyItemToRingbufNoSplit;
rb->getItemFromRingbufImpl=getItemFromRingbufDefault;
rb->returnItemToRingbufImpl=returnItemToRingbufDefault;
//Calculate max item size. Worst case, we have the write ptr in such a position that we are lacking four bytes of free
//memory to put an item into the rest of the memory. If this happens, we have to dummy-fill
//(item_data-4) bytes of buffer, then we only have (size-(item_data-4) bytes left to fill
//with the real item. (item size being header+data)
rb->maxItemSize=(rb->size/2)-sizeof(buf_entry_hdr_t)-4;
} else {
configASSERT(0);
}
if (rb->free_space_sem == NULL || rb->items_buffered_sem == NULL) goto err;
vPortCPUInitializeMutex(&rb->mux);
return (RingbufHandle_t)rb;
err:
@ -303,18 +463,7 @@ size_t xRingbufferGetMaxItemSize(RingbufHandle_t ringbuf)
{
ringbuf_t *rb=(ringbuf_t *)ringbuf;
configASSERT(rb);
//In both cases, we return 4 bytes less than what we actually can have. If the ringbuffer is
//indeed entirely filled, read_ptr==free_ptr, which throws off the free space calculation.
if (rb->flags & flag_allowsplit) {
//Worst case, we need to split an item into two, which means two headers of overhead.
return rb->size-(sizeof(buf_entry_hdr_t)*2)-4;
} else {
//Worst case, we have the write ptr in such a position that we are lacking four bytes of free
//memory to put an item into the rest of the memory. If this happens, we have to dummy-fill
//(item_data-4) bytes of buffer, then we only have (size-(item_data-4) bytes left to fill
//with the real item. (item size being header+data)
return (rb->size/2)-sizeof(buf_entry_hdr_t)-4;
}
return rb->maxItemSize;
}
BaseType_t xRingbufferSend(RingbufHandle_t ringbuf, void *data, size_t dataSize, TickType_t ticks_to_wait)
@ -352,7 +501,7 @@ BaseType_t xRingbufferSend(RingbufHandle_t ringbuf, void *data, size_t dataSize,
portENTER_CRITICAL(&rb->mux);
//Another thread may have been able to sneak its write first. Check again now we locked the ringbuff, and retry
//everything if this is the case. Otherwise, we can write and are done.
done=copyItemToRingbuf(rb, data, dataSize);
done=rb->copyItemToRingbufImpl(rb, data, dataSize);
portEXIT_CRITICAL(&rb->mux);
}
xSemaphoreGive(rb->items_buffered_sem);
@ -371,8 +520,7 @@ BaseType_t xRingbufferSendFromISR(RingbufHandle_t ringbuf, void *data, size_t da
//Does not fit in the remaining space in the ringbuffer.
write_succeeded=pdFALSE;
} else {
copyItemToRingbuf(rb, data, dataSize);
write_succeeded=pdTRUE;
write_succeeded = rb->copyItemToRingbufImpl(rb, data, dataSize);
}
portEXIT_CRITICAL_ISR(&rb->mux);
if (write_succeeded) {
@ -382,7 +530,7 @@ BaseType_t xRingbufferSendFromISR(RingbufHandle_t ringbuf, void *data, size_t da
}
void *xRingbufferReceive(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait)
static void *xRingbufferReceiveGeneric(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait, size_t wanted_size)
{
ringbuf_t *rb=(ringbuf_t *)ringbuf;
uint8_t *itemData;
@ -399,7 +547,7 @@ void *xRingbufferReceive(RingbufHandle_t ringbuf, size_t *item_size, TickType_t
}
//Okay, we seem to have data in the buffer. Grab the mux and copy it out if it's still there.
portENTER_CRITICAL(&rb->mux);
itemData=getItemFromRingbuf(rb, item_size);
itemData=rb->getItemFromRingbufImpl(rb, item_size, wanted_size);
portEXIT_CRITICAL(&rb->mux);
if (itemData) {
//We managed to get an item.
@ -409,6 +557,11 @@ void *xRingbufferReceive(RingbufHandle_t ringbuf, size_t *item_size, TickType_t
return (void*)itemData;
}
void *xRingbufferReceive(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait)
{
return xRingbufferReceiveGeneric(ringbuf, item_size, ticks_to_wait, 0);
}
void *xRingbufferReceiveFromISR(RingbufHandle_t ringbuf, size_t *item_size)
{
@ -416,7 +569,28 @@ void *xRingbufferReceiveFromISR(RingbufHandle_t ringbuf, size_t *item_size)
uint8_t *itemData;
configASSERT(rb);
portENTER_CRITICAL_ISR(&rb->mux);
itemData=getItemFromRingbuf(rb, item_size);
itemData=rb->getItemFromRingbufImpl(rb, item_size, 0);
portEXIT_CRITICAL_ISR(&rb->mux);
return (void*)itemData;
}
void *xRingbufferReceiveUpTo(RingbufHandle_t ringbuf, size_t *item_size, TickType_t ticks_to_wait, size_t wanted_size) {
ringbuf_t *rb=(ringbuf_t *)ringbuf;
if (wanted_size == 0) return NULL;
configASSERT(rb);
configASSERT(rb->flags & flag_bytebuf);
return xRingbufferReceiveGeneric(ringbuf, item_size, ticks_to_wait, wanted_size);
}
void *xRingbufferReceiveUpToFromISR(RingbufHandle_t ringbuf, size_t *item_size, size_t wanted_size)
{
ringbuf_t *rb=(ringbuf_t *)ringbuf;
uint8_t *itemData;
if (wanted_size == 0) return NULL;
configASSERT(rb);
configASSERT(rb->flags & flag_bytebuf);
portENTER_CRITICAL_ISR(&rb->mux);
itemData=rb->getItemFromRingbufImpl(rb, item_size, 0);
portEXIT_CRITICAL_ISR(&rb->mux);
return (void*)itemData;
}
@ -426,7 +600,7 @@ void vRingbufferReturnItem(RingbufHandle_t ringbuf, void *item)
{
ringbuf_t *rb=(ringbuf_t *)ringbuf;
portENTER_CRITICAL_ISR(&rb->mux);
returnItemToRingbuf(rb, item);
rb->returnItemToRingbufImpl(rb, item);
portEXIT_CRITICAL_ISR(&rb->mux);
xSemaphoreGive(rb->free_space_sem);
}
@ -436,7 +610,7 @@ void vRingbufferReturnItemFromISR(RingbufHandle_t ringbuf, void *item, BaseType_
{
ringbuf_t *rb=(ringbuf_t *)ringbuf;
portENTER_CRITICAL_ISR(&rb->mux);
returnItemToRingbuf(rb, item);
rb->returnItemToRingbufImpl(rb, item);
portEXIT_CRITICAL_ISR(&rb->mux);
xSemaphoreGiveFromISR(rb->free_space_sem, higher_prio_task_awoken);
}