Merge branch 'master' into open-libmain
Conflicts: core/include/esp/rtc_regs.h include/espressif/spi_flash.h
This commit is contained in:
commit
769ca0d8f8
89 changed files with 4766 additions and 634 deletions
|
@ -250,7 +250,7 @@ void IRAM sdk_user_start(void) {
|
|||
|
||||
// .text+0x3a8
|
||||
void IRAM vApplicationStackOverflowHook(xTaskHandle task, char *task_name) {
|
||||
printf("\"%s\"(stack_size = %lu) overflow the heap_size.\n", task_name, uxTaskGetStackHighWaterMark(task));
|
||||
printf("Task stack overflow (high water mark=%lu name=\"%s\")\n", uxTaskGetStackHighWaterMark(task), task_name);
|
||||
}
|
||||
|
||||
// .text+0x3d8
|
||||
|
|
41
core/esp_gpio.c
Normal file
41
core/esp_gpio.c
Normal file
|
@ -0,0 +1,41 @@
|
|||
/* GPIO management functions
|
||||
*
|
||||
* Part of esp-open-rtos
|
||||
* Copyright (C) 2015 Angus Gratton
|
||||
* BSD Licensed as described in the file LICENSE
|
||||
*/
|
||||
#include <esp/gpio.h>
|
||||
|
||||
void gpio_enable(const uint8_t gpio_num, const gpio_direction_t direction)
|
||||
{
|
||||
switch (direction) {
|
||||
case GPIO_INPUT:
|
||||
GPIO.ENABLE_OUT_CLEAR = BIT(gpio_num);
|
||||
iomux_set_gpio_function(gpio_num, false);
|
||||
break;
|
||||
case GPIO_OUTPUT:
|
||||
GPIO.CONF[gpio_num] &= ~GPIO_CONF_OPEN_DRAIN;
|
||||
GPIO.ENABLE_OUT_SET = BIT(gpio_num);
|
||||
iomux_set_gpio_function(gpio_num, true);
|
||||
break;
|
||||
case GPIO_OUT_OPEN_DRAIN:
|
||||
GPIO.CONF[gpio_num] |= GPIO_CONF_OPEN_DRAIN;
|
||||
GPIO.ENABLE_OUT_SET = BIT(gpio_num);
|
||||
iomux_set_gpio_function(gpio_num, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void gpio_set_pullup(uint8_t gpio_num, bool enabled, bool enabled_during_sleep)
|
||||
{
|
||||
uint32_t flags = 0;
|
||||
|
||||
if (enabled) {
|
||||
flags |= IOMUX_PIN_PULLUP;
|
||||
}
|
||||
if (enabled_during_sleep) {
|
||||
flags |= IOMUX_PIN_PULLUP_SLEEP;
|
||||
}
|
||||
iomux_set_pullup_flags(gpio_to_iomux(gpio_num), flags);
|
||||
}
|
||||
|
|
@ -7,14 +7,15 @@
|
|||
#include "esp/iomux.h"
|
||||
#include "common_macros.h"
|
||||
|
||||
/* These are non-static versions of the GPIO mapping tables in
|
||||
iomux.h, so if they need to be linked only one copy is linked for
|
||||
the entire program.
|
||||
const static IRAM_DATA uint32_t IOMUX_TO_GPIO[] = { 12, 13, 14, 15, 3, 1, 6, 7, 8, 9, 10, 11, 0, 2, 4, 5 };
|
||||
const static IRAM_DATA uint32_t GPIO_TO_IOMUX[] = { 12, 5, 13, 4, 14, 15, 6, 7, 8, 9, 10, 11, 0, 1, 2, 3 };
|
||||
|
||||
These are only ever linked in if the arguments to gpio_to_ionum
|
||||
or ionum_to_gpio are not known at compile time.
|
||||
uint8_t IRAM gpio_to_iomux(const uint8_t gpio_number)
|
||||
{
|
||||
return GPIO_TO_IOMUX[gpio_number];
|
||||
}
|
||||
|
||||
Arrays are declared as 32-bit integers in IROM to save RAM.
|
||||
*/
|
||||
const IROM uint32_t GPIO_TO_IOMUX_MAP[] = _GPIO_TO_IOMUX;
|
||||
const IROM uint32_t IOMUX_TO_GPIO_MAP[] = _IOMUX_TO_GPIO;
|
||||
uint8_t IRAM iomux_to_gpio(const uint8_t iomux_number)
|
||||
{
|
||||
return IOMUX_TO_GPIO[iomux_number];
|
||||
}
|
||||
|
|
251
core/esp_spi.c
Normal file
251
core/esp_spi.c
Normal file
|
@ -0,0 +1,251 @@
|
|||
/*
|
||||
* ESP hardware SPI master driver
|
||||
*
|
||||
* Part of esp-open-rtos
|
||||
* Copyright (c) Ruslan V. Uss, 2016
|
||||
* BSD Licensed as described in the file LICENSE
|
||||
*/
|
||||
#include "esp/spi.h"
|
||||
|
||||
#include "esp/iomux.h"
|
||||
#include "esp/gpio.h"
|
||||
#include <string.h>
|
||||
|
||||
#define _SPI0_SCK_GPIO 6
|
||||
#define _SPI0_MISO_GPIO 7
|
||||
#define _SPI0_MOSI_GPIO 8
|
||||
#define _SPI0_HD_GPIO 9
|
||||
#define _SPI0_WP_GPIO 10
|
||||
#define _SPI0_CS0_GPIO 11
|
||||
|
||||
#define _SPI1_MISO_GPIO 12
|
||||
#define _SPI1_MOSI_GPIO 13
|
||||
#define _SPI1_SCK_GPIO 14
|
||||
#define _SPI1_CS0_GPIO 15
|
||||
|
||||
#define _SPI0_FUNC 1
|
||||
#define _SPI1_FUNC 2
|
||||
|
||||
#define _SPI_BUF_SIZE 64
|
||||
|
||||
static bool _minimal_pins[2] = {false, false};
|
||||
|
||||
inline static void _set_pin_function(uint8_t pin, uint32_t function)
|
||||
{
|
||||
iomux_set_function(gpio_to_iomux(pin), function);
|
||||
}
|
||||
|
||||
bool spi_init(uint8_t bus, spi_mode_t mode, uint32_t freq_divider, bool msb, spi_endianness_t endianness, bool minimal_pins)
|
||||
{
|
||||
switch (bus)
|
||||
{
|
||||
case 0:
|
||||
_set_pin_function(_SPI0_MISO_GPIO, _SPI0_FUNC);
|
||||
_set_pin_function(_SPI0_MOSI_GPIO, _SPI0_FUNC);
|
||||
_set_pin_function(_SPI0_SCK_GPIO, _SPI0_FUNC);
|
||||
if (!minimal_pins)
|
||||
{
|
||||
_set_pin_function(_SPI0_HD_GPIO, _SPI0_FUNC);
|
||||
_set_pin_function(_SPI0_WP_GPIO, _SPI0_FUNC);
|
||||
_set_pin_function(_SPI0_CS0_GPIO, _SPI0_FUNC);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
_set_pin_function(_SPI1_MISO_GPIO, _SPI1_FUNC);
|
||||
_set_pin_function(_SPI1_MOSI_GPIO, _SPI1_FUNC);
|
||||
_set_pin_function(_SPI1_SCK_GPIO, _SPI1_FUNC);
|
||||
if (!minimal_pins)
|
||||
_set_pin_function(_SPI1_CS0_GPIO, _SPI1_FUNC);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
_minimal_pins[bus] = minimal_pins;
|
||||
SPI(bus).USER0 = SPI_USER0_MOSI | SPI_USER0_CLOCK_IN_EDGE | SPI_USER0_DUPLEX |
|
||||
(minimal_pins ? 0 : (SPI_USER0_CS_HOLD | SPI_USER0_CS_SETUP));
|
||||
|
||||
spi_set_frequency_div(bus, freq_divider);
|
||||
spi_set_mode(bus, mode);
|
||||
spi_set_msb(bus, msb);
|
||||
spi_set_endianness(bus, endianness);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void spi_get_settings(uint8_t bus, spi_settings_t *s)
|
||||
{
|
||||
s->mode = spi_get_mode(bus);
|
||||
s->freq_divider = spi_get_frequency_div(bus);
|
||||
s->msb = spi_get_msb(bus);
|
||||
s->endianness = spi_get_endianness(bus);
|
||||
s->minimal_pins = _minimal_pins[bus];
|
||||
}
|
||||
|
||||
void spi_set_mode(uint8_t bus, spi_mode_t mode)
|
||||
{
|
||||
bool cpha = (uint8_t)mode & 1;
|
||||
bool cpol = (uint8_t)mode & 2;
|
||||
if (cpol)
|
||||
cpha = !cpha; // CPHA must be inverted when CPOL = 1, I have no idea why
|
||||
|
||||
// CPHA
|
||||
if (cpha)
|
||||
SPI(bus).USER0 |= SPI_USER0_CLOCK_OUT_EDGE;
|
||||
else
|
||||
SPI(bus).USER0 &= ~SPI_USER0_CLOCK_OUT_EDGE;
|
||||
|
||||
// CPOL - see http://bbs.espressif.com/viewtopic.php?t=342#p5384
|
||||
if (cpol)
|
||||
SPI(bus).PIN |= SPI_PIN_IDLE_EDGE;
|
||||
else
|
||||
SPI(bus).PIN &= ~SPI_PIN_IDLE_EDGE;
|
||||
}
|
||||
|
||||
spi_mode_t spi_get_mode(uint8_t bus)
|
||||
{
|
||||
uint8_t cpha = SPI(bus).USER0 & SPI_USER0_CLOCK_OUT_EDGE ? 1 : 0;
|
||||
uint8_t cpol = SPI(bus).PIN & SPI_PIN_IDLE_EDGE ? 2 : 0;
|
||||
|
||||
return (spi_mode_t)(cpol | (cpol ? 1 - cpha : cpha)); // see spi_set_mode
|
||||
}
|
||||
|
||||
void spi_set_msb(uint8_t bus, bool msb)
|
||||
{
|
||||
if (msb)
|
||||
SPI(bus).CTRL0 &= ~(SPI_CTRL0_WR_BIT_ORDER | SPI_CTRL0_RD_BIT_ORDER);
|
||||
else
|
||||
SPI(bus).CTRL0 |= (SPI_CTRL0_WR_BIT_ORDER | SPI_CTRL0_RD_BIT_ORDER);
|
||||
}
|
||||
|
||||
void spi_set_endianness(uint8_t bus, spi_endianness_t endianness)
|
||||
{
|
||||
if (endianness == SPI_BIG_ENDIAN)
|
||||
SPI(bus).USER0 |= (SPI_USER0_WR_BYTE_ORDER | SPI_USER0_RD_BYTE_ORDER);
|
||||
else
|
||||
SPI(bus).USER0 &= ~(SPI_USER0_WR_BYTE_ORDER | SPI_USER0_RD_BYTE_ORDER);
|
||||
}
|
||||
|
||||
void spi_set_frequency_div(uint8_t bus, uint32_t divider)
|
||||
{
|
||||
uint32_t predivider = (divider & 0xffff) - 1;
|
||||
uint32_t count = (divider >> 16) - 1;
|
||||
if (count || predivider)
|
||||
{
|
||||
IOMUX.CONF &= ~(bus == 0 ? IOMUX_CONF_SPI0_CLOCK_EQU_SYS_CLOCK : IOMUX_CONF_SPI1_CLOCK_EQU_SYS_CLOCK);
|
||||
SPI(bus).CLOCK = VAL2FIELD_M(SPI_CLOCK_DIV_PRE, predivider) |
|
||||
VAL2FIELD_M(SPI_CLOCK_COUNT_NUM, count) |
|
||||
VAL2FIELD_M(SPI_CLOCK_COUNT_HIGH, count / 2) |
|
||||
VAL2FIELD_M(SPI_CLOCK_COUNT_LOW, count);
|
||||
}
|
||||
else
|
||||
{
|
||||
IOMUX.CONF |= bus == 0 ? IOMUX_CONF_SPI0_CLOCK_EQU_SYS_CLOCK : IOMUX_CONF_SPI1_CLOCK_EQU_SYS_CLOCK;
|
||||
SPI(bus).CLOCK = SPI_CLOCK_EQU_SYS_CLOCK;
|
||||
}
|
||||
}
|
||||
|
||||
inline static void _set_size(uint8_t bus, uint8_t bytes)
|
||||
{
|
||||
uint32_t bits = ((uint32_t)bytes << 3) - 1;
|
||||
SPI(bus).USER1 = SET_FIELD(SPI(bus).USER1, SPI_USER1_MISO_BITLEN, bits);
|
||||
SPI(bus).USER1 = SET_FIELD(SPI(bus).USER1, SPI_USER1_MOSI_BITLEN, bits);
|
||||
}
|
||||
|
||||
inline static void _wait(uint8_t bus)
|
||||
{
|
||||
while (SPI(bus).CMD & SPI_CMD_USR)
|
||||
;
|
||||
}
|
||||
|
||||
inline static void _start(uint8_t bus)
|
||||
{
|
||||
SPI(bus).CMD |= SPI_CMD_USR;
|
||||
}
|
||||
|
||||
inline static uint32_t _swap_bytes(uint32_t value)
|
||||
{
|
||||
return (value << 24) | ((value << 8) & 0x00ff0000) | ((value >> 8) & 0x0000ff00) | (value >> 24);
|
||||
}
|
||||
|
||||
inline static uint32_t _swap_words(uint32_t value)
|
||||
{
|
||||
return (value << 16) | (value >> 16);
|
||||
}
|
||||
|
||||
static void _spi_buf_prepare(uint8_t bus, size_t len, spi_endianness_t e, spi_word_size_t word_size)
|
||||
{
|
||||
if (e == SPI_LITTLE_ENDIAN || word_size == SPI_32BIT) return;
|
||||
|
||||
size_t count = word_size == SPI_16BIT ? (len + 1) / 2 : (len + 3) / 4;
|
||||
uint32_t *data = (uint32_t *)&SPI(bus).W0;
|
||||
for (size_t i = 0; i < count; i ++)
|
||||
{
|
||||
data[i] = word_size == SPI_16BIT
|
||||
? _swap_words(data[i])
|
||||
: _swap_bytes(data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void _spi_buf_transfer(uint8_t bus, const void *out_data, void *in_data,
|
||||
size_t len, spi_endianness_t e, spi_word_size_t word_size)
|
||||
{
|
||||
_wait(bus);
|
||||
size_t bytes = len * (uint8_t)word_size;
|
||||
_set_size(bus, bytes);
|
||||
memcpy((void *)&SPI(bus).W0, out_data, bytes);
|
||||
_spi_buf_prepare(bus, len, e, word_size);
|
||||
_start(bus);
|
||||
_wait(bus);
|
||||
if (in_data)
|
||||
{
|
||||
_spi_buf_prepare(bus, len, e, word_size);
|
||||
memcpy(in_data, (void *)&SPI(bus).W0, bytes);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t spi_transfer_8(uint8_t bus, uint8_t data)
|
||||
{
|
||||
uint8_t res;
|
||||
_spi_buf_transfer(bus, &data, &res, 1, spi_get_endianness(bus), SPI_8BIT);
|
||||
return res;
|
||||
}
|
||||
|
||||
uint16_t spi_transfer_16(uint8_t bus, uint16_t data)
|
||||
{
|
||||
uint16_t res;
|
||||
_spi_buf_transfer(bus, &data, &res, 1, spi_get_endianness(bus), SPI_16BIT);
|
||||
return res;
|
||||
}
|
||||
|
||||
uint32_t spi_transfer_32(uint8_t bus, uint32_t data)
|
||||
{
|
||||
uint32_t res;
|
||||
_spi_buf_transfer(bus, &data, &res, 1, spi_get_endianness(bus), SPI_32BIT);
|
||||
return res;
|
||||
}
|
||||
|
||||
size_t spi_transfer(uint8_t bus, const void *out_data, void *in_data, size_t len, spi_word_size_t word_size)
|
||||
{
|
||||
if (!out_data || !len) return 0;
|
||||
|
||||
spi_endianness_t e = spi_get_endianness(bus);
|
||||
uint8_t buf_size = _SPI_BUF_SIZE / (uint8_t)word_size;
|
||||
|
||||
size_t blocks = len / buf_size;
|
||||
for (size_t i = 0; i < blocks; i++)
|
||||
{
|
||||
size_t offset = i * _SPI_BUF_SIZE;
|
||||
_spi_buf_transfer(bus, (const uint8_t *)out_data + offset,
|
||||
in_data ? (uint8_t *)in_data + offset : NULL, buf_size, e, word_size);
|
||||
}
|
||||
|
||||
uint8_t tail = len % buf_size;
|
||||
if (tail)
|
||||
{
|
||||
_spi_buf_transfer(bus, (const uint8_t *)out_data + blocks * _SPI_BUF_SIZE,
|
||||
in_data ? (uint8_t *)in_data + blocks * _SPI_BUF_SIZE : NULL, tail, e, word_size);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
139
core/esp_timer.c
139
core/esp_timer.c
|
@ -6,30 +6,143 @@
|
|||
* BSD Licensed as described in the file LICENSE
|
||||
*/
|
||||
#include <esp/timer.h>
|
||||
#include <esp/dport_regs.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/*
|
||||
* These are the runtime implementations for functions that are linked in if any of
|
||||
* the arguments aren't known at compile time (values are evaluated at
|
||||
* compile time otherwise.)
|
||||
*/
|
||||
uint32_t _timer_freq_to_count_runtime(const timer_frc_t frc, const uint32_t freq, const timer_clkdiv_t div)
|
||||
/* Timer divisor number to maximum frequency */
|
||||
#define _FREQ_DIV1 (80*1000*1000)
|
||||
#define _FREQ_DIV16 (5*1000*1000)
|
||||
#define _FREQ_DIV256 312500
|
||||
|
||||
const static uint32_t IROM _TIMER_FREQS[] = { _FREQ_DIV1, _FREQ_DIV16, _FREQ_DIV256 };
|
||||
|
||||
/* Timer divisor index to divisor value */
|
||||
const static uint32_t IROM _TIMER_DIV_VAL[] = { 1, 16, 256 };
|
||||
|
||||
void timer_set_interrupts(const timer_frc_t frc, bool enable)
|
||||
{
|
||||
return _timer_freq_to_count_impl(frc, freq, div);
|
||||
const uint32_t dp_bit = (frc == FRC1) ? DPORT_INT_ENABLE_FRC1 : DPORT_INT_ENABLE_FRC2;
|
||||
const uint32_t int_mask = BIT((frc == FRC1) ? INUM_TIMER_FRC1 : INUM_TIMER_FRC2);
|
||||
if(enable) {
|
||||
DPORT.INT_ENABLE |= dp_bit;
|
||||
_xt_isr_unmask(int_mask);
|
||||
} else {
|
||||
DPORT.INT_ENABLE &= ~dp_bit;
|
||||
_xt_isr_mask(int_mask);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t _timer_time_to_count_runtime(const timer_frc_t frc, uint32_t us, const timer_clkdiv_t div)
|
||||
uint32_t timer_freq_to_count(const timer_frc_t frc, const uint32_t freq, const timer_clkdiv_t div)
|
||||
{
|
||||
return _timer_time_to_count_runtime(frc, us, div);
|
||||
if(div < TIMER_CLKDIV_1 || div > TIMER_CLKDIV_256)
|
||||
return 0; /* invalid divider */
|
||||
|
||||
if(freq > _TIMER_FREQS[div])
|
||||
return 0; /* out of range for given divisor */
|
||||
|
||||
uint64_t counts = _TIMER_FREQS[div]/freq;
|
||||
return counts;
|
||||
}
|
||||
|
||||
bool _timer_set_frequency_runtime(const timer_frc_t frc, uint32_t freq)
|
||||
uint32_t timer_time_to_count(const timer_frc_t frc, uint32_t us, const timer_clkdiv_t div)
|
||||
{
|
||||
return _timer_set_frequency_runtime(frc, freq);
|
||||
if(div < TIMER_CLKDIV_1 || div > TIMER_CLKDIV_256)
|
||||
return 0; /* invalid divider */
|
||||
|
||||
const uint32_t TIMER_MAX = timer_max_load(frc);
|
||||
|
||||
if(div != TIMER_CLKDIV_256) /* timer tick in MHz */
|
||||
{
|
||||
/* timer is either 80MHz or 5MHz, so either 80 or 5 MHz counts per us */
|
||||
const uint32_t counts_per_us = ((div == TIMER_CLKDIV_1) ? _FREQ_DIV1 : _FREQ_DIV16)/1000/1000;
|
||||
if(us > TIMER_MAX/counts_per_us)
|
||||
return 0; /* Multiplying us by mhz_per_count will overflow TIMER_MAX */
|
||||
return us*counts_per_us;
|
||||
}
|
||||
else /* /256 divider, 312.5kHz freq so need to scale up */
|
||||
{
|
||||
/* derived from naive floating point equation that we can't use:
|
||||
counts = (us/1000/1000)*_FREQ_DIV256;
|
||||
counts = (us/2000)*(_FREQ_DIV256/500);
|
||||
counts = us*(_FREQ_DIV256/500)/2000;
|
||||
*/
|
||||
const uint32_t scalar = _FREQ_DIV256/500;
|
||||
if(us > 1+UINT32_MAX/scalar)
|
||||
return 0; /* Multiplying us by _FREQ_DIV256/500 will overflow uint32_t */
|
||||
|
||||
uint32_t counts = (us*scalar)/2000;
|
||||
if(counts > TIMER_MAX)
|
||||
return 0; /* counts value too high for timer type */
|
||||
return counts;
|
||||
}
|
||||
}
|
||||
|
||||
bool _timer_set_timeout_runtime(const timer_frc_t frc, uint32_t us)
|
||||
int timer_set_frequency(const timer_frc_t frc, uint32_t freq)
|
||||
{
|
||||
return _timer_set_timeout_impl(frc, us);
|
||||
uint32_t counts = 0;
|
||||
timer_clkdiv_t div = timer_freq_to_div(freq);
|
||||
|
||||
counts = timer_freq_to_count(frc, freq, div);
|
||||
if(counts == 0)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
timer_set_divider(frc, div);
|
||||
if(frc == FRC1)
|
||||
{
|
||||
timer_set_load(frc, counts);
|
||||
timer_set_reload(frc, true);
|
||||
}
|
||||
else /* FRC2 */
|
||||
{
|
||||
/* assume that if this overflows it'll wrap, so we'll get desired behaviour */
|
||||
TIMER(1).ALARM = counts + TIMER(1).COUNT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _timer_set_timeout_impl(const timer_frc_t frc, uint32_t us)
|
||||
{
|
||||
uint32_t counts = 0;
|
||||
timer_clkdiv_t div = timer_time_to_div(us);
|
||||
|
||||
counts = timer_time_to_count(frc, us, div);
|
||||
if(counts == 0)
|
||||
return -EINVAL; /* can't set frequency */
|
||||
|
||||
timer_set_divider(frc, div);
|
||||
if(frc == FRC1)
|
||||
{
|
||||
timer_set_load(frc, counts);
|
||||
}
|
||||
else /* FRC2 */
|
||||
{
|
||||
TIMER(1).ALARM = counts + TIMER(1).COUNT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int timer_set_timeout(const timer_frc_t frc, uint32_t us)
|
||||
{
|
||||
uint32_t counts = 0;
|
||||
timer_clkdiv_t div = timer_time_to_div(us);
|
||||
|
||||
counts = timer_time_to_count(frc, us, div);
|
||||
if(counts == 0)
|
||||
return -EINVAL; /* can't set frequency */
|
||||
|
||||
timer_set_divider(frc, div);
|
||||
if(frc == FRC1)
|
||||
{
|
||||
timer_set_load(frc, counts);
|
||||
}
|
||||
else /* FRC2 */
|
||||
{
|
||||
TIMER(1).ALARM = counts + TIMER(1).COUNT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -53,8 +53,30 @@
|
|||
#define IROM __attribute__((section(".irom0.literal"))) const
|
||||
#endif
|
||||
|
||||
#define INLINED inline static __attribute__((always_inline)) __attribute__((unused))
|
||||
/* Use this macro to place functions into Instruction RAM (IRAM)
|
||||
instead of flash memory (IROM).
|
||||
|
||||
This is useful for functions which are called when the flash may
|
||||
not be available (for example during NMI exceptions), or for
|
||||
functions which are called very frequently and need high
|
||||
performance.
|
||||
|
||||
Bear in mind IRAM is limited (32KB), compared to up to 1MB of flash.
|
||||
*/
|
||||
#define IRAM __attribute__((section(".iram1.text")))
|
||||
|
||||
/* Use this macro to place read-only data into Instruction RAM (IRAM)
|
||||
instead of loaded into rodata which resides in DRAM.
|
||||
|
||||
This may be useful to free up data RAM. However all data read from
|
||||
the instruction space must be 32-bit aligned word reads
|
||||
(non-aligned reads will use an interrupt routine to "fix" them and
|
||||
still work, but are very slow..
|
||||
*/
|
||||
#ifdef __cplusplus
|
||||
#define IRAM_DATA __attribute__((section(".iram1.rodata")))
|
||||
#else
|
||||
#define IRAM_DATA __attribute__((section(".iram1.rodata"))) const
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -13,63 +13,74 @@
|
|||
#include "esp/iomux.h"
|
||||
#include "esp/interrupts.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
GPIO_INPUT,
|
||||
GPIO_OUTPUT, /* "Standard" push-pull output */
|
||||
GPIO_OUT_OPEN_DRAIN, /* Open drain output */
|
||||
GPIO_INPUT_PULLUP,
|
||||
} gpio_direction_t;
|
||||
|
||||
/* Enable GPIO on the specified pin, and set it to input/output/ with
|
||||
* pullup as needed
|
||||
/* Enable GPIO on the specified pin, and set it to input or output mode
|
||||
*/
|
||||
INLINED void gpio_enable(const uint8_t gpio_num, const gpio_direction_t direction)
|
||||
{
|
||||
uint32_t iomux_flags;
|
||||
void gpio_enable(const uint8_t gpio_num, const gpio_direction_t direction);
|
||||
|
||||
switch(direction) {
|
||||
case GPIO_INPUT:
|
||||
iomux_flags = 0;
|
||||
break;
|
||||
case GPIO_OUTPUT:
|
||||
iomux_flags = IOMUX_PIN_OUTPUT_ENABLE;
|
||||
break;
|
||||
case GPIO_OUT_OPEN_DRAIN:
|
||||
iomux_flags = IOMUX_PIN_OUTPUT_ENABLE;
|
||||
break;
|
||||
case GPIO_INPUT_PULLUP:
|
||||
iomux_flags = IOMUX_PIN_PULLUP;
|
||||
break;
|
||||
}
|
||||
iomux_set_gpio_function(gpio_num, iomux_flags);
|
||||
if(direction == GPIO_OUT_OPEN_DRAIN)
|
||||
GPIO.CONF[gpio_num] |= GPIO_CONF_OPEN_DRAIN;
|
||||
else
|
||||
GPIO.CONF[gpio_num] &= ~GPIO_CONF_OPEN_DRAIN;
|
||||
if (iomux_flags & IOMUX_PIN_OUTPUT_ENABLE)
|
||||
GPIO.ENABLE_OUT_SET = BIT(gpio_num);
|
||||
else
|
||||
GPIO.ENABLE_OUT_CLEAR = BIT(gpio_num);
|
||||
}
|
||||
/* Enable/disable internal pullup resistor for a particular GPIO
|
||||
*
|
||||
* Note: According to Espressif, pullup resistor values are between 30K and
|
||||
* 100K ohms (see http://bbs.espressif.com/viewtopic.php?t=1079#p4097)
|
||||
* However, measured values suggest that the actual value is likely to be close
|
||||
* to 47K in reality.
|
||||
*
|
||||
* NOTE: The enabled_during_sleep setting is currently untested (please send
|
||||
* feedback if you give it a try)
|
||||
*/
|
||||
void gpio_set_pullup(uint8_t gpio_num, bool enabled, bool enabled_during_sleep);
|
||||
|
||||
/* Disable GPIO on the specified pin, and set it Hi-Z.
|
||||
*
|
||||
* If later muxing this pin to a different function, make sure to set
|
||||
* IOMUX_PIN_OUTPUT_ENABLE if necessary to enable the output buffer.
|
||||
*/
|
||||
INLINED void gpio_disable(const uint8_t gpio_num)
|
||||
static inline void gpio_disable(const uint8_t gpio_num)
|
||||
{
|
||||
GPIO.ENABLE_OUT_CLEAR = BIT(gpio_num);
|
||||
*gpio_iomux_reg(gpio_num) &= ~IOMUX_PIN_OUTPUT_ENABLE;
|
||||
}
|
||||
|
||||
/* Set whether the specified pin continues to drive its output when the ESP8266
|
||||
* goes into sleep mode. Note that this setting is reset to off whenever
|
||||
* gpio_enable is called, so this must be called after calling that function.
|
||||
*
|
||||
* NOTE: This functionality is currently untested (please send feedback if you
|
||||
* give it a try)
|
||||
*/
|
||||
static inline void gpio_set_output_on_sleep(const uint8_t gpio_num, bool enabled)
|
||||
{
|
||||
if (enabled) {
|
||||
IOMUX.PIN[gpio_to_iomux(gpio_num)] |= IOMUX_PIN_OUTPUT_ENABLE_SLEEP;
|
||||
} else {
|
||||
IOMUX.PIN[gpio_to_iomux(gpio_num)] &= ~IOMUX_PIN_OUTPUT_ENABLE_SLEEP;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set output of a pin high or low.
|
||||
*
|
||||
* Only works if pin has been set to GPIO_OUTPUT via gpio_enable()
|
||||
* Only works if pin has been set to GPIO_OUTPUT or GPIO_OUT_OPEN_DRAIN via
|
||||
* gpio_enable()
|
||||
*
|
||||
* If the mode is GPIO_OUT_OPEN_DRAIN, setting it low (false) will pull the pin
|
||||
* down to ground, but setting it high (true) will allow it to float. Note
|
||||
* that even in GPIO_OUT_OPEN_DRAIN mode, the input gates are still physically
|
||||
* connected to the pin, and can be damaged if the voltage is not in either the
|
||||
* "low" or "high" range. Make sure there is some sort of pull-up resistor on
|
||||
* the line to avoid floating logic lines!
|
||||
*/
|
||||
INLINED void gpio_write(const uint8_t gpio_num, const bool set)
|
||||
static inline void gpio_write(const uint8_t gpio_num, const bool set)
|
||||
{
|
||||
if(set)
|
||||
if (set)
|
||||
GPIO.OUT_SET = BIT(gpio_num);
|
||||
else
|
||||
GPIO.OUT_CLEAR = BIT(gpio_num);
|
||||
|
@ -77,9 +88,12 @@ INLINED void gpio_write(const uint8_t gpio_num, const bool set)
|
|||
|
||||
/* Toggle output of a pin
|
||||
*
|
||||
* Only works if pin has been set to GPIO_OUTPUT via gpio_enable()
|
||||
* Only works if pin has been set to GPIO_OUTPUT or GPIO_OUT_OPEN_DRAIN via
|
||||
* gpio_enable()
|
||||
*
|
||||
* See notes in gpio_write() about GPIO_OUT_OPEN_DRAIN mode.
|
||||
*/
|
||||
INLINED void gpio_toggle(const uint8_t gpio_num)
|
||||
static inline void gpio_toggle(const uint8_t gpio_num)
|
||||
{
|
||||
/* Why implement like this instead of GPIO_OUT_REG ^= xxx?
|
||||
Concurrency. If an interrupt or higher priority task writes to
|
||||
|
@ -95,10 +109,14 @@ INLINED void gpio_toggle(const uint8_t gpio_num)
|
|||
|
||||
/* Read input value of a GPIO pin.
|
||||
*
|
||||
* If pin is set as an input, this reads the value on the pin.
|
||||
* If pin is set as an output, this reads the last value written to the pin.
|
||||
* If pin is set GPIO_INPUT, this reads the level on the pin.
|
||||
* If pin is set GPIO_OUTPUT, this reads the level at which the pin is
|
||||
* currently being driven (i.e. the last value written).
|
||||
* If pin is set GPIO_OUT_OPEN_DRAIN, when the pin is written low, this will
|
||||
* return low (false), when the pin is written high, this will behave like
|
||||
* GPIO_INPUT.
|
||||
*/
|
||||
INLINED bool gpio_read(const uint8_t gpio_num)
|
||||
static inline bool gpio_read(const uint8_t gpio_num)
|
||||
{
|
||||
return GPIO.IN & BIT(gpio_num);
|
||||
}
|
||||
|
@ -107,9 +125,10 @@ extern void gpio_interrupt_handler(void);
|
|||
|
||||
/* Set the interrupt type for a given pin
|
||||
*
|
||||
* If int_type is not GPIO_INTTYPE_NONE, the gpio_interrupt_handler will be attached and unmasked.
|
||||
* If int_type is not GPIO_INTTYPE_NONE, the gpio_interrupt_handler will be
|
||||
* attached and unmasked.
|
||||
*/
|
||||
INLINED void gpio_set_interrupt(const uint8_t gpio_num, const gpio_inttype_t int_type)
|
||||
static inline void gpio_set_interrupt(const uint8_t gpio_num, const gpio_inttype_t int_type)
|
||||
{
|
||||
GPIO.CONF[gpio_num] = SET_FIELD(GPIO.CONF[gpio_num], GPIO_CONF_INTTYPE, int_type);
|
||||
if(int_type != GPIO_INTTYPE_NONE) {
|
||||
|
@ -119,9 +138,13 @@ INLINED void gpio_set_interrupt(const uint8_t gpio_num, const gpio_inttype_t int
|
|||
}
|
||||
|
||||
/* Return the interrupt type set for a pin */
|
||||
INLINED gpio_inttype_t gpio_get_interrupt(const uint8_t gpio_num)
|
||||
static inline gpio_inttype_t gpio_get_interrupt(const uint8_t gpio_num)
|
||||
{
|
||||
return (gpio_inttype_t)FIELD2VAL(GPIO_CONF_INTTYPE, GPIO.CONF[gpio_num]);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
137
core/include/esp/i2s_regs.h
Normal file
137
core/include/esp/i2s_regs.h
Normal file
|
@ -0,0 +1,137 @@
|
|||
/* esp/i2s_regs.h
|
||||
*
|
||||
* ESP8266 I2S register definitions
|
||||
*
|
||||
* Not compatible with ESP SDK register access code.
|
||||
*/
|
||||
|
||||
#ifndef _ESP_I2S_REGS_H
|
||||
#define _ESP_I2S_REGS_H
|
||||
|
||||
#include "esp/types.h"
|
||||
#include "common_macros.h"
|
||||
|
||||
#define I2S_BASE 0x60000e00
|
||||
#define I2S (*(struct I2S_REGS *)I2S_BASE)
|
||||
|
||||
struct I2S_REGS {
|
||||
uint32_t volatile TXFIFO; // 0x00
|
||||
uint32_t volatile RXFIFO; // 0x04
|
||||
uint32_t volatile CONF; // 0x08
|
||||
uint32_t volatile INT_RAW; // 0x0c
|
||||
uint32_t volatile INT_STATUS; // 0x10
|
||||
uint32_t volatile INT_ENABLE; // 0x14
|
||||
uint32_t volatile INT_CLEAR; // 0x18
|
||||
uint32_t volatile TIMING; // 0x1c
|
||||
uint32_t volatile FIFO_CONF; // 0x20
|
||||
uint32_t volatile RX_EOF_NUM; // 0x24
|
||||
uint32_t volatile CONF_SINGLE_DATA; // 0x28
|
||||
uint32_t volatile CONF_CHANNELS; // 0x2c
|
||||
};
|
||||
|
||||
_Static_assert(sizeof(struct I2S_REGS) == 0x30, "I2S_REGS is the wrong size");
|
||||
|
||||
/* Details for CONF register */
|
||||
|
||||
#define I2S_CONF_BCK_DIV_M 0x0000003f
|
||||
#define I2S_CONF_BCK_DIV_S 22
|
||||
#define I2S_CONF_CLKM_DIV_M 0x0000003f
|
||||
#define I2S_CONF_CLKM_DIV_S 16
|
||||
#define I2S_CONF_BITS_MOD_M 0x0000000f
|
||||
#define I2S_CONF_BITS_MOD_S 12
|
||||
#define I2S_CONF_RX_MSB_SHIFT BIT(11)
|
||||
#define I2S_CONF_TX_MSB_SHIFT BIT(10)
|
||||
#define I2S_CONF_RX_START BIT(9)
|
||||
#define I2S_CONF_TX_START BIT(8)
|
||||
#define I2S_CONF_MSB_RIGHT BIT(7)
|
||||
#define I2S_CONF_RIGHT_FIRST BIT(6)
|
||||
#define I2S_CONF_RX_SLAVE_MOD BIT(5)
|
||||
#define I2S_CONF_TX_SLAVE_MOD BIT(4)
|
||||
#define I2S_CONF_RX_FIFO_RESET BIT(3)
|
||||
#define I2S_CONF_TX_FIFO_RESET BIT(2)
|
||||
#define I2S_CONF_RX_RESET BIT(1)
|
||||
#define I2S_CONF_TX_RESET BIT(0)
|
||||
#define I2S_CONF_RESET_MASK 0xf
|
||||
|
||||
/* Details for INT_RAW register */
|
||||
|
||||
#define I2S_INT_RAW_TX_REMPTY BIT(5)
|
||||
#define I2S_INT_RAW_TX_WFULL BIT(4)
|
||||
#define I2S_INT_RAW_RX_REMPTY BIT(3)
|
||||
#define I2S_INT_RAW_RX_WFULL BIT(2)
|
||||
#define I2S_INT_RAW_TX_PUT_DATA BIT(1)
|
||||
#define I2S_INT_RAW_RX_TAKE_DATA BIT(0)
|
||||
|
||||
/* Details for INT_STATUS register */
|
||||
|
||||
#define I2S_INT_STATUS_TX_REMPTY BIT(5)
|
||||
#define I2S_INT_STATUS_TX_WFULL BIT(4)
|
||||
#define I2S_INT_STATUS_RX_REMPTY BIT(3)
|
||||
#define I2S_INT_STATUS_RX_WFULL BIT(2)
|
||||
#define I2S_INT_STATUS_TX_PUT_DATA BIT(1)
|
||||
#define I2S_INT_STATUS_RX_TAKE_DATA BIT(0)
|
||||
|
||||
/* Details for INT_ENABLE register */
|
||||
|
||||
#define I2S_INT_ENABLE_TX_REMPTY BIT(5)
|
||||
#define I2S_INT_ENABLE_TX_WFULL BIT(4)
|
||||
#define I2S_INT_ENABLE_RX_REMPTY BIT(3)
|
||||
#define I2S_INT_ENABLE_RX_WFULL BIT(2)
|
||||
#define I2S_INT_ENABLE_TX_PUT_DATA BIT(1)
|
||||
#define I2S_INT_ENABLE_RX_TAKE_DATA BIT(0)
|
||||
|
||||
/* Details for INT_CLEAR register */
|
||||
|
||||
#define I2S_INT_CLEAR_TX_REMPTY BIT(5)
|
||||
#define I2S_INT_CLEAR_TX_WFULL BIT(4)
|
||||
#define I2S_INT_CLEAR_RX_REMPTY BIT(3)
|
||||
#define I2S_INT_CLEAR_RX_WFULL BIT(2)
|
||||
#define I2S_INT_CLEAR_TX_PUT_DATA BIT(1)
|
||||
#define I2S_INT_CLEAR_RX_TAKE_DATA BIT(0)
|
||||
|
||||
/* Details for TIMING register */
|
||||
|
||||
#define I2S_TIMING_TX_BCK_IN_INV BIT(22)
|
||||
#define I2S_TIMING_RX_DSYNC_SW BIT(21)
|
||||
#define I2S_TIMING_TX_DSYNC_SW BIT(20)
|
||||
#define I2S_TIMING_RX_BCK_OUT_DELAY_M 0x00000003
|
||||
#define I2S_TIMING_RX_BCK_OUT_DELAY_S 18
|
||||
#define I2S_TIMING_RX_WS_OUT_DELAY_M 0x00000003
|
||||
#define I2S_TIMING_RX_WS_OUT_DELAY_S 16
|
||||
#define I2S_TIMING_TX_SD_OUT_DELAY_M 0x00000003
|
||||
#define I2S_TIMING_TX_SD_OUT_DELAY_S 14
|
||||
#define I2S_TIMING_TX_WS_OUT_DELAY_M 0x00000003
|
||||
#define I2S_TIMING_TX_WS_OUT_DELAY_S 12
|
||||
#define I2S_TIMING_TX_BCK_OUT_DELAY_M 0x00000003
|
||||
#define I2S_TIMING_TX_BCK_OUT_DELAY_S 10
|
||||
#define I2S_TIMING_RX_SD_IN_DELAY_M 0x00000003
|
||||
#define I2S_TIMING_RX_SD_IN_DELAY_S 8
|
||||
#define I2S_TIMING_RX_WS_IN_DELAY 0x00000003
|
||||
#define I2S_TIMING_RX_WS_IN_DELAY_S 6
|
||||
#define I2S_TIMING_RX_BCK_IN_DELAY_M 0x00000003
|
||||
#define I2S_TIMING_RX_BCK_IN_DELAY_S 4
|
||||
#define I2S_TIMING_TX_WS_IN_DELAY_M 0x00000003
|
||||
#define I2S_TIMING_TX_WS_IN_DELAY_S 2
|
||||
#define I2S_TIMING_TX_BCK_IN_DELAY_M 0x00000003
|
||||
#define I2S_TIMING_TX_BCK_IN_DELAY_S 0
|
||||
|
||||
/* Details for FIFO_CONF register */
|
||||
|
||||
#define I2S_FIFO_CONF_RX_FIFO_MOD_M 0x00000007
|
||||
#define I2S_FIFO_CONF_RX_FIFO_MOD_S 16
|
||||
#define I2S_FIFO_CONF_TX_FIFO_MOD_M 0x00000007
|
||||
#define I2S_FIFO_CONF_TX_FIFO_MOD_S 13
|
||||
#define I2S_FIFO_CONF_DESCRIPTOR_ENABLE BIT(12)
|
||||
#define I2S_FIFO_CONF_TX_DATA_NUM_M 0x0000003f
|
||||
#define I2S_FIFO_CONF_TX_DATA_NUM_S 6
|
||||
#define I2S_FIFO_CONF_RX_DATA_NUM_M 0x0000003f
|
||||
#define I2S_FIFO_CONF_RX_DATA_NUM_S 0
|
||||
|
||||
/* Details for CONF_CHANNEL register */
|
||||
|
||||
#define I2S_CONF_CHANNELS_RX_CHANNEL_MOD_M 0x00000003
|
||||
#define I2S_CONF_CHANNELS_RX_CHANNEL_MOD_S 3
|
||||
#define I2S_CONF_CHANNELS_TX_CHANNEL_MOD_M 0x00000007
|
||||
#define I2S_CONF_CHANNELS_TX_CHANNEL_MOD_S 0
|
||||
|
||||
#endif /* _ESP_I2S_REGS_H */
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
/* Interrupt numbers for level 1 exception handler. */
|
||||
typedef enum {
|
||||
INUM_SLC = 1,
|
||||
INUM_SPI = 2,
|
||||
INUM_GPIO = 4,
|
||||
INUM_UART = 5,
|
||||
|
@ -38,7 +39,7 @@ void sdk__xt_tick_timer_init (void);
|
|||
void sdk__xt_timer_int(void);
|
||||
void sdk__xt_timer_int1(void);
|
||||
|
||||
INLINED uint32_t _xt_get_intlevel(void)
|
||||
static inline uint32_t _xt_get_intlevel(void)
|
||||
{
|
||||
uint32_t level;
|
||||
__asm__ volatile("rsr %0, intlevel" : "=a"(level));
|
||||
|
@ -52,7 +53,7 @@ INLINED uint32_t _xt_get_intlevel(void)
|
|||
portDISABLE_INTERRUPTS/portENABLE_INTERRUPTS for
|
||||
non-FreeRTOS & non-portable code.
|
||||
*/
|
||||
INLINED uint32_t _xt_disable_interrupts(void)
|
||||
static inline uint32_t _xt_disable_interrupts(void)
|
||||
{
|
||||
uint32_t old_level;
|
||||
__asm__ volatile ("rsil %0, " XTSTR(XCHAL_EXCM_LEVEL) : "=a" (old_level));
|
||||
|
@ -60,14 +61,14 @@ INLINED uint32_t _xt_disable_interrupts(void)
|
|||
}
|
||||
|
||||
/* Restore PS level. Intended to be used with _xt_disable_interrupts */
|
||||
INLINED void _xt_restore_interrupts(uint32_t new_ps)
|
||||
static inline void _xt_restore_interrupts(uint32_t new_ps)
|
||||
{
|
||||
__asm__ volatile ("wsr %0, ps; rsync" :: "a" (new_ps));
|
||||
}
|
||||
|
||||
/* ESPTODO: the mask/unmask functions aren't thread safe */
|
||||
|
||||
INLINED void _xt_isr_unmask(uint32_t unmask)
|
||||
static inline void _xt_isr_unmask(uint32_t unmask)
|
||||
{
|
||||
uint32_t intenable;
|
||||
asm volatile ("rsr %0, intenable" : "=a" (intenable));
|
||||
|
@ -75,7 +76,7 @@ INLINED void _xt_isr_unmask(uint32_t unmask)
|
|||
asm volatile ("wsr %0, intenable; esync" :: "a" (intenable));
|
||||
}
|
||||
|
||||
INLINED void _xt_isr_mask (uint32_t mask)
|
||||
static inline void _xt_isr_mask (uint32_t mask)
|
||||
{
|
||||
uint32_t intenable;
|
||||
asm volatile ("rsr %0, intenable" : "=a" (intenable));
|
||||
|
@ -83,14 +84,14 @@ INLINED void _xt_isr_mask (uint32_t mask)
|
|||
asm volatile ("wsr %0, intenable; esync" :: "a" (intenable));
|
||||
}
|
||||
|
||||
INLINED uint32_t _xt_read_ints (void)
|
||||
static inline uint32_t _xt_read_ints (void)
|
||||
{
|
||||
uint32_t interrupt;
|
||||
asm volatile ("rsr %0, interrupt" : "=a" (interrupt));
|
||||
return interrupt;
|
||||
}
|
||||
|
||||
INLINED void _xt_clear_ints(uint32_t mask)
|
||||
static inline void _xt_clear_ints(uint32_t mask)
|
||||
{
|
||||
asm volatile ("wsr %0, intclear; esync" :: "a" (mask));
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ extern "C" {
|
|||
* known at compile time, or return the result from a lookup table if not.
|
||||
*
|
||||
*/
|
||||
inline static uint8_t gpio_to_iomux(const uint8_t gpio_number);
|
||||
uint8_t IRAM gpio_to_iomux(const uint8_t gpio_number);
|
||||
|
||||
/**
|
||||
* Convert an iomux register index to a GPIO pin number.
|
||||
|
@ -31,7 +31,7 @@ inline static uint8_t gpio_to_iomux(const uint8_t gpio_number);
|
|||
* known at compile time, or return the result from a lookup table if not.
|
||||
*
|
||||
*/
|
||||
inline static uint8_t iomux_to_gpio(const uint8_t iomux_num);
|
||||
uint8_t IRAM iomux_to_gpio(const uint8_t iomux_num);
|
||||
|
||||
/**
|
||||
* Directly get the IOMUX register for a particular gpio number
|
||||
|
@ -43,31 +43,43 @@ inline static esp_reg_t gpio_iomux_reg(const uint8_t gpio_number)
|
|||
return &(IOMUX.PIN[gpio_to_iomux(gpio_number)]);
|
||||
}
|
||||
|
||||
inline static void iomux_set_function(uint8_t iomux_num, uint32_t func)
|
||||
{
|
||||
uint32_t prev = IOMUX.PIN[iomux_num] & ~IOMUX_PIN_FUNC_MASK;
|
||||
IOMUX.PIN[iomux_num] = IOMUX_FUNC(func) | prev;
|
||||
}
|
||||
|
||||
inline static void iomux_set_direction_flags(uint8_t iomux_num, uint32_t dir_flags)
|
||||
{
|
||||
uint32_t mask = IOMUX_PIN_OUTPUT_ENABLE | IOMUX_PIN_OUTPUT_ENABLE_SLEEP;
|
||||
uint32_t prev = IOMUX.PIN[iomux_num] & ~mask;
|
||||
IOMUX.PIN[iomux_num] = dir_flags | prev;
|
||||
}
|
||||
|
||||
inline static void iomux_set_pullup_flags(uint8_t iomux_num, uint32_t pullup_flags)
|
||||
{
|
||||
uint32_t mask = IOMUX_PIN_PULLUP | IOMUX_PIN_PULLDOWN | IOMUX_PIN_PULLUP_SLEEP | IOMUX_PIN_PULLDOWN_SLEEP;
|
||||
uint32_t prev = IOMUX.PIN[iomux_num] & ~mask;
|
||||
IOMUX.PIN[iomux_num] = pullup_flags | prev;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a pin to the GPIO function.
|
||||
*
|
||||
* This allows you to set pins to GPIO without knowing in advance the
|
||||
* exact register masks to use.
|
||||
*
|
||||
* flags can be any of IOMUX_PIN_OUTPUT_ENABLE, IOMUX_PIN_PULLUP, IOMUX_PIN_PULLDOWN, etc. Any other flags will be cleared.
|
||||
*
|
||||
* Equivalent to a direct register operation if gpio_number is known at compile time.
|
||||
* ie the following are equivalent:
|
||||
*
|
||||
* iomux_set_gpio_function(12, IOMUX_PIN_OUTPUT_ENABLE);
|
||||
* IOMUX_GPIO12 = IOMUX_GPIO12_FUNC_GPIO | IOMUX_PIN_OUTPUT_ENABLE;
|
||||
* Sets the function and direction, but leaves the pullup configuration the
|
||||
* same as before.
|
||||
*/
|
||||
inline static void iomux_set_gpio_function(const uint8_t gpio_number, const uint32_t flags)
|
||||
inline static void iomux_set_gpio_function(uint8_t gpio_number, bool output_enable)
|
||||
{
|
||||
const uint8_t reg_idx = gpio_to_iomux(gpio_number);
|
||||
const uint32_t func = (reg_idx > 11 ? IOMUX_FUNC(0) : IOMUX_FUNC(3)) | flags;
|
||||
IOMUX.PIN[reg_idx] = func | flags;
|
||||
const uint8_t iomux_num = gpio_to_iomux(gpio_number);
|
||||
const uint32_t func = iomux_num > 11 ? 0 : 3;
|
||||
iomux_set_function(iomux_num, func);
|
||||
iomux_set_direction_flags(iomux_num, output_enable ? IOMUX_PIN_OUTPUT_ENABLE : 0);
|
||||
}
|
||||
|
||||
/* esp_iomux_private contains implementation parts of the inline functions
|
||||
declared above */
|
||||
#include "esp/iomux_private.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
/** esp/iomux_private.h
|
||||
*
|
||||
* Private implementation parts of iomux registers. In headers to
|
||||
* allow compile-time optimisations.
|
||||
*
|
||||
* Part of esp-open-rtos
|
||||
* Copyright (C) 2015 Superhouse Automation Pty Ltd
|
||||
* BSD Licensed as described in the file LICENSE
|
||||
*/
|
||||
|
||||
/* Mapping from register index to GPIO and from GPIO index to register
|
||||
number. DO NOT USE THESE IN YOUR CODE, call gpio_to_iomux(xxx) or
|
||||
iomux_to_gpio(xxx) instead.
|
||||
*/
|
||||
#ifndef _IOMUX_PRIVATE
|
||||
#define _IOMUX_PRIVATE
|
||||
|
||||
#include "common_macros.h"
|
||||
|
||||
#define _IOMUX_TO_GPIO { 12, 13, 14, 15, 3, 1, 6, 7, 8, 9, 10, 11, 0, 2, 4, 5 }
|
||||
#define _GPIO_TO_IOMUX { 12, 5, 13, 4, 14, 15, 6, 7, 8, 9, 10, 11, 0, 1, 2, 3 }
|
||||
|
||||
extern const IROM uint32_t GPIO_TO_IOMUX_MAP[];
|
||||
extern const IROM uint32_t IOMUX_TO_GPIO_MAP[];
|
||||
|
||||
INLINED uint8_t gpio_to_iomux(const uint8_t gpio_number)
|
||||
{
|
||||
if(__builtin_constant_p(gpio_number)) {
|
||||
static const uint8_t _regs[] = _GPIO_TO_IOMUX;
|
||||
return _regs[gpio_number];
|
||||
} else {
|
||||
return GPIO_TO_IOMUX_MAP[gpio_number];
|
||||
}
|
||||
}
|
||||
|
||||
INLINED uint8_t iomux_to_gpio(const uint8_t iomux_number)
|
||||
{
|
||||
if(__builtin_constant_p(iomux_number)) {
|
||||
static const uint8_t _regs[] = _IOMUX_TO_GPIO;
|
||||
return _regs[iomux_number];
|
||||
} else {
|
||||
return IOMUX_TO_GPIO_MAP[iomux_number];
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -40,9 +40,9 @@ _Static_assert(sizeof(struct IOMUX_REGS) == 0x44, "IOMUX_REGS is the wrong size"
|
|||
#define IOMUX_PIN_PULLDOWN BIT(6)
|
||||
#define IOMUX_PIN_PULLUP BIT(7)
|
||||
#define IOMUX_PIN_FUNC_HIGH_M 0x00000004
|
||||
#define IOMUX_PIN_FUNC_HIGH_S 8
|
||||
#define IOMUX_PIN_FUNC_HIGH_S 6
|
||||
|
||||
#define IOMUX_PIN_FUNC_MASK 0x00001030
|
||||
#define IOMUX_PIN_FUNC_MASK 0x00000130
|
||||
|
||||
/* WARNING: Macro evaluates argument twice */
|
||||
#define IOMUX_FUNC(val) (VAL2FIELD_M(IOMUX_PIN_FUNC_LOW, val) | VAL2FIELD_M(IOMUX_PIN_FUNC_HIGH, val))
|
||||
|
@ -80,8 +80,8 @@ _Static_assert(sizeof(struct IOMUX_REGS) == 0x44, "IOMUX_REGS is the wrong size"
|
|||
|
||||
#define IOMUX_GPIO2_FUNC_GPIO IOMUX_FUNC(0)
|
||||
#define IOMUX_GPIO2_FUNC_I2SO_WS IOMUX_FUNC(1)
|
||||
#define IOMUX_GPIO2_FUNC_UART1_TXD_BLINK IOMUX_FUNC(2)
|
||||
#define IOMUX_GPIO2_FUNC_UART0_TXD_BLINK IOMUX_FUNC(4)
|
||||
#define IOMUX_GPIO2_FUNC_UART1_TXD IOMUX_FUNC(2)
|
||||
#define IOMUX_GPIO2_FUNC_UART0_TXD IOMUX_FUNC(4)
|
||||
|
||||
#define IOMUX_GPIO3_FUNC_UART0_RXD IOMUX_FUNC(0)
|
||||
#define IOMUX_GPIO3_FUNC_I2SO_DATA IOMUX_FUNC(1)
|
||||
|
|
|
@ -16,14 +16,17 @@
|
|||
#include "common_macros.h"
|
||||
#include "esp/types.h"
|
||||
|
||||
#include "esp/uart_regs.h"
|
||||
#include "esp/spi_regs.h"
|
||||
#include "esp/iomux_regs.h"
|
||||
#include "esp/dport_regs.h"
|
||||
#include "esp/gpio_regs.h"
|
||||
#include "esp/i2s_regs.h"
|
||||
#include "esp/iomux_regs.h"
|
||||
#include "esp/rtc_regs.h"
|
||||
#include "esp/rtcmem_regs.h"
|
||||
#include "esp/slc_regs.h"
|
||||
#include "esp/spi_regs.h"
|
||||
#include "esp/timer_regs.h"
|
||||
#include "esp/wdt_regs.h"
|
||||
#include "esp/rtcmem_regs.h"
|
||||
#include "esp/dport_regs.h"
|
||||
#include "esp/uart_regs.h"
|
||||
|
||||
/* Register base addresses
|
||||
|
||||
|
@ -44,7 +47,7 @@
|
|||
//#define SPI_BASE (MMIO_BASE + 0x0200)
|
||||
//#define GPIO0_BASE (MMIO_BASE + 0x0300)
|
||||
//#define TIMER_BASE (MMIO_BASE + 0x0600)
|
||||
#define RTC_BASE (MMIO_BASE + 0x0700)
|
||||
//#define RTC_BASE (MMIO_BASE + 0x0700)
|
||||
//#define IOMUX_BASE (MMIO_BASE + 0x0800)
|
||||
//#define WDT_BASE (MMIO_BASE + 0x0900)
|
||||
#define I2C_BASE (MMIO_BASE + 0x0d00)
|
||||
|
|
|
@ -3,6 +3,16 @@
|
|||
* ESP8266 RTC register definitions
|
||||
*
|
||||
* Not compatible with ESP SDK register access code.
|
||||
*
|
||||
* RTC peripheral remains powered during deep sleep, and RTC clock
|
||||
* is used to wake from deep sleep when RTC.COUNTER == RTC.COUNTER_ALARM.
|
||||
*
|
||||
* "GPIO16" is a special GPIO pin connected to the RTC subsystem,
|
||||
* GPIO16 must be connected to reset to allow wake from deep sleep.
|
||||
*
|
||||
* The contents of scratch registers RTC.SCRATCH[] are preserved
|
||||
* across reset, including wake from sleep (unconfirmed). Contents of
|
||||
* RTCMEM are also preserved.
|
||||
*/
|
||||
|
||||
#ifndef _ESP_RTC_REGS_H
|
||||
|
@ -18,7 +28,7 @@
|
|||
// Note: GPIO_CFG[3] is also known as PAD_XPD_DCDC_CONF in eagle_soc.h
|
||||
|
||||
struct RTC_REGS {
|
||||
uint32_t volatile _unknown0; // 0x00
|
||||
uint32_t volatile CTRL0; // 0x00
|
||||
uint32_t volatile COUNTER_ALARM; // 0x04
|
||||
uint32_t volatile RESET_REASON0; // 0x08 //FIXME: need better name
|
||||
uint32_t volatile _unknownc[2]; // 0x0c - 0x10
|
||||
|
@ -42,6 +52,14 @@ struct RTC_REGS {
|
|||
|
||||
_Static_assert(sizeof(struct RTC_REGS) == 0xac, "RTC_REGS is the wrong size");
|
||||
|
||||
/* Details for CTRL0 register */
|
||||
|
||||
/* Writing this bit causes a software reset but
|
||||
the device then fails in ets_main.c (needs other parameters set?) */
|
||||
#define RTC_CTRL0_BIT31 BIT(31)
|
||||
|
||||
/* Details for RESET_REASONx registers */
|
||||
|
||||
/* The following are used in sdk_rtc_get_reset_reason(). Details are still a
|
||||
* bit sketchy regarding exactly what they mean/do.. */
|
||||
|
||||
|
@ -51,6 +69,32 @@ _Static_assert(sizeof(struct RTC_REGS) == 0xac, "RTC_REGS is the wrong size");
|
|||
#define RTC_RESET_REASON2_CODE_M 0x0000003f
|
||||
#define RTC_RESET_REASON2_CODE_S 8
|
||||
|
||||
#define RTC_RESET_REASON0_SOMETHING BIT(21)
|
||||
/* Writing this bit causes the ESP to go into some kind of unrecoverable boot loop */
|
||||
#define RTC_RESET_REASON0_BIT20 BIT(20)
|
||||
|
||||
/* Both bits 20 & 21 can be set & cleared from software,
|
||||
BIT21 appears to be checked inside sdk_rtc_get_reset_reason() */
|
||||
#define RTC_RESET_REASON0_BIT21 BIT(21)
|
||||
#define RTC_RESET_REASON0_BIT22 BIT(22)
|
||||
|
||||
/* Details for GPIO_CONF register */
|
||||
|
||||
#define RTC_GPIO_CONF_OUT_ENABLE BIT(0)
|
||||
|
||||
/* Details for GPIO_CFG[3] register controlling GPIO16 (possibly others?) */
|
||||
|
||||
#define RTC_GPIO_CFG3_PIN_PULLUP BIT(2)
|
||||
#define RTC_GPIO_CFG3_PIN_PULLDOWN BIT(3)
|
||||
#define RTC_GPIO_CFG3_PIN_PULLUP_SLEEP BIT(4)
|
||||
#define RTC_GPIO_CFG3_PIN_PULLDOWN_SLEEP BIT(5)
|
||||
|
||||
/* The PIN_FUNC values here are probably similar to the
|
||||
values used to set the iomux registers...? */
|
||||
#define RTC_GPIO_CFG3_PIN_FUNC_M 0x00000043
|
||||
#define RTC_GPIO_CFG3_PIN_FUNC_S 0
|
||||
|
||||
/* This should be the function value needed to have GPIO16 be the alarm
|
||||
output from the RTC. FIXME: Needs to be validated. */
|
||||
#define RTC_GPIO_CFG3_PIN_FUNC_RTC_GPIO0 BIT(0)
|
||||
|
||||
#endif /* _ESP_RTC_REGS_H */
|
||||
|
|
37
core/include/esp/slc.h
Normal file
37
core/include/esp/slc.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
/* esp/slc_regs.h
|
||||
*
|
||||
* ESP8266 SLC functions
|
||||
*/
|
||||
|
||||
#ifndef _ESP_SLC_H
|
||||
#define _ESP_SLC_H
|
||||
|
||||
#include "esp/slc_regs.h"
|
||||
|
||||
/* Memory layout for DMA transfer descriptors. */
|
||||
|
||||
struct SLCDescriptor
|
||||
{
|
||||
uint32_t flags;
|
||||
uint32_t buf_ptr;
|
||||
uint32_t next_link_ptr;
|
||||
};
|
||||
|
||||
#define SLC_DESCRIPTOR_FLAGS_BLOCKSIZE_M 0x00000fff
|
||||
#define SLC_DESCRIPTOR_FLAGS_BLOCKSIZE_S 0
|
||||
#define SLC_DESCRIPTOR_FLAGS_DATA_LENGTH_M 0x00000fff
|
||||
#define SLC_DESCRIPTOR_FLAGS_DATA_LENGTH_S 12
|
||||
#define SLC_DESCRIPTOR_FLAGS_SUB_SOF BIT(29)
|
||||
#define SLC_DESCRIPTOR_FLAGS_EOF BIT(30)
|
||||
#define SLC_DESCRIPTOR_FLAGS_OWNER BIT(31)
|
||||
|
||||
#define SLC_DESCRIPTOR_FLAGS(blocksize,datalen,sub_sof,eof,owner) ( \
|
||||
VAL2FIELD_M(SLC_DESCRIPTOR_FLAGS_BLOCKSIZE,blocksize)| \
|
||||
VAL2FIELD_M(SLC_DESCRIPTOR_FLAGS_DATA_LENGTH,datalen)| \
|
||||
((sub_sof)?SLC_DESCRIPTOR_FLAGS_SUB_SOF:0)| \
|
||||
((eof)?SLC_DESCRIPTOR_FLAGS_EOF:0)| \
|
||||
((owner)?SLC_DESCRIPTOR_FLAGS_OWNER:0) \
|
||||
)
|
||||
|
||||
|
||||
#endif /* _ESP_SLC_REGS_H */
|
289
core/include/esp/slc_regs.h
Normal file
289
core/include/esp/slc_regs.h
Normal file
|
@ -0,0 +1,289 @@
|
|||
/* esp/slc_regs.h
|
||||
*
|
||||
* ESP8266 SLC register definitions
|
||||
*
|
||||
* Not compatible with ESP SDK register access code.
|
||||
*/
|
||||
|
||||
#ifndef _ESP_SLC_REGS_H
|
||||
#define _ESP_SLC_REGS_H
|
||||
|
||||
#include "esp/types.h"
|
||||
#include "common_macros.h"
|
||||
|
||||
#define SLC_BASE 0x60000b00
|
||||
#define SLC (*(struct SLC_REGS *)SLC_BASE)
|
||||
|
||||
struct SLC_REGS {
|
||||
uint32_t volatile CONF0; // 0x00
|
||||
uint32_t volatile INT_RAW; // 0x04
|
||||
uint32_t volatile INT_STATUS; // 0x08
|
||||
uint32_t volatile INT_ENABLE; // 0x0c
|
||||
uint32_t volatile INT_CLEAR; // 0x10
|
||||
uint32_t volatile RX_STATUS; // 0x14
|
||||
uint32_t volatile RX_FIFO_PUSH; // 0x18
|
||||
uint32_t volatile TX_STATUS; // 0x1c
|
||||
uint32_t volatile TX_FIFO_POP; // 0x20
|
||||
uint32_t volatile RX_LINK; // 0x24
|
||||
uint32_t volatile TX_LINK; // 0x28
|
||||
uint32_t volatile INTVEC_TO_HOST; // 0x2c
|
||||
uint32_t volatile TOKEN0; // 0x30
|
||||
uint32_t volatile TOKEN1; // 0x34
|
||||
uint32_t volatile CONF1; // 0x38
|
||||
uint32_t volatile STATE0; // 0x3c
|
||||
uint32_t volatile STATE1; // 0x40
|
||||
uint32_t volatile BRIDGE_CONF; // 0x44
|
||||
uint32_t volatile RX_EOF_DESCRIPTOR_ADDR; // 0x48
|
||||
uint32_t volatile TX_EOF_DESCRIPTOR_ADDR; // 0x4c
|
||||
uint32_t volatile RX_EOF_BUFFER_DESCRIPTOR_ADDR; // 0x50 - Naming uncertain
|
||||
uint32_t volatile AHB_TEST; // 0x54
|
||||
uint32_t volatile SDIO_STATUS; // 0x58
|
||||
uint32_t volatile RX_DESCRIPTOR_CONF; // 0x5c
|
||||
uint32_t volatile TX_LINK_DESCRIPTOR; // 0x60
|
||||
uint32_t volatile TX_LINK_DESCRIPTOR_BF0; // 0x64
|
||||
uint32_t volatile TX_LINK_DESCRIPTOR_BF1; // 0x68
|
||||
uint32_t volatile RX_LINK_DESCRIPTOR; // 0x6c
|
||||
uint32_t volatile RX_LINK_DESCRIPTOR_BF0; // 0x70
|
||||
uint32_t volatile RX_LINK_DESCRIPTOR_BF1; // 0x74
|
||||
uint32_t volatile DATE; // 0x78
|
||||
uint32_t volatile ID; // 0x7c
|
||||
uint32_t volatile UNKNOWN_80; // 0x80
|
||||
uint32_t volatile UNKNOWN_84; // 0x84
|
||||
uint32_t volatile HOST_INT_RAW; // 0x88
|
||||
uint32_t volatile UNKNOWN_8C; // 0x8c
|
||||
uint32_t volatile UNKNOWN_90; // 0x90
|
||||
uint32_t volatile HOST_CONF_W0; // 0x94
|
||||
uint32_t volatile HOST_CONF_W1; // 0x98
|
||||
uint32_t volatile HOST_INT_STATUS; // 0x9c
|
||||
uint32_t volatile HOST_CONF_W2; // 0xa0
|
||||
uint32_t volatile HOST_CONF_W3; // 0xa4
|
||||
uint32_t volatile HOST_CONF_W4; // 0xa8
|
||||
uint32_t volatile UNKNOWN_AC; // 0xac
|
||||
uint32_t volatile HOST_INT_CLEAR; // 0xb0
|
||||
uint32_t volatile HOST_INT_ENABLE; // 0xb4
|
||||
uint32_t volatile UNKNOWN_B8; // 0xb8
|
||||
uint32_t volatile HOST_CONF_W5; // 0xbc
|
||||
};
|
||||
|
||||
_Static_assert(sizeof(struct SLC_REGS) == 0xc0, "SLC_REGS is the wrong size");
|
||||
|
||||
/* Details for CONF0 register */
|
||||
|
||||
#define SLC_CONF0_MODE_M 0x00000003
|
||||
#define SLC_CONF0_MODE_S 12
|
||||
#define SLC_CONF0_DATA_BURST_ENABLE BIT(9)
|
||||
#define SLC_CONF0_DESCRIPTOR_BURST_ENABLE BIT(8)
|
||||
#define SLC_CONF0_RX_NO_RESTART_CLEAR BIT(7)
|
||||
#define SLC_CONF0_RX_AUTO_WRITE_BACK BIT(6)
|
||||
#define SLC_CONF0_RX_LOOP_TEST BIT(5)
|
||||
#define SLC_CONF0_TX_LOOP_TEST BIT(4)
|
||||
#define SLC_CONF0_AHBM_RESET BIT(3)
|
||||
#define SLC_CONF0_AHBM_FIFO_RESET BIT(2)
|
||||
#define SLC_CONF0_RX_LINK_RESET BIT(1)
|
||||
#define SLC_CONF0_TX_LINK_RESET BIT(0)
|
||||
|
||||
/* Details for INT_RAW register */
|
||||
|
||||
#define SLC_INT_RAW_TX_DSCR_EMPTY BIT(21)
|
||||
#define SLC_INT_RAW_RX_DSCR_ERROR BIT(20)
|
||||
#define SLC_INT_RAW_TX_DSCR_ERROR BIT(19)
|
||||
#define SLC_INT_RAW_TO_HOST BIT(18)
|
||||
#define SLC_INT_RAW_RX_EOF BIT(17)
|
||||
#define SLC_INT_RAW_RX_DONE BIT(16)
|
||||
#define SLC_INT_RAW_TX_EOF BIT(15)
|
||||
#define SLC_INT_RAW_TX_DONE BIT(14)
|
||||
#define SLC_INT_RAW_TOKEN1_1TO0 BIT(13)
|
||||
#define SLC_INT_RAW_TOKEN0_1TO0 BIT(12)
|
||||
#define SLC_INT_RAW_TX_OVERFLOW BIT(11)
|
||||
#define SLC_INT_RAW_RX_UNDEFLOW BIT(10)
|
||||
#define SLC_INT_RAW_TX_START BIT(9)
|
||||
#define SLC_INT_RAW_RX_START BIT(8)
|
||||
#define SLC_INT_RAW_FROM_HOST_BIT7 BIT(7)
|
||||
#define SLC_INT_RAW_FROM_HOST_BIT6 BIT(6)
|
||||
#define SLC_INT_RAW_FROM_HOST_BIT5 BIT(5)
|
||||
#define SLC_INT_RAW_FROM_HOST_BIT4 BIT(4)
|
||||
#define SLC_INT_RAW_FROM_HOST_BIT3 BIT(3)
|
||||
#define SLC_INT_RAW_FROM_HOST_BIT2 BIT(2)
|
||||
#define SLC_INT_RAW_FROM_HOST_BIT1 BIT(1)
|
||||
#define SLC_INT_RAW_FROM_HOST_BIT0 BIT(0)
|
||||
|
||||
/* Details for INT_STATUS register */
|
||||
|
||||
#define SLC_INT_STATUS_TX_DSCR_EMPTY BIT(21)
|
||||
#define SLC_INT_STATUS_RX_DSCR_ERROR BIT(20)
|
||||
#define SLC_INT_STATUS_TX_DSCR_ERROR BIT(19)
|
||||
#define SLC_INT_STATUS_TO_HOST BIT(18)
|
||||
#define SLC_INT_STATUS_RX_EOF BIT(17)
|
||||
#define SLC_INT_STATUS_RX_DONE BIT(16)
|
||||
#define SLC_INT_STATUS_TX_EOF BIT(15)
|
||||
#define SLC_INT_STATUS_TX_DONE BIT(14)
|
||||
#define SLC_INT_STATUS_TOKEN1_1TO0 BIT(13)
|
||||
#define SLC_INT_STATUS_TOKEN0_1TO0 BIT(12)
|
||||
#define SLC_INT_STATUS_TX_OVERFLOW BIT(11)
|
||||
#define SLC_INT_STATUS_RX_UNDEFLOW BIT(10)
|
||||
#define SLC_INT_STATUS_TX_START BIT(9)
|
||||
#define SLC_INT_STATUS_RX_START BIT(8)
|
||||
#define SLC_INT_STATUS_FROM_HOST_BIT7 BIT(7)
|
||||
#define SLC_INT_STATUS_FROM_HOST_BIT6 BIT(6)
|
||||
#define SLC_INT_STATUS_FROM_HOST_BIT5 BIT(5)
|
||||
#define SLC_INT_STATUS_FROM_HOST_BIT4 BIT(4)
|
||||
#define SLC_INT_STATUS_FROM_HOST_BIT3 BIT(3)
|
||||
#define SLC_INT_STATUS_FROM_HOST_BIT2 BIT(2)
|
||||
#define SLC_INT_STATUS_FROM_HOST_BIT1 BIT(1)
|
||||
#define SLC_INT_STATUS_FROM_HOST_BIT0 BIT(0)
|
||||
|
||||
/* Details for INT_ENABLE register */
|
||||
|
||||
#define SLC_INT_ENABLE_TX_DSCR_EMPTY BIT(21)
|
||||
#define SLC_INT_ENABLE_RX_DSCR_ERROR BIT(20)
|
||||
#define SLC_INT_ENABLE_TX_DSCR_ERROR BIT(19)
|
||||
#define SLC_INT_ENABLE_TO_HOST BIT(18)
|
||||
#define SLC_INT_ENABLE_RX_EOF BIT(17)
|
||||
#define SLC_INT_ENABLE_RX_DONE BIT(16)
|
||||
#define SLC_INT_ENABLE_TX_EOF BIT(15)
|
||||
#define SLC_INT_ENABLE_TX_DONE BIT(14)
|
||||
#define SLC_INT_ENABLE_TOKEN1_1TO0 BIT(13)
|
||||
#define SLC_INT_ENABLE_TOKEN0_1TO0 BIT(12)
|
||||
#define SLC_INT_ENABLE_TX_OVERFLOW BIT(11)
|
||||
#define SLC_INT_ENABLE_RX_UNDEFLOW BIT(10)
|
||||
#define SLC_INT_ENABLE_TX_START BIT(9)
|
||||
#define SLC_INT_ENABLE_RX_START BIT(8)
|
||||
#define SLC_INT_ENABLE_FROM_HOST_BIT7 BIT(7)
|
||||
#define SLC_INT_ENABLE_FROM_HOST_BIT6 BIT(6)
|
||||
#define SLC_INT_ENABLE_FROM_HOST_BIT5 BIT(5)
|
||||
#define SLC_INT_ENABLE_FROM_HOST_BIT4 BIT(4)
|
||||
#define SLC_INT_ENABLE_FROM_HOST_BIT3 BIT(3)
|
||||
#define SLC_INT_ENABLE_FROM_HOST_BIT2 BIT(2)
|
||||
#define SLC_INT_ENABLE_FROM_HOST_BIT1 BIT(1)
|
||||
#define SLC_INT_ENABLE_FROM_HOST_BIT0 BIT(0)
|
||||
|
||||
#define SLC_INT_ENABLE_FROM_HOST_BIT_ALL 0xff
|
||||
|
||||
/* Details for INT_CLEAR register */
|
||||
|
||||
#define SLC_INT_CLEAR_TX_DSCR_EMPTY BIT(21)
|
||||
#define SLC_INT_CLEAR_RX_DSCR_ERROR BIT(20)
|
||||
#define SLC_INT_CLEAR_TX_DSCR_ERROR BIT(19)
|
||||
#define SLC_INT_CLEAR_TO_HOST BIT(18)
|
||||
#define SLC_INT_CLEAR_RX_EOF BIT(17)
|
||||
#define SLC_INT_CLEAR_RX_DONE BIT(16)
|
||||
#define SLC_INT_CLEAR_TX_EOF BIT(15)
|
||||
#define SLC_INT_CLEAR_TX_DONE BIT(14)
|
||||
#define SLC_INT_CLEAR_TOKEN1_1TO0 BIT(13)
|
||||
#define SLC_INT_CLEAR_TOKEN0_1TO0 BIT(12)
|
||||
#define SLC_INT_CLEAR_TX_OVERFLOW BIT(11)
|
||||
#define SLC_INT_CLEAR_RX_UNDEFLOW BIT(10)
|
||||
#define SLC_INT_CLEAR_TX_START BIT(9)
|
||||
#define SLC_INT_CLEAR_RX_START BIT(8)
|
||||
#define SLC_INT_CLEAR_FROM_HOST_BIT7 BIT(7)
|
||||
#define SLC_INT_CLEAR_FROM_HOST_BIT6 BIT(6)
|
||||
#define SLC_INT_CLEAR_FROM_HOST_BIT5 BIT(5)
|
||||
#define SLC_INT_CLEAR_FROM_HOST_BIT4 BIT(4)
|
||||
#define SLC_INT_CLEAR_FROM_HOST_BIT3 BIT(3)
|
||||
#define SLC_INT_CLEAR_FROM_HOST_BIT2 BIT(2)
|
||||
#define SLC_INT_CLEAR_FROM_HOST_BIT1 BIT(1)
|
||||
#define SLC_INT_CLEAR_FROM_HOST_BIT0 BIT(0)
|
||||
|
||||
/* Details for RX_STATUS register */
|
||||
|
||||
#define SLC_RX_STATUS_EMPTY BIT(1)
|
||||
#define SLC_RX_STATUS_FULL BIT(0)
|
||||
|
||||
/* Details for RX_FIFO_PUSH register */
|
||||
|
||||
#define SLC_RX_FIFO_PUSH_FLAG BIT(16)
|
||||
#define SLC_RX_FIFO_PUSH_DATA_M 0x000001ff
|
||||
#define SLC_RX_FIFO_PUSH_DATA_S 0
|
||||
|
||||
/* Details for TX_STATUS register */
|
||||
|
||||
#define SLC_TX_STATUS_EMPTY BIT(1)
|
||||
#define SLC_TX_STATUS_FULL BIT(0)
|
||||
|
||||
/* Details for TX_FIFO_POP register */
|
||||
|
||||
#define SLC_TX_FIFO_POP_FLAG BIT(16)
|
||||
#define SLC_TX_FIFO_POP_DATA_M 0x000007ff
|
||||
#define SLC_TX_FIFO_POP_DATA_S 0
|
||||
|
||||
/* Details for RX_LINK register */
|
||||
|
||||
#define SLC_RX_LINK_PARK BIT(31)
|
||||
#define SLC_RX_LINK_RESTART BIT(30)
|
||||
#define SLC_RX_LINK_START BIT(29)
|
||||
#define SLC_RX_LINK_STOP BIT(28)
|
||||
#define SLC_RX_LINK_DESCRIPTOR_ADDR_M 0x000fffff
|
||||
#define SLC_RX_LINK_DESCRIPTOR_ADDR_S 0
|
||||
|
||||
/* Details for TX_LINK register */
|
||||
|
||||
#define SLC_TX_LINK_PARK BIT(31)
|
||||
#define SLC_TX_LINK_RESTART BIT(30)
|
||||
#define SLC_TX_LINK_START BIT(29)
|
||||
#define SLC_TX_LINK_STOP BIT(28)
|
||||
#define SLC_TX_LINK_DESCRIPTOR_ADDR_M 0x000fffff
|
||||
#define SLC_TX_LINK_DESCRIPTOR_ADDR_S 0
|
||||
|
||||
/* Details for INTVEC_TO_HOST register */
|
||||
|
||||
#define SLC_INTVEC_TO_HOST_INTVEC_M 0x000000ff
|
||||
#define SLC_INTVEC_TO_HOST_INTVEC_S 0
|
||||
|
||||
/* Details for TOKEN0 register */
|
||||
|
||||
#define SLC_TOKEN0_M 0x00000fff
|
||||
#define SLC_TOKEN0_S 16
|
||||
#define SLC_TOKEN0_LOCAL_INC_MORE BIT(14)
|
||||
#define SLC_TOKEN0_LOCAL_INC BIT(13)
|
||||
#define SLC_TOKEN0_LOCAL_WRITE BIT(12)
|
||||
#define SLC_TOKEN0_LOCAL_DATA_M 0x00000FFF
|
||||
#define SLC_TOKEN0_LOCAL_DATA_S 0
|
||||
|
||||
/* Details for TOKEN1 register */
|
||||
|
||||
#define SLC_TOKEN1_MASK 0x00000fff
|
||||
#define SLC_TOKEN1_S 16
|
||||
#define SLC_TOKEN1_LOCAL_INC_MORE BIT(14)
|
||||
#define SLC_TOKEN1_LOCAL_INC BIT(13)
|
||||
#define SLC_TOKEN1_LOCAL_WRITE BIT(12)
|
||||
#define SLC_TOKEN1_LOCAL_DATA_M 0x00000fff
|
||||
#define SLC_TOKEN1_LOCAL_DATA_S 0
|
||||
|
||||
/* Details for BRIDGE_CONF register */
|
||||
|
||||
#define SLC_BRIDGE_CONF_TX_PUSH_IDLE_M 0x0000ffff
|
||||
#define SLC_BRIDGE_CONF_TX_PUSH_IDLE_S 16
|
||||
#define SLC_BRIDGE_CONF_TX_DUMMY_MODE BIT(12)
|
||||
#define SLC_BRIDGE_CONF_FIFO_MAP_ENABLE_M 0x0000000f
|
||||
#define SLC_BRIDGE_CONF_FIFO_MAP_ENABLE_S 8
|
||||
#define SLC_BRIDGE_CONF_TX_EOF_ENABLE_M 0x0000003f
|
||||
#define SLC_BRIDGE_CONF_TX_EOF_ENABLE_S 0
|
||||
|
||||
/* Details for AHB_TEST register */
|
||||
|
||||
#define SLC_AHB_TEST_ADDR_M 0x00000003
|
||||
#define SLC_AHB_TEST_ADDR_S 4
|
||||
#define SLC_AHB_TEST_MODE_M 0x00000007
|
||||
#define SLC_AHB_TEST_MODE_S 0
|
||||
|
||||
/* Details for SDIO_STATUS register */
|
||||
|
||||
#define SLC_SDIO_STATUS_BUS_M 0x00000007
|
||||
#define SLC_SDIO_STATUS_BUS_S 12
|
||||
#define SLC_SDIO_STATUS_WAKEUP BIT(8)
|
||||
#define SLC_SDIO_STATUS_FUNC_M 0x0000000f
|
||||
#define SLC_SDIO_STATUS_FUNC_S 4
|
||||
#define SLC_SDIO_STATUS_COMMAND_M 0x00000007
|
||||
#define SLC_SDIO_STATUS_COMMAND_S 0
|
||||
|
||||
/* Details for RX_DESCRIPTOR_CONF register */
|
||||
|
||||
#define SLC_RX_DESCRIPTOR_CONF_RX_FILL_ENABLE BIT(20)
|
||||
#define SLC_RX_DESCRIPTOR_CONF_RX_EOF_MODE BIT(19)
|
||||
#define SLC_RX_DESCRIPTOR_CONF_RX_FILL_MODE BIT(18)
|
||||
#define SLC_RX_DESCRIPTOR_CONF_INFOR_NO_REPLACE BIT(17)
|
||||
#define SLC_RX_DESCRIPTOR_CONF_TOKEN_NO_REPLACE BIT(16)
|
||||
#define SLC_RX_DESCRIPTOR_CONF_POP_IDLE_COUNT_M 0x0000ffff
|
||||
#define SLC_RX_DESCRIPTOR_CONF_POP_IDLE_COUNT_S 0
|
||||
|
||||
#endif /* _ESP_SLC_REGS_H */
|
272
core/include/esp/spi.h
Normal file
272
core/include/esp/spi.h
Normal file
|
@ -0,0 +1,272 @@
|
|||
/**
|
||||
* \file Hardware SPI master driver
|
||||
*
|
||||
* Part of esp-open-rtos
|
||||
*
|
||||
* \copyright Ruslan V. Uss, 2016
|
||||
* BSD Licensed as described in the file LICENSE
|
||||
*/
|
||||
#ifndef _ESP_SPI_H_
|
||||
#define _ESP_SPI_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "esp/spi_regs.h"
|
||||
#include "esp/clocks.h"
|
||||
|
||||
/**
|
||||
* Macro for use with spi_init and spi_set_frequency_div.
|
||||
* SPI frequency = 80000000 / divider / count
|
||||
* dvider must be in 1..8192 and count in 1..64
|
||||
*/
|
||||
#define SPI_GET_FREQ_DIV(divider, count) (((count) << 16) | ((divider) & 0xffff))
|
||||
|
||||
/**
|
||||
* Predefinded SPI frequency dividers
|
||||
*/
|
||||
#define SPI_FREQ_DIV_125K SPI_GET_FREQ_DIV(64, 10) ///< 125kHz
|
||||
#define SPI_FREQ_DIV_250K SPI_GET_FREQ_DIV(32, 10) ///< 250kHz
|
||||
#define SPI_FREQ_DIV_500K SPI_GET_FREQ_DIV(16, 10) ///< 500kHz
|
||||
#define SPI_FREQ_DIV_1M SPI_GET_FREQ_DIV(8, 10) ///< 1MHz
|
||||
#define SPI_FREQ_DIV_2M SPI_GET_FREQ_DIV(4, 10) ///< 2MHz
|
||||
#define SPI_FREQ_DIV_4M SPI_GET_FREQ_DIV(2, 10) ///< 4MHz
|
||||
#define SPI_FREQ_DIV_8M SPI_GET_FREQ_DIV(5, 2) ///< 8MHz
|
||||
#define SPI_FREQ_DIV_10M SPI_GET_FREQ_DIV(4, 2) ///< 10MHz
|
||||
#define SPI_FREQ_DIV_20M SPI_GET_FREQ_DIV(2, 2) ///< 20MHz
|
||||
#define SPI_FREQ_DIV_40M SPI_GET_FREQ_DIV(1, 2) ///< 40MHz
|
||||
#define SPI_FREQ_DIV_80M SPI_GET_FREQ_DIV(1, 1) ///< 80MHz
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
typedef enum _spi_mode_t {
|
||||
SPI_MODE0 = 0, ///< CPOL = 0, CPHA = 0
|
||||
SPI_MODE1, ///< CPOL = 0, CPHA = 1
|
||||
SPI_MODE2, ///< CPOL = 1, CPHA = 0
|
||||
SPI_MODE3 ///< CPOL = 1, CPHA = 1
|
||||
} spi_mode_t;
|
||||
|
||||
typedef enum _spi_endianness_t {
|
||||
SPI_LITTLE_ENDIAN = 0,
|
||||
SPI_BIG_ENDIAN
|
||||
} spi_endianness_t;
|
||||
|
||||
typedef enum _spi_word_size_t {
|
||||
SPI_8BIT = 1, ///< 1 byte
|
||||
SPI_16BIT = 2, ///< 2 bytes
|
||||
SPI_32BIT = 4 ///< 4 bytes
|
||||
} spi_word_size_t;
|
||||
|
||||
/**
|
||||
* SPI bus settings
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
spi_mode_t mode; ///< Bus mode
|
||||
uint32_t freq_divider; ///< Bus frequency as a divider. See spi_init()
|
||||
bool msb; ///< MSB first if true
|
||||
spi_endianness_t endianness; ///< Bus byte order
|
||||
bool minimal_pins; ///< Minimal set of pins if true. Spee spi_init()
|
||||
} spi_settings_t;
|
||||
|
||||
/**
|
||||
* \brief Initalize SPI bus
|
||||
* Initalize specified SPI bus and setup appropriate pins:
|
||||
* Bus 0:
|
||||
* - MISO = GPIO 7
|
||||
* - MOSI = GPIO 8
|
||||
* - SCK = GPIO 6
|
||||
* - CS0 = GPIO 11 (if minimal_pins is false)
|
||||
* - HD = GPIO 9 (if minimal_pins is false)
|
||||
* - WP = GPIO 10 (if minimal_pins is false)
|
||||
* Bus 1:
|
||||
* - MISO = GPIO 12
|
||||
* - MOSI = GPIO 13
|
||||
* - SCK = GPIO 14
|
||||
* - CS0 = GPIO 15 (if minimal_pins is false)
|
||||
* Note that system flash memory is on the bus 0!
|
||||
* \param bus Bus ID: 0 - system, 1 - user
|
||||
* \param mode Bus mode
|
||||
* \param freq_divider SPI bus frequency divider, use SPI_GET_FREQ_DIV() or predefined value
|
||||
* \param msb Bit order, MSB first if true
|
||||
* \param endianness Byte order
|
||||
* \param minimal_pins If true use the minimal set of pins: MISO, MOSI and SCK.
|
||||
* \return false when error
|
||||
*/
|
||||
bool spi_init(uint8_t bus, spi_mode_t mode, uint32_t freq_divider, bool msb, spi_endianness_t endianness, bool minimal_pins);
|
||||
/**
|
||||
* \brief Initalize SPI bus
|
||||
* spi_init() wrapper.
|
||||
* Example:
|
||||
*
|
||||
* const spi_settings_t my_settings = {
|
||||
* .mode = SPI_MODE0,
|
||||
* .freq_divider = SPI_FREQ_DIV_4M,
|
||||
* .msb = true,
|
||||
* .endianness = SPI_LITTLE_ENDIAN,
|
||||
* .minimal_pins = true
|
||||
* }
|
||||
* ....
|
||||
* spi_settings_t old;
|
||||
* spi_get_settings(1, &old); // save current settings
|
||||
* //spi_init(1, SPI_MODE0, SPI_FREQ_DIV_4M, true, SPI_LITTLE_ENDIAN, true); // use own settings
|
||||
* // or
|
||||
* spi_set_settings(1, &my_settings);
|
||||
* // some work with spi here
|
||||
* ....
|
||||
* spi_set_settings(1, &old); // restore saved settings
|
||||
*
|
||||
* \param s Pointer to the settings structure
|
||||
* \return false when error
|
||||
*/
|
||||
static inline bool spi_set_settings(uint8_t bus, const spi_settings_t *s)
|
||||
{
|
||||
return spi_init(bus, s->mode, s->freq_divider, s->msb, s->endianness, s->minimal_pins);
|
||||
}
|
||||
/**
|
||||
* \brief Get current settings of the SPI bus
|
||||
* See spi_set_settings().
|
||||
* \param bus Bus ID: 0 - system, 1 - user
|
||||
* \param s Pointer to the structure that receives SPI bus settings
|
||||
*/
|
||||
void spi_get_settings(uint8_t bus, spi_settings_t *s);
|
||||
|
||||
/**
|
||||
* \brief Set SPI bus mode
|
||||
* \param bus Bus ID: 0 - system, 1 - user
|
||||
* \param mode Bus mode
|
||||
*/
|
||||
void spi_set_mode(uint8_t bus, spi_mode_t mode);
|
||||
/**
|
||||
* \brief Get mode of the SPI bus
|
||||
* \param bus Bus ID: 0 - system, 1 - user
|
||||
* \return Bus mode
|
||||
*/
|
||||
spi_mode_t spi_get_mode(uint8_t bus);
|
||||
|
||||
/**
|
||||
* \brief Set SPI bus frequency
|
||||
* Examples:
|
||||
*
|
||||
* spi_set_frequency_div(1, SPI_FREQ_DIV_8M); // 8 MHz, predefined value
|
||||
* ...
|
||||
* spi_set_frequency_div(1, SPI_GET_FREQ_DIV(8, 10)); // divider = 8, count = 10,
|
||||
* // frequency = 80000000 Hz / 8 / 10 = 1000000 Hz
|
||||
*
|
||||
* \param bus Bus ID: 0 - system, 1 - user
|
||||
* \param divider Predivider of the system bus frequency (80MHz) in the 2 low
|
||||
* bytes and period pulses count in the third byte. Please note that
|
||||
* divider must be be in range 1..8192 and count in range 2..64. Use the
|
||||
* macro SPI_GET_FREQ_DIV(divider, count) to get the correct parameter value.
|
||||
*/
|
||||
void spi_set_frequency_div(uint8_t bus, uint32_t divider);
|
||||
/**
|
||||
* \brief Get SPI bus frequency as a divider
|
||||
* Example:
|
||||
*
|
||||
* uint32_t old_freq = spi_get_frequency_div(1);
|
||||
* spi_set_frequency_div(1, SPI_FREQ_DIV_8M);
|
||||
* ...
|
||||
* spi_set_frequency_div(1, old_freq);
|
||||
*
|
||||
* \param bus Bus ID: 0 - system, 1 - user
|
||||
* \return SPI frequency, as divider.
|
||||
*/
|
||||
inline uint32_t spi_get_frequency_div(uint8_t bus)
|
||||
{
|
||||
return (FIELD2VAL(SPI_CLOCK_DIV_PRE, SPI(bus).CLOCK) + 1) |
|
||||
(FIELD2VAL(SPI_CLOCK_COUNT_NUM, SPI(bus).CLOCK) + 1);
|
||||
}
|
||||
/**
|
||||
* \brief Get SPI bus frequency in Hz
|
||||
* \param bus Bus ID: 0 - system, 1 - user
|
||||
* \return SPI frequency, Hz
|
||||
*/
|
||||
inline uint32_t spi_get_frequency_hz(uint8_t bus)
|
||||
{
|
||||
return APB_CLK_FREQ /
|
||||
(FIELD2VAL(SPI_CLOCK_DIV_PRE, SPI(bus).CLOCK) + 1) /
|
||||
(FIELD2VAL(SPI_CLOCK_COUNT_NUM, SPI(bus).CLOCK) + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set SPI bus bit order
|
||||
* \param bus Bus ID: 0 - system, 1 - user
|
||||
* \param msb Bit order, MSB first if true
|
||||
*/
|
||||
void spi_set_msb(uint8_t bus, bool msb);
|
||||
/**
|
||||
* \brief Get SPI bus bit order
|
||||
* \param bus Bus ID: 0 - system, 1 - user
|
||||
* \return msb Bit order, MSB first if true
|
||||
*/
|
||||
inline bool spi_get_msb(uint8_t bus)
|
||||
{
|
||||
return !(SPI(bus).CTRL0 & (SPI_CTRL0_WR_BIT_ORDER | SPI_CTRL0_RD_BIT_ORDER));
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set SPI bus byte order
|
||||
* \param bus Bus ID: 0 - system, 1 - user
|
||||
* \param endianness Byte order
|
||||
*/
|
||||
void spi_set_endianness(uint8_t bus, spi_endianness_t endianness);
|
||||
/**
|
||||
* \brief Get SPI bus byte order
|
||||
* \param bus Bus ID: 0 - system, 1 - user
|
||||
* \return endianness Byte order
|
||||
*/
|
||||
inline spi_endianness_t spi_get_endianness(uint8_t bus)
|
||||
{
|
||||
return SPI(bus).USER0 & (SPI_USER0_WR_BYTE_ORDER | SPI_USER0_RD_BYTE_ORDER)
|
||||
? SPI_BIG_ENDIAN
|
||||
: SPI_LITTLE_ENDIAN;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Transfer 8 bits over SPI
|
||||
* \param bus Bus ID: 0 - system, 1 - user
|
||||
* \param data Byte to send
|
||||
* \return Received byte
|
||||
*/
|
||||
uint8_t spi_transfer_8(uint8_t bus, uint8_t data);
|
||||
/**
|
||||
* \brief Transfer 16 bits over SPI
|
||||
* \param bus Bus ID: 0 - system, 1 - user
|
||||
* \param data Word to send
|
||||
* \return Received word
|
||||
*/
|
||||
uint16_t spi_transfer_16(uint8_t bus, uint16_t data);
|
||||
/**
|
||||
* \brief Transfer 32 bits over SPI
|
||||
* \param bus Bus ID: 0 - system, 1 - user
|
||||
* \param data dword to send
|
||||
* \return Received dword
|
||||
*/
|
||||
uint32_t spi_transfer_32(uint8_t bus, uint32_t data);
|
||||
/**
|
||||
* \brief Transfer buffer of words over SPI
|
||||
* Please note that the buffer size is in words, not in bytes!
|
||||
* Example:
|
||||
*
|
||||
* const uint16_t out_buf[] = { 0xa0b0, 0xa1b1, 0xa2b2, 0xa3b3 };
|
||||
* uint16_t in_buf[sizeof(out_buf)];
|
||||
* spi_init(1, SPI_MODE1, SPI_FREQ_DIV_4M, true, SPI_BIG_ENDIAN, true);
|
||||
* spi_transfer(1, out_buf, in_buf, sizeof(out_buf), SPI_16BIT); // len = 4 words * 2 bytes = 8 bytes
|
||||
*
|
||||
* \param bus Bus ID: 0 - system, 1 - user
|
||||
* \param out_data Data to send.
|
||||
* \param in_data Receive buffer. If NULL, received data will be lost.
|
||||
* \param len Buffer size in words
|
||||
* \param word_size Size of the word
|
||||
* \return Transmitted/received words count
|
||||
*/
|
||||
size_t spi_transfer(uint8_t bus, const void *out_data, void *in_data, size_t len, spi_word_size_t word_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _ESP_SPI_H_ */
|
|
@ -150,6 +150,7 @@ _Static_assert(sizeof(struct SPI_REGS) == 0x100, "SPI_REGS is the wrong size");
|
|||
#define SPI_USER0_CS_SETUP BIT(5)
|
||||
#define SPI_USER0_CS_HOLD BIT(4)
|
||||
#define SPI_USER0_FLASH_MODE BIT(2)
|
||||
#define SPI_USER0_DUPLEX BIT(0)
|
||||
|
||||
/* Details for USER1 register */
|
||||
|
||||
|
@ -171,6 +172,7 @@ _Static_assert(sizeof(struct SPI_REGS) == 0x100, "SPI_REGS is the wrong size");
|
|||
|
||||
/* Details for PIN register */
|
||||
|
||||
#define SPI_PIN_IDLE_EDGE BIT(29) ///< CPOL
|
||||
#define SPI_PIN_CS2_DISABLE BIT(2)
|
||||
#define SPI_PIN_CS1_DISABLE BIT(1)
|
||||
#define SPI_PIN_CS0_DISABLE BIT(0)
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#define _ESP_TIMER_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
#include "esp/timer_regs.h"
|
||||
#include "esp/interrupts.h"
|
||||
|
||||
|
@ -23,43 +24,73 @@ typedef enum {
|
|||
} timer_frc_t;
|
||||
|
||||
/* Return current count value for timer. */
|
||||
INLINED uint32_t timer_get_count(const timer_frc_t frc);
|
||||
static inline uint32_t timer_get_count(const timer_frc_t frc)
|
||||
{
|
||||
return TIMER(frc).COUNT;
|
||||
}
|
||||
|
||||
/* Return current load value for timer. */
|
||||
INLINED uint32_t timer_get_load(const timer_frc_t frc);
|
||||
static inline uint32_t timer_get_load(const timer_frc_t frc)
|
||||
{
|
||||
return TIMER(frc).LOAD;
|
||||
}
|
||||
|
||||
/* Write load value for timer. */
|
||||
INLINED void timer_set_load(const timer_frc_t frc, const uint32_t load);
|
||||
static inline void timer_set_load(const timer_frc_t frc, const uint32_t load)
|
||||
{
|
||||
TIMER(frc).LOAD = load;
|
||||
}
|
||||
|
||||
/* Returns maximum load value for timer. */
|
||||
INLINED uint32_t timer_max_load(const timer_frc_t frc);
|
||||
static inline uint32_t timer_max_load(const timer_frc_t frc)
|
||||
{
|
||||
return (frc == FRC1) ? TIMER_FRC1_MAX_LOAD : UINT32_MAX;
|
||||
}
|
||||
|
||||
/* Set the timer divider value */
|
||||
INLINED void timer_set_divider(const timer_frc_t frc, const timer_clkdiv_t div);
|
||||
static inline void timer_set_divider(const timer_frc_t frc, const timer_clkdiv_t div)
|
||||
{
|
||||
if(div < TIMER_CLKDIV_1 || div > TIMER_CLKDIV_256)
|
||||
return;
|
||||
TIMER(frc).CTRL = SET_FIELD(TIMER(frc).CTRL, TIMER_CTRL_CLKDIV, div);
|
||||
}
|
||||
|
||||
/* Enable or disable timer interrupts
|
||||
|
||||
This both sets the xtensa interrupt mask and writes to the DPORT register
|
||||
that allows timer interrupts.
|
||||
*/
|
||||
INLINED void timer_set_interrupts(const timer_frc_t frc, bool enable);
|
||||
void timer_set_interrupts(const timer_frc_t frc, bool enable);
|
||||
|
||||
/* Turn the timer on or off */
|
||||
INLINED void timer_set_run(const timer_frc_t frc, const bool run);
|
||||
static inline void timer_set_run(const timer_frc_t frc, const bool run)
|
||||
{
|
||||
if (run)
|
||||
TIMER(frc).CTRL |= TIMER_CTRL_RUN;
|
||||
else
|
||||
TIMER(frc).CTRL &= ~TIMER_CTRL_RUN;
|
||||
}
|
||||
|
||||
/* Get the run state of the timer (on or off) */
|
||||
INLINED bool timer_get_run(const timer_frc_t frc);
|
||||
static inline bool timer_get_run(const timer_frc_t frc)
|
||||
{
|
||||
return TIMER(frc).CTRL & TIMER_CTRL_RUN;
|
||||
}
|
||||
|
||||
/* Set timer auto-reload on or off */
|
||||
INLINED void timer_set_reload(const timer_frc_t frc, const bool reload);
|
||||
static inline void timer_set_reload(const timer_frc_t frc, const bool reload)
|
||||
{
|
||||
if (reload)
|
||||
TIMER(frc).CTRL |= TIMER_CTRL_RELOAD;
|
||||
else
|
||||
TIMER(frc).CTRL &= ~TIMER_CTRL_RELOAD;
|
||||
}
|
||||
|
||||
/* Get the auto-reload state of the timer (on or off) */
|
||||
INLINED bool timer_get_reload(const timer_frc_t frc);
|
||||
|
||||
/* Return a suitable timer divider for the specified frequency,
|
||||
or -1 if none is found.
|
||||
*/
|
||||
INLINED timer_clkdiv_t timer_freq_to_div(uint32_t freq);
|
||||
static inline bool timer_get_reload(const timer_frc_t frc)
|
||||
{
|
||||
return TIMER(frc).CTRL & TIMER_CTRL_RELOAD;
|
||||
}
|
||||
|
||||
/* Return the number of timer counts to achieve the specified
|
||||
* frequency with the specified divisor.
|
||||
|
@ -68,14 +99,41 @@ INLINED timer_clkdiv_t timer_freq_to_div(uint32_t freq);
|
|||
*
|
||||
* Returns 0 if the given freq/divisor combo cannot be achieved.
|
||||
*
|
||||
* Compile-time evaluates if all arguments are available at compile time.
|
||||
*/
|
||||
INLINED uint32_t timer_freq_to_count(const timer_frc_t frc, uint32_t freq, const timer_clkdiv_t div);
|
||||
uint32_t timer_freq_to_count(const timer_frc_t frc, uint32_t freq, const timer_clkdiv_t div);
|
||||
|
||||
/* Return a suitable timer divider for the specified frequency,
|
||||
or -1 if none is found.
|
||||
*/
|
||||
static inline timer_clkdiv_t timer_freq_to_div(uint32_t freq)
|
||||
{
|
||||
/*
|
||||
try to maintain resolution without risking overflows.
|
||||
these values are a bit arbitrary at the moment! */
|
||||
if(freq > 100*1000)
|
||||
return TIMER_CLKDIV_1;
|
||||
else if(freq > 100)
|
||||
return TIMER_CLKDIV_16;
|
||||
else
|
||||
return TIMER_CLKDIV_256;
|
||||
}
|
||||
|
||||
/* Return a suitable timer divider for the specified duration in
|
||||
microseconds or -1 if none is found.
|
||||
*/
|
||||
INLINED timer_clkdiv_t timer_time_to_div(uint32_t us);
|
||||
static inline timer_clkdiv_t timer_time_to_div(uint32_t us)
|
||||
{
|
||||
/*
|
||||
try to maintain resolution without risking overflows. Similar to
|
||||
timer_freq_to_div, these values are a bit arbitrary at the
|
||||
moment! */
|
||||
if(us < 1000)
|
||||
return TIMER_CLKDIV_1;
|
||||
else if(us < 10*1000)
|
||||
return TIMER_CLKDIV_16;
|
||||
else
|
||||
return TIMER_CLKDIV_256;
|
||||
}
|
||||
|
||||
/* Return the number of timer counts for the specified timer duration
|
||||
* in microseconds, when using the specified divisor.
|
||||
|
@ -84,9 +142,8 @@ INLINED timer_clkdiv_t timer_time_to_div(uint32_t us);
|
|||
*
|
||||
* Returns 0 if the given time/divisor combo cannot be achieved.
|
||||
*
|
||||
* Compile-time evaluates if all arguments are available at compile time.
|
||||
*/
|
||||
INLINED uint32_t timer_time_to_count(const timer_frc_t frc, uint32_t us, const timer_clkdiv_t div);
|
||||
uint32_t timer_time_to_count(const timer_frc_t frc, uint32_t us, const timer_clkdiv_t div);
|
||||
|
||||
/* Set a target timer interrupt frequency in Hz.
|
||||
|
||||
|
@ -103,12 +160,9 @@ INLINED uint32_t timer_time_to_count(const timer_frc_t frc, uint32_t us, const t
|
|||
Does not start/stop the timer, you have to do this manually via
|
||||
timer_set_run.
|
||||
|
||||
Returns true on success, false if given frequency could not be set.
|
||||
|
||||
Compile-time evaluates to simple register writes if all arguments
|
||||
are available at compile time.
|
||||
Returns 0 on success, or -EINVAL if given frequency could not be set.
|
||||
*/
|
||||
INLINED bool timer_set_frequency(const timer_frc_t frc, uint32_t freq);
|
||||
int timer_set_frequency(const timer_frc_t frc, uint32_t freq);
|
||||
|
||||
/* Sets the timer for a oneshot interrupt in 'us' microseconds.
|
||||
|
||||
|
@ -123,14 +177,9 @@ INLINED bool timer_set_frequency(const timer_frc_t frc, uint32_t freq);
|
|||
also disable FRC1 in the timer interrupt handler by calling
|
||||
timer_set_run(TIMER_FRC1, false);
|
||||
|
||||
Returns true if the timeout was successfully set.
|
||||
|
||||
Compile-time evaluates to simple register writes if all arguments
|
||||
are available at compile time.
|
||||
Returns 0 on success, or -EINVAL if the given timeout could not be set.
|
||||
*/
|
||||
INLINED bool timer_set_timeout(const timer_frc_t frc, uint32_t us);
|
||||
|
||||
#include "timer_private.h"
|
||||
int timer_set_timeout(const timer_frc_t frc, uint32_t us);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -1,266 +0,0 @@
|
|||
/* Private header parts of the timer API implementation
|
||||
*
|
||||
* Part of esp-open-rtos
|
||||
* Copyright (C) 2015 Superhouse Automation Pty Ltd
|
||||
* BSD Licensed as described in the file LICENSE
|
||||
*/
|
||||
#ifndef _ESP_TIMER_PRIVATE_H
|
||||
#define _ESP_TIMER_PRIVATE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "esp/dport_regs.h"
|
||||
|
||||
/* Timer divisor index to max frequency */
|
||||
#define _FREQ_DIV1 (80*1000*1000)
|
||||
#define _FREQ_DIV16 (5*1000*1000)
|
||||
#define _FREQ_DIV256 312500
|
||||
const static uint32_t IROM _TIMER_FREQS[] = { _FREQ_DIV1, _FREQ_DIV16, _FREQ_DIV256 };
|
||||
|
||||
/* Timer divisor index to divisor value */
|
||||
const static uint32_t IROM _TIMER_DIV_VAL[] = { 1, 16, 256 };
|
||||
|
||||
INLINED uint32_t timer_get_count(const timer_frc_t frc)
|
||||
{
|
||||
return TIMER(frc).COUNT;
|
||||
}
|
||||
|
||||
INLINED uint32_t timer_get_load(const timer_frc_t frc)
|
||||
{
|
||||
return TIMER(frc).LOAD;
|
||||
}
|
||||
|
||||
INLINED void timer_set_load(const timer_frc_t frc, const uint32_t load)
|
||||
{
|
||||
TIMER(frc).LOAD = load;
|
||||
}
|
||||
|
||||
INLINED uint32_t timer_max_load(const timer_frc_t frc)
|
||||
{
|
||||
return (frc == FRC1) ? TIMER_FRC1_MAX_LOAD : UINT32_MAX;
|
||||
}
|
||||
|
||||
INLINED void timer_set_divider(const timer_frc_t frc, const timer_clkdiv_t div)
|
||||
{
|
||||
if(div < TIMER_CLKDIV_1 || div > TIMER_CLKDIV_256)
|
||||
return;
|
||||
TIMER(frc).CTRL = SET_FIELD(TIMER(frc).CTRL, TIMER_CTRL_CLKDIV, div);
|
||||
}
|
||||
|
||||
INLINED void timer_set_interrupts(const timer_frc_t frc, bool enable)
|
||||
{
|
||||
const uint32_t dp_bit = (frc == FRC1) ? DPORT_INT_ENABLE_FRC1 : DPORT_INT_ENABLE_FRC2;
|
||||
const uint32_t int_mask = BIT((frc == FRC1) ? INUM_TIMER_FRC1 : INUM_TIMER_FRC2);
|
||||
if(enable) {
|
||||
DPORT.INT_ENABLE |= dp_bit;
|
||||
_xt_isr_unmask(int_mask);
|
||||
} else {
|
||||
DPORT.INT_ENABLE &= ~dp_bit;
|
||||
_xt_isr_mask(int_mask);
|
||||
}
|
||||
}
|
||||
|
||||
INLINED void timer_set_run(const timer_frc_t frc, const bool run)
|
||||
{
|
||||
if (run)
|
||||
TIMER(frc).CTRL |= TIMER_CTRL_RUN;
|
||||
else
|
||||
TIMER(frc).CTRL &= ~TIMER_CTRL_RUN;
|
||||
}
|
||||
|
||||
INLINED bool timer_get_run(const timer_frc_t frc)
|
||||
{
|
||||
return TIMER(frc).CTRL & TIMER_CTRL_RUN;
|
||||
}
|
||||
|
||||
INLINED void timer_set_reload(const timer_frc_t frc, const bool reload)
|
||||
{
|
||||
if (reload)
|
||||
TIMER(frc).CTRL |= TIMER_CTRL_RELOAD;
|
||||
else
|
||||
TIMER(frc).CTRL &= ~TIMER_CTRL_RELOAD;
|
||||
}
|
||||
|
||||
INLINED bool timer_get_reload(const timer_frc_t frc)
|
||||
{
|
||||
return TIMER(frc).CTRL & TIMER_CTRL_RELOAD;
|
||||
}
|
||||
|
||||
INLINED timer_clkdiv_t timer_freq_to_div(uint32_t freq)
|
||||
{
|
||||
/*
|
||||
try to maintain resolution without risking overflows.
|
||||
these values are a bit arbitrary at the moment! */
|
||||
if(freq > 100*1000)
|
||||
return TIMER_CLKDIV_1;
|
||||
else if(freq > 100)
|
||||
return TIMER_CLKDIV_16;
|
||||
else
|
||||
return TIMER_CLKDIV_256;
|
||||
}
|
||||
|
||||
/* timer_timer_to_count implementation - inline if all args are constant, call normally otherwise */
|
||||
|
||||
INLINED uint32_t _timer_freq_to_count_impl(const timer_frc_t frc, const uint32_t freq, const timer_clkdiv_t div)
|
||||
{
|
||||
if(div < TIMER_CLKDIV_1 || div > TIMER_CLKDIV_256)
|
||||
return 0; /* invalid divider */
|
||||
|
||||
if(freq > _TIMER_FREQS[div])
|
||||
return 0; /* out of range for given divisor */
|
||||
|
||||
uint64_t counts = _TIMER_FREQS[div]/freq;
|
||||
return counts;
|
||||
}
|
||||
|
||||
uint32_t _timer_freq_to_count_runtime(const timer_frc_t frc, const uint32_t freq, const timer_clkdiv_t div);
|
||||
|
||||
INLINED uint32_t timer_freq_to_count(const timer_frc_t frc, const uint32_t freq, const timer_clkdiv_t div)
|
||||
{
|
||||
if(__builtin_constant_p(frc) && __builtin_constant_p(freq) && __builtin_constant_p(div))
|
||||
return _timer_freq_to_count_impl(frc, freq, div);
|
||||
else
|
||||
return _timer_freq_to_count_runtime(frc, freq, div);
|
||||
}
|
||||
|
||||
INLINED timer_clkdiv_t timer_time_to_div(uint32_t us)
|
||||
{
|
||||
/*
|
||||
try to maintain resolution without risking overflows. Similar to
|
||||
timer_freq_to_div, these values are a bit arbitrary at the
|
||||
moment! */
|
||||
if(us < 1000)
|
||||
return TIMER_CLKDIV_1;
|
||||
else if(us < 10*1000)
|
||||
return TIMER_CLKDIV_16;
|
||||
else
|
||||
return TIMER_CLKDIV_256;
|
||||
}
|
||||
|
||||
/* timer_timer_to_count implementation - inline if all args are constant, call normally otherwise */
|
||||
|
||||
INLINED uint32_t _timer_time_to_count_impl(const timer_frc_t frc, uint32_t us, const timer_clkdiv_t div)
|
||||
{
|
||||
if(div < TIMER_CLKDIV_1 || div > TIMER_CLKDIV_256)
|
||||
return 0; /* invalid divider */
|
||||
|
||||
const uint32_t TIMER_MAX = timer_max_load(frc);
|
||||
|
||||
if(div != TIMER_CLKDIV_256) /* timer tick in MHz */
|
||||
{
|
||||
/* timer is either 80MHz or 5MHz, so either 80 or 5 MHz counts per us */
|
||||
const uint32_t counts_per_us = ((div == TIMER_CLKDIV_1) ? _FREQ_DIV1 : _FREQ_DIV16)/1000/1000;
|
||||
if(us > TIMER_MAX/counts_per_us)
|
||||
return 0; /* Multiplying us by mhz_per_count will overflow TIMER_MAX */
|
||||
return us*counts_per_us;
|
||||
}
|
||||
else /* /256 divider, 312.5kHz freq so need to scale up */
|
||||
{
|
||||
/* derived from naive floating point equation that we can't use:
|
||||
counts = (us/1000/1000)*_FREQ_DIV256;
|
||||
counts = (us/2000)*(_FREQ_DIV256/500);
|
||||
counts = us*(_FREQ_DIV256/500)/2000;
|
||||
*/
|
||||
const uint32_t scalar = _FREQ_DIV256/500;
|
||||
if(us > 1+UINT32_MAX/scalar)
|
||||
return 0; /* Multiplying us by _FREQ_DIV256/500 will overflow uint32_t */
|
||||
|
||||
uint32_t counts = (us*scalar)/2000;
|
||||
if(counts > TIMER_MAX)
|
||||
return 0; /* counts value too high for timer type */
|
||||
return counts;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t _timer_time_to_count_runtime(const timer_frc_t frc, uint32_t us, const timer_clkdiv_t div);
|
||||
|
||||
INLINED uint32_t timer_time_to_count(const timer_frc_t frc, uint32_t us, const timer_clkdiv_t div)
|
||||
{
|
||||
if(__builtin_constant_p(frc) && __builtin_constant_p(us) && __builtin_constant_p(div))
|
||||
return _timer_time_to_count_impl(frc, us, div);
|
||||
else
|
||||
return _timer_time_to_count_runtime(frc, us, div);
|
||||
}
|
||||
|
||||
/* timer_set_frequency implementation - inline if all args are constant, call normally otherwise */
|
||||
|
||||
INLINED bool _timer_set_frequency_impl(const timer_frc_t frc, uint32_t freq)
|
||||
{
|
||||
uint32_t counts = 0;
|
||||
timer_clkdiv_t div = timer_freq_to_div(freq);
|
||||
|
||||
counts = timer_freq_to_count(frc, freq, div);
|
||||
if(counts == 0)
|
||||
{
|
||||
printf("ABORT: No counter for timer %u frequency %u\r\n", frc, freq);
|
||||
abort();
|
||||
}
|
||||
|
||||
timer_set_divider(frc, div);
|
||||
if(frc == FRC1)
|
||||
{
|
||||
timer_set_load(frc, counts);
|
||||
timer_set_reload(frc, true);
|
||||
}
|
||||
else /* FRC2 */
|
||||
{
|
||||
/* assume that if this overflows it'll wrap, so we'll get desired behaviour */
|
||||
TIMER(1).ALARM = counts + TIMER(1).COUNT;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _timer_set_frequency_runtime(const timer_frc_t frc, uint32_t freq);
|
||||
|
||||
INLINED bool timer_set_frequency(const timer_frc_t frc, uint32_t freq)
|
||||
{
|
||||
if(__builtin_constant_p(frc) && __builtin_constant_p(freq))
|
||||
return _timer_set_frequency_impl(frc, freq);
|
||||
else
|
||||
return _timer_set_frequency_runtime(frc, freq);
|
||||
}
|
||||
|
||||
/* timer_set_timeout implementation - inline if all args are constant, call normally otherwise */
|
||||
|
||||
INLINED bool _timer_set_timeout_impl(const timer_frc_t frc, uint32_t us)
|
||||
{
|
||||
uint32_t counts = 0;
|
||||
timer_clkdiv_t div = timer_time_to_div(us);
|
||||
|
||||
counts = timer_time_to_count(frc, us, div);
|
||||
if(counts == 0)
|
||||
return false; /* can't set frequency */
|
||||
|
||||
timer_set_divider(frc, div);
|
||||
if(frc == FRC1)
|
||||
{
|
||||
timer_set_load(frc, counts);
|
||||
}
|
||||
else /* FRC2 */
|
||||
{
|
||||
TIMER(1).ALARM = counts + TIMER(1).COUNT;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _timer_set_timeout_runtime(const timer_frc_t frc, uint32_t us);
|
||||
|
||||
INLINED bool timer_set_timeout(const timer_frc_t frc, uint32_t us)
|
||||
{
|
||||
if(__builtin_constant_p(frc) && __builtin_constant_p(us))
|
||||
return _timer_set_timeout_impl(frc, us);
|
||||
else
|
||||
return _timer_set_timeout_runtime(frc, us);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -52,7 +52,7 @@ long _write_r(struct _reent *r, int fd, const char *ptr, int len )
|
|||
}
|
||||
|
||||
/* syscall implementation for stdio read from UART */
|
||||
long _read_r( struct _reent *r, int fd, char *ptr, int len )
|
||||
__attribute__((weak)) long _read_r( struct _reent *r, int fd, char *ptr, int len )
|
||||
{
|
||||
int ch, i;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue