Merge branch 'master' into open-libmain

Conflicts:
	core/include/esp/rtc_regs.h
	include/espressif/spi_flash.h
This commit is contained in:
Alex Stewart 2016-03-22 16:24:50 -07:00
commit 769ca0d8f8
89 changed files with 4766 additions and 634 deletions

View file

@ -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
View 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);
}

View file

@ -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
View 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;
}

View file

@ -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;
}

View file

@ -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

View file

@ -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
View 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 */

View file

@ -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));
}

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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
View 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
View 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
View 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_ */

View file

@ -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)

View file

@ -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
}

View file

@ -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

View file

@ -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;