1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-01-17 10:12:45 +01:00

cpu: add esp8266

This commit is contained in:
Schorcht 2018-09-05 02:39:50 +02:00
parent e528fb8e2d
commit e4ca897661
63 changed files with 9544 additions and 0 deletions

9
cpu/esp8266/Makefile Normal file
View File

@ -0,0 +1,9 @@
# Define the module that is built:
MODULE = cpu
# Add a list of subdirectories, that should also be built:
DIRS += periph
DIRS += sdk
DIRS += vendor
include $(RIOTBASE)/Makefile.base

47
cpu/esp8266/Makefile.dep Normal file
View File

@ -0,0 +1,47 @@
# additional modules dependencies
ifneq (, $(filter lua, $(USEPKG)))
USEMODULE += newlib_syscalls_default
USEMODULE += xtimer
endif
ifneq (, $(filter lwip%, $(USEMODULE)))
USEMODULE += newlib_syscalls_default
endif
ifneq (,$(filter ndn-riot,$(USEPKG)))
USEMODULE += crypto
USEMODULE += cipher_modes
endif
ifneq (, $(filter posix%, $(USEMODULE)))
USEMODULE += newlib_syscalls_default
endif
ifneq (, $(filter shell, $(USEMODULE)))
USEMODULE += newlib_syscalls_default
USEMODULE += xtimer
endif
ifneq (, $(filter xtimer, $(USEMODULE)))
USEMODULE += newlib_syscalls_default
endif
ifneq (, $(filter vfs, $(USEMODULE)))
USEMODULE += newlib_syscalls_default
USEMODULE += xtimer
endif
ifneq (, $(filter newlib_syscalls_default, $(USEMODULE)))
USEMODULE += stdio_uart
endif
# network interface dependencies
ifneq (, $(filter netdev_default, $(USEMODULE)))
# if NETDEV_DEFAULT is empty, we use module mrf24j40 as default network device
ifndef NETDEV_DEFAULT
USEMODULE += mrf24j40
else
USEMODULE += $(NETDEV_DEFAULT)
endif
endif

View File

@ -0,0 +1,6 @@
# MCU defined features that are provided independent on board definitions
FEATURES_PROVIDED += periph_cpuid
FEATURES_PROVIDED += periph_hwrng
FEATURES_PROVIDED += periph_pm
FEATURES_PROVIDED += periph_timer

View File

@ -0,0 +1,148 @@
# check some environment variables first
ifndef ESP8266_NEWLIB_DIR
$(info ESP8266_NEWLIB_DIR should be defined as /path/to/newlib directory)
$(info ESP8266_NEWLIB_DIR is set by default to /opt/esp/newlib-xtensa)
export ESP8266_NEWLIB_DIR=/opt/esp/newlib-xtensa
endif
ifndef ESP8266_SDK_DIR
$(info ESP8266_SDK_DIR should be defined as /path/to/sdk directory)
$(info ESP8266_SDK_DIR is set by default to /opt/esp/esp-open-sdk/sdk)
export ESP8266_SDK_DIR=/opt/esp/esp-open-sdk/sdk
endif
# Options to control the compilation
ifeq ($(USE_SDK), 1)
USEMODULE += esp_sdk
endif
ifeq ($(ENABLE_GDB), 1)
USEMODULE += esp_gdb
endif
ifeq ($(ENABLE_GDBSTUB), 1)
USEMODULE += esp_gdbstub
USEMODULE += esp_gdb
endif
# regular Makefile
export CPU ?= esp8266
export TARGET_ARCH ?= xtensa-lx106-elf
# ESP8266 pseudomodules
PSEUDOMODULES += esp_gdb
PSEUDOMODULES += esp_now
PSEUDOMODULES += esp_sdk
PSEUDOMODULES += esp_sdk_int_handling
PSEUDOMODULES += esp_sw_timer
PSEUDOMODULES += esp_spiffs
ifneq (, $(filter pthread, $(USEMODULE)))
INCLUDES += -I$(RIOTBASE)/sys/posix/pthread/include
endif
INCLUDES += -I$(ESP8266_NEWLIB_DIR)/$(TARGET_ARCH)/include
INCLUDES += -I$(RIOTBOARD)/common/$(CPU)/include
INCLUDES += -I$(RIOTCPU)/$(CPU)
INCLUDES += -I$(RIOTCPU)/$(CPU)/vendor
INCLUDES += -I$(RIOTCPU)/$(CPU)/vendor/espressif
CFLAGS += -DESP_OPEN_SDK
CFLAGS += -Wno-unused-parameter -Wformat=0
CFLAGS += -mlongcalls -mtext-section-literals -fdata-sections
ASFLAGS += --longcalls --text-section-literals
ifneq (, $(filter esp_sw_timer, $(USEMODULE)))
USEMODULE += esp_sdk
endif
ifneq (, $(filter esp_sdk, $(USEMODULE)))
INCLUDES += -I$(ESP8266_SDK_DIR)/include
CFLAGS += -DUSE_US_TIMER
endif
ifneq (, $(filter esp_gdbstub, $(USEMODULE)))
GDBSTUB_DIR ?= $(RIOTCPU)/$(CPU)/vendor/esp-gdbstub
CFLAGS += -DGDBSTUB_FREERTOS=0
INCLUDES += -I$(GDBSTUB_DIR)
endif
ifneq (, $(filter esp_gdb, $(USEMODULE)))
CFLAGS_OPT = -fzero-initialized-in-bss -Og -ggdb -g3
else
CFLAGS_OPT = -fzero-initialized-in-bss -O2
endif
CFLAGS += $(CFLAGS_OPT)
ifneq (, $(filter esp_spiffs, $(USEMODULE)))
export SPIFFS_STD_OPTION = -std=c99
USEMODULE += spiffs
USEMODULE += vfs
endif
ifeq ($(QEMU), 1)
CFLAGS += -DQEMU
endif
ifeq ($(FLASH_MODE), qio)
CFLAGS += -DFLASH_MODE_QIO
endif
ifeq ($(FLASH_MODE), qout)
CFLAGS += -DFLASH_MODE_QOUT
endif
LINKFLAGS += -L$(ESP8266_NEWLIB_DIR)/$(TARGET_ARCH)/lib
LINKFLAGS += -L$(ESP8266_SDK_DIR)/lib
ifneq (, $(filter esp_sdk, $(USEMODULE)))
LINKFLAGS += -Wl,--start-group $(BINDIR)/sdk.a
ifneq (, $(filter esp_now, $(USEMODULE)))
LINKFLAGS += -lespnow
endif
LINKFLAGS += -lmain -lnet80211 -lcrypto -lwpa2 -lwpa -llwip -lpp -lphy -lc -lhal
LINKFLAGS += -Wl,--end-group
LINKFLAGS += -T$(RIOTCPU)/$(CPU)/ld/esp8266.riot-os.sdk.app.ld
else
LINKFLAGS += -Wl,--start-group -lphy -lhal -lc -Wl,--end-group
LINKFLAGS += -T$(RIOTCPU)/$(CPU)/ld/esp8266.riot-os.no_sdk.app.ld
endif
LINKFLAGS += -T$(RIOTCPU)/$(CPU)/ld/eagle.rom.addr.v6.ld
LINKFLAGS += -nostdlib -lgcc -u ets_run -Wl,-gc-sections # -Wl,--print-gc-sections
LINKFLAGS += -Wl,--warn-unresolved-symbols
USEMODULE += esp
USEMODULE += mtd
USEMODULE += periph
USEMODULE += periph_common
USEMODULE += ps
USEMODULE += random
USEMODULE += sdk
USEMODULE += xtensa
# configure preflasher to convert .elf to .bin before flashing
FLASH_SIZE = -fs 8m
export PREFLASHER ?= esptool.py
export PREFFLAGS ?= elf2image $(FLASH_SIZE) $(ELFFILE)
export FLASHDEPS ?= preflash
# flasher configuration
ifeq ($(QEMU), 1)
export FLASHER = cat
export FFLAGS += $(ELFFILE)-0x00000.bin /dev/zero | head -c $$((0x10000)) | cat -
export FFLAGS += $(ELFFILE)-0x10000.bin /dev/zero | head -c $$((0xfc000)) | cat -
export FFLAGS += $(RIOTCPU)/$(CPU)/bin/esp_init_data_default.bin > $(ELFFILE).bin
else
FLASH_MODE ?= dout
export PROGRAMMER_SPEED ?= 460800
export FLASHER = esptool.py
export FFLAGS += -p $(PORT) -b $(PROGRAMMER_SPEED) write_flash
export FFLAGS += -fm $(FLASH_MODE)
export FFLAGS += 0 $(ELFFILE)-0x00000.bin
export FFLAGS += 0x10000 $(ELFFILE)-0x10000.bin
endif

734
cpu/esp8266/README.md Normal file
View File

@ -0,0 +1,734 @@
# <a name="esp8266_riot"> RIOT-OS on ESP8266 and ESP8285 boards </a>
## <a name="esp8266_toc"> Table of Contents </a>
1. [Overview](#esp8266_overview)
2. [MCU ESP8266](#esp8266_mcu_esp8266)
3. [Toolchain](#esp8266_toolchain)
1. [RIOT Docker Toolchain (riotdocker)](#esp8266_riot_docker_toolchain)
2. [Precompiled Toolchain](#esp8266_precompiled_toolchain)
3. [Manual Toolchain Installation](#esp8266_manual_toolchain_installation)
4. [Flashing the Device](#esp8266_flashing_the_device)
1. [Toolchain Usage](#esp8266_toolchain_usage)
2. [Compile Options](#esp8266_compile_options)
3. [Flash Modes](#esp8266_flash_modes)
5. [Peripherals](#esp8266_peripherals)
1. [GPIO pins](#esp8266_gpio_pins)
2. [ADC Channels](#esp8266_adc_channels)
3. [SPI Interfaces](#esp8266_spi_interfaces)
4. [I2C Interfaces](#esp8266_i2c_interfaces)
5. [PWM Channels](#esp8266_pwm_channels)
6. [Timers](#esp8266_timers)
7. [SPIFFS Device](#esp8266_spiffs_device)
8. [Other Peripherals](#esp8266_other_peripherals)
6. [Preconfigured Devices](#esp8266_preconfigured_devices)
1. [Network Devices](#esp8266_network_devices)
2. [SD-Card Device](#esp8266_sd_card_device)
7. [Application-Specific Configurations](#esp8266_application_specific_configurations)
1. [Application-Specific Board Configuration](#esp8266_application_specific_board_configuration)
2. [Application-Specific Driver Configuration](#esp8266_application_specific_driver_configuration)
8. [SDK Task Handling](#esp8266_sdk_task_handling)
9. [QEMU Mode and GDB](#esp8266_qemu_mode_and_gdb)
# <a name="esp8266_overview"> Overview </a> &nbsp;&nbsp; [[TOC](#esp8266_toc)]
There are two implementations that can be used:
- the **SDK version** which is realized on top of an SDK (*esp-open-sdk* or *ESP8266_NONOS_SDK*) and
- the **non-SDK version** which is realized without the SDK.
The non-SDK version produces a much smaller code size than the SDK version and is more efficient in execution because it does not need to run additional SDK functions to keep the SDK system alive.
The **non-SDK version** is probably the **best choice if you do not need the built-in WiFi module**, for example, when you plan to connect an IEEE 802.15.4 radio module to the MCU for communication.
By **default**, the **non-SDK version** is compiled. To compile the SDK version, add ```USE_SDK=1``` to the make command line, e.g.,
```
make flash BOARD=esp8266-esp-12x -C tests/shell USE_SDK=1 ...
```
For more information about the make command variables, see section [Compile Options](#esp8266_compile_options).
# <a name=esp8266_mcu_esp8266> MCU ESP8266 </a> &nbsp;[[TOC](#esp8266_toc)]
ESP8266 is a low-cost, ultra-low-power, single-core SoCs with an integrated WiFi module from Espressif Systems. The processor core is based on the Tensilica Xtensa Diamond Standard 106Micro 32-bit Controller Processor Core, which Espressif calls L106. The key features of ESP8266 are:
<center>
MCU | ESP8266EX
------------|----------------------------
Vendor | Espressif
Cores | 1 x Tensilica Xtensa LX106
FPU | no
RAM | 80 kByte user-data RAM <br> 32 kByte instruction RAM <br> 32 kByte instruction cache <br/> 16 kByte EST system-data RAM
Flash | 512 kByte ... 16 MByte
Frequency | 80 MHz or 160 MHz
Power Consumption | 70 mA in normal operating mode <br> 20 uA in deep sleep mode
Timers | 1 x 32 bit
ADCs | 1 x 10 bit (1 channel)
GPIOs | 16
I2Cs | 2 (software implementation)
SPIs | 2
UARTs | 1 (console) + 1 transmit-only
WiFi | IEEE 802.11 b/g/n built in
Vcc | 2.5 - 3.6 V
Datasheet | [Datasheet](https://www.espressif.com/sites/default/files/documentation/0a-esp8266ex_datasheet_en.pdf)
Technical Reference | [Technical Reference](https://www.espressif.com/sites/default/files/documentation/esp8266-technical_reference_en.pdf)
</center><br>
@note ESP8285 is simply an ESP8266 SoC with 1 MB built-in flash. Therefore, the documentation also applies to the SoC ESP8285, even if only the ESP8266 SoC is described below.
# <a name="esp8266_toolchain"> Toolchain</a> &nbsp;[[TOC](#esp8266_toc)]
To compile RIOT for The ESP8266 SoC, the following software components are required:
- **esp-open-sdk** which includes the **Xtensa GCC** compiler toolchain, the hardware abstraction library **libhal** for Xtensa LX106, and the flash programmer tool <b>```esptool.py```</b>
- **newlib-c** library for Xtensa (esp-open-rtos version)
- **SDK (optional)**, either as part of <b>```esp-open-sdk```</b> or the <b>```ESP8266_NONOS_SDK```</b>
You have the following options to install the Toolchain:
- <b>```riotdocker```</b> image and <b>```esptool.py```</b>, see section [RIOT Docker Toolchain (riotdocker)](#esp8266_riot_docker_toolchain)
- **precompiled toolchain** installation from GIT, see section [Precompiled Toolchain](#esp8266_toolchain_installation)
- **manual installation**, see section [Manual Toolchain Installation](#esp8266_manual_toolchain_installation)
## <a name="esp8266_riot_docker_toolchain"> RIOT Docker Toolchain (riotdocker) </a> &nbsp;[[TOC](#esp8266_toc)]
The easiest use the toolchain is Docker.
### <a name="esp8266_preparing_the_environment"> Preparing the Environment </a> &nbsp;[[TOC](#esp8266_toc)]
Using RIOT Docker requires at least the following software:
- <b>```Docker```</b> container virtualization software
- RIOT Docker (<b>```riotdocker```</b>) image
- flasher tool <b>```esptool.py```</b>
For information about installing Docker on your host, refer to the appropriate manuals for your operating system. For example, the easiest way to install Docker on the Ubuntu/Debian system is:
```
sudo apt-get install docker.io
```
The ESP Flasher tool <b>```esptool.py```</b> is available at [GitHub](https://github.com/espressif/esptool). To install the tool, either Python 2.7 or Python 3.4 or later must be installed. The latest stable version of ```esptool.py``` can be installed with ```pip```:
```
pip install esptool
```
<b>```esptool.py```</b> depends on ```pySerial``` which can be installed either using ```pip```
```
pip install pyserial
```
or the package manager of your OS, for example on Debian/Ubuntu systems:
```
apt-get install pyserial
```
For more information on ```esptool.py```, please refer the [git repository](https://github.com/espressif/esptool)
Please make sure that ```esptool.py``` is in your ```PATH``` variable.
### <a name="esp8266_generating_docker_image"> Generating a riotdocker Image </a> &nbsp;[[TOC](#esp8266_toc)]
A ```riotdocker``` fork that only installs the ```RIOT-Xtensa-ESP8266-toolchain``` is available at [GitHub](https://github.com/gschorcht/riotdocker-Xtensa-ESP.git). After cloning this git repository, you can use branch ```esp8266_only``` to generate a Docker image with a size of "only" 990 MByte:
```
git clone https://github.com/gschorcht/riotdocker-Xtensa-ESP.git
cd riotdocker-Xtensa-ESP
git checkout esp8266_only
docker build -t riotbuild .
```
A ```riotdocker``` version that contains the toolchains for all different RIOT platforms can be found at [GitHub](https://github.com/RIOT-OS/riotdocker). However, the Docker image generated from the this Docker file has a size of about 1.5 GByte.
Once a Docker image has been created, it can be started with the following commands while in the RIOT root directory:
```
cd /path/to/RIOT
docker run -i -t --privileged -v /dev:/dev -u $UID -v $(pwd):/data/riotbuild riotbuild
```
@note RIOT's root directory ```/path/to/RIOT``` becomes visible as the home directory of the ```riotbuild``` user in the Docker image. That is, the output of compilations performed in RIOT Docker is also accessible on the host system.
Please refer the [RIOT wiki](https://github.com/RIOT-OS/RIOT/wiki/Use-Docker-to-build-RIOT) on how to use the Docker image to compile RIOT OS.
### <a name="esp8266_using_existing_docker_image"> Using an Existing riotdocker Image </a> &nbsp;[[TOC](#esp8266_toc)]
Alternatively, an existing Docker image from Docker Hub can be used. You can either pull and start the [schorcht/riotbuild_esp8266](https://hub.docker.com/r/schorcht/riotbuild_esp8266) Docker image which only contains the ```RIOT-Xtensa-ESP8266-toolchain``` using
```
cd /path/to/RIOT
docker run -i -t --privileged -v /dev:/dev -u $UID -v $(pwd):/data/riotbuild schorcht/riotbuild_esp8266
```
or the [riot/riotbuild](https://hub.docker.com/r/riot/riotbuild/) Docker image (size is about 1.5 GB) which contains the toolchains for all platforms using
```
cd /path/to/RIOT
docker run -i -t --privileged -v /dev:/dev -u $UID -v $(pwd):/data/riotbuild riot/riotbuild
```
### <a name="esp8266_flashing_using_docker"> Make Process with Docker Image </a> &nbsp;[[TOC](#esp8266_toc)]
Using Docker, the make process consists of the following two steps:
1. **making** the RIOT binary **within a RIOT Docker image**
2. **flashing** the RIOT binary using a flasher program **on the host system**
Once the RIOT Docker image has been started from RIOT's root directory, a RIOT application can be compiled inside the Docker using the make command as usual, for example:
```
make BOARD=esp8266-esp-12x -C tests/shell ...
```
This will generate a RIOT binary in ELF format.
@note You can't use the ```flash``` target inside the Docker image.
The RIOT binary has to be flash outside docker on the host system. Since the Docker image was stared while in RIOT's root directory, the output of the compilations is also accessible on the host system. On the host system, the ```flash-only``` target can then be used to flash the binary.
```
make flash-only BOARD=esp8266-esp-12x -C tests/shell
```
## <a name="esp8266_precompiled_toolchain"> Precompiled Toolchain </a> &nbsp;[[TOC](#esp8266_toc)]
You can get a precompiled version of the whole toolchain from the GIT repository [RIOT-Xtensa-ESP8266-toolchain](https://github.com/gschorcht/RIOT-Xtensa-ESP8266-toolchain). This repository contains the precompiled toolchain including all libraries that are necessary to compile RIOT-OS for ESP8266.
@note To use the precompiled toolchain the following packages (Debian/Ubuntu) have to be installed:<br> ```cppcheck``` ```coccinelle``` ```curl``` ```doxygen``` ```git``` ```graphviz``` ```make``` ```pcregrep``` ```python``` ```python-serial``` ```python3``` ```python3-flake8``` ```unzip``` ```wget```
To install the toolchain use the following commands:
```
cd /opt
sudo git clone https://github.com/gschorcht/RIOT-Xtensa-ESP8266-toolchain.git esp
```
After the installation, all components of the toolchain are installed in directory ```/opt/esp```. Of course, you can use any other location for the installation.
To use the toolchain, you have to add the path of the binaries to your ```PATH``` variable according to your toolchain location
```
export PATH=$PATH:/path/to/toolchain/esp-open-sdk/xtensa-lx106-elf/bin
```
where ```/path/to/toolchain/``` is the directory you selected for the installation of the toolchain. For the default installation in ```/opt/esp``` this would be:
```
export PATH=$PATH:/opt/esp/esp-open-sdk/xtensa-lx106-elf/bin
```
Furthermore, you have to set variables ```ESP8266_SDK_DIR``` and ```ESP8266_NEWLIB_DIR``` according to the location of the toolchain.
```
export ESP8266_SDK_DIR=/path/to/toolchain/esp-open-sdk/sdk
export ESP8266_NEWLIB_DIR=/path/to/toolchain/newlib-xtensa
```
If you have used ```/opt/esp``` as installation directory, it is not necessary to set these variables since makefiles use them as default directories.
## <a name="esp8266_manual_toolchain_installation"> Manual Toolchain Installation </a> &nbsp;[[TOC](#esp8266_toc)]
The most difficult way to install the toolchain is the manual installation of required components as described below.
@note Manual toolchain installation requires that the following packages (Debian/Ubuntu) are installed: ```autoconf``` ```automake``` ```bash``` ```bison``` ```build-essential``` ```bzip2``` ```coccinelle``` ```cppcheck``` ```curl``` ```doxygen``` ```g++``` ```gperf``` ```gawk``` ```gcc``` ```git``` ```graphviz``` ```help2man``` ```flex``` ```libexpat-dev``` ```libtool``` ```libtool-bin``` ```make``` ```ncurses-dev``` ```pcregrep``` ```python``` ```python-dev``` ```python-serial``` ```python3``` ```python3-flake8``` ```sed``` ```texinfo``` ```unrar-free``` ```unzip wget```
### <a name="esp8266_installation_of_esp_open_sdk"> Installation of esp-open-sdk </a> &nbsp;[[TOC](#esp8266_toc)]
esp-open-sdk is directly installed inside its source directory. Therefore, change directly to the target directory of the toolchain to build it.
```
cd /path/to/esp
git clone --recursive https://github.com/pfalcon/esp-open-sdk.git
cd esp-open-sdk
export ESP_OPEN_SDK_DIR=$PWD
```
If you plan to use the SDK version of the RIOT port and to use the SDK as part of esp-open-sdk, simply build its standalone version.
```
make STANDALONE=y
```
If you only plan to use the non-SDK version of the RIOT port or if you want to use one of Espressif's original SDKs, it is enough to build the toolchain.
```
make toolchain esptool libhal STANDALONE=n
```
Once compilation has been finished, the toolchain is available in ```$PWD/xtensa-lx106-elf/bin```. To use it, set the ```PATH``` variable accordingly.
```
export PATH=$ESP_OPEN_SDK_DIR/xtensa-lx106-elf/bin:$PATH
```
If you have compiled the standalone version of esp-open-sdk and you plan to use this SDK version, set additionally the ```ESP8266_SDK_DIR``` variable.
```
export ESP8266_SDK_DIR=$ESP_OPEN_SDK_DIR/sdk
```
### <a name="esp8266_installation_of_newlib-c"> Installation of newlib-c </a> &nbsp;[[TOC](#esp8266_toc)]
First, set the target directory for the installation.
```
export ESP8266_NEWLIB_DIR=/path/to/esp/newlib-xtensa
```
Please take care, to use the newlib-c version that was modified for esp-open-rtos since it includes ```stdatomic.h```.
```
cd /my/source/dir
git clone https://github.com/ourairquality/newlib.git
```
Once you have cloned the GIT repository, build and install it with following commands.
```
cd newlib
./configure --prefix=$ESP8266_NEWLIB_DIR --with-newlib --enable-multilib --disable-newlib-io-c99-formats --enable-newlib-supplied-syscalls --enable-target-optspace --program-transform-name="s&^&xtensa-lx106-elf-&" --disable-option-checking --with-target-subdir=xtensa-lx106-elf --target=xtensa-lx106-elf --enable-newlib-nano-formatted-io --enable-newlib-reent-small
make
make install
```
### <a name="esp8266_installation_of_espressif_original_sdk"> Installation of Espressif original SDK (optional) </a> &nbsp;[[TOC](#esp8266_toc)]
If you plan to use the SDK version of the RIOT port and if you want to use one of Espressif's original SDKs, you have to install it.
First, download the _ESP8266_NONOS_SDK_ version 2.1.0 from the [Espressif web site](https://github.com/espressif/ESP8266_NONOS_SDK/releases/tag/v2.1.0). Probably other version might also work. However, RIOT port is tested with version 2.1.0.
Once you have downloaded it, you can install it with following commands.
```
cd /path/to/esp
tar xvfz /downloads/ESP8266_NONOS_SDK-2.1.0.tar.gz
```
To use the installed SDK, set variable ```ESP8266_SDK_DIR``` accordingly.
```
export ESP8266_SDK_DIR=/path/to/esp/ESP8266_NONOS_SDK-2.1.0
```
# <a name="esp8266_flashing_the_device"> Flashing the Device </a> &nbsp;[[TOC](#esp8266_toc)]
## <a name="esp8266_toolchain_usage"> Toolchain Usage </a> &nbsp;[[TOC](#esp8266_toc)]
Once you have installed all required components, you should have the following directories.
```
/path/to/esp/esp-open-sdk
/path/to/esp/newlib-xtensa
/path/to/esp/ESP8266_NONOS_SDK-2.1.0 (optional)
```
To use the toolchain and optionally the SDK, please check that your environment variables are set correctly to
```
export PATH=/path/to/esp/esp-open-sdk/xtensa-lx106-elf/bin:$PATH
export ESP8266_NEWLIB_DIR=/path/to/esp/newlib-xtensa
```
and
```
export ESP8266_SDK_DIR=/path/to/esp/esp-open-sdk/sdk
```
or
```
export ESP8266_SDK_DIR=/path/to/esp/ESP8266_NONOS_SDK-2.1.0
```
## <a name="esp8266_compile_options"> Compile Options </a> &nbsp;[[TOC](#esp8266_toc)]
The compilation process can be controlled by a number of variables for the make command:
<center>
Option | Values | Default | Description
-------|--------|---------|------------
ENABLE_GDB | 0, 1 | 0 | Enable compilation with debug information for debugging with QEMU (```QEMU=1```), see section [QEMU Mode and GDB](#esp8266_qemu_mode_and_gdb)
FLASH_MODE | dout, dio, qout, qio | dout | Set the flash mode, please take care with your module, see section [Flash Modes](#esp8266_flash_modes)
NETDEV_DEFAULT | module name | mrf24j40 | Set the module that is used as default network device, see section [Network Devices](#esp8266_network_devices)
PORT | /dev/ttyUSBx | /dev/ttyUSB0 | Set the USB port for flashing the firmware
QEMU | 0, 1 | 0 | Generate an image for QEMU, see section [QEMU Mode and GDB](#esp8266_qemu_mode_and_gdb).
USE_SDK | 0, 1 | 0 | Compile the SDK version (```USE_SDK=1```), see section [SDK Task Handling](#esp8266_sdk_task_handling)
</center><br>
Optional features of ESP8266 can be enabled by ```USEMODULE``` definitions in the makefile of the application. These are:
<center>
Module | Description
-------|------------
esp_spiffs | Enables the SPIFFS file system, see section [SPIFFS Device](#esp8266_spiffs_device)
esp_sw_timer | Enables software timer implementation, implies the setting ```USE_SDK=1```, see section [Timers](#esp8266_timers)
</center><br>
## <a name="esp8266_flash_modes"> Flash Modes </a> &nbsp;[[TOC](#esp8266_toc)]
The ```FLASH_MODE``` make command variable determines the mode that is used for flash access in normal operation.
The flash mode determines whether 2 data lines (```dio``` and ```dout```) or 4 data lines (```qio``` and ```qout```) for addressing and data access. For each data line, one GPIO is required. Therefore, using ```qio``` or ```qout``` increases the performance of SPI Flash data transfers, but uses two additional GPIOs (GPIO9 and GPIO10). That is, in this flash modes these GPIOs are not available for other purposes. If you can live with lower flash data transfer rates, you should always use ```dio``` or ```dout``` to keep GPIO9 and GPIO10 free for other purposes.
For more information about these flash modes, refer the documentation of [esptool.py](https://github.com/espressif/esptool/wiki/SPI-Flash-Modes).
@note While ESP8266 modules can be flashed with ```qio```, ```qout```, ```dio``` and ```dout```, ESP8285 modules have to be always flashed in ```dout``` mode. The default flash mode is ```dout```.
# <a name="esp8266_peripherals"> Peripherals </a> &nbsp;[[TOC](#esp8266_toc)]
## <a name="esp8266_gpio_pins"> GPIO pins </a> &nbsp;[[TOC](#esp8266_toc)]
ESP8266 has 17 GPIO pins, which are all digital pins. Some of them can not be used at all or have bootstrapping capabilities and are therefore not available on all boards.
<center>
Pin | Remarks
-------|--------
GPIO0 | usually pulled up
GPIO1 | UART TxD
GPIO2 | usually pulled up
GPIO3 | UART RxD
GPIO4 | |
GPIO5 | |
GPIO6 | Flash SPI
GPIO7 | Flash SPI
GPIO8 | Flash SPI
GPIO9 | Flash SPI in ```qout``` and ```qio``` mode, see section [Flash Modes](#esp8266_flash_modes)
GPIO10 | Flash SPI in ```qout``` and ```qio``` mode, see section [Flash Modes](#esp8266_flash_modes)
GPIO11 | Flash SPI
GPIO12 | |
GPIO13 | |
GPIO14 | |
GPIO15 | usually pulled down
GPIO16 | RTC pin and wake up signal in deep sleep mode
</center>
GPIO0, GPIO2, and GPIO15 are bootstrapping pins which are used to boot ESP8266 in different modes:
<center>
GPIO0 | GPIO2 | GPIO15 (MTDO) | Mode
:----:|:-----:|:-------------:|:------------------
1 | X | X | boot in SDIO mode to start OCD
0 | 0 | 1 | boot in UART mode for flashing the firmware
0 | 1 | 1 | boot in FLASH mode to boot the firmware from flash (default mode)
</center>
## <a name="esp8266_adc_channels"> ADC Channels </a> &nbsp;[[TOC](#esp8266_toc)]
ESP8266 has **one dedicated ADC** pin with a resolution of 10 bits. This ADC pin can measure voltages in the range of **0 V ... 1.1 V**.
@note Some boards have voltage dividers to scale this range to a maximum of 3.3 V. For more information, see the hardware manual for the board.
## <a name="esp8266_spi_interfaces"> SPI Interfaces </a> &nbsp;[[TOC](#esp8266_toc)]
ESP8266 provides two hardware SPI interfaces:
- _FSPI_ for flash memory access that is usually simply referred to as _SPI_
- _HSPI_ for peripherals
Even though _FSPI_ (or simply _SPI_) is a normal SPI interface, it is not possible to use it for peripherals. **HSPI is therefore the only usable SPI interface** available for peripherals as RIOT's ```SPI_DEV(0)```.
The pin configuration of the _HSPI_ interface ```SPI_DEV(0)``` is fixed. The only pin definition that can be overridden by an [application-specific board configuration](#esp8266_application_specific_board_configuration) is the CS signal defined by ```SPI0_CS0_GPIO```.
<center>
Signal of _HSPI_ | Pin
-----------------|-------
MISO | GPIO12
MOSI | GPIO13
SCK | GPIO14
CS | GPIOn with n = 0, 2, 4, 5, 15, 16 (additionally 9, 10 in ```dout``` and ```dio``` flash mode)
</center>
When the SPI is enabled using module ```periph_spi```, these GPIOs cannot be used for any other purpose. GPIOs 0, 2, 4, 5, 15, and 16 can be used as CS signal. In ```dio``` and ```dout``` flash modes (see section [Flash Modes](#esp8266_flash_modes)), GPIOs 9 and 10 can also be used as CS signal.
## <a name="esp8266_i2c_interfaces"> I2C Interfaces </a> &nbsp;[[TOC](#esp8266_toc)]
Since the ESP8266 does not or only partially support the I2C in hardware, I2C interfaces are realized as **bit-banging protocol in software**. The maximum usable bus speed is therefore ```I2C_SPEED_FAST_PLUS```. The maximum number of buses that can be defined is 2, ```I2C_DEV(0)``` ... ```I2C_DEV(1)```.
Number of I2C buses (```I2C_NUMOF```) and used GPIO pins (```I2Cx_SCL``` and ```I2Cx_SDA``` where ```x``` stands for the bus device ```x```) have to be defined in the board-specific peripheral configuration in ```$BOARD/periph_conf.h```. Furthermore, the default I2C bus speed (```I2Cx_SPEED```) that is used for bus ```x``` has to be defined.
In the following example, only one I2C bus is defined:
```
#define I2C_NUMOF (1)
#define I2C0_SPEED I2C_SPEED_FAST
#define I2C0_SDA GPIO4
#define I2C0_SCL GPIO5
```
A configuration with two I2C buses would look like the following:
```
#define I2C_NUMOF (2)
#define I2C0_SPEED I2C_SPEED_FAST
#define I2C0_SDA GPIO4
#define I2C0_SCL GPIO5
#define I2C1_SPEED I2C_SPEED_NORMAL
#define I2C1_SDA GPIO2
#define I2C1_SCL GPIO14
```
All these configurations can be overridden by an [application-specific board configuration](#esp8266_application_specific_board_configuration).
## <a name="esp8266_pwm_channels"> PWM Channels </a> &nbsp;[[TOC](#esp8266_toc)]
The hardware implementation of ESP8266 PWM supports only frequencies as power of two. Therefore, a **software implementation** of **one PWM device** (```PWM_DEV(0)```) with up to **8 PWM channels** (```PWM_CHANNEL_NUM_MAX```) is used.
@note The minimum PWM period that can be realized with this software implementation is 10 us or 100.000 PWM clock cycles per second. Therefore, the product of frequency and resolution should not be greater than 100.000. Otherwise the frequency is scaled down automatically.
GPIOs that can be used as channels of the PWM device ```PWM_DEV(0)``` are defined by ```PWM0_CHANNEL_GPIOS```. By default, GPIOs 2, 4 and 5 are defined as PWM channels. As long as these channels are not started with function ```pwm_set```, they can be used as normal GPIOs for other purposes.
GPIOs in ```PWM0_CHANNEL_GPIOS``` with a duty cycle value of 0 can be used as normal GPIOs for other purposes. GPIOs in ```PWM0_CHANNEL_GPIOS``` that are used for other purposes, e.g., I2C or SPI, are no longer available as PWM channels.
To define other GPIOs as PWM channels, just overwrite the definition of ```PWM_CHANNEL_GPIOS``` in an [application-specific board configuration](#esp8266_application_specific_board_configuration)
```
#define PWM0_CHANNEL_GPIOS { GPIO12, GPIO13, GPIO14, GPIO15 }
```
## <a name="esp8266_timers"> Timers </a> &nbsp;[[TOC](#esp8266_toc)]
There are two timer implementations:
- **hardware timer** implementation with **1 timer device** and only **1 channel**, the default
- **software timer** implementation with **1 timer device** and only **10 channels**
By default, the **hardware timer implementation** is used.
When the SDK version of the RIOT port (```USE_SDK=1```) is used, the **software timer** implementation is activated by using module ```esp_sw_timer```.
The software timer uses SDK's software timers to implement the timer channels. Although these SDK timers usually have a precision of a few microseconds, they can deviate up to 500 microseconds. So if you need a timer with high accuracy, you'll need to use the hardware timer with only one timer channel.
@note When module ```esp_sw_timer``` is used, the SDK version is automatically compiled (```USE_SDK=1```).
## <a name="esp8266_spiffs_device"> SPIFFS Device </a> &nbsp;[[TOC](#esp8266_toc)]
If SPIFFS module is enabled (```USEMODULE += esp_spiffs```), the implemented MTD device ```mtd0``` for the on-board SPI flash memory is used together with modules ```spiffs``` and ```vfs``` to realize a persistent file system.
For this purpose, the flash memory is formatted as SPIFFS starting at the address ```0x80000``` (512 kByte) on first boot. All sectors up to the last 5 sectors of the flash memory are then used for the file system. With a fixed sector size of 4096 bytes, the top address of the SPIFF is ```flash_size - 5 * 4096```, e.g., ```0xfb000``` for a flash memory of 1 MByte. The size of the SPIFF then results from:
```
flash_size - 5 * 4096 - 512 kByte
```
Please refer file ```$RIOTBASE/tests/unittests/test-spiffs/tests-spiffs.c``` for more information on how to use SPIFFS and VFS together with a MTD device ```mtd0``` alias ```MTD_0```.
## <a name="esp8266_other_peripherals"> Other Peripherals </a> &nbsp;[[TOC](#esp8266_toc)]
The ESP8266 port of RIOT also supports
- one hardware number generator with 32 bit,
- one UART interface (GPIO1 and GPIO3),
- a CPU-ID function, and
- power management functions.
RTC is not yet implemented.
# <a name="esp8266_preconfigured_devices"> Preconfigured Devices </a> &nbsp;[[TOC](#esp8266_toc)]
The ESP8266 port of RIOT has been tested with several common external devices that can be connected to ESP8266 boards and are preconfigured accordingly.
## <a name="esp8266_network_devices"> Network Devices </a> &nbsp;[[TOC](#esp8266_toc)]
RIOT provides a number of driver modules for different types of network devices, e.g., IEEE 802.15.4 radio modules and Ethernet modules. The RIOT ESP8266 port has been tested with the following network devices and is preconfigured to create RIOT network applications with these devices:
- [mrf24j40](http://riot-os.org/api/group__drivers__mrf24j40.html) (driver for Microchip MRF24j40 based IEEE 802.15.4
- [enc28j60](http://riot-os.org/api/group__drivers__enc28j60.html) (driver for Microchip ENC28J60 based Ethernet modules)
If the RIOT network application uses a default network device (module ```netdev_default```), the ```NETDEV_DEFAULT``` make command variable can be used to define the device that will be used as the default network device. The value of this variable must match the name of the driver module for this network device. If ```NETDEV_DEFAULT``` is not defined, the ```mrf24j40``` module is used as default network device.
### <a name="esp8266_using_mrf24j40"> Using MRF24J40 (module ```mrf24j40```) </a> &nbsp;[[TOC](#esp8266_toc)]
To use MRF24J40 based IEEE 802.15.4 modules as network device, module ```mrf24j40``` has to be added to a makefile:
```
USEMODULE += mrf24j40
```
@note If module ```netdev_default``` is used (which is usually the case in all networking applications), module ```mrf24j40``` is added automatically.
Module ```mrf24j40``` uses the following interface parameters by default:
<center>
Parameter | Default | Remarks
-----------------------|--------------|----------------------------
MRF24J40_PARAM_SPI | SPI_DEV(0) | fix, see section [SPI Interfaces](#esp8266_spi_interfaces)
MRF24J40_PARAM_SPI_CLK | SPI_CLK_1MHZ | fix
MRF24J40_PARAM_CS | GPIO16 | can be overridden
MRF24J40_PARAM_INT | GPIO0 | can be overridden
MRF24J40_PARAM_RESET | GPIO2 | can be overridden
</center><br>
Used GPIOs can be overridden by corresponding make command variables, e.g,:
```
make flash BOARD=esp8266-esp-12x -C examples/gnrc_networking MRF24J40_PARAM_CS=GPIO15
```
or by an [application-specific board configuration](#esp8266_application_specific_board_configuration).
### <a name="esp8266_using_enc28j60"> Using ENC28J60 (module ```enc28j60```) </a> &nbsp;[[TOC](#esp8266_toc)]
To use ENC28J60 Ethernet modules as network device, module ```enc28j60``` has to be added to your makefile:
```
USEMODULE += enc28j60
```
Module ```enc28j60``` uses the following interface parameters by default:
<center>
Parameter | Default | Remarks
---------------------|--------------|----------------------------
ENC28J60_PARAM_SPI | SPI_DEV(0) | fix, see section [SPI Interfaces](#esp8266_spi_interfaces)
ENC28J60_PARAM_CS | GPIO4 | can be overridden
ENC28J60_PARAM_INT | GPIO9 | can be overridden
ENC28J60_PARAM_RESET | GPIO10 | can be overridden
</center>
Used GPIOs can be overridden by corresponding make command variables, e.g.:
```
make flash BOARD=esp8266-esp-12x -C examples/gnrc_networking ENC28J60_PARAM_CS=GPIO15
```
or by an [application-specific board configuration](#esp8266_application_specific_board_configuration).
To use module ```enc28j60``` as default network device, the ```NETDEV_DEFAULT``` make command variable has to set, for example:
```
make flash BOARD=esp8266-esp-12x -C examples/gnrc_networking NETDEV_DEFAULT=enc28j60
```
## <a name="esp8266_sd_card_device"> SD-Card Device </a> &nbsp;[[TOC](#esp8266_toc)]
ESP8266 port of RIOT is preconfigured for RIOT applications that use the [SPI SD-Card driver](http://riot-os.org/api/group__drivers__sdcard__spi.html). To use SPI SD-Card driver, the ```sdcard_spi``` module has to be added to a makefile:
```
USEMODULE += sdcard_spi
```
Module ```sdcard_spi``` uses the following interface parameters by default:
<center>
Parameter | Default | Remarks
-----------------------|---------------|----------------------------
SDCARD_SPI_PARAM_SPI | SPI0_DEV | fix, see section [SPI Interfaces](#esp8266_spi_interfaces)
SDCARD_SPI_PARAM_CS | SPI0_CS0_GPIO | can be overridden
</center>
The GPIO used as CS signal can be overridden by an [application-specific board configuration](#esp8266_application_specific_board_configuration).
# <a name="esp8266_application_specific_configurations"> Application-Specific Configurations </a> &nbsp;[[TOC](#esp8266_toc)]
Configuration used for peripheral devices and for device driver modules, such as GPIO pins, bus interfaces or bus speeds are typically defined in the board-specific configurations ```board.h``` and ```periph_conf.h``` or in the driver parameter configuration ```< driver>_params.h```. Most of these definitions are enclosed by
```
#ifndef ANY_PARAMETER
...
#endif
```
constructs, so it is possible to override them by defining them in front of these constructs.
## <a name="esp8266_application_specific_board_configuration"> Application-Specific Board Configuration </a> &nbsp;[[TOC](#esp8266_toc)]
To override standard board configurations, simply create an application-specific board configuration file ```$APPDIR/board.h``` in the source directory of the application ```$APPDIR``` and add the definitions to be overridden. To force the preprocessor to include board's original ```board.h``` after that, add the ```include_next``` preprocessor directive as the **last** line.
For example to override the default definition of the GPIOs that are used as PWM channels, the application-specific board configuration file ```$APPDIR/board.h``` could look like the following:
```
#ifdef CPU_ESP8266
#define PWM0_CHANNEL_GPIOS { GPIO12, GPIO13, GPIO14, GPIO15 }
#endif
#include_next "board.h"
```
To make such application-specific board configurations dependent on the ESP8266 MCU or a particular ESP8266 card, you should always enclose these definitions in the following constructs:
```
#ifdef CPU_ESP8266
...
#endif
#ifdef BOARD_ESP8266_ESP_12X
...
#endif
```
To ensure that the application-specific board configuration ```$APPDIR/board.h``` is included first, insert the following line as the **first** line to the application makefile ```$APPDIR/Makefile```.
```
INCLUDES += -I$(APPDIR)
```
## <a name="esp8266_application_specific_driver_configuration"> Application-Specific Driver Configuration </a> &nbsp;[[TOC](#esp8266_toc)]
Using the approach for overriding board configurations, the parameters of drivers that are typically defined in ```drivers/<device>/include/<device>_params.h``` can also be overridden. For that purpose just create an application-specific driver parameter file ```$APPDIR/<device>_params.h``` in the source directory ```$APPDIR``` of the application and add the definitions to be overridden. To force the preprocessor to include driver's original ```<device>_params.h``` after that, add the ```include_next``` preprocessor directive as the **last** line.
For example, to override a GPIO used for LIS3DH sensor, the application-specific driver parameter file ```$APPDIR/<device>_params.h``` could look like the following:
```
#ifdef CPU_ESP8266
#define LIS3DH_PARAM_INT2 (GPIO_PIN(0, 4))
#endif
#include_next "lis3dh_params.h"
```
To make such application-specific board configurations dependent on the ESP8266 MCU or a particular ESP8266 card, you should always enclose these definitions in the following constructs:
```
#ifdef CPU_ESP8266
...
#endif
#ifdef BOARD_ESP8266_ESP_12X
...
#endif
```
To ensure that the application-specific driver parameter file ```$APPDIR/<device>_params.h``` is included first, insert the following line as the **first** line to the application makefile ```$APPDIR/Makefile```.
```
INCLUDES += -I$(APPDIR)
```
# <a name="esp8266_sdk_task_handling"> SDK Task Handling </a> &nbsp;[[TOC](#esp8266_toc)]
With make command variable ```USE_SDK=1``` the Espressif SDK is used. This is necessary, for example, if you want to use the built-in WLAN module. The SDK internally uses its own tasks (SDK tasks) and its own scheduling mechanism to realize event-driven SDK functions such as WiFi functions and software timers, and to keep the system alive. For this purpose, the SDK regularly executes SDK tasks with pending events in an endless loop using the ROM function ```ets_run```.
Interrupt service routines do not process interrupts directly but use the ```ets_post``` ROM function to send an event to one of these SDK tasks, which then processes the interrupts asynchronously. A context switch is not possible in the interrupt service routines.
In the RIOT port, the task management of the SDK is replaced by the task management of the RIOT. To handle SDK tasks with pending events so that the SDK functions work and the system keeps alive, the ROM functions ```ets_run``` and ```ets_post``` are overwritten. The ```ets_run``` function performs all SDK tasks with pending events exactly once. It is executed at the end of the ```ets_post``` function and thus usually at the end of an SDK interrupt service routine or before the system goes into the lowest power mode.
@note Since the non-SDK version of RIOT is much smaller and faster than the SDK version, you should always compile your application without the SDK (```USE_SDK=0```, the default) if you don't need the built-in WiFi module.
# <a name="esp8266_qemu_mode_and_gdb"> QEMU Mode and GDB </a> &nbsp;[[TOC](#esp8266_toc)]
When QEMU mode is enabled (```QEMU=1```), instead of loading the image to the target hardware, a binary image ```$ELFFILE.bin``` is created in the target directory. This binary image file can be used together with QEMU to debug the code in GDB.
The binary image can be compiled with debugging information (```ENABLE_GDB=1```) or optimized without debugging information (```ENABLE_GDB=0```). The latter one is the default. The version with debugging information can be debugged in source code while the optimized version can only be debugged in assembler mode.
To use QEMU, you have to install QEMU for Xtensa with ESP8266 machine implementation as following.
```
cd /my/source/dir
git clone https://github.com/gschorcht/qemu-xtensa
cd qemu-xtensa/
git checkout xtensa-esp8266
export QEMU=/path/to/esp/qemu
./configure --prefix=$QEMU --target-list=xtensa-softmmu --disable-werror
make
make install
```
Once the compilation has been finished, QEMU for Xtensa with ESP8266 machine implementation should be available in ```/path/to/esp/qemu/bin``` and you can start it with
```
$QEMU/bin/qemu-system-xtensa -M esp8266 -nographic -serial stdio -monitor none -s -S -kernel /path/to/the/target/image.elf.bin
```
where ```/path/to/the/target/image.elf.bin``` is the path to the binary image as generated by the ```make``` command as ```$ELFFILE.bin```. After that you can start GDB in another terminal window using command:
```
xtensa-lx106-elf-gdb
```
If you have compiled your binary image with debugging information, you can load the ELF file in gdb with:
```
(gdb) file /path/to/the/target/image.elf
```
To start debugging, you have to connect to QEMU with command:
```
(gdb) target remote :1234
```

View File

@ -0,0 +1 @@
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>

Binary file not shown.

BIN
cpu/esp8266/bin/null.bin Normal file

Binary file not shown.

Binary file not shown.

740
cpu/esp8266/doc.txt Normal file
View File

@ -0,0 +1,740 @@
/**
@defgroup cpu_esp8266 ESP8266 / ESP8285 MCU
@ingroup cpu
@brief RIOT-OS port for Espressif's ESP8266 / ESP8285 MCUs
\section esp8266_riot RIOT-OS on ESP8266 and ESP8285 boards
## <a name="esp8266_toc"> Table of Contents </a>
1. [Overview](#esp8266_overview)
2. [MCU ESP8266](#esp8266_mcu_esp8266)
3. [Toolchain](#esp8266_toolchain)
1. [RIOT Docker Toolchain (riotdocker)](#esp8266_riot_docker_toolchain)
2. [Precompiled Toolchain](#esp8266_precompiled_toolchain)
3. [Manual Toolchain Installation](#esp8266_manual_toolchain_installation)
4. [Flashing the Device](#esp8266_flashing_the_device)
1. [Toolchain Usage](#esp8266_toolchain_usage)
2. [Compile Options](#esp8266_compile_options)
3. [Flash Modes](#esp8266_flash_modes)
5. [Peripherals](#esp8266_peripherals)
1. [GPIO pins](#esp8266_gpio_pins)
2. [ADC Channels](#esp8266_adc_channels)
3. [SPI Interfaces](#esp8266_spi_interfaces)
4. [I2C Interfaces](#esp8266_i2c_interfaces)
5. [PWM Channels](#esp8266_pwm_channels)
6. [Timers](#esp8266_timers)
7. [SPIFFS Device](#esp8266_spiffs_device)
8. [Other Peripherals](#esp8266_other_peripherals)
6. [Preconfigured Devices](#esp8266_preconfigured_devices)
1. [Network Devices](#esp8266_network_devices)
2. [SD-Card Device](#esp8266_sd_card_device)
7. [Application-Specific Configurations](#esp8266_application_specific_configurations)
1. [Application-Specific Board Configuration](#esp8266_application_specific_board_configuration)
2. [Application-Specific Driver Configuration](#esp8266_application_specific_driver_configuration)
8. [SDK Task Handling](#esp8266_sdk_task_handling)
9. [QEMU Mode and GDB](#esp8266_qemu_mode_and_gdb)
# <a name="esp8266_overview"> Overview </a> &nbsp;&nbsp; [[TOC](#esp8266_toc)]
There are two implementations that can be used:
- the **SDK version** which is realized on top of an SDK (*esp-open-sdk* or *ESP8266_NONOS_SDK*) and
- the **non-SDK version** which is realized without the SDK.
The non-SDK version produces a much smaller code size than the SDK version and is more efficient in execution because it does not need to run additional SDK functions to keep the SDK system alive.
The **non-SDK version** is probably the **best choice if you do not need the built-in WiFi module**, for example, when you plan to connect an IEEE 802.15.4 radio module to the MCU for communication.
By **default**, the **non-SDK version** is compiled. To compile the SDK version, add ```USE_SDK=1``` to the make command line, e.g.,
```
make flash BOARD=esp8266-esp-12x -C tests/shell USE_SDK=1 ...
```
For more information about the make command variables, see section [Compile Options](#esp8266_compile_options).
# <a name=esp8266_mcu_esp8266> MCU ESP8266 </a> &nbsp;[[TOC](#esp8266_toc)]
ESP8266 is a low-cost, ultra-low-power, single-core SoCs with an integrated WiFi module from Espressif Systems. The processor core is based on the Tensilica Xtensa Diamond Standard 106Micro 32-bit Controller Processor Core, which Espressif calls L106. The key features of ESP8266 are:
<center>
MCU | ESP8266EX
------------|----------------------------
Vendor | Espressif
Cores | 1 x Tensilica Xtensa LX106
FPU | no
RAM | 80 kByte user-data RAM <br> 32 kByte instruction RAM <br> 32 kByte instruction cache <br/> 16 kByte EST system-data RAM
Flash | 512 kByte ... 16 MByte
Frequency | 80 MHz or 160 MHz
Power Consumption | 70 mA in normal operating mode <br> 20 uA in deep sleep mode
Timers | 1 x 32 bit
ADCs | 1 x 10 bit (1 channel)
GPIOs | 16
I2Cs | 2 (software implementation)
SPIs | 2
UARTs | 1 (console) + 1 transmit-only
WiFi | IEEE 802.11 b/g/n built in
Vcc | 2.5 - 3.6 V
Datasheet | [Datasheet](https://www.espressif.com/sites/default/files/documentation/0a-esp8266ex_datasheet_en.pdf)
Technical Reference | [Technical Reference](https://www.espressif.com/sites/default/files/documentation/esp8266-technical_reference_en.pdf)
</center><br>
@note ESP8285 is simply an ESP8266 SoC with 1 MB built-in flash. Therefore, the documentation also applies to the SoC ESP8285, even if only the ESP8266 SoC is described below.
# <a name="esp8266_toolchain"> Toolchain</a> &nbsp;[[TOC](#esp8266_toc)]
To compile RIOT for The ESP8266 SoC, the following software components are required:
- **esp-open-sdk** which includes the **Xtensa GCC** compiler toolchain, the hardware abstraction library **libhal** for Xtensa LX106, and the flash programmer tool <b>```esptool.py```</b>
- **newlib-c** library for Xtensa (esp-open-rtos version)
- **SDK (optional)**, either as part of <b>```esp-open-sdk```</b> or the <b>```ESP8266_NONOS_SDK```</b>
You have the following options to install the Toolchain:
- <b>```riotdocker```</b> image and <b>```esptool.py```</b>, see section [RIOT Docker Toolchain (riotdocker)](#esp8266_riot_docker_toolchain)
- **precompiled toolchain** installation from GIT, see section [Precompiled Toolchain](#esp8266_toolchain_installation)
- **manual installation**, see section [Manual Toolchain Installation](#esp8266_manual_toolchain_installation)
## <a name="esp8266_riot_docker_toolchain"> RIOT Docker Toolchain (riotdocker) </a> &nbsp;[[TOC](#esp8266_toc)]
The easiest use the toolchain is Docker.
### <a name="esp8266_preparing_the_environment"> Preparing the Environment </a> &nbsp;[[TOC](#esp8266_toc)]
Using RIOT Docker requires at least the following software:
- <b>```Docker```</b> container virtualization software
- RIOT Docker (<b>```riotdocker```</b>) image
- flasher tool <b>```esptool.py```</b>
For information about installing Docker on your host, refer to the appropriate manuals for your operating system. For example, the easiest way to install Docker on the Ubuntu/Debian system is:
```
sudo apt-get install docker.io
```
The ESP Flasher tool <b>```esptool.py```</b> is available at [GitHub](https://github.com/espressif/esptool). To install the tool, either Python 2.7 or Python 3.4 or later must be installed. The latest stable version of ```esptool.py``` can be installed with ```pip```:
```
pip install esptool
```
<b>```esptool.py```</b> depends on ```pySerial``` which can be installed either using ```pip```
```
pip install pyserial
```
or the package manager of your OS, for example on Debian/Ubuntu systems:
```
apt-get install pyserial
```
For more information on ```esptool.py```, please refer the [git repository](https://github.com/espressif/esptool)
Please make sure that ```esptool.py``` is in your ```PATH``` variable.
### <a name="esp8266_generating_docker_image"> Generating a riotdocker Image </a> &nbsp;[[TOC](#esp8266_toc)]
A ```riotdocker``` fork that only installs the ```RIOT-Xtensa-ESP8266-toolchain``` is available at [GitHub](https://github.com/gschorcht/riotdocker-Xtensa-ESP.git). After cloning this git repository, you can use branch ```esp8266_only``` to generate a Docker image with a size of "only" 990 MByte:
```
git clone https://github.com/gschorcht/riotdocker-Xtensa-ESP.git
cd riotdocker-Xtensa-ESP
git checkout esp8266_only
docker build -t riotbuild .
```
A ```riotdocker``` version that contains the toolchains for all different RIOT platforms can be found at [GitHub](https://github.com/RIOT-OS/riotdocker). However, the Docker image generated from the this Docker file has a size of about 1.5 GByte.
Once a Docker image has been created, it can be started with the following commands while in the RIOT root directory:
```
cd /path/to/RIOT
docker run -i -t --privileged -v /dev:/dev -u $UID -v $(pwd):/data/riotbuild riotbuild
```
@note RIOT's root directory ```/path/to/RIOT``` becomes visible as the home directory of the ```riotbuild``` user in the Docker image. That is, the output of compilations performed in RIOT Docker is also accessible on the host system.
Please refer the [RIOT wiki](https://github.com/RIOT-OS/RIOT/wiki/Use-Docker-to-build-RIOT) on how to use the Docker image to compile RIOT OS.
### <a name="esp8266_using_existing_docker_image"> Using an Existing riotdocker Image </a> &nbsp;[[TOC](#esp8266_toc)]
Alternatively, an existing Docker image from Docker Hub can be used. You can either pull and start the [schorcht/riotbuild_esp8266](https://hub.docker.com/r/schorcht/riotbuild_esp8266) Docker image which only contains the ```RIOT-Xtensa-ESP8266-toolchain``` using
```
cd /path/to/RIOT
docker run -i -t --privileged -v /dev:/dev -u $UID -v $(pwd):/data/riotbuild schorcht/riotbuild_esp8266
```
or the [riot/riotbuild](https://hub.docker.com/r/riot/riotbuild/) Docker image (size is about 1.5 GB) which contains the toolchains for all platforms using
```
cd /path/to/RIOT
docker run -i -t --privileged -v /dev:/dev -u $UID -v $(pwd):/data/riotbuild riot/riotbuild
```
### <a name="esp8266_flashing_using_docker"> Make Process with Docker Image </a> &nbsp;[[TOC](#esp8266_toc)]
Using Docker, the make process consists of the following two steps:
1. **making** the RIOT binary **within a RIOT Docker image**
2. **flashing** the RIOT binary using a flasher program **on the host system**
Once the RIOT Docker image has been started from RIOT's root directory, a RIOT application can be compiled inside the Docker using the make command as usual, for example:
```
make BOARD=esp8266-esp-12x -C tests/shell ...
```
This will generate a RIOT binary in ELF format.
@note You can't use the ```flash``` target inside the Docker image.
The RIOT binary has to be flash outside docker on the host system. Since the Docker image was stared while in RIOT's root directory, the output of the compilations is also accessible on the host system. On the host system, the ```flash-only``` target can then be used to flash the binary.
```
make flash-only BOARD=esp8266-esp-12x -C tests/shell
```
## <a name="esp8266_precompiled_toolchain"> Precompiled Toolchain </a> &nbsp;[[TOC](#esp8266_toc)]
You can get a precompiled version of the whole toolchain from the GIT repository [RIOT-Xtensa-ESP8266-toolchain](https://github.com/gschorcht/RIOT-Xtensa-ESP8266-toolchain). This repository contains the precompiled toolchain including all libraries that are necessary to compile RIOT-OS for ESP8266.
@note To use the precompiled toolchain the following packages (Debian/Ubuntu) have to be installed:<br> ```cppcheck``` ```coccinelle``` ```curl``` ```doxygen``` ```git``` ```graphviz``` ```make``` ```pcregrep``` ```python``` ```python-serial``` ```python3``` ```python3-flake8``` ```unzip``` ```wget```
To install the toolchain use the following commands:
```
cd /opt
sudo git clone https://github.com/gschorcht/RIOT-Xtensa-ESP8266-toolchain.git esp
```
After the installation, all components of the toolchain are installed in directory ```/opt/esp```. Of course, you can use any other location for the installation.
To use the toolchain, you have to add the path of the binaries to your ```PATH``` variable according to your toolchain location
```
export PATH=$PATH:/path/to/toolchain/esp-open-sdk/xtensa-lx106-elf/bin
```
where ```/path/to/toolchain/``` is the directory you selected for the installation of the toolchain. For the default installation in ```/opt/esp``` this would be:
```
export PATH=$PATH:/opt/esp/esp-open-sdk/xtensa-lx106-elf/bin
```
Furthermore, you have to set variables ```ESP8266_SDK_DIR``` and ```ESP8266_NEWLIB_DIR``` according to the location of the toolchain.
```
export ESP8266_SDK_DIR=/path/to/toolchain/esp-open-sdk/sdk
export ESP8266_NEWLIB_DIR=/path/to/toolchain/newlib-xtensa
```
If you have used ```/opt/esp``` as installation directory, it is not necessary to set these variables since makefiles use them as default directories.
## <a name="esp8266_manual_toolchain_installation"> Manual Toolchain Installation </a> &nbsp;[[TOC](#esp8266_toc)]
The most difficult way to install the toolchain is the manual installation of required components as described below.
@note Manual toolchain installation requires that the following packages (Debian/Ubuntu) are installed: ```autoconf``` ```automake``` ```bash``` ```bison``` ```build-essential``` ```bzip2``` ```coccinelle``` ```cppcheck``` ```curl``` ```doxygen``` ```g++``` ```gperf``` ```gawk``` ```gcc``` ```git``` ```graphviz``` ```help2man``` ```flex``` ```libexpat-dev``` ```libtool``` ```libtool-bin``` ```make``` ```ncurses-dev``` ```pcregrep``` ```python``` ```python-dev``` ```python-serial``` ```python3``` ```python3-flake8``` ```sed``` ```texinfo``` ```unrar-free``` ```unzip wget```
### <a name="esp8266_installation_of_esp_open_sdk"> Installation of esp-open-sdk </a> &nbsp;[[TOC](#esp8266_toc)]
esp-open-sdk is directly installed inside its source directory. Therefore, change directly to the target directory of the toolchain to build it.
```
cd /path/to/esp
git clone --recursive https://github.com/pfalcon/esp-open-sdk.git
cd esp-open-sdk
export ESP_OPEN_SDK_DIR=$PWD
```
If you plan to use the SDK version of the RIOT port and to use the SDK as part of esp-open-sdk, simply build its standalone version.
```
make STANDALONE=y
```
If you only plan to use the non-SDK version of the RIOT port or if you want to use one of Espressif's original SDKs, it is enough to build the toolchain.
```
make toolchain esptool libhal STANDALONE=n
```
Once compilation has been finished, the toolchain is available in ```$PWD/xtensa-lx106-elf/bin```. To use it, set the ```PATH``` variable accordingly.
```
export PATH=$ESP_OPEN_SDK_DIR/xtensa-lx106-elf/bin:$PATH
```
If you have compiled the standalone version of esp-open-sdk and you plan to use this SDK version, set additionally the ```ESP8266_SDK_DIR``` variable.
```
export ESP8266_SDK_DIR=$ESP_OPEN_SDK_DIR/sdk
```
### <a name="esp8266_installation_of_newlib-c"> Installation of newlib-c </a> &nbsp;[[TOC](#esp8266_toc)]
First, set the target directory for the installation.
```
export ESP8266_NEWLIB_DIR=/path/to/esp/newlib-xtensa
```
Please take care, to use the newlib-c version that was modified for esp-open-rtos since it includes ```stdatomic.h```.
```
cd /my/source/dir
git clone https://github.com/ourairquality/newlib.git
```
Once you have cloned the GIT repository, build and install it with following commands.
```
cd newlib
./configure --prefix=$ESP8266_NEWLIB_DIR --with-newlib --enable-multilib --disable-newlib-io-c99-formats --enable-newlib-supplied-syscalls --enable-target-optspace --program-transform-name="s&^&xtensa-lx106-elf-&" --disable-option-checking --with-target-subdir=xtensa-lx106-elf --target=xtensa-lx106-elf --enable-newlib-nano-formatted-io --enable-newlib-reent-small
make
make install
```
### <a name="esp8266_installation_of_espressif_original_sdk"> Installation of Espressif original SDK (optional) </a> &nbsp;[[TOC](#esp8266_toc)]
If you plan to use the SDK version of the RIOT port and if you want to use one of Espressif's original SDKs, you have to install it.
First, download the _ESP8266_NONOS_SDK_ version 2.1.0 from the [Espressif web site](https://github.com/espressif/ESP8266_NONOS_SDK/releases/tag/v2.1.0). Probably other version might also work. However, RIOT port is tested with version 2.1.0.
Once you have downloaded it, you can install it with following commands.
```
cd /path/to/esp
tar xvfz /downloads/ESP8266_NONOS_SDK-2.1.0.tar.gz
```
To use the installed SDK, set variable ```ESP8266_SDK_DIR``` accordingly.
```
export ESP8266_SDK_DIR=/path/to/esp/ESP8266_NONOS_SDK-2.1.0
```
# <a name="esp8266_flashing_the_device"> Flashing the Device </a> &nbsp;[[TOC](#esp8266_toc)]
## <a name="esp8266_toolchain_usage"> Toolchain Usage </a> &nbsp;[[TOC](#esp8266_toc)]
Once you have installed all required components, you should have the following directories.
```
/path/to/esp/esp-open-sdk
/path/to/esp/newlib-xtensa
/path/to/esp/ESP8266_NONOS_SDK-2.1.0 (optional)
```
To use the toolchain and optionally the SDK, please check that your environment variables are set correctly to
```
export PATH=/path/to/esp/esp-open-sdk/xtensa-lx106-elf/bin:$PATH
export ESP8266_NEWLIB_DIR=/path/to/esp/newlib-xtensa
```
and
```
export ESP8266_SDK_DIR=/path/to/esp/esp-open-sdk/sdk
```
or
```
export ESP8266_SDK_DIR=/path/to/esp/ESP8266_NONOS_SDK-2.1.0
```
## <a name="esp8266_compile_options"> Compile Options </a> &nbsp;[[TOC](#esp8266_toc)]
The compilation process can be controlled by a number of variables for the make command:
<center>
Option | Values | Default | Description
-------|--------|---------|------------
ENABLE_GDB | 0, 1 | 0 | Enable compilation with debug information for debugging with QEMU (```QEMU=1```), see section [QEMU Mode and GDB](#esp8266_qemu_mode_and_gdb)
FLASH_MODE | dout, dio, qout, qio | dout | Set the flash mode, please take care with your module, see section [Flash Modes](#esp8266_flash_modes)
NETDEV_DEFAULT | module name | mrf24j40 | Set the module that is used as default network device, see section [Network Devices](#esp8266_network_devices)
PORT | /dev/ttyUSBx | /dev/ttyUSB0 | Set the USB port for flashing the firmware
QEMU | 0, 1 | 0 | Generate an image for QEMU, see section [QEMU Mode and GDB](#esp8266_qemu_mode_and_gdb).
USE_SDK | 0, 1 | 0 | Compile the SDK version (```USE_SDK=1```), see section [SDK Task Handling](#esp8266_sdk_task_handling)
</center><br>
Optional features of ESP8266 can be enable by ```USEMODULE``` definitions in the makefile of the application. These are:
<center>
Module | Description
-------|------------
esp_spiffs | Enables the SPIFFS file system, see section [SPIFFS Device](#esp8266_spiffs_device)
esp_sw_timer | Enables software timer implementation, implies the setting ```USE_SDK=1```, see section [Timers](#esp8266_timers)
</center><br>
## <a name="esp8266_flash_modes"> Flash Modes </a> &nbsp;[[TOC](#esp8266_toc)]
The ```FLASH_MODE``` make command variable determines the mode that is used for flash access in normal operation.
The flash mode determines whether 2 data lines (```dio``` and ```dout```) or 4 data lines (```qio``` and ```qout```) for addressing and data access. For each data line, one GPIO is required. Therefore, using ```qio``` or ```qout``` increases the performance of SPI Flash data transfers, but uses two additional GPIOs (GPIO9 and GPIO10). That is, in this flash modes these GPIOs are not available for other purposes. If you can live with lower flash data transfer rates, you should always use ```dio``` or ```dout``` to keep GPIO9 and GPIO10 free for other purposes.
For more information about these flash modes, refer the documentation of [esptool.py](https://github.com/espressif/esptool/wiki/SPI-Flash-Modes).
@note While ESP8266 modules can be flashed with ```qio```, ```qout```, ```dio``` and ```dout```, ESP8285 modules have to be always flashed in ```dout``` mode. The default flash mode is ```dout```.
# <a name="esp8266_peripherals"> Peripherals </a> &nbsp;[[TOC](#esp8266_toc)]
## <a name="esp8266_gpio_pins"> GPIO pins </a> &nbsp;[[TOC](#esp8266_toc)]
ESP8266 has 17 GPIO pins, which are all digital pins. Some of them can not be used at all or have bootstrapping capabilities and are therefore not available on all boards.
<center>
Pin | Remarks
-------|--------
GPIO0 | usually pulled up
GPIO1 | UART TxD
GPIO2 | usually pulled up
GPIO3 | UART RxD
GPIO4 | |
GPIO5 | |
GPIO6 | Flash SPI
GPIO7 | Flash SPI
GPIO8 | Flash SPI
GPIO9 | Flash SPI in ```qout``` and ```qio``` mode, see section [Flash Modes](#esp8266_flash_modes)
GPIO10 | Flash SPI in ```qout``` and ```qio``` mode, see section [Flash Modes](#esp8266_flash_modes)
GPIO11 | Flash SPI
GPIO12 | |
GPIO13 | |
GPIO14 | |
GPIO15 | usually pulled down
GPIO16 | RTC pin and wake up signal in deep sleep mode
</center>
GPIO0, GPIO2, and GPIO15 are bootstrapping pins which are used to boot ESP8266 in different modes:
<center>
GPIO0 | GPIO2 | GPIO15 (MTDO) | Mode
:----:|:-----:|:-------------:|:------------------
1 | X | X | boot in SDIO mode to start OCD
0 | 0 | 1 | boot in UART mode for flashing the firmware
0 | 1 | 1 | boot in FLASH mode to boot the firmware from flash (default mode)
</center>
## <a name="esp8266_adc_channels"> ADC Channels </a> &nbsp;[[TOC](#esp8266_toc)]
ESP8266 has **one dedicated ADC** pin with a resolution of 10 bits. This ADC pin can measure voltages in the range of **0 V ... 1.1 V**.
@note Some boards have voltage dividers to scale this range to a maximum of 3.3 V. For more information, see the hardware manual for the board.
## <a name="esp8266_spi_interfaces"> SPI Interfaces </a> &nbsp;[[TOC](#esp8266_toc)]
ESP8266 provides two hardware SPI interfaces:
- _FSPI_ for flash memory access that is usually simply referred to as _SPI_
- _HSPI_ for peripherals
Even though _FSPI_ (or simply _SPI_) is a normal SPI interface, it is not possible to use it for peripherals. **HSPI is therefore the only usable SPI interface** available for peripherals as RIOT's ```SPI_DEV(0)```.
The pin configuration of the _HSPI_ interface ```SPI_DEV(0)``` is fixed. The only pin definition that can be overridden by an [application-specific board configuration](#esp8266_application_specific_board_configuration) is the CS signal defined by ```SPI0_CS0_GPIO```.
<center>
Signal of _HSPI_ | Pin
-----------------|-------
MISO | GPIO12
MOSI | GPIO13
SCK | GPIO14
CS | GPIOn with n = 0, 2, 4, 5, 15, 16 (additionally 9, 10 in ```dout``` and ```dio``` flash mode)
</center>
When the SPI is enabled using module ```periph_spi```, these GPIOs cannot be used for any other purpose. GPIOs 0, 2, 4, 5, 15, and 16 can be used as CS signal. In ```dio``` and ```dout``` flash modes (see section [Flash Modes](#esp8266_flash_modes)), GPIOs 9 and 10 can also be used as CS signal.
## <a name="esp8266_i2c_interfaces"> I2C Interfaces </a> &nbsp;[[TOC](#esp8266_toc)]
Since the ESP8266 does not or only partially support the I2C in hardware, I2C interfaces are realized as **bit-banging protocol in software**. The maximum usable bus speed is therefore ```I2C_SPEED_FAST_PLUS```. The maximum number of buses that can be defined is 2, ```I2C_DEV(0)``` ... ```I2C_DEV(1)```.
Number of I2C buses (```I2C_NUMOF```) and used GPIO pins (```I2Cx_SCL``` and ```I2Cx_SDA``` where ```x``` stands for the bus device ```x```) have to be defined in the board-specific peripheral configuration in ```$BOARD/periph_conf.h```. Furthermore, the default I2C bus speed (```I2Cx_SPEED```) that is used for bus ```x``` has to be defined.
In the following example, only one I2C bus is defined:
```
#define I2C_NUMOF (1)
#define I2C0_SPEED I2C_SPEED_FAST
#define I2C0_SDA GPIO4
#define I2C0_SCL GPIO5
```
A configuration with two I2C buses would look like the following:
```
#define I2C_NUMOF (2)
#define I2C0_SPEED I2C_SPEED_FAST
#define I2C0_SDA GPIO4
#define I2C0_SCL GPIO5
#define I2C1_SPEED I2C_SPEED_NORMAL
#define I2C1_SDA GPIO2
#define I2C1_SCL GPIO14
```
All these configurations can be overridden by an [application-specific board configuration](#esp8266_application_specific_board_configuration).
## <a name="esp8266_pwm_channels"> PWM Channels </a> &nbsp;[[TOC](#esp8266_toc)]
The hardware implementation of ESP8266 PWM supports only frequencies as power of two. Therefore, a **software implementation** of **one PWM device** (```PWM_DEV(0)```) with up to **8 PWM channels** (```PWM_CHANNEL_NUM_MAX```) is used.
@note The minimum PWM period that can be realized with this software implementation is 10 us or 100.000 PWM clock cycles per second. Therefore, the product of frequency and resolution should not be greater than 100.000. Otherwise the frequency is scaled down automatically.
GPIOs that can be used as channels of the PWM device ```PWM_DEV(0)``` are defined by ```PWM0_CHANNEL_GPIOS```. By default, GPIOs 2, 4 and 5 are defined as PWM channels. As long as these channels are not started with function ```pwm_set```, they can be used as normal GPIOs for other purposes.
GPIOs in ```PWM0_CHANNEL_GPIOS``` with a duty cycle value of 0 can be used as normal GPIOs for other purposes. GPIOs in ```PWM0_CHANNEL_GPIOS``` that are used for other purposes, e.g., I2C or SPI, are no longer available as PWM channels.
To define other GPIOs as PWM channels, just overwrite the definition of ```PWM_CHANNEL_GPIOS``` in an [application-specific board configuration](#esp8266_application_specific_board_configuration)
```
#define PWM0_CHANNEL_GPIOS { GPIO12, GPIO13, GPIO14, GPIO15 }
```
## <a name="esp8266_timers"> Timers </a> &nbsp;[[TOC](#esp8266_toc)]
There are two timer implementations:
- **hardware timer** implementation with **1 timer device** and only **1 channel**, the default
- **software timer** implementation with **1 timer device** and only **10 channels**
By default, the **hardware timer implementation** is used.
When the SDK version of the RIOT port (```USE_SDK=1```) is used, the **software timer** implementation is activated by using module ```esp_sw_timer```.
The software timer uses SDK's software timers to implement the timer channels. Although these SDK timers usually have a precision of a few microseconds, they can deviate up to 500 microseconds. So if you need a timer with high accuracy, you'll need to use the hardware timer with only one timer channel.
@note When module ```esp_sw_timer``` is used, the SDK version is automatically compiled (```USE_SDK=1```).
## <a name="esp8266_spiffs_device"> SPIFFS Device </a> &nbsp;[[TOC](#esp8266_toc)]
If SPIFFS module is enabled (```USE_SPIFFS=1```), the implemented MTD device ```mtd0``` for the on-board SPI flash memory is used together with modules ```spiffs``` and ```vfs``` to realize a persistent file system.
For this purpose, the flash memory is formatted as SPIFFS starting at the address ```0x80000``` (512 kByte) on first boot. All sectors up to the last 5 sectors of the flash memory are then used for the file system. With a fixed sector size of 4096 bytes, the top address of the SPIFF is ```flash_size - 5 * 4096```, e.g., ```0xfb000``` for a flash memory of 1 MByte. The size of the SPIFF then results from:
```
flash_size - 5 * 4096 - 512 kByte
```
Please refer file ```$RIOTBASE/tests/unittests/test-spiffs/tests-spiffs.c``` for more information on how to use SPIFFS and VFS together with a MTD device ```mtd0``` alias ```MTD_0```.
## <a name="esp8266_other_peripherals"> Other Peripherals </a> &nbsp;[[TOC](#esp8266_toc)]
The ESP8266 port of RIOT also supports
- one hardware number generator with 32 bit,
- one UART interface (GPIO1 and GPIO3),
- a CPU-ID function, and
- power management functions.
RTC is not yet implemented.
# <a name="esp8266_preconfigured_devices"> Preconfigured Devices </a> &nbsp;[[TOC](#esp8266_toc)]
The ESP8266 port of RIOT has been tested with several common external devices that can be connected to ESP8266 boards and are preconfigured accordingly.
## <a name="esp8266_network_devices"> Network Devices </a> &nbsp;[[TOC](#esp8266_toc)]
RIOT provides a number of driver modules for different types of network devices, e.g., IEEE 802.15.4 radio modules and Ethernet modules. The RIOT ESP8266 port has been tested with the following network devices and is preconfigured to create RIOT network applications with these devices:
- [mrf24j40](http://riot-os.org/api/group__drivers__mrf24j40.html) (driver for Microchip MRF24j40 based IEEE 802.15.4
- [enc28j60](http://riot-os.org/api/group__drivers__enc28j60.html) (driver for Microchip ENC28J60 based Ethernet modules)
If the RIOT network application uses a default network device (module ```netdev_default```), the ```NETDEV_DEFAULT``` make command variable can be used to define the device that will be used as the default network device. The value of this variable must match the name of the driver module for this network device. If ```NETDEV_DEFAULT``` is not defined, the ```mrf24j40``` module is used as default network device.
### <a name="esp8266_using_mrf24j40"> Using MRF24J40 (module ```mrf24j40```) </a> &nbsp;[[TOC](#esp8266_toc)]
To use MRF24J40 based IEEE 802.15.4 modules as network device, module ```mrf24j40``` has to be added to a makefile:
```
USEMODULE += mrf24j40
```
@note If module ```netdev_default``` is used (which is usually the case in all networking applications), module ```mrf24j40``` is added automatically.
Module ```mrf24j40``` uses the following interface parameters by default:
<center>
Parameter | Default | Remarks
-----------------------|--------------|----------------------------
MRF24J40_PARAM_SPI | SPI_DEV(0) | fix, see section [SPI Interfaces](#esp8266_spi_interfaces)
MRF24J40_PARAM_SPI_CLK | SPI_CLK_1MHZ | fix
MRF24J40_PARAM_CS | GPIO16 | can be overridden
MRF24J40_PARAM_INT | GPIO0 | can be overridden
MRF24J40_PARAM_RESET | GPIO2 | can be overridden
</center><br>
Used GPIOs can be overridden by corresponding make command variables, e.g,:
```
make flash BOARD=esp8266-esp-12x -C examples/gnrc_networking MRF24J40_PARAM_CS=GPIO15
```
or by an [application-specific board configuration](#esp8266_application_specific_board_configuration).
### <a name="esp8266_using_enc28j60"> Using ENC28J60 (module ```enc28j60```) </a> &nbsp;[[TOC](#esp8266_toc)]
To use ENC28J60 Ethernet modules as network device, module ```enc28j60``` has to be added to your makefile:
```
USEMODULE += enc28j60
```
Module ```enc28j60``` uses the following interface parameters by default:
<center>
Parameter | Default | Remarks
---------------------|--------------|----------------------------
ENC28J60_PARAM_SPI | SPI_DEV(0) | fix, see section [SPI Interfaces](#esp8266_spi_interfaces)
ENC28J60_PARAM_CS | GPIO4 | can be overridden
ENC28J60_PARAM_INT | GPIO9 | can be overridden
ENC28J60_PARAM_RESET | GPIO10 | can be overridden
</center>
Used GPIOs can be overridden by corresponding make command variables, e.g.:
```
make flash BOARD=esp8266-esp-12x -C examples/gnrc_networking ENC28J60_PARAM_CS=GPIO15
```
or by an [application-specific board configuration](#esp8266_application_specific_board_configuration).
To use module ```enc28j60``` as default network device, the ```NETDEV_DEFAULT``` make command variable has to set, for example:
```
make flash BOARD=esp8266-esp-12x -C examples/gnrc_networking NETDEV_DEFAULT=enc28j60
```
## <a name="esp8266_sd_card_device"> SD-Card Device </a> &nbsp;[[TOC](#esp8266_toc)]
ESP8266 port of RIOT is preconfigured for RIOT applications that use the [SPI SD-Card driver](http://riot-os.org/api/group__drivers__sdcard__spi.html). To use SPI SD-Card driver, the ```sdcard_spi``` module has to be added to a makefile:
```
USEMODULE += sdcard_spi
```
Module ```sdcard_spi``` uses the following interface parameters by default:
<center>
Parameter | Default | Remarks
-----------------------|---------------|----------------------------
SDCARD_SPI_PARAM_SPI | SPI0_DEV | fix, see section [SPI Interfaces](#esp8266_spi_interfaces)
SDCARD_SPI_PARAM_CS | SPI0_CS0_GPIO | can be overridden
</center>
The GPIO used as CS signal can be overridden by an [application-specific board configuration](#esp8266_application_specific_board_configuration).
# <a name="esp8266_application_specific_configurations"> Application-Specific Configurations </a> &nbsp;[[TOC](#esp8266_toc)]
Configuration used for peripheral devices and for device driver modules, such as GPIO pins, bus interfaces or bus speeds are typically defined in the board-specific configurations ```board.h``` and ```periph_conf.h``` or in the driver parameter configuration ```< driver>_params.h```. Most of these definitions are enclosed by
```
#ifndef ANY_PARAMETER
...
#endif
```
constructs, so it is possible to override them by defining them in front of these constructs.
\anchor esp8266_app_spec_conf
## <a name="esp8266_application_specific_board_configuration"> Application-Specific Board Configuration </a> &nbsp;[[TOC](#esp8266_toc)]
To override standard board configurations, simply create an application-specific board configuration file ```$APPDIR/board.h``` in the source directory of the application ```$APPDIR``` and add the definitions to be overridden. To force the preprocessor to include board's original ```board.h``` after that, add the ```include_next``` preprocessor directive as the **last** line.
For example to override the default definition of the GPIOs that are used as PWM channels, the application-specific board configuration file ```$APPDIR/board.h``` could look like the following:
```
#ifdef CPU_ESP8266
#define PWM0_CHANNEL_GPIOS { GPIO12, GPIO13, GPIO14, GPIO15 }
#endif
#include_next "board.h"
```
To make such application-specific board configurations dependent on the ESP8266 MCU or a particular ESP8266 card, you should always enclose these definitions in the following constructs:
```
#ifdef CPU_ESP8266
...
#endif
#ifdef BOARD_ESP8266_ESP_12X
...
#endif
```
To ensure that the application-specific board configuration ```$APPDIR/board.h``` is included first, insert the following line as the **first** line to the application makefile ```$APPDIR/Makefile```.
```
INCLUDES += -I$(APPDIR)
```
## <a name="esp8266_application_specific_driver_configuration"> Application-Specific Driver Configuration </a> &nbsp;[[TOC](#esp8266_toc)]
Using the approach for overriding board configurations, the parameters of drivers that are typically defined in ```drivers/<device>/include/<device>_params.h``` can also be overridden. For that purpose just create an application-specific driver parameter file ```$APPDIR/<device>_params.h``` in the source directory ```$APPDIR``` of the application and add the definitions to be overridden. To force the preprocessor to include driver's original ```<device>_params.h``` after that, add the ```include_next``` preprocessor directive as the **last** line.
For example, to override a GPIO used for LIS3DH sensor, the application-specific driver parameter file ```$APPDIR/<device>_params.h``` could look like the following:
```
#ifdef CPU_ESP8266
#define LIS3DH_PARAM_INT2 (GPIO_PIN(0, 4))
#endif
#include_next "lis3dh_params.h"
```
To make such application-specific board configurations dependent on the ESP8266 MCU or a particular ESP8266 card, you should always enclose these definitions in the following constructs:
```
#ifdef CPU_ESP8266
...
#endif
#ifdef BOARD_ESP8266_ESP_12X
...
#endif
```
To ensure that the application-specific driver parameter file ```$APPDIR/<device>_params.h``` is included first, insert the following line as the **first** line to the application makefile ```$APPDIR/Makefile```.
```
INCLUDES += -I$(APPDIR)
```
# <a name="esp8266_sdk_task_handling"> SDK Task Handling </a> &nbsp;[[TOC](#esp8266_toc)]
With make command variable ```USE_SDK=1``` the Espressif SDK is used. This is necessary, for example, if you want to use the built-in WLAN module. The SDK internally uses its own tasks (SDK tasks) and its own scheduling mechanism to realize event-driven SDK functions such as WiFi functions and software timers, and to keep the system alive. For this purpose, the SDK regularly executes SDK tasks with pending events in an endless loop using the ROM function ```ets_run```.
Interrupt service routines do not process interrupts directly but use the ```ets_post``` ROM function to send an event to one of these SDK tasks, which then processes the interrupts asynchronously. A context switch is not possible in the interrupt service routines.
In the RIOT port, the task management of the SDK is replaced by the task management of the RIOT. To handle SDK tasks with pending events so that the SDK functions work and the system keeps alive, the ROM functions ```ets_run``` and ```ets_post``` are overwritten. The ```ets_run``` function performs all SDK tasks with pending events exactly once. It is executed at the end of the ```ets_post``` function and thus usually at the end of an SDK interrupt service routine or before the system goes into the lowest power mode.
@note Since the non-SDK version of RIOT is much smaller and faster than the SDK version, you should always compile your application without the SDK (```USE_SDK=0```, the default) if you don't need the built-in WiFi module.
# <a name="esp8266_qemu_mode_and_gdb"> QEMU Mode and GDB </a> &nbsp;[[TOC](#esp8266_toc)]
When QEMU mode is enabled (```QEMU=1```), instead of loading the image to the target hardware, a binary image ```$ELFFILE.bin``` is created in the target directory. This binary image file can be used together with QEMU to debug the code in GDB.
The binary image can be compiled with debugging information (```ENABLE_GDB=1```) or optimized without debugging information (```ENABLE_GDB=0```). The latter one is the default. The version with debugging information can be debugged in source code while the optimized version can only be debugged in assembler mode.
To use QEMU, you have to install QEMU for Xtensa with ESP8266 machine implementation as following.
```
cd /my/source/dir
git clone https://github.com/gschorcht/qemu-xtensa
cd qemu-xtensa/
git checkout xtensa-esp8266
export QEMU=/path/to/esp/qemu
./configure --prefix=$QEMU --target-list=xtensa-softmmu --disable-werror
make
make install
```
Once the compilation has been finished, QEMU for Xtensa with ESP8266 machine implementation should be available in ```/path/to/esp/qemu/bin``` and you can start it with
```
$QEMU/bin/qemu-system-xtensa -M esp8266 -nographic -serial stdio -monitor none -s -S -kernel /path/to/the/target/image.elf.bin
```
where ```/path/to/the/target/image.elf.bin``` is the path to the binary image as generated by the ```make``` command as ```$ELFFILE.bin```. After that you can start GDB in another terminal window using command:
```
xtensa-lx106-elf-gdb
```
If you have compiled your binary image with debugging information, you can load the ELF file in gdb with:
```
(gdb) file /path/to/the/target/image.elf
```
To start debugging, you have to connect to QEMU with command:
```
(gdb) target remote :1234
```
*/

192
cpu/esp8266/exceptions.c Normal file
View File

@ -0,0 +1,192 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266
* @{
*
* @file
* @brief ESP8266 exception handling
*
* @author Gunar Schorcht <gunar@schorcht.net>
* @}
*/
#define ENABLE_DEBUG 0
#include "debug.h"
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#include "common.h"
#include "log.h"
#include "periph/pm.h"
#include "ps.h"
#include "esp/common_macros.h"
#include "esp/xtensa_ops.h"
#include "sdk/ets.h"
#include "xtensa/corebits.h"
#ifndef MODULE_ESP_SDK_INT_HANDLING
#include "xtensa/xtensa_api.h"
#endif
extern void malloc_stats (void);
extern unsigned int get_free_heap_size (void);
extern uint8_t _eheap; /* end of heap (defined in esp8266.riot-os.app.ld) */
extern uint8_t _sheap; /* start of heap (defined in esp8266.riot-os.app.ld) */
static const char* exception_names [] =
{
"IllegalInstructionCause", /* 0 */
"SyscallCause", /* 1 */
"InstructionFetchErrorCause", /* 2 */
"LoadStoreErrorCause", /* 3 */
"Level1InterruptCause", /* 4 */
"AllocaCause", /* 5 */
"IntegerDivideByZeroCause", /* 6 */
"", /* 7 - reserved */
"PrivilegedCause", /* 8 */
"LoadStoreAlignmentCause", /* 9 */
"", /* 10 - reserved */
"", /* 11 - reserved */
"InstrPIFDataErrorCause", /* 12 */
"LoadStorePIFDataErrorCause", /* 13 */
"InstrPIFAddrErrorCause", /* 14 */
"LoadStorePIFAddrErrorCause", /* 15 */
"InstTLBMissCause", /* 16 */
"InstTLBMultiHitCause", /* 17 */
"InstFetchPrivilegeCause", /* 18 */
"", /* 19 - reserved */
"InstFetchProhibitedCause", /* 20 */
"", /* 21 - reserved */
"", /* 22 - reserved */
"", /* 23 - reserved */
"LoadStoreTLBMissCause", /* 24 */
"LoadStoreTLBMultiHitCause", /* 25 */
"LoadStorePrivilegeCause", /* 26 */
"", /* 27 - reserved */
"LoadProhibitedCause", /* 28 */
"StoreProhibitedCause", /* 29 */
"", /* 30 - reserved */
"", /* 31 - reserved */
"Coprocessor0Disabled", /* 32 */
"Coprocessor1Disabled", /* 33 */
"Coprocessor2Disabled", /* 34 */
"Coprocessor3Disabled", /* 35 */
"Coprocessor4Disabled", /* 36 */
"Coprocessor5Disabled", /* 37 */
"Coprocessor6Disabled", /* 38 */
"Coprocessor7Disabled", /* 39 */
};
#ifdef MODULE_ESP_SDK_INT_HANDLING
void IRAM NORETURN exception_handler (void *arg)
{
(void)arg;
uint32_t excsave1;
uint32_t excvaddr;
uint32_t exccause;
RSR(excsave1, excsave1);
RSR(excvaddr, excvaddr);
RSR(exccause, exccause);
(void)exception_names;
ets_printf("EXCEPTION!! exccause=%d (%s) @%08lx excvaddr=%08lx\n",
exccause, exception_names[exccause],
excsave1, excvaddr);
#if defined(DEVELHELP)
#if defined(MODULE_PS)
ps();
#endif
struct mallinfo minfo = mallinfo();
ets_printf("heap: %lu (free %lu) byte\n", &_eheap - &_sheap, get_free_heap_size());
ets_printf("sysmem: %d (used %d, free %d)\n", minfo.arena, minfo.uordblks, minfo.fordblks);
#endif
/* flushing the buffer */
ets_printf(" \n");
ets_printf(" \n");
ets_printf(" \n");
/* hard reset */
__asm__ volatile (" call0 0x40000080 ");
UNREACHABLE();
}
void init_exceptions (void)
{
_xtos_set_exception_handler(EXCCAUSE_UNALIGNED, exception_handler);
_xtos_set_exception_handler(EXCCAUSE_ILLEGAL, exception_handler);
_xtos_set_exception_handler(EXCCAUSE_INSTR_ERROR, exception_handler);
_xtos_set_exception_handler(EXCCAUSE_LOAD_STORE_ERROR, exception_handler);
_xtos_set_exception_handler(EXCCAUSE_LOAD_PROHIBITED, exception_handler);
_xtos_set_exception_handler(EXCCAUSE_STORE_PROHIBITED, exception_handler);
_xtos_set_exception_handler(EXCCAUSE_PRIVILEGED, exception_handler);
}
#else /* MODULE_ESP_SDK_INT_HANDLING */
void IRAM NORETURN exception_handler (XtExcFrame *frame)
{
uint32_t excsave1;
RSR(excsave1, excsave1);
ets_printf("EXCEPTION!! exccause=%d (%s) @%08lx excvaddr=%08lx\n",
frame->exccause, exception_names[frame->exccause],
excsave1, frame->excvaddr);
#if defined(DEVELHELP)
#if defined(MODULE_PS)
ps();
#endif
struct mallinfo minfo = mallinfo();
ets_printf("heap: %lu (free %lu) byte\n", &_eheap - &_sheap, get_free_heap_size());
ets_printf("sysmem: %d (used %d, free %d)\n", minfo.arena, minfo.uordblks, minfo.fordblks);
#endif
/* flushing the buffer */
ets_printf(" \n");
ets_printf(" \n");
ets_printf(" \n");
/* hard reset */
__asm__ volatile (" call0 0x40000080 ");
UNREACHABLE();
}
void init_exceptions (void)
{
xt_set_exception_handler(EXCCAUSE_UNALIGNED, exception_handler);
xt_set_exception_handler(EXCCAUSE_ILLEGAL, exception_handler);
xt_set_exception_handler(EXCCAUSE_INSTR_ERROR, exception_handler);
xt_set_exception_handler(EXCCAUSE_LOAD_STORE_ERROR, exception_handler);
xt_set_exception_handler(EXCCAUSE_LOAD_PROHIBITED, exception_handler);
xt_set_exception_handler(EXCCAUSE_STORE_PROHIBITED, exception_handler);
xt_set_exception_handler(EXCCAUSE_PRIVILEGED, exception_handler);
}
#endif /* MODULE_ESP_SDK_INT_HANDLING */
void IRAM NORETURN panic_arch(void)
{
#if defined(DEVELHELP)
struct mallinfo minfo = mallinfo();
ets_printf("heap: %lu (free %lu) byte\n", &_eheap - &_sheap, get_free_heap_size());
ets_printf("sysmem: %d (used %d, free %d)\n", minfo.arena, minfo.uordblks, minfo.fordblks);
ets_printf(" \n");
ets_printf(" \n");
#endif
/* hard reset */
__asm__ volatile (" call0 0x40000080 ");
UNREACHABLE();
}

View File

@ -0,0 +1,92 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266
* @{
*
* @file
* @brief Common helper macros
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
*/
#ifndef COMMON_H
#define COMMON_H
#ifndef DOXYGEN
#ifdef __cplusplus
extern "C" {
#endif
/** string representation of x */
#ifndef XTSTR
#define _XTSTR(x) # x
#define XTSTR(x) _XTSTR(x)
#endif
#endif
#if !defined(ICACHE_FLASH) || defined (DOXYGEN)
#ifndef ICACHE_RAM_ATTR
/** Places the code with this attribute in the IRAM. */
#define ICACHE_RAM_ATTR __attribute__((section(".iram.text")))
#endif
#else
#ifndef ICACHE_RAM_ATTR
#define ICACHE_RAM_ATTR
#endif
#endif
/** Print out a message that function is not yet implementd */
#define NOT_YET_IMPLEMENTED() LOG_INFO("%s not yet implemented\n", __func__)
/** Print out a message that function is not supported */
#define NOT_SUPPORTED() LOG_INFO("%s not supported\n", __func__)
#if defined(ENABLE_DEBUG) || defined(DOXYGEN)
/**
* @brief Parameter check with return a value.
*
* If ENABLE_DEBUG is true, the macro checks a condition and returns with a value
* if the condition is not fulfilled.
* @param cond the condition
* @param err the return value in the case the condition is not fulfilled.
*/
#define CHECK_PARAM_RET(cond,err) if (!(cond)) \
{ \
DEBUG("%s\n", "parameter condition (" #cond ") not fulfilled"); \
return err; \
}
/**
* @brief Parameter check without return value.
*
* If ENABLE_DEBUG is true, the macro checks a condition and returns without a
* value if the condition is not fulfilled.
* @param cond the condition
*/
#define CHECK_PARAM(cond) if (!(cond)) \
{ \
DEBUG("%s\n", "parameter condition (" #cond ") not fulfilled"); \
return; \
}
#else
#define CHECK_PARAM_RET(cond,err) if (!(cond)) return err;
#define CHECK_PARAM(cond) if (!(cond)) return;
#endif
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* COMMON_H */

47
cpu/esp8266/include/cpu.h Normal file
View File

@ -0,0 +1,47 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266
* @{
*
* @file
* @brief CPU common functions
*
* @author Gunar Schorcht <gunar@schorcht.net>
*/
#ifndef CPU_H
#define CPU_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#include <stdint.h>
#include "irq.h"
#define PROVIDES_PM_SET_LOWEST
/**
* @brief Print the last instruction's address
*
* @todo: Not supported
*/
static inline void cpu_print_last_instruction(void)
{
/* This function must exist else RIOT won't compile */
}
#ifdef __cplusplus
}
#endif
#endif /* CPU_H */
/** @} */

View File

@ -0,0 +1,63 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266
* @{
*
* @file
* @brief CPU specific configuration options
*
* @author Gunar Schorcht <gunar@schorcht.net>
*/
#ifndef CPU_CONF_H
#define CPU_CONF_H
#include "xtensa_conf.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Stack size configuration
* @{
*/
#ifdef MODULE_ESP_SDK_INT_HANDLING
#define THREAD_EXTRA_STACKSIZE_PRINTF (0)
#define THREAD_STACKSIZE_DEFAULT (2048)
#define THREAD_STACKSIZE_IDLE (2048)
#else
#define THREAD_EXTRA_STACKSIZE_PRINTF (0)
#define THREAD_STACKSIZE_DEFAULT (2048)
#define THREAD_STACKSIZE_IDLE (2048)
#endif
/** @} */
/**
* Buffer size used for printf functions (maximum length of formatted output).
*/
#define PRINTF_BUFSIZ 256
/* Following include is neccessary to overwrite newlib's PRI*8 and PRI*32. */
/* PLEASE NOTE: ets_vprintf does not understand %i %li, or %hi */
#ifndef DOXYGEN
#include <inttypes.h>
#undef __INT8
#define __INT8
#undef __INT32
#define __INT32
#endif
#ifdef __cplusplus
}
#endif /* CPU_CONF_H */
#endif /* CPU_CONF_H */
/** @} */

View File

@ -0,0 +1,34 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266
* @{
*
* @file
* @brief ESP8266 exception handling
*
* @author Gunar Schorcht <gunar@schorcht.net>
* @}
*/
#ifndef EXCEPTIONS_H
#define EXCEPTIONS_H
#ifdef __cplusplus
extern "C" {
#endif
/** Initalize exception handler */
extern void init_exceptions(void);
#ifdef __cplusplus
}
#endif
#endif /* EXCEPTIONS_H */

View File

@ -0,0 +1,55 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266
* @ingroup drivers_periph_gpio
* @{
*
* @file
* @brief Low-level GPIO driver implementation for ESP8266
*
* @author Gunar Schorcht <gunar@schorcht.net>
* @}
*/
#ifndef GPIO_COMMON_H
#define GPIO_COMMON_H
#ifdef __cplusplus
extern "C" {
#endif
/** Map of GPIO pin numbers to IOMUX pin numbers */
extern const uint8_t _gpio_to_iomux[];
/** Map of IOMUX pin numbers to GPIO pin numbers */
extern const uint8_t _iomux_to_gpio[];
/**
* @brief Definition of possible GPIO usage types
*/
typedef enum
{
_GPIO = 0, /**< pin used as standard GPIO */
_I2C, /**< pin used as I2C signal */
_PWM, /**< pin used as PWM output */
_SPI, /**< pin used as SPI interface */
_SPIF, /**< pin used as SPI flash interface */
_UART, /**< pin used as UART interface */
} _gpio_pin_usage_t;
/**
* Holds the usage type of each GPIO pin
*/
extern _gpio_pin_usage_t _gpio_pin_usage [GPIO_PIN_NUMOF];
#ifdef __cplusplus
}
#endif
#endif /* GPIO_COMMON_H */

View File

@ -0,0 +1,83 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266
* @{
*
* @file
* @brief Implementation of the kernels irq interface
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
#ifndef IRQ_ARCH_H
#define IRQ_ARCH_H
#include "irq.h"
#include "sched.h"
#include "thread.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Indicates the interrupt nesting depth
*
* The variable is increment on entry into and decremented on exit from an ISR.
*/
extern uint32_t irq_interrupt_nesting;
#if defined(MODULE_ESP_SDK_INT_HANDLING) || defined(DOXYGEN)
/**
* @brief Macros that have to be used on entry into and reset from an ISR
*
* NOTE: since they use a local variable they can be used only in same function
* @{
*/
/** Macro that has to be used at the entry point of an ISR */
#define irq_isr_enter() int _irq_state = irq_disable (); \
irq_interrupt_nesting++;
/** Macro that has to be used at the exit point of an ISR */
#define irq_isr_exit() if (irq_interrupt_nesting) \
irq_interrupt_nesting--; \
irq_restore (_irq_state); \
if (sched_context_switch_request) \
thread_yield();
#else /* MODULE_ESP_SDK_INT_HANDLING */
/* in non SDK task handling all the stuff is done in _frxt_int_enter and _frxt_int_exit */
#define irq_isr_enter() /* int _irq_state = irq_disable (); \
irq_interrupt_nesting++; */
#define irq_isr_exit() /* if (irq_interrupt_nesting) \
irq_interrupt_nesting--; \
irq_restore (_irq_state); */
#endif /* MODULE_ESP_SDK_INT_HANDLING */
/**
* @brief Macros to enter and exit from critical region
*
* NOTE: since they use a local variable they can be used only in same function
* @{
*/
#define critical_enter() int _irq_state = irq_disable ()
#define critical_exit() irq_restore(_irq_state)
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* IRQ_ARCH_H */

View File

@ -0,0 +1,169 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266
* @{
*
* @file
* @brief CPU specific definitions and functions for peripheral handling
*
* @author Gunar Schorcht <gunar@schorcht.net>
*/
#ifndef PERIPH_CPU_H
#define PERIPH_CPU_H
#include <stdint.h>
#include "eagle_soc.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Override the ADC resolution configuration
* @{
*/
#define HAVE_ADC_RES_T
typedef enum {
ADC_RES_10BIT /* only one resolution is supported */
} adc_res_t;
/** @} */
/**
* @brief Length of the CPU_ID in octets
*/
#define CPUID_LEN (4U)
/**
* @name GPIO configuration of ESP8266
* @{
*/
/**
* @brief Available ports on the ESP8266
* @{
*/
#define PORT_GPIO 0 /**< port GPIO */
/** @} */
/**
* @brief Definition of a fitting UNDEF value
*/
#define GPIO_UNDEF (GPIO_ID_NONE)
/**
* @brief Define CPU specific GPIO pin generator macro
*/
#define GPIO_PIN(x, y) ((x << 4) | y)
/**
* @brief Define CPU specific number of GPIO pins
*/
#define GPIO_PIN_NUMOF GPIO_PIN_COUNT+1
/**
* @name Predefined GPIO names
* @{
*/
#define GPIO0 (GPIO_PIN(PORT_GPIO,0))
#define GPIO1 (GPIO_PIN(PORT_GPIO,1))
#define GPIO2 (GPIO_PIN(PORT_GPIO,2))
#define GPIO3 (GPIO_PIN(PORT_GPIO,3))
#define GPIO4 (GPIO_PIN(PORT_GPIO,4))
#define GPIO5 (GPIO_PIN(PORT_GPIO,5))
#define GPIO6 (GPIO_PIN(PORT_GPIO,6))
#define GPIO7 (GPIO_PIN(PORT_GPIO,7))
#define GPIO8 (GPIO_PIN(PORT_GPIO,8))
#define GPIO9 (GPIO_PIN(PORT_GPIO,9))
#define GPIO10 (GPIO_PIN(PORT_GPIO,10))
#define GPIO11 (GPIO_PIN(PORT_GPIO,11))
#define GPIO12 (GPIO_PIN(PORT_GPIO,12))
#define GPIO13 (GPIO_PIN(PORT_GPIO,13))
#define GPIO14 (GPIO_PIN(PORT_GPIO,14))
#define GPIO15 (GPIO_PIN(PORT_GPIO,15))
#define GPIO16 (GPIO_PIN(PORT_GPIO,16))
/** @} */
#ifndef DOXYGEN
#define GPIO0_MASK (BIT(0))
#define GPIO1_MASK (BIT(1))
#define GPIO2_MASK (BIT(2))
#define GPIO3_MASK (BIT(3))
#define GPIO4_MASK (BIT(4))
#define GPIO5_MASK (BIT(5))
#define GPIO6_MASK (BIT(6))
#define GPIO7_MASK (BIT(7))
#define GPIO8_MASK (BIT(8))
#define GPIO9_MASK (BIT(9))
#define GPIO10_MASK (BIT(10))
#define GPIO11_MASK (BIT(11))
#define GPIO12_MASK (BIT(12))
#define GPIO13_MASK (BIT(13))
#define GPIO14_MASK (BIT(14))
#define GPIO15_MASK (BIT(15))
#define GPIO16_MASK (BIT(16))
/**
* @brief Override flank selection values
* @{
*/
#define HAVE_GPIO_FLANK_T
typedef enum {
GPIO_NONE = 0,
GPIO_RISING = 1, /**< emit interrupt on rising flank */
GPIO_FALLING = 2, /**< emit interrupt on falling flank */
GPIO_BOTH = 3, /**< emit interrupt on both flanks */
GPIO_LOW = 4, /**< emit interrupt on low level */
GPIO_HIGH = 5 /**< emit interrupt on low level */
} gpio_flank_t;
/** @} */
#endif /* DOXYGEN */
/** @} */
/**
* @name I2C configuration
* @{
*/
#define PERIPH_I2C_NEED_READ_REG
#define PERIPH_I2C_NEED_READ_REGS
#define PERIPH_I2C_NEED_WRITE_REG
#define PERIPH_I2C_NEED_WRITE_REGS
/** @} */
/**
* @name Power management configuration
* @{
*/
#define PROVIDES_PM_SET_LOWEST
#define PROVIDES_PM_RESTART
#define PROVIDES_PM_OFF
/** @} */
/**
* @name SPI configuration
* @{
*/
#if defined(MODULE_PERIPH_SPI)
#define PERIPH_SPI_NEEDS_TRANSFER_BYTE
#define PERIPH_SPI_NEEDS_TRANSFER_REG
#define PERIPH_SPI_NEEDS_TRANSFER_REGS
#endif /* MODULE_PERIPH_SPI */
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* PERIPH_CPU_H */
/** @} */

View File

@ -0,0 +1,228 @@
/*
* Copyright (c) 2004, 2005 by
* Ralf Corsepius, Ulm/Germany. All rights reserved.
*
* Permission to use, copy, modify, and distribute this software
* is freely granted, provided that this notice is preserved.
*/
/**
* @brief Modified _intsup.h for compilation with RIOT OS
* Source: /path/to/newlib-xtensa/xtensa-lx106-elf/include/sys
* Changes: __INT32 (see below)
*/
#ifndef SYS__INTSUP_H
#define SYS__INTSUP_H
#ifndef DOXYGEN
#ifdef __cplusplus
extern "C" {
#endif
/* additional header guard to avoid conflicts if original file is included later */
#ifndef _SYS__INTSUP_H
#define _SYS__INTSUP_H
#include <sys/features.h>
#if __GNUC_PREREQ (3, 2)
/* gcc > 3.2 implicitly defines the values we are interested */
#define __STDINT_EXP(x) __##x##__
#else
#define __STDINT_EXP(x) x
#include <limits.h>
#endif
/* Determine how intptr_t and intN_t fastN_t and leastN_t are defined by gcc
for this target. This is used to determine the correct printf() constant in
inttypes.h and other constants in stdint.h.
So we end up with
?(signed|unsigned) char == 0
?(signed|unsigned) short == 1
?(signed|unsigned) int == 2
?(signed|unsigned) short int == 3
?(signed|unsigned) long == 4
?(signed|unsigned) long int == 6
?(signed|unsigned) long long == 8
?(signed|unsigned) long long int == 10
*/
#pragma push_macro("signed")
#pragma push_macro("unsigned")
#pragma push_macro("char")
#pragma push_macro("short")
#pragma push_macro("__int20")
#pragma push_macro("int")
#pragma push_macro("long")
#undef signed
#undef unsigned
#undef char
#undef short
#undef int
#undef __int20
#undef long
#define signed +0
#define unsigned +0
#define char +0
#define short +1
#define __int20 +2
#define int +2
#define long +4
#if (__INTPTR_TYPE__ == 8 || __INTPTR_TYPE__ == 10)
#define _INTPTR_EQ_LONGLONG
#elif (__INTPTR_TYPE__ == 4 || __INTPTR_TYPE__ == 6)
#define _INTPTR_EQ_LONG
/* Note - the tests for _INTPTR_EQ_INT and _INTPTR_EQ_SHORT are currently
redundant as the values are not used. But one day they may be needed
and so the tests remain. */
#elif __INTPTR_TYPE__ == 2
#define _INTPTR_EQ_INT
#elif (__INTPTR_TYPE__ == 1 || __INTPTR_TYPE__ == 3)
#define _INTPTR_EQ_SHORT
#else
#error "Unable to determine type definition of intptr_t"
#endif
#if (__INT32_TYPE__ == 4 || __INT32_TYPE__ == 6)
#define _INT32_EQ_LONG
#elif __INT32_TYPE__ == 2
/* Nothing to define because int32_t is safe to print as an int. */
#else
#error "Unable to determine type definition of int32_t"
#endif
#if (__INT8_TYPE__ == 0)
#define __INT8 "hh"
#elif (__INT8_TYPE__ == 1 || __INT8_TYPE__ == 3)
#define __INT8 "h"
#elif (__INT8_TYPE__ == 2)
#define __INT8
#elif (__INT8_TYPE__ == 4 || __INT8_TYPE__ == 6)
#define __INT8 "l"
#elif (__INT8_TYPE__ == 8 || __INT8_TYPE__ == 10)
#define __INT8 "ll"
#endif
#if (__INT16_TYPE__ == 1 || __INT16_TYPE__ == 3)
#define __INT16 "h"
#elif (__INT16_TYPE__ == 2)
#define __INT16
#elif (__INT16_TYPE__ == 4 || __INT16_TYPE__ == 6)
#define __INT16 "l"
#elif (__INT16_TYPE__ == 8 || __INT16_TYPE__ == 10)
#define __INT16 "ll"
#endif
#if (__INT32_TYPE__ == 2)
#define __INT32
#elif (__INT32_TYPE__ == 4 || __INT32_TYPE__ == 6)
/**
* Definition of __INT32 had to be changed since to avoid format warnings/
* errors since xtensa-lx106-elf-gcc defines *__INT32_TYPE__* as *long int*
* while newlib defines *int32_t* as *signed int*. PRI*32 there throw format
* warnings/errors.
*/
#if 0
#define __INT32 "l"
#else
#define __INT32
#endif
#elif (__INT32_TYPE__ == 8 || __INT32_TYPE__ == 10)
#define __INT32 "ll"
#endif
#if (__INT64_TYPE__ == 2)
#define __INT64
#elif (__INT64_TYPE__ == 4 || __INT64_TYPE__ == 6)
#define __INT64 "l"
#elif (__INT64_TYPE__ == 8 || __INT64_TYPE__ == 10)
#define __INT64 "ll"
#endif
#if (__INT_FAST8_TYPE__ == 0)
#define __FAST8 "hh"
#elif (__INT_FAST8_TYPE__ == 1 || __INT_FAST8_TYPE__ == 3)
#define __FAST8 "h"
#elif (__INT_FAST8_TYPE__ == 2)
#define __FAST8
#elif (__INT_FAST8_TYPE__ == 4 || __INT_FAST8_TYPE__ == 6)
#define __FAST8 "l"
#elif (__INT_FAST8_TYPE__ == 8 || __INT_FAST8_TYPE__ == 10)
#define __FAST8 "ll"
#endif
#if (__INT_FAST16_TYPE__ == 1 || __INT_FAST16_TYPE__ == 3)
#define __FAST16 "h"
#elif (__INT_FAST16_TYPE__ == 2)
#define __FAST16
#elif (__INT_FAST16_TYPE__ == 4 || __INT_FAST16_TYPE__ == 6)
#define __FAST16 "l"
#elif (__INT_FAST16_TYPE__ == 8 || __INT_FAST16_TYPE__ == 10)
#define __FAST16 "ll"
#endif
#if (__INT_FAST32_TYPE__ == 2)
#define __FAST32
#elif (__INT_FAST32_TYPE__ == 4 || __INT_FAST32_TYPE__ == 6)
#define __FAST32 "l"
#elif (__INT_FAST32_TYPE__ == 8 || __INT_FAST32_TYPE__ == 10)
#define __FAST32 "ll"
#endif
#if (__INT_FAST64_TYPE__ == 2)
#define __FAST64
#elif (__INT_FAST64_TYPE__ == 4 || __INT_FAST64_TYPE__ == 6)
#define __FAST64 "l"
#elif (__INT_FAST64_TYPE__ == 8 || __INT_FAST64_TYPE__ == 10)
#define __FAST64 "ll"
#endif
#if (__INT_LEAST8_TYPE__ == 0)
#define __LEAST8 "hh"
#elif (__INT_LEAST8_TYPE__ == 1 || __INT_LEAST8_TYPE__ == 3)
#define __LEAST8 "h"
#elif (__INT_LEAST8_TYPE__ == 2)
#define __LEAST8
#elif (__INT_LEAST8_TYPE__ == 4 || __INT_LEAST8_TYPE__ == 6)
#define __LEAST8 "l"
#elif (__INT_LEAST8_TYPE__ == 8 || __INT_LEAST8_TYPE__ == 10)
#define __LEAST8 "ll"
#endif
#if (__INT_LEAST16_TYPE__ == 1 || __INT_LEAST16_TYPE__ == 3)
#define __LEAST16 "h"
#elif (__INT_LEAST16_TYPE__ == 2)
#define __LEAST16
#elif (__INT_LEAST16_TYPE__ == 4 || __INT_LEAST16_TYPE__ == 6)
#define __LEAST16 "l"
#elif (__INT_LEAST16_TYPE__ == 8 || __INT_LEAST16_TYPE__ == 10)
#define __LEAST16 "ll"
#endif
#if (__INT_LEAST32_TYPE__ == 2)
#define __LEAST32
#elif (__INT_LEAST32_TYPE__ == 4 || __INT_LEAST32_TYPE__ == 6)
#define __LEAST32 "l"
#elif (__INT_LEAST32_TYPE__ == 8 || __INT_LEAST32_TYPE__ == 10)
#define __LEAST32 "ll"
#endif
#if (__INT_LEAST64_TYPE__ == 2)
#define __LEAST64
#elif (__INT_LEAST64_TYPE__ == 4 || __INT_LEAST64_TYPE__ == 6)
#define __LEAST64 "l"
#elif (__INT_LEAST64_TYPE__ == 8 || __INT_LEAST64_TYPE__ == 10)
#define __LEAST64 "ll"
#endif
#undef signed
#undef unsigned
#undef char
#undef short
#undef int
#undef long
#pragma pop_macro("signed")
#pragma pop_macro("unsigned")
#pragma pop_macro("char")
#pragma pop_macro("short")
#pragma pop_macro("__int20")
#pragma pop_macro("int")
#pragma pop_macro("long")
#endif /* _SYS__INTSUP_H */
#ifdef __cplusplus
}
#endif
#endif /* DOXYGEN */
#endif /* SYS__INTSUP_H */

View File

@ -0,0 +1,48 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266
* @{
*
* @file
* @brief Implementation of required system calls
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
#ifndef SYSCALLS_H
#define SYSCALLS_H
#include <stdarg.h>
#include <stdbool.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
/** Necessary initializations of system call functions */
extern void syscalls_init (void);
/** System standard printf function */
extern int printf(const char* format, ...);
/** System standard puts function */
extern int puts(const char * str);
/** Determine free heap size */
extern unsigned int get_free_heap_size (void);
#ifdef __cplusplus
}
#endif
#endif /* SYSCALLS_H */

View File

@ -0,0 +1,39 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266
* @{
*
* @file
* @brief Implementation of the kernel's architecture dependent thread interface
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
#ifndef THREAD_ARCH_H
#define THREAD_ARCH_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Initializes the ISR stack
* Initializes the ISR stack with predefined values (address value) to be able to
* measure used ISR stack size.
*/
extern void thread_isr_stack_init(void);
#ifdef __cplusplus
}
#endif
#endif /* THREAD_ARCH_H */

View File

@ -0,0 +1,43 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266
* @{
*
* @file
* @brief Implementation of some tools
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
#ifndef TOOLS_H
#define TOOLS_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Hexdump of memory
* @param[in] addr start address in memory
* @param[in] num number of items to dump
* @param[in] width format of the items
* @param[in] per_line number of items per line
*/
extern void esp_hexdump (const void* addr, uint32_t num, char width, uint8_t per_line);
#ifdef __cplusplus
}
#endif
#endif /* TOOLS_H */

View File

@ -0,0 +1,22 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
#ifndef USER_CONFIG_H
#define USER_CONFIG_H
#ifdef __cplusplus
extern "C" {
#endif
/* This file is just to satisfy SDK */
#ifdef __cplusplus
}
#endif
#endif /* USER_CONFIG_H */

View File

@ -0,0 +1,41 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266
* @{
*
* @file
* @brief Xtensa ASM code specific configuration options
*
* @author Gunar Schorcht <gunar@schorcht.net>
*/
#ifndef XTENSA_CONF_H
#define XTENSA_CONF_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Xtensa ASM code specific default stack sizes
* @{
*/
#if defined(MODULE_ESP_SDK_INT_HANDLING)
#define ISR_STACKSIZE (8)
#else
#define ISR_STACKSIZE (2048)
#endif
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* XTENSA_CONF_H */
/** @} */

87
cpu/esp8266/irq_arch.c Normal file
View File

@ -0,0 +1,87 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266
* @{
*
* @file
* @brief Implementation of the kernels irq interface
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
#define ENABLE_DEBUG 0
#include "debug.h"
#include <stdint.h>
#include <stdio.h>
#include "irq.h"
#include "cpu.h"
#include "common.h"
#include "esp/common_macros.h"
#include "esp/xtensa_ops.h"
#include "sdk/ets.h"
#include "xtensa/xtensa_context.h"
/**
* @brief Set on entry into and reset on exit from an ISR
*/
volatile uint32_t irq_interrupt_nesting = 0;
/**
* @brief Disable all maskable interrupts
*/
unsigned int IRAM irq_disable(void)
{
uint32_t _saved_interrupt_level;
/* read and set interrupt level (RSIL) */
__asm__ volatile ("rsil %0, " XTSTR(XCHAL_NUM_INTLEVELS+1) : "=a" (_saved_interrupt_level));
DEBUG ("%s %02x(%02x)\n", __func__,
(_saved_interrupt_level & 0xfffffff0) | (XCHAL_NUM_INTLEVELS+1),
_saved_interrupt_level);
return _saved_interrupt_level;
}
/**
* @brief Enable all maskable interrupts
*/
unsigned int IRAM irq_enable(void)
{
uint32_t _saved_interrupt_level;
/* read and set interrupt level (RSIL) */
__asm__ volatile ("rsil %0, 0" : "=a" (_saved_interrupt_level));
DEBUG ("%s %02x(%02x)\n", __func__,
_saved_interrupt_level & 0xfffffff0, _saved_interrupt_level);
return _saved_interrupt_level;
}
/**
* @brief Restore the state of the IRQ flags
*/
void IRAM irq_restore(unsigned int state)
{
/* write interrupt level and sync */
DEBUG ("%s %02x\n", __func__, state);
__asm__ volatile ("wsr %0, ps; rsync" :: "a" (state));
}
/**
* @brief See if the current context is inside an ISR
*/
int IRAM irq_is_in(void)
{
DEBUG("irq_interrupt_nesting = %d\n", irq_interrupt_nesting);
return irq_interrupt_nesting;
}

View File

@ -0,0 +1,350 @@
PROVIDE ( Cache_Read_Disable = 0x400047f0 );
PROVIDE ( Cache_Read_Enable = 0x40004678 );
PROVIDE ( FilePacketSendReqMsgProc = 0x400035a0 );
PROVIDE ( FlashDwnLdParamCfgMsgProc = 0x4000368c );
PROVIDE ( FlashDwnLdStartMsgProc = 0x40003538 );
PROVIDE ( FlashDwnLdStopReqMsgProc = 0x40003658 );
PROVIDE ( GetUartDevice = 0x40003f4c );
PROVIDE ( MD5Final = 0x40009900 );
PROVIDE ( MD5Init = 0x40009818 );
PROVIDE ( MD5Update = 0x40009834 );
PROVIDE ( MemDwnLdStartMsgProc = 0x400036c4 );
PROVIDE ( MemDwnLdStopReqMsgProc = 0x4000377c );
PROVIDE ( MemPacketSendReqMsgProc = 0x400036f0 );
PROVIDE ( RcvMsg = 0x40003eac );
PROVIDE ( SHA1Final = 0x4000b648 );
PROVIDE ( SHA1Init = 0x4000b584 );
PROVIDE ( SHA1Transform = 0x4000a364 );
PROVIDE ( SHA1Update = 0x4000b5a8 );
PROVIDE ( SPI_read_status = 0x400043c8 );
PROVIDE ( SPI_write_status = 0x40004400 );
PROVIDE ( SPI_write_enable = 0x4000443c );
PROVIDE ( Wait_SPI_Idle = 0x4000448c );
PROVIDE ( Enable_QMode = 0x400044c0 );
PROVIDE ( SPIEraseArea = 0x40004b44 );
PROVIDE ( SPIEraseBlock = 0x400049b4 );
PROVIDE ( SPIEraseChip = 0x40004984 );
PROVIDE ( SPIEraseSector = 0x40004a00 );
PROVIDE ( SPILock = 0x400048a8 );
PROVIDE ( SPIParamCfg = 0x40004c2c );
PROVIDE ( SPIRead = 0x40004b1c );
PROVIDE ( SPIReadModeCnfig = 0x400048ec );
PROVIDE ( SPIUnlock = 0x40004878 );
PROVIDE ( SPIWrite = 0x40004a4c );
PROVIDE ( SelectSpiFunction = 0x40003f58 );
PROVIDE ( SendMsg = 0x40003cf4 );
PROVIDE ( UartConnCheck = 0x40003230 );
PROVIDE ( UartConnectProc = 0x400037a0 );
PROVIDE ( UartDwnLdProc = 0x40003368 );
PROVIDE ( UartGetCmdLn = 0x40003ef4 );
PROVIDE ( UartRegReadProc = 0x4000381c );
PROVIDE ( UartRegWriteProc = 0x400037ac );
PROVIDE ( UartRxString = 0x40003c30 );
PROVIDE ( Uart_Init = 0x40003a14 );
PROVIDE ( _DebugExceptionVector = 0x40000010 );
PROVIDE ( _DoubleExceptionVector = 0x40000070 );
PROVIDE ( _KernelExceptionVector = 0x40000030 );
PROVIDE ( _NMIExceptionVector = 0x40000020 );
PROVIDE ( _ResetHandler = 0x400000a4 );
PROVIDE ( _ResetVector = 0x40000080 );
PROVIDE ( _UserExceptionVector = 0x40000050 );
PROVIDE ( __adddf3 = 0x4000c538 );
PROVIDE ( __addsf3 = 0x4000c180 );
PROVIDE ( __divdf3 = 0x4000cb94 );
PROVIDE ( __divdi3 = 0x4000ce60 );
PROVIDE ( __divsi3 = 0x4000dc88 );
PROVIDE ( __extendsfdf2 = 0x4000cdfc );
PROVIDE ( __fixdfsi = 0x4000ccb8 );
PROVIDE ( __fixunsdfsi = 0x4000cd00 );
PROVIDE ( __fixunssfsi = 0x4000c4c4 );
PROVIDE ( __floatsidf = 0x4000e2f0 );
PROVIDE ( __floatsisf = 0x4000e2ac );
PROVIDE ( __floatunsidf = 0x4000e2e8 );
PROVIDE ( __floatunsisf = 0x4000e2a4 );
PROVIDE ( __muldf3 = 0x4000c8f0 );
PROVIDE ( __muldi3 = 0x40000650 );
PROVIDE ( __mulsf3 = 0x4000c3dc );
PROVIDE ( __subdf3 = 0x4000c688 );
PROVIDE ( __subsf3 = 0x4000c268 );
PROVIDE ( __truncdfsf2 = 0x4000cd5c );
PROVIDE ( __udivdi3 = 0x4000d310 );
PROVIDE ( __udivsi3 = 0x4000e21c );
PROVIDE ( __umoddi3 = 0x4000d770 );
PROVIDE ( __umodsi3 = 0x4000e268 );
PROVIDE ( __umulsidi3 = 0x4000dcf0 );
PROVIDE ( _rom_store = 0x4000e388 );
PROVIDE ( _rom_store_table = 0x4000e328 );
PROVIDE ( _start = 0x4000042c );
PROVIDE ( _xtos_alloca_handler = 0x4000dbe0 );
PROVIDE ( _xtos_c_wrapper_handler = 0x40000598 );
PROVIDE ( _xtos_cause3_handler = 0x40000590 );
PROVIDE ( _xtos_ints_off = 0x4000bda4 );
PROVIDE ( _xtos_ints_on = 0x4000bd84 );
PROVIDE ( _xtos_l1int_handler = 0x4000048c );
PROVIDE ( _xtos_p_none = 0x4000dbf8 );
PROVIDE ( _xtos_restore_intlevel = 0x4000056c );
PROVIDE ( _xtos_return_from_exc = 0x4000dc54 );
PROVIDE ( _xtos_set_exception_handler = 0x40000454 );
PROVIDE ( _xtos_set_interrupt_handler = 0x4000bd70 );
PROVIDE ( _xtos_set_interrupt_handler_arg = 0x4000bd28 );
PROVIDE ( _xtos_set_intlevel = 0x4000dbfc );
PROVIDE ( _xtos_set_min_intlevel = 0x4000dc18 );
PROVIDE ( _xtos_set_vpri = 0x40000574 );
PROVIDE ( _xtos_syscall_handler = 0x4000dbe4 );
PROVIDE ( _xtos_unhandled_exception = 0x4000dc44 );
PROVIDE ( _xtos_unhandled_interrupt = 0x4000dc3c );
PROVIDE ( aes_decrypt = 0x400092d4 );
PROVIDE ( aes_decrypt_deinit = 0x400092e4 );
PROVIDE ( aes_decrypt_init = 0x40008ea4 );
PROVIDE ( aes_unwrap = 0x40009410 );
PROVIDE ( base64_decode = 0x40009648 );
PROVIDE ( base64_encode = 0x400094fc );
PROVIDE ( bzero = 0x4000de84 );
PROVIDE ( cmd_parse = 0x40000814 );
PROVIDE ( conv_str_decimal = 0x40000b24 );
PROVIDE ( conv_str_hex = 0x40000cb8 );
PROVIDE ( convert_para_str = 0x40000a60 );
PROVIDE ( dtm_get_intr_mask = 0x400026d0 );
PROVIDE ( dtm_params_init = 0x4000269c );
PROVIDE ( dtm_set_intr_mask = 0x400026c8 );
PROVIDE ( dtm_set_params = 0x400026dc );
PROVIDE ( eprintf = 0x40001d14 );
PROVIDE ( eprintf_init_buf = 0x40001cb8 );
PROVIDE ( eprintf_to_host = 0x40001d48 );
PROVIDE ( est_get_printf_buf_remain_len = 0x40002494 );
PROVIDE ( est_reset_printf_buf_len = 0x4000249c );
PROVIDE ( ets_bzero = 0x40002ae8 );
PROVIDE ( ets_char2xdigit = 0x40002b74 );
PROVIDE ( ets_delay_us = 0x40002ecc );
PROVIDE ( ets_enter_sleep = 0x400027b8 );
PROVIDE ( ets_external_printf = 0x40002578 );
PROVIDE ( ets_get_cpu_frequency = 0x40002f0c );
PROVIDE ( ets_getc = 0x40002bcc );
PROVIDE ( ets_install_external_printf = 0x40002450 );
PROVIDE ( ets_install_putc1 = 0x4000242c );
PROVIDE ( ets_install_putc2 = 0x4000248c );
PROVIDE ( ets_install_uart_printf = 0x40002438 );
PROVIDE ( ets_intr_lock = 0x40000f74 );
PROVIDE ( ets_intr_unlock = 0x40000f80 );
PROVIDE ( ets_isr_attach = 0x40000f88 );
PROVIDE ( ets_isr_mask = 0x40000f98 );
PROVIDE ( ets_isr_unmask = 0x40000fa8 );
PROVIDE ( ets_memcmp = 0x400018d4 );
PROVIDE ( ets_memcpy = 0x400018b4 );
PROVIDE ( ets_memmove = 0x400018c4 );
PROVIDE ( ets_memset = 0x400018a4 );
PROVIDE ( ets_post = 0x40000e24 );
PROVIDE ( ets_printf = 0x400024cc );
PROVIDE ( ets_putc = 0x40002be8 );
PROVIDE ( ets_rtc_int_register = 0x40002a40 );
PROVIDE ( ets_run = 0x40000e04 );
PROVIDE ( ets_set_idle_cb = 0x40000dc0 );
PROVIDE ( ets_set_user_start = 0x40000fbc );
PROVIDE ( ets_str2macaddr = 0x40002af8 );
PROVIDE ( ets_strcmp = 0x40002aa8 );
PROVIDE ( ets_strcpy = 0x40002a88 );
PROVIDE ( ets_strlen = 0x40002ac8 );
PROVIDE ( ets_strncmp = 0x40002ab8 );
PROVIDE ( ets_strncpy = 0x40002a98 );
PROVIDE ( ets_strstr = 0x40002ad8 );
PROVIDE ( ets_task = 0x40000dd0 );
PROVIDE ( ets_timer_arm = 0x40002cc4 );
PROVIDE ( ets_timer_disarm = 0x40002d40 );
PROVIDE ( ets_timer_done = 0x40002d80 );
PROVIDE ( ets_timer_handler_isr = 0x40002da8 );
PROVIDE ( ets_timer_init = 0x40002e68 );
PROVIDE ( ets_timer_setfn = 0x40002c48 );
PROVIDE ( ets_uart_printf = 0x40002544 );
PROVIDE ( ets_update_cpu_frequency = 0x40002f04 );
PROVIDE ( ets_vprintf = 0x40001f00 );
PROVIDE ( ets_wdt_disable = 0x400030f0 );
PROVIDE ( ets_wdt_enable = 0x40002fa0 );
PROVIDE ( ets_wdt_get_mode = 0x40002f34 );
PROVIDE ( ets_wdt_init = 0x40003170 );
PROVIDE ( ets_wdt_restore = 0x40003158 );
PROVIDE ( ets_write_char = 0x40001da0 );
PROVIDE ( get_first_seg = 0x4000091c );
PROVIDE ( gpio_init = 0x40004c50 );
PROVIDE ( gpio_input_get = 0x40004cf0 );
PROVIDE ( gpio_intr_ack = 0x40004dcc );
PROVIDE ( gpio_intr_handler_register = 0x40004e28 );
PROVIDE ( gpio_intr_pending = 0x40004d88 );
PROVIDE ( gpio_intr_test = 0x40004efc );
PROVIDE ( gpio_output_set = 0x40004cd0 );
PROVIDE ( gpio_pin_intr_state_set = 0x40004d90 );
PROVIDE ( gpio_pin_wakeup_disable = 0x40004ed4 );
PROVIDE ( gpio_pin_wakeup_enable = 0x40004e90 );
PROVIDE ( gpio_register_get = 0x40004d5c );
PROVIDE ( gpio_register_set = 0x40004d04 );
PROVIDE ( hmac_md5 = 0x4000a2cc );
PROVIDE ( hmac_md5_vector = 0x4000a160 );
PROVIDE ( hmac_sha1 = 0x4000ba28 );
PROVIDE ( hmac_sha1_vector = 0x4000b8b4 );
PROVIDE ( lldesc_build_chain = 0x40004f40 );
PROVIDE ( lldesc_num2link = 0x40005050 );
PROVIDE ( lldesc_set_owner = 0x4000507c );
PROVIDE ( main = 0x40000fec );
PROVIDE ( md5_vector = 0x400097ac );
PROVIDE ( mem_calloc = 0x40001c2c );
PROVIDE ( mem_free = 0x400019e0 );
PROVIDE ( mem_init = 0x40001998 );
PROVIDE ( mem_malloc = 0x40001b40 );
PROVIDE ( mem_realloc = 0x40001c6c );
PROVIDE ( mem_trim = 0x40001a14 );
PROVIDE ( mem_zalloc = 0x40001c58 );
PROVIDE ( memcmp = 0x4000dea8 );
PROVIDE ( memcpy = 0x4000df48 );
PROVIDE ( memmove = 0x4000e04c );
PROVIDE ( memset = 0x4000e190 );
PROVIDE ( multofup = 0x400031c0 );
PROVIDE ( pbkdf2_sha1 = 0x4000b840 );
PROVIDE ( phy_get_romfuncs = 0x40006b08 );
PROVIDE ( rand = 0x40000600 );
PROVIDE ( rc4_skip = 0x4000dd68 );
PROVIDE ( recv_packet = 0x40003d08 );
PROVIDE ( remove_head_space = 0x40000a04 );
PROVIDE ( rijndaelKeySetupDec = 0x40008dd0 );
PROVIDE ( rijndaelKeySetupEnc = 0x40009300 );
PROVIDE ( rom_abs_temp = 0x400060c0 );
PROVIDE ( rom_ana_inf_gating_en = 0x40006b10 );
PROVIDE ( rom_cal_tos_v50 = 0x40007a28 );
PROVIDE ( rom_chip_50_set_channel = 0x40006f84 );
PROVIDE ( rom_chip_v5_disable_cca = 0x400060d0 );
PROVIDE ( rom_chip_v5_enable_cca = 0x400060ec );
PROVIDE ( rom_chip_v5_rx_init = 0x4000711c );
PROVIDE ( rom_chip_v5_sense_backoff = 0x4000610c );
PROVIDE ( rom_chip_v5_tx_init = 0x4000718c );
PROVIDE ( rom_dc_iq_est = 0x4000615c );
PROVIDE ( rom_en_pwdet = 0x400061b8 );
PROVIDE ( rom_get_bb_atten = 0x40006238 );
PROVIDE ( rom_get_corr_power = 0x40006260 );
PROVIDE ( rom_get_fm_sar_dout = 0x400062dc );
PROVIDE ( rom_get_noisefloor = 0x40006394 );
PROVIDE ( rom_get_power_db = 0x400063b0 );
PROVIDE ( rom_i2c_readReg = 0x40007268 );
PROVIDE ( rom_i2c_readReg_Mask = 0x4000729c );
PROVIDE ( rom_i2c_writeReg = 0x400072d8 );
PROVIDE ( rom_i2c_writeReg_Mask = 0x4000730c );
PROVIDE ( rom_iq_est_disable = 0x40006400 );
PROVIDE ( rom_iq_est_enable = 0x40006430 );
PROVIDE ( rom_linear_to_db = 0x40006484 );
PROVIDE ( rom_mhz2ieee = 0x400065a4 );
PROVIDE ( rom_pbus_dco___SA2 = 0x40007bf0 );
PROVIDE ( rom_pbus_debugmode = 0x4000737c );
PROVIDE ( rom_pbus_enter_debugmode = 0x40007410 );
PROVIDE ( rom_pbus_exit_debugmode = 0x40007448 );
PROVIDE ( rom_pbus_force_test = 0x4000747c );
PROVIDE ( rom_pbus_rd = 0x400074d8 );
PROVIDE ( rom_pbus_set_rxgain = 0x4000754c );
PROVIDE ( rom_pbus_set_txgain = 0x40007610 );
PROVIDE ( rom_pbus_workmode = 0x40007648 );
PROVIDE ( rom_pbus_xpd_rx_off = 0x40007688 );
PROVIDE ( rom_pbus_xpd_rx_on = 0x400076cc );
PROVIDE ( rom_pbus_xpd_tx_off = 0x400076fc );
PROVIDE ( rom_pbus_xpd_tx_on = 0x40007740 );
PROVIDE ( rom_pbus_xpd_tx_on__low_gain = 0x400077a0 );
PROVIDE ( rom_phy_reset_req = 0x40007804 );
PROVIDE ( rom_restart_cal = 0x4000781c );
PROVIDE ( rom_rfcal_pwrctrl = 0x40007eb4 );
PROVIDE ( rom_rfcal_rxiq = 0x4000804c );
PROVIDE ( rom_rfcal_rxiq_set_reg = 0x40008264 );
PROVIDE ( rom_rfcal_txcap = 0x40008388 );
PROVIDE ( rom_rfcal_txiq = 0x40008610 );
PROVIDE ( rom_rfcal_txiq_cover = 0x400088b8 );
PROVIDE ( rom_rfcal_txiq_set_reg = 0x40008a70 );
PROVIDE ( rom_rfpll_reset = 0x40007868 );
PROVIDE ( rom_rfpll_set_freq = 0x40007968 );
PROVIDE ( rom_rxiq_cover_mg_mp = 0x40008b6c );
PROVIDE ( rom_rxiq_get_mis = 0x40006628 );
PROVIDE ( rom_sar_init = 0x40006738 );
PROVIDE ( rom_set_ana_inf_tx_scale = 0x4000678c );
PROVIDE ( rom_set_channel_freq = 0x40006c50 );
PROVIDE ( rom_set_loopback_gain = 0x400067c8 );
PROVIDE ( rom_set_noise_floor = 0x40006830 );
PROVIDE ( rom_set_rxclk_en = 0x40006550 );
PROVIDE ( rom_set_txbb_atten = 0x40008c6c );
PROVIDE ( rom_set_txclk_en = 0x4000650c );
PROVIDE ( rom_set_txiq_cal = 0x40008d34 );
PROVIDE ( rom_start_noisefloor = 0x40006874 );
PROVIDE ( rom_start_tx_tone = 0x400068b4 );
PROVIDE ( rom_stop_tx_tone = 0x4000698c );
PROVIDE ( rom_tx_mac_disable = 0x40006a98 );
PROVIDE ( rom_tx_mac_enable = 0x40006ad4 );
PROVIDE ( rom_txtone_linear_pwr = 0x40006a1c );
PROVIDE ( rom_write_rfpll_sdm = 0x400078dc );
PROVIDE ( roundup2 = 0x400031b4 );
PROVIDE ( rtc_enter_sleep = 0x40002870 );
PROVIDE ( rtc_get_reset_reason = 0x400025e0 );
PROVIDE ( rtc_intr_handler = 0x400029ec );
PROVIDE ( rtc_set_sleep_mode = 0x40002668 );
PROVIDE ( save_rxbcn_mactime = 0x400027a4 );
PROVIDE ( save_tsf_us = 0x400027ac );
PROVIDE ( send_packet = 0x40003c80 );
PROVIDE ( sha1_prf = 0x4000ba48 );
PROVIDE ( sha1_vector = 0x4000a2ec );
PROVIDE ( sip_alloc_to_host_evt = 0x40005180 );
PROVIDE ( sip_get_ptr = 0x400058a8 );
PROVIDE ( sip_get_state = 0x40005668 );
PROVIDE ( sip_init_attach = 0x4000567c );
PROVIDE ( sip_install_rx_ctrl_cb = 0x4000544c );
PROVIDE ( sip_install_rx_data_cb = 0x4000545c );
PROVIDE ( sip_post = 0x400050fc );
PROVIDE ( sip_post_init = 0x400056c4 );
PROVIDE ( sip_reclaim_from_host_cmd = 0x4000534c );
PROVIDE ( sip_reclaim_tx_data_pkt = 0x400052c0 );
PROVIDE ( sip_send = 0x40005808 );
PROVIDE ( sip_to_host_chain_append = 0x40005864 );
PROVIDE ( sip_to_host_evt_send_done = 0x40005234 );
PROVIDE ( slc_add_credits = 0x400060ac );
PROVIDE ( slc_enable = 0x40005d90 );
PROVIDE ( slc_from_host_chain_fetch = 0x40005f24 );
PROVIDE ( slc_from_host_chain_recycle = 0x40005e94 );
PROVIDE ( slc_init_attach = 0x40005c50 );
PROVIDE ( slc_init_credit = 0x4000608c );
PROVIDE ( slc_pause_from_host = 0x40006014 );
PROVIDE ( slc_reattach = 0x40005c1c );
PROVIDE ( slc_resume_from_host = 0x4000603c );
PROVIDE ( slc_select_tohost_gpio = 0x40005dc0 );
PROVIDE ( slc_select_tohost_gpio_mode = 0x40005db8 );
PROVIDE ( slc_send_to_host_chain = 0x40005de4 );
PROVIDE ( slc_set_host_io_max_window = 0x40006068 );
PROVIDE ( slc_to_host_chain_recycle = 0x40005f10 );
PROVIDE ( software_reset = 0x4000264c );
PROVIDE ( spi_flash_attach = 0x40004644 );
PROVIDE ( srand = 0x400005f0 );
PROVIDE ( strcmp = 0x4000bdc8 );
PROVIDE ( strcpy = 0x4000bec8 );
PROVIDE ( strlen = 0x4000bf4c );
PROVIDE ( strncmp = 0x4000bfa8 );
PROVIDE ( strncpy = 0x4000c0a0 );
PROVIDE ( strstr = 0x4000e1e0 );
PROVIDE ( timer_insert = 0x40002c64 );
PROVIDE ( uartAttach = 0x4000383c );
PROVIDE ( uart_baudrate_detect = 0x40003924 );
PROVIDE ( uart_buff_switch = 0x400038a4 );
PROVIDE ( uart_div_modify = 0x400039d8 );
PROVIDE ( uart_rx_intr_handler = 0x40003bbc );
PROVIDE ( uart_rx_one_char = 0x40003b8c );
PROVIDE ( uart_rx_one_char_block = 0x40003b64 );
PROVIDE ( uart_rx_readbuff = 0x40003ec8 );
PROVIDE ( uart_tx_one_char = 0x40003b30 );
PROVIDE ( wepkey_128 = 0x4000bc40 );
PROVIDE ( wepkey_64 = 0x4000bb3c );
PROVIDE ( xthal_bcopy = 0x40000688 );
PROVIDE ( xthal_copy123 = 0x4000074c );
PROVIDE ( xthal_get_ccompare = 0x4000dd4c );
PROVIDE ( xthal_get_ccount = 0x4000dd38 );
PROVIDE ( xthal_get_interrupt = 0x4000dd58 );
PROVIDE ( xthal_get_intread = 0x4000dd58 );
PROVIDE ( xthal_memcpy = 0x400006c4 );
PROVIDE ( xthal_set_ccompare = 0x4000dd40 );
PROVIDE ( xthal_set_intclear = 0x4000dd60 );
PROVIDE ( xthal_spill_registers_into_stack_nw = 0x4000e320 );
PROVIDE ( xthal_window_spill = 0x4000e324 );
PROVIDE ( xthal_window_spill_nw = 0x4000e320 );
PROVIDE ( Te0 = 0x3fffccf0 );
PROVIDE ( Td0 = 0x3fffd100 );
PROVIDE ( Td4s = 0x3fffd500);
PROVIDE ( rcons = 0x3fffd0f0);
PROVIDE ( UartDev = 0x3fffde10 );

View File

@ -0,0 +1,295 @@
/**
* This linker script is a modified version of eagle.app.v6.ld that
* was generated from xt-genldscripts.tpp for LSP and shipped with
* ESP8266_NONOS_SDK
*/
/* Linker Script for ld -N */
MEMORY
{
dport0_0_seg : org = 0x3FF00000, len = 0x10
dram0_0_seg : org = 0x3FFE8000, len = 0x14000
iram1_0_seg : org = 0x40100000, len = 0x8000
irom0_0_seg : org = 0x40210000, len = 0x5C000
}
PHDRS
{
dport0_0_phdr PT_LOAD;
dram0_0_phdr PT_LOAD;
dram0_0_bss_phdr PT_LOAD;
iram1_0_phdr PT_LOAD;
irom0_0_phdr PT_LOAD;
}
/* Default entry point: */
ENTRY(_call_user_start)
EXTERN(_DebugExceptionVector)
EXTERN(_DoubleExceptionVector)
EXTERN(_KernelExceptionVector)
EXTERN(_NMIExceptionVector)
EXTERN(_UserExceptionVector)
PROVIDE(_memmap_vecbase_reset = 0x40000000);
/* Various memory-map dependent cache attribute settings: */
_memmap_cacheattr_wb_base = 0x00000110;
_memmap_cacheattr_wt_base = 0x00000110;
_memmap_cacheattr_bp_base = 0x00000220;
_memmap_cacheattr_unused_mask = 0xFFFFF00F;
_memmap_cacheattr_wb_trapnull = 0x2222211F;
_memmap_cacheattr_wba_trapnull = 0x2222211F;
_memmap_cacheattr_wbna_trapnull = 0x2222211F;
_memmap_cacheattr_wt_trapnull = 0x2222211F;
_memmap_cacheattr_bp_trapnull = 0x2222222F;
_memmap_cacheattr_wb_strict = 0xFFFFF11F;
_memmap_cacheattr_wt_strict = 0xFFFFF11F;
_memmap_cacheattr_bp_strict = 0xFFFFF22F;
_memmap_cacheattr_wb_allvalid = 0x22222112;
_memmap_cacheattr_wt_allvalid = 0x22222112;
_memmap_cacheattr_bp_allvalid = 0x22222222;
PROVIDE(_memmap_cacheattr_reset = _memmap_cacheattr_wb_trapnull);
/* ROM variables */
/* source: disassembly of boot rom at https://github.com/trebisky/esp8266 */
PROVIDE( ets_task_min_prio = 0x3fffc6fc );
PROVIDE( ets_idle_cb = 0x3fffdab0 );
PROVIDE( ets_idle_arg = 0x3fffdab4 );
PROVIDE( ets_task_exec_mask = 0x3fffdab8 );
PROVIDE( ets_task_tab = 0x3fffdac0 );
PROVIDE( flashchip = 0x3fffc714 );
PROVIDE( sdk_flashchip = 0x3fffc718 );
PROVIDE( ets_phy_mactime = 0x3ff20a00 );
SECTIONS
{
.dport0.rodata : ALIGN(4)
{
_dport0_rodata_start = ABSOLUTE(.);
*(.dport0.rodata)
*(.dport.rodata)
_dport0_rodata_end = ABSOLUTE(.);
} >dport0_0_seg :dport0_0_phdr
.dport0.literal : ALIGN(4)
{
_dport0_literal_start = ABSOLUTE(.);
*(.dport0.literal)
*(.dport.literal)
_dport0_literal_end = ABSOLUTE(.);
} >dport0_0_seg :dport0_0_phdr
.dport0.data : ALIGN(4)
{
_dport0_data_start = ABSOLUTE(.);
*(.dport0.data)
*(.dport.data)
_dport0_data_end = ABSOLUTE(.);
} >dport0_0_seg :dport0_0_phdr
.data : ALIGN(4)
{
_data_start = ABSOLUTE(.);
*(.data)
*(.data.*)
*(.gnu.linkonce.d.*)
*(.data1)
*(.sdata)
*(.sdata.*)
*(.gnu.linkonce.s.*)
*(.sdata2)
*(.sdata2.*)
*(.gnu.linkonce.s2.*)
*(.jcr)
_data_end = ABSOLUTE(.);
} >dram0_0_seg :dram0_0_phdr
.rodata : ALIGN(4)
{
_rodata_start = ABSOLUTE(.);
*(.sdk.version)
/* TODO put only necessary .rodata to dram
*libc.a:*.o(.rodata.* .rodata)
*core.a:*(.rodata.* .rodata)
*cpu.a:*(.rodata .rodata.*)
*/
*(.rodata .rodata.*)
*(.gnu.linkonce.r.*)
*(.rodata1)
__XT_EXCEPTION_TABLE__ = ABSOLUTE(.);
*(.xt_except_table)
*(.gcc_except_table)
*(.gnu.linkonce.e.*)
*(.gnu.version_r)
*(.eh_frame)
/* C++ constructor and destructor tables, properly ordered: */
KEEP (*crtbegin.o(.ctors))
KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
KEEP (*crtbegin.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
/* C++ exception handlers table: */
__XT_EXCEPTION_DESCS__ = ABSOLUTE(.);
*(.xt_except_desc)
*(.gnu.linkonce.h.*)
__XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.);
*(.xt_except_desc_end)
*(.dynamic)
*(.gnu.version_d)
. = ALIGN(4); /* this table MUST be 4-byte aligned */
_bss_table_start = ABSOLUTE(.);
LONG(_bss_start)
LONG(_bss_end)
_bss_table_end = ABSOLUTE(.);
_rodata_end = ABSOLUTE(.);
} >dram0_0_seg :dram0_0_phdr
.bss ALIGN(8) (NOLOAD) : ALIGN(4)
{
. = ALIGN (8);
_bss_start = ABSOLUTE(.);
*(.dynsbss)
*(.sbss)
*(.sbss.*)
*(.gnu.linkonce.sb.*)
*(.scommon)
*(.sbss2)
*(.sbss2.*)
*(.gnu.linkonce.sb2.*)
*(.dynbss)
*(.bss)
*(.bss.*)
*(.gnu.linkonce.b.*)
*(COMMON)
. = ALIGN (8);
_bss_end = ABSOLUTE(.);
_sheap = ABSOLUTE(.);
_heap_start = ABSOLUTE(.);
} >dram0_0_seg :dram0_0_bss_phdr
/* ETS system memory starts at 0x3FFFC000 which is the top of the heap for the app */
. = 0x3FFFC000;
_heap_top = ABSOLUTE(.);
_eheap = ABSOLUTE(.);
.text : ALIGN(4)
{
_stext = .;
_text_start = ABSOLUTE(.);
*(.UserEnter.text)
. = ALIGN(16);
*(.DebugExceptionVector.text)
. = ALIGN(16);
*(.NMIExceptionVector.text)
. = ALIGN(16);
*(.KernelExceptionVector.text)
LONG(0)
LONG(0)
LONG(0)
LONG(0)
. = ALIGN(16);
*(.UserExceptionVector.text)
LONG(0)
LONG(0)
LONG(0)
LONG(0)
. = ALIGN(16);
*(.DoubleExceptionVector.text)
LONG(0)
LONG(0)
LONG(0)
LONG(0)
. = ALIGN (16);
*(.entry.text)
*(.init.literal)
*(.init)
/* normal code should be in irom0 */
/*
*(.literal .text .literal.* .text.* .stub)
*(.gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*)
*/
/* RIOT-OS compiled source files that use the .iram1.* section names for IRAM
functions, etc. */
*(.iram1.*)
/* SDK libraries expect their .text sections to link to iram, not irom */
*libcrypto.a:*(.literal .text)
*libmain.a:*(.literal .text .literal.* .text.*)
*libnet80211.a:*(.literal .text)
*libpp.a:*(.literal .text .literal.* .text.*)
*libphy.a:*(.literal .text .literal.* .text.*)
*libwpa.a:*(.literal .text)
*libwpa2.a:*(.literal .text)
*liblwip.a:*(.literal .text)
/* Xtensa basic functionality written in assembler should be placed in iram */
*xtensa.a:*(.literal .text .literal.* .text.*)
/* libgcc integer functions also need to be in .text, as some are called before
flash is mapped (also performance)
*/
*libgcc.a:*i3.o(.literal .text .literal.* .text.*)
*libgcc.a:*mulsf3.o(.literal .text .literal.* .text.*)
*libgcc.a:*divsf3.o(.literal .text .literal.* .text.*)
*libgcc.a:*fixsfsi.o(.literal .text .literal.* .text.*)
/* libc also in IRAM */
*libc.a:*malloc.o(.literal .text .literal.* .text.*)
*libc.a:*mallocr.o(.literal .text .literal.* .text.*)
*libc.a:*freer.o(.literal .text .literal.* .text.*)
*libc.a:*memcpy.o(.literal .text .literal.* .text.*)
*libc.a:*memchr.o(.literal .text .literal.* .text.*)
*libc.a:*memset.o(.literal .text .literal.* .text.*)
*libc.a:*memcmp.o(.literal .text .literal.* .text.*)
*libc.a:*memmove.o(.literal .text .literal.* .text.*)
*libc.a:*rand.o(.literal .text .literal.* .text.*)
*libc.a:*bzero.o(.literal .text .literal.* .text.*)
*libc.a:*lock.o(.literal .text .literal.* .text.*)
*libc.a:*printf.o(.literal .text .literal.* .text.*)
*libc.a:*findfp.o(.literal .text .literal.* .text.*)
*libc.a:*fputwc.o(.literal .text .literal.* .text.*)
enc28j60.a:*(.literal .text .literal.* .text.*)
*(.fini.literal)
*(.fini)
*(.gnu.version)
_text_end = ABSOLUTE(.);
_etext = .;
} >iram1_0_seg :iram1_0_phdr
.irom0.text : ALIGN(4)
{
_irom0_text_start = ABSOLUTE(.);
*libmbedtls.a:(.literal .text .literal.* .text.*)
/* RIOT-OS compiled code goes into IROM by default
(except for libgcc which is matched above.) */
*(.literal .text .literal.* .text.* .rodata .rodata.*)
/* Anything explicitly marked as "irom" or "irom0" should go here */
*(.irom0.literal .irom.literal .irom.text.literal .irom0.text .irom.text)
_irom0_text_end = ABSOLUTE(.);
} >irom0_0_seg :irom0_0_phdr
.lit4 : ALIGN(4)
{
_lit4_start = ABSOLUTE(.);
*(*.lit4)
*(.lit4.*)
*(.gnu.linkonce.lit4.*)
_lit4_end = ABSOLUTE(.);
} >iram1_0_seg :iram1_0_phdr
}

View File

@ -0,0 +1,293 @@
/**
* This linker script is a modified version of eagle.app.v6.ld that
* was generated from xt-genldscripts.tpp for LSP and shipped with
* ESP8266_NONOS_SDK
*/
/* Linker Script for ld -N */
MEMORY
{
dport0_0_seg : org = 0x3FF00000, len = 0x10
dram0_0_seg : org = 0x3FFE8000, len = 0x14000
iram1_0_seg : org = 0x40100000, len = 0x8000
irom0_0_seg : org = 0x40210000, len = 0x5C000
}
PHDRS
{
dport0_0_phdr PT_LOAD;
dram0_0_phdr PT_LOAD;
dram0_0_bss_phdr PT_LOAD;
iram1_0_phdr PT_LOAD;
irom0_0_phdr PT_LOAD;
}
/* Default entry point: */
ENTRY(call_user_start)
EXTERN(_DebugExceptionVector)
EXTERN(_DoubleExceptionVector)
EXTERN(_KernelExceptionVector)
EXTERN(_NMIExceptionVector)
EXTERN(_UserExceptionVector)
PROVIDE(_memmap_vecbase_reset = 0x40000000);
/* Various memory-map dependent cache attribute settings: */
_memmap_cacheattr_wb_base = 0x00000110;
_memmap_cacheattr_wt_base = 0x00000110;
_memmap_cacheattr_bp_base = 0x00000220;
_memmap_cacheattr_unused_mask = 0xFFFFF00F;
_memmap_cacheattr_wb_trapnull = 0x2222211F;
_memmap_cacheattr_wba_trapnull = 0x2222211F;
_memmap_cacheattr_wbna_trapnull = 0x2222211F;
_memmap_cacheattr_wt_trapnull = 0x2222211F;
_memmap_cacheattr_bp_trapnull = 0x2222222F;
_memmap_cacheattr_wb_strict = 0xFFFFF11F;
_memmap_cacheattr_wt_strict = 0xFFFFF11F;
_memmap_cacheattr_bp_strict = 0xFFFFF22F;
_memmap_cacheattr_wb_allvalid = 0x22222112;
_memmap_cacheattr_wt_allvalid = 0x22222112;
_memmap_cacheattr_bp_allvalid = 0x22222222;
PROVIDE(_memmap_cacheattr_reset = _memmap_cacheattr_wb_trapnull);
/* System task handling variables */
/* source: disassembly of boot rom at https://github.com/trebisky/esp8266 */
PROVIDE( ets_task_min_prio = 0x3fffc6fc );
PROVIDE( ets_idle_cb = 0x3fffdab0 );
PROVIDE( ets_idle_arg = 0x3fffdab4 );
PROVIDE( ets_task_exec_mask = 0x3fffdab8 );
PROVIDE( ets_task_tab = 0x3fffdac0 );
PROVIDE( flashchip = 0x3fffc714 );
PROVIDE( sdk_flashchip = 0x3fffc718 );
PROVIDE( _xt_interrupt_table = 0x3fffc200 );
SECTIONS
{
.dport0.rodata : ALIGN(4)
{
_dport0_rodata_start = ABSOLUTE(.);
*(.dport0.rodata)
*(.dport.rodata)
_dport0_rodata_end = ABSOLUTE(.);
} >dport0_0_seg :dport0_0_phdr
.dport0.literal : ALIGN(4)
{
_dport0_literal_start = ABSOLUTE(.);
*(.dport0.literal)
*(.dport.literal)
_dport0_literal_end = ABSOLUTE(.);
} >dport0_0_seg :dport0_0_phdr
.dport0.data : ALIGN(4)
{
_dport0_data_start = ABSOLUTE(.);
*(.dport0.data)
*(.dport.data)
_dport0_data_end = ABSOLUTE(.);
} >dport0_0_seg :dport0_0_phdr
.data : ALIGN(4)
{
_data_start = ABSOLUTE(.);
*(.data)
*(.data.*)
*(.gnu.linkonce.d.*)
*(.data1)
*(.sdata)
*(.sdata.*)
*(.gnu.linkonce.s.*)
*(.sdata2)
*(.sdata2.*)
*(.gnu.linkonce.s2.*)
*(.jcr)
_data_end = ABSOLUTE(.);
} >dram0_0_seg :dram0_0_phdr
.rodata : ALIGN(4)
{
_rodata_start = ABSOLUTE(.);
*(.sdk.version)
/* TODO put only necessary .rodata to dram
*libc.a:*.o(.rodata.* .rodata)
*core.a:*(.rodata.* .rodata)
*cpu.a:*(.rodata .rodata.*)
*/
*(.rodata .rodata.*)
*(.gnu.linkonce.r.*)
*(.rodata1)
__XT_EXCEPTION_TABLE__ = ABSOLUTE(.);
*(.xt_except_table)
*(.gcc_except_table)
*(.gnu.linkonce.e.*)
*(.gnu.version_r)
*(.eh_frame)
/* C++ constructor and destructor tables, properly ordered: */
KEEP (*crtbegin.o(.ctors))
KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
KEEP (*crtbegin.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
/* C++ exception handlers table: */
__XT_EXCEPTION_DESCS__ = ABSOLUTE(.);
*(.xt_except_desc)
*(.gnu.linkonce.h.*)
__XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.);
*(.xt_except_desc_end)
*(.dynamic)
*(.gnu.version_d)
. = ALIGN(4); /* this table MUST be 4-byte aligned */
_bss_table_start = ABSOLUTE(.);
LONG(_bss_start)
LONG(_bss_end)
_bss_table_end = ABSOLUTE(.);
_rodata_end = ABSOLUTE(.);
} >dram0_0_seg :dram0_0_phdr
.bss ALIGN(8) (NOLOAD) : ALIGN(4)
{
. = ALIGN (8);
_bss_start = ABSOLUTE(.);
*(.dynsbss)
*(.sbss)
*(.sbss.*)
*(.gnu.linkonce.sb.*)
*(.scommon)
*(.sbss2)
*(.sbss2.*)
*(.gnu.linkonce.sb2.*)
*(.dynbss)
*(.bss)
*(.bss.*)
*(.gnu.linkonce.b.*)
*(COMMON)
. = ALIGN (8);
_bss_end = ABSOLUTE(.);
_sheap = ABSOLUTE(.);
_heap_start = ABSOLUTE(.);
} >dram0_0_seg :dram0_0_bss_phdr
/* ETS system memory starts at 0x3FFFC000 which is the top of the heap for the app */
. = 0x3FFFC000;
_heap_top = ABSOLUTE(.);
_eheap = ABSOLUTE(.);
.text : ALIGN(4)
{
_stext = .;
_text_start = ABSOLUTE(.);
*(.UserEnter.text)
. = ALIGN(16);
*(.DebugExceptionVector.text)
. = ALIGN(16);
*(.NMIExceptionVector.text)
. = ALIGN(16);
*(.KernelExceptionVector.text)
LONG(0)
LONG(0)
LONG(0)
LONG(0)
. = ALIGN(16);
*(.UserExceptionVector.text)
LONG(0)
LONG(0)
LONG(0)
LONG(0)
. = ALIGN(16);
*(.DoubleExceptionVector.text)
LONG(0)
LONG(0)
LONG(0)
LONG(0)
. = ALIGN (16);
*(.entry.text)
*(.init.literal)
*(.init)
/* normal code should be in irom0 */
/*
*(.literal .text .literal.* .text.* .stub)
*(.gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*)
*/
/* RIOT-OS compiled source files that use the .iram1.* section names for IRAM
functions, etc. */
*(.iram1.*)
/* SDK libraries expect their .text sections to link to iram, not irom */
*libcrypto.a:*(.literal .text)
*libmain.a:*(.literal .text .literal.* .text.*)
*libnet80211.a:*(.literal .text)
*libpp.a:*(.literal .text .literal.* .text.*)
*libphy.a:*(.literal .text .literal.* .text.*)
*libwpa.a:*(.literal .text)
*libwpa2.a:*(.literal .text)
*liblwip.a:*(.literal .text)
/* Xtensa basic functionality written in assembler should be placed in iram */
*xtensa.a:*(.literal .text .literal.* .text.*)
/* libgcc integer functions also need to be in .text, as some are called before
flash is mapped (also performance) */
*libgcc.a:*i3.o(.literal .text .literal.* .text.*)
*libgcc.a:*mulsf3.o(.literal .text .literal.* .text.*)
*libgcc.a:*divsf3.o(.literal .text .literal.* .text.*)
*libgcc.a:*fixsfsi.o(.literal .text .literal.* .text.*)
/* libc also in IRAM */
*libc.a:*malloc.o(.literal .text .literal.* .text.*)
*libc.a:*mallocr.o(.literal .text .literal.* .text.*)
*libc.a:*freer.o(.literal .text .literal.* .text.*)
*libc.a:*memchr.o(.literal .text .literal.* .text.*)
*libc.a:*memcpy.o(.literal .text .literal.* .text.*)
*libc.a:*memset.o(.literal .text .literal.* .text.*)
*libc.a:*memcmp.o(.literal .text .literal.* .text.*)
*libc.a:*memmove.o(.literal .text .literal.* .text.*)
*libc.a:*rand.o(.literal .text .literal.* .text.*)
*libc.a:*bzero.o(.literal .text .literal.* .text.*)
*libc.a:*lock.o(.literal .text .literal.* .text.*)
*libc.a:*findfp.o(.literal .text .literal.* .text.*)
*libc.a:*fputwc.o(.literal .text .literal.* .text.*)
enc28j60.a:*(.literal .text .literal.* .text.*)
*(.fini.literal)
*(.fini)
*(.gnu.version)
_text_end = ABSOLUTE(.);
_etext = .;
} >iram1_0_seg :iram1_0_phdr
.irom0.text : ALIGN(4)
{
_irom0_text_start = ABSOLUTE(.);
*libmbedtls.a:(.literal .text .literal.* .text.*)
/* RIOT-OS compiled code goes into IROM by default
(except for functions with section names defined in .text above.) */
*(.literal .text .literal.* .text.* .rodata .rodata.*)
/* Anything explicitly marked as "irom" or "irom0" should go here */
*(.irom0.literal .irom.literal .irom.text.literal .irom0.text .irom.text)
_irom0_text_end = ABSOLUTE(.);
} >irom0_0_seg :irom0_0_phdr
.lit4 : ALIGN(4)
{
_lit4_start = ABSOLUTE(.);
*(*.lit4)
*(.lit4.*)
*(.gnu.linkonce.lit4.*)
_lit4_end = ABSOLUTE(.);
} >iram1_0_seg :iram1_0_phdr
}

View File

@ -0,0 +1,3 @@
MODULE = periph
include $(RIOTBASE)/Makefile.base

57
cpu/esp8266/periph/adc.c Normal file
View File

@ -0,0 +1,57 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser General
* Public License v2.1. See the file LICENSE in the top level directory for more
* details.
*/
/**
* @ingroup cpu_esp8266
* @ingroup drivers_periph_adc
* @{
*
* @file
* @brief Low-level ADC driver implementation
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
#define ENABLE_DEBUG 0
#include "debug.h"
#include "cpu.h"
#include "mutex.h"
#include "periph/adc.h"
#include "periph_conf.h"
#include "board.h"
#include "common.h"
#include "sdk/sdk.h"
#if defined(ADC_NUMOF) && ADC_NUMOF > 0
int adc_init(adc_t line)
{
CHECK_PARAM_RET (line < ADC_NUMOF, -1)
/* no special inialization needed */
return 0;
}
int adc_sample(adc_t line, adc_res_t res)
{
CHECK_PARAM_RET (line < ADC_NUMOF, -1)
CHECK_PARAM_RET (res == ADC_RES_10BIT, -1)
#ifdef MODULE_ESP_SDK
return system_adc_read ();
#else
return test_tout(false);
#endif
}
#endif

View File

@ -0,0 +1,32 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266
* @ingroup drivers_periph_cpuid
* @{
*
* @file
* @brief Implementation
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
#include <string.h>
#include <stdint.h>
#include "periph/cpuid.h"
#include "sdk/sdk.h"
void cpuid_get(void *id)
{
uint32_t chip_id = system_get_chip_id();
memcpy(id, &(chip_id), CPUID_LEN);
}

287
cpu/esp8266/periph/flash.c Normal file
View File

@ -0,0 +1,287 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266
* @{
*
* @file
* @brief Low-level MTD flash drive implementation
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
#define ENABLE_DEBUG (0)
#include "debug.h"
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include "board.h"
#include "common.h"
#include "irq_arch.h"
#include "log.h"
#include "mtd.h"
#include "c_types.h"
#include "esp/spiflash.h"
#include "spi_flash.h"
#include "sdk/rom.h"
#define SDK_FLASH_FUNCTIONS
/* the external pointer to the system MTD device */
mtd_dev_t* mtd0 = 0;
mtd_dev_t _flash_dev;
mtd_desc_t _flash_driver;
/* forward declaration of mtd functions */
static int _flash_init (mtd_dev_t *dev);
static int _flash_read (mtd_dev_t *dev, void *buff, uint32_t addr, uint32_t size);
static int _flash_write (mtd_dev_t *dev, const void *buff, uint32_t addr, uint32_t size);
static int _flash_erase (mtd_dev_t *dev, uint32_t addr, uint32_t size);
static int _flash_power (mtd_dev_t *dev, enum mtd_power_state power);
static uint32_t _flash_beg; /* first byte addr of the flash drive in SPI flash */
static uint32_t _flash_end; /* first byte addr after the flash drive in SPI flash */
static uint32_t _flash_size; /* resulting size of the flash drive in SPI flash */
#define SPIFFS_FLASH_BEGIN 0x80000 /* TODO determine real possible value */
void flash_drive_init (void)
{
DEBUG("%s\n", __func__);
_flash_driver.init = &_flash_init;
_flash_driver.read = &_flash_read;
_flash_driver.write = &_flash_write;
_flash_driver.erase = &_flash_erase;
_flash_driver.power = &_flash_power;
_flash_beg = SPIFFS_FLASH_BEGIN;
_flash_end = flashchip->chip_size - 5 * flashchip->sector_size;
_flash_size = _flash_end - _flash_beg;
_flash_dev.driver = &_flash_driver;
_flash_dev.sector_count = _flash_size / flashchip->sector_size;
mtd0 = &_flash_dev;
_flash_dev.pages_per_sector = flashchip->sector_size / flashchip->page_size;
_flash_dev.page_size = flashchip->page_size;
DEBUG("%s flashchip chip_size=%d block_size=%d sector_size=%d page_size=%d\n", __func__,
flashchip->chip_size, flashchip->block_size,
flashchip->sector_size, flashchip->page_size);
DEBUG("%s flash_dev sector_count=%d pages_per_sector=%d page_size=%d\n", __func__,
_flash_dev.sector_count, _flash_dev.pages_per_sector, _flash_dev.page_size);
DEBUG("\n");
}
static int _flash_init (mtd_dev_t *dev)
{
DEBUG("%s dev=%p driver=%p\n", __func__, dev, &_flash_driver);
CHECK_PARAM_RET (dev == &_flash_dev, -ENODEV);
#ifdef SPI_FLASH_CHIP_SIZE
if (SPI_FLASH_CHIP_SIZE <= SPIFFS_FLASH_BEGIN) {
#else
if (flashchip->chip_size <= SPIFFS_FLASH_BEGIN) {
#endif
LOG_ERROR("Flash size is equal or less than %d Byte, "
"SPIFFS cannot be used\n", SPIFFS_FLASH_BEGIN);
return -ENODEV;
}
return 0;
}
#define SPI_FLASH_BUF_SIZE 64
uint8_t _flash_buf[SPI_FLASH_BUF_SIZE];
static int _flash_read (mtd_dev_t *dev, void *buff, uint32_t addr, uint32_t size)
{
DEBUG("%s dev=%p addr=%08x size=%u buf=%p\n", __func__, dev, addr, size, buff);
CHECK_PARAM_RET (dev == &_flash_dev, -ENODEV);
CHECK_PARAM_RET (buff != NULL, -ENOTSUP);
/* size must be within the flash address space */
CHECK_PARAM_RET (_flash_beg + addr + size <= _flash_end, -EOVERFLOW);
#ifndef SDK_FLASH_FUNCTIONS
bool result = spiflash_read (_flash_beg + addr, buff, size);
return result ? (int)size : -EIO;
#else
critical_enter();
/* it would be great if would work in that way, but would be too easy :-( */
/* memcpy(buff, (void*)(_flash_beg + addr + 0x40200000), size); */
int result = SPI_FLASH_RESULT_OK;
uint32_t len = size;
/* if addr is not 4 byte aligned, we need to read the first full word */
if (addr & 0x3) {
uint32_t word_addr = addr & ~0x3;
uint32_t pos_in_word = addr & 0x3;
uint32_t len_in_word = 4 - pos_in_word;
len_in_word = (len_in_word < len) ? len_in_word : len;
result = spi_flash_read (_flash_beg + word_addr, (uint32_t*)_flash_buf, 4);
memcpy(buff, _flash_buf + pos_in_word, len_in_word);
buff = (uint8_t*)buff + len_in_word;
addr += len_in_word;
len -= len_in_word;
}
/* read all full words, maximum SPI_FLASH_BUF_SIZE in one write operation */
while (result == SPI_FLASH_RESULT_OK && len > 4) {
uint32_t len_full_words = len & ~0x3;
if (len_full_words > SPI_FLASH_BUF_SIZE) {
len_full_words = SPI_FLASH_BUF_SIZE;
}
result |= spi_flash_read (_flash_beg + addr, (uint32_t*)_flash_buf, len_full_words);
memcpy(buff, _flash_buf, len_full_words);
buff = (uint8_t*)buff + len_full_words;
addr += len_full_words;
len -= len_full_words;
}
/* if there is some remaining, we need to prepare last word */
if (result == SPI_FLASH_RESULT_OK && len) {
result |= spi_flash_read (_flash_beg + addr, (uint32_t*)_flash_buf, 4);
memcpy(buff, _flash_buf, len);
}
critical_exit();
return (result == SPI_FLASH_RESULT_OK) ? (int)size : -EIO;
#endif
}
static int _flash_write (mtd_dev_t *dev, const void *buff, uint32_t addr, uint32_t size)
{
DEBUG("%s dev=%p addr=%08x size=%u buf=%p\n", __func__, dev, addr, size, buff);
CHECK_PARAM_RET (dev == &_flash_dev, -ENODEV);
CHECK_PARAM_RET (buff != NULL, -ENOTSUP);
/* size must be within the flash address space */
CHECK_PARAM_RET (_flash_beg + addr + size <= _flash_end, -EOVERFLOW);
#ifndef SDK_FLASH_FUNCTIONS
bool result = spiflash_write (_flash_beg + addr, (uint8_t*)buff, size);
return result ? (int)size : -EIO;
#else
critical_enter();
int result = SPI_FLASH_RESULT_OK;
uint32_t len = size;
/* if addr is not 4 byte aligned, we need to prepare first full word */
if (addr & 0x3) {
uint32_t word_addr = addr & ~0x3;
uint32_t pos_in_word = addr & 0x3;
uint32_t len_in_word = 4 - pos_in_word;
len_in_word = (len_in_word < len) ? len_in_word : len;
result = spi_flash_read (_flash_beg + word_addr, (uint32_t*)_flash_buf, 4);
memcpy(_flash_buf + pos_in_word, buff, len_in_word);
result |= spi_flash_write (_flash_beg + word_addr, (uint32_t*)_flash_buf, 4);
buff = (uint8_t*)buff + len_in_word;
addr += len_in_word;
len -= len_in_word;
}
/* write all full words, maximum SPI_FLASH_BUF_SIZE in one write operation */
while (result == SPI_FLASH_RESULT_OK && len > 4) {
uint32_t len_full_words = len & ~0x3;
if (len_full_words > SPI_FLASH_BUF_SIZE) {
len_full_words = SPI_FLASH_BUF_SIZE;
}
memcpy(_flash_buf, buff, len_full_words);
result |= spi_flash_write (_flash_beg + addr, (uint32_t*)_flash_buf, len_full_words);
buff = (uint8_t*)buff + len_full_words;
addr += len_full_words;
len -= len_full_words;
}
/* if there is some remaining, we need to prepare last word */
if (result == SPI_FLASH_RESULT_OK && len) {
result |= spi_flash_read (_flash_beg + addr, (uint32_t*)_flash_buf, 4);
memcpy(_flash_buf, buff, len);
result |= spi_flash_write (_flash_beg + addr, (uint32_t*)_flash_buf, 4);
}
critical_exit();
return (result == SPI_FLASH_RESULT_OK) ? (int)size : -EIO;
#endif
}
static int _flash_erase (mtd_dev_t *dev, uint32_t addr, uint32_t size)
{
DEBUG("%s dev=%p addr=%08x size=%u\n", __func__, dev, addr, size);
CHECK_PARAM_RET (dev == &_flash_dev, -ENODEV);
/* size must be within the flash address space */
CHECK_PARAM_RET (_flash_beg + addr + size <= _flash_end, -EOVERFLOW);
/* size must be a multiple of sector_size && at least one sector */
CHECK_PARAM_RET (size >= flashchip->sector_size, -ENOTSUP);
CHECK_PARAM_RET (size % flashchip->sector_size == 0, -ENOTSUP)
#ifndef SDK_FLASH_FUNCTIONS
bool result = false;
uint32_t count = size / flashchip->sector_size;
while (count--) {
uint32_t sec = _flash_beg + addr + count * flashchip->sector_size;
if (!(result = spiflash_erase_sector (sec))) {
break;
}
}
return result ? 0 : -EIO;
#else
critical_enter();
uint32_t result = SPI_FLASH_RESULT_OK;
uint32_t count = size / flashchip->sector_size;
while (count--) {
uint32_t sec = (_flash_beg + addr) / flashchip->sector_size + count;
if ((result = spi_flash_erase_sector (sec)) != SPI_FLASH_RESULT_OK) {
break;
}
}
critical_exit();
return result;
#endif
}
static int _flash_power (mtd_dev_t *dev, enum mtd_power_state power)
{
DEBUG("%s\n", __func__);
return -ENOTSUP;
}

284
cpu/esp8266/periph/gpio.c Normal file
View File

@ -0,0 +1,284 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266
* @ingroup drivers_periph_gpio
* @{
*
* @file
* @brief Low-level GPIO driver implementation for ESP8266
*
* @author Gunar Schorcht <gunar@schorcht.net>
* @}
*/
#define ENABLE_DEBUG (0)
#include "debug.h"
#include <stdbool.h>
#include "log.h"
#include "periph/gpio.h" /* RIOT gpio.h */
#include "c_types.h"
#include "eagle_soc.h"
#include "ets_sys.h"
#include "sdk/ets.h"
#include "esp/gpio_regs.h"
#include "esp/iomux_regs.h"
#include "esp/rtc_regs.h"
#include "common.h"
#include "gpio_common.h"
#include "irq_arch.h"
#include "syscalls.h"
/*
* IOMUX to GPIO mapping
* source https://www.espressif.com/sites/default/files/documentation/0d-esp8266_pin_list_release_15-11-2014.xlsx
*/
const uint8_t _gpio_to_iomux[] = { 12, 5, 13, 4, 14, 15, 6, 7, 8, 9, 10, 11, 0, 1, 2, 3 };
const uint8_t _iomux_to_gpio[] = { 12, 13, 14, 15, 3, 1, 6, 7, 8, 9, 10, 11, 0, 2, 4, 5 };
_gpio_pin_usage_t _gpio_pin_usage [GPIO_PIN_NUMOF] =
{
_GPIO, /* gpio0 */
_UART, /* gpio1 UART0 RxD */
_GPIO, /* gpio2 */
_UART, /* gpio3 UART0 TxD */
_GPIO, /* gpio4 */
_GPIO, /* gpio5 */
_SPIF, /* gpio6 SPI flash CLK */
_SPIF, /* gpio7 SPI flash MISO */
_SPIF, /* gpio8 SPI flash MOSI */
#if defined(FLASH_MODE_QIO) || defined(FLASH_MODE_QOUT)
_SPIF, /* gpio9 SPI flash HD (qio/qout flash mode) */
_SPIF, /* gpio10 SPI flash WP (qio/qout flash mode) */
#else
_GPIO, /* gpio9 can be used as GPIO if not needed for flash */
_GPIO, /* gpio10 can be used as GPIO if not needed for flash */
#endif
_SPIF, /* gpio11 SPI flash CS */
_GPIO, /* gpio12 */
_GPIO, /* gpio13 */
_GPIO, /* gpio14 */
_GPIO, /* gpio15 */
_GPIO /* gpio16 */
};
int gpio_init(gpio_t pin, gpio_mode_t mode)
{
DEBUG("%s: %d %d\n", __func__, pin, mode);
CHECK_PARAM_RET(pin < GPIO_PIN_NUMOF, -1);
/* check whether pin can be used as GPIO or is used in anyway else */
switch (_gpio_pin_usage[pin]) {
case _I2C: LOG_ERROR("GPIO%d is used as I2C signal.\n", pin); return -1;
case _PWM: LOG_ERROR("GPIO%d is used as PWM output.\n", pin); return -1;
case _SPI: LOG_ERROR("GPIO%d is used as SPI interface.\n", pin); return -1;
case _SPIF: LOG_ERROR("GPIO%d is used as SPI flash.\n", pin); return -1;
case _UART: LOG_ERROR("GPIO%d is used as UART interface.\n", pin); return -1;
default: break;
}
/* GPIO16 requires separate handling */
if (pin == GPIO16) {
RTC.GPIO_CFG[3] = (RTC.GPIO_CFG[3] & 0xffffffbc) | BIT(0);
RTC.GPIO_CONF = RTC.GPIO_CONF & ~RTC_GPIO_CONF_OUT_ENABLE;
switch (mode) {
case GPIO_OUT:
RTC.GPIO_ENABLE = RTC.GPIO_OUT | RTC_GPIO_CONF_OUT_ENABLE;
break;
case GPIO_OD:
LOG_ERROR("GPIO mode GPIO_OD is not supported for GPIO16.\n");
return -1;
case GPIO_OD_PU:
LOG_ERROR("GPIO mode GPIO_OD_PU is not supported for GPIO16.\n");
return -1;
case GPIO_IN:
RTC.GPIO_ENABLE = RTC.GPIO_OUT & ~RTC_GPIO_CONF_OUT_ENABLE;
RTC.GPIO_CFG[3] &= ~RTC_GPIO_CFG3_PIN_PULLUP;
break;
case GPIO_IN_PU:
LOG_ERROR("GPIO mode GPIO_IN_PU is not supported for GPIO16.\n");
return -1;
case GPIO_IN_PD:
LOG_ERROR("GPIO mode GPIO_IN_PD is not supported for GPIO16.\n");
return -1;
default:
LOG_ERROR("Invalid GPIO mode for GPIO%d.\n", pin);
return -1;
}
return 0;
}
uint8_t iomux = _gpio_to_iomux [pin];
uint32_t iomux_conf = (iomux > 11) ? IOMUX_FUNC(0) : IOMUX_FUNC(3);
switch (mode) {
case GPIO_OUT: iomux_conf |= IOMUX_PIN_OUTPUT_ENABLE;
iomux_conf |= IOMUX_PIN_OUTPUT_ENABLE_SLEEP;
GPIO.CONF[pin] &= ~GPIO_CONF_OPEN_DRAIN;
GPIO.ENABLE_OUT_SET = BIT(pin);
break;
case GPIO_OD_PU: iomux_conf |= IOMUX_PIN_PULLUP;
iomux_conf |= IOMUX_PIN_PULLUP_SLEEP;
case GPIO_OD: iomux_conf |= IOMUX_PIN_OUTPUT_ENABLE;
iomux_conf |= IOMUX_PIN_OUTPUT_ENABLE_SLEEP;
GPIO.CONF[pin] |= GPIO_CONF_OPEN_DRAIN;
GPIO.ENABLE_OUT_SET = BIT(pin);
break;
case GPIO_IN_PU: iomux_conf |= IOMUX_PIN_PULLUP;
iomux_conf |= IOMUX_PIN_PULLUP_SLEEP;
case GPIO_IN: GPIO.ENABLE_OUT_CLEAR = BIT(pin);
break;
case GPIO_IN_PD: LOG_ERROR("GPIO mode GPIO_IN_PD is not supported.\n");
return -1;
default: LOG_ERROR("Invalid GPIO mode for GPIO%d.\n", pin);
}
IOMUX.PIN[iomux] = iomux_conf;
return 0;
}
static gpio_isr_ctx_t gpio_isr_ctx_table [GPIO_PIN_NUMOF] = { };
static bool gpio_int_enabled_table [GPIO_PIN_NUMOF] = { };
void IRAM gpio_int_handler (void* arg)
{
irq_isr_enter();
for (int i = 0; i < GPIO_PIN_NUMOF; i++) {
uint32_t mask = BIT(i);
if (GPIO.STATUS & mask) {
GPIO.STATUS_CLEAR = mask;
if (gpio_int_enabled_table[i] && (GPIO.CONF[i] & GPIO_PIN_INT_TYPE_MASK)) {
gpio_isr_ctx_table[i].cb (gpio_isr_ctx_table[i].arg);
}
}
mask = mask << 1;
}
irq_isr_exit();
}
int gpio_init_int(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank,
gpio_cb_t cb, void *arg)
{
if (gpio_init(pin, mode)) {
return -1;
}
if (pin == GPIO16) {
/* GPIO16 requires separate handling */
LOG_ERROR("GPIO16 cannot generate interrupts.\n");
return -1;
}
gpio_isr_ctx_table[pin].cb = cb;
gpio_isr_ctx_table[pin].arg = arg;
GPIO.CONF[pin] = SET_FIELD(GPIO.CONF[pin], GPIO_CONF_INTTYPE, flank);
if (flank != GPIO_NONE) {
gpio_int_enabled_table [pin] = (gpio_isr_ctx_table[pin].cb != NULL);
ets_isr_attach (ETS_GPIO_INUM, gpio_int_handler, 0);
ets_isr_unmask ((1 << ETS_GPIO_INUM));
}
return 0;
}
void gpio_irq_enable (gpio_t pin)
{
CHECK_PARAM(pin < GPIO_PIN_NUMOF);
gpio_int_enabled_table [pin] = true;
}
void gpio_irq_disable (gpio_t pin)
{
CHECK_PARAM(pin < GPIO_PIN_NUMOF);
gpio_int_enabled_table [pin] = false;
}
int gpio_read (gpio_t pin)
{
CHECK_PARAM_RET(pin < GPIO_PIN_NUMOF, -1);
if (pin == GPIO16) {
/* GPIO16 requires separate handling */
return RTC.GPIO_IN & BIT(0);
}
return (GPIO.IN & BIT(pin)) ? 1 : 0;
}
void gpio_write (gpio_t pin, int value)
{
DEBUG("%s: %d %d\n", __func__, pin, value);
CHECK_PARAM(pin < GPIO_PIN_NUMOF);
if (pin == GPIO16) {
/* GPIO16 requires separate handling */
RTC.GPIO_OUT = (RTC.GPIO_OUT & ~BIT(0)) | (value ? 1 : 0);
return;
}
if (value) {
GPIO.OUT_SET = BIT(pin) & GPIO_OUT_PIN_MASK;
}
else {
GPIO.OUT_CLEAR = BIT(pin) & GPIO_OUT_PIN_MASK;
}
}
void gpio_set (gpio_t pin)
{
gpio_write (pin, 1);
}
void gpio_clear (gpio_t pin)
{
gpio_write (pin, 0);
}
void gpio_toggle (gpio_t pin)
{
DEBUG("%s: %d\n", __func__, pin);
CHECK_PARAM(pin < GPIO_PIN_NUMOF);
if (pin == GPIO16) {
/* GPIO16 requires separate handling */
RTC.GPIO_OUT = (RTC.GPIO_OUT & ~BIT(0)) | ((RTC.GPIO_IN & BIT(0)) ? 0 : 1);
return;
}
GPIO.OUT ^= BIT(pin);
}

View File

@ -0,0 +1,55 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266
* @ingroup drivers_periph_hwrng
* @{
*
* @file
* @brief Low-level random number generator driver implementation
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
#include "cpu.h"
#include "periph_conf.h"
#include "periph/hwrng.h"
#include "esp/wdev_regs.h"
void hwrng_init(void)
{
/* no need for initialization */
}
void hwrng_read(void *buf, unsigned int num)
{
unsigned int count = 0;
uint8_t *b = (uint8_t *)buf;
while (count < num) {
/* read next 4 bytes of random data */
uint32_t tmp = WDEV.HWRNG;
/* copy data into result vector */
for (int i = 0; i < 4 && count < num; i++) {
b[count++] = (uint8_t)tmp;
tmp = tmp >> 8;
}
}
}
uint32_t hwrand (void)
{
uint32_t _tmp;
hwrng_read(&_tmp, sizeof(uint32_t));
return _tmp;
}

643
cpu/esp8266/periph/i2c.c Normal file
View File

@ -0,0 +1,643 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266
* @ingroup drivers_periph_i2c
* @{
*
* @file
* @brief Low-level I2C driver implementation using ESP8266 SDK
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
/*
PLEASE NOTE:
Some parts of the implementation bases on the bit-banging implementation as
described in [wikipedia](https://en.wikipedia.org/wiki/I%C2%B2C) as well as
its implementation in [esp-open-rtos](https://github.com/SuperHouse/esp-open-rtos.git).
These parts are under the copyright of their respective owners.
*/
#define ENABLE_DEBUG (0)
#include "debug.h"
#include <stdbool.h>
#include <errno.h>
#include "cpu.h"
#include "log.h"
#include "mutex.h"
#include "periph_conf.h"
#include "periph/gpio.h"
#include "periph/i2c.h"
#include "common.h"
#include "esp/gpio_regs.h"
#include "sdk/ets.h"
#if defined(I2C_NUMOF) && I2C_NUMOF > 0
/* has to be declared as extern since it is not possible to include */
/* user_interface.h due to conflicts with gpio_init */
extern uint8_t system_get_cpu_freq(void);
extern bool system_update_cpu_freq(uint8_t freq);
/* max clock stretching counter */
#define I2C_CLOCK_STRETCH 400
typedef struct
{
i2c_speed_t speed;
i2c_t dev;
bool started;
gpio_t scl;
gpio_t sda;
uint32_t scl_bit; /* gpio bit mask for faster access */
uint32_t sda_bit; /* gpio bit mask for faster access */
uint32_t delay;
} _i2c_bus_t;
static _i2c_bus_t _i2c_bus[] =
{
#if defined(I2C0_SDA) && defined(I2C0_SDA)
{
.speed = I2C0_SPEED,
.sda = I2C0_SDA,
.scl = I2C0_SCL
},
#endif
#if defined(I2C1_SDA) && defined(I2C1_SDA)
{
.speed = I2C1_SPEED,
.sda = I2C1_SDA,
.scl = I2C1_SCL
},
#endif
#if defined(I2C2_SDA) && defined(I2C2_SDA)
{
.speed = I2C2_SPEED,
.sda = I2C2_SDA,
.scl = I2C2_SCL
},
#endif
};
static const uint32_t _i2c_delays[][2] =
{
/* values specify one half-period and are only valid for -O2 option */
/* value = [period - 0.5us(160MHz) or 1.0us(80MHz)] * cycles per second / 2 */
/* cycles per us = ca. 20 (80 MHz) / ca. 40 (160 MHz) */
[I2C_SPEED_LOW] = {1990, 990}, /* 10 kbps (period 100 us) */
[I2C_SPEED_NORMAL] = { 190, 90}, /* 100 kbps (period 10 us) */
[I2C_SPEED_FAST] = { 40, 17}, /* 400 kbps (period 2.5 us) */
[I2C_SPEED_FAST_PLUS] = { 13, 0}, /* 1 Mbps (period 1 us) */
[I2C_SPEED_HIGH] = { 0, 0} /* 3.4 Mbps (period 0.3 us) is not working */
};
static mutex_t i2c_bus_lock[I2C_NUMOF] = { MUTEX_INIT };
/* forward declaration of internal functions */
static inline void _i2c_delay (_i2c_bus_t* bus);
static inline bool _i2c_read_scl (_i2c_bus_t* bus);
static inline bool _i2c_read_sda (_i2c_bus_t* bus);
static inline void _i2c_set_scl (_i2c_bus_t* bus);
static inline void _i2c_clear_scl (_i2c_bus_t* bus);
static inline void _i2c_set_sda (_i2c_bus_t* bus);
static inline void _i2c_clear_sda (_i2c_bus_t* bus);
static int _i2c_start_cond (_i2c_bus_t* bus);
static int _i2c_stop_cond (_i2c_bus_t* bus);
static int _i2c_write_bit (_i2c_bus_t* bus, bool bit);
static int _i2c_read_bit (_i2c_bus_t* bus, bool* bit);
static int _i2c_write_byte (_i2c_bus_t* bus, uint8_t byte);
static int _i2c_read_byte (_i2c_bus_t* bus, uint8_t* byte, bool ack);
static int _i2c_arbitration_lost (_i2c_bus_t* bus, const char* func);
/* implementation of i2c interface */
void i2c_init(i2c_t dev)
{
if (I2C_NUMOF != sizeof(_i2c_bus)/sizeof(_i2c_bus_t)) {
LOG_INFO("I2C_NUMOF does not match number of I2C_SDA_x/I2C_SCL_x definitions\n");
LOG_INFO("Please check your board configuration in 'board.h'\n");
assert(I2C_NUMOF < sizeof(_i2c_bus)/sizeof(_i2c_bus_t));
return;
}
CHECK_PARAM (dev < I2C_NUMOF)
if (_i2c_bus[dev].speed == I2C_SPEED_HIGH) {
LOG_INFO("I2C_SPEED_HIGH is not supported\n");
return;
}
i2c_acquire (dev);
_i2c_bus[dev].dev = dev;
_i2c_bus[dev].delay =_i2c_delays[_i2c_bus[dev].speed][ets_get_cpu_frequency() == 80 ? 1 : 0];
_i2c_bus[dev].scl_bit = BIT(_i2c_bus[dev].scl); /* store bit mask for faster access */
_i2c_bus[dev].sda_bit = BIT(_i2c_bus[dev].sda); /* store bit mask for faster access */
_i2c_bus[dev].started = false; /* for handling of repeated start condition */
DEBUG ("%s: scl=%d sda=%d speed=%d\n", __func__,
_i2c_bus[dev].scl, _i2c_bus[dev].sda, _i2c_bus[dev].speed);
/* configure SDA and SCL pin as GPIO in open-drain mode with enabled pull-ups */
gpio_init (_i2c_bus[dev].scl, GPIO_OD_PU);
gpio_init (_i2c_bus[dev].sda, GPIO_OD_PU);
/* set SDA and SCL to be floating and pulled-up to high */
_i2c_set_sda (&_i2c_bus[dev]);
_i2c_set_scl (&_i2c_bus[dev]);
i2c_release (dev);
return;
}
int i2c_acquire(i2c_t dev)
{
CHECK_PARAM_RET (dev < I2C_NUMOF, -1)
mutex_lock(&i2c_bus_lock[dev]);
return 0;
}
int i2c_release(i2c_t dev)
{
CHECK_PARAM_RET (dev < I2C_NUMOF, -1)
mutex_unlock(&i2c_bus_lock[dev]);
return 0;
}
int /* IRAM */ i2c_read_bytes(i2c_t dev, uint16_t addr, void *data, size_t len, uint8_t flags)
{
DEBUG ("%s: dev=%u addr=%02x data=%p len=%d flags=%01x\n",
__func__, dev, addr, data, len, flags);
CHECK_PARAM_RET (dev < I2C_NUMOF, -EINVAL);
CHECK_PARAM_RET (len > 0, -EINVAL);
CHECK_PARAM_RET (data != NULL, -EINVAL);
_i2c_bus_t* bus = &_i2c_bus[dev];
int res = 0;
/* send START condition and address if I2C_NOSTART is not set */
if (!(flags & I2C_NOSTART)) {
/* START condition */
if ((res = _i2c_start_cond (bus)) != 0) {
return res;
}
/* send 10 bit or 7 bit address */
if (flags & I2C_ADDR10) {
/* prepare 10 bit address bytes */
uint8_t addr1 = 0xf0 | (addr & 0x0300) >> 7 | I2C_READ;
uint8_t addr2 = addr & 0xff;
/* send address bytes wit read flag */
if ((res = _i2c_write_byte (bus, addr1)) != 0 ||
(res = _i2c_write_byte (bus, addr2)) != 0) {
return res;
}
}
else {
/* send address byte with read flag */
if ((res = _i2c_write_byte (bus, (addr << 1 | I2C_READ))) != 0) {
return res;
}
}
}
/* receive bytes if send address was successful */
for (unsigned int i = 0; i < len; i++) {
if ((res = _i2c_read_byte (bus, &(((uint8_t*)data)[i]), i < len-1)) != 0) {
break;
}
}
/* send STOP condition if I2C_NOSTOP flag is not set */
if (!(flags & I2C_NOSTOP)) {
_i2c_stop_cond (bus);
}
return res;
}
int /* IRAM */ i2c_write_bytes(i2c_t dev, uint16_t addr, const void *data, size_t len, uint8_t flags)
{
DEBUG ("%s: dev=%u addr=%02x data=%p len=%d flags=%01x\n",
__func__, dev, addr, data, len, flags);
CHECK_PARAM_RET (dev < I2C_NUMOF, -EINVAL);
CHECK_PARAM_RET (len > 0, -EINVAL);
CHECK_PARAM_RET (data != NULL, -EINVAL);
_i2c_bus_t* bus = &_i2c_bus[dev];
int res = 0;
/* if I2C_NOSTART is not set, send START condition and ADDR */
if (!(flags & I2C_NOSTART)) {
/* START condition */
if ((res = _i2c_start_cond (bus)) != 0) {
return res;
}
/* send 10 bit or 7 bit address */
if (flags & I2C_ADDR10) {
/* prepare 10 bit address bytes */
uint8_t addr1 = 0xf0 | (addr & 0x0300) >> 7;
uint8_t addr2 = addr & 0xff;
/* send address bytes without read flag */
if ((res = _i2c_write_byte (bus, addr1)) != 0 ||
(res = _i2c_write_byte (bus, addr2)) != 0) {
return res;
}
}
else {
/* send address byte without read flag */
if ((res = _i2c_write_byte (bus, addr << 1)) != 0) {
return res;
}
}
}
/* send bytes if send address was successful */
for (unsigned int i = 0; i < len; i++) {
if ((res = _i2c_write_byte (bus, ((uint8_t*)data)[i])) != 0) {
break;
}
}
/* send STOP condition if I2C_NOSTOP flag is not set */
if (!(flags & I2C_NOSTOP)) {
return _i2c_stop_cond (bus);
}
return res;
}
void i2c_poweron(i2c_t dev)
{
/* since I2C is realized in software there is no device to power on */
/* just return */
}
void i2c_poweroff(i2c_t dev)
{
/* since I2C is realized in software there is no device to power off */
/* just return */
}
/* --- internal functions --- */
static inline void _i2c_delay (_i2c_bus_t* bus)
{
/* produces a delay */
/* ca. 20 cycles = 1 us (80 MHz) or ca. 40 cycles = 1 us (160 MHz) */
uint32_t cycles = bus->delay;
if (cycles) {
__asm__ volatile ("1: _addi.n %0, %0, -1 \n"
" bnez %0, 1b \n" : "=r" (cycles) : "0" (cycles));
}
}
/*
* Please note: SDA and SDL pins are used in GPIO_OD_PU mode
* (open-drain with pull-ups).
*
* Setting a pin which is in open-drain mode leaves the pin floating and
* the signal is pulled up to high. The signal can then be actively driven
* to low by a slave. A read operation returns the current signal at the pin.
*
* Clearing a pin which is in open-drain mode actively drives the signal to
* low.
*/
static inline bool _i2c_read_scl(_i2c_bus_t* bus)
{
/* read SCL status (pin is in open-drain mode and set) */
return GPIO.IN & bus->scl_bit;
}
static inline bool _i2c_read_sda(_i2c_bus_t* bus)
{
/* read SDA status (pin is in open-drain mode and set) */
return GPIO.IN & bus->sda_bit;
}
static inline void _i2c_set_scl(_i2c_bus_t* bus)
{
/* set SCL signal high (pin is in open-drain mode and pulled-up) */
GPIO.OUT_SET = bus->scl_bit;
}
static inline void _i2c_clear_scl(_i2c_bus_t* bus)
{
/* set SCL signal low (actively driven to low) */
GPIO.OUT_CLEAR = bus->scl_bit;
}
static inline void _i2c_set_sda(_i2c_bus_t* bus)
{
/* set SDA signal high (pin is in open-drain mode and pulled-up) */
GPIO.OUT_SET = bus->sda_bit;
}
static inline void _i2c_clear_sda(_i2c_bus_t* bus)
{
/* set SDA signal low (actively driven to low) */
GPIO.OUT_CLEAR = bus->sda_bit;
}
static /* IRAM */ int _i2c_arbitration_lost (_i2c_bus_t* bus, const char* func)
{
DEBUG("%s: arbitration lost dev=%u\n", func, bus->dev);
/* reset SCL and SDA to passive HIGH (floating and pulled-up) */
_i2c_set_sda (bus);
_i2c_set_scl (bus);
/* reset repeated start indicator */
bus->started = false;
return -EAGAIN;
}
static /* IRAM */ int _i2c_start_cond(_i2c_bus_t* bus)
{
/*
* send start condition
* on entry: SDA and SCL are set to be floating and pulled-up to high
* on exit : SDA and SCL are actively driven to low
*/
int res = 0;
if (bus->started) {
/* prepare the repeated start condition */
/* SDA = passive HIGH (floating and pulled-up) */
_i2c_set_sda (bus);
/* t_VD;DAT not neccessary */
/* _i2c_delay (bus); */
/* SCL = passive HIGH (floating and pulled-up) */
_i2c_set_scl (bus);
/* clock stretching, wait as long as clock is driven to low by the slave */
uint32_t stretch = I2C_CLOCK_STRETCH;
while (!_i2c_read_scl (bus) && stretch--) {}
if (stretch == 0) {
DEBUG("%s: clock stretching timeout dev=%u\n", __func__, bus->dev);
res = -ETIMEDOUT;
}
/* wait t_SU;STA - set-up time for a repeated START condition */
/* min. in us: 4.7 (SM), 0.6 (FM), 0.26 (FPM), 0.16 (HSM); no max. */
_i2c_delay (bus);
}
/* if SDA is low, arbitration is lost and someone else is driving the bus */
if (!_i2c_read_sda (bus)) {
return _i2c_arbitration_lost (bus, __func__);
}
/* begin the START condition: SDA = active LOW */
_i2c_clear_sda (bus);
/* wait t_HD;STA - hold time (repeated) START condition, */
/* max none */
/* min 4.0 us (SM), 0.6 us (FM), 0.26 us (FPM), 0.16 us (HSM) */
_i2c_delay (bus);
/* complete the START condition: SCL = active LOW */
_i2c_clear_scl (bus);
/* needed for repeated start condition */
bus->started = true;
return res;
}
static /* IRAM */ int _i2c_stop_cond(_i2c_bus_t* bus)
{
/*
* send stop condition
* on entry: SCL is active low and SDA can be changed
* on exit : SCL and SDA are set to be floating and pulled-up to high
*/
int res = 0;
/* begin the STOP condition: SDA = active LOW */
_i2c_clear_sda (bus);
/* wait t_LOW - LOW period of SCL clock */
/* min. in us: 4.7 (SM), 1.3 (FM), 0.5 (FPM), 0.16 (HSM); no max. */
_i2c_delay (bus);
/* SCL = passive HIGH (floating and pulled up) while SDA = active LOW */
_i2c_set_scl (bus);
/* clock stretching, wait as long as clock is driven to low by the slave */
uint32_t stretch = I2C_CLOCK_STRETCH;
while (!_i2c_read_scl (bus) && stretch--) {}
if (stretch == 0) {
DEBUG("%s: clock stretching timeout dev=%u\n", __func__, bus->dev);
res = -ETIMEDOUT;
}
/* wait t_SU;STO - hold time (repeated) START condition, */
/* min. in us: 4.0 (SM), 0.6 (FM), 0.26 (FPM), 0.16 (HSM); no max. */
_i2c_delay (bus);
/* complete the STOP condition: SDA = passive HIGH (floating and pulled up) */
_i2c_set_sda (bus);
/* reset repeated start indicator */
bus->started = false;
/* wait t_BUF - bus free time between a STOP and a START condition */
/* min. in us: 4.7 (SM), 1.3 (FM), 0.5 (FPM), 0.16 (HSM); no max. */
_i2c_delay (bus);
/* if SDA is low, arbitration is lost and someone else is driving the bus */
if (_i2c_read_sda (bus) == 0) {
return _i2c_arbitration_lost (bus, __func__);
}
return res;
}
static /* IRAM */ int _i2c_write_bit (_i2c_bus_t* bus, bool bit)
{
/*
* send one bit
* on entry: SCL is active low, SDA can be changed
* on exit : SCL is active low, SDA can be changed
*/
int res = 0;
/* SDA = bit */
if (bit) {
_i2c_set_sda (bus);
}
else {
_i2c_clear_sda (bus);
}
/* wait t_VD;DAT - data valid time (time until data are valid) */
/* max. in us: 3.45 (SM), 0.9 (FM), 0.45 (FPM); no min */
_i2c_delay (bus);
/* SCL = passive HIGH (floating and pulled-up), SDA value is available */
_i2c_set_scl (bus);
/* wait t_HIGH - time for the slave to read SDA */
/* min. in us: 4 (SM), 0.6 (FM), 0.26 (FPM), 0.09 (HSM); no max. */
_i2c_delay (bus);
/* clock stretching, wait as long as clock is driven low by the slave */
uint32_t stretch = I2C_CLOCK_STRETCH;
while (!_i2c_read_scl (bus) && stretch--) {}
if (stretch == 0) {
DEBUG("%s: clock stretching timeout dev=%u\n", __func__, bus->dev);
res = -ETIMEDOUT;
}
/* if SCL is high, now data is valid */
/* if SDA is high, check that nobody else is driving SDA low */
if (bit && !_i2c_read_sda(bus)) {
return _i2c_arbitration_lost (bus, __func__);
}
/* SCL = active LOW to allow next SDA change */
_i2c_clear_scl(bus);
return res;
}
static /* IRAM */ int _i2c_read_bit (_i2c_bus_t* bus, bool* bit)
{
/* read one bit
* on entry: SCL is active low, SDA can be changed
* on exit : SCL is active low, SDA can be changed
*/
int res = 0;
/* SDA = passive HIGH (floating and pulled-up) to let the slave drive data */
_i2c_set_sda (bus);
/* wait t_VD;DAT - data valid time (time until data are valid) */
/* max. in us: 3.45 (SM), 0.9 (FM), 0.45 (FPM); no min */
_i2c_delay (bus);
/* SCL = passive HIGH (floating and pulled-up), SDA value is available */
_i2c_set_scl (bus);
/* clock stretching, wait as long as clock is driven to low by the slave */
uint32_t stretch = I2C_CLOCK_STRETCH;
while (!_i2c_read_scl (bus) && stretch--) {}
if (stretch == 0) {
DEBUG("%s: clock stretching timeout dev=%u\n", __func__, bus->dev);
res = -ETIMEDOUT;
}
/* wait t_HIGH - time for the slave to read SDA */
/* min. in us: 4 (SM), 0.6 (FM), 0.26 (FPM), 0.09 (HSM); no max. */
_i2c_delay (bus);
/* SCL is high, read out bit */
*bit = _i2c_read_sda (bus);
/* SCL = active LOW to allow next SDA change */
_i2c_clear_scl(bus);
return res;
}
static /* IRAM */ int _i2c_write_byte (_i2c_bus_t* bus, uint8_t byte)
{
/* send one byte and returns 0 in case of ACK from slave */
/* send the byte from MSB to LSB */
for (unsigned i = 0; i < 8; i++) {
int res = _i2c_write_bit(bus, (byte & 0x80) != 0);
if (res != 0) {
return res;
}
byte = byte << 1;
}
/* read acknowledge bit (low) from slave */
bool bit;
int res = _i2c_read_bit (bus, &bit);
if (res != 0) {
return res;
}
return !bit ? 0 : -EIO;
}
static /* IRAM */ int _i2c_read_byte(_i2c_bus_t* bus, uint8_t *byte, bool ack)
{
bool bit;
/* read the byte */
for (unsigned i = 0; i < 8; i++) {
int res = _i2c_read_bit (bus, &bit);
if (res != 0) {
return res;
}
*byte = (*byte << 1) | bit;
}
/* write acknowledgement flag */
_i2c_write_bit(bus, !ack);
return 0;
}
void i2c_print_config(void)
{
for (unsigned bus = 0; bus < I2C_NUMOF; bus++) {
LOG_INFO("\tI2C_DEV(%d): scl=%d sda=%d\n",
bus, _i2c_bus[bus].scl, _i2c_bus[bus].sda);
}
}
#else /* if defined(I2C_NUMOF) && I2C_NUMOF */
void i2c_print_config(void)
{
LOG_INFO("\tI2C: no devices\n");
}
#endif /* if defined(I2C_NUMOF) && I2C_NUMOF */

71
cpu/esp8266/periph/pm.c Normal file
View File

@ -0,0 +1,71 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266
* @ingroup drivers_periph_pm
* @{
*
* @file
* @brief Implementation of power management functions
*
* @author Gunar Schorcht <gunar@schorcht.net>
* @}
*/
#define ENABLE_DEBUG 0
#include "debug.h"
#include <stdbool.h>
#include "irq.h"
#include "esp/xtensa_ops.h"
#include "sdk/ets_task.h"
#include "sdk/sdk.h"
#include "syscalls.h"
void pm_set_lowest(void)
{
DEBUG ("%s\n", __func__);
/* execute all pending system tasks before going to sleep */
/* is it really necessary, the timer interrupt is thrown every some ms? */
ets_tasks_run ();
#if !defined(QEMU)
DEBUG ("%s enter to sleep @%u\n", __func__, phy_get_mactime());
/* passive wait for interrupt to leave lowest power mode */
__asm__ volatile ("waiti 0");
DEBUG ("%s exit from sleep @%u\n", __func__, phy_get_mactime());
#endif
/*
* We could execute all pending system tasks after an interrupt before
* continuing RIOT. However, to give RIOT tasks the highest priority,
* *ets_tasks_run* should be called only before going to sleep
*/
ets_tasks_run ();
}
void pm_off(void)
{
DEBUG ("%s\n", __func__);
system_deep_sleep(0);
}
void pm_reboot(void)
{
DEBUG ("%s\n", __func__);
/* shut down WIFI and call system_restart_local after timer */
system_restart ();
}

238
cpu/esp8266/periph/pwm.c Normal file
View File

@ -0,0 +1,238 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266
* @ingroup drivers_periph_pwm
* @{
*
* @file
* @brief Low-level PWM driver implementation
*
* @author Gunar Schorcht <gunar@schorcht.net>
* @}
*/
#define ENABLE_DEBUG 0
#include "debug.h"
#include "cpu.h"
#include "log.h"
#include "irq_arch.h"
#include "periph/pwm.h"
#include "periph/gpio.h"
#include "common.h"
#include "esp/iomux_regs.h"
#include "esp/timer_regs.h"
#include "gpio_common.h"
#include "sdk/ets.h"
#if defined(PWM_NUMOF) && PWM_NUMOF > 0
#define TIMER_FRC1_CLKDIV_16 BIT(2)
#define TIMER_FRC1_CLKDIV_256 BIT(3)
#define ETS_FRC1_INT_ENABLE ETS_FRC1_INTR_ENABLE
#define ETS_FRC1_INT_DISABLE ETS_FRC1_INTR_DISABLE
#define ETS_FRC1_INT_ATTACH ETS_FRC_TIMER1_INTR_ATTACH
#define ETS_FRC1_NMI_ATTACH ETS_FRC_TIMER1_NMI_INTR_ATTACH
typedef struct
{
uint16_t duty;
uint32_t next_on;
uint32_t next_off;
gpio_t gpio;
} _pwm_chn_t;
typedef struct
{
pwm_mode_t mode;
uint16_t res;
uint32_t load;
uint32_t cycles;
uint8_t chn_num;
_pwm_chn_t chn[PWM_CHANNEL_NUM_MAX];
} _pwm_dev_t;
static _pwm_dev_t _pwm_dev;
static const uint32_t _pwm_channel_gpios[] = PWM0_CHANNEL_GPIOS;
static void _pwm_timer_handler (void* arg)
{
irq_isr_enter ();
_pwm_dev.cycles++;
for (int i = 0; i < _pwm_dev.chn_num; i++) {
if (_pwm_dev.chn[i].duty != 0 &&
_pwm_dev.chn[i].next_on == _pwm_dev.cycles) {
gpio_set (_pwm_dev.chn[i].gpio);
_pwm_dev.chn[i].next_on += _pwm_dev.res;
}
else if (_pwm_dev.chn[i].duty < _pwm_dev.res &&
_pwm_dev.chn[i].next_off == _pwm_dev.cycles) {
gpio_clear (_pwm_dev.chn[i].gpio);
_pwm_dev.chn[i].next_off += _pwm_dev.res;
}
}
irq_isr_exit ();
}
static void _pwm_start(void)
{
/* enable the timer and the interrupt and load the counter */
TIMER_FRC1.CTRL = TIMER_FRC1_CLKDIV_16 | TIMER_CTRL_RELOAD | TIMER_CTRL_RUN;
TM1_EDGE_INT_ENABLE();
ETS_FRC1_INT_ENABLE();
TIMER_FRC1.LOAD = _pwm_dev.load;
_pwm_dev.cycles = 0;
/* set the duty for all channels to start them */
for (int i = 0; i < _pwm_dev.chn_num; i++) {
pwm_set(PWM_DEV(0), i, _pwm_dev.chn[i].duty);
}
}
static void _pwm_stop(void)
{
/* disable the interrupt and the timer */
ETS_FRC1_INT_DISABLE();
TM1_EDGE_INT_DISABLE();
TIMER_FRC1.CTRL &= ~TIMER_CTRL_RUN;
}
#define PWM_MAX_CPS 100000UL /* maximum cycles per second */
uint32_t pwm_init(pwm_t pwm, pwm_mode_t mode, uint32_t freq, uint16_t res)
{
DEBUG ("%s pwm=%u mode=%u freq=%u, res=%u\n", __func__, pwm, mode, freq, res);
uint8_t _pwm_channel_gpio_num = sizeof(_pwm_channel_gpios) >> 2;
CHECK_PARAM_RET (pwm < PWM_NUMOF, 0);
CHECK_PARAM_RET (freq > 0, 0);
CHECK_PARAM_RET (_pwm_channel_gpio_num <= PWM_CHANNEL_NUM_MAX, 0);
/* maximum number of cycles per second (freq*res) should not be greater than */
/* 100.000 (period of 10 us), reduce freq if neccessary and keep resolution */
if (res * freq > PWM_MAX_CPS) {
freq = PWM_MAX_CPS / res;
}
_pwm_dev.load = 5e6 / freq / res; /* load value for FRC1 at TIMER_FRC1_CLKDIV_16 */
_pwm_dev.res = res;
_pwm_dev.chn_num = 0;
_pwm_dev.cycles = 0;
_pwm_dev.mode = mode;
for (int i = 0; i < _pwm_channel_gpio_num; i++) {
if (_gpio_pin_usage[_pwm_channel_gpios[i]] != _GPIO) {
LOG_ERROR("GPIO%d is used for something else and cannot be used as PWM output\n", i);
return 0;
}
if (gpio_init(_pwm_channel_gpios[i], GPIO_OUT) < 0) {
return 0;
}
gpio_clear (_pwm_channel_gpios[i]);
_pwm_dev.chn[_pwm_dev.chn_num].duty = 0;
_pwm_dev.chn[_pwm_dev.chn_num].next_on = 0;
_pwm_dev.chn[_pwm_dev.chn_num].next_off = 0;
_pwm_dev.chn[_pwm_dev.chn_num].gpio = _pwm_channel_gpios[i];
_pwm_dev.chn_num++;
}
TIMER_FRC1.CTRL = TIMER_FRC1_CLKDIV_16 | TIMER_CTRL_RELOAD | TIMER_CTRL_RUN;
ETS_FRC1_INT_ATTACH(_pwm_timer_handler,0);
TM1_EDGE_INT_ENABLE();
ETS_FRC1_INT_ENABLE();
TIMER_FRC1.LOAD = _pwm_dev.load;
return freq;
}
uint8_t pwm_channels(pwm_t pwm)
{
CHECK_PARAM_RET (pwm < PWM_NUMOF, 0);
return _pwm_dev.chn_num;
}
void pwm_set(pwm_t pwm, uint8_t channel, uint16_t value)
{
DEBUG("%s pwm=%u channel=%u value=%u\n", __func__, pwm, channel, value);
CHECK_PARAM (pwm < PWM_NUMOF);
CHECK_PARAM (channel < _pwm_dev.chn_num);
CHECK_PARAM (value <= _pwm_dev.res);
uint32_t state = irq_disable();
uint32_t phase = _pwm_dev.cycles - _pwm_dev.cycles % _pwm_dev.res + _pwm_dev.res;
switch (_pwm_dev.mode) {
case PWM_LEFT:
_pwm_dev.chn[channel].next_on = phase;
break;
case PWM_RIGHT:
_pwm_dev.chn[channel].next_on = phase + _pwm_dev.res - value;
break;
case PWM_CENTER:
_pwm_dev.chn[channel].next_on = phase + (_pwm_dev.res - value) / 2;
break;
}
_pwm_dev.chn[channel].next_off = _pwm_dev.chn[channel].next_on + value;
_pwm_dev.chn[channel].duty = value;
irq_restore(state);
}
void pwm_poweron(pwm_t pwm)
{
CHECK_PARAM (pwm < PWM_NUMOF);
_pwm_start();
}
void pwm_poweroff(pwm_t pwm)
{
CHECK_PARAM (pwm < PWM_NUMOF);
_pwm_stop ();
}
void pwm_print_config(void)
{
LOG_INFO("\tPWM_DEV(0): channels=[ ");
for (unsigned i = 0; i < sizeof(_pwm_channel_gpios) >> 2; i++) {
LOG_INFO("%d ", _pwm_channel_gpios[i]);
}
LOG_INFO("]\n");
}
#else /* defined(PWM_NUMOF) && PWM_NUMOF > 0 */
void pwm_print_config(void)
{
LOG_INFO("\tPWM: no devices\n");
}
#endif /* defined(PWM_NUMOF) && PWM_NUMOF > 0 */

91
cpu/esp8266/periph/rtc.c Normal file
View File

@ -0,0 +1,91 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266
* @ingroup drivers_periph_rtc
* @{
*
* @file
* @brief Low-level RTC driver implementation using ESP8266 SDK
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
#define ENABLE_DEBUG (0)
#include "debug.h"
#include "cpu.h"
#include "log.h"
#include "periph/rtc.h"
#include "common.h"
#include "sdk/ets.h"
void rtc_init(void)
{
/* TODO implement */
NOT_YET_IMPLEMENTED();
}
void rtc_poweron(void)
{
/* TODO implement */
NOT_YET_IMPLEMENTED();
}
void rtc_poweroff(void)
{
/* TODO implement */
NOT_YET_IMPLEMENTED();
}
int rtc_set_time(struct tm *ttime)
{
(void) time;
/* TODO implement */
NOT_YET_IMPLEMENTED();
return -1;
}
int rtc_get_time(struct tm *ttime)
{
(void) time;
/* TODO implement */
NOT_YET_IMPLEMENTED();
return -1;
}
int rtc_set_alarm(struct tm *time, rtc_alarm_cb_t cb, void *arg)
{
(void) time;
(void) cb;
(void) arg;
/* TODO implement */
NOT_YET_IMPLEMENTED();
return -1;
}
int rtc_get_alarm(struct tm *time)
{
(void) time;
/* TODO implement */
NOT_YET_IMPLEMENTED();
return -1;
}
void rtc_clear_alarm(void)
{
/* TODO implement */
NOT_YET_IMPLEMENTED();
}

416
cpu/esp8266/periph/spi.c Normal file
View File

@ -0,0 +1,416 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266
* @ingroup drivers_periph_spi
* @{
*
* @file
* @brief Low-level SPI driver implementation
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
#define ENABLE_DEBUG (0)
#include "debug.h"
#include "common.h"
#include "log.h"
#if defined(MODULE_PERIPH_SPI)
#include <string.h>
#include "cpu.h"
#include "mutex.h"
#include "periph/spi.h"
#include "esp/iomux_regs.h"
#include "esp/spi_regs.h"
#include "gpio_common.h"
#define SPI_BUS_NUM 2
#define SPI_BLOCK_SIZE 64 /* number of bytes per SPI transfer */
static mutex_t _spi_lock[SPI_BUS_NUM] = { MUTEX_INIT };
/* indicate whether SPI interface were already initilized */
static bool _spi_initialized[SPI_BUS_NUM] = { false };
/* indicate whether pins of the SPI interface were already initilized */
static bool _spi_pins_initialized[SPI_BUS_NUM] = { false };
/*
* GPIOs that were once initialized as SPI interface pins can not be used
* afterwards for anything else. Therefore, SPI interfaces are not initialized
* until they are used for the first time. The *spi_init* function is just a
* dummy for source code compatibility. The initialization of an SPI interface
* is performed by the *_spi_init_internal* function, which is called either by
* the *spi_init_cs* function or the *spi_acquire* function when the interface
* is used for the first time.
*/
void IRAM spi_init (spi_t bus)
{
return;
}
void _spi_init_internal(spi_t bus)
{
/* only one physical SPI(1) bus (HSPI) can be used for peripherals */
/* RIOT's SPI_DEV(0) is mapped to SPI(1) bus (HSPI) */
/* TODO SPI overlap mode SPI and HSPI */
CHECK_PARAM (bus == SPI_DEV(0));
/* avoid multiple initializations */
if (_spi_initialized[bus]) {
return;
}
_spi_initialized[bus] = true;
DEBUG("%s bus=%u\n", __func__, bus);
/* initialize pins */
spi_init_pins(bus);
/* check whether pins could be initialized, otherwise return, CS is not
initialized in spi_init_pins */
if (_gpio_pin_usage[SPI0_SCK_GPIO] != _SPI &&
_gpio_pin_usage[SPI0_MOSI_GPIO] != _SPI &&
_gpio_pin_usage[SPI0_MISO_GPIO] != _SPI) {
return;
}
/* set bus into a defined state */
SPI(bus).USER0 = SPI_USER0_MOSI | SPI_USER0_CLOCK_IN_EDGE | SPI_USER0_DUPLEX;
SPI(bus).USER0 |= SPI_USER0_CS_SETUP | SPI_USER0_CS_HOLD;
/* set byte order to little endian for read and write operations */
SPI(bus).USER0 &= ~(SPI_USER0_WR_BYTE_ORDER | SPI_USER0_RD_BYTE_ORDER);
/* set bit order to most significant first for read and write operations */
SPI(bus).CTRL0 = 0; /* ~(SPI_CTRL0_WR_BIT_ORDER | SPI_CTRL0_RD_BIT_ORDER); */
DEBUG("%s SPI(bus).USER0=%08x SPI(bus).CTRL0=%08x\n",
__func__, SPI(bus).USER0, SPI(bus).CTRL0);
}
void spi_init_pins(spi_t bus)
{
/* see spi_init */
CHECK_PARAM (bus == SPI_DEV(0));
/* call initialization of the SPI interface if it is not initialized yet */
if (!_spi_initialized[bus]) {
_spi_init_internal(bus);
}
/* avoid multiple pin initializations */
if (_spi_pins_initialized[bus]) {
return;
}
_spi_pins_initialized[bus] = true;
DEBUG("%s bus=%u\n", __func__, bus);
uint32_t iomux_func = (bus == 0) ? IOMUX_FUNC(1) : IOMUX_FUNC(2);
/*
* CS is handled as normal GPIO ouptut. Due to the small number of GPIOs
* we have, we do not initialize the default CS pin here. Either the app
* uses spi_init_cs to initialize the CS pin explicitly, or we initialize
* the default CS when spi_aquire is used first time.
*/
IOMUX.PIN[_gpio_to_iomux[SPI0_MISO_GPIO]] &= ~IOMUX_PIN_FUNC_MASK;
IOMUX.PIN[_gpio_to_iomux[SPI0_MOSI_GPIO]] &= ~IOMUX_PIN_FUNC_MASK;
IOMUX.PIN[_gpio_to_iomux[SPI0_SCK_GPIO]] &= ~IOMUX_PIN_FUNC_MASK;
IOMUX.PIN[_gpio_to_iomux[SPI0_MISO_GPIO]] |= iomux_func;
IOMUX.PIN[_gpio_to_iomux[SPI0_MOSI_GPIO]] |= iomux_func;
IOMUX.PIN[_gpio_to_iomux[SPI0_SCK_GPIO]] |= iomux_func;
_gpio_pin_usage [SPI0_MISO_GPIO] = _SPI; /* pin cannot be used for anything else */
_gpio_pin_usage [SPI0_MOSI_GPIO] = _SPI; /* pin cannot be used for anything else */
_gpio_pin_usage [SPI0_SCK_GPIO] = _SPI; /* pin cannot be used for anything else */
}
int spi_init_cs(spi_t bus, spi_cs_t cs)
{
DEBUG("%s bus=%u cs=%u\n", __func__, bus, cs);
/* see spi_init */
CHECK_PARAM_RET (bus == SPI_DEV(0), SPI_NODEV);
/* call initialization of the SPI interface if it is not initialized yet */
if (!_spi_initialized[bus]) {
_spi_init_internal(bus);
}
/* return if pin is already initialized as SPI CS signal */
if (_gpio_pin_usage [cs] == _SPI) {
return SPI_OK;
}
if (_gpio_pin_usage [cs] != _GPIO) {
return SPI_NOCS;
}
gpio_init(cs, GPIO_OUT);
gpio_set (cs);
_gpio_pin_usage [cs] = _SPI; /* pin cannot be used for anything else */
return SPI_OK;
}
int spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk)
{
DEBUG("%s bus=%u cs=%u mode=%u clk=%u\n", __func__, bus, cs, mode, clk);
/* see spi_init */
CHECK_PARAM_RET (bus == SPI_DEV(0), SPI_NODEV);
/* call initialization of the SPI interface if it is not initialized yet */
if (!_spi_initialized[bus]) {
_spi_init_internal(bus);
}
/* if parameter cs is GPIO_UNDEF, the default CS pin is used */
cs = (cs == GPIO_UNDEF) ? SPI0_CS0_GPIO : cs;
/* if the CS pin used is not yet initialized, we do it now */
if (_gpio_pin_usage[cs] != _SPI && spi_init_cs(bus, cs) != SPI_OK) {
LOG_ERROR("SPI_DEV(%d) CS signal could not be initialized\n", bus);
return SPI_NOCS;
}
/* lock the bus */
mutex_lock(&_spi_lock[bus]);
/* set SPI mode */
bool cpha = (mode == SPI_MODE_1 || mode == SPI_MODE_3);
bool cpol = (mode == SPI_MODE_2 || mode == SPI_MODE_3);
if (cpol) {
cpha = !cpha; /* CPHA must be inverted when CPOL = 1 */
}
if (cpha) {
SPI(bus).USER0 |= SPI_USER0_CLOCK_OUT_EDGE;
}
else {
SPI(bus).USER0 &= ~SPI_USER0_CLOCK_OUT_EDGE;
}
if (cpol) {
SPI(bus).PIN |= SPI_PIN_IDLE_EDGE;
}
else {
SPI(bus).PIN &= ~SPI_PIN_IDLE_EDGE;
}
/* set SPI clock
* see ESP8266 Technical Reference Appendix 2 - SPI registers
* https://www.espressif.com/sites/default/files/documentation/esp8266-technical_reference_en.pdf
*/
uint32_t spi_clkdiv_pre;
uint32_t spi_clkcnt_N;
switch (clk) {
case SPI_CLK_10MHZ: spi_clkdiv_pre = 2; /* predivides 80 MHz to 40 MHz */
spi_clkcnt_N = 4; /* 4 cycles results into 10 MHz */
break;
case SPI_CLK_5MHZ: spi_clkdiv_pre = 2; /* predivides 80 MHz to 40 MHz */
spi_clkcnt_N = 8; /* 8 cycles results into 5 MHz */
break;
case SPI_CLK_1MHZ: spi_clkdiv_pre = 2; /* predivides 80 MHz to 40 MHz */
spi_clkcnt_N = 40; /* 40 cycles results into 1 MHz */
break;
case SPI_CLK_400KHZ: spi_clkdiv_pre = 20; /* predivides 80 MHz to 4 MHz */
spi_clkcnt_N = 10; /* 10 cycles results into 400 kHz */
break;
case SPI_CLK_100KHZ: spi_clkdiv_pre = 20; /* predivides 80 MHz to 4 MHz */
spi_clkcnt_N = 40; /* 20 cycles results into 100 kHz */
break;
default: spi_clkdiv_pre = 20; /* predivides 80 MHz to 4 MHz */
spi_clkcnt_N = 40; /* 20 cycles results into 100 kHz */
}
/* register values are set to deviders-1 */
spi_clkdiv_pre--;
spi_clkcnt_N--;
DEBUG("%s spi_clkdiv_prev=%u spi_clkcnt_N=%u\n", __func__, spi_clkdiv_pre, spi_clkcnt_N);
/* SPI clock is derived from system bus frequency and should not be affected by */
/* CPU clock */
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, spi_clkdiv_pre) |
VAL2FIELD_M (SPI_CLOCK_COUNT_NUM, spi_clkcnt_N) |
VAL2FIELD_M (SPI_CLOCK_COUNT_HIGH, (spi_clkcnt_N+1)/2-1) |
VAL2FIELD_M (SPI_CLOCK_COUNT_LOW, spi_clkcnt_N);
DEBUG("%s IOMUX.CONF=%08x SPI(bus).CLOCK=%08x\n",
__func__, IOMUX.CONF, SPI(bus).CLOCK);
return SPI_OK;
}
void spi_release(spi_t bus)
{
/* see spi_init */
CHECK_PARAM (bus == SPI_DEV(0));
/* release the bus */
mutex_unlock(&_spi_lock[bus]);
}
/*
* Following functions are from the hardware SPI driver of the esp-open-rtos
* project.
*
* Copyright (c) Ruslan V. Uss, 2016
* BSD Licensed as described in the file LICENSE
* https://github.com/SuperHouse/esp-open-rtos/blob/master/LICENSE
*/
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 void _store_data(uint8_t bus, const void *data, size_t len)
{
uint8_t words = len / 4;
uint8_t tail = len % 4;
memcpy((void *)SPI(bus).W, data, len - tail);
if (!tail) {
return;
}
uint32_t last = 0;
uint8_t *offs = (uint8_t *)data + len - tail;
for (uint8_t i = 0; i < tail; i++) {
last = last | (offs[i] << (i * 8));
}
SPI(bus).W[words] = last;
}
static const uint8_t spi_empty_out[SPI_BLOCK_SIZE] = { 0 };
static void _spi_buf_transfer(uint8_t bus, const void *out, void *in, size_t len)
{
DEBUG("%s bus=%u out=%p in=%p len=%u\n", __func__, bus, out, in, len);
/* transfer one block data */
_wait(bus);
_set_size(bus, len);
_store_data(bus, out ? out : spi_empty_out, len);
_start(bus);
_wait(bus);
if (in) {
memcpy(in, (void *)SPI(bus).W, len);
}
}
void spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont,
const void *out, void *in, size_t len)
{
/* see spi_init */
CHECK_PARAM (bus == SPI_DEV(0));
DEBUG("%s bus=%u cs=%u cont=%d out=%p in=%p len=%u\n",
__func__, bus, cs, cont, out, in, len);
if (!len) {
return;
}
#if ENABLE_DEBUG
if (out) {
DEBUG("out = ");
for (size_t i = 0; i < len; i++) {
DEBUG("%02x ", ((const uint8_t *)out)[i]);
}
DEBUG("\n");
}
#endif
if (cs != SPI_CS_UNDEF) {
gpio_clear (cs);
}
size_t blocks = len / SPI_BLOCK_SIZE;
uint8_t tail = len % SPI_BLOCK_SIZE;
DEBUG("%s bus=%u cs=%u blocks=%d tail=%d\n",
__func__, bus, cs, blocks, tail);
for (size_t i = 0; i < blocks; i++) {
_spi_buf_transfer(bus,
out ? (const uint8_t *)out + i * SPI_BLOCK_SIZE : NULL,
in ? (uint8_t *)in + i * SPI_BLOCK_SIZE : NULL, SPI_BLOCK_SIZE);
}
if (tail) {
_spi_buf_transfer(bus,
out ? (const uint8_t *)out + blocks * SPI_BLOCK_SIZE : 0,
in ? (uint8_t *)in + blocks * SPI_BLOCK_SIZE : NULL, tail);
}
if (!cont && (cs != SPI_CS_UNDEF)) {
gpio_set (cs);
}
#if ENABLE_DEBUG
if (in) {
DEBUG("in = ");
for (size_t i = 0; i < len; i++) {
DEBUG("%02x ", ((const uint8_t *)in)[i]);
}
DEBUG("\n");
}
#endif
}
void spi_print_config(void)
{
LOG_INFO("\tSPI_DEV(0): ");
LOG_INFO("sck=%d " , SPI0_SCK_GPIO);
LOG_INFO("miso=%d ", SPI0_MISO_GPIO);
LOG_INFO("mosi=%d ", SPI0_MOSI_GPIO);
LOG_INFO("cs=%d\n" , SPI0_CS0_GPIO);
}
#else /* MODULE_PERIPH_SPI */
void spi_print_config(void)
{
LOG_INFO("\tSPI: no devices\n");
}
#endif /* MODULE_PERIPH_SPI */

571
cpu/esp8266/periph/timer.c Normal file
View File

@ -0,0 +1,571 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266
* @ingroup drivers_periph_timer
* @{
*
* @file
* @brief Low-level timer driver implementation using ESP8266 SDK
*
* @author Gunar Schorcht <gunar@schorcht.net>
* @}
*/
/* WARNING! enable debugging will have timing side effects and can lead
* to timer underflows, system crashes or system dead locks in worst case. */
#define ENABLE_DEBUG 0
#include "debug.h"
#include "log.h"
#include "xtimer.h"
#include "periph/timer.h"
#include "common.h"
#include "irq_arch.h"
#include "esp/common_macros.h"
#include "sdk/sdk.h"
#include "xtensa/hal.h"
#if !defined(MODULE_ESP_SW_TIMER)
/* hardware timer used */
#define HW_TIMER_NUMOF 1
#define HW_TIMER_CHANNELS 1
#define HW_TIMER_MASK 0xffffffff
#define HW_TIMER_DELTA_MAX 0x00ffffff /* in us */
#define HW_TIMER_DELTA_MIN 0x00000001 /* in us */
#define HW_TIMER_DELTA_MASK 0x00ffffff
#define HW_TIMER_DELTA_RSHIFT 24
#define HW_TIMER_CORRECTION 2 /* overhead in us */
#define HW_TIMER_CLOCK (APB_CLK_FREQ)
#define US_TO_HW_TIMER_TICKS(t) (t * system_get_cpu_freq())
#define HW_TIMER_TICKS_TO_US(t) (t / system_get_cpu_freq())
struct hw_channel_t
{
bool used; /* indicates whether the channel is used */
uint32_t start_time; /* physical time when the timer channel has been started */
uint32_t delta_time; /* timer delta value (delta = cycles * timer_max + remainder) */
uint32_t cycles; /* number of complete max timer cycles */
uint32_t remainder; /* remainder timer value */
};
struct hw_timer_t
{
bool initialized; /* indicates whether timer is already initialized */
bool started; /* indicates whether timer is already started */
timer_isr_ctx_t isr_ctx;
struct hw_channel_t channels[HW_TIMER_CHANNELS];
};
static struct hw_timer_t timers[HW_TIMER_NUMOF] = { };
static void __timer_channel_start (struct hw_timer_t* timer, struct hw_channel_t* channel);
static void __timer_channel_stop (struct hw_timer_t* timer, struct hw_channel_t* channel);
static uint32_t __hw_timer_ticks_max;
static uint32_t __hw_timer_ticks_min;
void IRAM hw_timer_handler(void* arg)
{
uint32_t dev = (uint32_t)arg >> 4;
uint32_t chn = (uint32_t)arg & 0xf;
if (dev >= HW_TIMER_NUMOF && chn >= HW_TIMER_CHANNELS) {
return;
}
irq_isr_enter();
DEBUG("%s arg=%p\n", __func__, arg);
struct hw_timer_t* timer = &timers[dev];
struct hw_channel_t* channel = &timer->channels[chn];
if (channel->cycles) {
channel->cycles--;
xthal_set_ccompare(0, __hw_timer_ticks_max + xthal_get_ccount());
}
else if (channel->remainder >= HW_TIMER_DELTA_MIN) {
xthal_set_ccompare (0, US_TO_HW_TIMER_TICKS(channel->remainder) + xthal_get_ccount());
channel->remainder = 0;
}
else {
channel->remainder = 0;
channel->used = false;
ets_isr_mask (BIT(ETS_CCOM_INUM));
xthal_set_ccompare (0, 0);
timer->isr_ctx.cb(timer->isr_ctx.arg, chn);
}
irq_isr_exit();
}
int timer_init (tim_t dev, unsigned long freq, timer_cb_t cb, void *arg)
{
DEBUG("%s dev=%u freq=%lu cb=%p arg=%p\n", __func__, dev, freq, cb, arg);
CHECK_PARAM_RET (dev < HW_TIMER_NUMOF, -1);
CHECK_PARAM_RET (freq == XTIMER_HZ_BASE, -1);
CHECK_PARAM_RET (cb != NULL, -1);
if (timers[dev].initialized) {
DEBUG("%s timer dev=%u is already initialized (used)\n", __func__, dev);
return -1;
}
timers[dev].initialized = true;
timers[dev].started = false;
timers[dev].isr_ctx.cb = cb;
timers[dev].isr_ctx.arg = arg;
ets_isr_attach (ETS_CCOM_INUM, hw_timer_handler, NULL);
for (int i = 0; i < HW_TIMER_CHANNELS; i++) {
timers[dev].channels[i].used = false;
timers[dev].channels[i].cycles = 0;
timers[dev].channels[i].remainder = 0;
}
timer_start(dev);
return 0;
}
int IRAM timer_set(tim_t dev, int chn, unsigned int delta)
{
DEBUG("%s dev=%u channel=%d delta=%u\n", __func__, dev, chn, delta);
CHECK_PARAM_RET (dev < HW_TIMER_NUMOF, -1);
CHECK_PARAM_RET (chn < HW_TIMER_CHANNELS, -1);
int state = irq_disable ();
struct hw_timer_t* timer = &timers[dev];
struct hw_channel_t* channel = &timer->channels[chn];
/* set delta time and channel used flag */
channel->delta_time = delta > HW_TIMER_CORRECTION ? delta - HW_TIMER_CORRECTION : 0;
channel->used = true;
/* start channel with new delta time */
__timer_channel_start (timer, channel);
irq_restore (state);
return 0;
}
int IRAM timer_set_absolute(tim_t dev, int chn, unsigned int value)
{
DEBUG("%s dev=%u channel=%d value=%u\n", __func__, dev, chn, value);
return timer_set (dev, chn, value - timer_read(dev));
}
int timer_clear(tim_t dev, int chn)
{
DEBUG("%s dev=%u channel=%d\n", __func__, dev, chn);
CHECK_PARAM_RET (dev < HW_TIMER_NUMOF, -1);
CHECK_PARAM_RET (chn < HW_TIMER_CHANNELS, -1);
int state = irq_disable ();
/* stop running timer channel */
__timer_channel_stop (&timers[dev], &timers[dev].channels[chn]);
irq_restore (state);
return 0;
}
unsigned int IRAM timer_read(tim_t dev)
{
(void)dev;
return phy_get_mactime ();
}
void IRAM timer_start(tim_t dev)
{
DEBUG("%s dev=%u @%u\n", __func__, dev, phy_get_mactime());
CHECK_PARAM (dev < HW_TIMER_NUMOF);
CHECK_PARAM (!timers[dev].started);
int state = irq_disable ();
__hw_timer_ticks_max = US_TO_HW_TIMER_TICKS(HW_TIMER_DELTA_MAX);
__hw_timer_ticks_min = US_TO_HW_TIMER_TICKS(HW_TIMER_DELTA_MIN);
struct hw_timer_t* timer = &timers[dev];
timer->started = true;
for (int i = 0; i < HW_TIMER_CHANNELS; i++) {
__timer_channel_start (timer, &timer->channels[i]);
}
irq_restore (state);
}
void IRAM timer_stop(tim_t dev)
{
DEBUG("%s dev=%u\n", __func__, dev);
CHECK_PARAM (dev < HW_TIMER_NUMOF);
int state = irq_disable ();
struct hw_timer_t* timer = &timers[dev];
timer->started = false;
for (int i = 0; i < HW_TIMER_CHANNELS; i++) {
__timer_channel_stop (timer, &timer->channels[i]);
}
irq_restore (state);
}
static void IRAM __timer_channel_start (struct hw_timer_t* timer, struct hw_channel_t* channel)
{
if (!timer->started || !channel->used) {
return;
}
/* save channel starting time */
channel->start_time = timer_read (0);
channel->cycles = channel->delta_time >> HW_TIMER_DELTA_RSHIFT;
channel->remainder = channel->delta_time & HW_TIMER_DELTA_MASK;
DEBUG("%s cycles=%u remainder=%u @%u\n",
__func__, channel->cycles, channel->remainder, phy_get_mactime());
/* start timer either with full cycles, remaining or minimum time */
if (channel->cycles) {
channel->cycles--;
xthal_set_ccompare(0, __hw_timer_ticks_max + xthal_get_ccount());
}
else if (channel->remainder >= HW_TIMER_DELTA_MIN) {
xthal_set_ccompare (0, US_TO_HW_TIMER_TICKS(channel->remainder) + xthal_get_ccount());
channel->remainder = 0;
}
else {
channel->remainder = 0;
xthal_set_ccompare(0, __hw_timer_ticks_min + xthal_get_ccount());
}
ets_isr_unmask (BIT(ETS_CCOM_INUM));
}
static void IRAM __timer_channel_stop (struct hw_timer_t* timer, struct hw_channel_t* channel)
{
if (!channel->used) {
return;
}
ets_isr_mask (BIT(ETS_CCOM_INUM));
/* compute elapsed time */
uint32_t elapsed_time = timer_read (0) - channel->start_time;
if (channel->delta_time > elapsed_time) {
/* compute new delta time if the timer has no been expired */
channel->delta_time -= elapsed_time;
}
else {
/* otherwise deactivate the channel */
channel->used = false;
}
}
void timer_print_config(void)
{
for (int i = 0; i < HW_TIMER_NUMOF; i++) {
LOG_INFO("\tTIMER_DEV(%d): %d channel(s)\n", i,
sizeof(timers[i].channels) / sizeof(struct hw_channel_t));
}
}
#else /* MODULE_ESP_SW_TIMER */
#ifndef MODULE_ESP_SDK
#error Software timers are not available in Non-SDK version, use USE_SDK=1 to enable SDK-version.
#else
/* software timer based on os_timer_arm functions */
#define OS_TIMER_NUMOF 1
#define OS_TIMER_CHANNELS 10
#define OS_TIMER_MASK 0xffffffff
#define OS_TIMER_DELTA_MAX 0x0000ffff
#define OS_TIMER_DELTA_MIN 0x00000064
#define OS_TIMER_DELTA_MASK 0x0000ffff
#define OS_TIMER_DELTA_RSHIFT 16
#define OS_TIMER_CORRECTION 4
/* Since hardware timer FRC1 is needed to implement PWM, we have to map our */
/* timer using the exsting ETS timer with 1 us clock rate */
struct phy_channel_t
{
bool used; /* indicates whether the channel is used */
uint32_t start_time; /* physical time when the timer channel has been started */
uint32_t delta_time; /* timer delta value (delta = cycles * timer_max + remainder) */
uint32_t cycles; /* number of complete max timer cycles */
uint32_t remainder; /* remainder timer value */
os_timer_t os_timer; /* used system software timer */
};
struct phy_timer_t
{
bool initialized; /* indicates whether timer is already initialized */
bool started; /* indicates whether timer is already started */
timer_isr_ctx_t isr_ctx;
struct phy_channel_t channels[OS_TIMER_CHANNELS];
};
static struct phy_timer_t timers[OS_TIMER_NUMOF] = { };
static void __timer_channel_start (struct phy_timer_t* timer, struct phy_channel_t* channel);
static void __timer_channel_stop (struct phy_timer_t* timer, struct phy_channel_t* channel);
/* Since we use ETS software timers, it is not really an ISR. Therefore */
/* we don't need to run in interrupt context. */
void IRAM os_timer_handler (void* arg)
{
uint32_t dev = (uint32_t)arg >> 4;
uint32_t chn = (uint32_t)arg & 0xf;
if (dev >= OS_TIMER_NUMOF && chn >= OS_TIMER_CHANNELS) {
return;
}
irq_isr_enter ();
struct phy_timer_t* timer = &timers[dev];
struct phy_channel_t* channel = &timer->channels[chn];
if (channel->cycles) {
channel->cycles--;
os_timer_arm_us (&channel->os_timer, OS_TIMER_DELTA_MAX, false);
}
else if (channel->remainder >= OS_TIMER_DELTA_MIN) {
os_timer_arm_us (&channel->os_timer, channel->remainder, false);
channel->remainder = 0;
}
else {
channel->remainder = 0;
channel->used = false;
timer->isr_ctx.cb(timer->isr_ctx.arg, chn);
}
irq_isr_exit ();
}
int timer_init (tim_t dev, unsigned long freq, timer_cb_t cb, void *arg)
{
DEBUG("%s dev=%u freq=%lu cb=%p arg=%p\n", __func__, dev, freq, cb, arg);
CHECK_PARAM_RET (dev < OS_TIMER_NUMOF, -1);
CHECK_PARAM_RET (freq == XTIMER_HZ_BASE, -1);
CHECK_PARAM_RET (cb != NULL, -1);
if (timers[dev].initialized) {
DEBUG("%s timer dev=%u is already initialized (used)\n", __func__, dev);
return -1;
}
timers[dev].initialized = true;
timers[dev].started = false;
timers[dev].isr_ctx.cb = cb;
timers[dev].isr_ctx.arg = arg;
for (int i = 0; i < OS_TIMER_CHANNELS; i++) {
os_timer_setfn(&timers[dev].channels[i].os_timer,
os_timer_handler, (void*)((dev << 4) | i));
timers[dev].channels[i].used = false;
timers[dev].channels[i].cycles = 0;
timers[dev].channels[i].remainder = 0;
}
timer_start(dev);
return 0;
}
int IRAM timer_set(tim_t dev, int chn, unsigned int delta)
{
DEBUG("%s dev=%u channel=%d delta=%u\n", __func__, dev, chn, delta);
CHECK_PARAM_RET (dev < OS_TIMER_NUMOF, -1);
CHECK_PARAM_RET (chn < OS_TIMER_CHANNELS, -1);
int state = irq_disable ();
struct phy_timer_t* timer = &timers[dev];
struct phy_channel_t* channel = &timer->channels[chn];
/* set delta time and channel used flag */
channel->delta_time = delta > OS_TIMER_CORRECTION ? delta - OS_TIMER_CORRECTION : 0;
channel->used = true;
/* start channel with new delta time */
__timer_channel_start (timer, channel);
irq_restore (state);
return 0;
}
int IRAM timer_set_absolute(tim_t dev, int chn, unsigned int value)
{
DEBUG("%s dev=%u channel=%d value=%u\n", __func__, dev, chn, value);
return timer_set (dev, chn, value - timer_read(dev));
}
int timer_clear(tim_t dev, int chn)
{
DEBUG("%s dev=%u channel=%d\n", __func__, dev, chn);
CHECK_PARAM_RET (dev < OS_TIMER_NUMOF, -1);
CHECK_PARAM_RET (chn < OS_TIMER_CHANNELS, -1);
int state = irq_disable ();
/* stop running timer channel */
__timer_channel_stop (&timers[dev], &timers[dev].channels[chn]);
irq_restore (state);
return 0;
}
unsigned int IRAM timer_read(tim_t dev)
{
(void)dev;
return phy_get_mactime ();
}
void IRAM timer_start(tim_t dev)
{
DEBUG("%s dev=%u\n", __func__, dev);
CHECK_PARAM (dev < OS_TIMER_NUMOF);
CHECK_PARAM (!timers[dev].started);
int state = irq_disable ();
struct phy_timer_t* timer = &timers[dev];
timer->started = true;
for (int i = 0; i < OS_TIMER_CHANNELS; i++) {
__timer_channel_start (timer, &timer->channels[i]);
}
irq_restore (state);
}
void IRAM timer_stop(tim_t dev)
{
DEBUG("%s dev=%u\n", __func__, dev);
CHECK_PARAM (dev < OS_TIMER_NUMOF);
int state = irq_disable ();
struct phy_timer_t* timer = &timers[dev];
timer->started = false;
for (int i = 0; i < OS_TIMER_CHANNELS; i++) {
__timer_channel_stop (timer, &timer->channels[i]);
}
irq_restore (state);
}
static void IRAM __timer_channel_start (struct phy_timer_t* timer, struct phy_channel_t* channel)
{
if (!timer->started || !channel->used) {
return;
}
/* disarm old timer if already started */
os_timer_disarm (&channel->os_timer);
/* save channel starting time */
channel->start_time = timer_read (0);
channel->cycles = channel->delta_time >> OS_TIMER_DELTA_RSHIFT;
channel->remainder = channel->delta_time & OS_TIMER_DELTA_MASK;
DEBUG("%s cycles=%u remainder=%u @%u\n",
__func__, channel->cycles, channel->remainder, phy_get_mactime());
/* start timer either with full cycles, remainder or minimum time */
if (channel->cycles) {
channel->cycles--;
os_timer_arm_us (&channel->os_timer, OS_TIMER_DELTA_MAX, false);
}
else if (channel->remainder > OS_TIMER_DELTA_MIN) {
os_timer_arm_us (&channel->os_timer, channel->remainder, false);
channel->remainder = 0;
}
else {
channel->remainder = 0;
os_timer_arm_us (&channel->os_timer, OS_TIMER_DELTA_MIN, false);
}
}
static void IRAM __timer_channel_stop (struct phy_timer_t* timer, struct phy_channel_t* channel)
{
if (!channel->used) {
return;
}
os_timer_disarm (&channel->os_timer);
/* compute elapsed time */
uint32_t elapsed_time = timer_read (0) - channel->start_time;
if (channel->delta_time > elapsed_time) {
/* compute new delta time if the timer has no been expired */
channel->delta_time -= elapsed_time;
}
else {
/* otherwise deactivate the channel */
channel->used = false;
}
}
void timer_print_config(void)
{
for (int i = 0; i < OS_TIMER_NUMOF; i++) {
LOG_INFO("\tTIMER_DEV(%d): %d channel(s)\n", i,
sizeof(timers[i].channels) / sizeof(struct phy_channel_t));
}
}
#endif /* NON_SDK */
#endif /* MODULE_ESP_SW_TIMER */

165
cpu/esp8266/periph/uart.c Normal file
View File

@ -0,0 +1,165 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266
* @ingroup drivers_periph_uart
* @{
*
* @file
* @brief Low-level UART driver implementation
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
#define ENABLE_DEBUG 0
#include "debug.h"
#include "common.h"
#include "cpu.h"
#include "irq_arch.h"
#include "log.h"
#include "sched.h"
#include "thread.h"
#include "periph/uart.h"
#include "eagle_soc.h"
#include "esp/uart_regs.h"
#include "sdk/sdk.h"
/**
* @brief Allocate memory to store the callback functions.
*/
static uart_isr_ctx_t isr_ctx[UART_NUMOF];
static uint8_t IRAM __uart_rx_one_char (uart_t uart);
static void __uart_tx_one_char(uart_t uart, uint8_t data);
static void __uart_intr_enable (uart_t uart);
static void IRAM __uart_intr_handler (void *para);
int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg)
{
CHECK_PARAM_RET (uart < UART_NUMOF, -1);
DEBUG("%s uart=%d, rate=%d, rx_cb=%p, arg=%p\n", __func__, uart, baudrate, rx_cb, arg);
/* setup the baudrate */
uart_div_modify(uart, UART_CLK_FREQ / baudrate);
/* register interrupt context */
isr_ctx[uart].rx_cb = rx_cb;
isr_ctx[uart].arg = arg;
if (rx_cb) {
ets_isr_attach (ETS_UART_INUM, __uart_intr_handler, 0);
/* since reading is done byte for byte we set the RX FIFO FULL */
/* interrupt level to 1 byte */
UART(uart).CONF1 = SET_FIELD(UART(uart).CONF1, UART_CONF1_RXFIFO_FULL_THRESHOLD, 1);
/* enable the RX FIFO FULL interrupt */
__uart_intr_enable (uart);
}
return UART_OK;
}
void uart_write(uart_t uart, const uint8_t *data, size_t len)
{
CHECK_PARAM (uart < UART_NUMOF);
for (size_t i = 0; i < len; i++) {
__uart_tx_one_char(uart, data[i]);
}
}
void uart_poweron (uart_t uart)
{
/* UART can't be powered on/off, just return */
}
void uart_poweroff (uart_t uart)
{
/* UART can't be powered on/off, just return */
}
void IRAM __uart_intr_handler (void *arg)
{
/* to satisfy the compiler */
(void)arg;
irq_isr_enter ();
DEBUG("%s \n", __func__);
/*
* UART0 and UART1 interrupts are combined togther. So we have to
* iterate over all UART devices and test the INT_STATUS register for
* interrupts
*/
for (int i = 0; i < UART_NUMOF; i++) {
uart_t uart = UART_DEV(0); /* UartDev.buff_uart_no; */
if (UART(uart).INT_STATUS & UART_INT_STATUS_RXFIFO_FULL) {
/* clear interrupt flag */
uint8_t data = __uart_rx_one_char (uart);
/* call registered RX callback function */
isr_ctx[uart].rx_cb(isr_ctx[uart].arg, data);
/* clear interrupt flag */
UART(uart).INT_CLEAR |= UART_INT_CLEAR_RXFIFO_FULL;
}
else {
/* TODO handle other type of interrupts, for the moment just clear them */
UART(uart).INT_CLEAR = 0x1f;
}
}
irq_isr_exit ();
}
/* RX/TX FIFO capacity is 128 byte */
#define UART_FIFO_MAX 127
/* receive one data byte with wait */
static uint8_t IRAM __uart_rx_one_char (uart_t uart)
{
/* uint8_t fifo_len = FIELD2VAL(UART_STATUS_RXFIFO_COUNT, UART(uart).STATUS); */
/* wait until at least von byte is in RX FIFO */
while (!FIELD2VAL(UART_STATUS_RXFIFO_COUNT, UART(uart).STATUS)) {}
/* read the lowest byte from RX FIFO register */
return UART(uart).FIFO & 0xff; /* only bit 0 ... 7 */
}
/* send one data byte with wait */
static void __uart_tx_one_char(uart_t uart, uint8_t data)
{
/* wait until at least one byte is avaiable in the TX FIFO */
while (FIELD2VAL(UART_STATUS_TXFIFO_COUNT, UART(uart).STATUS) >= UART_FIFO_MAX) {}
/* send the byte by placing it in the TX FIFO */
UART(uart).FIFO = data;
}
static void __uart_intr_enable(uart_t uart)
{
UART(uart).INT_ENABLE |= UART_INT_ENABLE_RXFIFO_FULL;
ETS_INTR_ENABLE(ETS_UART_INUM);
DEBUG("%s %08x\n", __func__, UART(uart).INT_ENABLE);
}
void uart_print_config(void)
{
LOG_INFO("\tUART_DEV(0): txd=%d rxd=%d\n", UART0_TXD, UART0_RXD);
}

22
cpu/esp8266/periph_cpu.c Normal file
View File

@ -0,0 +1,22 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266
* @{
*
* @file
* @brief CPU specific definitions and functions for peripheral handling
*
* @author Gunar Schorcht <gunar@schorcht.net>
*/
#define ENABLE_DEBUG 0
#include "debug.h"
#include "periph_cpu.h"

3
cpu/esp8266/sdk/Makefile Normal file
View File

@ -0,0 +1,3 @@
MODULE=sdk
include $(RIOTBASE)/Makefile.base

13
cpu/esp8266/sdk/doc.txt Normal file
View File

@ -0,0 +1,13 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup cpu_esp8266_sdk ESP8266 SDK interface
* @ingroup cpu_esp8266
* @brief Function declarations and mappings for compatibility with ESP8266 SDK
*/

23
cpu/esp8266/sdk/ets.c Normal file
View File

@ -0,0 +1,23 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266_sdk
* @{
*
* @file
* @brief ESP8266 ETS ROM functions
*
* @author Gunar Schorcht <gunar@schorcht.net>
* @}
*/
#ifndef MODULE_ESP_SDK
#include "sdk/ets.h"
#endif /* MODULE_ESP_SDK */

97
cpu/esp8266/sdk/ets.h Normal file
View File

@ -0,0 +1,97 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266_sdk
* @{
*
* @file
* @brief ESP8266 ETS ROM function prototypes
*
* @author Gunar Schorcht <gunar@schorcht.net>
* @}
*/
#ifndef ETS_H
#define ETS_H
#ifndef DOXYGEN
#include <stdint.h>
#include <stdarg.h>
#include "c_types.h"
#include "ets_sys.h"
#ifdef __cplusplus
extern "C" {
#endif
/* interrupts that are not defined in espressif/ets_sys.h */
#define ETS_WDEV_INUM 0 /* WDEV process FIQ interrupt */
#define ETS_RTC_INUM 3 /* RTC interrupt */
#define ETS_CCOM_INUM 6 /* CCOMPARE0 match interrupt */
#define ETS_SOFT_INUM 7 /* software interrupt */
#define ETS_WDT_INUM 8 /* SDK watchdog timer */
#define ETS_FRC2_INUM 10 /* SDK FRC2 timer interrupt */
#ifndef MODULE_ESP_SDK_INT_HANDLING
/*
* The following functions are mappings or dummies for source code
* compatibility of SDK and NON-SDK version
*/
#include "xtensa/xtensa_api.h"
#define ets_isr_mask(x) xt_ints_off(x)
#define ets_isr_unmask(x) xt_ints_on(x)
#define ets_isr_attach(i,f,a) xt_set_interrupt_handler(i,f,a)
#define _xtos_set_exception_handler(n,f) xt_set_exception_handler(n,f)
#else /* MODULE_ESP_SDK_INT_HANDLING */
extern void ets_isr_mask(uint32_t);
extern void ets_isr_unmask(uint32_t);
typedef void (_xtos_handler_t)(void*);
extern void _xtos_set_exception_handler(int n, _xtos_handler_t* f);
#endif /* MODULE_ESP_SDK_INT_HANDLING */
#ifndef MODULE_ESP_SDK
extern void ets_delay_us(uint16_t us);
extern void ets_timer_arm_new(ETSTimer *ptimer, uint32_t ms_us, int repeat_flag, int is_ms);
extern void ets_timer_disarm(ETSTimer *ptimer);
extern void ets_timer_setfn(ETSTimer *ptimer, ETSTimerFunc *pfunction, void *arg);
#endif
extern void ets_timer_arm(ETSTimer *ptimer, uint32_t ms, bool repeat_flag);
extern void ets_timer_handler_isr(void);
extern void ets_install_uart_printf(void);
extern void ets_install_putc1(void (*p)(char c));
extern int ets_uart_printf(const char *format, ...);
extern int ets_putc(int);
extern int ets_printf(const char * format, ...);
extern int ets_vprintf(void *function, const char *format, va_list arg);
extern uint8_t ets_get_cpu_frequency(void);
extern void ets_update_cpu_frequency(uint8_t);
extern void *ets_memcpy(void *to, const void *from, size_t size);
extern void ets_wdt_disable(void);
extern void ets_wdt_enable (void);
#ifdef __cplusplus
}
#endif
#endif /* DOXYGEN */
#endif /* ETS_H */

167
cpu/esp8266/sdk/ets_task.c Normal file
View File

@ -0,0 +1,167 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*
* PLEASE NOTE: This file is only used in SDK version
*/
#ifdef MODULE_ESP_SDK
#define ENABLE_DEBUG 0
#include <stdio.h>
#include "irq_arch.h"
#include "mutex.h"
#include "thread.h"
#include "esp/common_macros.h"
#include "sdk/ets_task.h"
#include "sdk/sdk.h"
#define TIMER_TASK_PRIORITY 31
static uint8_t min_prio = 0;
uint8_t ets_highest_1_bit (uint32_t mask)
{
__asm__ volatile ("nsau %0, %1;" :"=r"(mask) : "r"(mask));
return 32 - mask;
}
/**
* @brief Perform execution of all pending ETS system tasks.
*
* This is necessary to keep the underlying ETS system used by the
* SDK alive.
*/
void IRAM ets_tasks_run (void)
{
#if ENABLE_DEBUG
uint32_t _entry = phy_get_mactime();
uint32_t _exit;
ets_printf("ets_tasks_run @%lu\n", _entry);
#endif
/* reset hardware watchdog here */
system_soft_wdt_feed();
while (1) {
uint8_t hbit;
int state = irq_disable();
hbit = ets_highest_1_bit (ets_task_exec_mask);
if (min_prio < hbit) {
ets_task_tcb_t* task = &ets_task_tab[hbit-1];
ETSEvent * event = &task->queue[task->qposr++];
if (task->qposr == task->qlength) {
task->qposr = 0;
}
if (--task->qpending == 0) {
ets_task_exec_mask &= ~task->maskbit;
}
ets_task_min_prio = hbit;
irq_restore(state);
task->task(event);
ets_task_min_prio = min_prio;
}
else {
irq_restore(state);
break;
}
}
#if ENABLE_DEBUG
_exit = phy_get_mactime();
ets_printf("ets_tasks_run @%lu for %lu us\n", _entry, _exit - _entry);
#endif
/* reset hardware watchdog here again */
system_soft_wdt_feed();
}
/**
* To realize event-driven SDK functions such as WiFi functions and software
* timers, and to keep the system alive, the SDK internally uses its own
* tasks (SDK tasks) and its own scheduling mechanism. For this purpose, the
* SDK regularly executes SDK tasks with pending events in an endless loop
* using the ROM function *ets_run*.
*
* Interrupt service routines do not process interrupts directly but use
* the *ets_post* ROM function to send an event to one of these SDK tasks,
* which then processes the interrupts asynchronously. A context switch is
* not possible in the interrupt service routines.
*
* In the RIOT port, the task management of the SDK is replaced by the task
* management of the RIOT. To handle SDK tasks with pending events so that
* the SDK functions work and the system keeps alive, the ROM functions
* *ets_run* and *ets_post* are overwritten. The *ets_run* function performs
* all SDK tasks with pending events exactly once. It is executed at the end
* of the *ets_post* function and thus usually at the end of an SDK interrupt
* service routine or before the system goes into the lowest power mode.
*
* PLEASE REMEBER: we are doing that in interrupt context
*
* -> it must not take to much time (how can we ensure that)?
*
* -> we have to indicate that we are in interrupt context see *irq_is_in*
* and *irq_interrupt_nesting* (as realized by the level 1 exception handler
* in non SDK task handling environment, option MODULE_ESP_SDK_INT_HANDLING=0,
* the default)
*
* -> we must not execute a context switch or we have to execute the context
* switch from interrupt as following (as realized by the level 1
* interrupt exception handler in non SDK task handling environment, option
* MODULE_ESP_SDK_INT_HANDLING=0, the default)
* _frxt_int_enter();
* _frxt_switch_context();
* _frxt_int_exit();
*/
typedef uint32_t (*ets_post_function_t)(uint32_t prio, ETSSignal sig, ETSParam par);
static ets_post_function_t ets_post_rom = (ets_post_function_t)0x40000e24;
#ifdef MODULE_ESP_SDK
#define irom_cache_enabled() (*((uint32_t*)0x60000208) & (1 << 17))
#else
#define irom_cache_enabled() (1)
#endif
uint32_t IRAM ets_post (uint32_t prio, ETSSignal sig, ETSParam par)
{
uint32_t ret;
critical_enter();
/* test whether we are in hardware timer interrupt handling routine */
if (prio == TIMER_TASK_PRIORITY) {
/* first call ETS system post function */
ret = ets_post_rom (prio, sig, par);
/* handle only pending timer events */
if (irom_cache_enabled()) {
ets_timer_handler_isr();
}
}
else {
/* simply call ROM ets_post function */
ret = ets_post_rom (prio, sig, par);
}
/* since only timer events are handled we have to reset watch dog timer */
if (irom_cache_enabled()) {
system_soft_wdt_feed();
}
critical_exit();
return ret;
}
void ets_tasks_init(void)
{
/* there is nothing to do at the moment */
}
#endif /* MODULE_ESP_SDK */

View File

@ -0,0 +1,61 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
#ifndef ETS_TASK_H
#define ETS_TASK_H
#ifndef DOXYGEN
#ifdef __cplusplus
extern "C" {
#endif
#ifdef MODULE_ESP_SDK
#include "ets_sys.h"
extern uint8_t ets_task_min_prio;
/* Task control block definition as used for ETS task functions */
typedef struct {
ETSTask task; /* +0 task function */
ETSEvent* queue; /* +4 event queue (ring buffer) */
uint8_t qlength; /* +8 event queue length */
uint8_t qposw; /* +9 event queue position for write */
uint8_t qposr; /* +10 event queue position for read */
uint8_t qpending; /* +11 pending events */
uint32_t maskbit; /* +12 task mask bit */
} ets_task_tcb_t;
/* ROM variables, defined in esp8266.riot-os.app.ld */
/* source: disassembly of boot rom at https://github.com/trebisky/esp8266 */
extern uint8_t ets_task_min_prio; /* 0x3fffc6fc */
extern void* ets_idle_cb; /* 0x3fffdab0 */
extern void* ets_idle_arg; /* 0x3fffdab4 */
extern uint32_t ets_task_exec_mask; /* 0x3fffdab8 */
extern ets_task_tcb_t ets_task_tab[32]; /* 0x3fffdac0 */
extern uint32_t ets_post (uint32_t prio, ETSSignal sig, ETSParam par);
void ets_tasks_run (void);
void ets_tasks_init (void);
#else /* MODULE_ESP_SDK */
#define ets_tasks_run()
#define ets_tasks_init()
#endif /* MODULE_ESP_SDK */
#ifdef __cplusplus
}
#endif
#endif /* DOXYGEN */
#endif /* ETS_TASK_H */

189
cpu/esp8266/sdk/main.c Normal file
View File

@ -0,0 +1,189 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266_sdk
* @{
*
* @file
* @brief ESP8266 SDK libmain function
*
* @author Gunar Schorcht <gunar@schorcht.net>
* @}
*/
#ifndef MODULE_ESP_SDK
#include <stdio.h>
#include "c_types.h"
#include "common.h"
#include "cpu_conf.h"
#include "irq.h"
#include "irq_arch.h"
#include "log.h"
#include "esp/rtcmem_regs.h"
#include "spi_flash.h"
#include "sdk/ets.h"
#include "sdk/main.h"
#include "sdk/rom.h"
extern char* _printf_buf;
int IRAM os_printf_plus (const char* format, ...)
{
va_list arglist;
va_start(arglist, format);
int ret = vsnprintf(_printf_buf, PRINTF_BUFSIZ, format, arglist);
if (ret > 0) {
ets_printf (_printf_buf);
}
va_end(arglist);
return ret;
}
SpiFlashOpResult IRAM spi_flash_read (uint32_t faddr, uint32_t *dst, size_t size)
{
/*
* For simplicity, we use the ROM function. Since we need to disable the
* IROM cache function for that purpose, we have to be IRAM.
* Please note, faddr, src and size have to be aligned to 4 byte.
*/
SpiFlashOpResult ret;
CHECK_PARAM_RET (dst != NULL, SPI_FLASH_RESULT_ERR);
CHECK_PARAM_RET (faddr + size <= flashchip->chip_size, SPI_FLASH_RESULT_ERR);
critical_enter ();
Cache_Read_Disable ();
ret = SPIRead (faddr, dst, size);
Cache_Read_Enable(0, 0, 1);
critical_exit ();
return ret;
}
SpiFlashOpResult IRAM spi_flash_write (uint32_t faddr, uint32_t *src, size_t size)
{
/*
* For simplicity, we use the ROM function. Since we need to disable the
* IROM cache function for that purpose, we have to be in IRAM.
* Please note, faddr, src and size have to be aligned to 4 byte
*/
SpiFlashOpResult ret;
CHECK_PARAM_RET (src != NULL, SPI_FLASH_RESULT_ERR);
CHECK_PARAM_RET (faddr + size <= flashchip->chip_size, SPI_FLASH_RESULT_ERR);
critical_enter ();
Cache_Read_Disable ();
ret = SPIWrite (faddr, src, size);
Cache_Read_Enable(0, 0, 1);
critical_exit ();
return ret;
}
SpiFlashOpResult IRAM spi_flash_erase_sector(uint16_t sec)
{
CHECK_PARAM_RET (sec < flashchip->chip_size / flashchip->sector_size, SPI_FLASH_RESULT_ERR);
critical_enter ();
Cache_Read_Disable();
SpiFlashOpResult ret = SPIEraseSector (sec);
Cache_Read_Enable(0, 0, 1);
critical_exit ();
return ret;
}
void system_deep_sleep(uint32_t time_in_us)
{
/* TODO implement */
(void)time_in_us;
NOT_YET_IMPLEMENTED();
}
void system_restart(void)
{
/* TODO it's just a hard reset at the moment */
__asm__ volatile (" call0 0x40000080 ");
}
/**
* Following code is completly or at least partially from
* https://github.com/pvvx/esp8266web
* (c) PV` 2015
*
* @{
*/
uint8_t ICACHE_FLASH_ATTR system_get_checksum(uint8_t *ptr, uint32_t len)
{
uint8_t checksum = 0xEF;
while (len--) {
checksum ^= *ptr++;
}
return checksum;
}
#define RTCMEM_SIZE 0x300 /* user RTC RAM 768 bytes, 192 dword registers */
static bool IRAM _system_rtc_mem_access (uint32_t src, void *dst, uint32_t size, bool write)
{
/* src hast to be smaller than 192 */
CHECK_PARAM_RET (src <= (RTCMEM_SIZE >> 2), false);
/* src times 4 plus size must less than RTCMEM_SIZE */
CHECK_PARAM_RET (((src << 2) + size) < RTCMEM_SIZE, false);
/* des_addr has to be a multiple of 4 */
CHECK_PARAM_RET (((uint32_t)dst & 0x3) == 0, false);
/* align size to next higher multiple of 4 */
if (size & 0x3) {
size = (size + 4) & ~0x3;
}
for (uint32_t i = 0; i < (size >> 2); i++) {
if (write) {
RTCMEM_SYSTEM[src + i] = ((uint32_t*)dst)[i];
}
else {
((uint32_t*)dst)[i] = RTCMEM_SYSTEM[src + i];
}
}
return true;
}
bool IRAM system_rtc_mem_read (uint32_t src_addr, void *des_addr, uint32_t save_size)
{
return _system_rtc_mem_access (src_addr, des_addr, save_size, false);
}
bool IRAM system_rtc_mem_write (uint32_t src_addr, void *des_addr, uint32_t save_size)
{
return _system_rtc_mem_access (src_addr, des_addr, save_size, true);
}
/** @} */
#endif /* MODULE_ESP_SDK */

65
cpu/esp8266/sdk/main.h Normal file
View File

@ -0,0 +1,65 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266_sdk
* @{
*
* @file
* @brief ESP8266 SDK libmain function prototypes
*
* @author Gunar Schorcht <gunar@schorcht.net>
* @}
*/
#ifndef MAIN_H
#define MAIN_H
#ifndef DOXYGEN
#include <stdint.h>
#include <stdarg.h>
#include "c_types.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifndef MODULE_ESP_SDK
#include "esp/dport_regs.h"
/*
* The following functions are mappings or dummies for source code
* compatibility of SDK and NON-SDK version
*/
#define system_get_time phy_get_mactime
#define system_get_chip_id() (((DPORT.OTP_MAC1 & 0xffff) << 8) + ((DPORT.OTP_MAC0 >> 24) & 0xff))
#define system_get_cpu_freq ets_get_cpu_frequency
/* TODO #define system_update_cpu_freq ets_update_cpu_frequency */
extern int os_printf_plus (const char* format, ...);
extern void system_deep_sleep (uint32_t time_in_us);
extern uint8_t system_get_checksum(uint8_t *ptr, uint32_t len);
extern void system_restart (void);
extern bool system_rtc_mem_read(uint32_t src_addr, void *des_addr, uint32_t save_size);
extern bool system_rtc_mem_write(uint32_t src_addr, void *des_addr, uint32_t save_size);
#endif /* MODULE_ESP_SDK */
extern void NmiTimSetFunc(void (*func)(void));
#ifdef __cplusplus
}
#endif
#endif /* DOXYGEN */
#endif /* MAIN_H */

24
cpu/esp8266/sdk/phy.c Normal file
View File

@ -0,0 +1,24 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266_sdk
* @{
*
* @file
* @brief ESP8266 SDK libphy functions
*
* @author Gunar Schorcht <gunar@schorcht.net>
* @}
*/
#ifndef MODULE_ESP_SDK
#include "sdk/phy.h"
#endif /* MODULE_ESP_SDK */

58
cpu/esp8266/sdk/phy.h Normal file
View File

@ -0,0 +1,58 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266_sdk
* @{
*
* @file
* @brief ESP8266 SDK libphy function prototypes
*
* @author Gunar Schorcht <gunar@schorcht.net>
* @}
*/
#ifndef PHY_H
#define PHY_H
#ifndef DOXYGEN
#include <stdint.h>
#include <stdarg.h>
#include "c_types.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifndef MODULE_ESP_SDK
/*
* The following functions are mappings or dummies for source code
* compatibility of SDK and NON-SDK version
*/
#include "esp/dport_regs.h"
extern void phy_afterwake_set_rfoption(int op);
extern int phy_check_data_table(void * gdctbl, int x, int flg);
extern int register_chipv6_phy(uint8_t * esp_init_data);
extern void sleep_reset_analog_rtcreg_8266(void);
extern uint32_t test_tout(bool);
extern void write_data_to_rtc(uint8_t *);
#endif /* MODULE_ESP_SDK */
extern uint32_t phy_get_mactime(void);
#ifdef __cplusplus
}
#endif
#endif /* DOXYGEN */
#endif /* PHY_H */

24
cpu/esp8266/sdk/pp.c Normal file
View File

@ -0,0 +1,24 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266_sdk
* @{
*
* @file
* @brief ESP8266 other ROM functions
*
* @author Gunar Schorcht <gunar@schorcht.net>
* @}
*/
#ifndef MODULE_ESP_SDK
#include "sdk/pp.h"
#endif /* MODULE_ESP_SDK */

46
cpu/esp8266/sdk/pp.h Normal file
View File

@ -0,0 +1,46 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266_sdk
* @{
*
* @file
* @brief ESP8266 SDK libpp function prototypes
*
* @author Gunar Schorcht <gunar@schorcht.net>
* @}
*/
#ifndef PP_H
#define PP_H
#ifndef DOXYGEN
#ifdef __cplusplus
extern "C" {
#endif
#include "c_types.h"
#ifndef MODULE_ESP_SDK
/*
* The following functions are mappings or dummies for source code
* compatibility of SDK and NON-SDK version
*/
#define system_soft_wdt_feed()
#endif /* MODULE_ESP_SDK */
#ifdef __cplusplus
}
#endif
#endif /* DOXYGEN */
#endif /* PP_H */

24
cpu/esp8266/sdk/rom.c Normal file
View File

@ -0,0 +1,24 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266_sdk
* @{
*
* @file
* @brief ESP8266 other ROM functions
*
* @author Gunar Schorcht <gunar@schorcht.net>
* @}
*/
#ifndef MODULE_ESP_SDK
#include "sdk/rom.h"
#endif /* MODULE_ESP_SDK */

95
cpu/esp8266/sdk/rom.h Normal file
View File

@ -0,0 +1,95 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266_sdk
* @{
*
* @file
* @brief ESP8266 other ROM function prototypes
*
* @author Gunar Schorcht <gunar@schorcht.net>
* @}
*/
#ifndef ROM_H
#define ROM_H
#ifndef DOXYGEN
#include <stdint.h>
#include <stdarg.h>
#include "c_types.h"
#include "spi_flash.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifndef MODULE_ESP_SDK
/*
* The following functions are mappings or dummies for source code
* compatibility of SDK and NON-SDK version
*/
extern uint8_t rom_i2c_readReg(uint32_t block, uint32_t host_id, uint32_t reg_add);
extern uint8_t rom_i2c_readReg_Mask(uint32_t block, uint32_t host_id,
uint32_t reg_add,uint32_t Msb, uint32_t Lsb);
extern void rom_i2c_writeReg (uint32_t block, uint32_t host_id,
uint32_t reg_add, uint32_t data);
extern void rom_i2c_writeReg_Mask(uint32_t block, uint32_t host_id,
uint32_t reg_add, uint32_t Msb, uint32_t Lsb,
uint32_t indata);
extern uint32_t rtc_get_reset_reason(void);
#endif /* MODULE_ESP_SDK */
/* pointer to flash chip data structure */
extern SpiFlashChip* flashchip;
extern SpiFlashOpResult spi_flash_attach(void);
void Cache_Read_Disable(void);
void Cache_Read_Enable (uint32_t odd_even, uint32_t mb_count, uint32_t no_idea);
SpiFlashOpResult Wait_SPI_Idle(SpiFlashChip *chip);
SpiFlashOpResult SPI_write_enable(SpiFlashChip *chip);
SpiFlashOpResult SPIEraseArea (uint32_t off, size_t len);
SpiFlashOpResult SPIEraseBlock (uint32_t num);
SpiFlashOpResult SPIEraseSector(uint32_t num);
SpiFlashOpResult SPIEraseChip (void);
SpiFlashOpResult SPILock (void);
SpiFlashOpResult SPIUnlock(void);
SpiFlashOpResult SPIRead (uint32_t off, uint32_t *dst, size_t size);
SpiFlashOpResult SPIWrite (uint32_t off, const uint32_t *src, size_t size);
int SPIReadModeCnfig (uint32_t);
/* set elements of flashchip, see struct SpiFlashChip; */
SpiFlashOpResult SPIParamCfg (uint32_t deviceId,
uint32_t chip_size,
uint32_t block_size,
uint32_t sector_size,
uint32_t page_size,
uint32_t status_mask);
extern void uartAttach (void);
extern void uart_div_modify(uint8 uart_no, uint32_t DivLatchValue);
extern void Uart_Init (uint8 uart_no);
#ifdef __cplusplus
}
#endif
#endif /* DOXYGEN */
#endif /* ROM_H */

46
cpu/esp8266/sdk/sdk.h Normal file
View File

@ -0,0 +1,46 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266_sdk
* @{
*
* @file
* @brief ESP8266 SDK container
*
* @author Gunar Schorcht <gunar@schorcht.net>
* @}
*/
#ifndef SDK_H
#define SDK_H
#include "c_types.h"
#include "ets_sys.h"
#include "sdk/ets.h"
#include "sdk/main.h"
#include "sdk/phy.h"
#include "sdk/pp.h"
#include "sdk/rom.h"
#include "sdk/user.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifdef MODULE_ESP_SDK
#include "espressif/osapi.h"
#include "espressif/user_interface.h"
#endif /* MODULE_ESP_SDK */
#ifdef __cplusplus
}
#endif
#endif /* SDK_H */

64
cpu/esp8266/sdk/user.c Normal file
View File

@ -0,0 +1,64 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266_sdk
* @{
*
* @file
* @brief ESP8266 user defined SDK functions
*
* @author Gunar Schorcht <gunar@schorcht.net>
* @}
*/
#include "c_types.h"
#include "common.h"
#include "cpu_conf.h"
#include "esp/uart_regs.h"
#include "spi_flash.h"
#include "sdk/rom.h"
#include "sdk/user.h"
uint32_t __attribute__((weak)) user_rf_cal_sector_set(void)
{
uint32_t sec_num = flashchip->chip_size / flashchip->sector_size;
return sec_num - 5;
}
#ifndef MODULE_ESP_SDK
void uart_tx_flush (uint32_t num)
{
while ((UART(num).STATUS >> UART_STATUS_TXFIFO_COUNT_S) & UART_STATUS_TXFIFO_COUNT_M) {}
}
/**
* Following code is completly or at least partially from
* https://github.com/pvvx/esp8266web
* (c) PV` 2015
*
* @{
*/
void IRAM system_set_pll (uint8_t crystal_26m_en)
{
if(rom_i2c_readReg(103,4,1) != 136) { /* 8: 40MHz, 136: 26MHz */
if (crystal_26m_en == 1) { /* 0: 40MHz, 1: 26MHz */
/* set 80MHz PLL CPU */
rom_i2c_writeReg(103,4,1,136);
rom_i2c_writeReg(103,4,2,145);
}
}
}
/** @} */
#endif

47
cpu/esp8266/sdk/user.h Normal file
View File

@ -0,0 +1,47 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266_sdk
* @{
*
* @file
* @brief ESP8266 user defined SDK function prototypes
*
* @author Gunar Schorcht <gunar@schorcht.net>
* @}
*/
#ifndef USER_H
#define USER_H
#ifndef DOXYGEN
#include <stdint.h>
#include <stdarg.h>
#include "c_types.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifndef MODULE_ESP_SDK
extern uint32_t user_rf_cal_sector_set(void);
extern void uart_tx_flush (uint32_t);
extern void system_set_pll (uint8_t);
#endif /* MODULE_ESP_SDK */
#ifdef __cplusplus
}
#endif
#endif /* DOXYGEN */
#endif /* USER_H */

836
cpu/esp8266/startup.c Normal file
View File

@ -0,0 +1,836 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266
* @{
*
* @file
* @brief Implementation of the CPU initialization
*
* @author Gunar Schorcht <gunar@schorcht.net>
* @}
*/
#define ENABLE_DEBUG 0
#include "debug.h"
#include <string.h>
#include <stdlib.h>
#include "kernel_init.h"
#include "log.h"
#include "periph/init.h"
#include "c_types.h"
#include "spi_flash.h"
#include "board.h"
#include "common.h"
#include "exceptions.h"
#include "syscalls.h"
#include "tools.h"
#include "thread_arch.h"
#include "esp/iomux_regs.h"
#include "esp/spi_regs.h"
#include "esp/xtensa_ops.h"
#include "sdk/sdk.h"
#if MODULE_ESP_GDBSTUB
#include "esp-gdbstub/gdbstub.h"
#endif
extern void board_init(void);
extern void board_print_config(void);
uint32_t hwrand (void);
#ifdef MODULE_NEWLIB_SYSCALLS_DEFAULT
/* initialization as it should be called from newlibc */
extern void _init(void);
#endif
extern uint8_t _bss_start;
extern uint8_t _bss_end;
extern uint8_t _sheap;
extern uint8_t _eheap;
#ifdef MODULE_ESP_SDK
#include "sdk/ets_task.h"
/**
* @brief System main loop called by the ETS
*
* This function is called by ETS after all initializations has been
* finished to start periodic processing of defined ETS tasks. We
* overwrite this ETS function to take over the control and to start RIOT.
*/
void ets_run(void)
{
#if ENABLE_DEBUG
register uint32_t *sp __asm__ ("a1");
ets_uart_printf("_stack %p\n", sp);
ets_uart_printf("_bss_start %p\n", &_bss_start);
ets_uart_printf("_bss_end %p\n", &_bss_end);
ets_uart_printf("_heap_start %p\n", &_sheap);
ets_uart_printf("_heap_end %p\n", &_eheap);
ets_uart_printf("_heap_free %lu\n", get_free_heap_size());
#endif
/* init min task priority */
ets_task_min_prio = 0;
#ifdef MODULE_NEWLIB_SYSCALLS_DEFAULT
_init();
#endif
ets_tasks_init();
/* enable interrupts used by ETS/SDK */
#ifndef MODULE_ESP_SDK_INT_HANDLING
ets_isr_unmask(BIT(ETS_FRC2_INUM));
ets_isr_unmask(BIT(ETS_WDEV_INUM));
ets_isr_unmask(BIT(ETS_WDT_INUM));
#endif
#ifdef MODULE_ESP_GDBSTUB
gdbstub_init();
#endif
#if ENABLE_DEBUG==0
/* disable SDK messages */
system_set_os_print(0);
#endif
#ifdef CONTEXT_SWITCH_BY_INT
extern void IRAM thread_yield_isr(void* arg);
ets_isr_attach(ETS_SOFT_INUM, thread_yield_isr, NULL);
ets_isr_unmask(BIT(ETS_SOFT_INUM));
#endif
/* does not return */
kernel_init();
}
/**
* @brief Initialize the CPU, the board and the peripherals
*
* This function is called by ESP8266 SDK when all system initializations
* has been finished.
*/
void system_init(void)
{
LOG_INFO("\nStarting ESP8266 CPU with ID: %08x", system_get_chip_id());
LOG_INFO("\nSDK Version %s\n\n", system_get_sdk_version());
/* avoid reconnection all the time */
wifi_station_disconnect();
/* set exception handlers */
init_exceptions ();
/* init random number generator */
srand(hwrand());
/* init flash drive */
extern void flash_drive_init (void);
flash_drive_init();
/* trigger static peripheral initialization */
periph_init();
/* trigger board initialization */
board_init();
/* print the board config */
board_print_config();
}
/**
* @brief Entry point in user space after a system reset
*
* This function is called after system reset by the ESP8266 SDK. In this
* functions following steps are neccessary:
*
* 1. Reinit system timer as microsecond timer (precision is 500 us)
* 2. Set the UART parameters for serial output
* 3. Set the system initialization callback
*/
void IRAM user_init (void)
{
syscalls_init ();
thread_isr_stack_init ();
/* run system in high performance mode */
system_update_cpu_freq(160);
/* reinit system timer as microsecond timer */
system_timer_reinit ();
/* setup the serial communication */
uart_div_modify(0, UART_CLK_FREQ / STDIO_UART_BAUDRATE);
/* once the ETS initialization is done we can start with our code as callback */
system_init_done_cb(system_init);
/* keep wifi interface in null mode per default */
wifi_set_opmode_current (0);
}
#else /* MODULE_ESP_SDK */
#include "esp/dport_regs.h"
#include "esp/phy_info.h"
#include "esp/spiflash.h"
/**
* @brief Defines the structure of the file header in SPI flash
*
* @see https://github.com/espressif/esptool/wiki/Firmware-Image-Format
*/
typedef struct __attribute__((packed))
{
uint8_t magic; /* always 0xe9 */
uint8_t segments; /* number of segments */
uint8_t mode; /* 0 - qio, 1 - qout, 2 - dio, 3 - dout */
uint8_t speed:4; /* 0 - 40 MHz, 1 - 26 MHz, 2 - 20 MHz, 15 - 80 MHz */
uint8_t size:4; /* 0 - 512 kB, 1 - 256 kB, 2 - 1 MB, 3 - 2 MB, 4 - 4 MB */
} _spi_flash_header;
#define SPI_FLASH_SECTOR_SIZE 4096
struct s_info {
uint32 ap_ip; /* +00 */
uint32 ap_mask; /* +04 */
uint32 ap_gw; /* +08 */
uint32 st_ip; /* +0C */
uint32 st_mask; /* +10 */
uint32 st_gw; /* +14 */
uint8 ap_mac[6]; /* +18 */
uint8 st_mac[6]; /* +1E */
} __attribute__((packed, aligned(4)));
static struct s_info info;
enum rst_reason {
REASON_DEFAULT_RST = 0,
REASON_WDT_RST = 1,
REASON_EXCEPTION_RST = 2,
REASON_SOFT_WDT_RST = 3,
REASON_SOFT_RESTART = 4,
REASON_DEEP_SLEEP_AWAKE = 5,
REASON_EXT_SYS_RST = 6
};
struct rst_info{
uint32 reason;
uint32 exccause;
uint32 epc1;
uint32 epc2;
uint32 epc3;
uint32 excvaddr;
uint32 depc;
};
/**
* @brief System configuration store formats
*
* source https://github.com/pvvx/esp8266web
* (c) PV` 2015
*
* @{
*/
/* SDK 2.0.0 */
#define wifi_config_size 0x494 /* 1172 bytes */
#define g_ic_size (wifi_config_size + 532) /* 1704 bytes */
struct ets_store_wifi_hdr { /* Sector flash addr flashchip->chip_size-0x1000 (0x4027F000) */
uint8 bank; /* +00 = 0, 1 WiFi config flash addr: */
/* 0 - flashchip->chip_size-0x3000 (0x7D000), */
/* 1 - flashchip->chip_size-0x2000 */
uint8 unused[3]; /* +01 = 0xff, 0xff, 0xff */
uint32 flag; /* +04 = 0x55aa55aa */
uint32 wr_cnt; /* +08 = 0x00000001 */
uint32 len[2]; /* +12 = 0x00000020, 0xffffffff */
uint32 chk[2]; /* +20 = 0x000000ed, 0xffffffff */
uint32 flag2; /* +28 = 0xaa55aa55 */
};
static const uint8_t
ets_store_wifi_hdr_default[sizeof(struct ets_store_wifi_hdr)] =
{
0x00, 0xff, 0xff, 0xff, 0xaa, 0x55, 0xaa, 0x55,
0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xed, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0x55, 0xaa, 0x55, 0xaa
};
struct s_wifi_store { /* WiFi config flash addr: flashchip->chip_size - 0x3000 or -0x2000 */
/* SDK >= 2.0.0 */
uint8 boot_info[8]; /* +000 0x000 (boot_info[1]) boot_version */
uint8 wfmode[4]; /* +008 0x008 */
uint32 st_ssid_len; /* +012 0x00c */
uint8 st_ssid[32]; /* +016 0x010 */
uint8 field_048[7]; /* +048 0x030 */
uint8 st_passw[64]; /* +055 0x037 */
uint8 field_119; /* +119 0x077 */
uint8 data_120[32]; /* +120 0x078 */
uint8 field_152[17]; /* +152 0x098 */
uint8 field_169; /* +169 0x0a9 */
uint8 field_170[6]; /* +170 0x0aa */
uint32 ap_ssid_len; /* +176 0x0b0 */
uint8 ap_ssid[32]; /* +180 0x0b4 */
uint8 ap_passw[64]; /* +212 0x0d4 */
uint8 field_276[32]; /* +276 0x114 */
uint8 field_308; /* +308 0x134 */
uint8 wfchl; /* +309 0x135 */
uint8 field_310; /* +310 0x136 */
uint8 field_311; /* +311 0x137 */
uint8 field_312; /* +312 0x138 */
uint8 field_313; /* +313 0x139 */
uint8 field_314; /* +314 0x13a */
uint8 field_315; /* +315 0x13b */
uint16 field_316; /* +316 0x13c */
uint8 field_318[2]; /* +318 0x13e */
uint32 st1ssid_len; /* +320 0x140 */
uint8 st1ssid[32]; /* +324 0x144 */
uint8 st1passw[64]; /* +356 0x164 */
uint8 field_420[400]; /* +420 0x1a4 */
uint32 field_820[3]; /* +820 0x334 */
uint8 field_832[4]; /* +832 0x340 wifi_station_set_auto_connect */
uint32 phy_mode; /* +836 0x344 */
uint8 field_840[36]; /* +840 0x348 */
uint16 beacon; /* +876 0x36c 876+532 g_ic+1408 */
uint8 field_878[2]; /* +878 0x36e */
uint32 field_880; /* +880 0x370 */
uint32 field_884; /* +884 0x374 */
uint32 field_888; /* +888 0x378 */
uint8 field_892[284]; /* +892 0x37c ... 1176 0x498 */
};
struct s_g_ic {
uint32 boot_info; /* +0000 g_ic+0 0x3FFF2324 boot_version? */
uint32 field_004; /* +0004 g_ic+4 */
uint32 field_008; /* +0008 g_ic+8 */
uint32 field_00C; /* +000C g_ic+12 */
struct netif **netif1; /* +0010 g_ic+16 */
struct netif **netif2; /* +0014 g_ic+20 */
uint32 field_018; /* +0018 g_ic+24 */
uint32 field_01C; /* +001C g_ic+28 */
uint32 field_020; /* +0020 g_ic+32 */
uint32 field_024; /* +0024 g_ic+36 */
uint32 field_028; /* +0028 g_ic+40 */
uint8 field_02C[84]; /* +002C g_ic+44 */
uint32 field_080; /* +0080 g_ic+128 */
uint8 field_084[200]; /* +0084 g_ic+132 */
/* [0x12c] */
void * field_14C; /* +014C g_ic+332 */
uint32 ratetable; /* +0150 g_ic+336 */
uint8 field_154[44]; /* +0154 g_ic+340 */
uint32 field_180; /* +0180 g_ic+384 */
/* [0x170..0x180] wifi_get_user_ie */
void * field_184; /* +0184 g_ic+388 user_ie_manufacturer_recv_cb */
uint32 field_188; /* +0188 g_ic+392 */
uint32 field_18C; /* +018C g_ic+396 */
uint32 field_190; /* +0190 g_ic+400 */
uint32 field_194; /* +0194 g_ic+404 */
uint32 field_198; /* +0198 g_ic+408 */
uint32 field_19C; /* +019C g_ic+412 */
uint32 field_1A0; /* +01A0 g_ic+416 */
uint32 field_1A4; /* +01A4 g_ic+420 */
uint32 field_1A8; /* +01A8 g_ic+424 */
uint32 field_1AC; /* +01AC g_ic+428 */
uint32 field_1B0; /* +01B0 g_ic+432 */
uint32 field_1B4; /* +01B4 g_ic+436 */
uint32 field_1B8; /* +01B8 g_ic+440 */
uint32 field_1BC; /* +01BC g_ic+444 */
uint32 field_1C0; /* +01C0 g_ic+448 */
uint32 field_1C4; /* +01C4 g_ic+452 */
uint32 field_1C8[19]; /* +01C8 g_ic+456 ...532 */
struct s_wifi_store wifi_store; /* g_ic+??? */
};
typedef union _u_g_ic{
struct s_g_ic g;
uint8 c[g_ic_size];
uint16 w[g_ic_size/2];
uint32 d[g_ic_size/4];
} u_g_ic;
static u_g_ic g_ic;
/** }@ */
/**
* Following functions are from https://github.com/pvvx/esp8266web
* (c) PV` 2015
*
* @{
*/
void read_macaddr_from_otp(uint8_t* mac)
{
/* fixed prefix */
mac[0] = 0x18;
mac[1] = 0xfe;
mac[2] = 0x34;
if ((!(DPORT.OTP_CHIPID & (1 << 15))) ||
((DPORT.OTP_MAC0 == 0) && (DPORT.OTP_MAC1 == 0))) {
mac[3] = 0x18;
mac[4] = 0xfe;
mac[5] = 0x34;
}
else {
mac[3] = (DPORT.OTP_MAC1 >> 8) & 0xff;
mac[4] = DPORT.OTP_MAC1 & 0xff;
mac[5] = (DPORT.OTP_MAC0 >> 24) & 0xff;
}
}
int ICACHE_FLASH_ATTR flash_data_check(uint8 *check_buf)
{
uint32 cbuf[32];
uint32 *pcbuf = cbuf;
uint8 *pbuf = check_buf;
int i = 27;
while (i--) {
*pcbuf = pbuf[0] | (pbuf[1]<<8) | (pbuf[2]<<16) | (pbuf[3]<<24);
pcbuf++;
pbuf+=4;
}
cbuf[24] = (DPORT.OTP_MAC1 & 0xFFFFFFF) | ((DPORT.OTP_CHIPID & 0xF000) << 16);
cbuf[25] = (DPORT.OTP_MAC2 & 0xFFFFFFF) | (DPORT.OTP_MAC0 & 0xFF000000);
pcbuf = cbuf;
uint32 xsum = 0;
do {
xsum += *pcbuf++;
} while (pcbuf != &cbuf[26]);
xsum ^= 0xFFFFFFFF;
if (cbuf[26] != xsum) {
return 1;
}
return 0;
}
/** }@ */
/**
* @brief Startup function hook placed at 0x40100000
*/
void __attribute__((section(".UserEnter.text"))) NORETURN _call_user_start_hook(void)
{
__asm__ volatile (
" vectors_base: .word 0x40100000 \n" /* must contain entry point */
" \n"
" _call_user_start: \n" /* system startup function */
" .global _call_user_start \n"
" l32r a2, vectors_base \n" /* set vector base */
" wsr a2, vecbase \n"
" call0 cpu_user_start \n" /* call startup function */
);
UNREACHABLE();
}
void ICACHE_FLASH_ATTR start_phase2 (void);
/**
* @brief Startup function
*
* This function is the entry point in the user application. It is called
* after a system reset to startup the system.
*/
void __attribute__((noreturn)) IRAM cpu_user_start (void)
{
register uint32_t *sp __asm__ ("a1"); (void)sp;
/* PHASE 1: startup in SDK */
struct rst_info rst_if = { .reason = 0 };
system_rtc_mem_read(0, &rst_if, sizeof(struct rst_info));
/**
* Setup the serial communication speed. After cold start UART_CLK_FREQ is
* only 26/40 (65 %). So the baud rate would be only 74880. To have 115200
* at the beginning of the cold start, we have to set it to 177231 baud.
* This is changed later in function system_set_pll.
*/
system_set_pll(1); /* parameter is fix (from esp_init_data_default.bin byte 48) */
#if 0
if (rst_if.reason > REASON_DEFAULT_RST) {
/* warm start */
uart_div_modify(0, UART_CLK_FREQ / STDIO_UART_BAUDRATE);
}
else {
/* cold start */
uart_div_modify(0, UART_CLK_FREQ / 177231);
}
#else
uart_div_modify(0, UART_CLK_FREQ / STDIO_UART_BAUDRATE);
#endif
/* flush uart_tx_buffer */
ets_uart_printf(" \n");
#if ENABLE_DEBUG
ets_uart_printf("reset reason: %d %d\n", rst_if.reason, rtc_get_reset_reason());
ets_uart_printf("exccause=%ld excvaddr=%08lx\n", rst_if.exccause, rst_if.excvaddr);
ets_uart_printf("epc1=%08lx epc2=%08lx epc3=%08lx\n", rst_if.epc1, rst_if.epc2, rst_if.epc3);
ets_uart_printf("depc=%ld\n", rst_if.depc);
ets_uart_printf("_stack %p\n", sp);
ets_uart_printf("_bss_start %p\n", &_bss_start);
ets_uart_printf("_bss_end %p\n", &_bss_end);
ets_uart_printf("_heap_start %p\n", &_sheap);
ets_uart_printf("_heap_end %p\n", &_eheap);
ets_uart_printf("_heap_free %lu\n", get_free_heap_size());
#endif
uint32_t flash_sectors;
uint32_t flash_size;
_spi_flash_header flash_header;
SPI(0).USER0 |= SPI_USER0_CS_SETUP;
SPIRead(0, (uint32_t*)&flash_header, 4);
assert (flash_header.magic == 0xe9);
/* SPI flash sectors a 4.096 byte */
switch (flash_header.size) {
case 0: flash_sectors = 128; break; /* 512 kByte */
case 1: flash_sectors = 64; break; /* 256 kByte */
case 2: flash_sectors = 256; break; /* 1 MByte */
case 3: flash_sectors = 512; break; /* 2 MByte */
case 4: flash_sectors = 1024; break; /* 4 MByte */
default: flash_sectors = 128; break; /* default 512 kByte */
}
flash_size = flash_sectors * SPI_FLASH_SECTOR_SIZE;
flashchip->chip_size = flash_size;
flashchip->sector_size = SPI_FLASH_SECTOR_SIZE;
/*
* SPI flash speed params
* speed = 80MHz / clkdiv_pre / clkcnt_N
*/
uint32_t clkdiv_pre = 1; /* default 40 MHz = 80 MHz / 1 / 2 */
uint32_t clkcnt_N = 2;
switch (flash_header.speed) {
case 0: clkdiv_pre = 1; /* 40 MHz = 80 MHz / 1 / 2 */
clkcnt_N = 2;
break;
case 1: clkdiv_pre = 1; /* 26 MHz = 80 MHz / 1 / 3 */
clkcnt_N = 3;
break;
case 2: clkdiv_pre = 1; /* 20 MHz = 80 MHz / 1 / 4 */
clkcnt_N = 4;
break;
case 15: clkdiv_pre = 0; /* clock is equal to system clock */
break;
default: break;
}
if (clkdiv_pre) {
IOMUX.CONF &= ~IOMUX_CONF_SPI0_CLOCK_EQU_SYS_CLOCK;
SPI(0).CLOCK = VAL2FIELD_M(SPI_CLOCK_DIV_PRE, clkdiv_pre - 1) |
VAL2FIELD_M(SPI_CLOCK_COUNT_NUM, clkcnt_N - 1) |
VAL2FIELD_M(SPI_CLOCK_COUNT_HIGH, clkcnt_N/2 - 1) |
VAL2FIELD_M(SPI_CLOCK_COUNT_LOW, clkcnt_N - 1);
}
else {
IOMUX.CONF |= IOMUX_CONF_SPI0_CLOCK_EQU_SYS_CLOCK;
SPI(0).CLOCK |= SPI_CLOCK_EQU_SYS_CLOCK;
}
ets_uart_printf("Flash size: %lu byte, speed %d MHz",
flash_size, clkdiv_pre ? 80 / clkdiv_pre / clkcnt_N : 80);
switch (flash_header.mode) {
case 0: ets_printf(", mode QIO\n"); break;
case 1: ets_printf(", mode QOUT\n"); break;
case 2: ets_printf(", mode DIO\n"); break;
case 3: ets_printf(", mode DOUT\n"); break;
default: ets_printf("\n");
}
ets_uart_printf("\nStarting ESP8266 CPU with ID: %08x\n\n", system_get_chip_id());
/* clear .bss to avoid startup problems because of compiler optimization options */
memset(&_bss_start, 0x0, &_bss_end-&_bss_start);
/**
* Following parts of code are partially from or inspired by the
* following projects:
*
* [esp8266web](https://github.com/pvvx/esp8266web)
* (c) PV` 2015
*
* [esp-open-rtos](https://github.com/SuperHouse/esp-open-rtos.git).
* Copyright (C) 2015 Superhouse Automation Pty Ltd
* BSD Licensed as described in the file LICENSE
*
* @{
*/
struct ets_store_wifi_hdr binfo;
struct s_wifi_store wscfg;
/* load boot info (32 byte) from last sector and */
uint32_t binfo_addr = flash_size - SPI_FLASH_SECTOR_SIZE;
SPIRead (binfo_addr, (uint32_t*)&binfo, sizeof(binfo));
/* load the system config (1176 byte) from last 3 or 2 sectors */
uint32_t wscfg_addr = flash_size - (binfo.bank ? 2 : 3) * SPI_FLASH_SECTOR_SIZE;
SPIRead (wscfg_addr, (uint32_t*)&wscfg, sizeof(wscfg));
Cache_Read_Enable(0, 0, 1);
#if ENABLE_DEBUG
printf("boot_inf sector @0x%x\n", flash_size - SPI_FLASH_SECTOR_SIZE);
esp_hexdump(&binfo, sizeof(binfo), 'b', 16);
#endif
LOG_INFO("load boot_inf 0x%x, len %d, chk %02x\n",
binfo_addr, sizeof(binfo),
system_get_checksum((uint8_t*)&binfo, sizeof(binfo)));
LOG_INFO("load wifi_cfg 0x%x, len %d, chk %02x\n",
wscfg_addr, sizeof(wscfg),
system_get_checksum((uint8_t*)&wscfg, sizeof(wscfg)));
/* check whether boot_inf sector could be loaded */
if (binfo.bank > 0 && binfo.flag == 0xffffffff) {
LOG_INFO("no boot_inf sector @0x%x, write a default one to flash\n", binfo_addr);
memcpy (&binfo, ets_store_wifi_hdr_default, sizeof(binfo));
spi_flash_write (binfo_addr, (uint32_t*)&binfo, sizeof(binfo));
}
/* check the checksum */
if (binfo.flag == 0x55aa55aa &&
binfo.chk[binfo.bank] == system_get_checksum((uint8_t*)&wscfg, binfo.len[binfo.bank])) {
/* checksum test is ok */
}
else {
/* checksum error but continue */
LOG_INFO("flash check sum error @0x%x\n", wscfg_addr);
/* check whether there is no wifi_cfg sector */
uint8_t* wscfg_sec = (uint8_t*)&wscfg;
size_t i = 0;
for ( ; i < sizeof(wscfg); i++) {
if (wscfg_sec[i] != 0xff) {
break;
}
}
/* no data different from 0xff found, we assume that the flash was erased */
if (i == sizeof(wscfg)) {
/* TODO write a default wifi_cfg sector automatically into the flash in that case */
LOG_INFO("\nno wifi_cfg sector found, use following command:\n"
"esptool.py write_flash 0x%x $(RIOT_CPU)/bin/wifi_cfg_default.bin\n\n",
wscfg_addr);
}
}
memcpy(&g_ic.g.wifi_store, &wscfg, sizeof(wscfg));
uart_tx_flush(0);
uart_tx_flush(1);
init_exceptions ();
/* PHASE 2: sdk_init in SDK */
start_phase2();
/** }@ */
/* run system in high performance mode */
/* TODO system_update_cpu_freq(160); */
/* PHASE 3: start RIOT-OS kernel */
/* init random number generator */
srand(hwrand());
/* init flash drive */
extern void flash_drive_init (void);
flash_drive_init();
/* trigger static peripheral initialization */
periph_init();
/* initialize the board and startup the kernel */
board_init();
/* print the board config */
board_print_config();
#ifdef MODULE_NEWLIB_SYSCALLS_DEFAULT
_init();
#endif
#ifdef MODULE_ESP_GDBSTUB
gdbstub_init();
#endif
#ifdef CONTEXT_SWITCH_BY_INT
extern void IRAM thread_yield_isr(void* arg);
ets_isr_attach(ETS_SOFT_INUM, thread_yield_isr, NULL);
ets_isr_unmask(BIT(ETS_SOFT_INUM));
#endif
/* startup the kernel */
kernel_init();
/* should not be reached */
UNREACHABLE() ;
}
void start_phase2 (void)
{
uint32_t flash_size = flashchip->chip_size;
struct rst_info rst_if;
system_rtc_mem_read(0, &rst_if, sizeof(struct rst_info));
/**
* Following parts of code are partially from or inspired by the
* following projects:
*
* [esp8266web](https://github.com/pvvx/esp8266web)
* (c) PV` 2015
*
* [esp-open-rtos](https://github.com/SuperHouse/esp-open-rtos.git).
* Copyright (C) 2015 Superhouse Automation Pty Ltd
* BSD Licensed as described in the file LICENSE
*
* @{
*/
/* changes clock freqeuncy and should be done before system_set_pll */
sleep_reset_analog_rtcreg_8266();
/* set correct system clock and adopt UART frequency */
system_set_pll(1); /* parameter is fixed (from esp_init_data_default.bin byte 48) */
uart_div_modify(0, UART_CLK_FREQ / STDIO_UART_BAUDRATE);
uart_tx_flush(0);
uart_tx_flush(1);
syscalls_init ();
thread_isr_stack_init ();
read_macaddr_from_otp(info.st_mac);
LOG_INFO("MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
info.st_mac[0], info.st_mac[1], info.st_mac[2],
info.st_mac[3], info.st_mac[4], info.st_mac[5]);
/* load esp_init_data_default.bin */
uint8_t* pbuf = malloc(1024); (void)pbuf;
uint32_t phy_info_addr = flash_size - 4 * SPI_FLASH_SECTOR_SIZE;
sdk_phy_info_t* phy_info = (sdk_phy_info_t*)pbuf;
spi_flash_read (phy_info_addr, (uint32_t*)phy_info, sizeof(sdk_phy_info_t));
LOG_INFO("load phy_info 0x%x, len %d, chk %02x\n",
phy_info_addr, sizeof(sdk_phy_info_t),
system_get_checksum((uint8_t*)phy_info, sizeof(sdk_phy_info_t)));
/* load rf_cal_sec (default rf_cal_sec = flash_sectors - 5) */
uint32_t rf_cal_sec = user_rf_cal_sector_set();
uint32_t rf_cal_sec_addr = rf_cal_sec * SPI_FLASH_SECTOR_SIZE;
spi_flash_read (rf_cal_sec_addr, (uint32_t*)(pbuf + 128), 628);
LOG_INFO("rf_cal_sec=%d\n", rf_cal_sec);
LOG_INFO("load rf_cal 0x%x, len %d, chk %02x\n",
rf_cal_sec_addr, 628, system_get_checksum(pbuf + 128, 628));
LOG_INFO("reset reason: %d %d\n", rst_if.reason, rtc_get_reset_reason());
#if ENABLE_DEBUG
printf("phy_info sector @0x%x\n", phy_info_addr);
esp_hexdump(pbuf, 128, 'b', 16);
printf("rf_cal sector @0x%x\n", rf_cal_sec_addr);
/* esp_hexdump(pbuf+128, 628, 'b', 16); */
#endif
sdk_phy_info_t default_phy_info;
get_default_phy_info(&default_phy_info);
if (phy_info->version != default_phy_info.version) {
/* check whether there is no phy_info sector */
uint8_t* phy_info_sec = (uint8_t*)phy_info;
size_t i = 0;
for ( ; i < sizeof(sdk_phy_info_t); i++) {
if (phy_info_sec[i] != 0xff) {
break;
}
}
/* no data different from 0xff found, we assume that the flash was erased */
if (i == sizeof(sdk_phy_info_t)) {
/* write a default default phy_info sector into the flash */
LOG_INFO("no phy_info sector found @0x%x, "
"writing esp_init_data_default.bin into flash\n",
phy_info_addr);
spi_flash_write (phy_info_addr,
(uint32_t*)&default_phy_info, sizeof(sdk_phy_info_t));
}
LOG_INFO("set default esp_init_data!\n");
memcpy(pbuf, &default_phy_info, sizeof(sdk_phy_info_t));
}
extern uint8_t* phy_rx_gain_dc_table;
extern uint8_t phy_rx_gain_dc_flag;
phy_rx_gain_dc_table = &pbuf[0x100];
phy_rx_gain_dc_flag = 0;
int xflg = (flash_data_check(&pbuf[128]) != 0 ||
phy_check_data_table(phy_rx_gain_dc_table, 125, 1) != 0) ? 1 : 0;
if (rst_if.reason != REASON_DEEP_SLEEP_AWAKE) {
phy_afterwake_set_rfoption(1);
if (xflg == 0) {
write_data_to_rtc(pbuf+128);
}
}
g_ic.c[491] = ((phy_info->freq_correct_mode & 7 )== 3) ? 1 : 0;
pbuf[0xf8] = 0;
/* clear RTC memory */
memset(&rst_if, 0, sizeof(struct rst_info));
system_rtc_mem_write(0, &rst_if, sizeof(struct rst_info));
uart_tx_flush(0);
uart_tx_flush(1);
if (register_chipv6_phy(pbuf)) {
LOG_ERROR ("register_chipv6_phy failed");
}
free (pbuf);
/** @} */
}
#endif /* MODULE_ESP_SDK */

473
cpu/esp8266/syscalls.c Normal file
View File

@ -0,0 +1,473 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266
* @{
*
* @file
* @brief Implementation of required system calls
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
#define ENABLE_DEBUG 0
#define MEMLEAK_DEBUG 0
#include "debug.h"
#include <sys/types.h>
#include <sys/errno.h>
#include <sys/reent.h>
#include <sys/stat.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "ets_sys.h"
#include "c_types.h"
#include "common.h"
#include "cpu_conf.h"
#include "irq.h"
#include "kernel_defines.h"
#include "log.h"
#include "mutex.h"
#include "rmutex.h"
#include "sched.h"
#include "syscalls.h"
#include "esp/xtensa_ops.h"
#include "esp/common_macros.h"
#include "sdk/sdk.h"
int IRAM puts(const char * str)
{
char c;
while ((c = *str) != 0) {
ets_putc(c);
++str;
}
ets_putc('\n');
return true;
}
int IRAM putchar(int c)
{
/* function is neccessary to avoid unproducable results */
ets_putc(c);
return true;
}
char _printf_buf[PRINTF_BUFSIZ];
int /* IRAM */ printf(const char* format, ...)
{
va_list arglist;
va_start(arglist, format);
int ret = vsnprintf(_printf_buf, PRINTF_BUFSIZ, format, arglist);
if (ret > 0) {
ets_printf (_printf_buf);
}
va_end(arglist);
return ret;
}
#ifdef SDK_HEAP_USED
/**
* Map memory management functions to SDK memory management functions.
* This is necessary to use the same heap as the SDK internally does.
* Furthermore, these functions do at least avoid interrupts during the
* execution of memory management functions. Memory management function
* of ETS are not used and have not to considered therefore.
*/
extern void *pvPortMalloc (size_t size, const char *, unsigned);
extern void vPortFree (void *ptr, const char *, unsigned);
extern void *pvPortZalloc (size_t size, const char *, unsigned);
extern void *pvPortCalloc (size_t nmemb, size_t size, const char *, unsigned);
extern void *pvPortRealloc (void *ptr, size_t size, const char *, unsigned);
extern unsigned int xPortGetFreeHeapSize(void);
void* IRAM malloc(size_t size)
{
#if MEMLEAK_DEBUG
static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__;
return pvPortMalloc(size, mem_debug_file, __LINE__);
#else
return pvPortMalloc(size, "", 0);
#endif
}
void IRAM free(void *ptr)
{
#if MEMLEAK_DEBUG
static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__;
return vPortFree (ptr, mem_debug_file, __LINE__);
#else
return vPortFree (ptr, "", 0);
#endif
}
void* IRAM calloc(size_t nmemb, size_t size)
{
#if MEMLEAK_DEBUG
static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__;
return pvPortCalloc(nmemb, size, mem_debug_file, __LINE__);
#else
return pvPortCalloc(nmemb, size, "", 0);
#endif
}
void* IRAM realloc(void *ptr, size_t size)
{
#if MEMLEAK_DEBUG
static const char mem_debug_file[] ICACHE_RODATA_ATTR STORE_ATTR = __FILE__;
return pvPortRealloc(ptr, size, mem_debug_file, __LINE__);
#else
return pvPortRealloc(ptr, size, "", 0);
#endif
}
void* IRAM _malloc_r (struct _reent *r, size_t size)
{
return malloc (size);
}
void IRAM _free_r (struct _reent *r, void *ptr)
{
free (ptr);
}
unsigned int get_free_heap_size (void)
{
return xPortGetFreeHeapSize();
}
void IRAM syscalls_init (void) {}
#else /* SDK_HEAP_USED */
/*
* To use the same heap SDK memory management functions have to be replaced by
* newlib memory functions. In that case the _malloc_lock/_unlock functions
* have to be defined. Memory management functions of ETS are not used and
* have not to considered here.
*/
void* IRAM pvPortMalloc (size_t size, const char *file, unsigned line)
{
(void)file;
(void)line;
return malloc (size);
}
void IRAM vPortFree (void *ptr, const char *file, unsigned line)
{
(void)file;
(void)line;
free (ptr);
}
void* IRAM pvPortCalloc (size_t nmemb, size_t size, const char *file, unsigned line)
{
(void)file;
(void)line;
void *ptr = malloc (nmemb*size);
if (ptr) {
memset (ptr, 0x0, nmemb*size);
}
return ptr;
}
void* IRAM pvPortZalloc (size_t size, const char *file, unsigned line)
{
(void)file;
(void)line;
void *ptr = malloc (size);
if (ptr) {
memset (ptr, 0x0, size);
}
return ptr;
}
void* IRAM pvPortRealloc (void *ptr, size_t size, const char *file, unsigned line)
{
(void)file;
(void)line;
return realloc (ptr, size);
}
size_t IRAM xPortWantedSizeAlign(size_t size)
{
/* allign the size to a multiple of 8 */
return (size & 0x7) ? (size & ~0x7) + 8 : size;
}
size_t IRAM xPortGetFreeHeapSize (void)
{
return get_free_heap_size ();
}
/*
* Following function implement the lock mechanism in newlib. The only static
* mutex defined here is the __malloc_recursive_mutex to avoid that memory
* management functions try to lock before RIOT's threads are running.
*/
extern _lock_t __malloc_recursive_mutex;
static rmutex_t _malloc_rmtx = RMUTEX_INIT;
void IRAM syscalls_init (void)
{
__malloc_recursive_mutex = (_lock_t)&_malloc_rmtx;
}
void IRAM _lock_init(_lock_t *lock)
{
CHECK_PARAM (sched_active_thread != 0);
CHECK_PARAM (lock != NULL);
CHECK_PARAM (*lock != ((_lock_t)&_malloc_rmtx));
mutex_t* mtx = malloc (sizeof(mutex_t));
if (mtx) {
memset (mtx, 0, sizeof(mutex_t));
*lock = (_lock_t)mtx;
}
}
void IRAM _lock_init_recursive(_lock_t *lock)
{
CHECK_PARAM (sched_active_thread != 0);
CHECK_PARAM (lock != NULL);
CHECK_PARAM (*lock != ((_lock_t)&_malloc_rmtx));
rmutex_t* rmtx = malloc (sizeof(rmutex_t));
if (rmtx) {
memset (rmtx, 0, sizeof(rmutex_t));
*lock = (_lock_t)rmtx;
}
}
void IRAM _lock_close(_lock_t *lock)
{
CHECK_PARAM (lock != NULL);
CHECK_PARAM (*lock != ((_lock_t)&_malloc_rmtx));
free ((void*)*lock);
*lock = 0;
}
void IRAM _lock_close_recursive(_lock_t *lock)
{
CHECK_PARAM (lock != NULL);
CHECK_PARAM (*lock != ((_lock_t)&_malloc_rmtx));
free ((void*)*lock);
*lock = 0;
}
void IRAM _lock_acquire(_lock_t *lock)
{
CHECK_PARAM (sched_active_thread != 0);
CHECK_PARAM (lock != NULL && *lock != 0);
mutex_lock ((mutex_t*)*lock);
}
void IRAM _lock_acquire_recursive(_lock_t *lock)
{
CHECK_PARAM (sched_active_thread != 0);
CHECK_PARAM (lock != NULL && *lock != 0);
rmutex_lock ((rmutex_t*)*lock);
}
int IRAM _lock_try_acquire(_lock_t *lock)
{
CHECK_PARAM_RET (sched_active_thread != 0, 0);
CHECK_PARAM_RET (lock != NULL && *lock != 0, 0);
return rmutex_trylock ((rmutex_t*)*lock);
}
int IRAM _lock_try_acquire_recursive(_lock_t *lock)
{
CHECK_PARAM_RET (sched_active_thread != 0, 0);
CHECK_PARAM_RET (lock != NULL && *lock != 0, 0);
return mutex_trylock ((mutex_t*)*lock);
}
void IRAM _lock_release(_lock_t *lock)
{
CHECK_PARAM (sched_active_thread != 0);
CHECK_PARAM (lock != NULL && *lock != 0);
mutex_unlock ((mutex_t*)*lock);
}
void IRAM _lock_release_recursive(_lock_t *lock)
{
CHECK_PARAM (sched_active_thread != 0);
CHECK_PARAM (lock != NULL && *lock != 0);
rmutex_unlock ((rmutex_t*)*lock);
}
#ifdef MODULE_NEWLIB_SYSCALLS_DEFAULT
#define _cheap heap_top
extern char *heap_top;
extern char _eheap; /* end of heap (defined in esp8266.riot-os.app.ld) */
#else /* MODULE_NEWLIB_SYSCALLS_DEFAULT */
static uint8_t* _cheap = 0; /* last allocated chunk of heap */
extern uint8_t _eheap; /* end of heap (defined in esp8266.riot-os.app.ld) */
extern uint8_t _sheap; /* start of heap (defined in esp8266.riot-os.app.ld) */
void* IRAM _sbrk_r (struct _reent *r, ptrdiff_t incr)
{
uint8_t* _cheap_old;
/* initial _cheap */
if (_cheap == NULL) {
_cheap = &_sheap;
}
/* save old _cheap */
_cheap_old = _cheap;
/* check whether _cheap + incr overflows the heap */
if (_cheap + incr >= &_eheap) {
r->_errno = ENOMEM;
return (caddr_t)-1;
}
/* set new _cheap */
_cheap += incr;
#if ENABLE_DEBUG
uint32_t remaining = &_eheap - _cheap;
printf ("%s %lu byte allocated in %p .. %p, remaining %u\n",
__func__, incr, _cheap_old, _cheap, remaining);
#endif
/* return allocated memory */
return (void*) _cheap_old;
}
#endif /* MODULE_NEWLIB_SYSCALLS_DEFAULT */
unsigned int IRAM get_free_heap_size (void)
{
return (_cheap) ? &_eheap - _cheap : 0;
}
#endif /* SDK_HEAP_USED */
#if !defined(MODULE_NEWLIB_SYSCALLS_DEFAULT)
NORETURN void _exit(int status)
{
UNREACHABLE();
}
static int _no_sys_func (struct _reent *r, const char* f)
{
LOG_ERROR("system function %s does not exist\n", f);
r->_errno = ENOSYS;
return -1;
}
int _open_r(struct _reent *r, const char *path, int flag, int m)
{
return _no_sys_func (r, __func__);
}
int _close_r(struct _reent *r, int fd)
{
return _no_sys_func (r, __func__);
}
int _fstat_r(struct _reent *r, int fdes, struct stat *stat)
{
return _no_sys_func (r, __func__);
}
int _stat_r(struct _reent *r, const char *path, struct stat *buff)
{
return _no_sys_func (r, __func__);
}
int _lseek_r(struct _reent *r, int fdes, int off, int w)
{
return _no_sys_func (r, __func__);
}
int _write_r(struct _reent *r, int fd, const void *buff, size_t cnt)
{
return _no_sys_func (r, __func__);
}
int _read_r(struct _reent *r, int fd, void *buff, size_t cnt)
{
return _no_sys_func (r, __func__);
}
#include <sys/time.h>
int _gettimeofday_r(struct _reent *r, struct timeval *tv, void *tz)
{
(void) tz;
if (tv) {
uint32_t microseconds = system_get_time();
tv->tv_sec = microseconds / 1000000;
tv->tv_usec = microseconds % 1000000;
}
return 0;
}
#endif /* MODULE_NEWLIB_SYSCALLS_DEFAULT */
int _rename_r (struct _reent *r, const char* old, const char* new)
{
DEBUG("%s: system function does not exist\n", __func__);
r->_errno = ENOSYS;
return -1;
}
#include <math.h>
double __ieee754_remainder(double x, double y) {
return x - y * floor(x/y);
}
float __ieee754_remainderf(float x, float y) {
return x - y * floor(x/y);
}

326
cpu/esp8266/thread_arch.c Normal file
View File

@ -0,0 +1,326 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266
* @{
*
* @file
* @brief Implementation of the kernel's architecture dependent thread interface
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
/*
* PLEASE NOTE: Some parts of the code are taken from the FreeRTOS port for
* Xtensa processors from Cadence Design Systems. These parts are marked
* accordingly. For these parts, the following license is valid:
*
* Copyright (c) 2003-2015 Cadence Design Systems, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#define ENABLE_DEBUG 0
#include "debug.h"
#include <stdio.h>
#include <string.h>
#include "board.h"
#include "cpu.h"
#include "irq.h"
#include "log.h"
#include "thread.h"
#include "sched.h"
#include "common.h"
#include "esp/common_macros.h"
#include "esp/xtensa_ops.h"
#include "sdk/ets_task.h"
#include "sdk/rom.h"
#include "sdk/sdk.h"
#include "tools.h"
#include "xtensa/xtensa_context.h"
/* User exception dispatcher when exiting */
extern void _xt_user_exit(void);
/* Switch context to the highest priority ready task without context save */
extern void _frxt_dispatch(void);
/* Set an flag indicating that a task switch is required on return from interrupt */
extern void _frxt_setup_switch(void);
/* Switch context to the highest priority ready task with context save */
extern void vPortYield(void);
extern void vPortYieldFromInt(void);
char* thread_stack_init(thread_task_func_t task_func, void *arg, void *stack_start, int stack_size)
{
/*
* Stack layout after task stack initialization
*
* +------------------------+
* | | TOP
* | thread_control_block |
* stack_start + stack_size ==> | |
* +------------------------+
* top_of_stack ==> | |
* | XT_CP_FRAME |
* | (optional) |
* top_of_stack + 1 - XT_CP_SIZE ==> | |
* +------------------------+
* | |
* | XT_STK_FRAME |
* | | XT_STK_...
* | a2 = arg | XT_STK_A2
* | a1 = sp + XT_STK_FRMSZ | XT_STK_A1
* | a0 = sched_task_exit | XT_STK_A0
* | ps = PS_UM | PS_EXCM | XT_STK_PS
* | pc = task_func | XT_STK_PC
* sp = top_of_stack + 1 - XT_CP_SIZE ==> | exit = _xt_user_exit | XT_STK_EXIT
* - XT_STK_FRMSZ +------------------------+
* | |
* | remaining stack space |
* | available for data |
* stack_start (preallocated var) ==> | | BOTTOM
* +------------------------+
*
* Initialized stack frame represents the registers as set when the
* the task function would have been called.
*
* Registers in a called function
*
* pc - PC at the beginning in the function
* a0 - return address from the function (return address to caller)
* a1 - current stack pointer at the beginning in the function
* a2 - first argument of the function
*/
/* stack is [stack_start+0 ... stack_start+stack_size-1] */
uint8_t *top_of_stack;
uint8_t *sp;
top_of_stack = (uint8_t*)((uint32_t)stack_start + stack_size-1);
/* Clear whole stack with a known value to assist debugging */
#if !defined(DEVELHELP) && !defined(SCHED_TEST_STACK)
/* Unfortunatly, this affects thread_measure_stack_free function */
memset(stack_start, 0, stack_size);
#endif
/* BEGIN - code from FreeRTOS port for Xtensa from Cadence */
/* Create interrupt stack frame aligned to 16 byte boundary */
sp = (uint8_t*)(((uint32_t)(top_of_stack+1) - XT_STK_FRMSZ - XT_CP_SIZE) & ~0xf);
/* ensure that stack is big enough */
assert (sp > (uint8_t*)stack_start);
XtExcFrame* exc_frame = (XtExcFrame*)sp;
/* Explicitly initialize certain saved registers */
exc_frame->pc = (uint32_t)task_func; /* task entry point */
exc_frame->a0 = (uint32_t)sched_task_exit; /* task exit point (CHANGED) */
exc_frame->a1 = (uint32_t)sp + XT_STK_FRMSZ; /* physical top of stack frame */
exc_frame->a2 = (uint32_t)arg; /* parameters for task_func */
exc_frame->exit = (uint32_t)_xt_user_exit; /* user exception exit dispatcher */
/* Set initial PS to int level 0, EXCM disabled ('rfe' will enable), user mode. */
/* Also set entry point argument parameter. */
#ifdef __XTENSA_CALL0_ABI__
/* CALL0 ABI */
exc_frame->ps = PS_UM | PS_EXCM;
#else
/* + for windowed ABI also set WOE and CALLINC (pretend task was 'call4'd). */
exc_frame->ps = PS_UM | PS_EXCM | PS_WOE | PS_CALLINC(1);
#endif
#ifdef XT_USE_SWPRI
/* Set the initial virtual priority mask value to all 1's. */
exc_frame->vpri = 0xFFFFFFFF;
#endif
#if XCHAL_CP_NUM > 0
/*
* Init the coprocessor save area (see xtensa_context.h)
* No access to TCB here, so derive indirectly. Stack growth is top to bottom.
* p = (uint32_t *) xMPUSettings->coproc_area;
*/
uint32_t *p;
p = (uint32_t *)(((uint32_t) pxTopOfStack - XT_CP_SIZE) & ~0xf);
p[0] = 0;
p[1] = 0;
p[2] = (((uint32_t) p) + 12 + XCHAL_TOTAL_SA_ALIGN - 1) & -XCHAL_TOTAL_SA_ALIGN;
#endif
/* END - code from FreeRTOS port for Xtensa from Cadence */
DEBUG("%s start=%p size=%d top=%p sp=%p free=%u\n",
__func__, stack_start, stack_size, top_of_stack, sp, sp-(uint8_t*)stack_start);
return (char*)sp;
}
unsigned sched_interrupt_nesting = 0; /* Interrupt nesting level */
#ifdef CONTEXT_SWITCH_BY_INT
/**
* Context switches are realized using software interrupts
*/
void IRAM thread_yield_isr(void* arg)
{
/* set the context switch flag (indicates that context has to be switched
is switch on exit from interrupt in _frxt_int_exit */
_frxt_setup_switch();
}
#endif
void thread_yield_higher(void)
{
/* reset hardware watchdog */
system_soft_wdt_feed();
/* handle pending ets tasks first to keep system alive */
ets_tasks_run();
/* yield next task */
#if defined(ENABLE_DEBUG) && defined(DEVELHELP)
if (sched_active_thread) {
DEBUG("%u old task %u %s %u\n", phy_get_mactime(),
sched_active_thread->pid, sched_active_thread->name,
sched_active_thread->sp - sched_active_thread-> stack_start);
}
#endif
if (!irq_is_in()) {
#ifdef CONTEXT_SWITCH_BY_INT
WSR(BIT(ETS_SOFT_INUM), interrupt);
#else
vPortYield();
#endif
}
else {
_frxt_setup_switch();
}
#if defined(ENABLE_DEBUG) && defined(DEVELHELP)
if (sched_active_thread) {
DEBUG("%u new task %u %s %u\n", phy_get_mactime(),
sched_active_thread->pid, sched_active_thread->name,
sched_active_thread->sp - sched_active_thread-> stack_start);
}
#endif
return;
}
void thread_stack_print(void)
{
/* Print the current stack to stdout. */
#if defined(DEVELHELP)
volatile thread_t* task = thread_get(sched_active_pid);
if (task) {
char* stack_top = task->stack_start + task->stack_size;
int size = stack_top - task->sp;
printf("Printing current stack of thread %" PRIkernel_pid "\n", thread_getpid());
esp_hexdump((void*)(task->sp), size >> 2, 'w', 8);
}
#else
NOT_SUPPORTED();
#endif
}
void thread_print_stack(void)
{
/* Prints human readable, ps-like thread information for debugging purposes. */
/* because of Xtensa stack structure and call0 ABI, it is not possible to implement */
NOT_YET_IMPLEMENTED();
return;
}
#if defined(DEVELHELP)
extern uint8_t port_IntStack;
extern uint8_t port_IntStackTop;
void thread_isr_stack_init(void)
{
/* code from thread.c, please see the copyright notice there */
/* assign each int of the stack the value of it's address */
uintptr_t *stackmax = (uintptr_t *)&port_IntStackTop;
uintptr_t *stackp = (uintptr_t *)&port_IntStack;
while (stackp < stackmax) {
*stackp = (uintptr_t) stackp;
stackp++;
}
}
int thread_isr_stack_usage(void)
{
return &port_IntStackTop - &port_IntStack -
thread_measure_stack_free((char*)&port_IntStack);
}
void *thread_isr_stack_pointer(void)
{
/* Get the current ISR stack pointer. */
return &port_IntStackTop;
}
void *thread_isr_stack_start(void)
{
/* Get the start of the ISR stack. */
return &port_IntStack;
}
void thread_isr_stack_print(void)
{
printf("Printing current ISR\n");
esp_hexdump(&port_IntStack, &port_IntStackTop-&port_IntStack, 'w', 8);
}
#else /* DEVELHELP */
void thread_isr_stack_init(void) {}
#endif /* DEVELHELP */
NORETURN void cpu_switch_context_exit(void)
{
/* Switch context to the highest priority ready task without context save */
_frxt_dispatch();
UNREACHABLE();
}

62
cpu/esp8266/tools.c Normal file
View File

@ -0,0 +1,62 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp8266
* @{
*
* @file
* @brief Implementation of some tools
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
#include <stdio.h>
#include <inttypes.h>
#include "esp/common_macros.h"
#include "tools.h"
void esp_hexdump (const void* addr, uint32_t num, char width, uint8_t per_line)
{
uint32_t count = 0;
uint32_t size;
uint8_t* addr8 = (uint8_t*) addr;
uint16_t* addr16 = (uint16_t*)addr;
uint32_t* addr32 = (uint32_t*)addr;
uint64_t* addr64 = (uint64_t*)addr;
switch (width) {
case 'b': size = 1; break;
case 'h': size = 2; break;
case 'w': size = 4; break;
case 'g': size = 8; break;
default : size = 1; break;
}
while (count < num) {
if (count % per_line == 0) {
printf ("%08x: ", (uint32_t)((uint8_t*)addr+count*size));
}
switch (width) {
case 'b': printf("%02" PRIx8, addr8[count++]); break;
case 'h': printf("%04" PRIx16, addr16[count++]); break;
case 'w': printf("%08" PRIx32, addr32[count++]); break;
case 'g': printf("%016" PRIx64, addr64[count++]); break;
default : printf("."); count++; break;
}
if (count % per_line == 0) {
printf ("\n");
}
}
printf ("\n");
}