From ddc91df4ca44f5486703db22d6c146477c40338c Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Thu, 5 Sep 2019 13:35:58 +0200 Subject: [PATCH] cpu/esp8266: changes for RTOS SDK --- cpu/esp8266/Makefile | 2 + cpu/esp8266/Makefile.dep | 19 - cpu/esp8266/Makefile.features | 1 + cpu/esp8266/Makefile.include | 200 ++-- cpu/esp8266/doc.txt | 1033 ++++++++++------- cpu/esp8266/esp-wifi/Makefile | 4 + cpu/esp8266/esp-wifi/doc.txt | 2 +- cpu/esp8266/esp-wifi/esp_wifi_netdev.c | 1008 +++++++++------- cpu/esp8266/esp-wifi/esp_wifi_netdev.h | 41 +- cpu/esp8266/esp-wifi/esp_wifi_params.h | 18 +- cpu/esp8266/esp_events.c | 103 ++ cpu/esp8266/exceptions.c | 178 ++- cpu/esp8266/freertos/queue.c | 4 + cpu/esp8266/include/common.h | 98 -- cpu/esp8266/include/cpu.h | 2 +- cpu/esp8266/include/cpu_conf.h | 29 +- cpu/esp8266/include/esp_common.h | 130 +++ cpu/esp8266/include/esp_common_log.h | 150 +++ cpu/esp8266/include/exceptions.h | 4 +- cpu/esp8266/include/gpio_arch.h | 142 +++ cpu/esp8266/include/irq_arch.h | 27 +- cpu/esp8266/include/log_module.h | 60 + cpu/esp8266/include/periph_cpu.h | 237 +++- cpu/esp8266/include/sdk_conf.h | 86 ++ cpu/esp8266/include/stdio.h | 748 ++++++++++++ cpu/esp8266/include/sys/types.h | 337 ++++++ cpu/esp8266/include/syscalls.h | 15 +- cpu/esp8266/include/thread_arch.h | 2 +- cpu/esp8266/include/tools.h | 2 +- cpu/esp8266/include/xtensa_conf.h | 2 +- cpu/esp8266/irq_arch.c | 13 +- cpu/esp8266/ld/esp8266.peripherals.ld | 14 + ...ot-os.no_sdk.app.ld => esp8266.riot-os.ld} | 139 ++- cpu/esp8266/ld/esp8266.rom.ld | 60 + cpu/esp8266/periph/Makefile | 2 +- cpu/esp8266/periph/adc.c | 17 +- cpu/esp8266/periph/flash.c | 585 +++++++--- cpu/esp8266/periph/gpio.c | 37 +- cpu/esp8266/periph/hwrng.c | 11 +- cpu/esp8266/periph/i2c.c | 295 +++-- cpu/esp8266/periph/pm.c | 25 +- cpu/esp8266/periph/pwm.c | 66 +- cpu/esp8266/periph/rtc.c | 4 +- cpu/esp8266/periph/spi.c | 388 ++++--- cpu/esp8266/periph/timer.c | 27 +- cpu/esp8266/periph/uart.c | 456 ++++++-- cpu/esp8266/sdk/Makefile | 2 +- cpu/esp8266/sdk/ets.c | 35 +- cpu/esp8266/sdk/ets.h | 44 +- cpu/esp8266/sdk/phy.h | 21 +- cpu/esp8266/sdk/sdk.h | 15 +- cpu/esp8266/sdk/system.c | 56 + cpu/esp8266/sdk/system.h | 60 + cpu/esp8266/sdk/user.h | 47 - cpu/esp8266/startup.c | 850 +------------- cpu/esp8266/syscalls.c | 727 ++++++++---- cpu/esp8266/thread_arch.c | 237 +++- cpu/esp8266/tools.c | 28 +- 58 files changed, 5874 insertions(+), 3071 deletions(-) create mode 100644 cpu/esp8266/esp_events.c delete mode 100644 cpu/esp8266/include/common.h create mode 100644 cpu/esp8266/include/esp_common.h create mode 100644 cpu/esp8266/include/esp_common_log.h create mode 100644 cpu/esp8266/include/gpio_arch.h create mode 100644 cpu/esp8266/include/log_module.h create mode 100644 cpu/esp8266/include/sdk_conf.h create mode 100644 cpu/esp8266/include/stdio.h create mode 100644 cpu/esp8266/include/sys/types.h create mode 100644 cpu/esp8266/ld/esp8266.peripherals.ld rename cpu/esp8266/ld/{esp8266.riot-os.no_sdk.app.ld => esp8266.riot-os.ld} (65%) create mode 100644 cpu/esp8266/ld/esp8266.rom.ld create mode 100644 cpu/esp8266/sdk/system.c create mode 100644 cpu/esp8266/sdk/system.h delete mode 100644 cpu/esp8266/sdk/user.h diff --git a/cpu/esp8266/Makefile b/cpu/esp8266/Makefile index bb10b55787..bc70b8de96 100644 --- a/cpu/esp8266/Makefile +++ b/cpu/esp8266/Makefile @@ -12,4 +12,6 @@ ifneq (, $(filter esp_wifi, $(USEMODULE))) DIRS += esp-wifi endif +INCLUDES += -I$(ESP8266_SDK_DIR)/components/esp8266/include/internal + include $(RIOTBASE)/Makefile.base diff --git a/cpu/esp8266/Makefile.dep b/cpu/esp8266/Makefile.dep index 9cd556e24f..eb98e13223 100644 --- a/cpu/esp8266/Makefile.dep +++ b/cpu/esp8266/Makefile.dep @@ -1,17 +1,5 @@ # additional modules dependencies -ifneq (, $(filter esp_sdk, $(USEMODULE))) - USEMODULE += core_thread_flags - INCLUDES += -I$(ESP8266_SDK_DIR)/third_party/include - LINKFLAGS += -Wl,-wrap=malloc - LINKFLAGS += -Wl,-wrap=free - LINKFLAGS += -Wl,-wrap=calloc - LINKFLAGS += -Wl,-wrap=realloc - LINKFLAGS += -Wl,-wrap=_malloc_r - LINKFLAGS += -Wl,-wrap=_free_r - LINKFLAGS += -Wl,-wrap=_realloc_r -endif - ifneq (, $(filter esp_spiffs, $(USEMODULE))) export SPIFFS_STD_OPTION = -std=c99 USEMODULE += spiffs @@ -19,12 +7,5 @@ ifneq (, $(filter esp_spiffs, $(USEMODULE))) endif ifneq (, $(filter esp_wifi, $(USEMODULE))) - CFLAGS += -DLWIP_OPEN_SRC - LINKFLAGS += -Wl,-wrap=ethernet_input USEMODULE += netdev_eth endif - -ifneq (,$(filter ndn-riot,$(USEPKG))) - USEMODULE += crypto - USEMODULE += cipher_modes -endif diff --git a/cpu/esp8266/Makefile.features b/cpu/esp8266/Makefile.features index 3c3c6579de..82e159cedd 100644 --- a/cpu/esp8266/Makefile.features +++ b/cpu/esp8266/Makefile.features @@ -2,6 +2,7 @@ FEATURES_PROVIDED += arch_32bit FEATURES_PROVIDED += arch_esp8266 +FEATURES_PROVIDED += cpp FEATURES_PROVIDED += periph_cpuid FEATURES_PROVIDED += periph_hwrng FEATURES_PROVIDED += periph_pm diff --git a/cpu/esp8266/Makefile.include b/cpu/esp8266/Makefile.include index 55575925ec..e66b28d57a 100644 --- a/cpu/esp8266/Makefile.include +++ b/cpu/esp8266/Makefile.include @@ -1,23 +1,13 @@ # 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 + export ESP8266_SDK_DIR=/opt/esp/ESP8266-RTOS-SDK endif # Options to control the compilation -ifeq ($(USE_SDK), 1) - USEMODULE += esp_sdk -endif - ifeq ($(ENABLE_GDB), 1) USEMODULE += esp_gdb endif @@ -29,35 +19,37 @@ endif # SPECIAL module dependencies # cannot be done in Makefile.dep since Makefile.dep is included too late -ifneq (, $(filter esp_sw_timer, $(USEMODULE))) - USEMODULE += esp_sdk -endif - -ifneq (, $(filter esp_wifi, $(USEMODULE))) +ifneq (, $(filter esp_now esp_wifi, $(USEMODULE))) $(eval GNRC_NETIF_NUMOF=$(shell echo $$(($(GNRC_NETIF_NUMOF)+1)))) - USEMODULE += esp_sdk - USEMODULE += netopt -endif - -ifneq (, $(filter esp_now, $(USEMODULE))) - $(eval GNRC_NETIF_NUMOF=$(shell echo $$(($(GNRC_NETIF_NUMOF)+1)))) - USEMODULE += esp_sdk + USEMODULE += esp_wifi_any USEMODULE += netopt + USEMODULE += xtimer endif ifneq (, $(filter esp_gdbstub, $(USEMODULE))) USEMODULE += esp_gdb endif +ifneq (, $(filter spiffs, $(USEMODULE))) + export RIOT_TEST_TIMEOUT = 300 +endif + +ifneq (, $(filter littlefs, $(USEMODULE))) + export RIOT_TEST_TIMEOUT = 300 +endif + # regular Makefile export TARGET_ARCH ?= xtensa-lx106-elf # ESP8266 pseudomodules PSEUDOMODULES += esp_gdb -PSEUDOMODULES += esp_sdk +PSEUDOMODULES += esp_log_colored +PSEUDOMODULES += esp_log_tagged +PSEUDOMODULES += esp_qemu PSEUDOMODULES += esp_sw_timer PSEUDOMODULES += esp_spiffs +PSEUDOMODULES += esp_wifi_any USEMODULE += esp_freertos USEMODULE += esp_idf @@ -66,54 +58,78 @@ USEMODULE += esp_idf_nvs_flash USEMODULE += esp_idf_spi_flash USEMODULE += esp_idf_util USEMODULE += esp_idf_wpa_supplicant_crypto +USEMODULE += esp_sdk +USEMODULE += log USEMODULE += mtd USEMODULE += newlib -USEMODULE += newlib_nano USEMODULE += newlib_syscalls_default USEMODULE += periph +USEMODULE += periph_common +USEMODULE += periph_hrng +USEMODULE += periph_flash +USEMODULE += periph_uart USEMODULE += ps USEMODULE += random -USEMODULE += sdk USEMODULE += stdio_uart USEMODULE += xtensa -ifneq (, $(filter pthread, $(USEMODULE))) - # has to be included before $(ESP8266_NEWLIB_DIR) - 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)/esp_common/vendor/ INCLUDES += -I$(RIOTCPU)/$(CPU) +INCLUDES += -I$(RIOTCPU)/$(CPU)/include +INCLUDES += -I$(RIOTCPU)/$(CPU)/include/freertos INCLUDES += -I$(RIOTCPU)/$(CPU)/vendor -INCLUDES += -I$(RIOTCPU)/$(CPU)/vendor/espressif +INCLUDES += -I$(RIOTCPU)/$(CPU)/vendor/esp-idf/ +INCLUDES += -I$(RIOTCPU)/$(CPU)/vendor/esp-idf/bootloader_support/include +INCLUDES += -I$(RIOTCPU)/$(CPU)/vendor/esp-idf/esp8266/include +INCLUDES += -I$(RIOTCPU)/$(CPU)/vendor/esp-idf/esp8266/include/esp8266 +INCLUDES += -I$(RIOTCPU)/$(CPU)/vendor/esp-idf/heap/include +INCLUDES += -I$(RIOTCPU)/$(CPU)/vendor/esp-idf/log/include +INCLUDES += -I$(ESP8266_SDK_DIR)/components/ +INCLUDES += -I$(ESP8266_SDK_DIR)/components/bootloader_support/include/ +INCLUDES += -I$(ESP8266_SDK_DIR)/components/esp8266/include +INCLUDES += -I$(ESP8266_SDK_DIR)/components/esp8266/include/esp8266 +INCLUDES += -I$(ESP8266_SDK_DIR)/components/heap/include +INCLUDES += -I$(ESP8266_SDK_DIR)/components/heap/port/esp8266/include +INCLUDES += -I$(ESP8266_SDK_DIR)/components/nvs_flash/include +INCLUDES += -I$(ESP8266_SDK_DIR)/components/spi_flash/include -CFLAGS += -DESP_OPEN_SDK -DSCHED_PRIO_LEVELS=32 -DCONTEXT_SWITCH_BY_INT +CFLAGS += -DESP_OPEN_SDK -DSCHED_PRIO_LEVELS=32 -DDEVELHELP +CFLAGS += -D__ESP_FILE__=__FILE__ CFLAGS += -Wno-unused-parameter -Wformat=0 -CFLAGS += -mlongcalls -mtext-section-literals -CFLAGS += -ffunction-sections -fdata-sections -fzero-initialized-in-bss +CFLAGS += -mlongcalls -mtext-section-literals -fstrict-volatile-bitfields +CFLAGS += -fdata-sections -ffunction-sections -fzero-initialized-in-bss + +OPTIONAL_CFLAGS_BLACKLIST += -fdiagnostics-color +OPTIONAL_CFLAGS_BLACKLIST += -Wformat-overflow +OPTIONAL_CFLAGS_BLACKLIST += -Wformat-truncation +OPTIONAL_CFLAGS_BLACKLIST += -gz + ASFLAGS += --longcalls --text-section-literals -ifneq (, $(filter esp_sdk, $(USEMODULE))) - INCLUDES += -I$(ESP8266_SDK_DIR)/include - CFLAGS += -DUSE_US_TIMER -endif +# thin archives trigger a reboot loop - see #12258, #12035, #12346 +ARFLAGS = rcs ifneq (, $(filter esp_gdbstub, $(USEMODULE))) GDBSTUB_DIR ?= $(RIOTCPU)/$(CPU)/vendor/esp-gdbstub - CFLAGS += -DGDBSTUB_FREERTOS=0 + CFLAGS += -DGDBSTUB_BREAK_ON_INIT=1 INCLUDES += -I$(GDBSTUB_DIR) endif ifneq (, $(filter esp_gdb, $(USEMODULE))) - CFLAGS += -Og -ggdb -g3 + CFLAGS_OPT ?= -Og -ggdb -g3 else - CFLAGS += -Os + # TODO should be -Os + # With -Os char arrays have not to be 32-bit word aligned. This leads to + # an alignment exception when the address of an char array is assigned to + # an uint32_t pointer and the pointer is used for the access. + CFLAGS_OPT ?= -O2 endif +CFLAGS += $(CFLAGS_OPT) + ifeq ($(QEMU), 1) - CFLAGS += -DQEMU + USEMODULE += esp_qemu endif ifeq ($(FLASH_MODE), qio) @@ -124,51 +140,77 @@ ifeq ($(FLASH_MODE), qout) CFLAGS += -DFLASH_MODE_QOUT endif -LINKFLAGS += -L$(ESP8266_NEWLIB_DIR)/$(TARGET_ARCH)/lib -LINKFLAGS += -L$(ESP8266_SDK_DIR)/lib +LINKFLAGS += -L$(ESP8266_RTOS_SDK_DIR)/components/esp8266/lib +LINKFLAGS += $(CFLAGS_OPT) -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 +BASELIBS += -lc -lgcc -lwpa -lcore -lnet80211 -lphy -lpp -lhal -lstdc++ + +ifneq (, $(filter esp_now, $(USEMODULE))) + BASELIBS += -lespnow 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 += -u _malloc_r +LINKFLAGS += -nostdlib -Wl,-gc-sections -Wl,-static # -Wl,--print-gc-sections + +LINKFLAGS += -T$(RIOTCPU)/$(CPU)/ld/esp8266.rom.ld +LINKFLAGS += -T$(RIOTCPU)/$(CPU)/ld/esp8266.riot-os.ld +LINKFLAGS += -T$(RIOTCPU)/$(CPU)/ld/esp8266.peripherals.ld + +ifneq (, $(filter esp_idf_heap, $(USEMODULE))) + LINKFLAGS += -Wl,-wrap=_malloc_r + LINKFLAGS += -Wl,-wrap=_calloc_r + LINKFLAGS += -Wl,-wrap=_free_r + LINKFLAGS += -Wl,-wrap=_realloc_r +endif # The ELFFILE is the base one used for flashing FLASHFILE ?= $(ELFFILE) # configure preflasher to convert .elf to .bin before flashing -FLASH_SIZE = -fs 8m -PREFLASHER ?= esptool.py -PREFFLAGS ?= elf2image $(FLASH_SIZE) $(FLASHFILE) +FLASH_MODE = dout # FIX configuration, DO NOT CHANGE +FLASH_FREQ = 26m # FIX configuration, DO NOT CHANGE +FLASH_SIZE ?= 1MB FLASHDEPS += preflash -# flasher configuration -ifeq ($(QEMU), 1) - FLASHER = cat - FFLAGS += $(FLASHFILE)-0x00000.bin /dev/zero | head -c $$((0x10000)) | cat - - FFLAGS += $(FLASHFILE)-0x10000.bin /dev/zero | head -c $$((0xfc000)) | cat - - FFLAGS += $(RIOTCPU)/$(CPU)/bin/esp_init_data_default.bin > $(FLASHFILE).bin +PREFLASHER ?= $(ESP8266_SDK_DIR)/components/esptool_py/esptool/esptool.py +PREFFLAGS = --chip esp8266 elf2image +PREFFLAGS += --flash_mode $(FLASH_MODE) --flash_size $(FLASH_SIZE) +PREFFLAGS += --flash_freq $(FLASH_FREQ) --version 3 +PREFFLAGS += -o $(FLASHFILE).bin $(FLASHFILE); +PREFFLAGS += echo "" > $(BINDIR)/partitions.csv; +PREFFLAGS += echo "nvs, data, nvs, 0x9000, 0x6000" >> $(BINDIR)/partitions.csv; +PREFFLAGS += echo "phy_init, data, phy, 0xf000, 0x1000" >> $(BINDIR)/partitions.csv; +PREFFLAGS += echo -n "factory, app, factory, 0x10000, " >> $(BINDIR)/partitions.csv; +PREFFLAGS += ls -l $(FLASHFILE).bin | awk '{ print $$5 }' >> $(BINDIR)/partitions.csv; + +PREFFLAGS += python $(RIOTCPU)/$(CPU)/vendor/esp-idf/partition_table/gen_esp32part.py +PREFFLAGS += --verify $(BINDIR)/partitions.csv $(BINDIR)/partitions.bin + +ifneq (, $(filter esp_log_colored, $(USEMODULE))) + BOOTLOADER ?= bootloader_dout_115200_color.bin else - FLASH_MODE ?= dout - export PROGRAMMER_SPEED ?= 460800 - FLASHER = esptool.py - FFLAGS += -p $(PROG_DEV) -b $(PROGRAMMER_SPEED) write_flash - FFLAGS += -fm $(FLASH_MODE) - FFLAGS += 0 $(FLASHFILE)-0x00000.bin - FFLAGS += 0x10000 $(FLASHFILE)-0x10000.bin; esptool.py -p $(PROG_DEV) run + BOOTLOADER ?= bootloader_dout_115200_no_color.bin endif -OPTIONAL_CFLAGS_BLACKLIST += -fdiagnostics-color -OPTIONAL_CFLAGS_BLACKLIST += -Wformat-overflow -OPTIONAL_CFLAGS_BLACKLIST += -Wformat-truncation -OPTIONAL_CFLAGS_BLACKLIST += -gz +ifneq (, $(filter esp_qemu, $(USEMODULE))) + FLASHER = dd + FFLAGS += if=/dev/zero bs=1M count=1 | tr "\\000" "\\377" > tmp.bin && + FFLAGS += cat $(RIOTCPU)/$(CPU)/bin/$(BOOTLOADER) tmp.bin | + FFLAGS += head -c $$((0x8000)) | + FFLAGS += cat - $(BINDIR)/partitions.bin tmp.bin | + FFLAGS += head -c $$((0x10000)) | + FFLAGS += cat - $(FLASHFILE).bin tmp.bin | + FFLAGS += head -c $$((0xfc000)) | + FFLAGS += cat - $(RIOTCPU)/$(CPU)/bin/esp_init_data_default.bin tmp.bin | + FFLAGS += head -c $$((0x100000)) > $(BINDIR)/esp8266flash.bin && rm tmp.bin +else + export PROGRAMMER_SPEED ?= 460800 + FLASHER = esptool.py + FFLAGS += --chip esp8266 --port $(PORT) --baud $(PROGRAMMER_SPEED) + FFLAGS += --before default_reset --after hard_reset write_flash -z + FFLAGS += --flash_size detect + FFLAGS += --flash_mode $(FLASH_MODE) --flash_freq $(FLASH_FREQ) + FFLAGS += 0x0000 $(RIOTCPU)/$(CPU)/bin/$(BOOTLOADER) + FFLAGS += 0x8000 $(BINDIR)/partitions.bin + FFLAGS += 0x10000 $(FLASHFILE).bin +endif diff --git a/cpu/esp8266/doc.txt b/cpu/esp8266/doc.txt index 616cb5c184..63403459d1 100644 --- a/cpu/esp8266/doc.txt +++ b/cpu/esp8266/doc.txt @@ -10,12 +10,9 @@ 1. [Overview](#esp8266_overview) 2. [Short Configuration Reference](#esp8266_short_configuration_reference) 3. [MCU ESP8266](#esp8266_mcu_esp8266) - 1. [Features of ESP8266](#esp8266_features) - 2. [Features Supported by RIOT-OS](#esp8266_supported_features) 4. [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) + 2. [Manual Toolchain Installation](#esp8266_manual_toolchain_installation) 5. [Flashing the Device](#esp8266_flashing_the_device) 1. [Toolchain Usage](#esp8266_toolchain_usage) 2. [Compile Options](#esp8266_compile_options) @@ -24,9 +21,9 @@ 6. [Peripherals](#esp8266_peripherals) 1. [GPIO pins](#esp8266_gpio_pins) 2. [ADC Channels](#esp8266_adc_channels) - 3. [PWM Channels](#esp8266_pwm_channels) + 3. [SPI Interfaces](#esp8266_spi_interfaces) 4. [I2C Interfaces](#esp8266_i2c_interfaces) - 5. [SPI Interfaces](#esp8266_spi_interfaces) + 5. [PWM Channels](#esp8266_pwm_channels) 6. [Timers](#esp8266_timers) 7. [SPIFFS Device](#esp8266_spiffs_device) 8. [Other Peripherals](#esp8266_other_peripherals) @@ -39,42 +36,51 @@ 9. [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) -10. [SDK Task Handling](#esp8266_sdk_task_handling) -11. [QEMU Mode and GDB](#esp8266_qemu_mode_and_gdb) - +10. [SDK Specific Information](#esp8266_sdk_specifics) + 1. [Tasks](#esp8266_sdk_tasks) + 2. [Heap](#esp8266_esp_idf_heap_implementation) +11. [Debugging](#esp8266_debugging) + 1. [QEMU Mode and GDB](#esp8266_qemu_mode_and_gdb) + 2. [Module esp_gdbstub](#esp8266_esp_gdbstub) # Overview    [[TOC](#esp8266_toc)] -There are two implementations that can be used: +RIOT-Xtensa-ESP is a bare metal implementation of RIOT-OS for +ESP8266 / ESP8285 SOCs which supports most features of RIOT-OS. The +peripheral SPI and I2C interfaces allow to connect all external hardware +modules supported by RIOT-OS, such as sensors and actuators. SPI interface +can also be used to connect external IEEE802.15.4 modules to integrate +ESP8266 boards into a GNRC network. -- 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, enable module `esp_wifi`, for example, at the make command line: +The RIOT-OS port for ESP8266 supports ESP8266 as well as ESP8285 MCUs and requires +the [ESP8266 RTOS SDK v3.x](https://github.com/espressif/ESP8266_RTOS_SDK). +To build a RIOT application, simply use the `make` command and specify an +existing ESP8266 board, for example: ``` -USEMODULE=esp_sdk make flash BOARD=esp8266-esp-12x -C tests/shell ... +make flash BOARD=esp8266-esp-12x -C tests/shell ... ``` -The SDK version is compiled automatically if one of the modules `esp_wifi`, -`esp_now` or `esp_sw_timers` is enabled. +For more information about the `make` command variables and specific compile +options, see section [Compile Options](#esp8266_compile_options). -For more information about the make command variables, see section -[Compile Options](#esp8266_compile_options). +Although the port does not use the official ESP8266 RTOS SDK directly, +it must be installed for compilation. The reason is that the port uses most of +the ESP8266 SOC definitions provided by SDK header files. In addition, +it needs the hardware abstraction library (libhal), and ESP8266 WiFi stack +binary libraries which are part of the SDK. + +# MCU ESP8266  [[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: # Short Configuration Reference  [[TOC](#esp8266_toc)] -The following table gives a short reference of all board configuration parameters used by the ESP8266 port in alphabetical order. +The following table gives a short reference of all board configuration +parameters used by the ESP8266 port in alphabetical order.
@@ -82,13 +88,16 @@ Parameter | Short Description | Type* ----------|----------------------------------------|------ [I2C0_SPEED](#esp8266_i2c_interfaces)| Bus speed of I2C_DEV(0) | o [I2C0_SCL](#esp8266_i2c_interfaces) | GPIO used as SCL for I2C_DEV(0) | o -[I2C0_SDA](#esp8266_i2c_interfaces) | GPIO used as SCL for I2C_DEV(0 | o -[PWM0_GPIOS](#esp8266_pwm_channels) | GPIOs that can be used at channels of PWM_DEV(0) | o +[I2C0_SDA](#esp8266_i2c_interfaces) | GPIO used as SCL for I2C_DEV(0) | o +[I2C1_SPEED](#esp8266_i2c_interfaces)| Bus speed of I2C_DEV(1) | o +[I2C1_SCL](#esp8266_i2c_interfaces) | GPIO used as SCL for I2C_DEV(1) | o +[I2C1_SDA](#esp8266_i2c_interfaces) | GPIO used as SCL for I2C_DEV(1) | o +[PWM0_GPIOS](#esp8266_pwm_channels) | GPIOs that can be used at channels of PWM_DEV(0) | o [SPI0_CS0](#esp8266_spi_interfaces) | GPIO used as default CS for SPI_DEV(0) | o
-Type: m - mandatory, o - optional# MCU ESP8266  [[TOC](#esp8266_toc)] +*Type: m - mandatory, o - optional The following table gives a short reference in alphabetical order of modules that can be enabled/disabled by board configurations and/or application's makefile using `USEMODULE` and `DISABLE_MODULE`. @@ -96,21 +105,16 @@ The following table gives a short reference in alphabetical order of modules th Module | Default | Short description ----------|----------|------------------- -[esp_gdb](#esp8266_qemu_mode_and_gdb) | not used | Enable the compilation with debug information for debugging. -[esp_now](#esp8266_esp_now_network_interface) | not used | Enable the ESP-NOW network device, implies the module `esp_sdk`. -[esp_sdk](#esp8266_sdk_task_handling) | not used | Enable the SDK version. -[esp_spiffs](#esp8266_spiffs_device) | not used | Enable the SPIFFS drive in on-board flash memory. -[esp_sw_timer](#esp8266_timers) | not used | Enable software timer implementation, implies the module `esp_sdk`. -[esp_wifi](#esp8266_wifi_network_interface) | not used | Enable the built-in WiFi module as `netdev` network device, implies the module `esp_sdk`. +[esp_gdb](#esp8266_qemu_mode_and_gdb) | not used | enable the compilation with debug information for debugging +[esp_gdbstub](#esp8266_esp_gdbstub) | not used | enable the compilation of the `gdbstub` interface +[esp_idf_heap](#esp8266_esp_idf_heap_implementation) | not used | use SDK heap implementation +[esp_now](#esp8266_esp_now_network_interface) | not used | enable the ESP-NOW network device +[esp_qemu](#esp8266_qemu_mode_and_gdb) | not used | generate image for `QEMU` and `GDB` debugging +[esp_spiffs](#esp8266_spiffs_device) | not used | enable SPIFFS for on-board flash memory +[esp_wifi](#esp8266_wifi_network_interface) | not used | enable the Wifi network device -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: - -## Features of ESP8266  [[TOC](#esp8266_toc)] - -The key features of ESP8266 are: -
MCU | ESP8266EX @@ -135,106 +139,118 @@ Technical Reference | [Technical Reference](https://www.espressif.com/sites/defa

-@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. +@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. -## Features Supported by RIOT-OS  [[TOC](#esp8266_toc)] +# Toolchain -The RIOT-OS for ESP8266 SoCs supports the following features at the moment: +The following software components are required for compilation: -- I2C interfaces -- SPI interfaces -- UART interfaces -- CPU ID access -- ADC channel -- PWM channels -- SPI Flash Drive (MTD with SPIFFS and VFS) -- Hardware number generator -- Hardware timer devices -- ESP-NOW netdev interface +- Xtensa GCC compiler suite for ESP8266 +- Modified ESP8266 RTOS SDK which includes all SOC definitions, + the hardware abstraction library `libhal.a` and some other binary libraries +- ESP flash programmer tool `esptool.py` +There are two options to install the toolchain: -# Toolchain  [[TOC](#esp8266_toc)] +- using a riotdocker image, see section [RIOT Docker Toolchain (riotdocker)](#esp8266_riot_docker_toolchain) +- manual installation, see section [Manual Toolchain Installation](#esp8266_manual_toolchain_installation) -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 `esptool.py` -- **newlib-c** library for Xtensa (esp-open-rtos version) -- **SDK (optional)**, either as part of `esp-open-sdk` or the `ESP8266_NONOS_SDK` - -You have the following options to install the Toolchain: - -- `riotdocker` image and `esptool.py`, 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) +In both cases, the ESP flash programmer tool `esptool.py` has to be installed, +see section [Installation of `esptool.py`](#esp8266_installation_of_esptool). ## RIOT Docker Toolchain (riotdocker)  [[TOC](#esp8266_toc)] -The easiest way to use the toolchain is Docker. +The easiest way to install the toolchain is to use the RIOT Docker image +`riotdocker`. The compilation process using Docker consists of two steps + +1. making the RIOT application in Docker with command `make BOARD=...` +2. flashing the RIOT application on the host computer with command + `make flash-only BOARD=...` + +where step 2 requires that the ESP flash programmer tool `esptool.py` is +installed. Both steps can also be performed with a single command on the host +system using the `BUILD_IN_DOCKER` variable: + +``` +`BUILD_IN_DOCKER=1 make BOARD=... flash +``` ### Preparing the Environment  [[TOC](#esp8266_toc)] -Using RIOT Docker requires at least the following software: +Using RIOT Docker requires at least the following software components: -- `Docker` container virtualization software +- Docker container virtualization software - RIOT Docker (`riotdocker`) image -- flasher tool `esptool.py` +- ESP flash programmer tool `esptool.py` + +For information about installing Docker on your host, refer to the appropriate +manuals for your operating system. The easiest way to install Docker on an +Ubuntu/Debian system is for example: -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 `esptool.py` 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 -``` - -`esptool.py` 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. +For information on how to install `esptool.py`, see section +[Installation of `esptool.py`](#esp8266_installation_of_esptool). ### Generating a riotdocker Image  [[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: +A `riotdocker` fork that only installs the toolchain for ESP8266 RTOS SDK is +available at [GitHub](https://github.com/gschorcht/riotdocker-Xtensa-ESP.git). +After cloning this git repository, checkout branch `esp8266_only_rtos_sdk` to +generate a Docker image with a size of "only" 890 MByte: ``` +cd $HOME/esp git clone https://github.com/gschorcht/riotdocker-Xtensa-ESP.git cd riotdocker-Xtensa-ESP -git checkout esp8266_only +git checkout esp8266_only_rtos_sdk 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: +A `riotdocker` version that contains toolchains for all plattforms supported +by RIOT 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 +docker run -i -t -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. +@note RIOT's root directory `/path/to/RIOT` becomes visible as the home +directory of the `riotbuild` user in the Docker. That is, the output +of compilations performed in RIOT Docker are 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. ### Using an Existing riotdocker Image  [[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 +Alternatively, an existing Docker image from Docker Hub can be used. You can +either pull and start the +[schorcht/riotbuild_esp8266_rtos](https://hub.docker.com/r/schorcht/riotbuild_esp8266_rtos) +Docker image which only contains the toolchain for ESP8266 RTOS SDK using + ``` cd /path/to/RIOT -docker run -i -t --privileged -v /dev:/dev -u $UID -v $(pwd):/data/riotbuild schorcht/riotbuild_esp8266 +docker run -i -t -u $UID -v $(pwd):/data/riotbuild schorcht/riotbuild_esp8266_rtos_sdk ``` -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 + +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 +docker run -i -t -u $UID -v $(pwd):/data/riotbuild riot/riotbuild ``` + ### Make Process with Docker Image  [[TOC](#esp8266_toc)] Using Docker, the make process consists of the following two steps: @@ -242,7 +258,9 @@ 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: +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 ... @@ -251,203 +269,200 @@ 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. +The RIOT binary has to be flash outside docker on the host system. Since the +Docker image was started 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 ``` - -## Precompiled Toolchain  [[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:
`cppcheck` `coccinelle` `curl` `doxygen` `git` `graphviz` `make` `pcregrep` `python` `python-serial` `python3` `python3-flake8` `unzip` `wget` - -To install the toolchain use the following commands: +@note Both steps can also be performed with a single command on the host +system using the `BUILD_IN_DOCKER` variable: ``` -cd /opt -sudo git clone https://github.com/gschorcht/RIOT-Xtensa-ESP8266-toolchain.git esp +`BUILD_IN_DOCKER=1 make BOARD=... flash ``` -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. ## Manual Toolchain Installation  [[TOC](#esp8266_toc)] -The most difficult way to install the toolchain is the manual installation of required components as described below. +A more difficult way to install the toolchain is the manual installation of +all 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` +@note To install the toolchain manually, a 64-bit Linux system is required. +Furthermore, the following packages (Debian/Ubuntu) have to be installed:
+`build-essential`, `cppcheck`, `coccinelle`, `curl`, `doxygen`, `git`, +`graphviz`, `make`, `pcregrep`, `python`, `python-serial`, `python3`, +`python3-flake8`, `unzip`, `wget` -### Installation of esp-open-sdk  [[TOC](#esp8266_toc)] +### Installation of Xtensa GCC compiler suite  [[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. +The Xtensa GCC compiler for ESP8266 configured for use with RIOT-OS can +be downloaded and installed as precompiled binary archive from +[GitHub](https://github.com/gschorcht/xtensa-lx106-elf): ``` -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 +mkdir -p $HOME/esp +cd $HOME/esp +git clone https://github.com/gschorcht/xtensa-lx106-elf ``` -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. +Once the compiler is installed, you have to expand your `PATH` variable by +the directory with Xtensa GCC binaries: ``` -make STANDALONE=y +export PATH=$HOME/esp/xtensa-lx106-elf/bin:$PATH ``` -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. +### Installation of the ESP8266 RTOS SDK  [[TOC](#esp8266_toc)] + +To compile RIOT-OS with the ESP8266 RTOS SDK, a modified version of the SDK is +required. This modified version can also be downloaded as +[GIT](https://github.com/gschorcht/RIOT-Xtensa-ESP8266-RTOS-SDK.git) repository. ``` -make toolchain esptool libhal STANDALONE=n +cd $HOME/esp +git clone https://github.com/gschorcht/RIOT-Xtensa-ESP8266-RTOS-SDK.git ESP8266_RTOS_SDK +cd ESP8266_RTOS_SDK/ +git checkout release/v3.1-for-riot-os ``` +@note +- Please be sure to checkout the correct branch that was used for the +RIOT-OS port. Other versions will not work because they do not have the +necessary changes. +- Since we only use a few header files and some binary libraries, ESP8266 RTOS +SDK does not need to be compiled in any way. -Once compilation has been finished, the toolchain is available in `$PWD/xtensa-lx106-elf/bin`. To use it, set the `PATH` variable accordingly. +To use the installed ESP8266 RTOS SDK, set the environment variable +`ESP8266_SDK_DIR`. ``` -export PATH=$ESP_OPEN_SDK_DIR/xtensa-lx106-elf/bin:$PATH +export ESP8266_SDK_DIR=$HOME/esp/ESP8266_RTOS_SDK ``` -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. + +### Installation of `esptool.py` (ESP flash programmer tool)  [[TOC](#esp8266_toc)] + +The RIOT OS port does not work with the `esptool.py` ESP flasher program +available on GitHub [GitHub](https://github.com/espressif/esptool). +Instead, the modified version from the ESP8266 RTOS SDK has to be used. + +To avoid the installation of the ESP8266 RTOS SDK, for example because +`riotdocker` is used for compilation, `esptool.py` has been extracted +from the ESP8266 RTOS SDK and is available as standalone copy from +[GitHub](https://github.com/gschorcht/esptool-esp8266-rtos-sdk-v3). ``` -export ESP8266_SDK_DIR=$ESP_OPEN_SDK_DIR/sdk +cd $HOME/esp +git clone https://github.com/gschorcht/esptool-esp8266-rtos-sdk-v3 +chmod +x $HOME/esp/esptool-esp8266-rtos-sdk-v3/esptool.py ``` - -### Installation of newlib-c  [[TOC](#esp8266_toc)] - -First, set the target directory for the installation. +You have to expand your `PATH` variable to use it. ``` -export ESP8266_NEWLIB_DIR=/path/to/esp/newlib-xtensa +export PATH=$HOME/esp/esptool-esp8266-rtos-sdk-v3:$PATH ``` -Please take care, to use the newlib-c version that was modified for esp-open-rtos since it includes `stdatomic.h`. +`esptool.py` depends on `pySerial` which can be installed either using `pip` ``` -cd /my/source/dir -git clone https://github.com/ourairquality/newlib.git +sudo pip install pyserial ``` - -Once you have cloned the GIT repository, build and install it with following commands. +or the package manager of your OS, for example on Debian/Ubuntu systems: ``` -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 -``` - -### Installation of Espressif original SDK (optional)  [[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 +apt-get install pyserial ``` +For more information on `esptool.py`, please refer the +[git repository](https://github.com/espressif/esptool). # Flashing the Device  [[TOC](#esp8266_toc)] ## Toolchain Usage  [[TOC](#esp8266_toc)] -Once you have installed all required components, you should have the following directories. +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) +/path/to/esp/xtensa-lx106-elf +/path/to/esp/ESP8266_RTOS_SDK +/path/to/esp/esptool-esp8266-rtos-sdk-v3 ``` -To use the toolchain and optionally the SDK, please check that your environment variables are set correctly to +To use the toolchain, 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 +export PATH=/path/to/esp/esptool-esp8266-rtos-sdk-v3:/path/to/esp/xtensa-lx106-elf/bin:$PATH +export ESP8266_SDK_DIR=/path/to/esp/ESP8266_RTOS_SDK ``` ## Compile Options  [[TOC](#esp8266_toc)] -The compilation process can be controlled by a number of variables for the make command: +The compilation process can be controlled by following make variables:
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) -PORT | /dev/ttyUSBx | /dev/* | 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) +FLASH_MODE | dout, dio, qout, qio | dout | Set the flash mode, please take +care with your module, see section [Flash Modes](#esp8266_flash_modes) +PORT | /dev/<port> | /dev/USB0 | Set the USB port for flashing the firmware

-Optional features of ESP8266 can be enabled by `USEMODULE` definitions in the makefile of the application. These are: +Optional features of ESP8266 can be enabled using `USEMODULE` definitions +in the makefile of the application. These are:
Module | Description -------|------------ -[esp_gdb](#esp8266_qemu_mode_and_gdb) | Enable the compilation with debug information, which is equivalent to using `ENABLE_GDB=1`. -[esp_now](#esp8266_esp_now_network_interface) | Enable the built-in WiFi module with the ESP-NOW protocol as `netdev` network device, implies the setting module `esp_sdk`. -[esp_sdk](#esp8266_sdk_task_handling) | Enable the SDK version, which is equivalent to using `USE_SDK=1`. +[esp_gdb](#esp8266_qemu_mode_and_gdb) | Enable the compilation with debug information +[esp_gdbstub](#esp8266_esp_gdbstub) | Enable the compilation of the `gdbstub` interface for debugging with `GDB` +[esp_idf_heap](#esp8266_esp_idf_heap_implementation) | Enable SDK heap implementation which provides remaining IRAM as additional heap region +[esp_now](#esp8266_esp_now_network_interface) | Enable the built-in WiFi module with the ESP-NOW protocol as `netdev` network device +[esp_qemu](#esp8266_qemu_mode_and_gdb) | Enable the compilation of an `QEMU` image for debugging with `GDB` [esp_spiffs](#esp8266_spiffs_device) | Enable the SPIFFS drive in on-board flash memory -[esp_sw_timer](#esp8266_timers) | Enable software timer implementation, implies the setting module `esp_sdk`. -[esp_wifi](#esp8266_wifi_network_interface) | Use the built-in WiFi module as `netdev` network device, implies the setting module `esp_sdk`. +[esp_sw_timer](#esp8266_timers) | Enable software timer implementation +[esp_wifi](#esp8266_wifi_network_interface) | Enable the built-in WiFi module in infrastructure mode as `netdev` network device

-For example, to activate the SPIFFS drive in on-board flash memory, the makefile of application has simply to add the `esp_spiffs` module to `USEMODULE` make variable: +For example, to activate the SPIFFS drive in on-board flash memory, the +makefile of application has simply to add the `esp_spiffs` module to +`USEMODULE` make variable: + ``` USEMODULE += esp_spiffs ``` -Modules can also be activated temporarily at the command line when calling the make command: +Modules can also be activated temporarily at the command line when calling +the make command: + ``` USEMODULE="esp_spiffs" make BOARD=... ``` ## Flash Modes  [[TOC](#esp8266_toc)] -The `FLASH_MODE` make command variable determines the mode that is used for flash access in normal operation. +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. +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). +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`. +@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`. ## Erasing the Device  [[TOC](#esp8266_toc)] @@ -456,32 +471,12 @@ The flash memory of ESP8266 can be erased completely with following command: esptool.py erase_flash ``` -@note After deleting the flash, the default init data must be rewritten. In a non-SDK version, this will happen automatically when RIOT is started for the first time after flashing the image. In the SDK version, this must be done explicitly. There are two possible approaches to rewriting standard initialization data: - -- Flash and start a non-SDK image before the SDK version is flashed. -- Use the esptool.py file to update the default init data as following. - -``` -esptool.py write_flash
$RIOTBASE/cpu/esp8266/bin/esp_init_data_default.bin -``` - -where `address` depends on ESP8266 chip version. - -Chip version | `address` | Module examples ------------- | ------------- | ---------------- -512 kByte | 0x07c000 | ESP-01, ESP-03, ESP-07, etc. -1 MByte | 0x0fc000 | ESP8285-based modules like Wemos D1 mini lite, PSF-A85, some ESP-01, ESP-03 etc. -2 MByte | 0x1fc000 | | -4 MByte | 0x3fc000 | ESP-12E, NodeMCU devkit 1.0, WeMos D1 mini -8 MByte | 0x7fc000 | | -16 MByte | 0xffc000 | WeMos D1 mini pro (USE 0x07c000!) - - # Peripherals  [[TOC](#esp8266_toc)] ## GPIO pins  [[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. +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.
@@ -507,7 +502,8 @@ GPIO16 | RTC pin and wake up signal in deep sleep mode
-GPIO0, GPIO2, and GPIO15 are bootstrapping pins which are used to boot ESP8266 in different modes: +GPIO0, GPIO2, and GPIO15 are bootstrapping pins which are used +to boot ESP8266 in different modes:
@@ -521,43 +517,60 @@ GPIO0 | GPIO2 | GPIO15 (MTDO) | Mode ## ADC Channels  [[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**. +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. +@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. -## PWM Channels  [[TOC](#esp8266_toc)] +## SPI Interfaces  [[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. +ESP8266 has two SPI controllers: -@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. +- _CSPI_ for caching and accessing the flash memory
+- _HSPI_ for peripherals -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. +Thus, _HSPI_ is the only SPI interface that is available for peripherals. +It is exposed as RIOT's SPI_DEV(0). The pin configuration of the _HSPI_ +interface is fixed as shown in following table. -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) +Signal | Pin +-----------|------- +#SPI0_MISO | GPIO12 +#SPI0_MOSI | GPIO13 +#SPI0_SCK | GPIO14 +#SPI0_CS0 | GPIOn with n = 0, 2, 4, 5, 15, 16 (additionally 9, 10 in DOUT flash mode) -``` -#define PWM0_CHANNEL_GPIOS { GPIO12, GPIO13, GPIO14, GPIO15 } -``` +
+ +The only pin definition that can be overridden by an application-specific +board configuration is the CS signal defined by #SPI0_CS0. + +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. ## I2C Interfaces  [[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 `I2C_SPEED_FAST_PLUS`. The maximum number of buses that can be defined is 2, `I2C_DEV(0)` ... `I2C_DEV(1)`. +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) and #I2C_DEV(1). -The board-specific configuration of the I2C interface `I2C_DEV(n)` requires the definition of - -- `I2Cn_SPEED`, the bus speed, -- `I2Cn_SCL`, the GPIO used as SCL signal, and -- `I2Cn_SDA`, the GPIO used as SDA signal, - -where `n` can be 0 or 1. If they are not defined, the I2C interface `I2C_DEV(n)` is not used. +GPIO pins (#I2C0_SCL, #I2C0_SDA and/or `I2C1_SCL`, `I2C1_SDA`) +have to be defined in the board-specific peripheral configuration in +`$BOARD/periph_conf.h`. Furthermore, the default I2C bus speed (#I2C0_SPEED +and/or `I2C1_SPEED`) that is used for I2C bus(ses) has to be defined. The +number of configured buses #I2C_NUMOF is then determined automatically +from these definitions. 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 @@ -565,8 +578,6 @@ In the following example, only one I2C bus is defined: 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 @@ -576,64 +587,71 @@ A configuration with two I2C buses would look like the following: #define I2C1_SCL GPIO14 ``` -All these configurations can be overridden by an [application-specific board configuration](#esp8266_application_specific_board_configuration). +All these configurations can be overridden by an +[application-specific board configuration](#esp8266_application_specific_board_configuration). -## SPI Interfaces  [[TOC](#esp8266_toc)] +## PWM Channels  [[TOC](#esp8266_toc)] -ESP8266 provides two hardware SPI interfaces: +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. -- _FSPI_ for flash memory access that is usually simply referred to as _SPI_ -- _HSPI_ for peripherals +@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. -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 GPIOs that can be used as channels of the PWM device #PWM_DEV(0) are +defined by #PWM0_GPIOS. By default, all GPIOs that are not used as I2C, SPI, +or UART signals are defined as PWM channels in board definition. As long as +these channels are not initialized with function #pwm_init, they can be used as +normal GPIOs for other purposes. Even if they are already initialized, but +have a duty cycle value of 0 can be used as output GPIOs for other purposes. +GPIOs in #PWM0_GPIOS that are used for other purposes, e.g., I2C or SPI, are +no longer available as PWM channels. -The pin configuration of the _HSPI_ interface is defined as shown in the following table. Only the CS signal can be configured and overridden by [application-specific card configuration] (# esp8266_application_specific_board_configuration). +To define other GPIOs as PWM channels, just overwrite the definition of +#PWM0_GPIOS in an +[application-specific board configuration](#esp8266_application_specific_board_configuration) -
- -Signal of _HSPI_ | Pin ------------------|------- -MISO | GPIO12 -MOSI | GPIO13 -SCK | GPIO14 -CS | GPIO15 - -
- -When SPI is enabled using module `periph_spi`, these GPIOs cannot be used for any other purpose. The given CS pin is used when `spi_acquire` is called with `cs=GPIO_UNDEF` parameter. - -To the default CS can be overridden as following: ``` -#define SPI0_CS0_GPIO GPIO15 /* HSPI/SPI_DEV(0) CS default pin */ +#define PWM0_GPIOS { GPIO12, GPIO13, GPIO14, GPIO15 } ``` -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. - ## Timers  [[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** +- **hardware timer** implementation with **1 timer device** and + only **1 channel** (default) +- **software timer** implementation with **1 timer device** and **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`). +Software timers use SDK's timers to implement the timer device and the 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. ## SPIFFS Device  [[TOC](#esp8266_toc)] -If SPIFFS module is enabled (`USEMODULE += esp_spiffs`), the implemented MTD system drive `mtd0` for the on-board SPI flash memory is used together with modules `spiffs` and `vfs` to realize a persistent file system. +If SPIFFS module is enabled (`USEMODULE += esp_spiffs`), the implemented +MTD system drive #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: +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`. +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`. ## Other Peripherals  [[TOC](#esp8266_toc)] @@ -650,19 +668,18 @@ RTC is not yet implemented. ESP8266 provides different built-in possibilities to realize network devices: -- ESP WiFi, usual AP-based infrastructure mode wireless LAN -- ESP-NOW, a WiFi based AP-less and connectionless peer to peer communication protocol +- ESP WiFi, usual AP-based wireless LAN (not yet supported) +- ESP-NOW, a WiFi based AP-less and connectionless peer to + peer communication protocol \anchor esp8266_wifi_network_interface ## WiFi Network Interface  [[TOC](#esp8266_toc)] -The RIOT port for ESP8266 implements in module `esp_wifi` a `netdev` driver for +The RIOT port for ESP8266 implements in module `esp_wifi` a `netdev` +driver for the built-in WiFi interface. -@note Due to symbol conflicts with the `crypto` and `hash` modules of RIOT -in vendor library `libwpa.so`, which is required by module -`esp_wifi`, `esp_wifi` cannot be used for applications that use these modules. -Therefore, module **`esp_wifi` is not automatically enabled** when module +@note Module `esp_wifi` is not automatically enabled when module `netdev_default` is used. Instead, if necessary, the application has to add the module `esp_wifi` in the Makefile. @@ -674,17 +691,18 @@ Furthermore, the following configuration parameters have to be defined:
-Parameter | Default | Description -:------------------|:--------------------------|:------------ -ESP_WIFI_SSID | "RIOT_AP" | SSID of the AP to be used. -ESP_WIFI_PASS | "ThisistheRIOTporttoESP" | Passphrase used for the AP as clear text (max. 64 chars). -ESP_WIFI_STACKSIZE | 1536 | Stack size used for the WiFi netdev driver thread. +Parameter | Default | Description +:-------------------|:--------------------------|:------------ +#ESP_WIFI_SSID | "RIOT_AP" | SSID of the AP to be used. +#ESP_WIFI_PASS | "ThisistheRIOTporttoESP" | Passphrase used for the AP as clear text (max. 64 chars). +#ESP_WIFI_STACKSIZE | #THREAD_STACKSIZE_DEFAULT |Stack size used for the WiFi netdev driver thread.
These configuration parameter definitions, as well as enabling the `esp_wifi` module, can be done either in the makefile of the project or at make command line, e.g.: + ``` USEMODULE=esp_wifi \ CFLAGS='-DESP_WIFI_SSID=\"MySSID\" -DESP_WIFI_PASS=\"MyPassphrase\"' \ @@ -702,18 +720,17 @@ border router for a mesh network which uses ESP-NOW. With ESP-NOW, the ESP8266 provides a connectionless communication technology, featuring short packet transmission. It applies the IEEE802.11 Action Vendor frame technology, along with the IE function developed by Espressif, and CCMP -encryption technology, realizing a secure, connectionless communication solution. +encryption technology, realizing a secure, connectionless communication +solution. The RIOT port for ESP8266 implements in module `esp_now` a `netdev` driver which uses ESP-NOW to provide a link layer interface to a meshed network of ESP8266 nodes. In this network, each node can send short packets with up to 250 data bytes to all other nodes that are visible in its range. -@note Due to symbol conflicts in vendor library `libwpa.so` used by the -`esp_now` with RIOT's `crypto` and `hashes` modules, ESP-NOW cannot be used -for application that use these modules. Therefore, the module **`esp_now` is -not enabled automatically** if the `netdev_default` module is used. Instead, -the application has to add the `esp_now` module in its makefile when needed.
+@note Module `esp_now` is not enabled automatically if the `netdev_default` +module is used. Instead, the application has to add the `esp_now` module in +its makefile when needed.
``` USEMODULE += esp_now ``` @@ -731,24 +748,13 @@ be overriden by [application-specific board configurations](#esp8266_application Parameter | Default | Description :---------|:--------|:----------- -ESP_NOW_SCAN_PERIOD | 10000000UL | Defines the period in us at which an node scans for other nodes in its range. The default period is 10 s. -ESP_NOW_SOFT_AP_PASS | "ThisistheRIOTporttoESP" | Defines the passphrase as clear text (max. 64 chars) that is used for the SoftAP interface of ESP-NOW nodes. It has to be same for all nodes in one network. -ESP_NOW_CHANNEL | 6 | Defines the channel that is used as the broadcast medium by all nodes together. -ESP_NOW_KEY | NULL | Defines a key that is used for encrypted communication between nodes. If it is NULL, encryption is disabled. The key has to be of type `uint8_t[16]` and has to be exactly 16 bytes long. -ESP_WIFI_STACKSIZE | @ref THREAD_STACKSIZE_DEFAULT | Stack size used for the WiFi netdev driver thread. +#ESP_NOW_SCAN_PERIOD | 10000000UL | Defines the period in us at which an node scans for other nodes in its range. The default period is 10 s. +#ESP_NOW_SOFT_AP_PASS | "ThisistheRIOTporttoESP" | Defines the passphrase as clear text (max. 64 chars) that is used for the SoftAP interface of ESP-NOW nodes. It has to be same for all nodes in one network. +#ESP_NOW_CHANNEL | 6 | Defines the channel that is used as the broadcast medium by all nodes together. +#ESP_NOW_KEY | NULL | Defines a key that is used for encrypted communication between nodes. If it is NULL, encryption is disabled. The key has to be of type `uint8_t[16]` and has to be exactly 16 bytes long.
-These configuration parameter definitions, as well as enabling the `esp_wifi` -module, can be done either in the makefile of the project or at make command -line, e.g.: - -``` -USEMODULE=esp_now \ -CFLAGS='-DESP_NOW_CHANNEL=8 -DESP_NOW_SOFT_AP_PASS=\"MyPassphrase\"' \ -make -C examples/gnrc_networking BOARD=... -``` - @note The ESP-NOW network interface (module `esp_now`) and the [Wifi network interface](#esp8266_wifi_network_interface) (module `esp_wifi`) can be used simultaneously, for example, to realize a border router for @@ -756,42 +762,52 @@ a mesh network which uses ESP-NOW. # Preconfigured Devices  [[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. +The ESP8266 port of RIOT has been tested with several common external +devices that can be connected to ESP8266 boards and are preconfigured +accordingly. ## Network Devices  [[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 port for ESP8266 has been tested with the following network devices: +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 +port for ESP8266 has been tested with the following network devices: -- [mrf24j40](https://riot-os.org/api/group__drivers__mrf24j40.html) (driver for Microchip MRF24j40 based IEEE 802.15.4 -- [enc28j60](https://riot-os.org/api/group__drivers__enc28j60.html) (driver for Microchip ENC28J60 based Ethernet modules) +- [mrf24j40](https://riot-os.org/api/group__drivers__mrf24j40.html) + (driver for Microchip MRF24j40 based IEEE 802.15.4 +- [enc28j60](https://riot-os.org/api/group__drivers__enc28j60.html) + (driver for Microchip ENC28J60 based Ethernet modules) ### Using MRF24J40 (module `mrf24j40`)  [[TOC](#esp8266_toc)] -To use MRF24J40 based IEEE 802.15.4 modules as network device, the `mrf24j40` driver module has to be added to the makefile of the application: +To use MRF24J40 based IEEE 802.15.4 modules as network device, the +`mrf24j40` driver module has to be added to the makefile of the application: ``` USEMODULE += mrf24j40 ``` -The `mrf24j40` driver module uses the following preconfigured interface parameters for ESP8266 boards: +The `mrf24j40` driver module uses the following preconfigured interface +parameters for ESP8266 boards:
-Parameter | Default | Remarks ------------------------|--------------|---------------------------- -MRF24J40_PARAM_SPI | SPI_DEV(0) | fixed, see section [SPI Interfaces](#esp8266_spi_interfaces) -MRF24J40_PARAM_SPI_CLK | SPI_CLK_1MHZ | fixed -MRF24J40_PARAM_CS | GPIO16 | can be overridden -MRF24J40_PARAM_INT | GPIO0 | can be overridden -MRF24J40_PARAM_RESET | GPIO2 | can be overridden +Parameter | Default | Remarks +------------------------|--------------|---------------------------- +#MRF24J40_PARAM_SPI | SPI_DEV(0) | fixed, see section [SPI Interfaces](#esp8266_spi_interfaces) +#MRF24J40_PARAM_SPI_CLK | SPI_CLK_1MHZ | fixed +#MRF24J40_PARAM_CS | GPIO16 | can be overridden +#MRF24J40_PARAM_INT | GPIO0 | can be overridden +#MRF24J40_PARAM_RESET | GPIO2 | can be overridden

-The GPIOs in this configuration can be overridden by [application-specific board configurations](#esp8266_application_specific_board_configuration). +The GPIOs in this configuration can be overridden by +[application-specific board configurations](#esp8266_application_specific_board_configuration). ### Using ENC28J60 (module `enc28j60`)  [[TOC](#esp8266_toc)] -To use ENC28J60 Ethernet modules as network device, the `enc28j60` driver module has to be added to the makefile of the application: +To use ENC28J60 Ethernet modules as network device, the `enc28j60` driver +module has to be added to the makefile of the application: ``` USEMODULE += enc28j60 @@ -801,65 +817,91 @@ The `enc28j60` driver module uses the following preconfigured interface paramete
-Parameter | Default | Remarks ----------------------|--------------|---------------------------- -ENC28J60_PARAM_SPI | SPI_DEV(0) | fixed, 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 +Parameter | Default | Remarks +----------------------|--------------|---------------------------- +#ENC28J60_PARAM_SPI | SPI_DEV(0) | fixed, 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
-The GPIOs in this configuration can be overridden by [application-specific board configurations](#esp8266_application_specific_board_configuration). +The GPIOs in this configuration can be overridden by +[application-specific board configurations](#esp8266_application_specific_board_configuration). ## SD-Card Device  [[TOC](#esp8266_toc)] -ESP8266 port of RIOT is preconfigured for RIOT applications that use the [SPI SD-Card driver](https://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: +ESP8266 port of RIOT is preconfigured for RIOT applications that use the +[SPI SD-Card driver](https://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 ``` -The `sdcard_spi` driver module uses the following preconfigured interface parameters for ESP8266 boards: +The `sdcard_spi` driver module uses the following preconfigured interface +parameters for ESP8266 boards:
-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 +Parameter | Default | Remarks +------------------------|-------------|---------------------------- +#SDCARD_SPI_PARAM_SPI | #SPI_DEV(0) | fix, see section [SPI Interfaces](#esp8266_spi_interfaces) +#SDCARD_SPI_PARAM_CS | #SPI0_CS0 | can be overridden
-The GPIO used as CS signal can be overridden by [application-specific board configurations](#esp8266_application_specific_board_configuration). +The GPIO used as CS signal can be overridden by +[application-specific board configurations](#esp8266_application_specific_board_configuration). \anchor esp8266_app_spec_conf # Application-Specific Configurations  [[TOC](#esp8266_toc)] -The board-specific configuration files `board.h` and `periph_conf.h` as well as the driver parameter configuration files `_params.h` define the default configurations for peripherals and device driver modules. These are, for example, the GPIOs used, bus interfaces used or available bus speeds. Because there are many possible configurations and many different application requirements, these default configurations are usually only a compromise between different requirements. +The board-specific configuration files `board.h` and `periph_conf.h` as +well as the driver parameter configuration files `_params.h` +define the default configurations for peripherals and device driver +modules. These are, for example, the GPIOs used, bus interfaces used +or available bus speeds. Because there are many possible configurations +and many different application requirements, these default configurations +are usually only a compromise between different requirements. -Therefore, it is often necessary to change some of these default configurations for individual applications. For example, while many PWM channels are needed in one application, another application does not need PWM channels, but many ADC channels. +Therefore, it is often necessary to change some of these default +configurations for individual applications. For example, while many +PWM channels are needed in one application, another application does +not need PWM channels, but many ADC channels. ## Application-Specific Board Configuration  [[TOC](#esp8266_toc)] -To override default board configurations, simply create an application-specific board configuration file `$APPDIR/board.h` in the source directory `$APPDIR` of the application 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. +To override default board configurations, simply create an +application-specific board configuration file `$APPDIR/board.h` in +the source directory `$APPDIR` of the application 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: +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 } +#define PWM0_GPIOS { GPIO12, GPIO13, GPIO14, GPIO15 } #endif #include_next "board.h" ``` -It is important 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`. +It is important 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) ``` -@note To make such application-specific board configurations dependent on the ESP8266 MCU or a particular ESP8266 board, you should always enclose these definitions in the following constructs +@note To make such application-specific board configurations dependent +on the ESP8266 MCU or a particular ESP8266 board, you should always +enclose these definitions in the following constructs ``` #ifdef CPU_ESP8266 ... @@ -872,9 +914,18 @@ INCLUDES += -I$(APPDIR) ## Application-Specific Driver Configuration  [[TOC](#esp8266_toc)] -Using the approach for overriding board configurations, the parameters of drivers that are typically defined in `drivers//include/_params.h` can be overridden. For that purpose just create an application-specific driver parameter file `$APPDIR/_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 `_params.h` after that, add the `include_next` preprocessor directive as the last line. +Using the approach for overriding board configurations, the parameters +of drivers that are typically defined in +`drivers//include/_params.h` can be overridden. For that +purpose just create an application-specific driver parameter file +`$APPDIR/_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 `_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/_params.h` could look like the following: +For example, to override a GPIO used for LIS3DH sensor, the +application-specific driver parameter file `$APPDIR/_params.h` +could look like the following: ``` #ifdef CPU_ESP8266 #define LIS3DH_PARAM_INT2 (GPIO_PIN(0, 4)) @@ -883,12 +934,16 @@ For example, to override a GPIO used for LIS3DH sensor, the application-specific #include_next "lis3dh_params.h" ``` -It is important to ensure that the application-specific driver parameter file `$APPDIR/_params.h` is included first. Insert the following line as the first line to the application makefile `$APPDIR/Makefile`. +It is important to ensure that the application-specific driver parameter +file `$APPDIR/_params.h` is included first. Insert the following +line as the first line to the application makefile `$APPDIR/Makefile`. ``` INCLUDES += -I$(APPDIR) ``` -**Pleae note:** To make such application-specific board configurations dependent on the ESP8266 MCU or a particular ESP8266 board, you should always enclose these definitions in the following constructs: +**Pleae note:** To make such application-specific board configurations +dependent on the ESP8266 MCU or a particular ESP8266 board, you should +always enclose these definitions in the following constructs: ``` #ifdef CPU_ESP8266 ... @@ -899,53 +954,76 @@ INCLUDES += -I$(APPDIR) #endif ``` -# SDK Task Handling  [[TOC](#esp8266_toc)] +# SDK Specific Information  [[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 is also used automatically when software timers are used by activating -the `esp_sw_timer` module. +## SDK Tasks  [[TOC](#esp8266_toc)] -Internally, the SDK uses its own priority-based multitasking sytsem, -the **ETS**, to handle hardware components such as the WiFi interface, or to -implement event-driven functions such as software timers. ETS periodically -executes all ETS tasks with pending events in an infinite loop with the ROM -function `ets_run`. +ESP8266 RTOS SDK libraries create a number of high-priority threads, see the +listing below, which handle high priority interrupts from SoC and WiFi +hardware. These threads are also created, if the WiFi hardware is not used. -ETS doesn't process interrupts directly in interrupt service routines. -Instead, they use the `ets_post` ROM function to send an event to one of the -ETS tasks, which then processes the interrupts asynchronously. Context -switches are not possible in interrupt service routines. +``` + pid | name | state Q | pri | stack ( used) | base addr | current + - | isr_stack | - - | - | 2048 ( 832) | 0x3ffe8420 | 0x3ffe8c20 + 1 | ppT | bl rx _ | 2 | 3632 ( 1296) | 0x3fff5df0 | 0x3fff6ac0 + 2 | pmT | bl rx _ | 4 | 1072 ( 320) | 0x3fff6c70 | 0x3fff6f70 + 3 | rtT | bl rx _ | 3 | 2096 ( 1376) | 0x3fff70b0 | 0x3fff77b0 + 4 | esp_events | bl rx _ | 5 | 2096 ( 864) | 0x3fff7f20 | 0x3fff8600 +``` -To use SDK functions and keep the system alive, ETS tasks with pending events -have to be handled. For that purpose +## SDK Heap Implementation  [[TOC](#esp8266_toc)] -- the `ets_task_func` RIOT thread with highest possible priority is used -- the ROM functions `ets_run` and `ets_post` are overwritten. +Using module `esp_idf_heap` enables the compilation of SDK heap handling +instead of memory management provided by `newlibc`. -The `ets_task_func` RIOT thread is waiting for a thread flag, which is set -by the `ets_post` function at the end of an ETS interrupt service routine. -The flag indicates that there are ETS tasks with pending events that need -to be executed. The `ets_task_func` RIOT thread then calls the `ets_run` -function, which performs all ETS tasks with pending events exactly once. +Normally, the remaining ESP8266 DRAM, which is not used by static variables, +is provided as heap memory. The SDK heap implementation also provides the unused +ESP8266 IRAM (Command RAM) as the additional heap memory region. -Thus, when a hardware component used by the SDK triggers an interrupt, e.g. -the WiFi interface, the interrupt sevice routine posts an event to the ETS -task by calling the `ets_post` function. The overwritten version of this -function sets the thread flag of the `ets_task_func` thread. The thread -then calls function `ets_run` to process pending events. +@note The heap in IRAM allows only 32-bit-aligned word access. -@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. +The following example shows the heap when the `esp_idf_heap` is used: -# QEMU Mode and GDB  [[TOC](#esp8266_toc)] +``` +Heap region 0 @40107690: 18800 (used 8, free 18792) [bytes] +Heap region 1 @3fff1760: 59552 (used 8520, free 51032) [bytes] +``` -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. +In this example, heap region 0 at address `0x401xxxxx` is located in IRAM and +heap region 1 at address `0x3fffxxxx` in DRAM. While memory management +functions of `newlibc` use always heap region 1 in DRAM, functions of +binary SDK libraries like the WiFi stack can also also heap region 0 +for 32-bit aligned data. -The binary image can be compiled with debugging information (`ENABLE_GDB=1` or module `esp_gdb`) 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. +# Debugging  [[TOC](#esp8266_toc)] -To use QEMU, you have to install QEMU for Xtensa with ESP8266 machine implementation as following. +There are two options to debug your RIOT application for ESP8266 either + +- using `QEMU` and module `esp_gdb`, see [QEMU Mode andB](#esp8266_qemu_mode_and_gdb) or +- using module `esp_gdbstub`, see [Module esp_gdbstub](#esp8266_esp_gdbstub). + +## QEMU Mode and GDB  [[TOC](#esp8266_toc)] + +### Compilation for `QEMU` + +When `QEMU` mode is enabled by using the `esp_qemu` module, `make flash` does +not try to download the image of your RIOT application to the target hardware. +Instead, a binary image `$ELFFILE.bin` as well as the 1 MByte flash image +`esp8266flash.bin` is created in the application build directory `$(BINDIR)`. +This flash image can be used together with `QEMU` and `GDB` to debug the +application. + +For debugging purposes, the application should be compiled with debugging +information. This can either be done by using the `esp_gdb` module, for example: + +``` +USEMODULE=esp_gdb make flash BOARD=esp8266-esp-12x -C tests/shell +``` + +### Installation of `QEMU` + +To use `QEMU`, you have to install `QEMU` for Xtensa with ESP8266 machine +implementation first as following. ``` cd /my/source/dir @@ -958,27 +1036,152 @@ 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 +### Start Debugging with `QEMU` + +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 in first terminal with ``` -$QEMU/bin/qemu-system-xtensa -M esp8266 -nographic -serial stdio -monitor none -s -S -kernel /path/to/the/target/image.elf.bin +term1> $QEMU/bin/qemu-system-xtensa -M esp8266 -nographic -serial stdio -monitor none -s -S \ + -kernel /path/to/build/dir/esp8266flash.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: +where `/path/to/build/dir` is the path to the application build directory +`$(BINDIR)` where `$ELFFILE.bin` is generated by the `make` command, for example +`$(RIOTBASE)/tests/shell/bin/esp8266-esp-12x`. After that you can +start `GDB` in second terminal window using command: ``` -xtensa-lx106-elf-gdb +term2> xtensa-lx106-elf-gdb /path/to/build/dir/image.elf ``` -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: +To start debugging, you have to connect to `QEMU` from `GDB` with command: ``` (gdb) target remote :1234 ``` +@note: `QEMU` does not support the emulation of hardware interrupts or +special hardware modules like the WiFi module. Applications that rely on +interrupts or the WiFi interface can only be debugged with restrictions +with `QEMU` and `GDB`. + +## Module `esp_gdbstub`  [[TOC](#esp8266_toc)] + +### Compilation with `esp_gdbstub` + +Using the `esp_gdbstub` module enables the compilation of the `gdbstub` +interface for ESP8266. This interface implements the target side of the +[GDB Remote Protocol](https://sourceware.org/gdb/onlinedocs/gdb/Remote-Protocol.html#Remote-Protocol). +The initial `gdbstub` implementation for ESP8266 was provided by +[Espressif](https://github.com/espressif/esp-gdbstub). However, for using it +with RIOT it had to be changed a lot. + +@note Enabling the `gdbstub` interface automatically enables the compilation +with debug information (module `esp_gdb`) `gdbstub`. + +### Start Debugging with `esp_gdbstub` + +To start debugging, the application has to be compiled using +module `esp_gdbstub`, for example: + +``` +USEMODULE=esp_gdbstub make flash BOARD=esp8266-esp-12x -C tests/shell +``` + +Once, the application is flashed to ESP82666, debugging can be started as +following. + +1. Start in first terminal window a terminal program which connects to the +port of the ESP8266 module as console window: +``` +term1> python -m serial.tools.miniterm <port> 115200 +``` +where `<port>` is the serial interface to which the ESP8266 module is connected, +e.g., `/dev/ttyUSB0`. + +2. Start GDB with the application in a second terminal window: +``` +term2> xtensa-lx106-elf-gdb /path/to/the/build/dir/image.elf +``` +where `/path/to/build/dir` is the path to the application build directory +`$(BINDIR)` where `$ELFFILE.bin` is generated by the `make` command, for example +`$(RIOTBASE)/tests/shell/bin/esp8266-esp-12x`. + +3. Connect from `GDB` to the ESP8266 module with command: +``` +(gdb) target remote <port> +``` +where `<port>` is the serial interface to which the ESP8266 module is connected, +e.g., `/dev/ttyUSB0`. + +By default, `gdbstub` stops the execution automatically using function +`gdbstub_do_break` after the board initialization and before the kernel +is initialized. + +``` +(gdb) tar rem /dev/ttyUSB0 +Remote debugging using /dev/ttyUSB0 +gdbstub_do_break_breakpoint_addr () at cpu/esp8266/vendor/esp-gdbstub/gdbstub-entry.S:400 +400 break 0,0 +(gdb) bt +#0 gdbstub_do_break_breakpoint_addr () at cpu/esp8266/vendor/esp-gdbstub/gdbstub-entry.S:400 +#1 0x40100f89 in gdbstub_init () at cpu/esp8266/vendor/esp-gdbstub/gdbstub.c:985 +``` +At this time, you can set breakpoints, execute +the application stepwise or just continue the execution using the `continue` +command. Please note the limitations below. Once you have started the execution +in `GDB`, you can use `Ctrl-C` in `GDB` or the console window to break it anytime. + +If your application uses console inputs (stdio), such as the `tests/shell` +application, you can type characters in the console window that gdbstub will +forward to the application. However, the echo of these inputs occur in `GDB`. + +@note Since the terminal program and `GDB` are using the same serial port, +typed characters can be lost sporadically. + +When you reset the ESP8266 module, you will observe a message such as the +following after boot messages in the console window. +``` +$T05#b9 +``` +This is simply the first GDB Remote Protocol packet that is generated as a +result of the automatic break during the initialization when the GDB is not +yet connected to the ESP8266 module. + +### Limitations of `esp_gdbstub` + +Due to hardware limitations of the Xtensa architecture, `esp_gdbstub` +debugging has the following limitations: + +- Software breakpoints (command `br`) only work for code in IRAM with + addresses `0x4010xxxx`, see below. +- Code in IROM (flash) with addresses `0x402xxxxx` require a hardware + breakpoint (command `hbr`), see below. +- There is only one hardware breakpoint (command `hbr`). +- There is only one hardware watchpoint (command `watch`). +- Stepwise execution on source code level is only possible in IRAM with + addresses `0x4010xxxx`, see below. + +If you want to use multiple software breakpoints or execute stepwise on +source code level, you have to ensure that the function you want to debug is +located in RAM. For that purpose, add the `IRAM` attribute to that function, +for example: + +``` +#include "esp/common_macros.h" +... +voir IRAM my_func(void) +{ + ... +} +``` + +Then you should be able to set a breakpoint to this function using command `bp` +and to execute it stepwise after break. + +Another option is to use `gdbstub_do_break()` wherever you want to break the +execution. If you know where you want to break before downloading the program +to the target, you can use `gdbstub_do_break()` as much as you want. + */ diff --git a/cpu/esp8266/esp-wifi/Makefile b/cpu/esp8266/esp-wifi/Makefile index 1aee4b75d3..146f3bc6bd 100644 --- a/cpu/esp8266/esp-wifi/Makefile +++ b/cpu/esp8266/esp-wifi/Makefile @@ -1,3 +1,7 @@ MODULE=esp_wifi +# we have to do it in that way to avoid that pkg/lwip is found first +CFLAGS += -I$(ESP8266_SDK_DIR)/components/lwip/lwip/src/include +CFLAGS += -I$(ESP8266_SDK_DIR)/components/lwip/port/esp8266/include/ + include $(RIOTBASE)/Makefile.base diff --git a/cpu/esp8266/esp-wifi/doc.txt b/cpu/esp8266/esp-wifi/doc.txt index d61731b17d..97031d3a76 100644 --- a/cpu/esp8266/esp-wifi/doc.txt +++ b/cpu/esp8266/esp-wifi/doc.txt @@ -34,7 +34,7 @@ make -C examples/gnrc_networking BOARD=... ``` @note The Wifi network interface (module `esp_wifi`) and the -\ref esp32_esp_now_network_interface "ESP-NOW network interface" (module `esp_now`) +\ref esp8266_esp_now_network_interface "ESP-NOW network interface" (module `esp_now`) can be used simultaneously, for example, to realize a border router for a mesh network which uses ESP-NOW. */ diff --git a/cpu/esp8266/esp-wifi/esp_wifi_netdev.c b/cpu/esp8266/esp-wifi/esp_wifi_netdev.c index ef616ac252..e299e16892 100644 --- a/cpu/esp8266/esp-wifi/esp_wifi_netdev.c +++ b/cpu/esp8266/esp-wifi/esp_wifi_netdev.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Gunar Schorcht + * Copyright (C) 2019 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 @@ -16,226 +16,348 @@ * @author Gunar Schorcht */ -#include "log.h" -#include "tools.h" +#ifdef MODULE_ESP_WIFI +#include #include #include -#include -#include -#include "net/ethernet.h" -#include "net/ipv4/addr.h" #include "net/gnrc/netif/ethernet.h" +#include "net/gnrc/netif/raw.h" +#include "net/gnrc.h" +#include "net/ethernet.h" #include "net/netdev/eth.h" #include "od.h" #include "xtimer.h" -#include "common.h" -#include "espressif/c_types.h" -#include "espnow.h" -#include "esp/common_macros.h" +#include "esp_common.h" +#include "esp_attr.h" +#include "esp_event_loop.h" +#include "esp_now.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_wifi_internal.h" #include "irq_arch.h" -#include "sdk/sdk.h" +#include "tools.h" -#include "lwip/igmp.h" -#include "lwip/udp.h" +#include "nvs_flash/include/nvs_flash.h" #include "esp_wifi_params.h" #include "esp_wifi_netdev.h" -#define ENABLE_DEBUG (0) +#define ENABLE_DEBUG_HEXDUMP (0) +#define ENABLE_DEBUG (0) #include "debug.h" +#include "log.h" + +#define SYSTEM_EVENT_WIFI_RX_DONE (SYSTEM_EVENT_MAX + 3) +#define SYSTEM_EVENT_WIFI_TX_DONE (SYSTEM_EVENT_MAX + 4) #define ESP_WIFI_DEBUG(f, ...) \ DEBUG("[esp_wifi] %s: " f "\n", __func__, ## __VA_ARGS__) #define ESP_WIFI_LOG_INFO(f, ...) \ - LOG_INFO("[esp_wifi] " f "\n", ## __VA_ARGS__) + LOG_TAG_INFO("esp_wifi", f "\n", ## __VA_ARGS__) #define ESP_WIFI_LOG_ERROR(f, ...) \ - LOG_ERROR("[esp_wifi] " f "\n", ## __VA_ARGS__) - -#define ESP_WIFI_STATION_MODE (STATION_MODE) -#define ESP_WIFI_AP_MODE (SOFTAP_MODE) -#define ESP_WIFI_STATION_AP_MODE (STATIONAP_MODE) -#define ESP_WIFI_MODE (STATIONAP_MODE) - -#define ESP_WIFI_STATION_IF (STATION_IF) -#define ESP_WIFI_SOFTAP_IF (SOFTAP_IF) - -#define ESP_WIFI_RECONNECT_TIME (20 * US_PER_SEC) -#define ESP_WIFI_HEAP_MARGIN (2 * ETHERNET_MAX_LEN) + LOG_TAG_ERROR("esp_wifi", f "\n", ## __VA_ARGS__) #define MAC_STR "%02x:%02x:%02x:%02x:%02x:%02x" #define MAC_STR_ARG(m) m[0], m[1], m[2], m[3], m[4], m[5] -#define PBUF_IEEE80211_HLEN (36) +#ifdef MCU_ESP8266 -/** Timer used to reconnect automatically after 20 seconds if not connected */ -static xtimer_t _esp_wifi_reconnect_timer; +#include "esp_socket.h" +#include "net/sockio.h" +#include "xtensa/xtensa_context.h" + +#define CONFIG_TCP_OVERSIZE_MSS 1 +#define LL_ALIGN(s) (((uint32_t)s + 3) & 0xfffffffcU) + +#include "lwip/pbuf.h" + +#endif /* MCU_ESP8266 */ /** - * There is only one ESP WIFI device. We define it as static device variable - * to have accesss to the device inside ESP WIFI interrupt routines which do - * not provide an argument that could be used as pointer to the ESP WIFI + * There is only one ESP WiFi device. We define it as static device variable + * to have access to the device inside ESP WiFi interrupt routines which do + * not provide an argument that could be used as pointer to the ESP WiFi * device which triggers the interrupt. */ -static esp_wifi_netdev_t _esp_wifi_dev; - -/** forward declaration of the driver functions structure */ +esp_wifi_netdev_t _esp_wifi_dev; static const netdev_driver_t _esp_wifi_driver; -/** Stack for the netif thread */ +/* device thread stack */ static char _esp_wifi_stack[ESP_WIFI_STACKSIZE]; -/** Static station configuration used for the WiFi interface */ -static const struct station_config station_cfg = { - .bssid_set = 0, /* no check of MAC address of AP */ - .ssid = ESP_WIFI_SSID, - .password = ESP_WIFI_PASS, -}; +/** guard variable to avoid reentrance to _esp_wifi_send function */ +static bool _esp_wifi_send_is_in = false; -#ifndef MODULE_ESP_NOW -/** - * Static const configuration for the SoftAP which is used to configure the - * SoftAP interface if ESP-NOW is not enabled. - * - * Since we need to use the WiFi interface in SoftAP + Station mode for - * stability reasons, although in fact only the station interface is required, - * we make the SoftAP interface invisible and unusable. This configuration - * - * - uses the same hidden SSID that the station interface uses to - * connect to the AP, - * - uses the same channel that the station interface uses to connect to the AP, - * - defines a very long beacon interval - * - doesn't allow any connection. - */ -static const struct softap_config softap_cfg = { - .ssid = ESP_WIFI_SSID, - .ssid_len = ARRAY_SIZE(ESP_WIFI_SSID), - .ssid_hidden = 1, /* don't make the AP visible */ - .password = ESP_WIFI_PASS, - .authmode = AUTH_WPA2_PSK, - .max_connection = 0, /* don't allow connections */ - .beacon_interval = 60000, /* send beacon only every 60 s */ -}; -#endif +/** guard variable to to decive when receive buffer can be overwritten */ +static bool _esp_wifi_rx_in_progress = false; -extern struct netif * eagle_lwip_getif(uint8 index); +extern esp_err_t esp_system_event_add_handler (system_event_cb_t handler, + void *arg); -/** guard variable to avoid reentrance to _send */ -static bool _in_send = false; - -/** guard variable to avoid reentrance to _esp_wifi_recv_cb */ -static bool _in_esp_wifi_recv_cb = false; +#ifdef MCU_ESP8266 /** - * @brief Reconnect function called back by the reconnect timer + * The low level WiFi driver function expects a lwIP pbuf data structure as + * input. To avoid the integration of the whole lwIP package from ESP8266 RTOS + * SDK, only the pbuf allocation function is realized with a very restricted + * functionality. It uses malloc to allocate a packet buffer of type PBUF_RAM + * for layer PBUF_RAW_TX. */ -static void IRAM _esp_wifi_reconnect_timer_cb(void* arg) +static struct pbuf *_esp_wifi_pbuf_alloc(size_t size) { - DEBUG("%s\n", __func__); - - esp_wifi_netdev_t* dev = (esp_wifi_netdev_t*)arg; - - if (dev->state == ESP_WIFI_DISCONNECTED || - dev->state == ESP_WIFI_CONNECTING) { - ESP_WIFI_LOG_INFO("trying to reconnect to ssid " ESP_WIFI_SSID); - - wifi_station_disconnect(); - wifi_station_connect(); - dev->state = ESP_WIFI_CONNECTING; + /* Low level WiFi driver can only use 32-bit aligned DRAM memory */ + size_t mem_size = LL_ALIGN(sizeof(struct pbuf)) + LL_ALIGN(size + PBUF_LINK_ENCAPSULATION_HLEN); + struct pbuf *pb = heap_caps_malloc(mem_size, MALLOC_CAP_8BIT); + if (pb == NULL) { + ESP_WIFI_LOG_ERROR("no space left for packet buffer allocation"); + return NULL; } + memset(pb, 0, mem_size); - /* set the time for next connection check */ - xtimer_set(&_esp_wifi_reconnect_timer, ESP_WIFI_RECONNECT_TIME); + /* initialize pbuf data structure */ + pb->next = NULL; + pb->payload = (void *)LL_ALIGN((uint8_t *)pb + sizeof(struct pbuf) + PBUF_LINK_ENCAPSULATION_HLEN); + pb->tot_len = size; + pb->len = size; + pb->type = PBUF_RAM; + pb->flags = 0; + pb->ref = 1; + + ESP_WIFI_DEBUG("pb=%p size=%d", pb, size); + + return (struct pbuf*)pb; } /** - * @brief Callback when ethernet frame is received. Has to run in IRAM. + * Free function for pbuf allocation */ -void IRAM _esp_wifi_recv_cb(struct pbuf *pb, struct netif *netif) +static int _esp_wifi_pbuf_free(struct pbuf *pb) { assert(pb != NULL); - assert(netif != NULL); + + ESP_WIFI_DEBUG("pb=%p ref=%d", pb, pb->ref); + + if (pb->ref > 1) { + pb->ref--; + } + else if (pb->ref == 1) { + pb->ref = 0; + heap_caps_free(pb); + return 1; + } + return 0; +} + +/** + * Socket used for interaction with low level WiFi driver, -1 if not opened. + * Since we have only one WiFi interface, it has not to be a member of the + * netdev data structures. We can use a static variable instead. + */ +static int _esp_wifi_socket = -1; + +/** + * Function called when transmission of a packet has been finished. + */ +static int _esp_wifi_tx_cb(esp_aio_t* aio) +{ + assert(aio != NULL); + + ESP_WIFI_DEBUG("aio=%p buf=%p", aio, aio->pbuf); + + struct pbuf* pbuf = aio->arg; + _esp_wifi_pbuf_free(pbuf); + _esp_wifi_send_is_in = false; + _esp_wifi_dev.netdev.event_callback(&_esp_wifi_dev.netdev, NETDEV_EVENT_TX_COMPLETE); + + return 0; +} + +/** + * Function for source code compatibility with ESP-IDF for ESP32 + */ +int esp_wifi_internal_tx(wifi_interface_t wifi_if, void *buf, uint16_t len) +{ + ESP_WIFI_DEBUG("buf=%p len=%u", buf, len); + + struct pbuf *pb = _esp_wifi_pbuf_alloc(len); + if (pb == NULL) { + return ERR_MEM; + } + if (len) { + memcpy(pb->payload, buf, len); + } + + esp_aio_t aio; + aio.fd = wifi_if; + aio.pbuf = pb->payload; + aio.len = pb->len; + aio.cb = _esp_wifi_tx_cb; + aio.arg = pb; + aio.ret = 0; + + if (esp_aio_sendto(&aio, NULL, 0) != 0) { + return ERR_IF; + } + + ESP_WIFI_DEBUG("done"); + + return ERR_OK; +} + +/** + * Function for source code compatibility with ESP-IDF for ESP32 + */ +void esp_wifi_internal_free_rx_buffer(const char* buf) +{ + assert(buf != NULL); + assert(_esp_wifi_socket != -1); + + ESP_WIFI_DEBUG("buf=%p sock=%d", buf, _esp_wifi_socket); + + esp_free_pbuf(_esp_wifi_socket, (void *)buf); +} + +/** + * Type definition for source code compatibility with ESP-IDF for ESP32 + */ +typedef int (*wifi_rxcb_t)(struct esp_aio *aio); + +/** + * Function for source code compatibility with ESP-IDF for ESP32 + */ +esp_err_t esp_wifi_internal_reg_rxcb(wifi_interface_t ifx, wifi_rxcb_t fn) +{ + assert(ifx == ESP_IF_WIFI_STA); + + ESP_WIFI_DEBUG("%d %p", ifx, fn); + + extern int8_t wifi_get_netif(uint8_t fd); + + /* if function is NULL, it is deregistered */ + if (fn == NULL) { + /* if socket is allocated, it has to be closed */ + if (_esp_wifi_socket != -1 && esp_close(_esp_wifi_socket) < 0) { + return ESP_FAIL; + } + _esp_wifi_socket = -1; + return ESP_OK; + } + + /* if socket is already allocated we have to close it to register a function */ + if (_esp_wifi_socket != -1 && esp_close(_esp_wifi_socket) < 0) { + return ESP_FAIL; + } + + /* now, we have to allocate a new socket and register the function */ + _esp_wifi_socket = esp_socket(AF_PACKET, SOCK_RAW, ETH_P_ALL); + + if (_esp_wifi_socket < 0) { + ESP_WIFI_LOG_ERROR("create socket of (AF_PACKET, SOCK_RAW, ETH_P_ALL) error"); + return ESP_FAIL; + } + if (esp_ioctl(_esp_wifi_socket, SIOCGIFINDEX, "sta0") < 0) { + ESP_WIFI_LOG_ERROR("bind socket %d to netcard %s error", _esp_wifi_socket, "sta0"); + esp_close(_esp_wifi_socket); + return ESP_FAIL; + } + if (esp_aio_event(_esp_wifi_socket, ESP_SOCKET_RECV_EVENT, fn, &_esp_wifi_dev) < 0) { + ESP_WIFI_LOG_ERROR("socket %d register receive callback function %p error", + _esp_wifi_socket, fn); + esp_close(_esp_wifi_socket); + return ESP_FAIL; + } + return ESP_OK; +} + +#endif /* MCU_ESP8266 */ + +#ifdef MCU_ESP8266 + +/* Prolog for source code compatibility with ESP-IDF for ESP32 */ +static int _esp_wifi_rx_cb(struct esp_aio *aio) +{ + assert(aio != NULL); + + const char *eb = aio->pbuf; + const char *buffer = aio->pbuf; + uint16_t len = aio->len; + +#else /* MCU_ESP8266 */ + +esp_err_t _esp_wifi_rx_cb(void *buffer, uint16_t len, void *eb) +{ +#endif /* MCU_ESP8266 */ /* - * Function `esp_wifi_recv_cb` is executed in the context of the `ets` - * thread. ISRs which handle hardware interrupts from the WiFi interface - * simply pass events to a message queue of the `ets` thread which are then - * sequentially processed by the `ets` thread to asynchronously execute - * callback functions such as `esp_wifi_recv_cb`. - * - * It should be therefore not possible to reenter function - * `esp_wifi_recv_cb`. If it does occur inspite of that, we use a - * guard variable to avoid inconsistencies. This can not be realized - * by a mutex because `esp_wifi_recv_cb` would be reentered from same - * thread context. + * This callback function is not executed in interrupt context but in the + * context of the low level WiFi driver thread. That is, mutex_lock or + * msg_send functions could block. */ - if (_in_esp_wifi_recv_cb) { - pbuf_free(pb); - return; - } - _in_esp_wifi_recv_cb = true; + + assert(buffer != NULL); + assert(len <= ETHERNET_MAX_LEN); critical_enter(); - /* first, check packet buffer for the minimum packet size */ - if (pb->tot_len < sizeof(ethernet_hdr_t)) { - ESP_WIFI_DEBUG("frame length is less than the size of an Ethernet" - "header (%u < %u)", pb->tot_len, sizeof(ethernet_hdr_t)); - pbuf_free(pb); - _in_esp_wifi_recv_cb = false; - critical_exit(); - return; - } - - /* check whether the receive buffer is already holding a frame */ - if (_esp_wifi_dev.rx_len) { - ESP_WIFI_DEBUG("buffer used, dropping incoming frame of %d bytes", - pb->tot_len); - pbuf_free(pb); - _in_esp_wifi_recv_cb = false; - critical_exit(); - return; - } - - /* check whether packet buffer fits into receive buffer */ - if (pb->tot_len > ETHERNET_MAX_LEN) { - ESP_WIFI_DEBUG("frame length is greater than the maximum size of an " - "Ethernet frame (%u > %u)", pb->tot_len, ETHERNET_MAX_LEN); - pbuf_free(pb); - _in_esp_wifi_recv_cb = false; - critical_exit(); - return; - } - - /* we have to store the frame in the buffer and free lwIP pbuf immediatly */ - _esp_wifi_dev.rx_len = pb->tot_len; - pbuf_copy_partial(pb, _esp_wifi_dev.rx_buf, _esp_wifi_dev.rx_len, 0); - pbuf_free(pb); + ESP_WIFI_DEBUG("buf=%p len=%d eb=%p", buffer, len, eb); /* - * Since _esp_wifi_recv_cb is not executed in interrupt context but in - * the context of the `ets` thread, it is not necessary to pass the - * `NETDEV_EVENT_ISR` event first. Instead, the receive function can be - * called directly which result in much faster handling, a less frame lost - * rate and more robustness. There is no need for a mutex anymore to - * synchronize the access to the receive buffer between _esp_wifi_recv_cb - * and _recv function. + * The ring buffer uses a single byte for the pkt length, followed by the mac address, + * followed by the actual packet data. The MTU for ESP-NOW is 250 bytes, so len will never + * exceed the limits of a byte as the mac address length is not included. */ - if (_esp_wifi_dev.netdev.event_callback) { - _esp_wifi_dev.netdev.event_callback(&_esp_wifi_dev.netdev, - NETDEV_EVENT_RX_COMPLETE); + if (ringbuffer_get_free(&_esp_wifi_dev.rx_buf) < len + sizeof(uint16_t)) { + ESP_WIFI_DEBUG("buffer full, dropping incoming packet of %d bytes", len); + /* free the receive buffer */ + if (eb) { + esp_wifi_internal_free_rx_buffer(eb); + } + /* + * we must not return a failure code in this case, otherwise, + * the WiFi driver hangs up + */ + critical_exit(); + return ESP_OK; } - _in_esp_wifi_recv_cb = false; + /* store length information as first two bytes */ + ringbuffer_add(&_esp_wifi_dev.rx_buf, (char *)&len, sizeof(uint16_t)); + + /* copy the buffer and free WiFi driver buffer */ + ringbuffer_add(&_esp_wifi_dev.rx_buf, (char *)buffer, len); + if (eb) { + esp_wifi_internal_free_rx_buffer(eb); + } + + /* + * Because this function is not executed in interrupt context but in thread + * context, following msg_send could block on heavy network load, if frames + * are coming in faster than the ISR events can be handled. To avoid + * blocking during msg_send, we pretend we are in an ISR by incrementing + * the IRQ nesting counter. If IRQ nesting counter is greater 0, function + * irq_is_in returns true and the non-blocking version of msg_send is used. + */ + irq_interrupt_nesting++; + + /* trigger netdev event to read the data */ + _esp_wifi_dev.event_recv++; + _esp_wifi_dev.netdev.event_callback(&_esp_wifi_dev.netdev, NETDEV_EVENT_ISR); + + /* reset IRQ nesting counter */ + irq_interrupt_nesting--; + critical_exit(); + return ESP_OK; } -#define BEACON_TIMEOUT (200) -#define HANDSHAKE_TIMEOUT (204) +#define REASON_BEACON_TIMEOUT (200) +#define REASON_HANDSHAKE_TIMEOUT (204) +#define INDEX_BEACON_TIMEOUT (REASON_BEACON_TIMEOUT - 24) static const char *_esp_wifi_disc_reasons [] = { "INVALID", /* 0 */ @@ -269,217 +391,178 @@ static const char *_esp_wifi_disc_reasons [] = { "HANDSHAKE_TIMEOUT" /* 204 */ }; -/** - * @brief Event handler for esp system events. +/* + * Event handler for esp system events. */ -static void _esp_wifi_handle_event_cb(System_Event_t *evt) +static esp_err_t IRAM_ATTR _esp_system_event_handler(void *ctx, system_event_t *event) { - ESP_WIFI_DEBUG("event %d", evt->event); + assert(event != NULL); + + esp_err_t result; uint8_t reason; const char* reason_str = "UNKNOWN"; - switch (evt->event) { - case EVENT_STAMODE_CONNECTED: - ESP_WIFI_LOG_INFO("connected to ssid %s, channel %d", - evt->event_info.connected.ssid, - evt->event_info.connected.channel); - _esp_wifi_dev.state = ESP_WIFI_CONNECTED; - _esp_wifi_dev.event = EVENT_STAMODE_CONNECTED; - _esp_wifi_dev.netdev.event_callback(&_esp_wifi_dev.netdev, NETDEV_EVENT_ISR); + switch(event->event_id) { + case SYSTEM_EVENT_STA_START: + ESP_WIFI_DEBUG("WiFi started"); + result = esp_wifi_connect(); + if (result != ESP_OK) { + ESP_WIFI_LOG_ERROR("esp_wifi_connect failed with return " + "value %d", result); + } break; - case EVENT_STAMODE_DISCONNECTED: - reason = evt->event_info.disconnected.reason; + case SYSTEM_EVENT_SCAN_DONE: + ESP_WIFI_DEBUG("WiFi scan done"); + break; + + case SYSTEM_EVENT_STA_CONNECTED: + ESP_WIFI_LOG_INFO("connected to ssid %s, channel %d", + event->event_info.connected.ssid, + event->event_info.connected.channel); + + /* register RX callback function */ + esp_wifi_internal_reg_rxcb(ESP_IF_WIFI_STA, _esp_wifi_rx_cb); + + _esp_wifi_dev.connected = true; + _esp_wifi_dev.event_conn++; + _esp_wifi_dev.netdev.event_callback(&_esp_wifi_dev.netdev, NETDEV_EVENT_ISR); + + break; + + case SYSTEM_EVENT_STA_DISCONNECTED: + reason = event->event_info.disconnected.reason; if (reason < REASON_BEACON_TIMEOUT) { reason_str = _esp_wifi_disc_reasons[reason]; } else if (reason <= REASON_HANDSHAKE_TIMEOUT) { - reason_str = _esp_wifi_disc_reasons[reason - REASON_BEACON_TIMEOUT]; + reason_str = _esp_wifi_disc_reasons[reason - INDEX_BEACON_TIMEOUT]; } ESP_WIFI_LOG_INFO("disconnected from ssid %s, reason %d (%s)", - evt->event_info.disconnected.ssid, - evt->event_info.disconnected.reason, reason_str); - _esp_wifi_dev.state = ESP_WIFI_DISCONNECTED; - _esp_wifi_dev.event = EVENT_STAMODE_DISCONNECTED; - _esp_wifi_dev.netdev.event_callback(&_esp_wifi_dev.netdev, NETDEV_EVENT_ISR); - break; + event->event_info.disconnected.ssid, + event->event_info.disconnected.reason, reason_str); + + /* unregister RX callback function */ + esp_wifi_internal_reg_rxcb(ESP_IF_WIFI_STA, NULL); + + _esp_wifi_dev.connected = false; + _esp_wifi_dev.event_disc++; + _esp_wifi_dev.netdev.event_callback(&_esp_wifi_dev.netdev, NETDEV_EVENT_ISR); + + /* call disconnect to reset internal state */ + result = esp_wifi_disconnect(); + if (result != ESP_OK) { + ESP_WIFI_LOG_ERROR("esp_wifi_disconnect failed with " + "return value %d", result); + return result; + } + + /* try to reconnect */ + result = esp_wifi_connect(); + if (result != ESP_OK) { + ESP_WIFI_LOG_ERROR("esp_wifi_connect failed with " + "return value %d", result); + } - case EVENT_SOFTAPMODE_STACONNECTED: - ESP_WIFI_LOG_INFO("station " MACSTR " join, aid %d", - MAC2STR(evt->event_info.sta_connected.mac), - evt->event_info.sta_connected.aid); break; default: + ESP_WIFI_DEBUG("event %d", event->event_id); break; } + return ESP_OK; } -static int _init(netdev_t *netdev) +static int _esp_wifi_send(netdev_t *netdev, const iolist_t *iolist) { - ESP_WIFI_DEBUG("%p", netdev); - - return 0; -} - -#if ENABLE_DEBUG -/** buffer for sent packet dump */ -uint8_t _send_pkt_buf[ETHERNET_MAX_LEN]; -#endif - -/** function used to send an ethernet frame over WiFi */ -extern err_t ieee80211_output_pbuf(struct netif *netif, struct pbuf *p); - -/** function to get free heap */ -unsigned int IRAM get_free_heap_size (void); - -static int IRAM _send(netdev_t *netdev, const iolist_t *iolist) -{ - ESP_WIFI_DEBUG("%p %p", netdev, iolist); + ESP_WIFI_DEBUG("netdev=%p iolist=%p", netdev, iolist); assert(netdev != NULL); assert(iolist != NULL); - if (_in_send) { + if (_esp_wifi_send_is_in) { return 0; } - _in_send = true; + _esp_wifi_send_is_in = true; - esp_wifi_netdev_t *dev = (esp_wifi_netdev_t*)netdev; + esp_wifi_netdev_t* dev = (esp_wifi_netdev_t*)netdev; + + if (!_esp_wifi_dev.connected) { + ESP_WIFI_DEBUG("WiFi is still not connected to AP, cannot send"); + _esp_wifi_send_is_in = false; + return -ENODEV; + } critical_enter(); - if (dev->state != ESP_WIFI_CONNECTED) { - ESP_WIFI_DEBUG("WiFi is still not connected to AP, cannot send"); - _in_send = false; - critical_exit(); - return -EIO; - } + dev->tx_len = 0; -#ifndef MODULE_ESP_NOW - if (wifi_get_opmode() != ESP_WIFI_MODE) { - ESP_WIFI_DEBUG("WiFi is not in correct mode, cannot send"); - _in_send = false; - critical_exit(); - return -EIO; - } -#endif - - const iolist_t *iol = iolist; - size_t iol_len = 0; - - /* determine the frame size */ - while (iol) { - iol_len += iol->iol_len; - iol = iol->iol_next; - } - - /* limit checks */ - if (iol_len > ETHERNET_MAX_LEN) { - ESP_WIFI_DEBUG("frame length exceeds the maximum (%u > %u)", - iol_len, ETHERNET_MAX_LEN); - _in_send = false; - critical_exit(); - return -EBADMSG; - } - - if (iol_len < sizeof(ethernet_hdr_t)) { - ESP_WIFI_DEBUG("frame length is less than the size of an Ethernet" - "header (%u < %u)", iol_len, sizeof(ethernet_hdr_t)); - _in_send = false; - critical_exit(); - return -EBADMSG; - } - - struct netif *sta_netif = (struct netif *)eagle_lwip_getif(ESP_WIFI_STATION_IF); - netif_set_default(sta_netif); - - struct pbuf *pb; - - if (get_free_heap_size() < ESP_WIFI_HEAP_MARGIN || - (pb = pbuf_alloc(PBUF_LINK, iol_len, PBUF_RAM)) == NULL || - (pb->tot_len < iol_len)) { - ESP_WIFI_LOG_ERROR("could not allocate buffer to send %d bytes ", iol_len); - /* - * The memory of EPS8266 is quite small. Therefore, it may happen on - * heavy network load that we run into out of memory and we have - * to wait until lwIP pbuf has been flushed. We slow down sending a bit. - */ - critical_exit(); - /* wait 20 ms */ - xtimer_usleep(20 * US_PER_MS); - - _in_send = false; - return -EIO; - } - - struct pbuf *pbi = pb; - uint8_t *pbi_payload = pb->payload; - size_t pbi_pos = 0; - - /* prepare lwIP packet buffer direct from iolist without any buffer */ - for (const iolist_t *iol = iolist; iol && pbi; iol = iol->iol_next) { - uint8_t *iol_base = iol->iol_base; - for (unsigned i = 0; i < iol->iol_len && pbi; i++) { - pbi_payload[pbi_pos++] = iol_base[i]; - if (pbi_pos >= pbi->len) { - pbi = pbi->next; - } + /* load packet data into TX buffer */ + for (const iolist_t *iol = iolist; iol; iol = iol->iol_next) { + if (dev->tx_len + iol->iol_len > ETHERNET_MAX_LEN) { + _esp_wifi_send_is_in = false; + critical_exit(); + return -EOVERFLOW; + } + if (iol->iol_len) { + memcpy (dev->tx_buf + dev->tx_len, iol->iol_base, iol->iol_len); + dev->tx_len += iol->iol_len; } } #if ENABLE_DEBUG - pbi = pb; - pbi_pos = 0; - - for (; pbi; pbi = pbi->next) { - memcpy(_send_pkt_buf + pbi_pos, pbi->payload, pbi->len); - pbi_pos += pbi->len; - } - - const ethernet_hdr_t* hdr = (const ethernet_hdr_t *)_send_pkt_buf; - - ESP_WIFI_DEBUG("send %u byte to " MAC_STR, - (unsigned)iol_len, MAC_STR_ARG(hdr->dst)); -#if MODULE_OD - od_hex_dump(_send_pkt_buf, iol_len, OD_WIDTH_DEFAULT); -#endif /* MODULE_OD */ -#endif /* ENABLE_DEBUG */ - + ESP_WIFI_DEBUG("send %d byte", dev->tx_len); +#if MODULE_OD && ENABLE_DEBUG_HEXDUMP + od_hex_dump(dev->tx_buf, dev->tx_le, OD_WIDTH_DEFAULT); +#endif /* MODULE_OD && ENABLE_DEBUG_HEXDUMP */ +#endif critical_exit(); - /* sta_netif->linkoutput = ieee80211_output_pbuf */ - err_t res = sta_netif->linkoutput(sta_netif, pb); - pbuf_free(pb); - if (res == ERR_OK) { - /* There was no ieee80211_output_pbuf error and no send timeout. */ + int ret = 0; + + /* send the the packet to the peer(s) mac address */ + if (esp_wifi_internal_tx(ESP_IF_WIFI_STA, dev->tx_buf, dev->tx_len) == ESP_OK) { +#ifdef MCU_ESP32 + /* for ESP8266 it is done in _esp_wifi_tx_cb */ + _esp_wifi_send_is_in = false; netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE); - _in_send = false; - return iol_len; +#endif } else { - /* There was either a ieee80211_output_pbuf error or send timed out. */ - _in_send = false; - return -EIO; + _esp_wifi_send_is_in = false; + ESP_WIFI_DEBUG("sending WiFi packet failed"); + ret = -EIO; } + + return ret; } -static int _recv(netdev_t *netdev, void *buf, size_t len, void *info) +static int _esp_wifi_recv(netdev_t *netdev, void *buf, size_t len, void *info) { ESP_WIFI_DEBUG("%p %p %u %p", netdev, buf, len, info); assert(netdev != NULL); esp_wifi_netdev_t* dev = (esp_wifi_netdev_t*)netdev; + uint16_t size; - uint16_t size = dev->rx_len ? dev->rx_len : 0; + critical_enter(); + + _esp_wifi_rx_in_progress = true; + + if (ringbuffer_peek(&dev->rx_buf, (char *)&size, sizeof(uint16_t)) < sizeof(uint16_t)) { + critical_exit(); + return 0; + } if (!buf) { /* get the size of the frame */ if (len > 0 && size) { /* if len > 0, drop the frame */ - dev->rx_len = 0; + ringbuffer_remove(&dev->rx_buf, sizeof(uint16_t) + size); + _esp_wifi_rx_in_progress = false; } + critical_exit(); return size; } @@ -487,84 +570,74 @@ static int _recv(netdev_t *netdev, void *buf, size_t len, void *info) /* buffer is smaller than the number of received bytes */ ESP_WIFI_DEBUG("not enough space in receive buffer"); /* newest API requires to drop the frame in that case */ - dev->rx_len = 0; + ringbuffer_remove(&dev->rx_buf, sizeof(uint16_t) + size); + _esp_wifi_rx_in_progress = false; + critical_exit(); return -ENOBUFS; } - /* copy the buffer and free */ - memcpy(buf, dev->rx_buf, dev->rx_len); - dev->rx_len = 0; + /* remove length bytes, copy the buffer to the ringbuffer and free it */ + ringbuffer_remove(&dev->rx_buf, sizeof(uint16_t)); + ringbuffer_get(&dev->rx_buf, buf, size); #if ENABLE_DEBUG ethernet_hdr_t *hdr = (ethernet_hdr_t *)buf; ESP_WIFI_DEBUG("received %u byte from addr " MAC_STR, size, MAC_STR_ARG(hdr->src)); -#if MODULE_OD +#if MODULE_OD && ENABLE_DEBUG_HEXDUMP od_hex_dump(buf, size, OD_WIDTH_DEFAULT); -#endif /* MODULE_OD */ +#endif /* MODULE_OD && ENABLE_DEBUG_HEXDUMP */ #endif /* ENABLE_DEBUG */ + critical_exit(); return size; } -static int _get(netdev_t *netdev, netopt_t opt, void *val, size_t max_len) +static int _esp_wifi_get(netdev_t *netdev, netopt_t opt, void *val, size_t max_len) { ESP_WIFI_DEBUG("%s %p %p %u", netopt2str(opt), netdev, val, max_len); assert(netdev != NULL); assert(val != NULL); - esp_wifi_netdev_t *dev = (esp_wifi_netdev_t*)netdev; + esp_wifi_netdev_t* dev = (esp_wifi_netdev_t*)netdev; switch (opt) { - case NETOPT_IS_WIRED: return -ENOTSUP; - + case NETOPT_ADDRESS: + assert(max_len >= ETHERNET_ADDR_LEN); + esp_wifi_get_mac(ESP_MAC_WIFI_STA,(uint8_t *)val); + return ETHERNET_ADDR_LEN; case NETOPT_LINK_CONNECTED: assert(max_len == 1); - if (dev->state == ESP_WIFI_CONNECTED) { - *((netopt_enable_t *)val) = NETOPT_ENABLE; - } - else { - *((netopt_enable_t *)val) = NETOPT_DISABLE; - } + *((netopt_enable_t *)val) = (dev->connected) ? NETOPT_ENABLE + : NETOPT_DISABLE; return 1; - - case NETOPT_ADDRESS: - assert(max_len >= sizeof(dev->mac)); - memcpy(val, dev->mac, sizeof(dev->mac)); - return sizeof(dev->mac); - default: return netdev_eth_get(netdev, opt, val, max_len); - } } -static int _set(netdev_t *netdev, netopt_t opt, const void *val, size_t max_len) +static int _esp_wifi_set(netdev_t *netdev, netopt_t opt, const void *val, size_t max_len) { ESP_WIFI_DEBUG("%s %p %p %u", netopt2str(opt), netdev, val, max_len); assert(netdev != NULL); assert(val != NULL); - esp_wifi_netdev_t *dev = (esp_wifi_netdev_t *) netdev; - switch (opt) { - case NETOPT_ADDRESS: - assert(max_len >= sizeof(dev->mac)); - memcpy(dev->mac, val, sizeof(dev->mac)); - return sizeof(dev->mac); - + assert(max_len == ETHERNET_ADDR_LEN); + esp_wifi_set_mac(ESP_MAC_WIFI_STA, (uint8_t *)val); + return ETHERNET_ADDR_LEN; default: return netdev_eth_set(netdev, opt, val, max_len); } } -static void _isr(netdev_t *netdev) +static void _esp_wifi_isr(netdev_t *netdev) { ESP_WIFI_DEBUG("%p", netdev); @@ -572,131 +645,186 @@ static void _isr(netdev_t *netdev) esp_wifi_netdev_t *dev = (esp_wifi_netdev_t *) netdev; - switch (dev->event) { - case EVENT_STAMODE_CONNECTED: - dev->netdev.event_callback(netdev, NETDEV_EVENT_LINK_UP); - break; - case EVENT_STAMODE_DISCONNECTED: - dev->netdev.event_callback(netdev, NETDEV_EVENT_LINK_DOWN); - break; - default: - break; + while (dev->event_recv) { + dev->event_recv--; + dev->netdev.event_callback(netdev, NETDEV_EVENT_RX_COMPLETE); + } + if (dev->event_conn) { + dev->event_conn--; + dev->netdev.event_callback(netdev, NETDEV_EVENT_LINK_UP); + } + else if (dev->event_disc) { + dev->event_disc--; + dev->netdev.event_callback(netdev, NETDEV_EVENT_LINK_DOWN); } - _esp_wifi_dev.event = EVENT_MAX; /* no event */ return; } -/** override lwIP ethernet_intput to get ethernet frames */ -extern err_t __real_ethernet_input(struct pbuf *pb, struct netif* netif); - -err_t __wrap_ethernet_input(struct pbuf *pb, struct netif* netif) +static int _esp_wifi_init(netdev_t *netdev) { - ESP_WIFI_DEBUG("%p %p", pb, netif); - if (_esp_wifi_dev.state == ESP_WIFI_CONNECTED) { - _esp_wifi_recv_cb(pb, netif); - } - else { - __real_ethernet_input(pb, netif); - } - return ERR_OK; + ESP_WIFI_DEBUG("%p", netdev); + + return 0; } -static const netdev_driver_t _esp_wifi_driver = { - .send = _send, - .recv = _recv, - .init = _init, - .isr = _isr, - .get = _get, - .set = _set, +static const netdev_driver_t _esp_wifi_driver = +{ + .send = _esp_wifi_send, + .recv = _esp_wifi_recv, + .init = _esp_wifi_init, + .isr = _esp_wifi_isr, + .get = _esp_wifi_get, + .set = _esp_wifi_set, }; -static void _esp_wifi_setup(void) +/* + * Static configuration for the Station interface + */ +static wifi_config_t wifi_config_sta = { + .sta = { + .ssid = ESP_WIFI_SSID, + .password = ESP_WIFI_PASS, + .channel = 0, + .scan_method = WIFI_ALL_CHANNEL_SCAN, + .sort_method = WIFI_CONNECT_AP_BY_SIGNAL, + .threshold.rssi = -127, + .threshold.authmode = WIFI_AUTH_WPA_WPA2_PSK + } +}; + +#ifndef MODULE_ESP_NOW +/** + * Static configuration for the SoftAP interface if ESP-NOW is not enabled. + * + * Although only the Station interface is needed, the SoftAP interface must + * also be enabled for stability reasons to prevent the Station interface + * from being shut down by power management in the event of silence. + * Otherwise, the WLAN module and the WLAN task will hang sporadically. + * + * Since the SoftAP interface is not required, we make it invisible and + * unusable. This configuration + * + * - uses the same hidden SSID that the Station interface uses to + * connect to the AP, + * - uses the same channel that the Station interface uses to connect to the AP, + * - defines a very long beacon interval + * - doesn't allow any connection. + */ +static wifi_config_t wifi_config_ap = { + .ap = { + .ssid = ESP_WIFI_SSID, + .ssid_len = ARRAY_SIZE(ESP_WIFI_SSID), + .ssid_hidden = 1, /* don't make the AP visible */ + .password = ESP_WIFI_PASS, + .authmode = WIFI_AUTH_WPA2_PSK, + .max_connection = 0, /* don't allow connections */ + .beacon_interval = 60000, /* send beacon only every 60 s */ + } +}; +#endif + +void esp_wifi_setup (esp_wifi_netdev_t* dev) { - esp_wifi_netdev_t* dev = &_esp_wifi_dev; + ESP_WIFI_DEBUG("dev=%p", dev); - ESP_WIFI_DEBUG("%p", dev); + /* initialize buffer */ + ringbuffer_init(&dev->rx_buf, (char*)dev->rx_mem, sizeof(dev->rx_mem)); - if (dev->netdev.driver) { - ESP_WIFI_DEBUG("early returning previously initialized device"); + /* set the event handler */ + esp_system_event_add_handler(_esp_system_event_handler, NULL); + + /* + * Init the WiFi driver. TODO It is not only required before ESP_WIFI is + * initialized but also before other WiFi functions are used. Once other + * WiFi functions are realized it has to be moved to a more common place. + */ + esp_err_t result; + +#ifndef MODULE_ESP_NOW + /* if module esp_now is used, the following part is already done */ +#if MCU_ESP32 + extern portMUX_TYPE g_intr_lock_mux; + mutex_init(&g_intr_lock_mux); +#endif + +#if CONFIG_ESP32_WIFI_NVS_ENABLED + result = nvs_flash_init(); + if (result != ESP_OK) { + ESP_WIFI_LOG_ERROR("nfs_flash_init failed with return value %d", result); + return; + } +#endif /* CONFIG_ESP32_WIFI_NVS_ENABLED */ + + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + result = esp_wifi_init(&cfg); + if (result != ESP_OK) { + ESP_WIFI_LOG_ERROR("esp_wifi_init failed with return value %d", result); return; } - /* initialize netdev data structure */ - dev->rx_len = 0; - dev->state = ESP_WIFI_DISCONNECTED; - dev->event = EVENT_MAX; +#ifdef CONFIG_WIFI_COUNTRY + /* TODO */ +#endif + + /* activate the Station and the SoftAP interface */ + result = esp_wifi_set_mode(WIFI_MODE_APSTA); + if (result != ESP_OK) { + ESP_WIFI_LOG_ERROR("esp_wifi_set_mode failed with return value %d", result); + return; + } + + /* set the SoftAP configuration */ + result = esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config_ap); + if (result != ESP_OK) { + ESP_WIFI_LOG_ERROR("esp_wifi_set_config softap failed with return value %d", result); + return; + } + +#endif /* MODULE_ESP_NOW */ + + /* set the Station configuration */ + result = esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config_sta); + if (result != ESP_OK) { + ESP_WIFI_LOG_ERROR("esp_wifi_set_config station failed with return value %d", result); + return; + } + + /* start the WiFi driver */ + result = esp_wifi_start(); + if (result != ESP_OK) { + ESP_WIFI_LOG_ERROR("esp_wifi_start failed with return value %d", result); + return; + } + + /* register RX callback function */ + esp_wifi_internal_reg_rxcb(ESP_IF_WIFI_STA, _esp_wifi_rx_cb); /* set the netdev driver */ dev->netdev.driver = &_esp_wifi_driver; -#ifndef MODULE_ESP_NOW - /* set the WiFi interface mode */ - if (!wifi_set_opmode_current(ESP_WIFI_MODE)) { - ESP_WIFI_LOG_ERROR("could not set WiFi working mode"); - return; - } - - /* set the WiFi SoftAP configuration */ - if (!wifi_softap_set_config_current((struct softap_config *)&softap_cfg)) { - ESP_WIFI_LOG_ERROR("could not set WiFi configuration"); - return; - } -#endif - - /* set the WiFi station configuration */ - if (!wifi_station_set_config_current((struct station_config *)&station_cfg)) { - ESP_WIFI_LOG_ERROR("could not set WiFi configuration"); - return; - } - - /* get station mac address and store it in device address */ - if (!wifi_get_macaddr(ESP_WIFI_STATION_IF, dev->mac)) { - ESP_WIFI_LOG_ERROR("could not get MAC address of WiFi interface"); - return; - } - ESP_WIFI_DEBUG("own MAC addr is " MAC_STR, MAC_STR_ARG(dev->mac)); - - /* set auto reconnect policy */ - wifi_station_set_reconnect_policy(true); - wifi_station_set_auto_connect(true); - - /* register callbacks */ - wifi_set_event_handler_cb(_esp_wifi_handle_event_cb); - - /* reconnect timer initialization */ - _esp_wifi_reconnect_timer.callback = &_esp_wifi_reconnect_timer_cb; - _esp_wifi_reconnect_timer.arg = dev; - - /* set the the reconnect timer */ - xtimer_set(&_esp_wifi_reconnect_timer, ESP_WIFI_RECONNECT_TIME); - - /* avoid the WiFi modem going into sleep mode */ - wifi_set_sleep_type(NONE_SLEEP_T); - - /* connect */ - wifi_station_connect(); - _esp_wifi_dev.state = ESP_WIFI_CONNECTING; - - return; + /* initialize netdev data structure */ + dev->connected = false; + dev->event_recv = 0; + dev->event_conn = 0; + dev->event_disc = 0; } -void auto_init_esp_wifi(void) +void auto_init_esp_wifi (void) { - ESP_WIFI_DEBUG("auto initializing netdev\n"); + ESP_WIFI_DEBUG("initializing ESP WiFi device"); - /* setup netdev device */ - _esp_wifi_setup(); - - /* create netif */ - gnrc_netif_ethernet_create(_esp_wifi_stack, ESP_WIFI_STACKSIZE, + esp_wifi_setup(&_esp_wifi_dev); + _esp_wifi_dev.netif = gnrc_netif_ethernet_create(_esp_wifi_stack, + ESP_WIFI_STACKSIZE, #ifdef MODULE_ESP_NOW - ESP_WIFI_PRIO - 1, + ESP_WIFI_PRIO - 1, #else - ESP_WIFI_PRIO, + ESP_WIFI_PRIO, #endif - "esp-wifi", - (netdev_t *)&_esp_wifi_dev); + "esp_wifi", + (netdev_t *)&_esp_wifi_dev); } -/** @} */ +#endif /* MODULE_ESP_WIFI */ +/**@}*/ diff --git a/cpu/esp8266/esp-wifi/esp_wifi_netdev.h b/cpu/esp8266/esp-wifi/esp_wifi_netdev.h index 05d317d70c..756bc3e2db 100644 --- a/cpu/esp8266/esp-wifi/esp_wifi_netdev.h +++ b/cpu/esp8266/esp-wifi/esp_wifi_netdev.h @@ -11,7 +11,7 @@ * @{ * * @file - * @brief Network device driver for the ESP8266 WiFi interface + * @brief Network device driver for the ESP32 WiFi interface * * @author Gunar Schorcht */ @@ -20,35 +20,46 @@ #define ESP_WIFI_NETDEV_H #include "net/netdev.h" +#include "ringbuffer.h" #ifdef __cplusplus extern "C" { #endif /** - * @brief State of the WiFi interface + * @brief Buffer size used for RX buffering */ -typedef enum { - ESP_WIFI_NOT_WORKING, /**< interface is not working correctly */ - ESP_WIFI_DISCONNECTED, /**< interface is not associated to the AP */ - ESP_WIFI_CONNECTING, /**< interface is trying an association to the AP */ - ESP_WIFI_CONNECTED /**< interface is not associated to the AP */ -} esp_wifi_state_t; +#ifndef ESP_WIFI_BUFSIZE +#define ESP_WIFI_BUFSIZE (ETHERNET_MAX_LEN << 1) +#endif /** - * @brief Device descriptor for ESP infrastructure mode WIFI device + * @brief Reference to the netdev device driver struct + */ +extern const netdev_driver_t esp_wifi_driver; + +/** + * @brief Device descriptor for ESP WiFi devices */ typedef struct { - netdev_t netdev; /**< netdev parent struct */ + netdev_t netdev; /**< netdev parent struct */ - uint8_t mac[ETHERNET_ADDR_LEN]; /**< MAC address of the device */ + uint8_t rx_mem[ESP_WIFI_BUFSIZE]; /**< memory holding incoming packages */ + ringbuffer_t rx_buf; /**< ringbuffer for incoming packages */ - uint8_t rx_buf[ETHERNET_MAX_LEN]; /**< receive buffer */ - uint16_t rx_len; /**< number of bytes received from lwIP */ + uint16_t tx_len; /**< number of bytes in transmit buffer */ + uint8_t tx_buf[ETHERNET_MAX_LEN]; /**< transmit buffer */ - esp_wifi_state_t state; /**< indicates the interface state */ - uint32_t event; /**< received event */ + uint8_t event_recv; /**< number of frame received events */ + uint8_t event_conn; /**< number of pending connect events */ + uint8_t event_disc; /**< number of pending disc events */ + + bool connected; /**< indicates whether connected to AP */ + + gnrc_netif_t* netif; /**< reference to the corresponding netif */ + + mutex_t dev_lock; /**< device is already in use */ } esp_wifi_netdev_t; diff --git a/cpu/esp8266/esp-wifi/esp_wifi_params.h b/cpu/esp8266/esp-wifi/esp_wifi_params.h index 90dab212bb..9298c2d4a3 100644 --- a/cpu/esp8266/esp-wifi/esp_wifi_params.h +++ b/cpu/esp8266/esp-wifi/esp_wifi_params.h @@ -12,7 +12,7 @@ * @{ * * @file - * @brief Parameters for the ESP8266 WiFi netdev interface + * @brief Parameters for the ESP32 WiFi netdev interface * * @author Gunar Schorcht */ @@ -20,10 +20,10 @@ #ifndef ESP_WIFI_PARAMS_H #define ESP_WIFI_PARAMS_H -#if MODULE_ESP_WIFI || DOXYGEN +#if defined(MODULE_ESP_WIFI) || defined(DOXYGEN) /** - * @name Set default configuration parameters for the ESP WIFI netdev driver + * @name Set default configuration parameters for the ESP WiFi netdev driver * @{ */ @@ -31,30 +31,32 @@ * @brief The size of the stack used for the ESP WIFI netdev driver thread. */ #ifndef ESP_WIFI_STACKSIZE -#define ESP_WIFI_STACKSIZE (1536) +#define ESP_WIFI_STACKSIZE (THREAD_STACKSIZE_DEFAULT) #endif /** * @brief The priority of the ESP WiFi netdev driver thread. Should not be changed. */ #ifndef ESP_WIFI_PRIO -#define ESP_WIFI_PRIO (GNRC_NETIF_PRIO) +#define ESP_WIFI_PRIO (GNRC_NETIF_PRIO) #endif /** * @brief SSID of the AP to be used. */ #ifndef ESP_WIFI_SSID -#define ESP_WIFI_SSID "RIOT_AP" +#define ESP_WIFI_SSID "RIOT_AP" #endif /** - * @brief Passphrase used for the AP (max. 64 chars). + * @brief Passphrase used for the AP as clear text (max. 64 chars). */ #ifndef ESP_WIFI_PASS -#define ESP_WIFI_PASS "ThisistheRIOTporttoESP" +#define ESP_WIFI_PASS "ThisistheRIOTporttoESP" #endif +/**@}*/ + #ifdef __cplusplus extern "C" { #endif diff --git a/cpu/esp8266/esp_events.c b/cpu/esp8266/esp_events.c new file mode 100644 index 0000000000..13a6bfb00a --- /dev/null +++ b/cpu/esp8266/esp_events.c @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2019 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 ESP system event handler + * + * @author Gunar Schorcht + * + * @} + */ + +#if defined(MODULE_ESP_WIFI_ANY) || defined(MODULE_ESP_ETH) + +#define ENABLE_DEBUG 0 +#include "debug.h" + +#include + +#include "esp_common.h" +#include "log.h" + +#include "esp_attr.h" +#include "esp_event_loop.h" +#include "irq_arch.h" + +#define MAX_HANDLER_NUM 5 + +static system_event_cb_t _handler[MAX_HANDLER_NUM] = {}; +static void* _handler_arg[MAX_HANDLER_NUM] = {}; + +esp_err_t esp_system_event_add_handler (system_event_cb_t handler, void *arg) +{ + int i; + + /* determine next free handler entry */ + for (i = 0; i < MAX_HANDLER_NUM; i++) { + if (_handler[i] == NULL) { + break; + } + } + + /* return if there is no free entry */ + if (i == MAX_HANDLER_NUM) { + return ESP_FAIL; + } + + /* set the handler and argument entry */ + _handler[i] = handler; + _handler_arg[i] = arg; + + return ESP_OK; +} + +esp_err_t esp_system_event_del_handler (system_event_cb_t handler) +{ + int i; + + /* determine the handler entry */ + for (i = 0; i < MAX_HANDLER_NUM; i++) { + if (_handler[i] == handler) { + break; + } + } + + /* return if entry was not found */ + if (i == MAX_HANDLER_NUM) { + return ESP_FAIL; + } + + /* clean handler and arg entry */ + _handler[i] = NULL; + _handler_arg[i] = NULL; + + return ESP_OK; +} + +static esp_err_t esp_system_event_handler(void *ctx, system_event_t *event) +{ + for (int i = 0; i < MAX_HANDLER_NUM; i++) { + if (_handler[i] != NULL) { + _handler[i](_handler_arg[i], event); + } + } + return ESP_OK; +} + +#endif + +void esp_event_handler_init(void) +{ + #if defined(MODULE_ESP_WIFI_ANY) || defined(MODULE_ESP_ETH) + esp_event_loop_init(esp_system_event_handler, NULL); + #endif +} diff --git a/cpu/esp8266/exceptions.c b/cpu/esp8266/exceptions.c index 9d00b41bae..a81efb41fd 100644 --- a/cpu/esp8266/exceptions.c +++ b/cpu/esp8266/exceptions.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Gunar Schorcht + * Copyright (C) 2019 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 @@ -17,87 +17,144 @@ * @} */ -#define ENABLE_DEBUG 0 +#define ENABLE_DEBUG (0) #include "debug.h" +#include #include #include -#include "common.h" #include "log.h" #include "periph/pm.h" #include "ps.h" +#include "esp_common.h" #include "esp/common_macros.h" #include "esp/xtensa_ops.h" #include "sdk/ets.h" -#include "tools.h" #include "xtensa/corebits.h" #include "xtensa/xtensa_api.h" +extern void heap_stats(void); + 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 */ + "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 */ }; void IRAM NORETURN exception_handler (XtExcFrame *frame) { uint32_t excsave1; + uint32_t epc1; + uint32_t epc2; + uint32_t epc3; RSR(excsave1, excsave1); + RSR(epc1, epc1); + RSR(epc2, epc2); + RSR(epc3, epc3); - ets_printf("EXCEPTION!! exccause=%d (%s) @%08lx excvaddr=%08lx\n", +#ifdef MCU_ESP32 + uint32_t epc4; + RSR(epc4, epc4); +#endif + + ets_printf("EXCEPTION!! exccause=%d (%s) @%08x excvaddr=%08x\n", frame->exccause, exception_names[frame->exccause], excsave1, frame->excvaddr); - #if defined(DEVELHELP) - #if defined(MODULE_PS) - ps(); - #endif - print_meminfo(); - #endif - /* flushing the buffer */ - ets_printf(" \n"); - ets_printf(" \n"); - ets_printf(" \n"); +#if defined(DEVELHELP) + +#if defined(MODULE_PS) + ets_printf("processes:\n"); + ps(); + ets_printf("\n"); +#endif /* MODULE_PS */ + + heap_stats(); + + ets_printf("\nregister set\n"); + ets_printf("pc : %08x\t", frame->pc); + ets_printf("ps : %08x\t", frame->ps); + ets_printf("exccause: %08x\t", frame->exccause); + ets_printf("excvaddr: %08x\n", frame->excvaddr); + ets_printf("epc1 : %08x\t", epc1); + ets_printf("epc2 : %08x\t", epc2); + ets_printf("epc3 : %08x\t", epc3); +#ifdef MCU_ESP32 + ets_printf("epc4 : %08x\n", epc4); +#else /* MCU_ESP32 */ + ets_printf("epc3 : %08x\n", epc3); +#endif /* MCU_ESP32 */ + ets_printf("a0 : %08x\t", frame->a0); + ets_printf("a1 : %08x\t", frame->a1); + ets_printf("a2 : %08x\t", frame->a2); + ets_printf("a3 : %08x\n", frame->a3); + ets_printf("a4 : %08x\t", frame->a4); + ets_printf("a5 : %08x\t", frame->a5); + ets_printf("a6 : %08x\t", frame->a6); + ets_printf("a7 : %08x\n", frame->a7); + ets_printf("a8 : %08x\t", frame->a8); + ets_printf("a9 : %08x\t", frame->a9); + ets_printf("a10 : %08x\t", frame->a10); + ets_printf("a11 : %08x\n", frame->a11); + ets_printf("a12 : %08x\t", frame->a12); + ets_printf("a13 : %08x\t", frame->a13); + ets_printf("a14 : %08x\t", frame->a14); + ets_printf("a15 : %08x\n", frame->a15); +#if XCHAL_HAVE_LOOPS + ets_printf("lbeg : %08x\t", frame->lbeg); + ets_printf("lend : %08x\t", frame->lend); + ets_printf("lcount : %08x\n", frame->lcount); +#endif /* XCHAL_HAVE_LOOPS */ +#endif /* DEVELHELP */ + + /* restart */ + /* TODO: Improvement + Normally, we should try to restart the system. However, this + will not work after some exceptions, e.g., the LoadStoreErrorCause. + One option is to break the execution and wait for the WDT reset. Maybe + there is better way. If debugger is active, 'break 0,0' stops the + execution in debugger. */ + /* __asm__ volatile ("break 0,0"); */ /* hard reset */ __asm__ volatile (" call0 0x40000080 "); @@ -118,9 +175,7 @@ void init_exceptions (void) void IRAM NORETURN panic_arch(void) { #if defined(DEVELHELP) - print_meminfo(); - ets_printf(" \n"); - ets_printf(" \n"); + heap_stats(); #endif /* hard reset */ @@ -128,3 +183,10 @@ void IRAM NORETURN panic_arch(void) UNREACHABLE(); } + +void _panic_handler(uint32_t addr) +{ + ets_printf("#! _xt_panic called from 0x%08x: powering off\n", addr); + pm_off(); + while (1) { }; +} diff --git a/cpu/esp8266/freertos/queue.c b/cpu/esp8266/freertos/queue.c index f90f03ffae..6d1c072904 100644 --- a/cpu/esp8266/freertos/queue.c +++ b/cpu/esp8266/freertos/queue.c @@ -60,6 +60,10 @@ QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, uint32_t queue_size = uxQueueLength * uxItemSize; _queue_t* queue = malloc(sizeof(_queue_t) + queue_size); + if (!queue) { + return NULL; + } + queue->type = ucQueueType; queue->receiving.next = NULL; queue->sending.next = NULL; diff --git a/cpu/esp8266/include/common.h b/cpu/esp8266/include/common.h deleted file mode 100644 index d006cba148..0000000000 --- a/cpu/esp8266/include/common.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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 - * - */ - -#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 - - -#define LOG_TAG_ERROR(tag, fmt, ...) LOG_ERROR("[%s] " fmt, tag, ##__VA_ARGS__) -#define LOG_TAG_WARNING(tag, fmt, ...) LOG_WARNING("[%s] " fmt, tag, ##__VA_ARGS__) -#define LOG_TAG_INFO(tag, fmt, ...) LOG_INFO("[%s] " fmt, tag, ##__VA_ARGS__) -#define LOG_TAG_DEBUG(tag, fmt, ...) LOG_DEBUG("[%s] " fmt, tag, ##__VA_ARGS__) - -/** @} */ - -#ifdef __cplusplus -} -#endif - -#endif /* COMMON_H */ diff --git a/cpu/esp8266/include/cpu.h b/cpu/esp8266/include/cpu.h index e0e7747f7d..021d0a6247 100644 --- a/cpu/esp8266/include/cpu.h +++ b/cpu/esp8266/include/cpu.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Gunar Schorcht + * Copyright (C) 2019 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 diff --git a/cpu/esp8266/include/cpu_conf.h b/cpu/esp8266/include/cpu_conf.h index 11160e13d1..0f20079216 100644 --- a/cpu/esp8266/include/cpu_conf.h +++ b/cpu/esp8266/include/cpu_conf.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Gunar Schorcht + * Copyright (C) 2019 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 @@ -29,7 +29,16 @@ extern "C" { #endif /** - * @name Stack size configuration + * @brief Defines the CPU frequency in MHz + * + * Possible values are 80 and 160 MHz. + */ +#ifndef ESP8266_CPU_FREQUENCY +#define ESP8266_CPU_FREQUENCY (80) +#endif + +/** + * @name Stack size configurations * @{ */ #ifndef THREAD_EXTRA_STACKSIZE_PRINTF @@ -49,16 +58,12 @@ extern "C" { #define GNRC_IPV6_STACK_SIZE (1536) #endif #ifndef GNRC_PKTDUMP_STACKSIZE -#define GNRC_PKTDUMP_STACKSIZE (THREAD_STACKSIZE_DEFAULT) +#define GNRC_PKTDUMP_STACKSIZE (THREAD_STACKSIZE_DEFAULT << 1) #endif #ifndef ESP_NOW_STACKSIZE #define ESP_NOW_STACKSIZE (2560) #endif - -#ifndef ETS_THREAD_STACKSIZE -#define ETS_THREAD_STACKSIZE (1536) -#endif /** @} */ /** @@ -66,16 +71,6 @@ extern "C" { */ #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 -#undef __INT8 -#define __INT8 -#undef __INT32 -#define __INT32 -#endif - #ifdef __cplusplus } #endif diff --git a/cpu/esp8266/include/esp_common.h b/cpu/esp8266/include/esp_common.h new file mode 100644 index 0000000000..f81d80eb6a --- /dev/null +++ b/cpu/esp8266/include/esp_common.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2019 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 + * + */ + +#ifndef ESP_COMMON_H +#define ESP_COMMON_H + +#ifndef DOXYGEN + +#ifdef __cplusplus +extern "C" { +#endif + +#include "log.h" +#include "esp_common_log.h" + +#define asm __asm__ + +/** string representation of x */ +#ifndef XTSTR +#define _XTSTR(x) # x +#define XTSTR(x) _XTSTR(x) +#endif /* XSTR */ + +#if !defined(ICACHE_FLASH) +#ifndef ICACHE_RAM_ATTR +/** Places the code with this attribute in the IRAM. */ +#define ICACHE_RAM_ATTR __attribute__((section(".iram0.text"))) +#endif +#else /* ICACHE_FLASH */ +#ifndef ICACHE_RAM_ATTR +#define ICACHE_RAM_ATTR +#endif +#endif /* ICACHE_FLASH */ + +/** 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 ENABLE_DEBUG +/** + * @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 parameter condition (" #cond ") " \ + "not fulfilled\n", __func__); \ + 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 parameter condition (" #cond ") " \ + "not fulfilled\n", __func__); \ + return; \ + } + +#else /* ENABLE_DEBUG */ + +#define CHECK_PARAM_RET(cond,err) if (!(cond)) return err; +#define CHECK_PARAM(cond) if (!(cond)) return; + +#endif /* ENABLE_DEBUG */ + +/** gives the minimum of a and b */ +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +/** gives the maximum of a and b */ +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif + +/** + * @brief function name mappings for source code compatibility with ESP8266 port + * @{ + */ +#ifdef MCU_ESP32 +#define system_get_cpu_freq ets_get_cpu_frequency +#define system_update_cpu_freq ets_update_cpu_frequency +#endif /* MCU_ESP32 */ +/** @} */ + +/** @} */ + +/** microseconds per millisecond */ +#ifndef USEC_PER_MSEC +#define USEC_PER_MSEC 1000UL +#endif + +#ifndef MSEC_PER_SEC +#define MSEC_PER_SEC 1000UL +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* DOXYGEN */ + +#endif /* ESP_COMMON_H */ diff --git a/cpu/esp8266/include/esp_common_log.h b/cpu/esp8266/include/esp_common_log.h new file mode 100644 index 0000000000..a61e5349de --- /dev/null +++ b/cpu/esp8266/include/esp_common_log.h @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2019 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 log macros + * + * @author Gunar Schorcht + * + */ + +#ifndef ESP_COMMON_LOG_H +#define ESP_COMMON_LOG_H + +#ifndef DOXYGEN + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include "log.h" + +extern uint32_t system_get_time_ms (void); +extern int ets_printf(const char *fmt, ...); + +#if MODULE_ESP_LOG_COLORED + +#define LOG_RESET_COLOR "\033[0m" +#define LOG_COLOR_E "\033[1;31m" +#define LOG_COLOR_W "\033[1;33m" +#define LOG_COLOR_I "\033[1m" +#define LOG_COLOR_D "\033[0;32m" +#define LOG_COLOR_V + +#else /* MODULE_ESP_LOG_COLORED */ + +#define LOG_RESET_COLOR +#define LOG_COLOR_E +#define LOG_COLOR_W +#define LOG_COLOR_I +#define LOG_COLOR_D +#define LOG_COLOR_V + +#endif /* MODULE_ESP_LOG_COLORED */ + +#if MODULE_ESP_LOG_TAGGED + +#define LOG_FORMAT(letter, format) LOG_COLOR_ ## letter #letter " (%d) [%s] " format LOG_RESET_COLOR + +#define LOG_TAG(level, letter, tag, format, ...) \ + do { \ + if ((unsigned)level <= (unsigned)LOG_LEVEL) { \ + printf(LOG_FORMAT(letter, format), system_get_time_ms(), tag, ##__VA_ARGS__); \ + fflush(stdout); \ + } \ + } while(0) + +#define LOG_TAG_EARLY(level, letter, tag, format, ...) \ + do { \ + if (LOG_LEVEL >= level) { \ + ets_printf(LOG_FORMAT(letter, format), system_get_time_ms(), tag, ##__VA_ARGS__); \ + } \ + } while(0) + +#else /* MODULE_ESP_LOG_TAGGED */ + +#define LOG_FORMAT(letter, format) LOG_COLOR_ ## letter format LOG_RESET_COLOR + +#define LOG_TAG(level, letter, tag, format, ...) \ + do { \ + (void)tag; \ + if ((unsigned)level <= (unsigned)LOG_LEVEL) { \ + printf(LOG_FORMAT(letter, format), ##__VA_ARGS__); \ + fflush(stdout); \ + } \ + } while (0U) + +#define LOG_TAG_EARLY(level, letter, tag, format, ...) \ + do { \ + (void)tag; \ + if ((unsigned)level <= (unsigned)LOG_LEVEL) { \ + ets_printf(LOG_FORMAT(letter, format), ##__VA_ARGS__); \ + } \ + } while (0U) + +#endif /* MODULE_ESP_LOG_TAGGED */ + +/** + * Override LOG_* definitions with a tagged version. By default the function + * name is used as tag. + */ +#ifndef MODULE_LOG_PRINTFNOFORMAT +#undef LOG_ERROR +#undef LOG_INFO +#undef LOG_WARNING +#undef LOG_DEBUG +#define LOG_ERROR(format, ...) LOG_TAG(LOG_ERROR , E, __func__, format, ##__VA_ARGS__) +#define LOG_WARNING(format, ...) LOG_TAG(LOG_WARNING, W, __func__, format, ##__VA_ARGS__) +#define LOG_INFO(format, ...) LOG_TAG(LOG_INFO , I, __func__, format, ##__VA_ARGS__) +#define LOG_DEBUG(format, ...) LOG_TAG(LOG_DEBUG , D, __func__, format, ##__VA_ARGS__) +#endif + +/** Tagged LOG_* definitions */ +#define LOG_TAG_ERROR(tag, format, ...) LOG_TAG(LOG_ERROR , E, tag, format, ##__VA_ARGS__) +#define LOG_TAG_WARNING(tag, format, ...) LOG_TAG(LOG_WARNING, W, tag, format, ##__VA_ARGS__) +#define LOG_TAG_INFO(tag, format, ...) LOG_TAG(LOG_INFO , I, tag, format, ##__VA_ARGS__) +#define LOG_TAG_DEBUG(tag, format, ...) LOG_TAG(LOG_DEBUG , D, tag, format, ##__VA_ARGS__) + +/** definitions for source code compatibility with ESP-IDF */ +#define ESP_EARLY_LOGE(tag, format, ...) LOG_TAG_EARLY(LOG_ERROR , E, tag, format "\n", ##__VA_ARGS__) +#define ESP_EARLY_LOGW(tag, format, ...) LOG_TAG_EARLY(LOG_WARNING, W, tag, format "\n", ##__VA_ARGS__) +#define ESP_EARLY_LOGI(tag, format, ...) LOG_TAG_EARLY(LOG_INFO , I, tag, format "\n", ##__VA_ARGS__) +#define ESP_LOGE(tag, format, ...) LOG_TAG(LOG_ERROR , E, tag, format "\n", ##__VA_ARGS__) +#define ESP_LOGW(tag, format, ...) LOG_TAG(LOG_WARNING, W, tag, format "\n", ##__VA_ARGS__) +#define ESP_LOGI(tag, format, ...) LOG_TAG(LOG_INFO , I, tag, format "\n", ##__VA_ARGS__) + +#if ENABLE_DEBUG + +#define ESP_EARLY_LOGD(tag, format, ...) LOG_TAG_EARLY(LOG_DEBUG, D, tag, format "\n", ##__VA_ARGS__) +#define ESP_EARLY_LOGV(tag, format, ...) LOG_TAG_EARLY(LOG_ALL , V, tag, format "\n", ##__VA_ARGS__) +#define ESP_LOGD(tag, format, ...) LOG_TAG(LOG_DEBUG, D, tag, format "\n", ##__VA_ARGS__) +#define ESP_LOGV(tag, format, ...) LOG_TAG(LOG_ALL , V, tag, format "\n", ##__VA_ARGS__) + +#else /* ENABLE_DEBUG */ + +#define ESP_EARLY_LOGD( tag, format, ... ) (void)tag +#define ESP_EARLY_LOGV( tag, format, ... ) (void)tag +#define ESP_LOGD( tag, format, ... ) (void)tag +#define ESP_LOGV( tag, format, ... ) (void)tag + +#endif /* ENABLE_DEBUG */ + +#ifdef __cplusplus +} +#endif + +#endif /* DOXYGEN */ + +#endif /* ESP_COMMON_LOG_H */ diff --git a/cpu/esp8266/include/exceptions.h b/cpu/esp8266/include/exceptions.h index e159bf1050..611647d076 100644 --- a/cpu/esp8266/include/exceptions.h +++ b/cpu/esp8266/include/exceptions.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Gunar Schorcht + * Copyright (C) 2019 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 @@ -24,7 +24,7 @@ extern "C" { #endif -/** Initalize exception handler */ +/** Initialize exception handler */ extern void init_exceptions(void); #ifdef __cplusplus diff --git a/cpu/esp8266/include/gpio_arch.h b/cpu/esp8266/include/gpio_arch.h new file mode 100644 index 0000000000..ece90ae81a --- /dev/null +++ b/cpu/esp8266/include/gpio_arch.h @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2019 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 Architecture specific GPIO functions for ESP8266 + * + * @author Gunar Schorcht + * @} + */ + +#ifndef GPIO_ARCH_H +#define GPIO_ARCH_H + +#ifdef MCU_ESP32 +#include "periph/gpio.h" +#include "soc/io_mux_reg.h" +#include "soc/gpio_sig_map.h" +#endif /* MCU_ESP32 */ + +#ifndef DOXYGEN + +#ifdef __cplusplus +extern "C" { +#endif + +/** Definitions for source code compatibility with ESP-IDF */ +#define GPIO_MODE_INPUT GPIO_IN +#define GPIO_MODE_OUTPUT GPIO_OUT +#define GPIO_MODE_INPUT_OUTPUT GPIO_IN_OUT + +/** Definition of possible GPIO usage types (for internal use only) */ +typedef enum +{ + _GPIO = 0, /**< pin used as standard GPIO */ +#ifdef MCU_ESP32 + _ADC, /**< pin used as ADC input */ + _CAN, /**< pin used as CAN signal */ + _DAC, /**< pin used as DAC output */ + _EMAC, /**< pin used as EMAC signal */ +#endif /* MCU_ESP32 */ + _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 */ + _NOT_EXIST /**< pin cannot be used at all */ +} gpio_pin_usage_t; + + +#ifdef MCU_ESP32 + +/** Table of GPIO to IOMUX register mappings */ +extern const uint32_t _gpio_to_iomux_reg[]; +#define GPIO_PIN_MUX_REG _gpio_to_iomux_reg + +#else /* MCU_ESP32 */ + +/** 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[]; + +#endif /* MCU_ESP32 */ + +/** + * @brief Set the usage type of the pin + * @param pin GPIO pin + * @param usage GPIO pin usage type + * @return 0 on success + * -1 on error + */ +int gpio_set_pin_usage(gpio_t pin, gpio_pin_usage_t usage); + +/** + * @brief Get the usage type of the pin + * @param pin GPIO pin + * @return GPIO pin usage type on success + * _NOT_EXIST on error + */ +gpio_pin_usage_t gpio_get_pin_usage(gpio_t pin); + +/** + * @brief Get the usage type of the pin as string + * @param pin GPIO pin + * @return GPIO pin usage type string on success + * _NOT_EXIST on error + */ +const char* gpio_get_pin_usage_str(gpio_t pin); + +#ifdef MCU_ESP32 +/** + * @brief Disable the pullup of the pin + */ +void gpio_pullup_dis (gpio_t pin); + +/** + * @brief Returns the RTCIO pin number or -1 if the pin is not an RTCIO pin + */ +int8_t gpio_is_rtcio (gpio_t pin); + +/** + * @brief Configure sleep mode for an GPIO pin if the pin is an RTCIO pin + * @param pin GPIO pin + * @param mode active in sleep mode if true + * @param input as input if true, as output otherwise + * @return 0 on success + * @return -1 on invalid pin + */ +int gpio_config_sleep_mode (gpio_t pin, bool sleep_mode, bool input); + +/** + * @brief GPIO set direction init the pin calling gpio_init + * @param pin GPIO pin + * @param mode active in sleep mode if true + * @return 0 on success + * -1 on invalid argument + */ +int gpio_set_direction(gpio_t pin, gpio_mode_t mode); + +/** + * @brief extern declaration of ROM functions to avoid compilation problems + */ +void gpio_matrix_in (uint32_t gpio, uint32_t signal_idx, bool inv); +void gpio_matrix_out(uint32_t gpio, uint32_t signal_idx, bool out_inv, bool oen_inv); + +#endif /* MCU_ESP32 */ + +#ifdef __cplusplus +} +#endif + +#endif /* DOXYGEN */ +#endif /* GPIO_ARCH_H */ diff --git a/cpu/esp8266/include/irq_arch.h b/cpu/esp8266/include/irq_arch.h index b3dfbe8079..46626a0818 100644 --- a/cpu/esp8266/include/irq_arch.h +++ b/cpu/esp8266/include/irq_arch.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Gunar Schorcht + * Copyright (C) 2019 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 @@ -39,9 +39,8 @@ extern uint32_t irq_interrupt_nesting; /** * @name Macros to enter and exit an ISR * - * In non-SDK interrupt handling all stuff is done in `_frxt_int_enter` - * and `_frxt_int_exit`. These macros do therefore nothing and are kept only - * for source code compatibility. + * Since all the stuff is done in `_frxt_int_enter` and `_frxt_int_exit`, these + * macros are doing nothing and are kept only for source code compatibility. * * @{ */ @@ -56,10 +55,28 @@ extern uint32_t irq_interrupt_nesting; * * @{ */ -#define critical_enter() int _irq_state = irq_disable () +#define critical_enter() int _irq_state = irq_disable() #define critical_exit() irq_restore(_irq_state) /** @} */ +/** + * @name Macros to enter and exit a critical region with state variable + * @{ + */ +#define critical_enter_var(m) m = irq_disable() +#define critical_exit_var(m) irq_restore(m) +/** @} */ + +/** + * @name Software interrupt types + * + * These definitions are used to distinguish different types of software + * interrupts in software interrupt handler. + */ +#define ETS_SOFT_INT_NONE 0 +#define ETS_SOFT_INT_YIELD 1 +#define ETS_SOFT_INT_HDL_MAC 2 + #ifdef __cplusplus } #endif diff --git a/cpu/esp8266/include/log_module.h b/cpu/esp8266/include/log_module.h new file mode 100644 index 0000000000..08f566b87a --- /dev/null +++ b/cpu/esp8266/include/log_module.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2019 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 Log module to realize consistent log messages + * + * @author Gunar Schorcht + */ + +#ifndef LOG_MODULE_H +#define LOG_MODULE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "esp_common_log.h" + +#ifdef MODULE_LOG_PRINTFNOFORMAT + +static inline void log_write(unsigned level, const char *format, ...) { + (void)level; + puts(format); +} + +#else /* MODULE_LOG_PRINTFNOFORMAT */ + +#define log_write(level, ...) \ + do { \ + if (level == LOG_ERROR) { \ + LOG_TAG(LOG_ERROR, E, __func__, ##__VA_ARGS__); \ + } \ + else if (level == LOG_WARNING) { \ + LOG_TAG(LOG_WARNING, W, __func__, ##__VA_ARGS__); \ + } \ + else if (level == LOG_INFO) { \ + LOG_TAG(LOG_INFO, D, __func__, ##__VA_ARGS__); \ + } \ + else if (level == LOG_DEBUG) { \ + LOG_TAG(LOG_DEBUG, E, __func__, ##__VA_ARGS__); \ + } \ + } while (0) + +#endif /* MODULE_LOG_PRINTFNOFORMAT */ + +#ifdef __cplusplus +} +#endif +/**@}*/ +#endif /* LOG_MODULE_H */ diff --git a/cpu/esp8266/include/periph_cpu.h b/cpu/esp8266/include/periph_cpu.h index 09f0c0451c..ae916c08db 100644 --- a/cpu/esp8266/include/periph_cpu.h +++ b/cpu/esp8266/include/periph_cpu.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Gunar Schorcht + * Copyright (C) 2019 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 @@ -33,31 +33,55 @@ extern "C" { #define CPUID_LEN (4U) /** - * @name GPIO configuration of ESP8266 + * @name GPIO configuration * @{ */ /** - * @brief Available ports on the ESP8266 + * @brief Override the default gpio_t type definition + * + * This is required here to have gpio_t defined in this file. * @{ */ -#define PORT_GPIO 0 /**< port GPIO */ +#define HAVE_GPIO_T +typedef unsigned int gpio_t; /** @} */ /** * @brief Definition of a fitting UNDEF value */ -#define GPIO_UNDEF (GPIO_ID_NONE) +#define GPIO_UNDEF ((gpio_t)(UINT_MAX)) /** - * @brief Define CPU specific GPIO pin generator macro + * @brief Define a CPU specific GPIO pin generator macro */ -#define GPIO_PIN(x, y) ((x << 4) | y) +#define GPIO_PIN(x, y) ((x & 0) | y) + +/** + * @brief Available GPIO ports on ESP8266 + */ +#define PORT_GPIO (0) /** * @brief Define CPU specific number of GPIO pins */ -#define GPIO_PIN_NUMOF GPIO_PIN_COUNT+1 +#define GPIO_PIN_NUMOF (17) + +/** + * @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; +/** @} */ +/** @} */ /** * @name Predefined GPIO names @@ -82,51 +106,64 @@ extern "C" { #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 + * + * ESP8266 provides up to two bit-banging I2C interfaces. + * + * The board-specific configuration of the I2C interface I2C_DEV(n) requires + * the definition of + * + * I2Cn_SPEED, the bus speed, + * I2Cn_SCL, the GPIO used as SCL signal, and + * I2Cn_SDA, the GPIO used as SDA signal, + * + * where n can be 0 or 1. If they are not defined, the I2C interface + * I2C_DEV(n) is not used. + * + * @note The configuration of the I2C interfaces I2C_DEV(n) must be in + * continuous ascending order of n. + * + * I2C_NUMOF is determined automatically from board-specific peripheral + * definitions of I2Cn_SPEED, I2Cn_SCK, and I2Cn_SDA. + * * @{ */ -#define PERIPH_I2C_NEED_READ_REG -#define PERIPH_I2C_NEED_READ_REGS -#define PERIPH_I2C_NEED_WRITE_REG -#define PERIPH_I2C_NEED_WRITE_REGS + +/** + * @brief Override I2C clock speed values + * + * This is required here to have i2c_speed_t defined in this file. + * @{ + */ +#define HAVE_I2C_SPEED_T +typedef enum { + I2C_SPEED_LOW = 0, /**< 10 kbit/s */ + I2C_SPEED_NORMAL, /**< 100 kbit/s */ + I2C_SPEED_FAST, /**< 400 kbit/s */ + I2C_SPEED_FAST_PLUS, /**< 1 Mbit/s */ + I2C_SPEED_HIGH, /**< not supported */ +} i2c_speed_t; +/** @} */ + +/** + * @brief I2C configuration structure type + */ +typedef struct { + i2c_speed_t speed; /**< I2C bus speed */ + gpio_t scl; /**< GPIO used as SCL pin */ + gpio_t sda; /**< GPIO used as SDA pin */ +} i2c_conf_t; + +/** + * @brief Maximum number of I2C interfaces that can be used by board definitions + */ +#define I2C_NUMOF_MAX (2) + +#define PERIPH_I2C_NEED_READ_REG /**< i2c_read_reg required */ +#define PERIPH_I2C_NEED_READ_REGS /**< i2c_read_regs required */ +#define PERIPH_I2C_NEED_WRITE_REG /**< i2c_write_reg required */ +#define PERIPH_I2C_NEED_WRITE_REGS /**< i2c_write_regs required */ /** @} */ /** @@ -139,24 +176,114 @@ typedef enum { /** @} */ /** - * @name SPI configuration + * @name PWM configuration + * + * 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. The + * GPIOs that can be used as PWM channels are defined by #PWM0_GPIOS in board + * definition. + * + * @note The minimum PWM period that can be realized 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. + * * @{ */ -#if defined(MODULE_PERIPH_SPI) -#define PERIPH_SPI_NEEDS_TRANSFER_BYTE -#define PERIPH_SPI_NEEDS_TRANSFER_REG -#define PERIPH_SPI_NEEDS_TRANSFER_REGS +/** + * @brief Maximum number of PWM devices. + */ +#define PWM_NUMOF_MAX (1) -#endif /* MODULE_PERIPH_SPI */ +/** + * @brief Maximum number of channels per PWM device. + */ +#define PWM_CHANNEL_NUM_MAX (8) /** @} */ +/** + * @name SPI configuration + * + * ESP8266 has two SPI controllers: + * + * - _CSPI_ for caching and accessing the flash memory
+ * - _HSPI_ for peripherals + * + * Thus, _HSPI_ is the only SPI interface that is available for peripherals. + * It is exposed as RIOT's SPI_DEV(0). The pin configuration of the _HSPI_ + * interface is fixed as shown in following table. + * + * Signal | Pin + * -- --------|------- + * #SPI0_MISO | GPIO12 + * #SPI0_MOSI | GPIO13 + * #SPI0_SCK | GPIO14 + * #SPI0_CS0 | GPIOn with n = 0, 2, 4, 5, 15, 16 (additionally 9, 10 in DOUT flash mode) + * + * The only pin definition that can be overridden by an application-specific + * board configuration is the CS signal defined by #SPI0_CS0. + * + * @{ + */ + +/** + * @brief SPI controllers that can be used for peripheral interfaces + */ +typedef enum { + HSPI = 1, /**< HSPI interface controller */ +} spi_ctrl_t; + +/** + * @brief SPI configuration structure type + */ +typedef struct { + spi_ctrl_t ctrl; /**< SPI controller used for the interface */ + gpio_t sck; /**< GPIO used as SCK pin */ + gpio_t mosi; /**< GPIO used as MOSI pin */ + gpio_t miso; /**< GPIO used as MISO pin */ + gpio_t cs; /**< GPIO used as CS0 pin */ +} spi_conf_t; + +/** + * @brief Maximum number of SPI interfaces + */ +#define SPI_NUMOF_MAX (1) + +#define PERIPH_SPI_NEEDS_TRANSFER_BYTE /**< requires function spi_transfer_byte */ +#define PERIPH_SPI_NEEDS_TRANSFER_REG /**< requires function spi_transfer_reg */ +#define PERIPH_SPI_NEEDS_TRANSFER_REGS /**< requires function spi_transfer_regs */ +/** @} */ + /** * @brief Prevent shared timer functions from being used */ #define PERIPH_TIMER_PROVIDES_SET +/** + * @name UART configuration + * + * All ESP8266 boards have exactly one UART device with fixed pin mapping. + * + * @{ + */ + +/** + * @brief UART configuration structure type + */ +typedef struct { + gpio_t txd; /**< GPIO used as TxD pin */ + gpio_t rxd; /**< GPIO used as RxD pin */ +} uart_conf_t; + +/** + * @brief Maximum number of UART interfaces + */ +#define UART_NUMOF_MAX (1) +/** @} */ + #ifdef __cplusplus } #endif diff --git a/cpu/esp8266/include/sdk_conf.h b/cpu/esp8266/include/sdk_conf.h new file mode 100644 index 0000000000..67f8954e22 --- /dev/null +++ b/cpu/esp8266/include/sdk_conf.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2019 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 SDK configuration compatible to the ESP-IDF + * + * This file defines configuration parameters that are only required for source + * code compatibility with the SDK. These configuration parameters are not used + * directly to configure the compilation of RIOT-OS. However, some of them can + * be overrien overridden by application-specific board configuration. + * + * @author Gunar Schorcht + */ + +#ifndef SDK_CONF_H +#define SDK_CONF_H + +#ifndef DOXYGEN + +#include "board.h" +#include "esp_image_format.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief SDK version number + * + * Determined with `git describe --tags` in `$ESP8266_SDK_DIR` + */ +#define IDF_VER "v3.1-4-g08c234e" + +/** + * @name Default console configuration + * + * STDIO_UART_BAUDRATE is used as CONFIG_CONSOLE_UART_BAUDRATE and + * can be overridden by an application specific configuration. + * + * @{ + */ +#define CONFIG_CONSOLE_UART_NUM (0) +#ifndef CONFIG_CONSOLE_UART_BAUDRATE +#define CONFIG_CONSOLE_UART_BAUDRATE (STDIO_UART_BAUDRATE) +#endif +/** @} */ + +#define CONFIG_APP1_SIZE (0xf0000) +#define CONFIG_APP1_OFFSET (0x10000) + +#define CONFIG_SOC_IRAM_SIZE (0xc000) + +#define CONFIG_TASK_WDT_PANIC +#define CONFIG_TASK_WDT_TIMEOUT_S (15) + +#define CONFIG_WIFI_PPT_TASKSTACK_SIZE (3584) +#define CONFIG_MAIN_TASK_STACK_SIZE (2048) +#define CONFIG_EVENT_LOOP_STACK_SIZE (2048) + +#define CONFIG_ESP_PHY_CALIBRATION_AND_DATA_STORAGE +#define CONFIG_ESP_PHY_INIT_DATA_IN_PARTITION (0) + +#define CONFIG_SPI_FLASH_FREQ (ESP_IMAGE_SPI_SPEED_40M) /* 40 MHz */ +#define CONFIG_SPI_FLASH_MODE (ESP_IMAGE_SPI_MODE_DIO) /* DIO mode */ +#define CONFIG_SPI_FLASH_SIZE (0x100000) + +#define CONFIG_SCAN_AP_MAX (32) + +#define CONFIG_USING_NEW_ETS_VPRINTF +#define CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF + +#ifdef __cplusplus +} +#endif + +#endif /* DOXYGEN */ +#endif /* SDK_CONF_H */ diff --git a/cpu/esp8266/include/stdio.h b/cpu/esp8266/include/stdio.h new file mode 100644 index 0000000000..4fc9320acf --- /dev/null +++ b/cpu/esp8266/include/stdio.h @@ -0,0 +1,748 @@ +/** + * This file is a modification of the original file to overwrite the *putchar* + * and *getchar* macros in the case the *uart_stdio* module is used. If the + * *uart_stdio* module is used, *putchar* and *getchar* are redirections to + * according *uart_stdio_* functions. + */ + +/* + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * @(#)stdio.h 5.3 (Berkeley) 3/15/86 + */ + +/* + * NB: to fit things in six character monocase externals, the + * stdio code uses the prefix `__s' for stdio objects, typically + * followed by a three-character attempt at a mnemonic. + */ + +#ifndef STDIO_H +#define STDIO_H + +#ifndef DOXYGEN + +#ifdef __cplusplus +extern "C" { +#endif + +#include "_ansi.h" + +#define _FSTDIO /* ``function stdio'' */ + +#define __need_size_t +#define __need_NULL +#include +#include + +#define __need___va_list +#include + +/* + * defines __FILE, _fpos_t. + * They must be defined there because struct _reent needs them (and we don't + * want reent.h to include this file. + */ + +#include +#include + +_BEGIN_STD_C + +typedef __FILE FILE; + +#ifdef __CYGWIN__ +typedef _fpos64_t fpos_t; +#else +typedef _fpos_t fpos_t; +#ifdef __LARGE64_FILES +typedef _fpos64_t fpos64_t; +#endif +#endif /* !__CYGWIN__ */ + +#include + +#define __SLBF 0x0001 /* line buffered */ +#define __SNBF 0x0002 /* unbuffered */ +#define __SRD 0x0004 /* OK to read */ +#define __SWR 0x0008 /* OK to write */ + /* RD and WR are never simultaneously asserted */ +#define __SRW 0x0010 /* open for reading & writing */ +#define __SEOF 0x0020 /* found EOF */ +#define __SERR 0x0040 /* found error */ +#define __SMBF 0x0080 /* _buf is from malloc */ +#define __SAPP 0x0100 /* fdopen()ed in append mode - so must write to end */ +#define __SSTR 0x0200 /* this is an sprintf/snprintf string */ +#define __SOPT 0x0400 /* do fseek() optimisation */ +#define __SNPT 0x0800 /* do not do fseek() optimisation */ +#define __SOFF 0x1000 /* set iff _offset is in fact correct */ +#define __SORD 0x2000 /* true => stream orientation (byte/wide) decided */ +#if defined(__CYGWIN__) +#define __SCLE 0x4000 /* convert line endings CR/LF <-> NL */ +#endif +#define __SL64 0x8000 /* is 64-bit offset large file */ + +/* _flags2 flags */ +#define __SNLK 0x0001 /* stdio functions do not lock streams themselves */ +#define __SWID 0x2000 /* true => stream orientation wide, false => byte, only valid if __SORD in _flags is true */ + +/* + * The following three definitions are for ANSI C, which took them + * from System V, which stupidly took internal interface macros and + * made them official arguments to setvbuf(), without renaming them. + * Hence, these ugly _IOxxx names are *supposed* to appear in user code. + * + * Although these happen to match their counterparts above, the + * implementation does not rely on that (so these could be renumbered). + */ +#define _IOFBF 0 /* setvbuf should set fully buffered */ +#define _IOLBF 1 /* setvbuf should set line buffered */ +#define _IONBF 2 /* setvbuf should set unbuffered */ + +#define EOF (-1) + +#ifdef __BUFSIZ__ +#define BUFSIZ __BUFSIZ__ +#else +#define BUFSIZ 1024 +#endif + +#ifdef __FOPEN_MAX__ +#define FOPEN_MAX __FOPEN_MAX__ +#else +#define FOPEN_MAX 20 +#endif + +#ifdef __FILENAME_MAX__ +#define FILENAME_MAX __FILENAME_MAX__ +#else +#define FILENAME_MAX 1024 +#endif + +#ifdef __L_tmpnam__ +#define L_tmpnam __L_tmpnam__ +#else +#define L_tmpnam FILENAME_MAX +#endif + +#ifndef __STRICT_ANSI__ +#define P_tmpdir "/tmp" +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 /* set file offset to offset */ +#endif +#ifndef SEEK_CUR +#define SEEK_CUR 1 /* set file offset to current plus offset */ +#endif +#ifndef SEEK_END +#define SEEK_END 2 /* set file offset to EOF plus offset */ +#endif + +#define TMP_MAX 26 + +#define stdin (_REENT->_stdin) +#define stdout (_REENT->_stdout) +#define stderr (_REENT->_stderr) + +#define _stdin_r(x) ((x)->_stdin) +#define _stdout_r(x) ((x)->_stdout) +#define _stderr_r(x) ((x)->_stderr) + +/* + * Functions defined in ANSI C standard. + */ + +#ifndef __VALIST +#ifdef __GNUC__ +#define __VALIST __gnuc_va_list +#else +#define __VALIST char* +#endif +#endif + +FILE * _EXFUN(tmpfile, (void)); +char * _EXFUN(tmpnam, (char *)); +#if __BSD_VISIBLE || __XSI_VISIBLE || __POSIX_VISIBLE >= 200112 +char * _EXFUN(tempnam, (const char *, const char *)); +#endif +int _EXFUN(fclose, (FILE *)); +int _EXFUN(fflush, (FILE *)); +FILE * _EXFUN(freopen, (const char *__restrict, const char *__restrict, FILE *__restrict)); +void _EXFUN(setbuf, (FILE *__restrict, char *__restrict)); +int _EXFUN(setvbuf, (FILE *__restrict, char *__restrict, int, size_t)); +int _EXFUN(fprintf, (FILE *__restrict, const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__printf__, 2, 3)))); +int _EXFUN(fscanf, (FILE *__restrict, const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__scanf__, 2, 3)))); +int _EXFUN(printf, (const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__printf__, 1, 2)))); +int _EXFUN(scanf, (const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__scanf__, 1, 2)))); +int _EXFUN(sscanf, (const char *__restrict, const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__scanf__, 2, 3)))); +int _EXFUN(vfprintf, (FILE *__restrict, const char *__restrict, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 2, 0)))); +int _EXFUN(vprintf, (const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 1, 0)))); +int _EXFUN(vsprintf, (char *__restrict, const char *__restrict, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 2, 0)))); +int _EXFUN(fgetc, (FILE *)); +char * _EXFUN(fgets, (char *__restrict, int, FILE *__restrict)); +int _EXFUN(fputc, (int, FILE *)); +int _EXFUN(fputs, (const char *__restrict, FILE *__restrict)); +int _EXFUN(getc, (FILE *)); +int _EXFUN(getchar, (void)); +char * _EXFUN(gets, (char *)); +int _EXFUN(putc, (int, FILE *)); +int _EXFUN(putchar, (int)); +int _EXFUN(puts, (const char *)); +int _EXFUN(ungetc, (int, FILE *)); +size_t _EXFUN(fread, (_PTR __restrict, size_t _size, size_t _n, FILE *__restrict)); +size_t _EXFUN(fwrite, (const _PTR __restrict , size_t _size, size_t _n, FILE *)); +#ifdef _COMPILING_NEWLIB +int _EXFUN(fgetpos, (FILE *, _fpos_t *)); +#else +int _EXFUN(fgetpos, (FILE *__restrict, fpos_t *__restrict)); +#endif +int _EXFUN(fseek, (FILE *, long, int)); +#ifdef _COMPILING_NEWLIB +int _EXFUN(fsetpos, (FILE *, const _fpos_t *)); +#else +int _EXFUN(fsetpos, (FILE *, const fpos_t *)); +#endif +long _EXFUN(ftell, ( FILE *)); +void _EXFUN(rewind, (FILE *)); +void _EXFUN(clearerr, (FILE *)); +int _EXFUN(feof, (FILE *)); +int _EXFUN(ferror, (FILE *)); +void _EXFUN(perror, (const char *)); +#ifndef _REENT_ONLY +FILE * _EXFUN(fopen, (const char *__restrict _name, const char *__restrict _type)); +int _EXFUN(sprintf, (char *__restrict, const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__printf__, 2, 3)))); +int _EXFUN(remove, (const char *)); +int _EXFUN(rename, (const char *, const char *)); +#ifdef _COMPILING_NEWLIB +int _EXFUN(_rename, (const char *, const char *)); +#endif +#endif +#if !defined(__STRICT_ANSI__) || defined(__USE_XOPEN2K) +#ifdef _COMPILING_NEWLIB +int _EXFUN(fseeko, (FILE *, _off_t, int)); +_off_t _EXFUN(ftello, ( FILE *)); +#else +int _EXFUN(fseeko, (FILE *, off_t, int)); +off_t _EXFUN(ftello, ( FILE *)); +#endif +#endif +#if __GNU_VISIBLE +int _EXFUN(fcloseall, (_VOID)); +#endif +#if !defined(__STRICT_ANSI__) || (__STDC_VERSION__ >= 199901L) || (__cplusplus >= 201103L) +#ifndef _REENT_ONLY +int _EXFUN(asiprintf, (char **, const char *, ...) + _ATTRIBUTE ((__format__ (__printf__, 2, 3)))); +char * _EXFUN(asniprintf, (char *, size_t *, const char *, ...) + _ATTRIBUTE ((__format__ (__printf__, 3, 4)))); +char * _EXFUN(asnprintf, (char *__restrict, size_t *__restrict, const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__printf__, 3, 4)))); +int _EXFUN(asprintf, (char **__restrict, const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__printf__, 2, 3)))); +#ifndef diprintf +int _EXFUN(diprintf, (int, const char *, ...) + _ATTRIBUTE ((__format__ (__printf__, 2, 3)))); +#endif +int _EXFUN(fiprintf, (FILE *, const char *, ...) + _ATTRIBUTE ((__format__ (__printf__, 2, 3)))); +int _EXFUN(fiscanf, (FILE *, const char *, ...) + _ATTRIBUTE ((__format__ (__scanf__, 2, 3)))); +int _EXFUN(iprintf, (const char *, ...) + _ATTRIBUTE ((__format__ (__printf__, 1, 2)))); +int _EXFUN(iscanf, (const char *, ...) + _ATTRIBUTE ((__format__ (__scanf__, 1, 2)))); +int _EXFUN(siprintf, (char *, const char *, ...) + _ATTRIBUTE ((__format__ (__printf__, 2, 3)))); +int _EXFUN(siscanf, (const char *, const char *, ...) + _ATTRIBUTE ((__format__ (__scanf__, 2, 3)))); +int _EXFUN(snprintf, (char *__restrict, size_t, const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__printf__, 3, 4)))); +int _EXFUN(sniprintf, (char *, size_t, const char *, ...) + _ATTRIBUTE ((__format__ (__printf__, 3, 4)))); +int _EXFUN(vasiprintf, (char **, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 2, 0)))); +char * _EXFUN(vasniprintf, (char *, size_t *, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 3, 0)))); +char * _EXFUN(vasnprintf, (char *, size_t *, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 3, 0)))); +int _EXFUN(vasprintf, (char **, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 2, 0)))); +int _EXFUN(vdiprintf, (int, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 2, 0)))); +int _EXFUN(vfiprintf, (FILE *, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 2, 0)))); +int _EXFUN(vfiscanf, (FILE *, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__scanf__, 2, 0)))); +int _EXFUN(vfscanf, (FILE *__restrict, const char *__restrict, __VALIST) + _ATTRIBUTE ((__format__ (__scanf__, 2, 0)))); +int _EXFUN(viprintf, (const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 1, 0)))); +int _EXFUN(viscanf, (const char *, __VALIST) + _ATTRIBUTE ((__format__ (__scanf__, 1, 0)))); +int _EXFUN(vscanf, (const char *, __VALIST) + _ATTRIBUTE ((__format__ (__scanf__, 1, 0)))); +int _EXFUN(vsiprintf, (char *, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 2, 0)))); +int _EXFUN(vsiscanf, (const char *, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__scanf__, 2, 0)))); +int _EXFUN(vsniprintf, (char *, size_t, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 3, 0)))); +int _EXFUN(vsnprintf, (char *__restrict, size_t, const char *__restrict, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 3, 0)))); +int _EXFUN(vsscanf, (const char *__restrict, const char *__restrict, __VALIST) + _ATTRIBUTE ((__format__ (__scanf__, 2, 0)))); +#endif /* !_REENT_ONLY */ +#endif /* !__STRICT_ANSI__ */ + +/* + * Routines in POSIX 1003.1:2001. + */ + +#ifndef __STRICT_ANSI__ +#ifndef _REENT_ONLY +FILE * _EXFUN(fdopen, (int, const char *)); +#endif +int _EXFUN(fileno, (FILE *)); +int _EXFUN(getw, (FILE *)); +int _EXFUN(pclose, (FILE *)); +FILE * _EXFUN(popen, (const char *, const char *)); +int _EXFUN(putw, (int, FILE *)); +void _EXFUN(setbuffer, (FILE *, char *, int)); +int _EXFUN(setlinebuf, (FILE *)); +int _EXFUN(getc_unlocked, (FILE *)); +int _EXFUN(getchar_unlocked, (void)); +void _EXFUN(flockfile, (FILE *)); +int _EXFUN(ftrylockfile, (FILE *)); +void _EXFUN(funlockfile, (FILE *)); +int _EXFUN(putc_unlocked, (int, FILE *)); +int _EXFUN(putchar_unlocked, (int)); +#endif /* ! __STRICT_ANSI__ */ + +/* + * Routines in POSIX 1003.1:200x. + */ + +#ifndef __STRICT_ANSI__ +# ifndef _REENT_ONLY +# ifndef dprintf +int _EXFUN(dprintf, (int, const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__printf__, 2, 3)))); +# endif +FILE * _EXFUN(fmemopen, (void *__restrict, size_t, const char *__restrict)); +/* getdelim - see __getdelim for now */ +/* getline - see __getline for now */ +FILE * _EXFUN(open_memstream, (char **, size_t *)); +#if __BSD_VISIBLE || __POSIX_VISIBLE >= 200809 +int _EXFUN(renameat, (int, const char *, int, const char *)); +#endif +int _EXFUN(vdprintf, (int, const char *__restrict, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 2, 0)))); +# endif +#endif + +/* + * Recursive versions of the above. + */ + +int _EXFUN(_asiprintf_r, (struct _reent *, char **, const char *, ...) + _ATTRIBUTE ((__format__ (__printf__, 3, 4)))); +char * _EXFUN(_asniprintf_r, (struct _reent *, char *, size_t *, const char *, ...) + _ATTRIBUTE ((__format__ (__printf__, 4, 5)))); +char * _EXFUN(_asnprintf_r, (struct _reent *, char *__restrict, size_t *__restrict, const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__printf__, 4, 5)))); +int _EXFUN(_asprintf_r, (struct _reent *, char **__restrict, const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__printf__, 3, 4)))); +int _EXFUN(_diprintf_r, (struct _reent *, int, const char *, ...) + _ATTRIBUTE ((__format__ (__printf__, 3, 4)))); +int _EXFUN(_dprintf_r, (struct _reent *, int, const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__printf__, 3, 4)))); +int _EXFUN(_fclose_r, (struct _reent *, FILE *)); +int _EXFUN(_fcloseall_r, (struct _reent *)); +FILE * _EXFUN(_fdopen_r, (struct _reent *, int, const char *)); +int _EXFUN(_fflush_r, (struct _reent *, FILE *)); +int _EXFUN(_fgetc_r, (struct _reent *, FILE *)); +int _EXFUN(_fgetc_unlocked_r, (struct _reent *, FILE *)); +char * _EXFUN(_fgets_r, (struct _reent *, char *__restrict, int, FILE *__restrict)); +char * _EXFUN(_fgets_unlocked_r, (struct _reent *, char *__restrict, int, FILE *__restrict)); +#ifdef _COMPILING_NEWLIB +int _EXFUN(_fgetpos_r, (struct _reent *, FILE *__restrict, _fpos_t *__restrict)); +int _EXFUN(_fsetpos_r, (struct _reent *, FILE *, const _fpos_t *)); +#else +int _EXFUN(_fgetpos_r, (struct _reent *, FILE *, fpos_t *)); +int _EXFUN(_fsetpos_r, (struct _reent *, FILE *, const fpos_t *)); +#endif +int _EXFUN(_fiprintf_r, (struct _reent *, FILE *, const char *, ...) + _ATTRIBUTE ((__format__ (__printf__, 3, 4)))); +int _EXFUN(_fiscanf_r, (struct _reent *, FILE *, const char *, ...) + _ATTRIBUTE ((__format__ (__scanf__, 3, 4)))); +FILE * _EXFUN(_fmemopen_r, (struct _reent *, void *__restrict, size_t, const char *__restrict)); +FILE * _EXFUN(_fopen_r, (struct _reent *, const char *__restrict, const char *__restrict)); +FILE * _EXFUN(_freopen_r, (struct _reent *, const char *__restrict, const char *__restrict, FILE *__restrict)); +int _EXFUN(_fprintf_r, (struct _reent *, FILE *__restrict, const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__printf__, 3, 4)))); +int _EXFUN(_fpurge_r, (struct _reent *, FILE *)); +int _EXFUN(_fputc_r, (struct _reent *, int, FILE *)); +int _EXFUN(_fputc_unlocked_r, (struct _reent *, int, FILE *)); +int _EXFUN(_fputs_r, (struct _reent *, const char *__restrict, FILE *__restrict)); +int _EXFUN(_fputs_unlocked_r, (struct _reent *, const char *__restrict, FILE *__restrict)); +size_t _EXFUN(_fread_r, (struct _reent *, _PTR __restrict, size_t _size, size_t _n, FILE *__restrict)); +size_t _EXFUN(_fread_unlocked_r, (struct _reent *, _PTR __restrict, size_t _size, size_t _n, FILE *__restrict)); +int _EXFUN(_fscanf_r, (struct _reent *, FILE *__restrict, const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__scanf__, 3, 4)))); +int _EXFUN(_fseek_r, (struct _reent *, FILE *, long, int)); +int _EXFUN(_fseeko_r,(struct _reent *, FILE *, _off_t, int)); +long _EXFUN(_ftell_r, (struct _reent *, FILE *)); +_off_t _EXFUN(_ftello_r,(struct _reent *, FILE *)); +void _EXFUN(_rewind_r, (struct _reent *, FILE *)); +size_t _EXFUN(_fwrite_r, (struct _reent *, const _PTR __restrict, size_t _size, size_t _n, FILE *__restrict)); +size_t _EXFUN(_fwrite_unlocked_r, (struct _reent *, const _PTR __restrict, size_t _size, size_t _n, FILE *__restrict)); +int _EXFUN(_getc_r, (struct _reent *, FILE *)); +int _EXFUN(_getc_unlocked_r, (struct _reent *, FILE *)); +int _EXFUN(_getchar_r, (struct _reent *)); +int _EXFUN(_getchar_unlocked_r, (struct _reent *)); +char * _EXFUN(_gets_r, (struct _reent *, char *)); +int _EXFUN(_iprintf_r, (struct _reent *, const char *, ...) + _ATTRIBUTE ((__format__ (__printf__, 2, 3)))); +int _EXFUN(_iscanf_r, (struct _reent *, const char *, ...) + _ATTRIBUTE ((__format__ (__scanf__, 2, 3)))); +FILE * _EXFUN(_open_memstream_r, (struct _reent *, char **, size_t *)); +void _EXFUN(_perror_r, (struct _reent *, const char *)); +int _EXFUN(_printf_r, (struct _reent *, const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__printf__, 2, 3)))); +int _EXFUN(_putc_r, (struct _reent *, int, FILE *)); +int _EXFUN(_putc_unlocked_r, (struct _reent *, int, FILE *)); +int _EXFUN(_putchar_unlocked_r, (struct _reent *, int)); +int _EXFUN(_putchar_r, (struct _reent *, int)); +int _EXFUN(_puts_r, (struct _reent *, const char *)); +int _EXFUN(_remove_r, (struct _reent *, const char *)); +int _EXFUN(_rename_r, (struct _reent *, + const char *_old, const char *_new)); +int _EXFUN(_scanf_r, (struct _reent *, const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__scanf__, 2, 3)))); +int _EXFUN(_siprintf_r, (struct _reent *, char *, const char *, ...) + _ATTRIBUTE ((__format__ (__printf__, 3, 4)))); +int _EXFUN(_siscanf_r, (struct _reent *, const char *, const char *, ...) + _ATTRIBUTE ((__format__ (__scanf__, 3, 4)))); +int _EXFUN(_sniprintf_r, (struct _reent *, char *, size_t, const char *, ...) + _ATTRIBUTE ((__format__ (__printf__, 4, 5)))); +int _EXFUN(_snprintf_r, (struct _reent *, char *__restrict, size_t, const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__printf__, 4, 5)))); +int _EXFUN(_sprintf_r, (struct _reent *, char *__restrict, const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__printf__, 3, 4)))); +int _EXFUN(_sscanf_r, (struct _reent *, const char *__restrict, const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__scanf__, 3, 4)))); +char * _EXFUN(_tempnam_r, (struct _reent *, const char *, const char *)); +FILE * _EXFUN(_tmpfile_r, (struct _reent *)); +char * _EXFUN(_tmpnam_r, (struct _reent *, char *)); +int _EXFUN(_ungetc_r, (struct _reent *, int, FILE *)); +int _EXFUN(_vasiprintf_r, (struct _reent *, char **, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 3, 0)))); +char * _EXFUN(_vasniprintf_r, (struct _reent*, char *, size_t *, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 4, 0)))); +char * _EXFUN(_vasnprintf_r, (struct _reent*, char *, size_t *, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 4, 0)))); +int _EXFUN(_vasprintf_r, (struct _reent *, char **, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 3, 0)))); +int _EXFUN(_vdiprintf_r, (struct _reent *, int, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 3, 0)))); +int _EXFUN(_vdprintf_r, (struct _reent *, int, const char *__restrict, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 3, 0)))); +int _EXFUN(_vfiprintf_r, (struct _reent *, FILE *, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 3, 0)))); +int _EXFUN(_vfiscanf_r, (struct _reent *, FILE *, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__scanf__, 3, 0)))); +int _EXFUN(_vfprintf_r, (struct _reent *, FILE *__restrict, const char *__restrict, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 3, 0)))); +int _EXFUN(_vfscanf_r, (struct _reent *, FILE *__restrict, const char *__restrict, __VALIST) + _ATTRIBUTE ((__format__ (__scanf__, 3, 0)))); +int _EXFUN(_viprintf_r, (struct _reent *, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 2, 0)))); +int _EXFUN(_viscanf_r, (struct _reent *, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__scanf__, 2, 0)))); +int _EXFUN(_vprintf_r, (struct _reent *, const char *__restrict, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 2, 0)))); +int _EXFUN(_vscanf_r, (struct _reent *, const char *__restrict, __VALIST) + _ATTRIBUTE ((__format__ (__scanf__, 2, 0)))); +int _EXFUN(_vsiprintf_r, (struct _reent *, char *, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 3, 0)))); +int _EXFUN(_vsiscanf_r, (struct _reent *, const char *, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__scanf__, 3, 0)))); +int _EXFUN(_vsniprintf_r, (struct _reent *, char *, size_t, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 4, 0)))); +int _EXFUN(_vsnprintf_r, (struct _reent *, char *__restrict, size_t, const char *__restrict, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 4, 0)))); +int _EXFUN(_vsprintf_r, (struct _reent *, char *__restrict, const char *__restrict, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 3, 0)))); +int _EXFUN(_vsscanf_r, (struct _reent *, const char *__restrict, const char *__restrict, __VALIST) + _ATTRIBUTE ((__format__ (__scanf__, 3, 0)))); + +/* Other extensions. */ + +int _EXFUN(fpurge, (FILE *)); +ssize_t _EXFUN(__getdelim, (char **, size_t *, int, FILE *)); +ssize_t _EXFUN(__getline, (char **, size_t *, FILE *)); + +#if __BSD_VISIBLE +void _EXFUN(clearerr_unlocked, (FILE *)); +int _EXFUN(feof_unlocked, (FILE *)); +int _EXFUN(ferror_unlocked, (FILE *)); +int _EXFUN(fileno_unlocked, (FILE *)); +int _EXFUN(fflush_unlocked, (FILE *)); +int _EXFUN(fgetc_unlocked, (FILE *)); +int _EXFUN(fputc_unlocked, (int, FILE *)); +size_t _EXFUN(fread_unlocked, (_PTR __restrict, size_t _size, size_t _n, FILE *__restrict)); +size_t _EXFUN(fwrite_unlocked, (const _PTR __restrict , size_t _size, size_t _n, FILE *)); +#endif + +#if __GNU_VISIBLE +char * _EXFUN(fgets_unlocked, (char *__restrict, int, FILE *__restrict)); +int _EXFUN(fputs_unlocked, (const char *__restrict, FILE *__restrict)); +#endif + +#ifdef __LARGE64_FILES +#if !defined(__CYGWIN__) || defined(_COMPILING_NEWLIB) +FILE * _EXFUN(fdopen64, (int, const char *)); +FILE * _EXFUN(fopen64, (const char *, const char *)); +FILE * _EXFUN(freopen64, (_CONST char *, _CONST char *, FILE *)); +_off64_t _EXFUN(ftello64, (FILE *)); +_off64_t _EXFUN(fseeko64, (FILE *, _off64_t, int)); +int _EXFUN(fgetpos64, (FILE *, _fpos64_t *)); +int _EXFUN(fsetpos64, (FILE *, const _fpos64_t *)); +FILE * _EXFUN(tmpfile64, (void)); + +FILE * _EXFUN(_fdopen64_r, (struct _reent *, int, const char *)); +FILE * _EXFUN(_fopen64_r, (struct _reent *,const char *, const char *)); +FILE * _EXFUN(_freopen64_r, (struct _reent *, _CONST char *, _CONST char *, FILE *)); +_off64_t _EXFUN(_ftello64_r, (struct _reent *, FILE *)); +_off64_t _EXFUN(_fseeko64_r, (struct _reent *, FILE *, _off64_t, int)); +int _EXFUN(_fgetpos64_r, (struct _reent *, FILE *, _fpos64_t *)); +int _EXFUN(_fsetpos64_r, (struct _reent *, FILE *, const _fpos64_t *)); +FILE * _EXFUN(_tmpfile64_r, (struct _reent *)); +#endif /* !__CYGWIN__ */ +#endif /* __LARGE64_FILES */ + +/* + * Routines internal to the implementation. + */ + +int _EXFUN(__srget_r, (struct _reent *, FILE *)); +int _EXFUN(__swbuf_r, (struct _reent *, int, FILE *)); + +/* + * Stdio function-access interface. + */ + +#ifndef __STRICT_ANSI__ +# ifdef __LARGE64_FILES +FILE *_EXFUN(funopen,(const _PTR __cookie, + int (*__readfn)(_PTR __c, char *__buf, + _READ_WRITE_BUFSIZE_TYPE __n), + int (*__writefn)(_PTR __c, const char *__buf, + _READ_WRITE_BUFSIZE_TYPE __n), + _fpos64_t (*__seekfn)(_PTR __c, _fpos64_t __off, int __whence), + int (*__closefn)(_PTR __c))); +FILE *_EXFUN(_funopen_r,(struct _reent *, const _PTR __cookie, + int (*__readfn)(_PTR __c, char *__buf, + _READ_WRITE_BUFSIZE_TYPE __n), + int (*__writefn)(_PTR __c, const char *__buf, + _READ_WRITE_BUFSIZE_TYPE __n), + _fpos64_t (*__seekfn)(_PTR __c, _fpos64_t __off, int __whence), + int (*__closefn)(_PTR __c))); +# else +FILE *_EXFUN(funopen,(const _PTR __cookie, + int (*__readfn)(_PTR __cookie, char *__buf, + _READ_WRITE_BUFSIZE_TYPE __n), + int (*__writefn)(_PTR __cookie, const char *__buf, + _READ_WRITE_BUFSIZE_TYPE __n), + fpos_t (*__seekfn)(_PTR __cookie, fpos_t __off, int __whence), + int (*__closefn)(_PTR __cookie))); +FILE *_EXFUN(_funopen_r,(struct _reent *, const _PTR __cookie, + int (*__readfn)(_PTR __cookie, char *__buf, + _READ_WRITE_BUFSIZE_TYPE __n), + int (*__writefn)(_PTR __cookie, const char *__buf, + _READ_WRITE_BUFSIZE_TYPE __n), + fpos_t (*__seekfn)(_PTR __cookie, fpos_t __off, int __whence), + int (*__closefn)(_PTR __cookie))); +# endif /* !__LARGE64_FILES */ + +# define fropen(__cookie, __fn) funopen(__cookie, __fn, (int (*)())0, \ + (fpos_t (*)())0, (int (*)())0) +# define fwopen(__cookie, __fn) funopen(__cookie, (int (*)())0, __fn, \ + (fpos_t (*)())0, (int (*)())0) + +typedef ssize_t cookie_read_function_t(void *__cookie, char *__buf, size_t __n); +typedef ssize_t cookie_write_function_t(void *__cookie, const char *__buf, + size_t __n); +# ifdef __LARGE64_FILES +typedef int cookie_seek_function_t(void *__cookie, _off64_t *__off, + int __whence); +# else +typedef int cookie_seek_function_t(void *__cookie, off_t *__off, int __whence); +# endif /* !__LARGE64_FILES */ +typedef int cookie_close_function_t(void *__cookie); +typedef struct +{ + /* These four struct member names are dictated by Linux; hopefully, + they don't conflict with any macros. */ + cookie_read_function_t *read; + cookie_write_function_t *write; + cookie_seek_function_t *seek; + cookie_close_function_t *close; +} cookie_io_functions_t; +FILE *_EXFUN(fopencookie,(void *__cookie, + const char *__mode, cookie_io_functions_t __functions)); +FILE *_EXFUN(_fopencookie_r,(struct _reent *, void *__cookie, + const char *__mode, cookie_io_functions_t __functions)); +#endif /* ! __STRICT_ANSI__ */ + +#ifndef __CUSTOM_FILE_IO__ +/* + * The __sfoo macros are here so that we can + * define function versions in the C library. + */ +#define __sgetc_raw_r(__ptr, __f) (--(__f)->_r < 0 ? __srget_r(__ptr, __f) : (int)(*(__f)->_p++)) + +#ifdef __SCLE +/* For a platform with CR/LF, additional logic is required by + __sgetc_r which would otherwise simply be a macro; therefore we + use an inlined function. The function is only meant to be inlined + in place as used and the function body should never be emitted. + + There are two possible means to this end when compiling with GCC, + one when compiling with a standard C99 compiler, and for other + compilers we're just stuck. At the moment, this issue only + affects the Cygwin target, so we'll most likely be using GCC. */ + +_ELIDABLE_INLINE int __sgetc_r(struct _reent *__ptr, FILE *__p); + +_ELIDABLE_INLINE int __sgetc_r(struct _reent *__ptr, FILE *__p) + { + int __c = __sgetc_raw_r(__ptr, __p); + if ((__p->_flags & __SCLE) && (__c == '\r')) + { + int __c2 = __sgetc_raw_r(__ptr, __p); + if (__c2 == '\n') + __c = __c2; + else + ungetc(__c2, __p); + } + return __c; + } +#else +#define __sgetc_r(__ptr, __p) __sgetc_raw_r(__ptr, __p) +#endif + +#ifdef _never /* __GNUC__ */ +/* If this inline is actually used, then systems using coff debugging + info get hopelessly confused. 21sept93 rich@cygnus.com. */ +_ELIDABLE_INLINE int __sputc_r(struct _reent *_ptr, int _c, FILE *_p) { + if (--_p->_w >= 0 || (_p->_w >= _p->_lbfsize && (char)_c != '\n')) + return (*_p->_p++ = _c); + else + return (__swbuf_r(_ptr, _c, _p)); +} +#else +/* + * This has been tuned to generate reasonable code on the vax using pcc + */ +#define __sputc_raw_r(__ptr, __c, __p) \ + (--(__p)->_w < 0 ? \ + (__p)->_w >= (__p)->_lbfsize ? \ + (*(__p)->_p = (__c)), *(__p)->_p != '\n' ? \ + (int)*(__p)->_p++ : \ + __swbuf_r(__ptr, '\n', __p) : \ + __swbuf_r(__ptr, (int)(__c), __p) : \ + (*(__p)->_p = (__c), (int)*(__p)->_p++)) +#ifdef __SCLE +#define __sputc_r(__ptr, __c, __p) \ + ((((__p)->_flags & __SCLE) && ((__c) == '\n')) \ + ? __sputc_raw_r(__ptr, '\r', (__p)) : 0 , \ + __sputc_raw_r((__ptr), (__c), (__p))) +#else +#define __sputc_r(__ptr, __c, __p) __sputc_raw_r(__ptr, __c, __p) +#endif +#endif + +#define __sfeof(p) ((int)(((p)->_flags & __SEOF) != 0)) +#define __sferror(p) ((int)(((p)->_flags & __SERR) != 0)) +#define __sclearerr(p) ((void)((p)->_flags &= ~(__SERR|__SEOF))) +#define __sfileno(p) ((p)->_file) + +#ifndef _REENT_SMALL +#define feof(p) __sfeof(p) +#define ferror(p) __sferror(p) +#define clearerr(p) __sclearerr(p) + +#if __BSD_VISIBLE +#define feof_unlocked(p) __sfeof(p) +#define ferror_unlocked(p) __sferror(p) +#define clearerr_unlocked(p) __sclearerr(p) +#endif /* __BSD_VISIBLE */ +#endif /* _REENT_SMALL */ + +#if 0 /*ndef __STRICT_ANSI__ - FIXME: must initialize stdio first, use fn */ +#define fileno(p) __sfileno(p) +#endif + +#ifndef __CYGWIN__ +#ifndef lint +#define getc(fp) __sgetc_r(_REENT, fp) +#define putc(x, fp) __sputc_r(_REENT, x, fp) +#endif /* lint */ +#endif /* __CYGWIN__ */ + +#ifndef __STRICT_ANSI__ +/* fast always-buffered version, true iff error */ +#define fast_putc(x,p) (--(p)->_w < 0 ? \ + __swbuf_r(_REENT, (int)(x), p) == EOF : (*(p)->_p = (x), (p)->_p++, 0)) + +#define L_cuserid 9 /* posix says it goes in stdio.h :( */ +#ifdef __CYGWIN__ +#define L_ctermid 16 +#endif +#endif + +#endif /* !__CUSTOM_FILE_IO__ */ + +#define getchar() getc(stdin) +#define putchar(x) putc(x, stdout) + +#ifndef __STRICT_ANSI__ +#define getchar_unlocked() getc_unlocked(stdin) +#define putchar_unlocked(x) putc_unlocked(x, stdout) +#endif + +_END_STD_C + +#undef putchar +#undef getchar + +#ifdef __cplusplus +} +#endif + +#endif /* DOXYGEN */ +#endif /* STDIO_H */ diff --git a/cpu/esp8266/include/sys/types.h b/cpu/esp8266/include/sys/types.h new file mode 100644 index 0000000000..0c24654342 --- /dev/null +++ b/cpu/esp8266/include/sys/types.h @@ -0,0 +1,337 @@ +/* + * Copyright (C) 2019 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 This file is a modification of original sys/types.h + * + * @author Gunar Schorcht + * + * This file is just a wrapper around sys/types.h to fix missing types + * fsblkcnt_t and fsfilcnt_t needed in statvfs.h and to avoid type conflicts + * with pthread types from pthread module. + */ + + +/* unified sys/types.h: + start with sef's sysvi386 version. + merge go32 version -- a few ifdefs. + h8300hms, h8300xray, and sysvnecv70 disagree on the following types: + + typedef int gid_t; + typedef int uid_t; + typedef int dev_t; + typedef int ino_t; + typedef int mode_t; + typedef int caddr_t; + + however, these aren't "reasonable" values, the sysvi386 ones make far + more sense, and should work sufficiently well (in particular, h8300 + doesn't have a stat, and the necv70 doesn't matter.) -- eichin + */ + +#ifndef SYS_TYPES_H +#define SYS_TYPES_H + +#ifndef DOXYGEN + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _FSBLKCNT_T_DECLARED +#include +typedef uint32_t fsblkcnt_t; +typedef uint32_t fsfilcnt_t; +#define _FSBLKCNT_T_DECLARED +#endif + +#ifndef _SYS_TYPES_H + +#include <_ansi.h> + +#ifndef __INTTYPES_DEFINED__ +#define __INTTYPES_DEFINED__ + +#include + +#if defined(__rtems__) || defined(__XMK__) +/* + * The following section is RTEMS specific and is needed to more + * closely match the types defined in the BSD sys/types.h. + * This is needed to let the RTEMS/BSD TCP/IP stack compile. + */ + +/* deprecated */ +#if ___int8_t_defined +typedef __uint8_t u_int8_t; +#endif +#if ___int16_t_defined +typedef __uint16_t u_int16_t; +#endif +#if ___int32_t_defined +typedef __uint32_t u_int32_t; +#endif + +#if ___int64_t_defined +typedef __uint64_t u_int64_t; + +/* deprecated */ +typedef __uint64_t u_quad_t; +typedef __int64_t quad_t; +typedef quad_t * qaddr_t; +#endif + +#endif + +#endif /* ! __INTTYPES_DEFINED */ + +#ifndef __need_inttypes + +#define _SYS_TYPES_H +#include + +#ifdef __i386__ +#if defined (GO32) || defined (__MSDOS__) +#define __MS_types__ +#endif +#endif + +# include +# include + +/* To ensure the stat struct's layout doesn't change when sizeof(int), etc. + changes, we assume sizeof short and long never change and have all types + used to define struct stat use them and not int where possible. + Where not possible, _ST_INTxx are used. It would be preferable to not have + such assumptions, but until the extra fluff is necessary, it's avoided. + No 64 bit targets use stat yet. What to do about them is postponed + until necessary. */ +#ifdef __GNUC__ +#define _ST_INT32 __attribute__ ((__mode__ (__SI__))) +#else +#define _ST_INT32 +#endif + +# ifndef _POSIX_SOURCE + +# define physadr physadr_t +# define quad quad_t + +#ifndef _BSDTYPES_DEFINED +/* also defined in mingw/gmon.h and in w32api/winsock[2].h */ +#ifndef __u_char_defined +typedef unsigned char u_char; +#define __u_char_defined +#endif +#ifndef __u_short_defined +typedef unsigned short u_short; +#define __u_short_defined +#endif +#ifndef __u_int_defined +typedef unsigned int u_int; +#define __u_int_defined +#endif +#ifndef __u_long_defined +typedef unsigned long u_long; +#define __u_long_defined +#endif +#define _BSDTYPES_DEFINED +#endif + +typedef unsigned short ushort; /* System V compatibility */ +typedef unsigned int uint; /* System V compatibility */ +typedef unsigned long ulong; /* System V compatibility */ +# endif /*!_POSIX_SOURCE */ + +#ifndef __clock_t_defined +typedef _CLOCK_T_ clock_t; +#define __clock_t_defined +#endif + +#ifndef __time_t_defined +typedef _TIME_T_ time_t; +#define __time_t_defined +#endif + +#ifndef __timespec_defined +#define __timespec_defined +/* Time Value Specification Structures, P1003.1b-1993, p. 261 */ + +struct timespec { + time_t tv_sec; /* Seconds */ + long tv_nsec; /* Nanoseconds */ +}; +#endif + +struct itimerspec { + struct timespec it_interval; /* Timer period */ + struct timespec it_value; /* Timer expiration */ +}; + +#ifndef __daddr_t_defined +typedef long daddr_t; +#define __daddr_t_defined +#endif +#ifndef __caddr_t_defined +typedef char * caddr_t; +#define __caddr_t_defined +#endif + +#ifndef __CYGWIN__ +#if defined(__MS_types__) || defined(__rtems__) || \ + defined(__sparc__) || defined(__SPU__) +typedef unsigned long ino_t; +#else +typedef unsigned short ino_t; +#endif +#endif /*__CYGWIN__*/ + +#ifdef __MS_types__ +typedef unsigned long vm_offset_t; +typedef unsigned long vm_size_t; + +#define __BIT_TYPES_DEFINED__ + +typedef signed char int8_t; +typedef unsigned char u_int8_t; +typedef short int16_t; +typedef unsigned short u_int16_t; +typedef int int32_t; +typedef unsigned int u_int32_t; +typedef long long int64_t; +typedef unsigned long long u_int64_t; +typedef int32_t register_t; +#endif /* __MS_types__ */ + +/* + * All these should be machine specific - right now they are all broken. + * However, for all of Cygnus' embedded targets, we want them to all be + * the same. Otherwise things like sizeof (struct stat) might depend on + * how the file was compiled (e.g. -mint16 vs -mint32, etc.). + */ + +#ifndef __CYGWIN__ /* which defines these types in it's own types.h. */ +typedef _off_t off_t; +typedef __dev_t dev_t; +typedef __uid_t uid_t; +typedef __gid_t gid_t; +#endif + +#if defined(__XMK__) +typedef signed char pid_t; +#else +typedef int pid_t; +#endif + +#if defined(__rtems__) +typedef _mode_t mode_t; +#endif + +#ifndef __CYGWIN__ +typedef long key_t; +#endif +typedef _ssize_t ssize_t; + +#if !defined(__CYGWIN__) && !defined(__rtems__) +#ifdef __MS_types__ +typedef char * addr_t; +typedef int mode_t; +#else +#if defined (__sparc__) && !defined (__sparc_v9__) +#ifdef __svr4__ +typedef unsigned long mode_t; +#else +typedef unsigned short mode_t; +#endif +#else +typedef unsigned int mode_t _ST_INT32; +#endif +#endif /* ! __MS_types__ */ +#endif /*__CYGWIN__*/ + +typedef unsigned short nlink_t; + +/* We don't define fd_set and friends if we are compiling POSIX + source, or if we have included (or may include as indicated + by __USE_W32_SOCKETS) the W32api winsock[2].h header which + defines Windows versions of them. Note that a program which + includes the W32api winsock[2].h header must know what it is doing; + it must not call the cygwin32 select function. +*/ +# if !(defined (_POSIX_SOURCE) || defined (_WINSOCK_H) || defined (_WINSOCKAPI_) || defined (__USE_W32_SOCKETS)) +# define _SYS_TYPES_FD_SET +# define NBBY 8 /* number of bits in a byte */ +/* + * Select uses bit masks of file descriptors in longs. + * These macros manipulate such bit fields (the filesystem macros use chars). + * FD_SETSIZE may be defined by the user, but the default here + * should be >= NOFILE (param.h). + */ +# ifndef FD_SETSIZE +# define FD_SETSIZE 64 +# endif + +typedef long fd_mask; +# define NFDBITS (sizeof (fd_mask) * NBBY) /* bits per mask */ +# ifndef howmany +# define howmany(x,y) (((x)+((y)-1))/(y)) +# endif + +/* We use a macro for fd_set so that including Sockets.h afterwards + can work. */ +typedef struct _types_fd_set { + fd_mask fds_bits[howmany(FD_SETSIZE, NFDBITS)]; +} _types_fd_set; + +#define fd_set _types_fd_set + +# define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1L << ((n) % NFDBITS))) +# define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1L << ((n) % NFDBITS))) +# define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1L << ((n) % NFDBITS))) +# define FD_ZERO(p) (__extension__ (void)({ \ + size_t __i; \ + char *__tmp = (char *)p; \ + for (__i = 0; __i < sizeof (*(p)); ++__i) \ + *__tmp++ = 0; \ +})) + +# endif /* !(defined (_POSIX_SOURCE) || defined (_WINSOCK_H) || defined (_WINSOCKAPI_) || defined (__USE_W32_SOCKETS)) */ + +#undef __MS_types__ +#undef _ST_INT32 + + +#ifndef __clockid_t_defined +typedef _CLOCKID_T_ clockid_t; +#define __clockid_t_defined +#endif + +#ifndef __timer_t_defined +typedef _TIMER_T_ timer_t; +#define __timer_t_defined +#endif + +typedef unsigned long useconds_t; +typedef long suseconds_t; + +#endif /* !__need_inttypes */ + +#undef __need_inttypes + +#endif /* _SYS_TYPES_H */ + +#ifdef __cplusplus +} +#endif + +#endif /* DOXYGEN */ +#endif /* SYS_TYPES_H */ diff --git a/cpu/esp8266/include/syscalls.h b/cpu/esp8266/include/syscalls.h index cf5249f58a..21185ec36e 100644 --- a/cpu/esp8266/include/syscalls.h +++ b/cpu/esp8266/include/syscalls.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Gunar Schorcht + * Copyright (C) 2019 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 @@ -23,6 +23,7 @@ #include #include +#include #include #ifdef __cplusplus @@ -35,12 +36,24 @@ extern void syscalls_init (void); /** System standard printf function */ extern int printf(const char* format, ...); +/** Determine free heap size */ +unsigned int get_free_heap_size (void); + /** System standard puts function */ extern int puts(const char * str); /** Determine free heap size */ extern unsigned int get_free_heap_size (void); +/** Time since boot in us (32bit version) */ +uint32_t system_get_time (void); + +/** Time since boot in ms (32bit version) */ +uint32_t system_get_time_ms (void); + +/** memset version that the compiler should not be allowed to optimize this */ +void *system_secure_memset(void *s, int c, size_t n); + #ifdef __cplusplus } #endif diff --git a/cpu/esp8266/include/thread_arch.h b/cpu/esp8266/include/thread_arch.h index b525a6d94c..b4b585c67d 100644 --- a/cpu/esp8266/include/thread_arch.h +++ b/cpu/esp8266/include/thread_arch.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Gunar Schorcht + * Copyright (C) 2019 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 diff --git a/cpu/esp8266/include/tools.h b/cpu/esp8266/include/tools.h index f2de39969a..f6dc065b0e 100644 --- a/cpu/esp8266/include/tools.h +++ b/cpu/esp8266/include/tools.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Gunar Schorcht + * Copyright (C) 2019 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 diff --git a/cpu/esp8266/include/xtensa_conf.h b/cpu/esp8266/include/xtensa_conf.h index f6a9c7a792..594d810bf3 100644 --- a/cpu/esp8266/include/xtensa_conf.h +++ b/cpu/esp8266/include/xtensa_conf.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Gunar Schorcht + * Copyright (C) 2019 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 diff --git a/cpu/esp8266/irq_arch.c b/cpu/esp8266/irq_arch.c index a7563b5d54..c5f902cd99 100644 --- a/cpu/esp8266/irq_arch.c +++ b/cpu/esp8266/irq_arch.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Gunar Schorcht + * Copyright (C) 2019 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 @@ -18,7 +18,7 @@ * @} */ -#define ENABLE_DEBUG 0 +#define ENABLE_DEBUG (0) #include "debug.h" #include @@ -27,10 +27,9 @@ #include "irq.h" #include "cpu.h" -#include "common.h" +#include "esp_common.h" #include "esp/common_macros.h" #include "esp/xtensa_ops.h" -#include "sdk/ets.h" #include "xtensa/xtensa_context.h" /** @@ -46,9 +45,9 @@ 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)); + __asm__ volatile ("rsil %0, " XTSTR(XCHAL_EXCM_LEVEL) : "=a" (_saved_interrupt_level)); DEBUG ("%s %02x(%02x)\n", __func__, - (_saved_interrupt_level & 0xfffffff0) | (XCHAL_NUM_INTLEVELS+1), + (_saved_interrupt_level & 0xfffffff0) | (XCHAL_EXCM_LEVEL), _saved_interrupt_level); return _saved_interrupt_level; } @@ -62,7 +61,7 @@ unsigned int IRAM irq_enable(void) /* read and set interrupt level (RSIL) */ __asm__ volatile ("rsil %0, 0" : "=a" (_saved_interrupt_level)); - DEBUG ("%s %02x(%02x)\n", __func__, + DEBUG ("%s %02x (%02x)\n", __func__, _saved_interrupt_level & 0xfffffff0, _saved_interrupt_level); return _saved_interrupt_level; } diff --git a/cpu/esp8266/ld/esp8266.peripherals.ld b/cpu/esp8266/ld/esp8266.peripherals.ld new file mode 100644 index 0000000000..99d1697a29 --- /dev/null +++ b/cpu/esp8266/ld/esp8266.peripherals.ld @@ -0,0 +1,14 @@ +PROVIDE ( GPIO = 0x60000300); + +PROVIDE ( uart0 = 0x60000000 ); +PROVIDE ( uart1 = 0x60000f00 ); + +PROVIDE ( frc1 = 0x60000600 ); + +PROVIDE ( rtc_sys_info = 0x60001100 ); + +PROVIDE ( SLC = 0x60000B00 ); +PROVIDE ( I2S = 0x60000e00 ); + +PROVIDE ( SPI1 = 0x60000100 ); +PROVIDE ( SPI0 = 0x60000200 ); diff --git a/cpu/esp8266/ld/esp8266.riot-os.no_sdk.app.ld b/cpu/esp8266/ld/esp8266.riot-os.ld similarity index 65% rename from cpu/esp8266/ld/esp8266.riot-os.no_sdk.app.ld rename to cpu/esp8266/ld/esp8266.riot-os.ld index fd555cae01..4f00adcf4c 100644 --- a/cpu/esp8266/ld/esp8266.riot-os.no_sdk.app.ld +++ b/cpu/esp8266/ld/esp8266.riot-os.ld @@ -1,17 +1,15 @@ /** - * 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 */ + * This linker script is a combined and modified version of esp8266.ld and + * esp8266.common.ld from ESP8266-RTOS-SDK. + */ 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 + dram0_0_seg : org = 0x3FFE8000, len = 0x18000 + iram1_0_seg : org = 0x40100000, len = 0xC000 + irom0_0_seg : org = 0x40200010 + 0x10000, len = 0x80000 - 0x10 - 0x10000 + rtc_seg : org = 0x60001200, len = 0x200 } PHDRS @@ -25,7 +23,7 @@ PHDRS /* Default entry point: */ -ENTRY(_call_user_start) +ENTRY(call_user_start) EXTERN(_DebugExceptionVector) EXTERN(_DoubleExceptionVector) EXTERN(_KernelExceptionVector) @@ -50,17 +48,6 @@ _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 { @@ -88,6 +75,15 @@ SECTIONS _dport0_data_end = ABSOLUTE(.); } >dport0_0_seg :dport0_0_phdr + /* RTC memory holds user's data/rodata */ + .rtc.data : + { + _rtc_data_start = ABSOLUTE(.); + *(.rtc.data) + *(.rtc.rodata) + _rtc_data_end = ABSOLUTE(.); + } > rtc_seg + .data : ALIGN(4) { _data_start = ABSOLUTE(.); @@ -108,26 +104,30 @@ SECTIONS .rodata : ALIGN(4) { _rodata_start = ABSOLUTE(.); - *(.sdk.version) - /* TODO put only necessary .rodata to dram + /* TODO put only necessary .rodata to dram */ + /* *(.rodata .rodata.*) */ *libc.a:*.o(.rodata.* .rodata) *core.a:*(.rodata.* .rodata) *cpu.a:*(.rodata .rodata.*) - */ - *(.rodata .rodata.*) + *libpp.a:(.rodata.* .rodata) + *liblog.a:(.rodata.* .rodata) + *(.gnu.linkonce.r.*) *(.rodata1) __XT_EXCEPTION_TABLE__ = ABSOLUTE(.); *(.xt_except_table) - *(.gcc_except_table) + *(.gcc_except_table.*) *(.gnu.linkonce.e.*) *(.gnu.version_r) *(.eh_frame) + . = (. + 3) & ~ 3; /* C++ constructor and destructor tables, properly ordered: */ + __init_array_start = ABSOLUTE(.); KEEP (*crtbegin.o(.ctors)) KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) KEEP (*(SORT(.ctors.*))) KEEP (*(.ctors)) + __init_array_end = ABSOLUTE(.); KEEP (*crtbegin.o(.dtors)) KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) KEEP (*(SORT(.dtors.*))) @@ -140,7 +140,7 @@ SECTIONS *(.xt_except_desc_end) *(.dynamic) *(.gnu.version_d) - . = ALIGN(4); /* this table MUST be 4-byte aligned */ + . = ALIGN(4); /* this table MUST be 4-byte aligned */ _bss_table_start = ABSOLUTE(.); LONG(_bss_start) LONG(_bss_end) @@ -165,7 +165,6 @@ SECTIONS *(.bss.*) *(.gnu.linkonce.b.*) *(COMMON) - . = ALIGN (8); _bss_end = ABSOLUTE(.); _sheap = ABSOLUTE(.); @@ -173,40 +172,39 @@ SECTIONS } >dram0_0_seg :dram0_0_bss_phdr - /* ETS system memory starts at 0x3FFFC000 which is the top of the heap for the app */ - . = 0x3FFFC000; + . = 0x3FFFFFF0; _heap_top = ABSOLUTE(.); _eheap = ABSOLUTE(.); - .text : ALIGN(4) + .text : ALIGN(4) /* IRAM */ { _stext = .; _text_start = ABSOLUTE(.); - *(.UserEnter.text) + LONG(_text_start) . = ALIGN(16); - *(.DebugExceptionVector.text) + *(.DebugExceptionVector.text) /* 0x40100010 */ . = ALIGN(16); - *(.NMIExceptionVector.text) + *(.NMIExceptionVector.text) /* 0x40100020 */ . = ALIGN(16); - *(.KernelExceptionVector.text) + *(.KernelExceptionVector.text) /* 0x40100030 */ LONG(0) LONG(0) LONG(0) LONG(0) . = ALIGN(16); - *(.UserExceptionVector.text) + *(.UserExceptionVector.text) /* 0x40100050 */ LONG(0) LONG(0) LONG(0) LONG(0) . = ALIGN(16); - *(.DoubleExceptionVector.text) + *(.DoubleExceptionVector.text) /* 0x40100070 */ LONG(0) LONG(0) LONG(0) LONG(0) . = ALIGN (16); - *(.UserExceptionTrampoline.text) + *(.UserExceptionTrampoline.text) /* 0x40100090 */ . = ALIGN (16); *(.entry.text) *(.init.literal) @@ -214,30 +212,48 @@ SECTIONS /* normal code should be in irom0 */ /* - *(.literal .text .literal.* .text.* .stub) - *(.gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + *(.literal .text) + *core.a:*(.literal .text .literal.* .text.*) */ - + *gdbstub.a:*(.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.*) + *(.iram1 .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) + /* SDK libraries that expect their .text or .data sections to link to iram */ + /* TODO *libcore.a:(.bss .data .bss.* .data.* COMMON) */ + *esp_idf_spi_flash.a:spi_flash_raw.o(.literal .text .literal.* .text.*) + *esp_idf_esp8266.a:ets_printf.o(.literal .text .literal.* .text.*) + /* + *cpu.a:*.o(.literal .text .literal.* .text.*) + */ + *core.a:sched.o(.literal .text .literal.* .text.*) + *esp_wifi.a:*(.literal .text .literal.* .text.*) + *freertos.a:*(.literal .text .literal.* .text.*) + *periph.a:*(.literal .text .literal.* .text.*) + *xtimer.a:*(.literal .text .literal.* .text.*) + + *libhal.a:clock.o(.literal .text .literal.* .text.*) + *libhal.a:int_asm--set_intclear.o(.literal .text .literal.* .text.*) + *libpp.a:esf_buf.o(.literal .text .literal.* .text.*) + *libpp.a:lmac.o(.literal .text .literal.* .text.*) + *libpp.a:pp.o(.literal .text .literal.* .text.*) + *libpp.a:rate_control.o(.literal .text .literal.* .text.*) + *libpp.a:trc.o(.literal .text .literal.* .text.*) + *libpp.a:wdev.o(.literal .text .literal.* .text.*) + *libphy.a:phy.o(.literal .text .literal.* .text.*) + *libphy.a:phy_chip_v6_cal.o(.literal .text .literal.* .text.*) + *libphy.a:phy_sleep.o(.literal .text .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 functions required for debugging have to be in IRAM */ + *libgcc.a:unwind-dw2.o(.literal .text .literal.* .text.*) + + /* libgcc integer functions also need to be in .text */ + /* some are called before flash is mapped and also for performance) */ *libgcc.a:*i3.o(.literal .text .literal.* .text.*) *libgcc.a:*mulsf3.o(.literal .text .literal.* .text.*) @@ -245,6 +261,7 @@ SECTIONS *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.*) @@ -260,6 +277,7 @@ SECTIONS *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.*) @@ -274,15 +292,18 @@ SECTIONS { _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.*) + /* RIOT-OS compiled code and RO data go into IROM by default */ + *(.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) + . = ALIGN(16); + __start_ksymatabesp_socket = .; + *(ksymatabesp_socket) + __stop_ksymatabesp_socket = .; + _irom0_text_end = ABSOLUTE(.); } >irom0_0_seg :irom0_0_phdr diff --git a/cpu/esp8266/ld/esp8266.rom.ld b/cpu/esp8266/ld/esp8266.rom.ld new file mode 100644 index 0000000000..8a3d29cca7 --- /dev/null +++ b/cpu/esp8266/ld/esp8266.rom.ld @@ -0,0 +1,60 @@ +SPI_sector_erase = 0x400040c0; +SPI_page_program = 0x40004174; +SPI_read_data = 0x400042ac; +SPI_read_status = 0x400043c8; +SPI_write_status = 0x40004400; +SPI_write_enable = 0x4000443c; +Wait_SPI_Idle = 0x4000448c; +Enable_QMode = 0x400044c0; +Disable_QMode = 0x40004508; + +Cache_Read_Enable = 0x40004678; +Cache_Read_Disable = 0x400047f0; + +lldesc_build_chain = 0x40004f40; +lldesc_num2link = 0x40005050; +lldesc_set_owner = 0x4000507c; + +__adddf3 = 0x4000c538; +__addsf3 = 0x4000c180; +__divdf3 = 0x4000cb94; +__divdi3 = 0x4000ce60; +__divsi3 = 0x4000dc88; +__extendsfdf2 = 0x4000cdfc; +__fixdfsi = 0x4000ccb8; +__fixunsdfsi = 0x4000cd00; +__fixunssfsi = 0x4000c4c4; +__floatsidf = 0x4000e2f0; +__floatsisf = 0x4000e2ac; +__floatunsidf = 0x4000e2e8; +__floatunsisf = 0x4000e2a4; +__muldf3 = 0x4000c8f0; +__muldi3 = 0x40000650; +__mulsf3 = 0x4000c3dc; +__subdf3 = 0x4000c688; +__subsf3 = 0x4000c268; +__truncdfsf2 = 0x4000cd5c; +__udivdi3 = 0x4000d310; +__udivsi3 = 0x4000e21c; +__umoddi3 = 0x4000d770; +__umodsi3 = 0x4000e268; +__umulsidi3 = 0x4000dcf0; + +bzero = 0x4000de84; +memcmp = 0x4000dea8; +memcpy = 0x4000df48; +memmove = 0x4000e04c; +memset = 0x4000e190; + +strcmp = 0x4000bdc8; +strcpy = 0x4000bec8; +strlen = 0x4000bf4c; +strncmp = 0x4000bfa8; +strncpy = 0x4000c0a0; +strstr = 0x4000e1e0; + +gpio_input_get = 0x40004cf0; +gpio_pin_wakeup_disable = 0x40004ed4; +gpio_pin_wakeup_enable = 0x40004e90; + +ets_io_vprintf = 0x40001f00; diff --git a/cpu/esp8266/periph/Makefile b/cpu/esp8266/periph/Makefile index 6d1887b640..d6a5d0455c 100644 --- a/cpu/esp8266/periph/Makefile +++ b/cpu/esp8266/periph/Makefile @@ -1,3 +1,3 @@ MODULE = periph -include $(RIOTBASE)/Makefile.base +include $(RIOTMAKE)/periph.mk diff --git a/cpu/esp8266/periph/adc.c b/cpu/esp8266/periph/adc.c index 8e367a005e..6615cfed18 100644 --- a/cpu/esp8266/periph/adc.c +++ b/cpu/esp8266/periph/adc.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Gunar Schorcht + * Copyright (C) 2019 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 @@ -28,10 +28,10 @@ #include "periph_conf.h" #include "board.h" -#include "common.h" +#include "esp_common.h" #include "sdk/sdk.h" -#if defined(ADC_NUMOF) && ADC_NUMOF > 0 +extern uint16_t test_tout(void); int adc_init(adc_t line) { @@ -47,11 +47,10 @@ 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 + return test_tout(); } -#endif +void adc_print_config(void) +{ + printf("\tADC\t\tpins=[ A0 ]\n"); +} diff --git a/cpu/esp8266/periph/flash.c b/cpu/esp8266/periph/flash.c index 912c3ae927..8df422ea43 100644 --- a/cpu/esp8266/periph/flash.c +++ b/cpu/esp8266/periph/flash.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Gunar Schorcht + * Copyright (C) 2019 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 @@ -18,25 +18,42 @@ * @} */ -#define ENABLE_DEBUG (0) -#include "debug.h" +#if MODULE_MTD #include #include #include #include "board.h" -#include "common.h" +#include "esp_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" +#include "esp_flash_data_types.h" +#include "esp_partition.h" -#define SDK_FLASH_FUNCTIONS +#ifdef MCU_ESP32 + +#include "rom/cache.h" +#include "rom/spi_flash.h" +#include "esp_spi_flash.h" + +#else /* MCU_ESP32 */ + +#include "rom_functions.h" +#include "spi_flash.h" + +#endif /* MCU_ESP32 */ + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#define ESP_PART_TABLE_ADDR 0x8000 /* TODO configurable as used in Makefile.include */ +#define ESP_PART_TABLE_SIZE 0xC00 +#define ESP_PART_ENTRY_SIZE 0x20 +#define ESP_PART_ENTRY_MAGIC ESP_PARTITION_MAGIC /* the external pointer to the system MTD device */ mtd_dev_t* mtd0 = 0; @@ -44,6 +61,18 @@ mtd_dev_t* mtd0 = 0; mtd_dev_t _flash_dev; mtd_desc_t _flash_driver; +#ifdef MCU_ESP8266 + +/* for source code compatibility with ESP32 SDK */ +#define esp_rom_spiflash_chip_t esp_spi_flash_chip_t +#define g_rom_flashchip flashchip + +/* defined in vendor/esp-idf/spi_flash.c */ +extern esp_spi_flash_chip_t flashchip; +extern uint32_t spi_flash_get_id(void); + +#endif /* MCU_ESP8266 */ + /* 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); @@ -55,60 +84,420 @@ 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 */ +static esp_rom_spiflash_chip_t* _flashchip = NULL; -void flash_drive_init (void) +/* flash_id determines the flash size in kByte */ +static const uint32_t flash_sizes[] = { + 256, /* last byte of id is 0x12 */ + 512, /* last byte of id is 0x13 */ + 1 * 1024, /* last byte of id is 0x14 */ + 2 * 1024, /* last byte of id is 0x15 */ + 4 * 1024, /* last byte of id is 0x16 */ + 8 * 1024, /* last byte of id is 0x17 */ + 16 * 1024 /* last byte of id is 0x18 */ +}; + +void spi_flash_drive_init (void) { DEBUG("%s\n", __func__); + _flashchip = &g_rom_flashchip; + assert(_flashchip); + +#ifdef MCU_ESP8266 + _flashchip->deviceId = spi_flash_get_id(); + uint8_t devid_lb = _flashchip->deviceId >> 16 & 0xff; + if (devid_lb >= 0x12 && devid_lb <= 0x18) { + _flashchip->chip_size = flash_sizes[devid_lb - 0x12] << 10; + } + else { + LOG_TAG_WARNING("spi_flash", "could not determine flash size, " + "4 MBytes are used as default size\n"); + _flashchip->chip_size = 4 << 20; + } +#endif /* MCU_ESP8266 */ + _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; + /* first, set the beginning of flash to 0x0 to read partition table */ + _flash_beg = 0x0; + _flash_end = _flashchip->chip_size - 5 * _flashchip->sector_size; _flash_size = _flash_end - _flash_beg; + /* read in partition table an determine the top of all partitions */ + uint32_t part_addr = ESP_PART_TABLE_ADDR; + uint8_t part_buf[ESP_PART_ENTRY_SIZE]; + bool part_read = true; + uint32_t part_top = 0; + esp_partition_info_t* part = (esp_partition_info_t*)part_buf; + + while (part_read && part_addr < ESP_PART_TABLE_ADDR + ESP_PART_TABLE_SIZE) { + spi_flash_read (part_addr, (void*)part_buf, ESP_PART_ENTRY_SIZE); + + if (part->magic == ESP_PART_ENTRY_MAGIC) { + DEBUG("%s partition @%08x size=%08x label=%s\n", __func__, + part->pos.offset, part->pos.size, part->label); + if (part->pos.offset + part->pos.size > part_top) { + part_top = part->pos.offset + part->pos.size; + } + part_addr += ESP_PART_ENTRY_SIZE; + } + else { + part_read = false; + } + } + +#ifdef MCU_ESP32 + /* map the partition top address to next higher multiple of 0x100000 (1 MB) */ + part_top = (part_top + 0x100000) & ~0xfffff; +#else /* MCU_ESP32 */ + /* map the partition top address to next higher multiple of 0x80000 (512 kB) */ + part_top = (part_top + 0x80000) & ~0x7ffff; +#endif /* MCU_ESP32 */ + + /* + * if flash drive start address is not configured, use the determined + * one otherwise check the configured one and use it + */ + #if SPI_FLASH_DRIVE_START + if (part_top > SPI_FLASH_DRIVE_START) { + LOG_TAG_ERROR("spi_flash", "configured MTD start address in SPI Flash is to less\n"); + } + else if (SPI_FLASH_DRIVE_START % _flashchip->sector_size) { + LOG_TAG_ERROR("spi_flash", "configured start address has to be a " + "multiple of %d byte\n", _flashchip->sector_size); + part_top = ((SPI_FLASH_DRIVE_START + + _flashchip->sector_size)) & ~(_flashchip->sector_size-1); + } + else { + part_top = SPI_FLASH_DRIVE_START; + } + #endif + + /* second, change flash parameters according to partition table */ + _flash_beg = part_top; + _flash_end = _flashchip->chip_size - 5 * _flashchip->sector_size; + _flash_size = _flash_end - _flash_beg; /* MUST be at least 3 sectors (0x3000) */ + + LOG_TAG_INFO("spi_flash", "MTD in SPI flash starts at address 0x%08x " + "with a size of %d kbytes\n", _flash_beg, _flash_size >> 10); + _flash_dev.driver = &_flash_driver; - _flash_dev.sector_count = _flash_size / flashchip->sector_size; + _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; + _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); + _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"); } +#ifdef MCU_ESP32 + +#define RETURN_WITH_ESP_ERR_CODE(err) do { \ + switch (err) { \ + case ESP_ROM_SPIFLASH_RESULT_OK : return ESP_OK; \ + case ESP_ROM_SPIFLASH_RESULT_ERR : return ESP_ERR_FLASH_OP_FAIL; \ + case ESP_ROM_SPIFLASH_RESULT_TIMEOUT: return ESP_ERR_FLASH_OP_TIMEOUT; \ + } \ + return ESP_FAIL; \ +} while(0) + +uint8_t _flash_buf[ESP_ROM_SPIFLASH_BUFF_BYTE_READ_NUM]; + +esp_err_t IRAM_ATTR spi_flash_read(size_t addr, void *buff, size_t size) +{ + DEBUG("%s addr=%08x size=%u buf=%p\n", __func__, addr, size, buff); + + CHECK_PARAM_RET (buff != NULL, -ENOTSUP); + + /* size must be within the flash address space */ + CHECK_PARAM_RET (addr + size <= _flash_end, -EOVERFLOW); + + int result = ESP_ROM_SPIFLASH_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; + + /* disable interrupts and the cache */ + critical_enter(); + Cache_Read_Disable(PRO_CPU_NUM); + + result = esp_rom_spiflash_read (word_addr, (uint32_t*)_flash_buf, 4); + memcpy(buff, _flash_buf + pos_in_word, len_in_word); + + /* enable interrupts and the cache */ + Cache_Read_Enable(PRO_CPU_NUM); + critical_exit(); + + buff = (uint8_t*)buff + len_in_word; + addr += len_in_word; + len -= len_in_word; + } + + /* read all full words, maximum ESP_ROM_SPIFLASH_BUFF_BYTE_READ_NUM + in one read operation */ + while (len > 4 && result == ESP_ROM_SPIFLASH_RESULT_OK) { + uint32_t len_full_words = len & ~0x3; + if (len_full_words > ESP_ROM_SPIFLASH_BUFF_BYTE_READ_NUM) { + len_full_words = ESP_ROM_SPIFLASH_BUFF_BYTE_READ_NUM; + } + + /* disable interrupts and the cache */ + critical_enter(); + Cache_Read_Disable(PRO_CPU_NUM); + + result |= esp_rom_spiflash_read (addr, (uint32_t*)_flash_buf, len_full_words); + memcpy(buff, _flash_buf, len_full_words); + + /* enable interrupts and the cache */ + Cache_Read_Enable(PRO_CPU_NUM); + critical_exit(); + + 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 (len && result == ESP_ROM_SPIFLASH_RESULT_OK) { + /* disable interrupts and the cache */ + critical_enter(); + Cache_Read_Disable(PRO_CPU_NUM); + + result |= esp_rom_spiflash_read (addr, (uint32_t*)_flash_buf, 4); + memcpy(buff, _flash_buf, len); + + /* enable interrupts and the cache */ + Cache_Read_Enable(PRO_CPU_NUM); + critical_exit(); + } + + /* return with the ESP-IDF error code that is mapped from ROM error code */ + RETURN_WITH_ESP_ERR_CODE(result); +} + +esp_err_t IRAM_ATTR spi_flash_write(size_t addr, const void *buff, size_t size) +{ + DEBUG("%s addr=%08x size=%u buf=%p\n", __func__, addr, size, buff); + + CHECK_PARAM_RET (buff != NULL, -ENOTSUP); + + /* size must be within the flash address space */ + CHECK_PARAM_RET (addr + size <= _flash_end, -EOVERFLOW); + + /* prepare for write access */ + int result = esp_rom_spiflash_unlock(); + uint32_t len = size; + + /* if addr is not 4 byte aligned, we need to prepare first full word */ + if (addr & 0x3 && result == ESP_ROM_SPIFLASH_RESULT_OK) { + 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; + + /* disable interrupts and the cache */ + critical_enter(); + Cache_Read_Disable(PRO_CPU_NUM); + + result |= esp_rom_spiflash_read (word_addr, (uint32_t*)_flash_buf, 4); + memcpy(_flash_buf + pos_in_word, buff, len_in_word); + result |= esp_rom_spiflash_write (word_addr, (uint32_t*)_flash_buf, 4); + + /* enable interrupts and the cache */ + Cache_Read_Enable(PRO_CPU_NUM); + critical_exit(); + + buff = (uint8_t*)buff + len_in_word; + addr += len_in_word; + len -= len_in_word; + } + + /* write all full words, maximum ESP_ROM_SPIFLASH_BUFF_BYTE_WRITE_NUM + in one write operation */ + while (len > 4 && result == ESP_ROM_SPIFLASH_RESULT_OK) { + uint32_t len_full_words = len & ~0x3; + if (len_full_words > ESP_ROM_SPIFLASH_BUFF_BYTE_WRITE_NUM) { + len_full_words = ESP_ROM_SPIFLASH_BUFF_BYTE_WRITE_NUM; + } + + /* disable interrupts and the cache */ + critical_enter(); + Cache_Read_Disable(PRO_CPU_NUM); + + memcpy(_flash_buf, buff, len_full_words); + result |= esp_rom_spiflash_write (addr, (uint32_t*)_flash_buf, len_full_words); + + /* enable interrupts and the cache */ + Cache_Read_Enable(PRO_CPU_NUM); + critical_exit(); + + 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 (len && result == ESP_ROM_SPIFLASH_RESULT_OK) { + /* disable interrupts and the cache */ + critical_enter(); + Cache_Read_Disable(PRO_CPU_NUM); + + result |= esp_rom_spiflash_read (addr, (uint32_t*)_flash_buf, 4); + memcpy(_flash_buf, buff, len); + result |= esp_rom_spiflash_write (addr, (uint32_t*)_flash_buf, 4); + + /* enable interrupts and the cache */ + Cache_Read_Enable(PRO_CPU_NUM); + critical_exit(); + } + + /* reset write access */ + esp_rom_spiflash_lock(); + + /* return with the ESP-IDF error code that is mapped from ROM error code */ + RETURN_WITH_ESP_ERR_CODE(result); +} + +esp_err_t IRAM_ATTR spi_flash_erase_sector(size_t sector) +{ + return spi_flash_erase_range(sector * _flashchip->sector_size, 1); +} + +esp_err_t IRAM_ATTR spi_flash_erase_range(size_t addr, size_t size) +{ + /* size must be within the flash address space */ + CHECK_PARAM_RET (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) + + /* prepare for write access */ + uint32_t result = esp_rom_spiflash_unlock(); + + /* erase as many sectors as necessary */ + uint32_t sec = addr / _flashchip->sector_size; + uint32_t cnt = size / _flashchip->sector_size; + uint32_t sec_per_block = _flashchip->block_size / _flashchip->sector_size; + + while (cnt && result == ESP_ROM_SPIFLASH_RESULT_OK) { + /* disable interrupts and the cache */ + critical_enter(); + Cache_Read_Disable(PRO_CPU_NUM); + + /* erase block-wise (64 kByte) if cnt is at least sec_per_block */ + if (cnt >= sec_per_block) { + result = esp_rom_spiflash_erase_block (sec / sec_per_block); + sec += sec_per_block; + cnt -= sec_per_block; + } + else { + result = esp_rom_spiflash_erase_sector (sec++); + cnt--; + } + + /* enable interrupts and the cache */ + Cache_Read_Enable(PRO_CPU_NUM); + critical_exit(); + } + + /* reset write access */ + esp_rom_spiflash_lock(); + + /* return with the ESP-IDF error code that is mapped from ROM error code */ + RETURN_WITH_ESP_ERR_CODE(result); +} + +#endif /* MCU_ESP32 */ + +const esp_partition_t* esp_partition_find_first(esp_partition_type_t type, + esp_partition_subtype_t subtype, + const char* label) +{ + uint32_t info_addr = ESP_PART_TABLE_ADDR; + uint8_t info_buf[ESP_PART_ENTRY_SIZE]; + bool info_read = true; + + esp_partition_info_t* info = (esp_partition_info_t*)info_buf; + esp_partition_t* part; + + while (info_read && info_addr < ESP_PART_TABLE_ADDR + ESP_PART_TABLE_SIZE) { + spi_flash_read (info_addr, (void*)info_buf, ESP_PART_ENTRY_SIZE); + + if (info->magic == ESP_PART_ENTRY_MAGIC) { + DEBUG("%s partition @%08x size=%08x label=%s\n", __func__, + info->pos.offset, info->pos.size, info->label); + if ((info->type == type) && + (info->subtype == subtype || subtype == ESP_PARTITION_SUBTYPE_ANY) && + (label == NULL || strcmp((const char*)info->label, label) == 0)) { + part = malloc(sizeof(esp_partition_t)); + part->type = info->type; + part->subtype = info->subtype; + part->address = info->pos.offset; + part->size = info->pos.size; + part->encrypted = info->flags & PART_FLAG_ENCRYPTED; + strncpy(part->label, (const char*)info->label, sizeof(info->label)); + part->label[sizeof(part->label) - 1] = 0x0; + + return part; + } + info_addr += ESP_PART_ENTRY_SIZE; + } + else { + info_read = false; + } + } + return NULL; +} + +esp_err_t esp_partition_erase_range(const esp_partition_t* part, + size_t addr, size_t size) +{ + CHECK_PARAM_RET(part != NULL, ESP_ERR_INVALID_ARG); + + /* start addr and size must be inside the partition */ + CHECK_PARAM_RET(addr <= part->size, ESP_ERR_INVALID_ARG); + CHECK_PARAM_RET(addr + size <= part->size, ESP_ERR_INVALID_SIZE); + /* start addr and size must be a multiple of sector size */ + CHECK_PARAM_RET(addr % SPI_FLASH_SEC_SIZE == 0, ESP_ERR_INVALID_ARG); + CHECK_PARAM_RET(size % SPI_FLASH_SEC_SIZE == 0, ESP_ERR_INVALID_SIZE); + + return spi_flash_erase_range(part->address + addr, size); +} + + 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 + if (_flashchip->chip_size <= _flash_beg) { LOG_ERROR("Flash size is equal or less than %d Byte, " - "SPIFFS cannot be used\n", SPIFFS_FLASH_BEGIN); + "SPIFFS cannot be used\n", _flash_beg); 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); @@ -119,58 +508,7 @@ static int _flash_read (mtd_dev_t *dev, void *buff, uint32_t addr, uint32_t siz /* 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 + return (spi_flash_read(_flash_beg + addr, buff, size) == ESP_OK) ? (int)size : -EIO; } static int _flash_write (mtd_dev_t *dev, const void *buff, uint32_t addr, uint32_t size) @@ -183,60 +521,11 @@ static int _flash_write (mtd_dev_t *dev, const void *buff, uint32_t addr, uint32 /* size must be within the flash address space */ CHECK_PARAM_RET (_flash_beg + addr + size <= _flash_end, -EOVERFLOW); - #ifndef SDK_FLASH_FUNCTIONS + /* addr + size must be within a page */ + CHECK_PARAM_RET (size <= _flashchip->page_size, -EOVERFLOW); + CHECK_PARAM_RET ((addr % _flashchip->page_size) + size <= _flashchip->page_size, -EOVERFLOW); - 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 + return (spi_flash_write(_flash_beg + addr, buff, size) == ESP_OK) ? (int)size : -EIO; } static int _flash_erase (mtd_dev_t *dev, uint32_t addr, uint32_t size) @@ -249,34 +538,10 @@ static int _flash_erase (mtd_dev_t *dev, uint32_t addr, uint32_t size) 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) + CHECK_PARAM_RET (size >= _flashchip->sector_size, -EOVERFLOW); + CHECK_PARAM_RET (size % _flashchip->sector_size == 0, -EOVERFLOW) - #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 + return (spi_flash_erase_range(_flash_beg + addr, size) == ESP_OK) ? 0 : -EIO; } static int _flash_power (mtd_dev_t *dev, enum mtd_power_state power) @@ -285,3 +550,5 @@ static int _flash_power (mtd_dev_t *dev, enum mtd_power_state power) return -ENOTSUP; } + +#endif /* MODULE_MTD */ diff --git a/cpu/esp8266/periph/gpio.c b/cpu/esp8266/periph/gpio.c index 5608fad884..4a538436ff 100644 --- a/cpu/esp8266/periph/gpio.c +++ b/cpu/esp8266/periph/gpio.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Gunar Schorcht + * Copyright (C) 2019 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 @@ -26,17 +26,17 @@ #include "log.h" #include "periph/gpio.h" /* RIOT gpio.h */ -#include "c_types.h" -#include "eagle_soc.h" -#include "ets_sys.h" +#include "esp8266/eagle_soc.h" +#include "esp8266/gpio_register.h" +#include "rom/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 "esp_common.h" +#include "gpio_arch.h" #include "irq_arch.h" #include "syscalls.h" @@ -48,7 +48,7 @@ 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_pin_usage_t _gpio_pin_usage [GPIO_PIN_NUMOF] = { _GPIO, /* gpio0 */ @@ -75,6 +75,12 @@ _gpio_pin_usage_t _gpio_pin_usage [GPIO_PIN_NUMOF] = _GPIO /* gpio16 */ }; +/* String representation of usage types */ +const char* _gpio_pin_usage_str[] = +{ + "GPIO", "I2C", "PWM", "SPI", "SPI Flash", "UART", "N/A" +}; + int gpio_init(gpio_t pin, gpio_mode_t mode) { DEBUG("%s: %d %d\n", __func__, pin, mode); @@ -285,3 +291,20 @@ void gpio_toggle (gpio_t pin) GPIO.OUT ^= BIT(pin); } + +int gpio_set_pin_usage(gpio_t pin, gpio_pin_usage_t usage) +{ + CHECK_PARAM_RET(pin < GPIO_PIN_NUMOF, -1); + _gpio_pin_usage [pin] = usage; + return 0; +} + +gpio_pin_usage_t gpio_get_pin_usage (gpio_t pin) +{ + return (pin < GPIO_PIN_NUMOF) ? _gpio_pin_usage[pin] : _NOT_EXIST; +} + +const char* gpio_get_pin_usage_str(gpio_t pin) +{ + return _gpio_pin_usage_str[_gpio_pin_usage[((pin < GPIO_PIN_NUMOF) ? pin : _NOT_EXIST)]]; +} diff --git a/cpu/esp8266/periph/hwrng.c b/cpu/esp8266/periph/hwrng.c index 4696810e63..3984e309de 100644 --- a/cpu/esp8266/periph/hwrng.c +++ b/cpu/esp8266/periph/hwrng.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Gunar Schorcht + * Copyright (C) 2019 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 @@ -22,8 +22,13 @@ #include "cpu.h" #include "periph_conf.h" #include "periph/hwrng.h" +#include "rom/ets_sys.h" -#include "esp/wdev_regs.h" +#ifdef MCU_ESP32 +static const uint32_t* RNG_DATA_REG = (uint32_t*)0x3ff75144; +#else +static const uint32_t* RNG_DATA_REG = (uint32_t*)0x3ff20e44; +#endif void hwrng_init(void) { @@ -37,7 +42,7 @@ void hwrng_read(void *buf, unsigned int num) while (count < num) { /* read next 4 bytes of random data */ - uint32_t tmp = WDEV.HWRNG; + uint32_t tmp = *RNG_DATA_REG; /* copy data into result vector */ for (int i = 0; i < 4 && count < num; i++) { diff --git a/cpu/esp8266/periph/i2c.c b/cpu/esp8266/periph/i2c.c index 5a21624b76..d7608fc51d 100644 --- a/cpu/esp8266/periph/i2c.c +++ b/cpu/esp8266/periph/i2c.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Gunar Schorcht + * Copyright (C) 2019 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 @@ -7,8 +7,8 @@ */ /** - * @ingroup cpu_esp8266 - * @ingroup drivers_periph_i2c + * @ingroup cpu_esp8266 + * @ingroup drivers_periph_i2c * @{ * * @file @@ -22,18 +22,16 @@ /* 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. + The implementation bases on the bit-banging I2C master implementation as + described in [wikipedia](https://en.wikipedia.org/wiki/I%C2%B2C#Example_of_bit-banging_the_I%C2%B2C_master_protocol). */ #define ENABLE_DEBUG (0) #include "debug.h" #include -#include #include +#include #include "cpu.h" #include "log.h" @@ -42,20 +40,36 @@ #include "periph/gpio.h" #include "periph/i2c.h" -#include "common.h" +#include "esp_common.h" +#include "gpio_arch.h" +#include "rom/ets_sys.h" + +#ifdef MCU_ESP32 + +#include "soc/gpio_reg.h" +#include "soc/gpio_struct.h" + +/* max clock stretching counter */ +#define I2C_CLOCK_STRETCH 200 + +/* gpio access macros */ +#define GPIO_SET(l,h,b) if (b < 32) GPIO.l = BIT(b); else GPIO.h.val = BIT(32-b) +#define GPIO_GET(l,h,b) ((b < 32) ? GPIO.l & BIT(b) : GPIO.h.val & BIT(32-b)) + +#else /* MCU_ESP32 */ #include "esp/gpio_regs.h" #include "sdk/ets.h" -#if defined(I2C_NUMOF) && I2C_NUMOF > 0 +/* max clock stretching counter (ca. 10 ms) */ +#define I2C_CLOCK_STRETCH 40000 -/* has to be declared as extern since it is not possible to include */ -/* user_interface.h due to conflicts with gpio_init */ +/* following functions have 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 (ca. 10 ms) */ -#define I2C_CLOCK_STRETCH 40000 +#endif /* MCU_ESP32 */ typedef struct { @@ -71,50 +85,42 @@ typedef struct uint32_t sda_bit; /* gpio bit mask for faster access */ uint32_t delay; + mutex_t lock; } _i2c_bus_t; -static _i2c_bus_t _i2c_bus[] = -{ - #if defined(I2C0_SDA) && defined(I2C0_SCL) - { - .speed = I2C0_SPEED, - .sda = I2C0_SDA, - .scl = I2C0_SCL - }, - #endif - #if defined(I2C1_SDA) && defined(I2C1_SCL) - { - .speed = I2C1_SPEED, - .sda = I2C1_SDA, - .scl = I2C1_SCL - }, - #endif - #if defined(I2C2_SDA) && defined(I2C2_SCL) - { - .speed = I2C2_SPEED, - .sda = I2C2_SDA, - .scl = I2C2_SCL - }, - #endif -}; +static _i2c_bus_t _i2c_bus[I2C_NUMOF] = {}; /* to ensure that I2C is always optimized with -O2 to use the defined delays */ #pragma GCC optimize ("O2") +#ifdef MCU_ESP32 +static const uint32_t _i2c_delays[][3] = +{ + /* values specify one half-period and are only valid for -O2 option */ + /* value = [period - 0.25 us (240 MHz) / 0.5us(160MHz) / 1.0us(80MHz)] */ + /* * cycles per second / 2 */ + /* 1 us = 16 cycles (80 MHz) / 32 cycles (160 MHz) / 48 cycles (240) */ + /* values for 80, 160, 240 MHz */ + [I2C_SPEED_LOW] = {790, 1590, 2390}, /* 10 kbps (period 100 us) */ + [I2C_SPEED_NORMAL] = { 70, 150, 230}, /* 100 kbps (period 10 us) */ + [I2C_SPEED_FAST] = { 11, 31, 51}, /* 400 kbps (period 2.5 us) */ + [I2C_SPEED_FAST_PLUS] = { 0, 7, 15}, /* 1 Mbps (period 1 us) */ + [I2C_SPEED_HIGH] = { 0, 0, 0} /* 3.4 Mbps (period 0.3 us) not working */ +}; +#else /* MCU_ESP32 */ static const uint32_t _i2c_delays[][2] = { - /* values specify one half-period and are only valid for -O2 option */ + /* 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, 989}, /* 10 kbps (period 100 us) */ - [I2C_SPEED_NORMAL] = { 190, 89}, /* 100 kbps (period 10 us) */ - [I2C_SPEED_FAST] = { 40, 16}, /* 400 kbps (period 2.5 us) */ - [I2C_SPEED_FAST_PLUS] = { 13, 0}, /* 1 Mbps (period 1 us) */ + /* 1 us = 20 cycles (80 MHz) / 40 cycles (160 MHz) */ + [I2C_SPEED_LOW] = {989, 1990}, /* 10 kbps (period 100 us) */ + [I2C_SPEED_NORMAL] = { 89, 190}, /* 100 kbps (period 10 us) */ + [I2C_SPEED_FAST] = { 16, 40}, /* 400 kbps (period 2.5 us) */ + [I2C_SPEED_FAST_PLUS] = { 0, 13}, /* 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 }; +#endif /* MCU_ESP32 */ /* forward declaration of internal functions */ @@ -136,51 +142,86 @@ static void _i2c_abort (_i2c_bus_t* bus, const char* func); static void _i2c_clear (_i2c_bus_t* bus); /* implementation of i2c interface */ + void i2c_init(i2c_t dev) { - if (I2C_NUMOF != ARRAY_SIZE(_i2c_bus)) { - 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 < ARRAY_SIZE(_i2c_bus)); + assert(dev < I2C_NUMOF_MAX); + assert(dev < I2C_NUMOF); + if (i2c_config[dev].speed == I2C_SPEED_HIGH) { + LOG_TAG_INFO("i2c", "I2C_SPEED_HIGH is not supported\n"); return; } - CHECK_PARAM (dev < I2C_NUMOF) + mutex_init(&_i2c_bus[dev].lock); - if (_i2c_bus[dev].speed == I2C_SPEED_HIGH) { - LOG_INFO("I2C_SPEED_HIGH is not supported\n"); - return; - } - - i2c_acquire (dev); + _i2c_bus[dev].scl = i2c_config[dev].scl; + _i2c_bus[dev].sda = i2c_config[dev].sda; + _i2c_bus[dev].speed = i2c_config[dev].speed; _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); + switch (ets_get_cpu_frequency()) { + case 80: _i2c_bus[dev].delay = _i2c_delays[_i2c_bus[dev].speed][0]; break; + case 160: _i2c_bus[dev].delay = _i2c_delays[_i2c_bus[dev].speed][1]; break; +#ifdef MCU_ESP32 + case 240: _i2c_bus[dev].delay = _i2c_delays[_i2c_bus[dev].speed][2]; break; +#endif + default : LOG_TAG_INFO("i2c", "I2C software implementation is not " + "supported for this CPU frequency: %d MHz\n", + ets_get_cpu_frequency()); + return; + } + DEBUG("%s: scl=%d sda=%d speed=%d\n", __func__, + _i2c_bus[dev].scl, _i2c_bus[dev].sda, _i2c_bus[dev].speed); + + /* reset the GPIO usage if the pins were used for I2C before */ + if (gpio_get_pin_usage(_i2c_bus[dev].scl) == _I2C) { + gpio_set_pin_usage(_i2c_bus[dev].scl, _GPIO); + } + if (gpio_get_pin_usage(_i2c_bus[dev].sda) == _I2C) { + gpio_set_pin_usage(_i2c_bus[dev].sda, _GPIO); + } + + /* Configure and initialize SDA and SCL pin. */ +#ifdef MCU_ESP32 /* - * Configure and initialize SDA and SCL pin. - * Note: Due to critical timing required by the I2C software - * implementation, the ESP8266 GPIOs can not be used directly in GPIO_OD_PU - * mode. Instead, the GPIOs are configured in GPIO_IN_PU mode with - * open-drain output driver. Signal levels are then realized as following: + * ESP32 pins are used in input/output mode with open-drain output driver. + * Signal levels are then realized as following: + * + * - HIGH: Output value 1 lets the pin floating and is pulled-up to high. + * - LOW : Output value 0 actively drives the pin to low. + */ + if (gpio_init(_i2c_bus[dev].scl, GPIO_IN_OD_PU) || + gpio_init(_i2c_bus[dev].sda, GPIO_IN_OD_PU)) { + return; + } +#else /* MCU_ESP32 */ + /* + * Due to critical timing required by the I2C software implementation, + * the ESP8266 GPIOs can not be used directly in GPIO_OD_PU mode. + * Instead, the GPIOs are configured in GPIO_IN_PU mode with open-drain + * output driver. Signal levels are then realized as following: * * - HIGH: The GPIO is used in the configured GPIO_IN_PU mode. In this * mode, the output driver is in open-drain mode and pulled-up. * - LOW : The GPIO is temporarily switched to GPIO_OD_PU mode. In this * mode, the output value 0, which is written during - * initialization, actively drives the output to low. + * initialization, actively drives the pin to low. */ - gpio_init (_i2c_bus[dev].scl, GPIO_IN_PU); - gpio_init (_i2c_bus[dev].sda, GPIO_IN_PU); - gpio_clear (_i2c_bus[dev].scl); - gpio_clear (_i2c_bus[dev].sda); + if (gpio_init(_i2c_bus[dev].scl, GPIO_IN_PU) || + gpio_init(_i2c_bus[dev].sda, GPIO_IN_PU)) { + return; + } +#endif /* MCU_ESP32 */ + + /* store the usage type in GPIO table */ + gpio_set_pin_usage(_i2c_bus[dev].scl, _I2C); + gpio_set_pin_usage(_i2c_bus[dev].sda, _I2C); /* set SDA and SCL to be floating and pulled-up to high */ _i2c_sda_high (&_i2c_bus[dev]); @@ -189,16 +230,14 @@ void i2c_init(i2c_t dev) /* clear the bus if necessary (SDA is driven permanently low) */ _i2c_clear (&_i2c_bus[dev]); - i2c_release (dev); - return; } int i2c_acquire(i2c_t dev) { - CHECK_PARAM_RET (dev < I2C_NUMOF, -1) + assert(dev < I2C_NUMOF); - mutex_lock(&i2c_bus_lock[dev]); + mutex_lock(&_i2c_bus[dev].lock); return 0; } @@ -206,7 +245,7 @@ void i2c_release(i2c_t dev) { assert(dev < I2C_NUMOF); - mutex_unlock(&i2c_bus_lock[dev]); + mutex_unlock(&_i2c_bus[dev].lock); } int /* IRAM */ i2c_read_bytes(i2c_t dev, uint16_t addr, void *data, size_t len, uint8_t flags) @@ -214,7 +253,8 @@ int /* IRAM */ i2c_read_bytes(i2c_t dev, uint16_t addr, void *data, size_t len, 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); + assert(dev < I2C_NUMOF); + CHECK_PARAM_RET (len > 0, -EINVAL); CHECK_PARAM_RET (data != NULL, -EINVAL); @@ -235,7 +275,7 @@ int /* IRAM */ i2c_read_bytes(i2c_t dev, uint16_t addr, void *data, size_t len, /* 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 */ + /* send address bytes with read flag */ if ((res = _i2c_write_byte (bus, addr1)) != 0 || (res = _i2c_write_byte (bus, addr2)) != 0) { /* abort transfer */ @@ -275,7 +315,8 @@ int /* IRAM */ i2c_write_bytes(i2c_t dev, uint16_t addr, const void *data, size_ 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); + assert(dev < I2C_NUMOF); + CHECK_PARAM_RET (len > 0, -EINVAL); CHECK_PARAM_RET (data != NULL, -EINVAL); @@ -348,74 +389,99 @@ void i2c_poweroff(i2c_t dev) 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)); + __asm__ volatile ("1: _addi.n %0, %0, -1 \n" + " bnez %0, 1b \n" : "=r" (cycles) : "0" (cycles)); } } /* - * Note: Due to critical timing required by the I2C software implementation, - * the ESP8266 GPIOs can not be used directly in GPIO_OD_PU mode. Instead, - * the GPIOs are configured in GPIO_IN_PU mode with open-drain output driver. - * Signal levels are then realized as following: + * Please note: SDA and SDL pins are used in GPIO_OD_PU mode + * (open-drain with pull-ups). * - * - HIGH: The GPIO is used in the configured GPIO_IN_PU mode. In this mode, - * the output driver is in open-drain mode and pulled-up. - * - LOW : The GPIO is temporarily switched to GPIO_OD_PU mode. In this mode, - * the output value 0, which is written during initialization, - * actively drives the output to low. + * 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_scl_read(_i2c_bus_t* bus) { - /* read SCL status */ + /* read SCL status (pin is in open-drain mode and set) */ +#ifdef MCU_ESP32 + return GPIO_GET(in, in1, bus->scl); +#else /* MCU_ESP32 */ return GPIO.IN & bus->scl_bit; +#endif /* MCU_ESP32 */ } static inline bool _i2c_sda_read(_i2c_bus_t* bus) { - /* read SDA status */ + /* read SDA status (pin is in open-drain mode and set) */ +#ifdef MCU_ESP32 + return GPIO_GET(in, in1, bus->sda); +#else /* MCU_ESP32 */ return GPIO.IN & bus->sda_bit; -} - -static inline void _i2c_scl_low(_i2c_bus_t* bus) -{ - /* - * set SCL signal low (switch temporarily to GPIO_OD_PU where the - * written output value 0 drives the pin actively to low) - */ - GPIO.ENABLE_OUT_SET = bus->scl_bit; +#endif /* MCU_ESP32 */ } static inline void _i2c_scl_high(_i2c_bus_t* bus) { +#ifdef MCU_ESP32 + /* set SCL signal high (pin is in open-drain mode and pulled-up) */ + GPIO_SET(out_w1ts, out1_w1ts, bus->scl); +#else /* MCU_ESP32 */ /* * set SCL signal high (switch back to GPIO_IN_PU mode, that is the pin is * in open-drain mode and pulled-up to high) */ GPIO.ENABLE_OUT_CLEAR = bus->scl_bit; +#endif /* MCU_ESP32 */ } -static inline void _i2c_sda_low(_i2c_bus_t* bus) +static inline void _i2c_scl_low(_i2c_bus_t* bus) { +#ifdef MCU_ESP32 + /* set SCL signal low (actively driven to low) */ + GPIO_SET(out_w1tc, out1_w1tc, bus->scl); +#else /* MCU_ESP32 */ /* - * set SDA signal low (switch temporarily to GPIO_OD_PU where the + * set SCL signal low (switch temporarily to GPIO_OD_PU where the * written output value 0 drives the pin actively to low) */ - GPIO.ENABLE_OUT_SET = bus->sda_bit; + GPIO.ENABLE_OUT_SET = bus->scl_bit; +#endif /* MCU_ESP32 */ } static inline void _i2c_sda_high(_i2c_bus_t* bus) { +#ifdef MCU_ESP32 + /* set SDA signal high (pin is in open-drain mode and pulled-up) */ + GPIO_SET(out_w1ts, out1_w1ts, bus->sda); +#else /* MCU_ESP32 */ /* * set SDA signal high (switch back to GPIO_IN_PU mode, that is the pin is * in open-drain mode and pulled-up to high) */ GPIO.ENABLE_OUT_CLEAR = bus->sda_bit; +#endif /* MCU_ESP32 */ +} + +static inline void _i2c_sda_low(_i2c_bus_t* bus) +{ +#ifdef MCU_ESP32 + /* set SDA signal low (actively driven to low) */ + GPIO_SET(out_w1tc, out1_w1tc, bus->sda); +#else /* MCU_ESP32 */ + /* + * set SDA signal low (switch temporarily to GPIO_OD_PU where the + * written output value 0 drives the pin actively to low) + */ + GPIO.ENABLE_OUT_SET = bus->sda_bit; +#endif /* MCU_ESP32 */ } static void _i2c_clear(_i2c_bus_t* bus) @@ -500,7 +566,7 @@ static /* IRAM */ int _i2c_start_cond(_i2c_bus_t* bus) /* SDA = passive HIGH (floating and pulled-up) */ _i2c_sda_high (bus); - /* t_VD;DAT not neccessary */ + /* t_VD;DAT not necessary */ /* _i2c_delay (bus); */ /* SCL = passive HIGH (floating and pulled-up) */ @@ -724,7 +790,7 @@ static /* IRAM */ int _i2c_read_byte(_i2c_bus_t* bus, uint8_t *byte, bool ack) if (res != 0) { return res; } - *byte = (*byte << 1) | bit; + *byte = (*byte << 1) | (bit ? 1 : 0); } /* write acknowledgement flag */ @@ -735,17 +801,8 @@ static /* IRAM */ int _i2c_read_byte(_i2c_bus_t* bus, uint8_t *byte, bool ack) 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); + for (unsigned dev = 0; dev < I2C_NUMOF; dev++) { + printf("\tI2C_DEV(%u)\tscl=%d sda=%d\n", + dev, i2c_config[dev].scl, i2c_config[dev].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 */ diff --git a/cpu/esp8266/periph/pm.c b/cpu/esp8266/periph/pm.c index 8b07a4eb73..94c71457e4 100644 --- a/cpu/esp8266/periph/pm.c +++ b/cpu/esp8266/periph/pm.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Gunar Schorcht + * Copyright (C) 2019 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 @@ -18,31 +18,28 @@ * @} */ -#define ENABLE_DEBUG 0 +#define ENABLE_DEBUG (0) #include "debug.h" -#include - -#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__); + DEBUG ("%s enter to sleep @%u\n", __func__, system_get_time()); - #if !defined(QEMU) - DEBUG ("%s enter to sleep @%u\n", __func__, phy_get_mactime()); + /* reset system watchdog timer */ + system_wdt_feed(); + #ifndef MODULE_ESP_QEMU /* 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 + + DEBUG ("%s exit from sleep @%u\n", __func__, system_get_time()); + + /* reset system watchdog timer */ + system_wdt_feed(); } void pm_off(void) diff --git a/cpu/esp8266/periph/pwm.c b/cpu/esp8266/periph/pwm.c index 58f7437ab9..c8ff89b6b6 100644 --- a/cpu/esp8266/periph/pwm.c +++ b/cpu/esp8266/periph/pwm.c @@ -18,7 +18,7 @@ * @} */ -#define ENABLE_DEBUG 0 +#define ENABLE_DEBUG (0) #include "debug.h" #include "cpu.h" @@ -27,21 +27,19 @@ #include "periph/pwm.h" #include "periph/gpio.h" -#include "common.h" +#include "esp_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 +#include "gpio_arch.h" +#include "sdk/sdk.h" +#include "xtensa/xtensa_api.h" #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 +#define ETS_FRC1_INT_ENABLE() xt_ints_on(BIT(ETS_FRC_TIMER1_INUM)) +#define ETS_FRC1_INT_DISABLE() xt_ints_off(BIT(ETS_FRC_TIMER1_INUM)) +#define ETS_FRC1_INT_ATTACH(f, a) xt_set_interrupt_handler(ETS_FRC_TIMER1_INUM, f, a) typedef struct { @@ -65,8 +63,6 @@ typedef struct 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 (); @@ -119,14 +115,15 @@ 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; + uint8_t _pwm_channel_gpio_num = sizeof(pwm0_channels) >> 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); + assert(pwm < PWM_NUMOF_MAX); + assert(pwm < PWM_NUMOF); + assert(freq > 0); + assert(_pwm_channel_gpio_num <= PWM_CHANNEL_NUM_MAX); /* 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 */ + /* 100.000 (period of 10 us), reduce freq if necessary and keep resolution */ if (res * freq > PWM_MAX_CPS) { freq = PWM_MAX_CPS / res; } @@ -138,21 +135,21 @@ uint32_t pwm_init(pwm_t pwm, pwm_mode_t mode, uint32_t freq, uint16_t res) _pwm_dev.mode = mode; for (int i = 0; i < _pwm_channel_gpio_num; i++) { - if (_gpio_pin_usage[_pwm_channel_gpios[i]] != _GPIO) { + if (gpio_get_pin_usage(pwm0_channels[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) { + if (gpio_init(pwm0_channels[i], GPIO_OUT) < 0) { return 0; } - gpio_clear (_pwm_channel_gpios[i]); + gpio_clear (pwm0_channels[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[_pwm_dev.chn_num].gpio = pwm0_channels[i]; _pwm_dev.chn_num++; } @@ -169,7 +166,7 @@ uint32_t pwm_init(pwm_t pwm, pwm_mode_t mode, uint32_t freq, uint16_t res) uint8_t pwm_channels(pwm_t pwm) { - CHECK_PARAM_RET (pwm < PWM_NUMOF, 0); + assert(pwm < PWM_NUMOF); return _pwm_dev.chn_num; } @@ -178,9 +175,9 @@ 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); + assert(pwm < PWM_NUMOF); + assert(channel < _pwm_dev.chn_num); + assert(value <= _pwm_dev.res); uint32_t state = irq_disable(); uint32_t phase = _pwm_dev.cycles - _pwm_dev.cycles % _pwm_dev.res; @@ -226,25 +223,16 @@ void pwm_poweron(pwm_t pwm) void pwm_poweroff(pwm_t pwm) { - CHECK_PARAM (pwm < PWM_NUMOF); + assert(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]); + printf("\tPWM_DEV(0)\tchannels=[ "); + for (unsigned i = 0; i < sizeof(pwm0_channels) >> 2; i++) { + printf("%d ", pwm0_channels[i]); } - LOG_INFO("]\n"); + printf("]\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 */ diff --git a/cpu/esp8266/periph/rtc.c b/cpu/esp8266/periph/rtc.c index cfdabca47a..5a306d2fde 100644 --- a/cpu/esp8266/periph/rtc.c +++ b/cpu/esp8266/periph/rtc.c @@ -26,9 +26,9 @@ #include "log.h" #include "periph/rtc.h" -#include "common.h" +#include "esp_common.h" -#include "sdk/ets.h" +#include "sdk/sdk.h" void rtc_init(void) { diff --git a/cpu/esp8266/periph/spi.c b/cpu/esp8266/periph/spi.c index 7d0440f7c1..59d31a9f36 100644 --- a/cpu/esp8266/periph/spi.c +++ b/cpu/esp8266/periph/spi.c @@ -21,32 +21,77 @@ #define ENABLE_DEBUG (0) #include "debug.h" -#include "common.h" +#include "esp_common.h" #include "log.h" -#if defined(MODULE_PERIPH_SPI) - #include #include "cpu.h" #include "mutex.h" #include "periph/spi.h" +#include "esp_attr.h" +#include "gpio_arch.h" + +#ifdef MCU_ESP32 + +#include "driver/periph_ctrl.h" +#include "rom/ets_sys.h" +#include "soc/gpio_reg.h" +#include "soc/gpio_sig_map.h" +#include "soc/gpio_struct.h" +#include "soc/io_mux_reg.h" +#include "soc/spi_reg.h" +#include "soc/spi_struct.h" + +#else /* MCU_ESP32 */ + #include "esp/iomux_regs.h" -#include "esp/spi_regs.h" +#include "esp8266/spi_register.h" +#include "esp8266/spi_struct.h" -#include "gpio_common.h" +#define SPI_DOUTDIN (BIT(0)) -#define SPI_BUS_NUM 2 -#define SPI_BLOCK_SIZE 64 /* number of bytes per SPI transfer */ +#endif /* MCU_ESP32 */ -static mutex_t _spi_lock[SPI_BUS_NUM] = { MUTEX_INIT }; +#define SPI_BLOCK_SIZE 64 /* number of bytes per SPI transfer */ -/* indicate whether SPI interface were already initilized */ -static bool _spi_initialized[SPI_BUS_NUM] = { false }; +/* pins of FSI are fixed */ +#define FSPI_SCK GPIO6 +#define FSPI_MISO GPIO7 +#define FSPI_MOSI GPIO8 -/* indicate whether pins of the SPI interface were already initilized */ -static bool _spi_pins_initialized[SPI_BUS_NUM] = { false }; +/** structure which describes all properties of one SPI bus */ +struct _spi_bus_t { + spi_dev_t* regs; /* pointer to register data struct of the SPI device */ + mutex_t lock; /* mutex for each possible SPI interface */ + bool initialized; /* interface already initialized */ + bool pins_initialized; /* pins interface initialized */ +#ifdef MCU_ESP32 + uint8_t mod; /* peripheral hardware module of the SPI interface */ + uint8_t int_src; /* peripheral interrupt source used by the SPI device */ + uint8_t signal_sck; /* SCK signal from the controller */ + uint8_t signal_mosi; /* MOSI signal from the controller */ + uint8_t signal_miso; /* MISO signal to the controller */ +#endif /* MCU_ESP32 */ +}; + +static struct _spi_bus_t _spi[] = { + #ifdef SPI0_CTRL + { + .initialized = false, + .pins_initialized = false, + .lock = MUTEX_INIT + }, + #endif + #ifdef SPI1_CTRL + { + .initialized = false, + .pins_initialized = false, + .lock = MUTEX_INIT + }, + #endif +}; /* * GPIOs that were once initialized as SPI interface pins can not be used @@ -57,23 +102,48 @@ static bool _spi_pins_initialized[SPI_BUS_NUM] = { false }; * 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) +void IRAM_ATTR spi_init (spi_t bus) { + assert(bus < SPI_NUMOF_MAX); + assert(bus < SPI_NUMOF); + + switch (spi_config[bus].ctrl) { +#ifdef MCU_ESP32 + case HSPI: _spi[bus].regs = &SPI2; + _spi[bus].mod = PERIPH_HSPI_MODULE; + _spi[bus].int_src = ETS_SPI2_INTR_SOURCE; + _spi[bus].signal_sck = HSPICLK_OUT_IDX; + _spi[bus].signal_mosi = HSPID_OUT_IDX; + _spi[bus].signal_miso = HSPIQ_IN_IDX; + break; + case VSPI: _spi[bus].regs = &SPI3; + _spi[bus].mod = PERIPH_VSPI_MODULE; + _spi[bus].int_src = ETS_SPI3_INTR_SOURCE; + _spi[bus].signal_sck = VSPICLK_OUT_IDX; + _spi[bus].signal_mosi = VSPID_OUT_IDX; + _spi[bus].signal_miso = VSPIQ_IN_IDX; + break; +#else /* MCU_ESP32 */ + case HSPI: _spi[bus].regs = &SPI1; + break; +#endif /* MCU_ESP32 */ + default: LOG_TAG_ERROR("spi", "invalid SPI interface controller " + "used for SPI_DEV(%d)\n", bus); + break; + } return; } -void _spi_init_internal(spi_t bus) +/* Internal initialization function when the interface is used the first time */ +static void IRAM_ATTR _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)); + assert(bus < SPI_NUMOF); /* avoid multiple initializations */ - if (_spi_initialized[bus]) { + if (_spi[bus].initialized) { return; } - _spi_initialized[bus] = true; + _spi[bus].initialized = true; DEBUG("%s bus=%u\n", __func__, bus); @@ -82,139 +152,176 @@ void _spi_init_internal(spi_t 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) { + if (gpio_get_pin_usage(spi_config[bus].sck) != _SPI && + gpio_get_pin_usage(spi_config[bus].miso) != _SPI && + gpio_get_pin_usage(spi_config[bus].mosi) != _SPI && + gpio_get_pin_usage(spi_config[bus].cs) != _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; +#ifdef MCU_ESP32 + /* enable (power on) the according SPI module */ + periph_module_enable(_spi[bus].mod); +#endif /* MCU_ESP32 */ + + /* bring the bus into a defined state */ + _spi[bus].regs->user.val = SPI_USR_MOSI | SPI_CK_I_EDGE | SPI_DOUTDIN | + SPI_CS_SETUP | SPI_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); + _spi[bus].regs->user.wr_byte_order = 0; + _spi[bus].regs->user.rd_byte_order = 0; /* 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); */ + _spi[bus].regs->ctrl.wr_bit_order = 0; + _spi[bus].regs->ctrl.rd_bit_order = 0; - DEBUG("%s SPI(bus).USER0=%08x SPI(bus).CTRL0=%08x\n", - __func__, SPI(bus).USER0, SPI(bus).CTRL0); + /* reset all DIO or QIO flags */ + _spi[bus].regs->ctrl.fread_qio = 0; + _spi[bus].regs->ctrl.fread_dio = 0; + _spi[bus].regs->ctrl.fread_quad = 0; + _spi[bus].regs->ctrl.fread_dual = 0; + + /* disable fast read mode and write protection */ + _spi[bus].regs->ctrl.fastrd_mode = 0; +#ifdef MCU_ESP32 + _spi[bus].regs->ctrl.wp = 0; +#endif /* MCU_ESP32 */ + + /* acquire and release to set default parameters */ + spi_acquire(bus, GPIO_UNDEF, SPI_MODE_0, SPI_CLK_1MHZ); + spi_release(bus); } void spi_init_pins(spi_t bus) { - /* see spi_init */ - CHECK_PARAM (bus == SPI_DEV(0)); + assert(bus < SPI_NUMOF); /* call initialization of the SPI interface if it is not initialized yet */ - if (!_spi_initialized[bus]) { + if (!_spi[bus].initialized) { _spi_init_internal(bus); } /* avoid multiple pin initializations */ - if (_spi_pins_initialized[bus]) { + if (_spi[bus].pins_initialized) { return; } - _spi_pins_initialized[bus] = true; + _spi[bus].pins_initialized = true; DEBUG("%s bus=%u\n", __func__, bus); - uint32_t iomux_func = (bus == 0) ? IOMUX_FUNC(1) : IOMUX_FUNC(2); + if (gpio_init (spi_config[bus].sck, GPIO_OUT) || + gpio_init (spi_config[bus].mosi, GPIO_OUT) || + gpio_init (spi_config[bus].miso, GPIO_IN)) { + LOG_TAG_ERROR("spi", + "SPI_DEV(%d) pins could not be initialized\n", bus); + return; + } + if (spi_init_cs(bus, spi_config[bus].cs) != SPI_OK) { + LOG_TAG_ERROR("spi", + "SPI_DEV(%d) CS signal could not be initialized\n", + bus); + return; + } + /* store the usage type in GPIO table */ + gpio_set_pin_usage(spi_config[bus].sck, _SPI); + gpio_set_pin_usage(spi_config[bus].mosi, _SPI); + gpio_set_pin_usage(spi_config[bus].miso, _SPI); + +#ifdef MCU_ESP32 + /* connect SCK and MOSI pins to the output signal through the GPIO matrix */ + GPIO.func_out_sel_cfg[spi_config[bus].sck].func_sel = _spi[bus].signal_sck; + GPIO.func_out_sel_cfg[spi_config[bus].mosi].func_sel = _spi[bus].signal_mosi; + /* connect MISO input signal to the MISO pin through the GPIO matrix */ + GPIO.func_in_sel_cfg[_spi[bus].signal_miso].sig_in_sel = 1; + GPIO.func_in_sel_cfg[_spi[bus].signal_miso].sig_in_inv = 0; + GPIO.func_in_sel_cfg[_spi[bus].signal_miso].func_sel = spi_config[bus].miso; +#else /* MCU_ESP32 */ /* - * CS is handled as normal GPIO ouptut. Due to the small number of GPIOs + * CS is handled as normal GPIO output. 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; + uint32_t iomux_func = IOMUX_FUNC(2); - 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; + IOMUX.PIN[_gpio_to_iomux[spi_config[bus].miso]] &= ~IOMUX_PIN_FUNC_MASK; + IOMUX.PIN[_gpio_to_iomux[spi_config[bus].mosi]] &= ~IOMUX_PIN_FUNC_MASK; + IOMUX.PIN[_gpio_to_iomux[spi_config[bus].sck]] &= ~IOMUX_PIN_FUNC_MASK; - _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 */ + IOMUX.PIN[_gpio_to_iomux[spi_config[bus].miso]] |= iomux_func; + IOMUX.PIN[_gpio_to_iomux[spi_config[bus].mosi]] |= iomux_func; + IOMUX.PIN[_gpio_to_iomux[spi_config[bus].sck]] |= iomux_func; +#endif /* MCU_ESP32 */ } 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); + assert(bus < SPI_NUMOF); /* call initialization of the SPI interface if it is not initialized yet */ - if (!_spi_initialized[bus]) { + if (!_spi[bus].initialized) { _spi_init_internal(bus); } /* return if pin is already initialized as SPI CS signal */ - if (_gpio_pin_usage [cs] == _SPI) { + if (gpio_get_pin_usage(cs) == _SPI) { return SPI_OK; } - if (_gpio_pin_usage [cs] != _GPIO) { + /* check whether CS pin is used otherwise */ + if (gpio_get_pin_usage(cs) != _GPIO) { return SPI_NOCS; } + /* initialize the pin */ gpio_init(cs, GPIO_OUT); - gpio_set (cs); + gpio_set(cs); - _gpio_pin_usage [cs] = _SPI; /* pin cannot be used for anything else */ + /* pin cannot be used for anything else */ + gpio_set_pin_usage(cs, _SPI); return SPI_OK; } -int spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) +int IRAM_ATTR 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); + assert(bus < SPI_NUMOF); /* call initialization of the SPI interface if it is not initialized yet */ - if (!_spi_initialized[bus]) { + if (!_spi[bus].initialized) { _spi_init_internal(bus); } /* if parameter cs is GPIO_UNDEF, the default CS pin is used */ - cs = (cs == GPIO_UNDEF) ? SPI0_CS0_GPIO : cs; + cs = (cs == GPIO_UNDEF) ? spi_config[bus].cs : 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); + if (gpio_get_pin_usage(cs) != _SPI && spi_init_cs(bus, cs) != SPI_OK) { + LOG_TAG_ERROR("spi", + "SPI_DEV(%d) CS signal could not be initialized\n", + bus); return SPI_NOCS; } /* lock the bus */ - mutex_lock(&_spi_lock[bus]); + mutex_lock(&_spi[bus].lock); - /* 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 mode + * see ESP32 Technical Reference, Table 25 and Section 7.4.2 + */ + _spi[bus].regs->pin.ck_idle_edge = (mode == SPI_MODE_2 || mode == SPI_MODE_3); + _spi[bus].regs->user.ck_out_edge = (mode == SPI_MODE_1 || mode == SPI_MODE_2); + _spi[bus].regs->ctrl2.miso_delay_mode = (mode == SPI_MODE_0 || mode == SPI_MODE_3) ? 2 : 1; + _spi[bus].regs->ctrl2.miso_delay_num = 0; + _spi[bus].regs->ctrl2.mosi_delay_mode = 0; + _spi[bus].regs->ctrl2.mosi_delay_num = 0; /* set SPI clock * see ESP8266 Technical Reference Appendix 2 - SPI registers @@ -248,31 +355,53 @@ int spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) spi_clkdiv_pre--; spi_clkcnt_N--; - DEBUG("%s spi_clkdiv_prev=%u spi_clkcnt_N=%u\n", __func__, 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 */ +#ifdef MCU_ESP8266 + IOMUX.CONF &= ~IOMUX_CONF_SPI1_CLOCK_EQU_SYS_CLOCK; +#endif - 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); + /* SPI clock is derived from APB clock by dividers */ + _spi[bus].regs->clock.clk_equ_sysclk = 0; - DEBUG("%s IOMUX.CONF=%08x SPI(bus).CLOCK=%08x\n", - __func__, IOMUX.CONF, SPI(bus).CLOCK); + /* set SPI clock dividers */ + _spi[bus].regs->clock.clkdiv_pre = spi_clkdiv_pre; + _spi[bus].regs->clock.clkcnt_n = spi_clkcnt_N; + _spi[bus].regs->clock.clkcnt_h = (spi_clkcnt_N+1)/2-1; + _spi[bus].regs->clock.clkcnt_l = spi_clkcnt_N; + + DEBUG("%s bus %d: SPI_CLOCK_REG=%08x\n", + __func__, bus, _spi[bus].regs->clock.val); return SPI_OK; } -void spi_release(spi_t bus) +void IRAM_ATTR spi_release(spi_t bus) { - /* see spi_init */ - CHECK_PARAM (bus == SPI_DEV(0)); + DEBUG("%s bus=%u\n", __func__, bus); + + assert(bus < SPI_NUMOF); /* release the bus */ - mutex_unlock(&_spi_lock[bus]); + mutex_unlock(&_spi[bus].lock); +} + +#ifdef MCU_ESP32 +static const char* _spi_names[] = { "CSPI", "FSPI", "HSPI", "VSPI" }; +#else /* MCU_ESP32 */ +static const char* _spi_names[] = { "FSPI", "HSPI" }; +#endif /* MCU_ESP32 */ + +void spi_print_config(void) +{ + for (unsigned bus = 0; bus < SPI_NUMOF; bus++) { + printf("\tSPI_DEV(%u)\t%s ", bus, _spi_names[spi_config[bus].ctrl]); + printf("sck=%d " , spi_config[bus].sck); + printf("miso=%d ", spi_config[bus].miso); + printf("mosi=%d ", spi_config[bus].mosi); + printf("cs=%d\n" , spi_config[bus].cs); + } } /* @@ -284,29 +413,37 @@ void spi_release(spi_t bus) * https://github.com/SuperHouse/esp-open-rtos/blob/master/LICENSE */ -inline static void _set_size(uint8_t bus, uint8_t bytes) +inline static void IRAM_ATTR _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); + +#ifdef MCU_ESP32 + _spi[bus].regs->mosi_dlen.val = bits; + _spi[bus].regs->miso_dlen.val = bits; +#else /* MCU_ESP32 */ + _spi[bus].regs->user1.usr_mosi_bitlen = bits; + _spi[bus].regs->user1.usr_miso_bitlen = bits; +#endif /* MCU_ESP32 */ } -inline static void _wait(uint8_t bus) +inline static void IRAM_ATTR _wait(uint8_t bus) { - while (SPI(bus).CMD & SPI_CMD_USR) {} + /* SPI_CMD_REG.SPI_USR is cleared when operation has been finished */ + while (_spi[bus].regs->cmd.usr) {} } -inline static void _start(uint8_t bus) +inline static void IRAM_ATTR _start(uint8_t bus) { - SPI(bus).CMD |= SPI_CMD_USR; + /* set SPI_CMD_REG.SPI_USR to start an operation */ + _spi[bus].regs->cmd.usr = 1; } -inline static void _store_data(uint8_t bus, const void *data, size_t len) +inline static void IRAM_ATTR _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); + memcpy((void *)_spi[bus].regs->data_buf, data, len - tail); if (!tail) { return; @@ -317,12 +454,12 @@ inline static void _store_data(uint8_t bus, const void *data, size_t len) for (uint8_t i = 0; i < tail; i++) { last = last | (offs[i] << (i * 8)); } - SPI(bus).W[words] = last; + _spi[bus].regs->data_buf[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) +static void IRAM_ATTR _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); @@ -333,15 +470,14 @@ static void _spi_buf_transfer(uint8_t bus, const void *out, void *in, size_t len _start(bus); _wait(bus); if (in) { - memcpy(in, (void *)SPI(bus).W, len); + memcpy(in, (void *)_spi[bus].regs->data_buf, len); } } -void spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont, - const void *out, void *in, size_t len) +void IRAM_ATTR 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)); + assert(bus < SPI_NUMOF); DEBUG("%s bus=%u cs=%u cont=%d out=%p in=%p len=%u\n", __func__, bus, cs, cont, out, in, len); @@ -360,9 +496,7 @@ void spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont, } #endif - if (cs != SPI_CS_UNDEF) { - gpio_clear (cs); - } + gpio_clear(cs != SPI_CS_UNDEF ? cs : spi_config[bus].cs); size_t blocks = len / SPI_BLOCK_SIZE; uint8_t tail = len % SPI_BLOCK_SIZE; @@ -382,8 +516,8 @@ void spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont, in ? (uint8_t *)in + blocks * SPI_BLOCK_SIZE : NULL, tail); } - if (!cont && (cs != SPI_CS_UNDEF)) { - gpio_set (cs); + if (!cont) { + gpio_set(cs != SPI_CS_UNDEF ? cs : spi_config[bus].cs); } #if ENABLE_DEBUG @@ -396,21 +530,3 @@ void spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont, } #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 */ diff --git a/cpu/esp8266/periph/timer.c b/cpu/esp8266/periph/timer.c index 967787b081..06231a5376 100644 --- a/cpu/esp8266/periph/timer.c +++ b/cpu/esp8266/periph/timer.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Gunar Schorcht + * Copyright (C) 2019 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 @@ -27,9 +27,10 @@ #include "xtimer.h" #include "periph/timer.h" -#include "common.h" -#include "irq_arch.h" #include "esp/common_macros.h" +#include "esp_common.h" +#include "irq_arch.h" +#include "rom/ets_sys.h" #include "sdk/sdk.h" #include "xtensa/hal.h" @@ -295,18 +296,14 @@ static void IRAM __timer_channel_stop (struct hw_timer_t* timer, struct hw_chann void timer_print_config(void) { - for (int i = 0; i < HW_TIMER_NUMOF; i++) { - LOG_INFO("\tTIMER_DEV(%d): %d channel(s)\n", i, - ARRAY_SIZE(timers[i].channels)); + for (unsigned i = 0; i < HW_TIMER_NUMOF; i++) { + printf("\tTIMER_DEV(%u)\t%d channel(s)\n", i, + ARRAY_SIZE(timers[i].channels)); } } #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 @@ -319,6 +316,8 @@ void timer_print_config(void) #define OS_TIMER_DELTA_RSHIFT 16 #define OS_TIMER_CORRECTION 4 +extern void os_timer_arm_us(os_timer_t *ptimer, uint32_t time, bool repeat_flag); + /* 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 */ @@ -560,12 +559,10 @@ static void IRAM __timer_channel_stop (struct phy_timer_t* timer, struct phy_cha void timer_print_config(void) { - for (int i = 0; i < OS_TIMER_NUMOF; i++) { - LOG_INFO("\tTIMER_DEV(%d): %d channel(s)\n", i, - ARRAY_SIZE(timers[i].channels)); + for (unsigned i = 0; i < OS_TIMER_NUMOF; i++) { + printf("\tTIMER_DEV(%u)\t%d channel(s)\n", i, + ARRAY_SIZE(timers[i].channels)); } } -#endif /* NON_SDK */ - #endif /* MODULE_ESP_SW_TIMER */ diff --git a/cpu/esp8266/periph/uart.c b/cpu/esp8266/periph/uart.c index 58a17e6440..b1dd3ecb04 100644 --- a/cpu/esp8266/periph/uart.c +++ b/cpu/esp8266/periph/uart.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Gunar Schorcht + * Copyright (C) 2019 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 @@ -19,147 +19,461 @@ * @} */ -#define ENABLE_DEBUG 0 +#define ENABLE_DEBUG (0) #include "debug.h" -#include "common.h" - +#include "esp_common.h" #include "cpu.h" #include "irq_arch.h" #include "log.h" #include "sched.h" #include "thread.h" +#include "periph/gpio.h" #include "periph/uart.h" -#include "eagle_soc.h" +#include "stdio_uart.h" + +#include "esp/common_macros.h" +#include "rom/ets_sys.h" +#include "xtensa/xtensa_api.h" + +#ifdef MCU_ESP32 + +#include "gpio_arch.h" +#include "driver/periph_ctrl.h" +#include "soc/gpio_reg.h" +#include "soc/gpio_sig_map.h" +#include "soc/gpio_struct.h" +#include "soc/rtc.h" +#include "soc/uart_reg.h" +#include "soc/uart_struct.h" + +#undef UART_CLK_FREQ +#define UART_CLK_FREQ rtc_clk_apb_freq_get() /* APB_CLK is used */ + +#else /* MCU_ESP32 */ + +#include "esp8266/uart_struct.h" + +#ifdef MODULE_ESP_QEMU #include "esp/uart_regs.h" -#include "sdk/sdk.h" +#endif /* MODULE_ESP_QEMU */ -/** - * @brief Allocate memory to store the callback functions. - */ -static uart_isr_ctx_t isr_ctx[UART_NUMOF]; +#define UART0 uart0 +#define UART1 uart1 +#define CPU_INUM_UART ETS_UART_INUM -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); +#endif /* MCU_ESP32 */ + +struct uart_hw_t { + uart_dev_t* regs; /* pointer to register data struct of the UART device */ + uint8_t pin_txd; /* TxD pin used */ + uint8_t pin_rxd; /* RxD pin used */ + bool used; /* indicates whether UART is used */ + uint32_t baudrate; /* used baudrate */ + uart_data_bits_t data; /* used data bits */ + uart_stop_bits_t stop; /* used stop bits */ + uart_parity_t parity; /* used parity bits */ + uart_isr_ctx_t isr_ctx; /* callback functions */ +#ifdef MCU_ESP32 + uint8_t mod; /* peripheral hardware module of the UART interface */ + uint8_t signal_txd; /* TxD signal from the controller */ + uint8_t signal_rxd; /* RxD signal to the controller */ + uint8_t int_src; /* peripheral interrupt source used by the UART device */ +#endif +}; + +/* hardware resources */ +static struct uart_hw_t _uarts[] = { + { + .regs = &UART0, + .used = false, + .baudrate = STDIO_UART_BAUDRATE, + .data = UART_DATA_BITS_8, + .stop = UART_STOP_BITS_1, + .parity = UART_PARITY_NONE, +#ifdef MCU_ESP32 + .mod = PERIPH_UART0_MODULE, + .signal_txd = U0TXD_OUT_IDX, + .signal_rxd = U0RXD_IN_IDX, + .int_src = ETS_UART0_INTR_SOURCE + }, + { + .regs = &UART1, + .used = false, + .baudrate = STDIO_UART_BAUDRATE, + .data = UART_DATA_BITS_8, + .stop = UART_STOP_BITS_1, + .parity = UART_PARITY_NONE, + .mod = PERIPH_UART1_MODULE, + .signal_txd = U1TXD_OUT_IDX, + .signal_rxd = U1RXD_IN_IDX, + .int_src = ETS_UART1_INTR_SOURCE + }, + { + .regs = &UART2, + .used = false, + .baudrate = STDIO_UART_BAUDRATE, + .data = UART_DATA_BITS_8, + .stop = UART_STOP_BITS_1, + .parity = UART_PARITY_NONE, + .mod = PERIPH_UART2_MODULE, + .signal_txd = U2TXD_OUT_IDX, + .signal_rxd = U2RXD_IN_IDX, + .int_src = ETS_UART2_INTR_SOURCE +#endif /* MCU_ESP32 */ + }, +}; + +/* declaration of external functions */ +extern void uart_div_modify(uint8_t uart_no, uint32_t div); + +/* forward declaration of internal functions */ +static int _uart_set_baudrate(uart_t uart, uint32_t baudrate); +static int _uart_set_mode(uart_t uart, uart_data_bits_t data_bits, + uart_parity_t parity, uart_stop_bits_t stop_bits); +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 _uart_config (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); + assert(uart < UART_NUMOF_MAX); + assert(uart < UART_NUMOF); + + _uarts[uart].pin_txd = uart_config[uart].txd; + _uarts[uart].pin_rxd = uart_config[uart].rxd; + +#ifdef MCU_ESP32 + /* UART1 and UART2 have configurable pins */ + if ((uart == UART_DEV(1) || uart == UART_DEV(2))) { + + /* reset the pins when they were already used as UART pins */ + if (gpio_get_pin_usage(_uarts[uart].pin_txd) == _UART) { + gpio_set_pin_usage(_uarts[uart].pin_txd, _GPIO); + } + if (gpio_get_pin_usage(_uarts[uart].pin_rxd) == _UART) { + gpio_set_pin_usage(_uarts[uart].pin_rxd, _GPIO); + } + + /* try to initialize the pins as GPIOs first */ + if (gpio_init(_uarts[uart].pin_txd, GPIO_OUT) || + gpio_init(_uarts[uart].pin_rxd, GPIO_IN)) { + return -1; + } + + /* store the usage type in GPIO table */ + gpio_set_pin_usage(_uarts[uart].pin_txd, _UART); + gpio_set_pin_usage(_uarts[uart].pin_rxd, _UART); + + /* connect TxD pin to the TxD output signal through the GPIO matrix */ + GPIO.func_out_sel_cfg[_uarts[uart].pin_txd].func_sel = _uarts[uart].signal_txd; + + /* connect RxD input signal to the RxD pin through the GPIO matrix */ + GPIO.func_in_sel_cfg[_uarts[uart].signal_rxd].sig_in_sel = 1; + GPIO.func_in_sel_cfg[_uarts[uart].signal_rxd].sig_in_inv = 0; + GPIO.func_in_sel_cfg[_uarts[uart].signal_rxd].func_sel = _uarts[uart].pin_rxd; + } +#endif + _uarts[uart].baudrate = baudrate; /* register interrupt context */ - isr_ctx[uart].rx_cb = rx_cb; - isr_ctx[uart].arg = arg; + _uarts[uart].isr_ctx.rx_cb = rx_cb; + _uarts[uart].isr_ctx.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); - } + /* enable and configure the according UART module */ + uart_poweron(uart); return UART_OK; } +#if MODULE_PERIPH_UART_MODECFG +int uart_mode(uart_t uart, uart_data_bits_t data_bits, uart_parity_t parity, + uart_stop_bits_t stop_bits) +{ + return _uart_set_mode(uart, data_bits, parity, stop_bits); +} +#endif + void uart_write(uart_t uart, const uint8_t *data, size_t len) { - CHECK_PARAM (uart < UART_NUMOF); + CHECK_PARAM(uart < UART_NUMOF); for (size_t i = 0; i < len; i++) { - __uart_tx_one_char(uart, data[i]); + _uart_tx_one_char(uart, data[i]); } } void uart_poweron (uart_t uart) { - /* UART can't be powered on/off, just return */ + CHECK_PARAM(uart < UART_NUMOF); + +#ifdef MCU_ESP32 + periph_module_enable(_uarts[uart].mod); +#endif + _uart_config(uart); } void uart_poweroff (uart_t uart) { - /* UART can't be powered on/off, just return */ + CHECK_PARAM(uart < UART_NUMOF); + +#ifdef MCU_ESP32 + periph_module_disable(_uarts[uart].mod); +#endif } -void IRAM __uart_intr_handler (void *arg) +/* systemwide UART initializations */ +void uart_system_init (void) +{ + for (unsigned uart = 0; uart < UART_NUMOF; uart++) { + /* reset all UART interrupt status registers */ + _uarts[uart].regs->int_clr.val = ~0; + } +} + +void uart_print_config(void) +{ + for (unsigned uart = 0; uart < UART_NUMOF; uart++) { + printf("\tUART_DEV(%u)\ttxd=%d rxd=%d\n", uart, + uart_config[uart].txd, uart_config[uart].rxd); + } +} + +static void IRAM _uart_intr_handler(void *arg) { /* to satisfy the compiler */ (void)arg; - irq_isr_enter (); + irq_isr_enter(); - DEBUG("%s \n", __func__); + /* UART0, UART1, UART2 peripheral interrupt sources are routed to the same + interrupt, so we have to use the status to distinguish interruptees */ + for (unsigned uart = 0; uart < UART_NUMOF; uart++) { + if (_uarts[uart].used) { + DEBUG("%s uart=%d int_st=%08x\n", __func__, + uart, _uarts[uart].regs->int_st.val); - /* - * 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); + if (_uarts[uart].used && _uarts[uart].regs->int_st.rxfifo_full) { + /* read one byte of data */ + uint8_t data = _uart_rx_one_char (uart); + /* if registered, call the RX callback function */ + if (_uarts[uart].isr_ctx.rx_cb) { + _uarts[uart].isr_ctx.rx_cb(_uarts[uart].isr_ctx.arg, data); + } + /* clear interrupt flag */ + _uarts[uart].regs->int_clr.rxfifo_full = 1; + } - /* 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; + /* TODO handle other types of interrupts, for the moment just clear them */ + _uarts[uart].regs->int_clr.val = ~0x0; } } - irq_isr_exit (); + + irq_isr_exit(); } /* RX/TX FIFO capacity is 128 byte */ -#define UART_FIFO_MAX 1 +#define UART_FIFO_MAX 128 /* receive one data byte with wait */ -static uint8_t IRAM __uart_rx_one_char (uart_t uart) +static uint8_t IRAM _uart_rx_one_char (uart_t uart) { - /* uint8_t fifo_len = FIELD2VAL(UART_STATUS_RXFIFO_COUNT, UART(uart).STATUS); */ - +#if defined(MODULE_ESP_QEMU) && defined(MCU_ESP8266) /* 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 */ +#else + /* wait until at least von byte is in RX FIFO */ + while (!_uarts[uart].regs->status.rxfifo_cnt) {} + + /* read the lowest byte from RX FIFO register */ + return _uarts[uart].regs->fifo.rw_byte; +#endif + } /* send one data byte with wait */ -static void __uart_tx_one_char(uart_t uart, uint8_t data) +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) {} + /* wait until at least one byte is available in the TX FIFO */ + while (_uarts[uart].regs->status.txfifo_cnt >= UART_FIFO_MAX) {} - /* send the byte by placing it in the TX FIFO */ + /* send the byte by placing it in the TX FIFO using MPU */ +#ifdef MCU_ESP32 + WRITE_PERI_REG(UART_FIFO_AHB_REG(uart), data); +#else /* MCU_ESP32 */ +#ifdef MODULE_ESP_QEMU UART(uart).FIFO = data; +#else /* MODULE_ESP_QEMU */ + _uarts[uart].regs->fifo.rw_byte = data; +#endif /* MODULE_ESP_QEMU */ +#endif /* MCU_ESP32 */ } -static void __uart_intr_enable(uart_t uart) +static void _uart_intr_enable(uart_t uart) { - UART(uart).INT_ENABLE |= UART_INT_ENABLE_RXFIFO_FULL; - ETS_INTR_ENABLE(ETS_UART_INUM); + _uarts[uart].regs->int_ena.rxfifo_full = 1; + _uarts[uart].regs->int_clr.rxfifo_full = 1; + _uarts[uart].used = true; - DEBUG("%s %08x\n", __func__, UART(uart).INT_ENABLE); + DEBUG("%s %08x\n", __func__, _uarts[uart].regs->int_ena.val); } -void uart_print_config(void) +static void _uart_config(uart_t uart) { - LOG_INFO("\tUART_DEV(0): txd=%d rxd=%d\n", UART0_TXD, UART0_RXD); + assert(uart < UART_NUMOF); + + while (_uarts[uart].regs->status.txfifo_cnt != 0) { } +#if 0 + /* setup the baudrate */ + if (uart == UART_DEV(0) || uart == UART_DEV(1)) { + /* for UART0 and UART1, we can us the ROM function */ + uart_div_modify(uart, (UART_CLK_FREQ << 4) / _uarts[uart].baudrate); + } + else +#endif + if (_uart_set_baudrate(uart, _uarts[uart].baudrate) != UART_OK) { + return; + } + + /* set number of data bits, stop bits and parity mode */ + if (_uart_set_mode(uart, _uarts[uart].data, _uarts[uart].stop, + _uarts[uart].parity) != UART_OK) { + return; + } + + /* reset the FIFOs */ + _uarts[uart].regs->conf0.rxfifo_rst = 1; + _uarts[uart].regs->conf0.rxfifo_rst = 0; + _uarts[uart].regs->conf0.txfifo_rst = 1; + _uarts[uart].regs->conf0.txfifo_rst = 0; + + if (_uarts[uart].isr_ctx.rx_cb) { + /* since reading can only be done byte by byte, we set + UART_RXFIFO_FULL_THRHD interrupt level to 1 byte */ + _uarts[uart].regs->conf1.rxfifo_full_thrhd = 1; + + /* enable the RX FIFO FULL interrupt */ + _uart_intr_enable(uart); + +#ifdef MCU_ESP32 + /* route all UART interrupt sources to same the CPU interrupt */ + intr_matrix_set(PRO_CPU_NUM, _uarts[uart].int_src, CPU_INUM_UART); +#endif /* MCU_ESP32 */ + + /* we have to enable therefore the CPU interrupt here */ + xt_set_interrupt_handler(CPU_INUM_UART, _uart_intr_handler, NULL); + xt_ints_on(BIT(CPU_INUM_UART)); + } +} + +static int _uart_set_baudrate(uart_t uart, uint32_t baudrate) +{ + DEBUG("%s uart=%d, rate=%d\n", __func__, uart, baudrate); + + CHECK_PARAM_RET (uart < UART_NUMOF, -1); + + /* wait until TX FIFO is empty */ + while (_uarts[uart].regs->status.txfifo_cnt != 0) { } + + critical_enter(); + + _uarts[uart].baudrate = baudrate; + +#ifdef MCU_ESP32 + /* use APB_CLK */ + _uarts[uart].regs->conf0.tick_ref_always_on = 1; + /* compute and set the integral and the decimal part */ + uint32_t clk_div = (UART_CLK_FREQ << 4) / _uarts[uart].baudrate; + _uarts[uart].regs->clk_div.div_int = clk_div >> 4; + _uarts[uart].regs->clk_div.div_frag = clk_div & 0xf; +#else + /* compute and set clock divider */ + uint32_t clk_div = UART_CLK_FREQ / _uarts[uart].baudrate; + _uarts[uart].regs->clk_div.val = clk_div & 0xFFFFF; +#endif + + critical_exit(); + + return UART_OK; +} + +static int _uart_set_mode(uart_t uart, uart_data_bits_t data_bits, + uart_parity_t parity, uart_stop_bits_t stop_bits) +{ + DEBUG("%s uart=%d, data_bits=%d parity=%d stop_bits=%d\n", __func__, + uart, data_bits, parity, stop_bits); + + CHECK_PARAM_RET (uart < UART_NUMOF, UART_NODEV); + + critical_enter(); + + /* set number of data bits */ + switch (data_bits) { + case UART_DATA_BITS_5: _uarts[uart].regs->conf0.bit_num = 0; break; + case UART_DATA_BITS_6: _uarts[uart].regs->conf0.bit_num = 1; break; + case UART_DATA_BITS_7: _uarts[uart].regs->conf0.bit_num = 2; break; + case UART_DATA_BITS_8: _uarts[uart].regs->conf0.bit_num = 3; break; + default: LOG_TAG_ERROR("uart", "invalid number of data bits\n"); + critical_exit(); + return UART_NOMODE; + } + /* store changed number of data bits in configuration */ + _uarts[uart].data = data_bits; + + /* set number of stop bits */ + #ifdef MCU_ESP32 + /* workaround for hardware bug when stop bits are set to 2-bit mode. */ + switch (stop_bits) { + case UART_STOP_BITS_1: _uarts[uart].regs->conf0.stop_bit_num = 1; + _uarts[uart].regs->rs485_conf.dl1_en = 0; + break; + case UART_STOP_BITS_2: _uarts[uart].regs->conf0.stop_bit_num = 1; + _uarts[uart].regs->rs485_conf.dl1_en = 1; + break; + default: LOG_TAG_ERROR("uart", "invalid number of stop bits\n"); + critical_exit(); + return UART_NOMODE; + } + #else + switch (stop_bits) { + case UART_STOP_BITS_1: _uarts[uart].regs->conf0.stop_bit_num = 1; break; + case UART_STOP_BITS_2: _uarts[uart].regs->conf0.stop_bit_num = 3; break; + default: LOG_TAG_ERROR("uart", "invalid number of stop bits\n"); + critical_exit(); + return UART_NOMODE; + } + #endif + + /* store changed number of stop bits in configuration */ + _uarts[uart].stop = stop_bits; + + /* set parity mode */ + switch (parity) { + case UART_PARITY_NONE: _uarts[uart].regs->conf0.parity_en = 0; + break; + case UART_PARITY_EVEN: _uarts[uart].regs->conf0.parity = 0; + _uarts[uart].regs->conf0.parity_en = 1; + break; + case UART_PARITY_ODD: _uarts[uart].regs->conf0.parity = 1; + _uarts[uart].regs->conf0.parity_en = 1; + break; + default: LOG_TAG_ERROR("uart", "invalid or unsupported parity mode\n"); + critical_exit(); + return UART_NOMODE; + } + /* store changed parity in configuration */ + _uarts[uart].parity = parity; + + critical_exit(); + + return UART_OK; } diff --git a/cpu/esp8266/sdk/Makefile b/cpu/esp8266/sdk/Makefile index 167ad0f494..197959704f 100644 --- a/cpu/esp8266/sdk/Makefile +++ b/cpu/esp8266/sdk/Makefile @@ -1,3 +1,3 @@ -MODULE=sdk +MODULE=esp_sdk include $(RIOTBASE)/Makefile.base diff --git a/cpu/esp8266/sdk/ets.c b/cpu/esp8266/sdk/ets.c index 799d6cb0ea..80c17bde95 100644 --- a/cpu/esp8266/sdk/ets.c +++ b/cpu/esp8266/sdk/ets.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Gunar Schorcht + * Copyright (C) 2019 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 @@ -16,8 +16,35 @@ * @author Gunar Schorcht * @} */ -#ifndef MODULE_ESP_SDK -#include "sdk/ets.h" +#include -#endif /* MODULE_ESP_SDK */ +#include "eagle_soc.h" +#include "sdk/sdk.h" + +uint8_t ets_get_cpu_frequency(void) +{ + return system_get_cpu_freq(); +} + +void *ets_memcpy(void *dst, const void *src, size_t size) +{ + return memcpy(dst, src, size); +} + +void ets_wdt_disable(void) +{ + SET_PERI_REG_BITS(EDGE_INT_ENABLE_REG, BIT0, 0, BIT0); + SET_PERI_REG_BITS(PERIPHS_WDT_BASEADDR + WDT_CTL_ADDRESS, WDT_CTL_EN_MASK, 0, WDT_CTL_EN_LSB); +} + +void ets_wdt_enable (void) +{ + SET_PERI_REG_BITS(EDGE_INT_ENABLE_REG, BIT0, 1, BIT0); + SET_PERI_REG_BITS(PERIPHS_WDT_BASEADDR + WDT_CTL_ADDRESS, WDT_CTL_EN_MASK, 1, WDT_CTL_EN_LSB); +} + +void ets_install_putc1(void (*p)(char c)) +{ + os_install_putc1(p); +} diff --git a/cpu/esp8266/sdk/ets.h b/cpu/esp8266/sdk/ets.h index 2b2eca4088..f8abc88029 100644 --- a/cpu/esp8266/sdk/ets.h +++ b/cpu/esp8266/sdk/ets.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Gunar Schorcht + * Copyright (C) 2019 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 @@ -21,27 +21,25 @@ #ifndef DOXYGEN -#include #include +#include +#include -#include "c_types.h" -#include "ets_sys.h" +#include "rom/ets_sys.h" #ifdef __cplusplus extern "C" { #endif -/* interrupts that are not defined in espressif/ets_sys.h */ +/* interrupts that are not defined in rom/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 */ /* * The following functions are mappings or dummies for source code - * compatibility of SDK and NON-SDK version + * compatibility of NONOS-SDK and RTOS-SDK version */ #include "xtensa/xtensa_api.h" @@ -50,30 +48,20 @@ extern "C" { #define ets_isr_unmask(x) xt_ints_on(x) #define ets_isr_attach(i,f,a) xt_set_interrupt_handler(i,f,a) +#define ETS_INTR_ENABLE(inum) ets_isr_unmask((1< #include -#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 diff --git a/cpu/esp8266/sdk/sdk.h b/cpu/esp8266/sdk/sdk.h index c36ff451ad..0ac779c781 100644 --- a/cpu/esp8266/sdk/sdk.h +++ b/cpu/esp8266/sdk/sdk.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Gunar Schorcht + * Copyright (C) 2019 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 @@ -20,25 +20,16 @@ #ifndef SDK_H #define SDK_H -#include "c_types.h" -#include "ets_sys.h" +#include #include "sdk/ets.h" -#include "sdk/main.h" #include "sdk/phy.h" -#include "sdk/pp.h" -#include "sdk/rom.h" -#include "sdk/user.h" +#include "sdk/system.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 diff --git a/cpu/esp8266/sdk/system.c b/cpu/esp8266/sdk/system.c new file mode 100644 index 0000000000..c280c2297e --- /dev/null +++ b/cpu/esp8266/sdk/system.c @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2019 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 + * @} + */ + +#include "sdk/sdk.h" + +#include "esp/dport_regs.h" +#include "esp_sleep.h" +#include "esp_system.h" + +uint32_t system_get_chip_id(void) +{ + /* Chip ID as determined by NONOS SDK */ + return (((DPORT.OTP_MAC1 << 8) & 0xffffff00) + ((DPORT.OTP_MAC0 >> 24) & 0xff)); +} + +const char* system_get_sdk_version(void) +{ + return esp_get_idf_version(); +} + +void system_deep_sleep(uint32_t time_in_us) +{ + /* TODO test */ + esp_deep_sleep(time_in_us); +} + +void system_restart(void) +{ + esp_restart(); +} + +void system_update_cpu_freq(uint8_t freq) +{ + if (freq == 160) { + rtc_clk_cpu_freq_set(RTC_CPU_FREQ_160M); + } + else { + rtc_clk_cpu_freq_set(RTC_CPU_FREQ_80M); + } +} diff --git a/cpu/esp8266/sdk/system.h b/cpu/esp8266/sdk/system.h new file mode 100644 index 0000000000..336865ae3a --- /dev/null +++ b/cpu/esp8266/sdk/system.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2019 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 + * @} + */ + +#ifndef SYSTEM_H +#define SYSTEM_H + +#ifndef DOXYGEN + +#include +#include + +#include "esp_task_wdt.h" +#include "sdk/ets.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name Functions for NONOS SDK compatibility + * + * @{ + */ +extern uint8_t system_get_cpu_freq(void); +extern void system_update_cpu_freq(uint8_t); +extern uint32_t system_get_chip_id(void); +extern const char* system_get_sdk_version(void); + +extern void system_deep_sleep(uint32_t time_in_us); +extern void system_restart(void); + +#define system_wdt_init esp_task_wdt_init +#define system_wdt_feed esp_task_wdt_reset +#define system_wdt_start pp_soft_wdt_stop +#define system_wdt_stop pp_soft_wdt_restart + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* DOXYGEN */ +#endif /* SYSTEM_H */ diff --git a/cpu/esp8266/sdk/user.h b/cpu/esp8266/sdk/user.h deleted file mode 100644 index ac0e13e358..0000000000 --- a/cpu/esp8266/sdk/user.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 - * @} - */ - -#ifndef USER_H -#define USER_H - -#ifndef DOXYGEN - -#include -#include - -#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 */ diff --git a/cpu/esp8266/startup.c b/cpu/esp8266/startup.c index 06aa705f55..25243da0f6 100644 --- a/cpu/esp8266/startup.c +++ b/cpu/esp8266/startup.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Gunar Schorcht + * Copyright (C) 2019 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 @@ -16,149 +16,82 @@ * @author Gunar Schorcht * @} */ - #define ENABLE_DEBUG 0 #include "debug.h" +#include +#include +#include +#include #include -#include #include "kernel_init.h" -#include "log.h" #include "periph/init.h" - -#include "c_types.h" -#include "spi_flash.h" +#include "periph/uart.h" #include "board.h" -#include "common.h" + +#include "esp/common_macros.h" +#include "esp_log.h" #include "exceptions.h" #include "stdio_base.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 "rom_functions.h" #include "sdk/sdk.h" #if MODULE_ESP_GDBSTUB -#include "esp-gdbstub/gdbstub.h" +#include "gdbstub.h" #endif -extern void board_init(void); -extern void board_print_config(void); +/* external esp function declarations */ +extern uint32_t hwrand (void); -uint32_t hwrand (void); +void esp_riot_init(void) +{ + /* enable cached read from flash */ + Cache_Read_Enable_New(); -#ifdef MODULE_NEWLIB_SYSCALLS_DEFAULT -/* initialization as it should be called from newlibc */ -extern void _init(void); + /* initialize the ISR stack for usage measurements */ + thread_isr_stack_init(); + +#ifndef MCU_ESP8266 + /* initialize newlib system calls */ + syscalls_init (); #endif -extern uint8_t _bss_start; -extern uint8_t _bss_end; -extern uint8_t _sheap; -extern uint8_t _eheap; + /* set system frequency if not 80 MHz */ + if (ESP8266_CPU_FREQUENCY != 80) { + system_update_cpu_freq(ESP8266_CPU_FREQUENCY); + } -#ifdef MODULE_ESP_SDK - -#include "sdk/ets_task.h" - -/* EST Task priority */ -#define ETS_TASK_PRIORITY (1) - -/* stack for the ETS task */ -static char ets_task_stack[ETS_THREAD_STACKSIZE]; -/* ETS task code */ -extern void *ets_task_func(void *arg); - -/** - * @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 */ - ets_isr_unmask(BIT(ETS_FRC2_INUM)); - ets_isr_unmask(BIT(ETS_WDEV_INUM)); - ets_isr_unmask(BIT(ETS_WDT_INUM)); - - #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 - - /* initialize dummy lwIP library to link it even if esp_wifi is not used */ - extern void esp_lwip_init(void); - esp_lwip_init(); - - thread_create(ets_task_stack, sizeof(ets_task_stack), - ETS_TASK_PRIORITY, - THREAD_CREATE_WOUT_YIELD | THREAD_CREATE_STACKTEST, - ets_task_func, NULL, "ets"); - - - /* 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(); + ets_printf("\n"); + ets_printf("Starting ESP8266 CPU with ID: %08x\n", system_get_chip_id()); + ets_printf("ESP8266-RTOS-SDK Version %s\n\n", system_get_sdk_version()); + ets_printf("CPU clock frequency: %d MHz\n", system_get_cpu_freq()); + extern void heap_stats(void); + heap_stats(); + ets_printf("\n"); /* set exception handlers */ init_exceptions (); + /* systemwide UART initialization */ + extern void uart_system_init (void); + uart_system_init(); + + /* init watchdogs */ + system_wdt_init(); + /* init random number generator */ srand(hwrand()); +#if MODULE_MTD /* init flash drive */ - extern void flash_drive_init (void); - flash_drive_init(); + extern void spi_flash_drive_init (void); + spi_flash_drive_init(); +#endif /* initialize stdio*/ stdio_init(); @@ -169,698 +102,25 @@ void system_init(void) /* trigger board initialization */ board_init(); - /* print memory info */ - print_meminfo(); - - /* 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 (); - - /* set system frequency */ - system_update_cpu_freq(ESP8266_CPU_FREQUENCY); - - /* 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" -#include "user_config.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(); - - /** }@ */ - - /* set system frequency */ - system_update_cpu_freq(ESP8266_CPU_FREQUENCY); - - /* PHASE 3: start RIOT-OS kernel */ - - /* init random number generator */ - srand(hwrand()); - - /* init flash drive */ - extern void flash_drive_init (void); - flash_drive_init(); - - /* initialize stdio*/ - stdio_init(); - - /* trigger static peripheral initialization */ - periph_init(); - - /* initialize the board and startup the kernel */ - board_init(); - - /* print memory info */ - print_meminfo(); - /* print the board config */ board_print_config(); - #ifdef MODULE_NEWLIB_SYSCALLS_DEFAULT - _init(); - #endif + /* initialize ESP system event loop */ + extern void esp_event_handler_init(void); + esp_event_handler_init(); - #ifdef MODULE_ESP_GDBSTUB - gdbstub_init(); - #endif - - #ifdef CONTEXT_SWITCH_BY_INT + /* activate software interrupt based context switch */ 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() ; +#ifdef MODULE_ESP_GDBSTUB + gdbstub_init(); +#endif } - -void start_phase2 (void) +void esp_riot_start(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); - - /** @} */ + /* does not return */ + kernel_init(); } - -#endif /* MODULE_ESP_SDK */ diff --git a/cpu/esp8266/syscalls.c b/cpu/esp8266/syscalls.c index a3b99087c9..b8486903c1 100644 --- a/cpu/esp8266/syscalls.c +++ b/cpu/esp8266/syscalls.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Gunar Schorcht + * Copyright (C) 2019 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 @@ -18,342 +18,589 @@ * @} */ -#define ENABLE_DEBUG 0 -#define MEMLEAK_DEBUG 0 -#include "debug.h" +#include "periph/pm.h" +#include "timex.h" -#include -#include -#include -#include - -#include -#include -#include +#include #include #include -#include +#include +#include +#include +#include +#include -#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 "esp_common.h" +#include "esp/common_macros.h" +#include "esp_attr.h" +#include "sdk/sdk.h" #include "syscalls.h" -#include "esp/xtensa_ops.h" -#include "esp/common_macros.h" +#ifdef MODULE_STDIO_UART +#include "stdio_uart.h" +#endif -#include "sdk/sdk.h" +#ifdef MODULE_ESP_IDF_HEAP +#include "esp_heap_caps.h" +#else +#include "malloc.h" +#endif + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#define MHZ 1000000UL + +#ifndef MODULE_PTHREAD + +#define PTHREAD_CANCEL_DISABLE 1 +/* + * This is a dummy function to avoid undefined references when linking + * against newlib and module pthread is not used. + */ +int pthread_setcancelstate(int state, int *oldstate) +{ + if (oldstate) { + *oldstate = PTHREAD_CANCEL_DISABLE; + } + return 0; +} +#endif /* MODULE_PTHREAD */ + +/* + * TODO: When the lock functions in this section are enabled, an application + * crashes when an ISR calls a `newlib` function that uses `_lock_acquire` + * or `_log_acquire_recursive` to be thread-safe, for example, `puts` in + * `tests/isr_yield_higher`. The reason is that the implementation of these + * functions uses `mutex` and `rmutex` that do not work in the interrupt + * context. Therefore, the lock functions are disabled for the moment, and + * instead `newlib`'s dummy lock functions are used which do not guarantee + * thread safety. + */ + +#if 0 -#ifdef MODULE_ESP_SDK /** - * 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 at least avoid interrupts during the - * execution of memory management functions. Memory management functions - * of ETS are not used and have not to be 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 *__real_malloc(size_t size); -void __real_free(void *ptr); -void *__real_calloc(size_t nmemb, size_t size); -void *__real_realloc(void *ptr, size_t size); -void *__real__malloc_r (struct _reent *r, size_t size); -void __real__free_r (struct _reent *r, void *ptr); -void *__real__realloc_r (struct _reent *r, void *ptr, size_t size); - -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 __wrap_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 __wrap_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 __wrap_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 __wrap_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 __wrap__malloc_r (struct _reent *r, size_t size) -{ - return __wrap_malloc (size); -} - -void IRAM __wrap__free_r (struct _reent *r, void *ptr) -{ - __wrap_free (ptr); -} - -void IRAM *__wrap__realloc_r (struct _reent *r, void *ptr, size_t size) -{ - return __wrap_realloc (ptr, size); -} - -unsigned int get_free_heap_size (void) -{ - return xPortGetFreeHeapSize(); -} - -void IRAM syscalls_init (void) -{ -} - -#else /* MODULE_ESP_SDK */ -/* - * To use the same heap as SDK internally does, 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 be considered here. + * @name Locking functions + * + * Following functions implement the lock mechanism for newlib. */ -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 functions 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. - * These lock/unlock functions cannot be used in the SDK version since some ISR - * in SDK use malloc/free which does not work in interrupt context. +/** + * _malloc_rmtx is defined as static variable to avoid recursive calls of + * malloc when _malloc_r tries to lock __malloc_lock_object the first + * time. All other mutexes that are used for the lock mechanism are allocated + * dynamically. */ - -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; -} +/** + * To properly handle the static rmutex _malloc_rmtx, we have to know + * the address of newlib's static variable __malloc_lock_object. + */ +static _lock_t *__malloc_static_object = NULL; void IRAM _lock_init(_lock_t *lock) { - CHECK_PARAM (sched_active_thread != 0); - CHECK_PARAM (lock != NULL); - CHECK_PARAM (*lock != ((_lock_t)&_malloc_rmtx)); + assert(lock != NULL); - mutex_t* mtx = malloc (sizeof(mutex_t)); + mutex_t* mtx = malloc(sizeof(mutex_t)); if (mtx) { - memset (mtx, 0, sizeof(mutex_t)); + 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)); + assert(lock != NULL); - rmutex_t* rmtx = malloc (sizeof(rmutex_t)); + /** + * Since we don't have direct access to newlib's static variable + * __malloc_lock_object, we have to rely on the fact that function + * _lock_aqcuire_recursive, and thus function _lock_init_recursive + * is called for the first time with newlib's static variable + * __malloc_lock_object as parameter. This is ensured by calling + * malloc in the function syscalls_init. + */ + if (__malloc_static_object == NULL) { + *lock = (_lock_t)&_malloc_rmtx; + __malloc_static_object = lock; + return; + } + + /* _malloc_rmtx is static and has not to be allocated */ + if (lock == __malloc_static_object) { + return; + } + + rmutex_t* rmtx = malloc(sizeof(rmutex_t)); if (rmtx) { - memset (rmtx, 0, sizeof(rmutex_t)); + 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)); + assert(lock != NULL); + assert(lock != __malloc_static_object); - free ((void*)*lock); + free((void*)*lock); *lock = 0; } void IRAM _lock_close_recursive(_lock_t *lock) { - CHECK_PARAM (lock != NULL); - CHECK_PARAM (*lock != ((_lock_t)&_malloc_rmtx)); + assert(lock != NULL); + assert(lock != __malloc_static_object); - free ((void*)*lock); + free((void*)*lock); *lock = 0; } void IRAM _lock_acquire(_lock_t *lock) { - CHECK_PARAM (sched_active_thread != 0); - CHECK_PARAM (lock != NULL && *lock != 0); + assert(lock != NULL); - mutex_lock ((mutex_t*)*lock); + /* if the lock data structure is still not allocated, initialize it first */ + if (*lock == 0) { + _lock_init(lock); + } + + /* if scheduler is not running, we have not to lock the mutex */ + if (sched_active_thread == NULL) { + return; + } + + assert(!irq_is_in()); + 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); + assert(lock != NULL); - rmutex_lock ((rmutex_t*)*lock); + /* if the lock data structure is still not allocated, initialize it first */ + if (*lock == 0) { + _lock_init_recursive(lock); + } + + /* if scheduler is not running, we have not to lock the rmutex */ + if (sched_active_thread == NULL) { + return; + } + + assert(!irq_is_in()); + 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); + assert(lock != NULL); - return rmutex_trylock ((rmutex_t*)*lock); + /* if the lock data structure is still not allocated, initialize it first */ + if (*lock == 0) { + _lock_init(lock); + } + + /* if scheduler is not running, we have not to lock the mutex */ + if (sched_active_thread == NULL) { + return 0; + } + + if (irq_is_in()) { + return 0; + } + + return mutex_trylock((mutex_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); + assert(lock != NULL); - return mutex_trylock ((mutex_t*)*lock); + /* if the lock data structure is still not allocated, initialize it first */ + if (*lock == 0) { + _lock_init_recursive(lock); + } + + /* if scheduler is not running, we have not to lock the rmutex */ + if (sched_active_thread == NULL) { + return 0; + } + + if (irq_is_in()) { + return 0; + } + + return rmutex_trylock((rmutex_t*)*lock); } void IRAM _lock_release(_lock_t *lock) { - CHECK_PARAM (sched_active_thread != 0); - CHECK_PARAM (lock != NULL && *lock != 0); + assert(lock != NULL && *lock != 0); - mutex_unlock ((mutex_t*)*lock); + /* if scheduler is not running, we have not to unlock the mutex */ + if (sched_active_thread == NULL) { + return; + } + + 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); + assert(lock != NULL && *lock != 0); - rmutex_unlock ((rmutex_t*)*lock); + /* if scheduler is not running, we have not to unlock the rmutex */ + if (sched_active_thread == NULL) { + return; + } + + rmutex_unlock((rmutex_t*)*lock); } -#define _cheap heap_top +#endif -extern char *heap_top; -extern char _eheap; /* end of heap (defined in esp8266.riot-os.app.ld) */ -extern char _sheap; /* start of heap (defined in esp8266.riot-os.app.ld) */ +/** + * @name Memory allocation functions + */ -unsigned int IRAM get_free_heap_size (void) +#ifdef MODULE_ESP_IDF_HEAP + +#define MALLOC_CAP_DEFAULT MALLOC_CAP_8BIT +#define heap_caps_malloc_default(s) heap_caps_malloc(s, MALLOC_CAP_DEFAULT) +#define heap_caps_realloc_default(p,s) heap_caps_realloc(p, s, MALLOC_CAP_DEFAULT) + +void* IRAM_ATTR __wrap__malloc_r(struct _reent *r, size_t size) +{ + return heap_caps_malloc_default( size ); +} + +void IRAM_ATTR __wrap__free_r(struct _reent *r, void *ptr) +{ + heap_caps_free( ptr ); +} + +void* IRAM_ATTR __wrap__realloc_r(struct _reent *r, void* ptr, size_t size) +{ + return heap_caps_realloc_default( ptr, size ); +} + +void* IRAM_ATTR __wrap__calloc_r(struct _reent *r, size_t count, size_t size) +{ + void *result = heap_caps_malloc_default(count * size); + if (result) { + bzero(result, count * size); + } + return result; +} + +unsigned int get_free_heap_size(void) +{ + return heap_caps_get_free_size(MALLOC_CAP_DEFAULT); +} + +void heap_stats(void) +{ + extern heap_region_t g_heap_region[HEAP_REGIONS_MAX]; + + for (int i = 0; i < HEAP_REGIONS_MAX; i++) { + ets_printf("Heap region %u @%p: %u (used %u, free %u) [bytes]\n", i, + g_heap_region[i].start_addr, + g_heap_region[i].total_size, + g_heap_region[i].total_size - g_heap_region[i].free_bytes, + g_heap_region[i].free_bytes); + } +} + +#else /* MODULE_ESP_IDF_HEAP */ + +/* for compatibility with ESP-IDF heap functions */ + +void* _heap_caps_malloc(size_t size, uint32_t caps, const char *file, size_t line) +{ + (void)caps; + return malloc(size); +} + +void* _heap_caps_calloc(size_t n, size_t size, uint32_t caps, const char *file, size_t line) +{ + (void)caps; + return calloc(n, size); +} + +void* _heap_caps_realloc(void *ptr, size_t size, uint32_t caps, const char *file, size_t line) +{ + return realloc(ptr, size); +} + +void *_heap_caps_zalloc(size_t size, uint32_t caps, const char *file, size_t line) +{ + void *ptr = malloc(size); + if (ptr) { + memset(ptr, 0, size); + } + return ptr; +} + +void _heap_caps_free(void *ptr, const char *file, size_t line) +{ + (void)file; + (void)line; + free(ptr); +} + +void heap_caps_init(void) +{ +} + +extern uint8_t _eheap; /* end of heap (defined in esp32.common.ld) */ +extern uint8_t _sheap; /* start of heap (defined in esp32.common.ld) */ + +unsigned int IRAM get_free_heap_size(void) { struct mallinfo minfo = mallinfo(); return &_eheap - &_sheap - minfo.uordblks; } -#endif /* MODULE_ESP_SDK */ - void heap_stats(void) { - ets_printf("heap: %u (used %d, free %u) [bytes]\n", + ets_printf("heap: %u (used %u, free %u) [bytes]\n", &_eheap - &_sheap, &_eheap - &_sheap - get_free_heap_size(), get_free_heap_size()); } -int _rename_r (struct _reent *r, const char* old, const char* new) +/* alias for compatibility with espressif/wifi_libs */ +uint32_t esp_get_free_heap_size( void ) __attribute__((alias("get_free_heap_size"))); + +#endif /* MODULE_ESP_IDF_HEAP */ + +/** + * @name Other system functions + */ + +int _rename_r(struct _reent *r, const char *from, const char *to) +{ + return 0; +} + +void _abort(void) +{ + ets_printf("#! abort called: powering off\n"); + pm_off(); + while (1) { }; +} + +void _exit_r(struct _reent *r, int status) +{ + _exit(status); +} + +struct _reent* __getreent(void) { + return _GLOBAL_REENT; +} + +#ifdef MCU_ESP32 +static int _no_sys_func(struct _reent *r) { DEBUG("%s: system function does not exist\n", __func__); r->_errno = ENOSYS; return -1; } +#endif -#include +static struct _reent s_reent; -double __ieee754_remainder(double x, double y) { - return x - y * floor(x/y); +#ifdef MCU_ESP32 +static struct syscall_stub_table s_stub_table = +{ + .__getreent = &__getreent, + + ._malloc_r = &_malloc_r, + ._free_r = &_free_r, + ._realloc_r = &_realloc_r, + ._calloc_r = &_calloc_r, + ._sbrk_r = &_sbrk_r, + + ._system_r = (int (*)(struct _reent *, const char*))&_no_sys_func, + ._raise_r = (void (*)(struct _reent *))&_no_sys_func, + ._abort = &_abort, + ._exit_r = &_exit_r, + ._getpid_r = &_getpid_r, + ._kill_r = &_kill_r, + + ._times_r = &_times_r, + ._gettimeofday_r = _gettimeofday_r, + + ._open_r = &_open_r, + ._close_r = &_close_r, + ._lseek_r = (int (*)(struct _reent *r, int, int, int))&_lseek_r, + ._fstat_r = &_fstat_r, + ._stat_r = &_stat_r, + ._write_r = (int (*)(struct _reent *r, int, const void *, int))&_write_r, + ._read_r = (int (*)(struct _reent *r, int, void *, int))&_read_r, + ._unlink_r = &_unlink_r, + ._link_r = (int (*)(struct _reent *r, const char*, const char*))&_no_sys_func, + ._rename_r = (int (*)(struct _reent *r, const char*, const char*))&_no_sys_func, + + ._lock_init = &_lock_init, + ._lock_init_recursive = &_lock_init_recursive, + ._lock_close = &_lock_close, + ._lock_close_recursive = &_lock_close_recursive, + ._lock_acquire = &_lock_acquire, + ._lock_acquire_recursive = &_lock_acquire_recursive, + ._lock_try_acquire = &_lock_try_acquire, + ._lock_try_acquire_recursive = &_lock_try_acquire_recursive, + ._lock_release = &_lock_release, + ._lock_release_recursive = &_lock_release_recursive, + + #if CONFIG_NEWLIB_NANO_FORMAT + ._printf_float = &_printf_float, + ._scanf_float = &_scanf_float, + #else /* CONFIG_NEWLIB_NANO_FORMAT */ + ._printf_float = NULL, + ._scanf_float = NULL, + #endif /* CONFIG_NEWLIB_NANO_FORMAT */ +}; +#endif + +void syscalls_init(void) +{ +#ifdef MCU_ESP32 + /* enable the system timer in us (TMG0 is enabled by default) */ + TIMER_SYSTEM.config.divider = rtc_clk_apb_freq_get()/MHZ; + TIMER_SYSTEM.config.autoreload = 0; + TIMER_SYSTEM.config.enable = 1; + syscall_table_ptr_pro = &s_stub_table; + syscall_table_ptr_app = &s_stub_table; +#endif + _GLOBAL_REENT = &s_reent; + + environ = malloc(sizeof(char*)); + environ[0] = NULL; + + /* + * initialization of newlib, includes the ctors initialization + */ + extern void __libc_init_array(void); + __libc_init_array(); } -float __ieee754_remainderf(float x, float y) { - return x - y * floor(x/y); +uint32_t system_get_time(void) +{ + return phy_get_mactime(); +} + +uint32_t system_get_time_ms(void) +{ + return phy_get_mactime() / US_PER_MS; +} + +#ifdef MCU_ESP32 +uint64_t system_get_time_64(void) +{ + uint64_t ret; + /* latch 64 bit timer value before read */ + TIMER_SYSTEM.update = 0; + /* wait until instructions have been finished */ + __asm__ volatile ("isync"); + /* read the current timer value */ + ret = TIMER_SYSTEM.cnt_low; + ret += ((uint64_t)TIMER_SYSTEM.cnt_high) << 32; + return ret; +} + +/* alias for compatibility with espressif/wifi_libs */ +int64_t esp_timer_get_time(void) __attribute__((alias("system_get_time_64"))); + +static IRAM void system_wdt_int_handler(void *arg) +{ + TIMERG0.int_clr_timers.wdt=1; /* clear interrupt */ + system_wdt_feed(); +} + +void IRAM system_wdt_feed(void) +{ + DEBUG("%s\n", __func__); + TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; /* disable write protection */ + TIMERG0.wdt_feed=1; /* reset MWDT */ + TIMERG0.wdt_wprotect=0; /* enable write protection */ +} + +void system_wdt_init(void) +{ + /* disable boot watchdogs */ + TIMERG0.wdt_config0.flashboot_mod_en = 0; + RTCCNTL.wdt_config0.flashboot_mod_en = 0; + + /* enable system watchdog */ + TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; /* disable write protection */ + TIMERG0.wdt_config0.stg0 = TIMG_WDT_STG_SEL_INT; /* stage0 timeout: interrupt */ + TIMERG0.wdt_config0.stg1 = TIMG_WDT_STG_SEL_RESET_SYSTEM; /* stage1 timeout: sys reset */ + TIMERG0.wdt_config0.sys_reset_length = 7; /* sys reset signal length: 3.2 us */ + TIMERG0.wdt_config0.cpu_reset_length = 7; /* sys reset signal length: 3.2 us */ + TIMERG0.wdt_config0.edge_int_en = 0; + TIMERG0.wdt_config0.level_int_en = 1; + + /* MWDT clock = 80 * 12,5 ns = 1 us */ + TIMERG0.wdt_config1.clk_prescale = 80; + + /* define stage timeouts */ + TIMERG0.wdt_config2 = 2 * US_PER_SEC; /* stage 0: 2 s (interrupt) */ + TIMERG0.wdt_config3 = 4 * US_PER_SEC; /* stage 1: 4 s (sys reset) */ + + TIMERG0.wdt_config0.en = 1; /* enable MWDT */ + TIMERG0.wdt_feed = 1; /* reset MWDT */ + TIMERG0.wdt_wprotect = 0; /* enable write protection */ + + DEBUG("%s TIMERG0 wdt_config0=%08x wdt_config1=%08x wdt_config2=%08x\n", + __func__, TIMERG0.wdt_config0.val, TIMERG0.wdt_config1.val, + TIMERG0.wdt_config2); + + /* route WDT peripheral interrupt source to CPU_INUM_WDT */ + intr_matrix_set(PRO_CPU_NUM, ETS_TG0_WDT_LEVEL_INTR_SOURCE, CPU_INUM_WDT); + /* set the interrupt handler and activate the interrupt */ + xt_set_interrupt_handler(CPU_INUM_WDT, system_wdt_int_handler, NULL); + xt_ints_on(BIT(CPU_INUM_WDT)); +} + +void system_wdt_stop(void) +{ + xt_ints_off(BIT(CPU_INUM_WDT)); + TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; /* disable write protection */ + TIMERG0.wdt_config0.en = 0; /* disable MWDT */ + TIMERG0.wdt_feed = 1; /* reset MWDT */ + TIMERG0.wdt_wprotect = 0; /* enable write protection */ +} + +void system_wdt_start(void) +{ + TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; /* disable write protection */ + TIMERG0.wdt_config0.en = 1; /* disable MWDT */ + TIMERG0.wdt_feed = 1; /* reset MWDT */ + TIMERG0.wdt_wprotect = 0; /* enable write protection */ + xt_ints_on(BIT(CPU_INUM_WDT)); +} +#endif + +__attribute__((weak)) void +_system_prevent_memset_lto(void *const s, int c, const size_t n) +{ + (void)s; + (void)c; + (void)n; +} + +void *system_secure_memset(void *s, int c, size_t n) +{ + memset(s, c, n); + _system_prevent_memset_lto(s, c, n); + return s; } diff --git a/cpu/esp8266/thread_arch.c b/cpu/esp8266/thread_arch.c index eca968959a..7ed987d99d 100644 --- a/cpu/esp8266/thread_arch.c +++ b/cpu/esp8266/thread_arch.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Gunar Schorcht + * Copyright (C) 2019 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 @@ -45,7 +45,7 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#define ENABLE_DEBUG 0 +#define ENABLE_DEBUG (0) #include "debug.h" #include @@ -58,15 +58,23 @@ #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 "esp_common.h" +#include "irq_arch.h" +#include "syscalls.h" #include "tools.h" + +#include "esp_attr.h" +#include "esp/xtensa_ops.h" +#include "rom/ets_sys.h" #include "xtensa/xtensa_context.h" +#ifdef MCU_ESP32 +#include "soc/dport_reg.h" +#else /* MCU_ESP32 */ +#include "esp8266/rom_functions.h" +#include "sdk/sdk.h" +#endif /* MCU_ESP32 */ + /* User exception dispatcher when exiting */ extern void _xt_user_exit(void); @@ -80,21 +88,29 @@ extern void _frxt_setup_switch(void); extern void vPortYield(void); extern void vPortYieldFromInt(void); +#ifdef __XTENSA_CALL0_ABI__ +#define task_exit sched_task_exit +#else /* __XTENSA_CALL0_ABI__ */ +/* forward declarations */ +NORETURN void task_exit(void); +#endif /* __XTENSA_CALL0_ABI__ */ + char* thread_stack_init(thread_task_func_t task_func, void *arg, void *stack_start, int stack_size) { - /* - * Stack layout after task stack initialization + /* Stack layout after task stack initialization * * +------------------------+ * | | TOP * | thread_control_block | - * stack_start + stack_size ==> | | + * stack_start + stack_size ==> | | top_of_stack+1 * +------------------------+ * top_of_stack ==> | | - * | XT_CP_FRAME | + * | XT_CP_SA | * | (optional) | - * top_of_stack + 1 - XT_CP_SIZE ==> | | - * +------------------------+ + * | ... | ... + * | cpstored | XT_CPSTORED + * top_of_stack + 1 - XT_CP_SIZE ==> | cpenable | XT_CPENABLE + * (cp_state) +------------------------+ * | | * | XT_STK_FRAME | * | | XT_STK_... @@ -126,39 +142,43 @@ char* thread_stack_init(thread_task_func_t task_func, void *arg, void *stack_sta 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 + top_of_stack = (uint8_t*)((uint32_t)stack_start + stack_size - 1); /* 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); + sp = (uint8_t*)(((uint32_t)(top_of_stack + 1) - XT_STK_FRMSZ - XT_CP_SIZE) & ~0xf); + + /* Clear whole stack with a known value to assist debugging */ + #if !defined(DEVELHELP) && !defined(SCHED_TEST_STACK) + /* Unfortunately, this affects thread_measure_stack_free function */ + memset(stack_start, 0, stack_size); + #else + memset(sp, 0, XT_STK_FRMSZ + XT_CP_SIZE); + #endif /* ensure that stack is big enough */ assert (sp > (uint8_t*)stack_start); XtExcFrame* exc_frame = (XtExcFrame*)sp; - /* Explicitly initialize certain saved registers */ + /* Explicitly initialize certain saved registers for call0 ABI */ exc_frame->pc = (uint32_t)task_func; /* task entry point */ - exc_frame->a0 = (uint32_t)sched_task_exit; /* task exit point (CHANGED) */ + exc_frame->a0 = (uint32_t)task_exit; /* task exit point*/ 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 */ + /* for CALL0 ABI set in parameter a2 to task argument */ exc_frame->ps = PS_UM | PS_EXCM; + exc_frame->a2 = (uint32_t)arg; /* parameters for task_func */ #else - /* + for windowed ABI also set WOE and CALLINC (pretend task was 'call4'd). */ + /* 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); + exc_frame->a4 = (uint32_t)task_exit; /* task exit point*/ + exc_frame->a6 = (uint32_t)arg; /* parameters for task_func */ #endif #ifdef XT_USE_SWPRI @@ -174,7 +194,7 @@ char* thread_stack_init(thread_task_func_t task_func, void *arg, void *stack_sta */ uint32_t *p; - p = (uint32_t *)(((uint32_t) pxTopOfStack - XT_CP_SIZE)); + p = (uint32_t *)(((uint32_t)(top_of_stack + 1) - XT_CP_SIZE)); p[0] = 0; p[1] = 0; p[2] = (((uint32_t) p) + 12 + XCHAL_TOTAL_SA_ALIGN - 1) & -XCHAL_TOTAL_SA_ALIGN; @@ -188,54 +208,97 @@ char* thread_stack_init(thread_task_func_t task_func, void *arg, void *stack_sta 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(); -} +#ifdef MCU_ESP8266 +extern int MacIsrSigPostDefHdl(void); +unsigned int ets_soft_int_type = ETS_SOFT_INT_NONE; #endif -void thread_yield_higher(void) +/** + * Context switches are realized using software interrupts since interrupt + * entry and exit functions are the only way to save and restore complete + * context including spilling the register windows to the stack + */ +void IRAM_ATTR thread_yield_isr(void* arg) +{ +#ifdef MCU_ESP8266 + ETS_NMI_LOCK(); + + if (ets_soft_int_type == ETS_SOFT_INT_HDL_MAC) { + ets_soft_int_type = MacIsrSigPostDefHdl() ? ETS_SOFT_INT_YIELD + : ETS_SOFT_INT_NONE; + } + + if (ets_soft_int_type == ETS_SOFT_INT_YIELD) { + /* + * set the context switch flag (indicates that context has to be + * switched on exit from interrupt in _frxt_int_exit + */ + ets_soft_int_type = ETS_SOFT_INT_NONE; + _frxt_setup_switch(); + } + + ETS_NMI_UNLOCK(); +#else + /* clear the interrupt first */ + DPORT_WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_0_REG, 0); + /* 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 +} + +/** + * If we are already in an interrupt handler, the function simply sets the + * context switch flag, which indicates that the context has to be switched + * in the _frxt_int_exit function when exiting the interrupt. Otherwise, we + * will generate a software interrupt to force the context switch when + * terminating the software interrupt (see thread_yield_isr). + */ +void IRAM_ATTR thread_yield_higher(void) { /* reset hardware watchdog */ - system_soft_wdt_feed(); + system_wdt_feed(); /* yield next task */ #if defined(ENABLE_DEBUG) && defined(DEVELHELP) if (sched_active_thread) { - DEBUG("%u old task %u %s %u\n", phy_get_mactime(), + DEBUG("%u old task %u %s %u\n", system_get_time(), 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 +#ifdef MCU_ESP32 + /* generate the software interrupt to switch the context */ + DPORT_WRITE_PERI_REG(DPORT_CPU_INTR_FROM_CPU_0_REG, DPORT_CPU_INTR_FROM_CPU_0); +#else /* MCU_ESP32 */ + critical_enter(); + ets_soft_int_type = ETS_SOFT_INT_YIELD; WSR(BIT(ETS_SOFT_INUM), interrupt); - #else - vPortYield(); - #endif + critical_exit(); +#endif /* MCU_ESP32 */ } else { + /* set the context switch flag */ _frxt_setup_switch(); } #if defined(ENABLE_DEBUG) && defined(DEVELHELP) if (sched_active_thread) { - DEBUG("%u new task %u %s %u\n", phy_get_mactime(), + DEBUG("%u new task %u %s %u\n", system_get_time(), sched_active_thread->pid, sched_active_thread->name, sched_active_thread->sp - sched_active_thread-> stack_start); } #endif + /* + * Instruction fetch synchronize: Waits for all previously fetched load, + * store, cache, and special register write instructions that affect + * instruction fetch to be performed before fetching the next instruction. + */ + __asm__("isync"); + return; } @@ -265,7 +328,7 @@ void thread_print_stack(void) return; } -#if defined(DEVELHELP) +#ifdef DEVELHELP extern uint8_t port_IntStack; extern uint8_t port_IntStackTop; @@ -273,9 +336,14 @@ extern uint8_t port_IntStackTop; void thread_isr_stack_init(void) { /* code from thread.c, please see the copyright notice there */ +#ifdef MCU_ESP32 + #define sp (&port_IntStackTop) +#else /* MCU_ESP32 */ + register uint32_t *sp __asm__ ("a1"); +#endif /* MCU_ESP32 */ /* assign each int of the stack the value of it's address */ - uintptr_t *stackmax = (uintptr_t *)&port_IntStackTop; + uintptr_t *stackmax = (uintptr_t *)sp; uintptr_t *stackp = (uintptr_t *)&port_IntStack; while (stackp < stackmax) { @@ -314,10 +382,71 @@ void thread_isr_stack_init(void) {} #endif /* DEVELHELP */ +#ifndef __XTENSA_CALL0_ABI__ +static bool _initial_exit = true; +#endif /* __XTENSA_CALL0_ABI__ */ + NORETURN void cpu_switch_context_exit(void) { - /* Switch context to the highest priority ready task without context save */ - _frxt_dispatch(); + DEBUG("%s\n", __func__); + /* Switch context to the highest priority ready task without context save */ +#ifdef __XTENSA_CALL0_ABI__ + _frxt_dispatch(); +#else /* __XTENSA_CALL0_ABI__ */ + if (_initial_exit) { + _initial_exit = false; + __asm__ volatile ("call0 _frxt_dispatch"); + } + else { + task_exit(); + } +#endif /* __XTENSA_CALL0_ABI__ */ UNREACHABLE(); } + +#ifndef __XTENSA_CALL0_ABI__ +/** + * The function is used on task exit to switch to the context to the next + * running task. It realizes only the second half of a complete context by + * simulating the exit from an interrupt handling where a context switch is + * forced. The old context is not saved here since it is no longer needed. + */ +NORETURN void task_exit(void) +{ + DEBUG("sched_task_exit: ending thread %" PRIkernel_pid "...\n", + sched_active_thread ? sched_active_thread->pid : KERNEL_PID_UNDEF); + + (void) irq_disable(); + + /* remove old task from scheduling if it is not already done */ + if (sched_active_thread) { + sched_threads[sched_active_pid] = NULL; + sched_num_threads--; + sched_set_status((thread_t *)sched_active_thread, STATUS_STOPPED); + sched_active_thread = NULL; + } + + /* determine the new running task */ + sched_run(); + + /* 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(); + + /* set interrupt nesting level to the right value */ + irq_interrupt_nesting++; + + /* reset windowed registers */ + __asm__ volatile ("movi a2, 0\n" + "wsr a2, windowstart\n" + "wsr a2, windowbase\n" + "rsync\n"); + + /* exit from simulated interrupt to switch to the new context */ + __asm__ volatile ("call0 _frxt_int_exit"); + + /* should not be executed */ + UNREACHABLE(); +} +#endif /* __XTENSA_CALL0_ABI__ */ diff --git a/cpu/esp8266/tools.c b/cpu/esp8266/tools.c index 1aadb7e66c..b840aea01f 100644 --- a/cpu/esp8266/tools.c +++ b/cpu/esp8266/tools.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 Gunar Schorcht + * Copyright (C) 2019 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 @@ -19,11 +19,16 @@ */ #include -#include #include +#ifdef MCU_ESP8266 +#include +#include "sdk/sdk.h" +#else +#include "rom/ets_sys.h" +#endif + #include "esp/common_macros.h" -#include "sdk/ets.h" #include "tools.h" extern void malloc_stats (void); @@ -58,19 +63,18 @@ void esp_hexdump (const void* addr, uint32_t num, char width, uint8_t per_line) while (count < num) { if (count % per_line == 0) { - printf ("%08x: ", (uint32_t)((uint8_t*)addr+count*size)); + ets_printf ("%08" PRIx32 ": ", (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; + case 'b': ets_printf("%02" PRIx8 " ", addr8[count++]); break; + case 'h': ets_printf("%04" PRIx16 " ", addr16[count++]); break; + case 'w': ets_printf("%08" PRIx32 " ", addr32[count++]); break; + case 'g': ets_printf("%016" PRIx64 " ", addr64[count++]); break; + default : ets_printf("."); count++; break; } if (count % per_line == 0) { - printf ("\n"); + ets_printf ("\n"); } } - printf ("\n"); + ets_printf ("\n"); }