1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

cpu: add esp32

This commit is contained in:
Schorcht 2018-10-08 12:20:49 +02:00
parent 32e602680b
commit 3ac99877ac
102 changed files with 22558 additions and 0 deletions

25
cpu/esp32/Makefile Normal file
View File

@ -0,0 +1,25 @@
# Define the module that is built:
MODULE = cpu
# Add a list of subdirectories, that should also be built:
DIRS += periph
DIRS += freertos
DIRS += vendor
ifneq (, $(filter esp_can, $(USEMODULE)))
DIRS += esp-can
endif
ifneq (, $(filter esp_eth, $(USEMODULE)))
DIRS += esp-eth
endif
ifneq (, $(filter esp_now, $(USEMODULE)))
DIRS += esp-now
endif
ifneq (, $(filter esp_wifi, $(USEMODULE)))
DIRS += esp-wifi
endif
include $(RIOTBASE)/Makefile.base

102
cpu/esp32/Makefile.dep Normal file
View File

@ -0,0 +1,102 @@
# additional modules dependencies
ifneq (,$(filter esp_eth,$(USEMODULE)))
USEMODULE += esp_idf_eth
USEMODULE += esp_idf_eth_phy
USEMODULE += gnrc
USEMODULE += netdev_eth
USEMODULE += netopt
USEMODULE += riot_freertos
INCLUDES += -I$(RIOTCPU)/$(CPU)/vendor/esp-idf/include/ethernet
INCLUDES += -I$(ESP32_SDK_DIR)/components/ethernet/include
endif
ifneq (,$(filter esp_now,$(USEMODULE)))
$(eval GNRC_NETIF_NUMOF=$(shell echo $$(($(GNRC_NETIF_NUMOF)+1))))
USEMODULE += esp_wifi_any
USEMODULE += gnrc
USEMODULE += netopt
endif
ifneq (,$(filter esp_wifi,$(USEMODULE)))
$(eval GNRC_NETIF_NUMOF=$(shell echo $$(($(GNRC_NETIF_NUMOF)+1))))
USEMODULE += esp_wifi_any
USEMODULE += gnrc
USEMODULE += netopt
USEMODULE += netdev_eth
endif
ifneq (,$(filter esp_wifi_any,$(USEMODULE)))
# add additional modules used for any WiFi interface
USEMODULE += esp_idf_wpa_supplicant_crypto
USEMODULE += esp_idf_wpa_supplicant_port
USEMODULE += esp_idf_nvs_flash
# crypto and hashes produce symbol conflicts with esp_idf_wpa_supplicant_crypto
DISABLE_MODULE += crypto
DISABLE_MODULE += hashes
endif
ifneq (,$(filter esp_i2c_hw,$(USEMODULE)))
USEMODULE += xtimer
else
# PLEASE NOTE: because of the very poor and faulty hardware implementation
# we use software implementation by default for the moment (if module
# esp_i2c_hw is not explicitly used)
USEMODULE += esp_i2c_sw
endif
ifneq (, $(filter esp_spi_ram, $(DISABLE_MODULE)))
USEMODULE := $(filter-out esp_spi_ram, $(USEMODULE))
endif
# each device has SPI flash memory, but must be explicitly enabled
ifneq (,$(filter esp_spiffs,$(USEMODULE)))
USEMODULE += spiffs
USEMODULE += vfs
export SPIFFS_STD_OPTION = -std=c99
INCLUDES += -I$(RIOTBASE)/sys/include
INCLUDES += -I$(RIOTBASE)/sys/posix/include
endif
ifneq (,$(filter ndn-riot,$(USEPKG)))
USEMODULE += crypto
USEMODULE += cipher_modes
endif
ifneq (,$(findstring posix,$(USEMODULE)))
USEMODULE += newlib_syscalls_default
endif
ifneq (,$(filter shell,$(USEMODULE)))
USEMODULE += newlib_syscalls_default
USEMODULE += xtimer
endif
ifneq (,$(filter xtimer,$(USEMODULE)))
USEMODULE += newlib_syscalls_default
endif
ifneq (,$(filter newlib_syscalls_default,$(USEMODULE)))
USEMODULE += stdio_uart
endif
# if SPI RAM is enabled, ESP-IDF heap and quot flash mode have to be used
ifneq (,$(filter esp_spi_ram,$(USEMODULE)))
USEMODULE += esp_idf_heap
export FLASH_MODE = qout
CFLAGS += -DFLASH_MODE_QOUT=1
else
ifeq ($(FLASH_MODE), qio)
CFLAGS += -DFLASH_MODE_QIO=1
endif
ifeq ($(FLASH_MODE), qout)
CFLAGS += -DFLASH_MODE_QOUT=1
endif
ifeq ($(FLASH_MODE), dio)
CFLAGS += -DFLASH_MODE_DIO=1
endif
ifeq ($(FLASH_MODE), dout)
CFLAGS += -DFLASH_MODE_DOUT=1
endif
endif

View File

@ -0,0 +1,6 @@
# Features that are provided by CPU independent on the board
FEATURES_PROVIDED += periph_cpuid
FEATURES_PROVIDED += periph_hwrng
FEATURES_PROVIDED += periph_pm
FEATURES_PROVIDED += periph_rtc
FEATURES_PROVIDED += periph_timer

191
cpu/esp32/Makefile.include Normal file
View File

@ -0,0 +1,191 @@
# check some environment variables first
ifndef ESP32_SDK_DIR
$(info ESP32_SDK_DIR should be defined as /path/to/esp-idf directory)
$(info ESP32_SDK_DIR is set by default to /opt/esp/esp-idf)
export ESP32_SDK_DIR=/opt/esp/esp-idf
endif
# DEFAULT compile configuration
# FLASH_MODE=[ dout | dio | qout | qio ]
# use flash mode dout by default to keep GPIO9 and GPIO10 free for use
export FLASH_MODE ?= dout
# enable GDBSTUP for debugging on exceptions
ifeq ($(ENABLE_GDBSTUB), 1)
USEMODULE += esp_gdbstub
endif
# enable GDB for compilation with debug info
ifeq ($(ENABLE_GDB), 1)
USEMODULE += esp_gdb
endif
# enable modules at command line for testing
ifneq ($(USE_MODULES), )
USEMODULE += $(USE_MODULES)
endif
# SPECIAL module dependencies
# cannot be done in Makefile.dep since Makefile.dep is included too late
ifneq (,$(filter esp_gdbstub,$(USEMODULE)))
USEMODULE += esp_gdb
endif
ifneq (,$(filter esp_now,$(USEMODULE)))
USEMODULE += esp_wifi_any
endif
ifneq (,$(filter esp_wifi,$(USEMODULE)))
USEMODULE += esp_wifi_any
endif
# ESP32 pseudomodules
PSEUDOMODULES += esp_eth_hw
PSEUDOMODULES += esp_gdb
PSEUDOMODULES += esp_gdbstub
PSEUDOMODULES += esp_hw_counter
PSEUDOMODULES += esp_i2c_sw
PSEUDOMODULES += esp_i2c_hw
PSEUDOMODULES += esp_idf_newlib
PSEUDOMODULES += esp_spi_ram
PSEUDOMODULES += esp_spiffs
PSEUDOMODULES += esp_wifi_any
export CPU ?= esp32
export TARGET_ARCH ?= xtensa-esp32-elf
export ESPTOOL ?= $(ESP32_SDK_DIR)/components/esptool_py/esptool/esptool.py
USEMODULE += core_thread_flags
USEMODULE += esp_idf
USEMODULE += esp_idf_driver
USEMODULE += esp_idf_esp32
USEMODULE += esp_idf_soc
USEMODULE += esp_idf_spi_flash
USEMODULE += log
USEMODULE += mtd
USEMODULE += periph
USEMODULE += periph_common
USEMODULE += pthread
USEMODULE += ps
USEMODULE += random
USEMODULE += xtensa
INCLUDES += -I$(RIOTCPU)/$(CPU)/vendor/
INCLUDES += -I$(RIOTCPU)/$(CPU)/vendor/esp-idf/include
INCLUDES += -I$(RIOTCPU)/$(CPU)/vendor/esp-idf/include/esp32
INCLUDES += -I$(RIOTCPU)/$(CPU)/vendor/esp-idf/include/heap
INCLUDES += -I$(RIOTCPU)/$(CPU)/vendor/esp-idf/include/spi_flash
INCLUDES += -I$(RIOTCPU)/$(CPU)/vendor/esp-idf/include/tcpip_adapter
INCLUDES += -I$(ESP32_SDK_DIR)/components/
INCLUDES += -I$(ESP32_SDK_DIR)/components/driver/include
INCLUDES += -I$(ESP32_SDK_DIR)/components/esp32/include
INCLUDES += -I$(ESP32_SDK_DIR)/components/heap/include
INCLUDES += -I$(ESP32_SDK_DIR)/components/soc/esp32/include
INCLUDES += -I$(ESP32_SDK_DIR)/components/soc/include
INCLUDES += -I$(RIOTBOARD)/common/$(CPU)/include
INCLUDES += -I$(RIOTCPU)/$(CPU)
CFLAGS += -DRIOT_OS
CFLAGS += -DSCHED_PRIO_LEVELS=32
CFLAGS += -DSDK_NOT_USED -DCONFIG_FREERTOS_UNICORE=1 -DESP_PLATFORM
CFLAGS += -DLOG_TAG_IN_BRACKETS
CFLAGS += -Wno-unused-parameter -Wformat=0
CFLAGS += -mlongcalls -mtext-section-literals -fstrict-volatile-bitfields
CFLAGS += -fdata-sections -fzero-initialized-in-bss
ASFLAGS += --longcalls --text-section-literals
ifneq ($(CONFIGS),)
CFLAGS += $(CONFIGS)
endif
# if any WiFi interface is used, the number of priority levels has to be 32
ifneq (,$(filter esp_wifi_any,$(USEMODULE)))
CFLAGS += -DSCHED_PRIO_LEVELS=32
endif
ifneq (,$(filter esp_gdb,$(USEMODULE)))
CFLAGS += -Og -ggdb -g3
else
CFLAGS += -Os
endif
ifeq ($(QEMU), 1)
CFLAGS += -DQEMU
endif
# LINKFLAGS += -Wl,--verbose
LINKFLAGS += -L$(ESP32_SDK_DIR)/components/esp32
LINKFLAGS += -L$(ESP32_SDK_DIR)/components/esp32/lib
LINKFLAGS += -Wl,--start-group
ifneq (,$(filter esp_wifi_any,$(USEMODULE)))
LINKFLAGS += $(BINDIR)/cpu.a
LINKFLAGS += $(BINDIR)/esp_idf.a
LINKFLAGS += $(BINDIR)/esp_idf_esp32.a
LINKFLAGS += $(BINDIR)/esp_idf_nvs_flash.a
LINKFLAGS += $(BINDIR)/esp_idf_spi_flash.a
LINKFLAGS += $(BINDIR)/riot_freertos.a
LINKFLAGS += -lcore -lrtc -lnet80211 -lpp -lsmartconfig -lcoexist
LINKFLAGS += -lwps -lwpa -lwpa2 -lespnow -lmesh -lphy -lstdc++
endif
ifneq (,$(filter pthread,$(USEMODULE)))
LINKFLAGS += $(BINDIR)/core.a
LINKFLAGS += $(BINDIR)/pthread.a
endif
LINKFLAGS += -lhal -lc -lg
LINKFLAGS += -Wl,--end-group
LINKFLAGS += -L$(RIOTCPU)/$(CPU)/ld/
LINKFLAGS += -T$(RIOTCPU)/$(CPU)/ld/esp32.ld
LINKFLAGS += -T$(RIOTCPU)/$(CPU)/ld/esp32.common.ld
LINKFLAGS += -T$(RIOTCPU)/$(CPU)/ld/esp32.peripherals.ld
LINKFLAGS += -T$(RIOTCPU)/$(CPU)/ld/esp32.rom.ld
LINKFLAGS += -T$(RIOTCPU)/$(CPU)/ld/esp32.rom.nanofmt.ld
LINKFLAGS += -nostdlib -lgcc -u putchar -Wl,-gc-sections
LINKFLAGS += -Wl,--warn-unresolved-symbols
# configure preflasher to convert .elf to .bin before flashing
FLASH_MODE ?= dout # FIX configuration, DO NOT CHANGE
FLASH_FREQ = 40m # FIX configuration, DO NOT CHANGE
FLASH_SIZE ?= 16m
export PREFLASHER = $(ESPTOOL)
export PREFFLAGS = --chip esp32 elf2image
export PREFFLAGS += -fm $(FLASH_MODE) -fs $(FLASH_SIZE) -ff $(FLASH_FREQ)
export PREFFLAGS += -o $(ELFFILE).bin $(ELFFILE);
export PREFFLAGS += echo "" > $(BINDIR)/partitions.csv;
export PREFFLAGS += echo "nvs, data, nvs, 0x9000, 0x6000" >> $(BINDIR)/partitions.csv;
export PREFFLAGS += echo "phy_init, data, phy, 0xf000, 0x1000" >> $(BINDIR)/partitions.csv;
export PREFFLAGS += echo -n "factory, app, factory, 0x10000, " >> $(BINDIR)/partitions.csv;
export PREFFLAGS += ls -l $(ELFFILE).bin | awk '{ print $$5 }' >> $(BINDIR)/partitions.csv;
export PREFFLAGS += python $(RIOTCPU)/$(CPU)/gen_esp32part.py --disable-sha256sum
export PREFFLAGS += --verify $(BINDIR)/partitions.csv $(BINDIR)/partitions.bin
export FLASHDEPS = preflash
# flasher configuration
ifeq ($(QEMU), 1)
export FLASHER = dd
export FFLAGS += if=/dev/zero bs=1M count=4 | tr "\\000" "\\377" > tmp.bin && cat tmp.bin |
export FFLAGS += head -c $$((0x1000)) |
export FFLAGS += cat - $(RIOTCPU)/$(CPU)/bin/bootloader.bin tmp.bin |
export FFLAGS += head -c $$((0x8000)) |
export FFLAGS += cat - $(BINDIR)/partitions.bin tmp.bin |
export FFLAGS += head -c $$((0x10000)) |
export FFLAGS += cat - $(ELFFILE).bin tmp.bin |
export FFLAGS += head -c $$((0x400000)) > $(BINDIR)/esp32flash.bin && rm tmp.bin &&
export FFLAGS += cp $(RIOTCPU)/$(CPU)/bin/rom_0x3ff90000_0x00010000.bin $(BINDIR)/rom1.bin &&
export FFLAGS += cp $(RIOTCPU)/$(CPU)/bin/rom_0x40000000_0x000c2000.bin $(BINDIR)/rom.bin
else
export PROGRAMMER_SPEED ?= 460800
export FLASHER = $(ESPTOOL)
export FFLAGS += --chip esp32 -p $(PORT) -b $(PROGRAMMER_SPEED)
export FFLAGS += --before default_reset --after hard_reset write_flash
export FFLAGS += -z -fm $(FLASH_MODE) -fs detect -ff $(FLASH_FREQ)
export FFLAGS += 0x1000 $(RIOTCPU)/$(CPU)/bin/bootloader.bin
export FFLAGS += 0x8000 $(BINDIR)/partitions.bin
export FFLAGS += 0x10000 $(ELFFILE).bin
endif

1129
cpu/esp32/README.md Normal file

File diff suppressed because it is too large Load Diff

1
cpu/esp32/bin/blank.bin Normal file
View File

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

Binary file not shown.

BIN
cpu/esp32/bin/bootloader.elf Executable file

Binary file not shown.

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

Binary file not shown.

Binary file not shown.

Binary file not shown.

1144
cpu/esp32/doc.txt Normal file

File diff suppressed because it is too large Load Diff

View File

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

28
cpu/esp32/esp-can/doc.txt Normal file
View File

@ -0,0 +1,28 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup cpu_esp32_esp_can ESP32 CAN device driver
* @ingroup cpu_esp32
* @brief CAN device driver implementation for ESP32
* @author Gunar Schorcht <gunar@schorcht.net>
The ESP32 intregates a CAN controller which is compatible with the NXP SJA1000 CAN controller. Thus, it is CAN 2.0B specification compliant and supports two message formats:
- Base frame format (11-bit ID)
- Extended frame format (29-bit ID)
@note
- ESP32 CAN does not support CAN-FD and is not CAN-FD tolerant.
- ESP32 CAN does not support SJA1000's sleep mode and wake-up functionality.
As with the SJA1000, the ESP32 CAN controller provides only the data link layer and physical layer signaling sublayer. Therefore, depending on the physical layer requirements, an external transceiver module is required which converts the CAN-RX and CAN-TX signals of the ESP32 into CAN_H and CAN_L bus signals, e.g., the MCP2551 or SN65HVD23X transceiver for compatibility with ISO 11898-2.
The ```esp_can``` module realizes a low-level CAN driver interface for the ESP32 CAN controller and provides a CAN DLL device that can be used in RIOT's CAN protocol stack. It uses the ESP32 CAN controller in SJA1000 PeliCAN mode. Please refer the [SJA1000 Datasheet](https://www.nxp.com/documents/data_sheet/SJA1000.pdf) for detailed information about the CAN controller and its programming.
*/

1025
cpu/esp32/esp-can/esp_can.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,33 @@
/*
* 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_esp32_esp_can
* @brief CAN device driver implementation for ESP32
*
* This module realizes the low-level CAN driver interface for the ESP32 CAN
* controller which is compatible with the NXP SJA1000 CAN controller.
*
* @author Gunar Schorcht <gunar@schorcht.net>
* @file
* @{
*/
#ifndef ESP_CAN_H
#define ESP_CAN_H
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
#endif /* ESP_CAN_H */
/** @} */

View File

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

33
cpu/esp32/esp-eth/doc.txt Normal file
View File

@ -0,0 +1,33 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup cpu_esp32_esp_eth ESP32 Ethernet netdev interface
* @ingroup cpu_esp32
* @brief ESP32 ethernet network device driver
*
* This module realizes a netdev interface using the ESP32 Ethernet MAC
* module.
*
* @author Gunar Schorcht <gunar@schorcht.net>
ESP32 provides an <b>Ethernet MAC layer module (EMAC)</b> according to the IEEE 802.3 standard which can be used together with an external physical layer chip (PHY) to realize a 100/10 Mbps Ethernet interface. Supported PHY chips are the Microchip LAN8710/LAN8720 and the Texas Instruments TLK110.
The RIOT port for ESP32 realizes with module ```esp_eth``` a ```netdev``` driver for the EMAC which uses RIOT's standard Ethernet interface.
If the board has one of the supported PHY layer chips connected to the ESP32, the ```esp_eth``` module should be enabled by default in board's ```Makefile.dep``` when module ```netdev_default``` is used.
```
ifneq (,$(filter netdev_default gnrc_netdev_default,$(USEMODULE)))
USEMODULE += esp_eth
endif
```
Otherwise, the application has to add the ```esp_eth``` module in its makefile when needed.
@note
The board has to have one of the supported PHY modules to be able to use the Ethernet MAC module.
*/

View File

@ -0,0 +1,428 @@
/*
* 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_esp32_esp_eth
* @{
*
* @file
* @brief Netdev interface for the ESP32 Ethernet MAC (EMAC) module
*
* @author Gunar Schorcht <gunar@schorcht.net>
*/
#ifdef MODULE_ESP_ETH
#define ENABLE_DEBUG (0)
#include "debug.h"
#include "log.h"
#include "tools.h"
#include <string.h>
#include <assert.h>
#include <errno.h>
#include "net/gnrc/netif/ethernet.h"
#include "net/gnrc.h"
#include "net/ethernet.h"
#include "net/netdev/eth.h"
#include "xtimer.h"
#include "esp_common.h"
#include "esp_attr.h"
#include "irq_arch.h"
#include "esp_eth_params.h"
#include "esp_eth_netdev.h"
#include "esp32/esp_event_loop.h"
#if !defined(EMAC_PHY_SMI_MDC_PIN) || !defined(EMAC_PHY_SMI_MDIO_PIN)
#error Board definition does not provide EMAC configuration
#endif
#define SYSTEM_EVENT_ETH_RX_DONE (SYSTEM_EVENT_MAX + 1)
#define SYSTEM_EVENT_ETH_TX_DONE (SYSTEM_EVENT_MAX + 2)
#define EMAC_PHY_CLOCK_DELAY (500 * USEC_PER_MSEC) /* given in us */
#ifdef EMAC_PHY_LAN8720
#include "eth_phy/phy_lan8720.h"
#define EMAC_ETHERNET_PHY_CONFIG phy_lan8720_default_ethernet_config
#endif
#ifdef EMAC_PHY_TLK110
#include "eth_phy/phy_tlk110.h"
#define EMAC_ETHERNET_PHY_CONFIG phy_tlk110_default_ethernet_config
#endif
/**
* There is only one ESP-ETH device. We define it as static device variable
* to have accesss to the device inside ESP-ETH interrupt routines which do
* not provide an argument that could be used as pointer to the ESP-ETH
* device which triggers the interrupt.
*/
static esp_eth_netdev_t _esp_eth_dev;
/* device thread stack */
static char _esp_eth_stack[ESP_ETH_STACKSIZE];
static void _eth_gpio_config_rmii(void)
{
DEBUG("%s\n", __func__);
/*
* RMII data pins are fixed:
* TXD0 = GPIO19
* TXD1 = GPIO22
* TX_EN = GPIO21
* RXD0 = GPIO25
* RXD1 = GPIO26
* CLK = GPIO0
*/
phy_rmii_configure_data_interface_pins();
/* SMI pins can be configured in board definition */
phy_rmii_smi_configure_pins(EMAC_PHY_SMI_MDC_PIN, EMAC_PHY_SMI_MDIO_PIN);
}
static esp_err_t IRAM_ATTR _eth_input_callback(void *buffer, uint16_t len, void *eb)
{
DEBUG("%s: buf=%p len=%d eb=%p\n", __func__, buffer, len, eb);
CHECK_PARAM_RET (buffer != NULL, -EINVAL);
CHECK_PARAM_RET (len <= ETHERNET_DATA_LEN, -EINVAL);
mutex_lock(&_esp_eth_dev.dev_lock);
memcpy(_esp_eth_dev.rx_buf, buffer, len);
_esp_eth_dev.rx_len = len;
_esp_eth_dev.event = SYSTEM_EVENT_ETH_RX_DONE;
_esp_eth_dev.netdev.event_callback(&_esp_eth_dev.netdev, NETDEV_EVENT_ISR);
mutex_unlock(&_esp_eth_dev.dev_lock);
return ESP_OK;
}
#if EMAC_PHY_POWER_PIN != GPIO_UNDEF
static void _esp_eth_phy_power_enable_gpio(bool enable)
{
assert(EMAC_ETHERNET_PHY_CONFIG.phy_power_enable);
if (!enable) {
EMAC_ETHERNET_PHY_CONFIG.phy_power_enable(false);
}
gpio_init(EMAC_PHY_POWER_PIN, GPIO_OUT);
gpio_write(EMAC_PHY_POWER_PIN, enable);
xtimer_usleep (USEC_PER_MSEC);
if (enable) {
EMAC_ETHERNET_PHY_CONFIG.phy_power_enable(true);
}
}
#endif
static int _esp_eth_init(netdev_t *netdev)
{
DEBUG("%s: netdev=%p\n", __func__, netdev);
esp_eth_netdev_t* dev = (esp_eth_netdev_t*)netdev;
mutex_lock(&dev->dev_lock);
esp_err_t ret = ESP_OK;
eth_config_t config = EMAC_ETHERNET_PHY_CONFIG;
/* Set configuration */
config.phy_addr = EMAC_PHY_ADDRESS;
config.clock_mode = EMAC_PHY_CLOCK_MODE;
config.gpio_config = _eth_gpio_config_rmii;
config.tcpip_input = _eth_input_callback;
#if EMAC_PHY_POWER_PIN != GPIO_UNDEF
/*
* Replace the default 'power enable' function with an example-specific
* one that toggles a power GPIO.
*/
config.phy_power_enable = phy_device_power_enable_via_gpio;
#endif
/* initialize EMAC with config */
if (ret == ESP_OK && (ret = esp_eth_init(&config)) != ESP_OK) {
LOG_TAG_ERROR("esp_eth", "initialization failed");
}
/*
* after activating clock logic it can take some time before we can
* enable EMAC
*/
xtimer_usleep (EMAC_PHY_CLOCK_DELAY);
if (ret == ESP_OK && (ret = esp_eth_enable()) != ESP_OK) {
LOG_TAG_ERROR("esp_eth", "enable failed");
}
#ifdef MODULE_NETSTATS_L2
memset(&netdev->stats, 0, sizeof(netstats_t));
#endif
mutex_unlock(&dev->dev_lock);
return (ret == ESP_OK) ? 0 : -1;
}
static int _esp_eth_send(netdev_t *netdev, const iolist_t *iolist)
{
DEBUG("%s: netdev=%p iolist=%p\n", __func__, netdev, iolist);
CHECK_PARAM_RET (netdev != NULL, -ENODEV);
CHECK_PARAM_RET (iolist != NULL, -EINVAL);
esp_eth_netdev_t* dev = (esp_eth_netdev_t*)netdev;
if (!_esp_eth_dev.link_up) {
DEBUG("%s: link is down\n", __func__);
return -ENODEV;
}
mutex_lock(&dev->dev_lock);
dev->tx_len = 0;
/* 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_DATA_LEN) {
mutex_unlock(&dev->dev_lock);
return -EOVERFLOW;
}
memcpy (dev->tx_buf + dev->tx_len, iol->iol_base, iol->iol_len);
dev->tx_len += iol->iol_len;
}
#if ENABLE_DEBUG
printf ("%s: send %d byte\n", __func__, dev->tx_len);
/* esp_hexdump (dev->tx_buf, dev->tx_len, 'b', 16); */
#endif
int ret = 0;
/* send the the packet to the peer(s) mac address */
if (esp_eth_tx(dev->tx_buf, dev->tx_len) == ESP_OK) {
#ifdef MODULE_NETSTATS_L2
netdev->stats.tx_success++;
netdev->stats.tx_bytes += dev->tx_len;
#endif
netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE);
}
else {
#ifdef MODULE_NETSTATS_L2
netdev->stats.tx_failed++;
#endif
ret = -EIO;
}
mutex_unlock(&dev->dev_lock);
return ret;
}
static int _esp_eth_recv(netdev_t *netdev, void *buf, size_t len, void *info)
{
DEBUG("%s: netdev=%p buf=%p len=%u info=%p\n",
__func__, netdev, buf, len, info);
CHECK_PARAM_RET (netdev != NULL, -ENODEV);
esp_eth_netdev_t* dev = (esp_eth_netdev_t*)netdev;
mutex_lock(&dev->dev_lock);
uint8_t size = dev->rx_len;
if (!buf && !len) {
/* return the size without dropping received data */
mutex_unlock(&dev->dev_lock);
return size;
}
if (!buf && len) {
/* return the size and drop received data */
mutex_unlock(&dev->dev_lock);
dev->rx_len = 0;
return size;
}
if (buf && len && dev->rx_len) {
/* return the packet */
if (dev->rx_len > len) {
LOG_TAG_ERROR("esp_eth", "Not enough space in receive buffer "
"for %d byte\n", dev->rx_len);
mutex_unlock(&dev->dev_lock);
return -ENOBUFS;
}
#if ENABLE_DEBUG
/* esp_hexdump (dev->rx_buf, dev->rx_len, 'b', 16); */
#endif
/* copy received date and reset the receive length */
memcpy(buf, dev->rx_buf, dev->rx_len);
dev->rx_len = 0;
#ifdef MODULE_NETSTATS_L2
netdev->stats.rx_count++;
netdev->stats.rx_bytes += size;
#endif
mutex_unlock(&dev->dev_lock);
return size;
}
mutex_unlock(&dev->dev_lock);
return -EINVAL;
}
static int _esp_eth_get(netdev_t *netdev, netopt_t opt, void *val, size_t max_len)
{
DEBUG("%s: netdev=%p opt=%s val=%p len=%u\n",
__func__, netdev, netopt2str(opt), val, max_len);
CHECK_PARAM_RET (netdev != NULL, -ENODEV);
CHECK_PARAM_RET (val != NULL, -EINVAL);
switch (opt) {
case NETOPT_ADDRESS:
assert(max_len == ETHERNET_ADDR_LEN);
esp_eth_get_mac((uint8_t *)val);
return ETHERNET_ADDR_LEN;
default:
return netdev_eth_get(netdev, opt, val, max_len);
}
}
static int _esp_eth_set(netdev_t *netdev, netopt_t opt, const void *val, size_t max_len)
{
DEBUG("%s: netdev=%p opt=%s val=%p len=%u\n",
__func__, netdev, netopt2str(opt), val, max_len);
CHECK_PARAM_RET (netdev != NULL, -ENODEV);
CHECK_PARAM_RET (val != NULL, -EINVAL);
switch (opt) {
case NETOPT_ADDRESS:
assert(max_len == ETHERNET_ADDR_LEN);
esp_eth_set_mac((uint8_t *)val);
return ETHERNET_ADDR_LEN;
default:
return netdev_eth_set(netdev, opt, val, max_len);
}
}
static void _esp_eth_isr(netdev_t *netdev)
{
DEBUG("%s: netdev=%p\n", __func__, netdev);
CHECK_PARAM (netdev != NULL);
esp_eth_netdev_t *dev = (esp_eth_netdev_t *) netdev;
switch (dev->event) {
case SYSTEM_EVENT_ETH_RX_DONE:
/* if data were received */
if (dev->rx_len) {
dev->netdev.event_callback(netdev, NETDEV_EVENT_RX_COMPLETE);
}
break;
case SYSTEM_EVENT_ETH_CONNECTED:
dev->netdev.event_callback(netdev, NETDEV_EVENT_LINK_UP);
break;
case SYSTEM_EVENT_ETH_DISCONNECTED:
dev->netdev.event_callback(netdev, NETDEV_EVENT_LINK_DOWN);
break;
default:
break;
}
_esp_eth_dev.event = SYSTEM_EVENT_MAX; /* no event */
return;
}
static esp_err_t IRAM_ATTR _esp_system_event_handler(void *ctx, system_event_t *event)
{
switch(event->event_id) {
case SYSTEM_EVENT_ETH_START:
DEBUG("%s: Ethernet started\n", __func__);
break;
case SYSTEM_EVENT_ETH_STOP:
DEBUG("%s: Ethernet started\n", __func__);
break;
case SYSTEM_EVENT_ETH_CONNECTED:
DEBUG("%s Ethernet link up\n", __func__);
_esp_eth_dev.link_up = true;
if (SYSTEM_EVENT_MAX) {
_esp_eth_dev.event = SYSTEM_EVENT_ETH_CONNECTED;
_esp_eth_dev.netdev.event_callback(&_esp_eth_dev.netdev, NETDEV_EVENT_ISR);
}
break;
case SYSTEM_EVENT_ETH_DISCONNECTED:
DEBUG("%s: Ethernet link up\n", __func__);
_esp_eth_dev.link_up = false;
if (SYSTEM_EVENT_MAX) {
_esp_eth_dev.event = SYSTEM_EVENT_ETH_DISCONNECTED;
_esp_eth_dev.netdev.event_callback(&_esp_eth_dev.netdev, NETDEV_EVENT_ISR);
}
break;
default:
DEBUG("%s: event=%d\n", __func__, event->event_id);
break;
}
return ESP_OK;
}
static const netdev_driver_t _esp_eth_driver =
{
.send = _esp_eth_send,
.recv = _esp_eth_recv,
.init = _esp_eth_init,
.isr = _esp_eth_isr,
.get = _esp_eth_get,
.set = _esp_eth_set,
};
extern esp_err_t esp_system_event_add_handler (system_event_cb_t handler,
void *arg);
void auto_init_esp_eth (void)
{
LOG_TAG_INFO("esp_eth", "initializing ESP32 Ethernet MAC (EMAC) device\n");
/* initialize locking */
mutex_init(&_esp_eth_dev.dev_lock);
/* init esp system event loop */
esp_system_event_add_handler(_esp_system_event_handler, NULL);
/* set the netdev driver */
_esp_eth_dev.netdev.driver = &_esp_eth_driver;
/* initialize netdev data structure */
_esp_eth_dev.event = SYSTEM_EVENT_MAX; /* no event */
_esp_eth_dev.link_up = false;
_esp_eth_dev.rx_len = 0;
_esp_eth_dev.tx_len = 0;
_esp_eth_dev.netif = gnrc_netif_ethernet_create(_esp_eth_stack,
ESP_ETH_STACKSIZE,
ESP_ETH_PRIO,
"netdev-esp-eth",
(netdev_t *)&_esp_eth_dev);
}
#endif /* MODULE_ESP_ETH */
/**@}*/

View File

@ -0,0 +1,60 @@
/*
* 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_esp32_esp_eth
* @{
*
* @file
* @brief Netdev interface for the ESP32 Ethernet MAC module
*
* @author Gunar Schorcht <gunar@schorcht.net>
*/
#ifndef ESP_ETH_NETDEV_H
#define ESP_ETH_NETDEV_H
#include "net/netdev.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Reference to the netdev device driver struct
*/
extern const netdev_driver_t esp_eth_driver;
/**
* @brief Device descriptor for ESP-ETH devices
*/
typedef struct
{
netdev_t netdev; /**< netdev parent struct */
uint8_t rx_len; /**< number of bytes received */
uint8_t rx_buf[ETHERNET_DATA_LEN]; /**< receive buffer */
uint8_t tx_len; /**< number of bytes in transmit buffer */
uint8_t tx_buf[ETHERNET_DATA_LEN]; /**< transmit buffer */
uint32_t event; /**< received event */
bool link_up; /**< indicates whether link is up */
gnrc_netif_t* netif; /**< reference to the corresponding netif */
mutex_t dev_lock; /**< device is already in use */
} esp_eth_netdev_t;
#ifdef __cplusplus
}
#endif
#endif /* ESP_ETH_NETDEV_H */
/** @} */

View File

@ -0,0 +1,49 @@
/*
* 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_esp32_esp_eth
* @{
*
* @file
* @brief Parameters for the netdev interface for ESP32 Ethernet MAC module
*
* @author Gunar Schorcht <gunar@schorcht.net>
*/
#ifndef ESP_ETH_PARAMS_H
#define ESP_ETH_PARAMS_H
#if defined(MODULE_ESP_ETH) || defined(DOXYGEN)
/**
* @name Set default configuration parameters for the ESP-ETH netdev driver
* @{
*/
#ifndef ESP_ETH_STACKSIZE
/** The size of the stack used for the ESP-ETH netdev driver thread */
#define ESP_ETH_STACKSIZE THREAD_STACKSIZE_DEFAULT
#endif
#ifndef ESP_ETH_PRIO
/** The priority of the ESP-ETH netdev driver thread */
#define ESP_ETH_PRIO GNRC_NETIF_PRIO
#endif
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
#endif /* MODULE_ESP_ETH || DOXYGEN */
#endif /* ESP_ETH_PARAMS_H */
/**@}*/

View File

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

42
cpu/esp32/esp-now/dox.txt Normal file
View File

@ -0,0 +1,42 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup cpu_esp32_esp_now ESP-NOW netdev interface
* @ingroup cpu_esp32
* @brief WiFi based ESP-NOW network device driver
*
* @author Gunar Schorcht <gunar@schorcht.net>
This module realizes a netdev interface using Espressif's ESP-NOW technology which uses the built-in WiFi module.
With ESP-NOW, the ESP32 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.
The RIOT port for ESP32 implements in module ```esp_now``` a ```netdev``` driver which uses ESP-NOW to provide a link layer interface to a meshed network of ESP32 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 the ```esp_idf_wpa_supplicant_crypto``` module 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.<br>
```
USEMODULE += esp_now
```
For ESP-NOW, ESP32 nodes are used in WiFi SoftAP + Station mode to advertise their SSID and become visible to other ESP32 nodes. The SSID of an ESP32 node is the concatenation of the prefix ```RIOT_ESP_``` with the MAC address of its SoftAP WiFi interface. The driver periodically scans all visible ESP32 nodes.
The following parameters are defined for ESP-NOW nodes. These parameters can be overriden by [application-specific board configurations](#esp32_application_specific_board_configuration).
<center>
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_PASSPHRASE | ThisistheRIOTporttoESP | Defines the passphrase (max. 64 chars) that is used for the SoftAP interface of an 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.
</center>
*/

View File

@ -0,0 +1,872 @@
/*
* 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_esp32_esp_now
* @{
*
* @file
* @brief Netdev interface for the ESP-NOW WiFi P2P protocol
*
* @author Gunar Schorcht <gunar@schorcht.net>
*/
#ifdef MODULE_ESP_NOW
#define ENABLE_DEBUG (0)
#include "debug.h"
#include "log.h"
#include "tools.h"
#include <string.h>
#include <assert.h>
#include <errno.h>
#include "net/gnrc/netif/raw.h"
#include "net/gnrc.h"
#include "xtimer.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 "irq_arch.h"
#include "nvs_flash/include/nvs_flash.h"
#include "esp_now_params.h"
#include "esp_now_netdev.h"
#include "net/ipv6/hdr.h"
#include "net/gnrc/ipv6/nib.h"
#define ESP_NOW_UNICAST 1
#define ESP_NOW_WIFI_STA 1
#define ESP_NOW_WIFI_SOFTAP 2
#define ESP_NOW_WIFI_STA_SOFTAP (ESP_NOW_WIFI_STA + ESP_NOW_WIFI_SOFTAP)
#define ESP_NOW_AP_PREFIX "RIOT_ESP_"
#define ESP_NOW_AP_PREFIX_LEN (strlen(ESP_NOW_AP_PREFIX))
/**
* There is only one ESP-NOW device. We define it as static device variable
* to have accesss to the device inside ESP-NOW interrupt routines which do
* not provide an argument that could be used as pointer to the ESP-NOW
* device which triggers the interrupt.
*/
static esp_now_netdev_t _esp_now_dev;
static const netdev_driver_t _esp_now_driver;
/* device thread stack */
static char _esp_now_stack[ESP_NOW_STACKSIZE];
static inline int _get_mac_from_iid(uint8_t *iid, uint8_t *mac)
{
CHECK_PARAM_RET (iid != NULL, -EINVAL);
CHECK_PARAM_RET (mac != NULL, -EINVAL);
/* interface id according to */
/* https://tools.ietf.org/html/rfc4291#section-2.5.1 */
mac[0] = iid[0] ^ 0x02; /* invert bit1 */
mac[1] = iid[1];
mac[2] = iid[2];
mac[3] = iid[5];
mac[4] = iid[6];
mac[5] = iid[7];
return 0;
}
#if ESP_NOW_UNICAST
static xtimer_t _esp_now_scan_peers_timer;
static bool _esp_now_scan_peers_done = false;
static bool _esp_now_add_peer (uint8_t* bssid, uint8_t channel, uint8_t* key)
{
if (esp_now_is_peer_exist(bssid)) {
return false;
}
esp_now_peer_info_t peer = {};
memcpy(peer.peer_addr, bssid, ESP_NOW_ETH_ALEN);
peer.channel = channel;
peer.ifidx = ESP_IF_WIFI_AP;
if (esp_now_params.key) {
peer.encrypt = true;
memcpy(peer.lmk, esp_now_params.key, ESP_NOW_KEY_LEN);
}
esp_err_t ret = esp_now_add_peer(&peer);
DEBUG("esp_now_add_peer node %02x:%02x:%02x:%02x:%02x:%02x "
"added with return value %d\n",
bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5], ret);
return (ret == ESP_OK);
}
#define ESP_NOW_APS_BLOCK_SIZE 8 /* has to be power of two */
static wifi_ap_record_t* aps = NULL;
static uint32_t aps_size = 0;
static void IRAM_ATTR esp_now_scan_peers_done (void)
{
mutex_lock(&_esp_now_dev.dev_lock);
esp_err_t ret;
uint16_t ap_num;
ret = esp_wifi_scan_get_ap_num(&ap_num);
DEBUG("wifi_scan_get_ap_num ret=%d num=%d\n", ret ,ap_num);
if (ret == ESP_OK && ap_num) {
uint32_t state;
/* reallocation of memory must not be disturbed */
critical_enter_var(state);
/* allocate memory for APs record list blockwise and fetch them the list */
if (ap_num > aps_size) {
if (aps) {
/* free allocated AP record list memory */
aps_size = 0;
free (aps);
}
/* allocate new memory */
aps_size = (ap_num & ~(ESP_NOW_APS_BLOCK_SIZE - 1)) + ESP_NOW_APS_BLOCK_SIZE;
aps = malloc(sizeof(wifi_ap_record_t) * aps_size);
ap_num = aps_size;
}
critical_exit_var(state);
ret = esp_wifi_scan_get_ap_records(&ap_num, aps);
DEBUG("wifi_scan_get_aps ret=%d num=%d\n", ret, ap_num);
critical_enter_var(state);
/* iterate over APs records */
for (uint16_t i = 0; i < ap_num; i++) {
/* check whether the AP is an ESP_NOW node which is not already a peer */
if (strncmp((char*)aps[i].ssid, ESP_NOW_AP_PREFIX, ESP_NOW_AP_PREFIX_LEN) == 0 &&
!esp_now_is_peer_exist(aps[i].bssid)) {
/* add the AP as peer */
_esp_now_add_peer(aps[i].bssid, aps[i].primary, esp_now_params.key);
}
}
critical_exit_var(state);
}
#if ENABLE_DEBUG
esp_now_peer_num_t peer_num;
esp_now_get_peer_num (&peer_num);
DEBUG("associated peers total=%d, encrypted=%d\n",
peer_num.total_num, peer_num.encrypt_num);
#endif
_esp_now_scan_peers_done = true;
/* set the time for next scan */
xtimer_set (&_esp_now_scan_peers_timer, esp_now_params.scan_period);
mutex_unlock(&_esp_now_dev.dev_lock);
}
static void esp_now_scan_peers_start (void)
{
DEBUG("%s\n", __func__);
wifi_scan_config_t scan_cfg = {
.ssid = NULL,
.bssid = NULL,
.channel = esp_now_params.channel,
.show_hidden = true,
.scan_type = WIFI_SCAN_TYPE_ACTIVE,
.scan_time.active.min = 0,
.scan_time.active.max = 120 /* TODO tune value */
};
esp_wifi_scan_start(&scan_cfg, false);
}
#define ESP_NOW_EVENT_SCAN_PEERS 1
static kernel_pid_t esp_now_event_handler_pid;
char esp_now_event_handler_stack [THREAD_STACKSIZE_DEFAULT];
static void *esp_now_event_handler (void *arg)
{
msg_t event;
while (1) {
msg_receive(&event);
switch (event.content.value) {
case ESP_NOW_EVENT_SCAN_PEERS:
esp_now_scan_peers_start ();
break;
}
}
return NULL;
}
static void IRAM_ATTR esp_now_scan_peers_timer_cb (void* arg)
{
DEBUG("%s\n", __func__);
static msg_t event = { .content = { .value = ESP_NOW_EVENT_SCAN_PEERS } };
msg_send(&event, esp_now_event_handler_pid);
}
#else
static const uint8_t _esp_now_mac[6] = { 0x82, 0x73, 0x79, 0x84, 0x79, 0x83 }; /* RIOTOS */
#endif /* ESP_NOW_UNICAST */
static IRAM_ATTR void esp_now_recv_cb (const uint8_t *mac, const uint8_t *data, int len)
{
#if ESP_NOW_UNICAST
if (!_esp_now_scan_peers_done) {
/* if peers are not scanned, we cannot receive anything */
return;
}
#endif
if (_esp_now_dev.rx_len) {
/* there is already a packet in receive buffer, we drop the new one */
return;
}
critical_enter();
#if 0 /* don't printf anything in ISR */
printf ("%s\n", __func__);
printf ("%s: received %d byte from %02x:%02x:%02x:%02x:%02x:%02x\n",
__func__, len,
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
esp_hexdump (data, len, 'b', 16);
#endif
_esp_now_dev.rx_len = len;
memcpy(_esp_now_dev.rx_buf, data, len);
memcpy(_esp_now_dev.rx_mac, mac, ESP_NOW_ADDR_LEN);
if (_esp_now_dev.netdev.event_callback) {
_esp_now_dev.netdev.event_callback((netdev_t*)&_esp_now_dev, NETDEV_EVENT_ISR);
}
critical_exit();
}
static int _esp_now_sending = 0;
static void IRAM_ATTR esp_now_send_cb (const uint8_t *mac, esp_now_send_status_t status)
{
DEBUG("%s: sent to %02x:%02x:%02x:%02x:%02x:%02x with status %d\n",
__func__,
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], status);
if (_esp_now_sending) {
_esp_now_sending--;
}
}
/*
* Event handler for esp system events.
*/
static esp_err_t IRAM_ATTR _esp_system_event_handler(void *ctx, system_event_t *event)
{
switch(event->event_id) {
case SYSTEM_EVENT_STA_START:
DEBUG("%s WiFi started\n", __func__);
break;
case SYSTEM_EVENT_SCAN_DONE:
DEBUG("%s WiFi scan done\n", __func__);
esp_now_scan_peers_done();
break;
default:
break;
}
return ESP_OK;
}
/**
* Default WiFi configuration, overwrite them with your configs
*/
#ifndef CONFIG_WIFI_STA_SSID
#define CONFIG_WIFI_STA_SSID "RIOT_AP"
#endif
#ifndef CONFIG_WIFI_STA_PASSWORD
#define CONFIG_WIFI_STA_PASSWORD "ThisistheRIOTporttoESP"
#endif
#ifndef CONFIG_WIFI_STA_CHANNEL
#define CONFIG_WIFI_STA_CHANNEL 0
#endif
#define CONFIG_WIFI_STA_SCAN_METHOD WIFI_ALL_CHANNEL_SCAN
#define CONFIG_WIFI_STA_SORT_METHOD WIFI_CONNECT_AP_BY_SIGNAL
#define CONFIG_WIFI_STA_RSSI -127
#define CONFIG_WIFI_STA_AUTHMODE WIFI_AUTH_WPA_WPA2_PSK
#define CONFIG_WIFI_AP_PASSWORD ESP_NOW_SOFT_AP_PASSPHRASE
#define CONFIG_WIFI_AP_CHANNEL ESP_NOW_CHANNEL
#define CONFIG_WIFI_AP_AUTH WIFI_AUTH_WPA_WPA2_PSK
#define CONFIG_WIFI_AP_HIDDEN false
#define CONFIG_WIFI_AP_BEACON 100
#define CONFIG_WIFI_AP_MAX_CONN 4
extern esp_err_t esp_system_event_add_handler (system_event_cb_t handler,
void *arg);
static void esp_now_setup (esp_now_netdev_t* dev)
{
DEBUG("%s: %p\n", __func__, dev);
/*
* Init the WiFi driver. TODO It is not only required before ESP_NOW 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.
*/
extern portMUX_TYPE g_intr_lock_mux;
mutex_init(&g_intr_lock_mux);
esp_system_event_add_handler (_esp_system_event_handler, NULL);
esp_err_t result;
#if CONFIG_ESP32_WIFI_NVS_ENABLED
result = nvs_flash_init();
if (result != ESP_OK) {
LOG_TAG_ERROR("esp_now",
"nfs_flash_init failed with return value %d\n", result);
return;
}
#endif
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
result = esp_wifi_init(&cfg);
if (result != ESP_OK) {
LOG_TAG_ERROR("esp_now",
"esp_wifi_init failed with return value %d\n", result);
return;
}
#ifdef CONFIG_WIFI_COUNTRY
/* TODO */
#endif
/* we use predefined station configuration */
wifi_config_t wifi_config_sta = {
.sta = {
.ssid = CONFIG_WIFI_STA_SSID,
.password = CONFIG_WIFI_STA_PASSWORD,
.channel = CONFIG_WIFI_STA_CHANNEL,
.scan_method = CONFIG_WIFI_STA_SCAN_METHOD,
.sort_method = CONFIG_WIFI_STA_SORT_METHOD,
.threshold.rssi = CONFIG_WIFI_STA_RSSI,
.threshold.authmode = CONFIG_WIFI_STA_AUTHMODE
}
};
/* get SoftAP interface mac address and store it as device addresss */
esp_read_mac(dev->addr, ESP_MAC_WIFI_SOFTAP);
/* prepare the ESP_NOW configuration for SoftAP */
wifi_config_t wifi_config_ap = {};
strcpy ((char*)wifi_config_ap.ap.password, esp_now_params.softap_pass);
sprintf((char*)wifi_config_ap.ap.ssid, "%s%02x%02x%02x%02x%02x%02x",
ESP_NOW_AP_PREFIX,
dev->addr[0], dev->addr[1], dev->addr[2],
dev->addr[3], dev->addr[4], dev->addr[5]);
wifi_config_ap.ap.ssid_len = strlen((char*)wifi_config_ap.ap.ssid);
wifi_config_ap.ap.channel = esp_now_params.channel;
wifi_config_ap.ap.authmode = CONFIG_WIFI_AP_AUTH;
wifi_config_ap.ap.ssid_hidden = CONFIG_WIFI_AP_HIDDEN;
wifi_config_ap.ap.max_connection = CONFIG_WIFI_AP_MAX_CONN;
wifi_config_ap.ap.beacon_interval = CONFIG_WIFI_AP_BEACON;
/* set the WiFi interface to Station + SoftAP mode without DHCP */
result = esp_wifi_set_mode(WIFI_MODE_STA | WIFI_MODE_AP);
if (result != ESP_OK) {
LOG_TAG_ERROR("esp_now",
"esp_wifi_set_mode failed with return value %d\n",
result);
return;
}
/* set the Station and SoftAP configuration */
result = esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config_sta);
if (result != ESP_OK) {
LOG_TAG_ERROR("esp_now", "esp_wifi_set_config station failed with "
"return value %d\n", result);
return;
}
result = esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config_ap);
if (result != ESP_OK) {
LOG_TAG_ERROR("esp_now",
"esp_wifi_set_mode softap failed with return value %d\n",
result);
return;
}
/* start the WiFi driver */
result = esp_wifi_start();
if (result != ESP_OK) {
LOG_TAG_ERROR("esp_now",
"esp_wifi_start failed with return value %d\n", result);
return;
}
#if ESP_NOW_UNICAST==0 /* TODO */
/* all ESP-NOW nodes get the shared mac address on their station interface */
wifi_set_macaddr(STATION_IF, (uint8_t*)_esp_now_mac);
#endif
/* set the netdev driver */
dev->netdev.driver = &_esp_now_driver;
/* initialize netdev data structure */
dev->peers_all = 0;
dev->peers_enc = 0;
mutex_init(&dev->dev_lock);
/* initialize ESP-NOW and register callback functions */
result = esp_now_init();
if (result != ESP_OK) {
LOG_TAG_ERROR("esp_now", "esp_now_init failed with return value %d\n",
result);
return;
}
esp_now_register_send_cb (esp_now_send_cb);
esp_now_register_recv_cb (esp_now_recv_cb);
#if ESP_NOW_UNICAST
/* create the ESP_NOW event handler thread */
esp_now_event_handler_pid = thread_create(esp_now_event_handler_stack,
sizeof(esp_now_event_handler_stack),
ESP_NOW_PRIO + 1,
THREAD_CREATE_WOUT_YIELD |
THREAD_CREATE_STACKTEST,
esp_now_event_handler,
NULL, "net-esp-now-event");
/* timer for peer scan initialization */
_esp_now_scan_peers_done = false;
_esp_now_scan_peers_timer.callback = &esp_now_scan_peers_timer_cb;
_esp_now_scan_peers_timer.arg = dev;
/* execute the first scan */
esp_now_scan_peers_done();
#else /* ESP_NOW_UNICAST */
#if 0
int res = esp_now_add_peer((uint8_t*)_esp_now_mac, ESP_NOW_ROLE_COMBO,
esp_now_params.channel, NULL, 0);
DEBUG("%s: multicast node added %d\n", __func__, res);
#endif
#endif /* ESP_NOW_UNICAST */
}
static int _init(netdev_t *netdev)
{
DEBUG("%s: %p\n", __func__, netdev);
#ifdef MODULE_NETSTATS_L2
memset(&netdev->stats, 0x00, sizeof(netstats_t));
#endif
return 0;
}
static int _send(netdev_t *netdev, const iolist_t *iolist)
{
#if ESP_NOW_UNICAST
if (!_esp_now_scan_peers_done) {
return -ENODEV;
}
#endif
DEBUG("%s: %p %p\n", __func__, netdev, iolist);
CHECK_PARAM_RET (netdev != NULL, -ENODEV);
CHECK_PARAM_RET (iolist != NULL, -EINVAL);
esp_now_netdev_t* dev = (esp_now_netdev_t*)netdev;
mutex_lock(&dev->dev_lock);
dev->tx_len = 0;
/* load packet data into TX buffer */
for (const iolist_t *iol = iolist; iol; iol = iol->iol_next) {
if (dev->tx_len + iol->iol_len > ESP_NOW_MAX_SIZE) {
mutex_unlock(&dev->dev_lock);
return -EOVERFLOW;
}
memcpy (dev->tx_buf + dev->tx_len, iol->iol_base, iol->iol_len);
dev->tx_len += iol->iol_len;
}
#if ENABLE_DEBUG
printf ("%s: send %d byte\n", __func__, dev->tx_len);
/* esp_hexdump (dev->tx_buf, dev->tx_len, 'b', 16); */
#endif
_esp_now_sending = 1;
uint8_t* _esp_now_dst = 0;
#if ESP_NOW_UNICAST
ipv6_hdr_t* ipv6_hdr = (ipv6_hdr_t*)dev->tx_buf;
uint8_t _esp_now_dst_from_iid[6];
if (ipv6_hdr->dst.u8[0] == 0xff) {
/* packets to multicast prefix ff::/8 are sent to all peers */
DEBUG("multicast to all peers\n");
_esp_now_dst = 0;
_esp_now_sending = dev->peers_all;
#ifdef MODULE_NETSTATS_L2
netdev->stats.tx_mcast_count++;
#endif
}
else if ((byteorder_ntohs(ipv6_hdr->dst.u16[0]) & 0xffc0) == 0xfe80) {
/* for link local addresses fe80::/10, the MAC address is derived from dst address */
_get_mac_from_iid(&ipv6_hdr->dst.u8[8], _esp_now_dst_from_iid);
DEBUG("link local to %02x:%02x:%02x:%02x:%02x:%02x\n",
_esp_now_dst_from_iid[0], _esp_now_dst_from_iid[1],
_esp_now_dst_from_iid[2], _esp_now_dst_from_iid[3],
_esp_now_dst_from_iid[4], _esp_now_dst_from_iid[5]);
_esp_now_dst = _esp_now_dst_from_iid;
_esp_now_sending = 1;
}
else {
#ifdef MODULE_GNRC_IPV6_NIB
/* for other addresses, try to find an entry in NIB cache */
gnrc_ipv6_nib_nc_t nce;
int ret = gnrc_ipv6_nib_get_next_hop_l2addr (&ipv6_hdr->dst, dev->netif,
NULL, &nce);
if (ret == 0) {
/* entry was found in NIB, use MAC adress from the NIB cache entry */
DEBUG("global, next hop to neighbor %02x:%02x:%02x:%02x:%02x:%02x\n",
nce.l2addr[0], nce.l2addr[1], nce.l2addr[2],
nce.l2addr[3], nce.l2addr[4], nce.l2addr[5]);
_esp_now_dst = nce.l2addr;
_esp_now_sending = 1;
}
else {
#endif
/* entry was not found in NIB, send to all peers */
DEBUG("global, no neibhbor found, multicast to all peers\n");
_esp_now_dst = 0;
_esp_now_sending = dev->peers_all;
#ifdef MODULE_NETSTATS_L2
netdev->stats.tx_mcast_count++;
#endif
#ifdef MODULE_GNRC_IPV6_NIB
}
#endif
}
#else /* ESP_NOW_UNICAST */
ipv6_hdr_t* ipv6_hdr = (ipv6_hdr_t*)dev->tx_buf;
uint8_t _esp_now_dst_from_iid[6];
_esp_now_dst = (uint8_t*)_esp_now_mac;
_esp_now_sending = 1;
if (ipv6_hdr->dst.u8[0] == 0xff) {
/* packets to multicast prefix ff::/8 are sent to all peers */
DEBUG("multicast to all peers\n");
#ifdef MODULE_NETSTATS_L2
netdev->stats.tx_mcast_count++;
#endif
}
else if ((byteorder_ntohs(ipv6_hdr->dst.u16[0]) & 0xffc0) == 0xfe80) {
/* for link local addresses fe80::/10, the MAC address is derived from dst address */
_get_mac_from_iid(&ipv6_hdr->dst.u8[8], _esp_now_dst_from_iid);
DEBUG("link local to %02x:%02x:%02x:%02x:%02x:%02x\n",
_esp_now_dst_from_iid[0], _esp_now_dst_from_iid[1],
_esp_now_dst_from_iid[2], _esp_now_dst_from_iid[3],
_esp_now_dst_from_iid[4], _esp_now_dst_from_iid[5]);
if (esp_now_is_peer_exist(_esp_now_dst_from_iid) > 0) {
_esp_now_dst = _esp_now_dst_from_iid;
}
}
else
{
/* for other addresses, try to find an entry in NIB cache */
gnrc_ipv6_nib_nc_t nce;
int ret = gnrc_ipv6_nib_get_next_hop_l2addr (&ipv6_hdr->dst, dev->netif,
NULL, &nce);
if (ret == 0 && esp_now_is_peer_exist(nce.l2addr) > 0) {
/* entry was found in NIB, use MAC adress from the NIB cache entry */
DEBUG("global, next hop to neighbor %02x:%02x:%02x:%02x:%02x:%02x\n",
nce.l2addr[0], nce.l2addr[1], nce.l2addr[2],
nce.l2addr[3], nce.l2addr[4], nce.l2addr[5]);
_esp_now_dst = nce.l2addr;
}
else {
/* entry was not found in NIB, send to all peers */
DEBUG("global, no neibhbor found, multicast to all peers\n");
#ifdef MODULE_NETSTATS_L2
netdev->stats.tx_mcast_count++;
#endif
}
}
#endif /* ESP_NOW_UNICAST */
if (_esp_now_dst) {
DEBUG("%s: send to esp_now addr %02x:%02x:%02x:%02x:%02x:%02x\n", __func__,
_esp_now_dst[0], _esp_now_dst[1], _esp_now_dst[2],
_esp_now_dst[3], _esp_now_dst[4], _esp_now_dst[5]);
}
/* send the the packet to the peer(s) mac address */
if (esp_now_send (_esp_now_dst, dev->tx_buf, dev->tx_len) == 0) {
while (_esp_now_sending > 0) {
thread_yield_higher();
}
#ifdef MODULE_NETSTATS_L2
netdev->stats.tx_bytes += dev->tx_len;
netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE);
#endif
mutex_unlock(&dev->dev_lock);
return dev->tx_len;
}
else {
#ifdef MODULE_NETSTATS_L2
netdev->stats.tx_failed++;
#endif
}
mutex_unlock(&dev->dev_lock);
return -EIO;
}
static int _recv(netdev_t *netdev, void *buf, size_t len, void *info)
{
DEBUG("%s: %p %p %u %p\n", __func__, netdev, buf, len, info);
CHECK_PARAM_RET (netdev != NULL, -ENODEV);
esp_now_netdev_t* dev = (esp_now_netdev_t*)netdev;
mutex_lock(&dev->dev_lock);
uint8_t size = dev->rx_len;
if (!buf && !len) {
/* return the size without dropping received data */
mutex_unlock(&dev->dev_lock);
return size;
}
if (!buf && len) {
/* return the size and drop received data */
mutex_unlock(&dev->dev_lock);
dev->rx_len = 0;
return size;
}
if (buf && len && dev->rx_len) {
if (dev->rx_len > len) {
DEBUG("[esp_now] No space in receive buffers\n");
mutex_unlock(&dev->dev_lock);
return -ENOBUFS;
}
#if ENABLE_DEBUG
printf ("%s: received %d byte from %02x:%02x:%02x:%02x:%02x:%02x\n",
__func__, dev->rx_len,
dev->rx_mac[0], dev->rx_mac[1], dev->rx_mac[2],
dev->rx_mac[3], dev->rx_mac[4], dev->rx_mac[5]);
/* esp_hexdump (dev->rx_buf, dev->rx_len, 'b', 16); */
#endif
if (esp_now_is_peer_exist(dev->rx_mac) <= 0) {
_esp_now_add_peer(dev->rx_mac, esp_now_params.channel, esp_now_params.key);
}
memcpy(buf, dev->rx_buf, dev->rx_len);
dev->rx_len = 0;
#ifdef MODULE_NETSTATS_L2
netdev->stats.rx_count++;
netdev->stats.rx_bytes += size;
#endif
mutex_unlock(&dev->dev_lock);
return size;
}
mutex_unlock(&dev->dev_lock);
return -EINVAL;
}
static inline int _get_iid(esp_now_netdev_t *dev, eui64_t *value, size_t max_len)
{
CHECK_PARAM_RET (max_len >= sizeof(eui64_t), -EOVERFLOW);
/* interface id according to */
/* https://tools.ietf.org/html/rfc4291#section-2.5.1 */
value->uint8[0] = dev->addr[0] ^ 0x02; /* invert bit1 */
value->uint8[1] = dev->addr[1];
value->uint8[2] = dev->addr[2];
value->uint8[3] = 0xff;
value->uint8[4] = 0xfe;
value->uint8[5] = dev->addr[3];
value->uint8[6] = dev->addr[4];
value->uint8[7] = dev->addr[5];
return sizeof(eui64_t);
}
static int _get(netdev_t *netdev, netopt_t opt, void *val, size_t max_len)
{
DEBUG("%s: %s %p %p %u\n", __func__, netopt2str(opt), netdev, val, max_len);
CHECK_PARAM_RET (netdev != NULL, -ENODEV);
CHECK_PARAM_RET (val != NULL, -EINVAL);
esp_now_netdev_t *dev = (esp_now_netdev_t *)netdev;
int res = -ENOTSUP;
switch (opt) {
case NETOPT_DEVICE_TYPE:
CHECK_PARAM_RET (max_len >= sizeof(uint16_t), -EOVERFLOW);
*((uint16_t *)val) = NETDEV_TYPE_RAW;
res = sizeof(uint16_t);
break;
#ifdef MODULE_GNRC
case NETOPT_PROTO:
CHECK_PARAM_RET(max_len == sizeof(gnrc_nettype_t), -EOVERFLOW);
*((gnrc_nettype_t *)val) = dev->proto;
res = sizeof(gnrc_nettype_t);
break;
#endif
case NETOPT_MAX_PACKET_SIZE:
CHECK_PARAM_RET (max_len >= sizeof(uint16_t), -EOVERFLOW);
*((uint16_t *)val) = ESP_NOW_MAX_SIZE;
res = sizeof(uint16_t);
break;
case NETOPT_ADDR_LEN:
case NETOPT_SRC_LEN:
CHECK_PARAM_RET (max_len >= sizeof(uint16_t), -EOVERFLOW);
*((uint16_t *)val) = sizeof(dev->addr);
res = sizeof(uint16_t);
break;
case NETOPT_ADDRESS:
CHECK_PARAM_RET (max_len >= sizeof(dev->addr), -EOVERFLOW);
memcpy(val, dev->addr, sizeof(dev->addr));
res = sizeof(dev->addr);
break;
case NETOPT_IPV6_IID:
res = _get_iid(dev, val, max_len);
break;
#ifdef MODULE_NETSTATS_L2
case NETOPT_STATS:
CHECK_PARAM_RET (max_len == sizeof(uintptr_t), -EOVERFLOW);
*((netstats_t **)val) = &netdev->stats;
res = sizeof(uintptr_t);
break;
#endif
default:
DEBUG("%s: %s not supported\n", __func__, netopt2str(opt));
break;
}
return res;
}
static int _set(netdev_t *netdev, netopt_t opt, const void *val, size_t max_len)
{
DEBUG("%s: %s %p %p %u\n", __func__, netopt2str(opt), netdev, val, max_len);
CHECK_PARAM_RET (netdev != NULL, -ENODEV);
CHECK_PARAM_RET (val != NULL, -EINVAL);
esp_now_netdev_t *dev = (esp_now_netdev_t *) netdev;
int res = -ENOTSUP;
switch (opt) {
#ifdef MODULE_GNRC
case NETOPT_PROTO:
CHECK_PARAM_RET(max_len == sizeof(gnrc_nettype_t), -EOVERFLOW);
dev->proto = *((gnrc_nettype_t *)val);
res = sizeof(gnrc_nettype_t);
break;
#endif
case NETOPT_ADDRESS:
CHECK_PARAM_RET(max_len >= sizeof(dev->addr), -EOVERFLOW);
memcpy(dev->addr, val, sizeof(dev->addr));
res = sizeof(dev->addr);
break;
default:
DEBUG("%s: %s not supported\n", __func__, netopt2str(opt));
break;
}
return res;
}
static void _isr(netdev_t *netdev)
{
DEBUG("%s: %p\n", __func__, netdev);
CHECK_PARAM (netdev != NULL);
esp_now_netdev_t *dev = (esp_now_netdev_t *) netdev;
dev->netdev.event_callback(netdev, NETDEV_EVENT_RX_COMPLETE);
return;
}
static const netdev_driver_t _esp_now_driver =
{
.send = _send,
.recv = _recv,
.init = _init,
.isr = _isr,
.get = _get,
.set = _set,
};
void auto_init_esp_now (void)
{
LOG_TAG_INFO("esp_now", "initializing ESP-NOW device\n");
esp_now_setup(&_esp_now_dev);
_esp_now_dev.netif = gnrc_netif_raw_create(_esp_now_stack,
ESP_NOW_STACKSIZE, ESP_NOW_PRIO,
"net-esp-now",
(netdev_t *)&_esp_now_dev);
}
#endif /* MODULE_ESP_NOW */
/** @} */

View File

@ -0,0 +1,77 @@
/*
* 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_esp32_esp_now
* @{
*
* @file
* @brief Netdev interface for the ESP-NOW WiFi P2P protocol
*
* @author Gunar Schorcht <gunar@schorcht.net>
*/
#ifndef ESP_NOW_NETDEV_H
#define ESP_NOW_NETDEV_H
#include "net/netdev.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Maximum packet size that can be used with ESP-NOW
*/
#define ESP_NOW_MAX_SIZE (250)
/**
* @brief Length of ESP-NOW addresses
*/
#define ESP_NOW_ADDR_LEN ETHERNET_ADDR_LEN
/**
* @brief Reference to the netdev device driver struct
*/
extern const netdev_driver_t esp_now_driver;
/**
* @brief Device descriptor for ESP-NOW devices
*/
typedef struct
{
netdev_t netdev; /**< netdev parent struct */
uint8_t addr[ESP_NOW_ADDR_LEN]; /**< device addr (MAC address) */
uint8_t rx_len; /**< number of bytes received */
uint8_t rx_buf[ESP_NOW_MAX_SIZE]; /**< receive buffer */
uint8_t rx_mac[ESP_NOW_ADDR_LEN]; /**< source address */
uint8_t tx_len; /**< number of bytes in transmit buffer */
uint8_t tx_buf[ESP_NOW_MAX_SIZE]; /**< transmit buffer */
gnrc_netif_t* netif; /**< reference to the corresponding netif */
#ifdef MODULE_GNRC
gnrc_nettype_t proto; /**< protocol for upper layer */
#endif
uint8_t peers_all; /**< number of peers reachable */
uint8_t peers_enc; /**< number of encrypted peers */
mutex_t dev_lock; /**< device is already in use */
} esp_now_netdev_t;
#ifdef __cplusplus
}
#endif
#endif /* ESP_NOW_NETDEV_H */
/** @} */

View File

@ -0,0 +1,101 @@
/*
* 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_esp32_esp_now
* @{
*
* @file
* @brief Parameters for the netdev interface for ESP-NOW WiFi P2P
*
* @author Gunar Schorcht <gunar@schorcht.net>
*/
#ifndef ESP_NOW_PARAMS_H
#define ESP_NOW_PARAMS_H
#if defined(MODULE_ESP_NOW) || defined(DOXYGEN)
/**
* @name Set default configuration parameters for the ESP-NOW netdev driver
* @{
*/
#ifndef ESP_NOW_STACKSIZE
/** The size of the stack used for the ESP-NOW netdev driver thread */
#define ESP_NOW_STACKSIZE THREAD_STACKSIZE_DEFAULT
#endif
#ifndef ESP_NOW_PRIO
/** The priority of the ESP-NOW netdev driver thread */
#define ESP_NOW_PRIO GNRC_NETIF_PRIO
#endif
#ifndef ESP_NOW_SCAN_PERIOD
/** Period in us at which the node scans for other nodes in its range */
#define ESP_NOW_SCAN_PERIOD (10000000UL)
#endif
#ifndef ESP_NOW_SOFT_AP_PASS
/** Passphrase (max. 64 chars) used for the SoftAP interface of the nodes */
#define ESP_NOW_SOFT_AP_PASS "ThisistheRIOTporttoESP"
#endif
#ifndef ESP_NOW_CHANNEL
/** Channel used as broadcast medium by all ESP-NOW nodes together */
#define ESP_NOW_CHANNEL (6)
#endif
#ifndef ESP_NOW_KEY
/**
* @brief Key used for the communication between ESP-NOW nodes
*
* The key has to be defined to enable encrypted communication between ESP-NOW
* nodes. The key has to be of type *uint8_t [16]* and has to be exactly
* 16 bytes long. If the key is NULL (the default) communication is not
* encrypted.
*
* Please note: If encrypted communication is used, a maximum of 6 nodes can
* communicate with each other, while in unencrypted mode, up to 20 nodes can
* communicate.
*/
#define ESP_NOW_KEY (NULL)
#endif
#ifndef ESP_NOW_PARAMS
#define ESP_NOW_PARAMS { .key = ESP_NOW_KEY, \
.scan_period = ESP_NOW_SCAN_PERIOD, \
.softap_pass = ESP_NOW_SOFT_AP_PASS, \
.channel = ESP_NOW_CHANNEL }
#endif
/**
* @brief struct holding all params needed for device initialization
*/
typedef struct
{
uint8_t* key; /**< key of type uint8_t [16] or NULL (no encryption) */
uint32_t scan_period; /**< Period at which the node scans for other nodes */
char* softap_pass; /**< Passphrase used for the SoftAP interface */
uint8_t channel; /**< Channel used for ESP-NOW nodes */
} esp_now_params_t;
#ifdef __cplusplus
extern "C" {
#endif
static const esp_now_params_t esp_now_params = ESP_NOW_PARAMS;
#ifdef __cplusplus
}
#endif
#endif /* MODULE_ESP_NOW || DOXYGEN */
#endif /* ESP_NOW_PARAMS_H */
/**@}*/

View File

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

View File

@ -0,0 +1,18 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup cpu_esp32_esp_wifi ESP WiFi netdev interface
* @ingroup cpu_esp32
* @brief WiFi AP-based network device driver
*
* This module realizes a netdev interface using the built-in
* WiFi module and AP infrastructure.
*
* @author Gunar Schorcht <gunar@schorcht.net>
*/

View File

@ -0,0 +1,434 @@
/*
* 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_esp32_esp_wifi
* @{
*
* @file
* @brief Netdev interface for the ESP WiFi AP-based communication
*
* @author Gunar Schorcht <gunar@schorcht.net>
*/
#ifdef MODULE_ESP_WIFI
#define ENABLE_DEBUG (0)
#include "debug.h"
#include "log.h"
#include "tools.h"
#include <string.h>
#include <assert.h>
#include <errno.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 "xtimer.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 "nvs_flash/include/nvs_flash.h"
#include "esp_wifi_params.h"
#include "esp_wifi_netdev.h"
#include "net/ipv6/hdr.h"
#include "net/gnrc/ipv6/nib.h"
#define SYSTEM_EVENT_WIFI_RX_DONE (SYSTEM_EVENT_MAX + 3)
#define SYSTEM_EVENT_WIFI_TX_DONE (SYSTEM_EVENT_MAX + 4)
/**
* 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
* device which triggers the interrupt.
*/
static esp_wifi_netdev_t _esp_wifi_dev;
static const netdev_driver_t _esp_wifi_driver;
/* device thread stack */
static char _esp_wifi_stack[ESP_WIFI_STACKSIZE];
extern esp_err_t esp_system_event_add_handler (system_event_cb_t handler,
void *arg);
esp_err_t _esp_wifi_rx_cb(void *buffer, uint16_t len, void *eb)
{
DEBUG("%s: buf=%p len=%d eb=%p\n", __func__, buffer, len, eb);
CHECK_PARAM_RET (buffer != NULL, -EINVAL);
CHECK_PARAM_RET (len <= ETHERNET_DATA_LEN, -EINVAL);
mutex_lock(&_esp_wifi_dev.dev_lock);
memcpy(_esp_wifi_dev.rx_buf, buffer, len);
_esp_wifi_dev.rx_len = len;
_esp_wifi_dev.event = SYSTEM_EVENT_WIFI_RX_DONE;
_esp_wifi_dev.netdev.event_callback(&_esp_wifi_dev.netdev, NETDEV_EVENT_ISR);
mutex_unlock(&_esp_wifi_dev.dev_lock);
return ESP_OK;
}
/*
* Event handler for esp system events.
*/
static esp_err_t IRAM_ATTR _esp_system_event_handler(void *ctx, system_event_t *event)
{
switch(event->event_id) {
case SYSTEM_EVENT_STA_START:
DEBUG("%s WiFi started\n", __func__);
break;
case SYSTEM_EVENT_SCAN_DONE:
DEBUG("%s WiFi scan done\n", __func__);
break;
case SYSTEM_EVENT_STA_CONNECTED:
DEBUG("%s WiFi connected\n", __func__);
_esp_wifi_dev.connected = true;
_esp_wifi_dev.event = SYSTEM_EVENT_ETH_CONNECTED;
_esp_wifi_dev.netdev.event_callback(&_esp_wifi_dev.netdev, NETDEV_EVENT_ISR);
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
DEBUG("%s WiFi disconnected\n", __func__);
_esp_wifi_dev.connected = false;
_esp_wifi_dev.event = SYSTEM_EVENT_ETH_DISCONNECTED;
_esp_wifi_dev.netdev.event_callback(&_esp_wifi_dev.netdev, NETDEV_EVENT_ISR);
break;
default:
break;
}
return ESP_OK;
}
/** TODO better place
* Default WiFi configuration, overwrite them with your configs
*/
#ifndef CONFIG_WIFI_STA_SSID
#define CONFIG_WIFI_STA_SSID "RIOT_AP"
#endif
#ifndef CONFIG_WIFI_STA_PASSWORD
#define CONFIG_WIFI_STA_PASSWORD "ThisistheRIOTporttoESP"
#endif
#ifndef CONFIG_WIFI_STA_CHANNEL
#define CONFIG_WIFI_STA_CHANNEL 0
#endif
#define CONFIG_WIFI_STA_SCAN_METHOD WIFI_ALL_CHANNEL_SCAN
#define CONFIG_WIFI_STA_SORT_METHOD WIFI_CONNECT_AP_BY_SIGNAL
#define CONFIG_WIFI_STA_RSSI -127
#define CONFIG_WIFI_STA_AUTHMODE WIFI_AUTH_WPA_WPA2_PSK
static void esp_wifi_setup (esp_wifi_netdev_t* dev)
{
DEBUG("%s: %p\n", __func__, dev);
/*
* 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.
*/
extern portMUX_TYPE g_intr_lock_mux;
mutex_init(&g_intr_lock_mux);
esp_system_event_add_handler (_esp_system_event_handler, NULL);
esp_err_t result;
#if CONFIG_ESP32_WIFI_NVS_ENABLED
result = nvs_flash_init();
if (result != ESP_OK) {
LOG_TAG_ERROR("esp_wifi", "nfs_flash_init failed with return value %d\n", result);
return;
}
#endif
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
result = esp_wifi_init(&cfg);
if (result != ESP_OK) {
LOG_TAG_ERROR("esp_wifi", "esp_wifi_init failed with return value %d\n", result);
return;
}
#ifdef CONFIG_WIFI_COUNTRY
/* TODO */
#endif
/* we use predefined station configuration */
wifi_config_t wifi_config_sta = {
.sta = {
.ssid = CONFIG_WIFI_STA_SSID,
.password = CONFIG_WIFI_STA_PASSWORD,
.channel = CONFIG_WIFI_STA_CHANNEL,
.scan_method = CONFIG_WIFI_STA_SCAN_METHOD,
.sort_method = CONFIG_WIFI_STA_SORT_METHOD,
.threshold.rssi = CONFIG_WIFI_STA_RSSI,
.threshold.authmode = CONFIG_WIFI_STA_AUTHMODE
}
};
result = esp_wifi_set_mode(WIFI_MODE_STA);
if (result != ESP_OK) {
LOG_TAG_ERROR("esp_wifi", "esp_wifi_set_mode failed with return value %d\n", result);
return;
}
/* set the Station and SoftAP configuration */
result = esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config_sta);
if (result != ESP_OK) {
LOG_TAG_ERROR("esp_wifi", "esp_wifi_set_config station failed with return value %d\n", result);
return;
}
/* start the WiFi driver */
result = esp_wifi_start();
if (result != ESP_OK) {
LOG_TAG_ERROR("esp_wifi", "esp_wifi_start failed with return value %d\n", 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;
/* initialize netdev data structure */
dev->connected = false;
mutex_init(&dev->dev_lock);
result = esp_wifi_connect();
if (result != ESP_OK) {
LOG_TAG_ERROR("esp_wifi", "esp_wifi_connect failed with return value %d\n", result);
return;
}
}
static int _esp_wifi_init(netdev_t *netdev)
{
DEBUG("%s: %p\n", __func__, netdev);
#ifdef MODULE_NETSTATS_L2
memset(&netdev->stats, 0x00, sizeof(netstats_t));
#endif
return 0;
}
static int _esp_wifi_send(netdev_t *netdev, const iolist_t *iolist)
{
DEBUG("%s: netdev=%p iolist=%p\n", __func__, netdev, iolist);
CHECK_PARAM_RET (netdev != NULL, -ENODEV);
CHECK_PARAM_RET (iolist != NULL, -EINVAL);
esp_wifi_netdev_t* dev = (esp_wifi_netdev_t*)netdev;
if (!_esp_wifi_dev.connected) {
DEBUG("%s: WiFi is not connected\n", __func__);
return -ENODEV;
}
mutex_lock(&dev->dev_lock);
dev->tx_len = 0;
/* 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_DATA_LEN) {
mutex_unlock(&dev->dev_lock);
return -EOVERFLOW;
}
memcpy (dev->tx_buf + dev->tx_len, iol->iol_base, iol->iol_len);
dev->tx_len += iol->iol_len;
}
#if ENABLE_DEBUG
printf ("%s: send %d byte\n", __func__, dev->tx_len);
/* esp_hexdump (dev->tx_buf, dev->tx_len, 'b', 16); */
#endif
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 MODULE_NETSTATS_L2
netdev->stats.tx_success++;
netdev->stats.tx_bytes += dev->tx_len;
#endif
netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE);
}
else {
DEBUG("%s: sending WiFi packet failed\n", __func__);
#ifdef MODULE_NETSTATS_L2
netdev->stats.tx_failed++;
#endif
ret = -EIO;
}
mutex_unlock(&dev->dev_lock);
return ret;
}
static int _esp_wifi_recv(netdev_t *netdev, void *buf, size_t len, void *info)
{
DEBUG("%s: %p %p %u %p\n", __func__, netdev, buf, len, info);
CHECK_PARAM_RET (netdev != NULL, -ENODEV);
esp_wifi_netdev_t* dev = (esp_wifi_netdev_t*)netdev;
mutex_lock(&dev->dev_lock);
uint8_t size = dev->rx_len;
if (!buf && !len) {
/* return the size without dropping received data */
mutex_unlock(&dev->dev_lock);
return size;
}
if (!buf && len) {
/* return the size and drop received data */
mutex_unlock(&dev->dev_lock);
dev->rx_len = 0;
return size;
}
if (buf && len && dev->rx_len) {
if (dev->rx_len > len) {
DEBUG("[esp_wifi] No space in receive buffers\n");
mutex_unlock(&dev->dev_lock);
return -ENOBUFS;
}
#if ENABLE_DEBUG
/* esp_hexdump (dev->rx_buf, dev->rx_len, 'b', 16); */
#endif
/* copy received date and reset the receive length */
memcpy(buf, dev->rx_buf, dev->rx_len);
dev->rx_len = 0;
#ifdef MODULE_NETSTATS_L2
netdev->stats.rx_count++;
netdev->stats.rx_bytes += size;
#endif
mutex_unlock(&dev->dev_lock);
return size;
}
mutex_unlock(&dev->dev_lock);
return -EINVAL;
}
static int _esp_wifi_get(netdev_t *netdev, netopt_t opt, void *val, size_t max_len)
{
DEBUG("%s: %s %p %p %u\n", __func__, netopt2str(opt), netdev, val, max_len);
CHECK_PARAM_RET (netdev != NULL, -ENODEV);
CHECK_PARAM_RET (val != NULL, -EINVAL);
esp_wifi_netdev_t* dev = (esp_wifi_netdev_t*)netdev;
switch (opt) {
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_IS_WIRED:
return true;
case NETOPT_LINK_CONNECTED:
return dev->connected;
default:
return netdev_eth_get(netdev, opt, val, max_len);
}
}
static int _esp_wifi_set(netdev_t *netdev, netopt_t opt, const void *val, size_t max_len)
{
DEBUG("%s: %s %p %p %u\n", __func__, netopt2str(opt), netdev, val, max_len);
CHECK_PARAM_RET (netdev != NULL, -ENODEV);
CHECK_PARAM_RET (val != NULL, -EINVAL);
switch (opt) {
case NETOPT_ADDRESS:
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 _esp_wifi_isr(netdev_t *netdev)
{
DEBUG("%s: %p\n", __func__, netdev);
CHECK_PARAM (netdev != NULL);
esp_wifi_netdev_t *dev = (esp_wifi_netdev_t *) netdev;
switch (dev->event) {
case SYSTEM_EVENT_WIFI_RX_DONE:
if (dev->rx_len) {
dev->netdev.event_callback(netdev, NETDEV_EVENT_RX_COMPLETE);
}
case SYSTEM_EVENT_STA_CONNECTED:
dev->netdev.event_callback(netdev, NETDEV_EVENT_LINK_UP);
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
dev->netdev.event_callback(netdev, NETDEV_EVENT_LINK_DOWN);
break;
default:
break;
}
_esp_wifi_dev.event = SYSTEM_EVENT_MAX; /* no event */
return;
}
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,
};
void auto_init_esp_wifi (void)
{
LOG_TAG_DEBUG("esp_wifi", "initializing ESP WiFi device\n");
esp_wifi_setup(&_esp_wifi_dev);
_esp_wifi_dev.event = SYSTEM_EVENT_MAX; /* no event */
_esp_wifi_dev.netif = gnrc_netif_ethernet_create(_esp_wifi_stack,
ESP_WIFI_STACKSIZE, ESP_WIFI_PRIO,
"netdev-esp-wifi",
(netdev_t *)&_esp_wifi_dev);
}
#endif /* MODULE_ESP_WIFI */
/**@}*/

View File

@ -0,0 +1,60 @@
/*
* 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_esp32_esp_wifi
* @{
*
* @file
* @brief Netdev interface for the ESP WiFi AP-based communication
*
* @author Gunar Schorcht <gunar@schorcht.net>
*/
#ifndef ESP_WIFI_NETDEV_H
#define ESP_WIFI_NETDEV_H
#include "net/netdev.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @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 */
uint8_t rx_len; /**< number of bytes received */
uint8_t rx_buf[ETHERNET_DATA_LEN]; /**< receive buffer */
uint8_t tx_len; /**< number of bytes in transmit buffer */
uint8_t tx_buf[ETHERNET_DATA_LEN]; /**< transmit buffer */
uint32_t event; /**< received event */
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;
#ifdef __cplusplus
}
#endif
#endif /* ESP_WIFI_NETDEV_H */
/** @} */

View File

@ -0,0 +1,51 @@
/*
* 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_esp32_esp_wifi
* @{
*
* @file
* @brief Parameters for the netdev interface for ESP WiFi module
*
* @author Gunar Schorcht <gunar@schorcht.net>
*/
#ifndef ESP_WIFI_PARAMS_H
#define ESP_WIFI_PARAMS_H
#if defined(MODULE_ESP_WIFI) || defined(DOXYGEN)
/**
* @name Set default configuration parameters for the ESP WiFi netdev driver
* @{
*/
#ifndef ESP_WIFI_STACKSIZE
/** The size of the stack used for the ESP WiFi netdev driver thread */
#define ESP_WIFI_STACKSIZE THREAD_STACKSIZE_DEFAULT
#endif
#ifndef ESP_WIFI_PRIO
/** The priority of the ESP WiFi netdev driver thread */
#define ESP_WIFI_PRIO GNRC_NETIF_PRIO
#endif
/**@}*/
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
#endif /* MODULE_ESP_WIFI || DOXYGEN */
#endif /* ESP_WIFI_PARAMS_H */
/**@}*/

103
cpu/esp32/esp_events.c Normal file
View File

@ -0,0 +1,103 @@
/*
* 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_esp32
* @{
*
* @file
* @brief ESP system event handler
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
#if defined(MODULE_ESP_WIFI_ANY) || defined(MODULE_ESP_ETH)
#define ENABLE_DEBUG 0
#include "debug.h"
#include <string.h>
#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
}

190
cpu/esp32/esp_xtimer.c Normal file
View File

@ -0,0 +1,190 @@
/*
* 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_esp32
* @{
*
* @file
* @brief ETS timer to xtimer mapper
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
#define ENABLE_DEBUG 0
#include "debug.h"
#include <string.h>
#include "esp_common.h"
#include "log.h"
#include "esp_attr.h"
#include "irq_arch.h"
#include "xtimer.h"
#include "rom/ets_sys.h"
struct _ets_to_xtimer {
ETSTimer *ets_timer;
xtimer_t xtimer;
};
/* maximum number of ETS timer to xtimer mapper objects */
/* TODO tune the value */
#define ETS_TO_TIMER_NUM 40
/* table of ETS timer to xtimer mapper objects */
static struct _ets_to_xtimer _ets_to_xtimer_map[ETS_TO_TIMER_NUM] = {};
/**
* @brief Get the ETS timer to xtimer mapper object for the given timer.
*
* If there is no object, the function registers a new one and returns it.
* If there is no more object available, it returns NULL.
*
* @param pointer to the ETS timer
* @return pointer to the mapper object or NULL in case of error
*/
struct _ets_to_xtimer* _ets_to_xtimer_get(ETSTimer *timer)
{
/* search for an existing mapper object */
for (int i = 0; i < ETS_TO_TIMER_NUM; i++) {
if (_ets_to_xtimer_map[i].ets_timer == timer) {
return &_ets_to_xtimer_map[i];
}
}
/* search for a free mapper object */
for (int i = 0; i < ETS_TO_TIMER_NUM; i++) {
if (_ets_to_xtimer_map[i].ets_timer == NULL) {
_ets_to_xtimer_map[i].ets_timer = timer;
return &_ets_to_xtimer_map[i];
}
}
LOG_TAG_ERROR("esp_xtimer", "There is no free ETS timer to xtimer mapper "
"object available\n");
return NULL;
}
/**
* @brief Free the ETS timer to xtimer mapper object for the given timer.
* @param pointer to the ETS timer
*/
void _ets_to_xtimer_free(ETSTimer *timer)
{
/* search for an existing mapper object */
for (int i = 0; i < ETS_TO_TIMER_NUM; i++) {
if (_ets_to_xtimer_map[i].ets_timer == timer) {
_ets_to_xtimer_map[i].ets_timer = NULL;
return;
}
}
DEBUG("%s There is no ETS timer to xtimer for ETS timer %p\n",
__func__, timer);
}
/* xtimer call back function, distributes ets_timer callbacks */
void IRAM_ATTR _ets_to_xtimer_callback (void *arg)
{
struct _ets_to_xtimer* e2xt = (struct _ets_to_xtimer*)arg;
CHECK_PARAM (e2xt != NULL);
CHECK_PARAM (e2xt->ets_timer != NULL);
irq_isr_enter();
/* if timer is periodic, start it again with period */
if (e2xt->ets_timer->timer_period) {
ets_timer_arm_us(e2xt->ets_timer, e2xt->ets_timer->timer_period, true);
}
/* execute the ets_timer callback function */
e2xt->ets_timer->timer_func(e2xt->ets_timer->timer_arg);
irq_isr_exit();
}
void ets_timer_setfn(ETSTimer *ptimer, ETSTimerFunc *pfunc, void *parg)
{
DEBUG("%s timer=%p pfunc=%p parg=%p\n", __func__, ptimer, pfunc, parg);
struct _ets_to_xtimer* e2xt = _ets_to_xtimer_get(ptimer);
CHECK_PARAM(e2xt != NULL);
e2xt->ets_timer->timer_func = pfunc;
e2xt->ets_timer->timer_arg = parg;
e2xt->xtimer.callback = &_ets_to_xtimer_callback;
e2xt->xtimer.arg = (void*)e2xt;
}
void ets_timer_done(ETSTimer *ptimer)
{
DEBUG("%s timer=%p\n", __func__, ptimer);
struct _ets_to_xtimer* e2xt = _ets_to_xtimer_get(ptimer);
CHECK_PARAM(e2xt != NULL);
e2xt->ets_timer->timer_func = NULL;
e2xt->ets_timer->timer_arg = NULL;
}
void ets_timer_arm_us(ETSTimer *timer, uint32_t tmout, bool repeat)
{
DEBUG("%s timer=%p tmout=%u repeat=%d\n", __func__, timer, tmout, repeat);
struct _ets_to_xtimer* e2xt = _ets_to_xtimer_get(timer);
CHECK_PARAM(e2xt != NULL);
CHECK_PARAM(e2xt->xtimer.callback != NULL);
xtimer_set(&e2xt->xtimer, tmout);
e2xt->ets_timer->timer_expire = e2xt->xtimer.target;
e2xt->ets_timer->timer_period = repeat ? tmout : 0;
}
void ets_timer_arm(ETSTimer *timer, uint32_t tmout, bool repeat)
{
ets_timer_arm_us(timer, tmout * USEC_PER_MSEC, repeat);
}
void ets_timer_disarm(ETSTimer *timer)
{
DEBUG("%s timer=%p\n", __func__, timer);
struct _ets_to_xtimer* e2xt = _ets_to_xtimer_get(timer);
CHECK_PARAM(e2xt != NULL);
xtimer_remove(&e2xt->xtimer);
}
void ets_timer_init(void)
{
/* initialization is not necessary */
}
void ets_timer_deinit(void)
{
/* deinitialization is not necessary */
}
void os_timer_setfn(ETSTimer *ptimer, ETSTimerFunc *pfunction, void *parg)
__attribute__((alias("ets_timer_setfn")));
void os_timer_disarm(ETSTimer *ptimer)
__attribute__((alias("ets_timer_disarm")));
void os_timer_arm_us(ETSTimer *ptimer,uint32_t u_seconds,bool repeat_flag)
__attribute__((alias("ets_timer_arm_us")));
void os_timer_arm(ETSTimer *ptimer,uint32_t milliseconds,bool repeat_flag)
__attribute__((alias("ets_timer_arm")));
void os_timer_done(ETSTimer *ptimer)
__attribute__((alias("ets_timer_done")));

207
cpu/esp32/exceptions.c Normal file
View File

@ -0,0 +1,207 @@
/*
* 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_esp32
* @{
*
* @file
* @brief ESP32 exception handling
*
* @author Gunar Schorcht <gunar@schorcht.net>
* @}
*/
#define ENABLE_DEBUG 0
#include "debug.h"
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#include "esp_common.h"
#include "irq.h"
#include "log.h"
#include "periph/pm.h"
#include "ps.h"
#include "esp/common_macros.h"
#include "esp/xtensa_ops.h"
#include "rom/ets_sys.h"
#include "rom/rtc.h"
#include "rom/uart.h"
#include "sdk_conf.h"
#include "xtensa/corebits.h"
#include "freertos/xtensa_api.h"
#ifdef MODULE_ESP_GDBSTUB
#include "esp_gdbstub.h"
#endif
#ifdef MODULE_ESP_IDF_HEAP
#include "heap/esp_heap_caps.h"
#endif
extern void malloc_stats (void);
extern unsigned int get_free_heap_size (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) */
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 */
};
const char *reg_names[] = {
"pc ", "ps ",
"a0 ", "a1 ", "a2 ", "a3 ", "a4 ", "a5 ",
"a6 ", "a7 ", "a8 ", "a9 ", "a10 ", "a11 ",
"a12 ", "a13 ", "a14 ", "A15 ", "SAR ",
"exccause", "excvaddr", "lbeg ", "lend ", "lcount "
};
void IRAM NORETURN exception_handler (XtExcFrame *frame)
{
uint32_t excsave1;
uint32_t epc1;
uint32_t epc2;
uint32_t epc3;
uint32_t epc4;
RSR(excsave1, excsave1);
RSR(epc1, epc1);
RSR(epc2, epc2);
RSR(epc3, epc3);
RSR(epc4, epc4);
#ifdef MODULE_ESP_GDBSTUB
esp_gdbstub_panic_handler(frame);
#endif
ets_printf("EXCEPTION!! exccause=%d (%s) @%08x excvaddr=%08x\n\n",
frame->exccause, exception_names[frame->exccause],
excsave1, frame->excvaddr);
#if defined(DEVELHELP)
#if defined(MODULE_PS)
ets_printf("processes:\n");
ps();
ets_printf("\n");
#endif /* MODULE_PS */
#ifdef MODULE_ESP_IDF_HEAP
heap_caps_print_heap_info(MALLOC_CAP_DEFAULT);
#else
ets_printf("\nheap: %u (free %u) byte\n", &_eheap - &_sheap, get_free_heap_size());
#endif /* MODULE_ESP_IDF_HEAP */
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);
ets_printf("epc4 : %08x\n", epc4);
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);
ets_printf("lbeg : %08x\t", frame->lbeg);
ets_printf("lend : %08x\t", frame->lend);
ets_printf("lcount : %08x\n", frame->lcount);
#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.
Therefore, we break the execution and wait for the WDT reset. Maybe
there is better way. If debugger is active, 'break 0,0' stops
execution in debugger. */
__asm__ volatile ("break 0,0");
UNREACHABLE();
}
void init_exceptions (void)
{
xt_set_exception_handler(EXCCAUSE_UNALIGNED, exception_handler);
xt_set_exception_handler(EXCCAUSE_ILLEGAL, exception_handler);
xt_set_exception_handler(EXCCAUSE_INSTR_ERROR, exception_handler);
xt_set_exception_handler(EXCCAUSE_LOAD_STORE_ERROR, exception_handler);
xt_set_exception_handler(EXCCAUSE_LOAD_PROHIBITED, exception_handler);
xt_set_exception_handler(EXCCAUSE_STORE_PROHIBITED, exception_handler);
xt_set_exception_handler(EXCCAUSE_PRIVILEGED, exception_handler);
}
void IRAM NORETURN panic_arch(void)
{
#if defined(DEVELHELP)
#ifdef MODULE_ESP_IDF_HEAP
heap_caps_print_heap_info(MALLOC_CAP_DEFAULT);
#else
ets_printf("\nheap: %u (free %u) byte\n", &_eheap - &_sheap, get_free_heap_size());
#endif /* MODULE_ESP_IDF_HEAP */
#endif /* DEVELHELP */
/* restart */
software_reset();
UNREACHABLE();
}

View File

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

View File

@ -0,0 +1,59 @@
/*
* 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.
*
* FreeRTOS to RIOT-OS adaption module for source code compatibility
*/
#ifndef DOXYGEN
#define ENABLE_DEBUG (0)
#include "debug.h"
#include <string.h>
#include "rom/ets_sys.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
EventGroupHandle_t xEventGroupCreate (void)
{
ets_printf("%s\n", __func__);
return NULL;
}
void vEventGroupDelete (EventGroupHandle_t xEventGroup)
{
ets_printf("%s\n", __func__);
return NULL;
}
EventBits_t xEventGroupSetBits (EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet)
{
ets_printf("%s\n", __func__);
return NULL;
}
EventBits_t xEventGroupClearBits (EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToClear )
{
ets_printf("%s\n", __func__);
return NULL;
}
EventBits_t xEventGroupWaitBits (const EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToWaitFor,
const BaseType_t xClearOnExit,
const BaseType_t xWaitForAllBits,
TickType_t xTicksToWait)
{
ets_printf("%s\n", __func__);
return NULL;
}
#endif /* DOXYGEN */

View File

@ -0,0 +1,33 @@
/*
* 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.
*
* FreeRTOS to RIOT-OS adaption module for source code compatibility
*/
#ifndef DOXYGEN
#define ENABLE_DEBUG 0
#include "debug.h"
#include <string.h>
#include "esp_common.h"
#include "log.h"
#include "freertos/FreeRTOS.h"
uint32_t xPortGetTickRateHz(void) {
return MSEC_PER_SEC / portTICK_PERIOD_MS;
}
BaseType_t xPortInIsrContext(void)
{
/* is working on single core in that way */
return irq_is_in();
}
#endif /* DOXYGEN */

364
cpu/esp32/freertos/queue.c Normal file
View File

@ -0,0 +1,364 @@
/*
* 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.
*
* FreeRTOS to RIOT-OS adaption module for source code compatibility
*/
#ifndef DOXYGEN
#define ENABLE_DEBUG (0)
#include "debug.h"
#include <string.h>
#include "esp_common.h"
#include "esp_attr.h"
#include "log.h"
#include "mutex.h"
#include "rmutex.h"
#include "thread.h"
#include "rom/ets_sys.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#undef portENTER_CRITICAL
#undef portEXIT_CRITICAL
#define portENTER_CRITICAL(mux) vTaskEnterCritical(mux)
#define portEXIT_CRITICAL(mux) vTaskExitCritical(mux)
/*
* In FreeRTOS different types of semaphores, mutexes and queues are all
* mapped to a single generic queue type. With all these different types,
* single functions for send, receive, give and take are then used. To be
* able to dsitinguish between these different types in RIOT, we need typed
* objects.
*/
typedef struct {
uint8_t type; /* type of the queue, MUST be the first element */
mutex_t mutex; /* mutex to secure operations on the queue */
list_node_t sending; /* threads that are waiting to send */
list_node_t receiving; /* threads that are waiting to receive */
uint8_t* queue; /* the queue of waiting items */
uint32_t item_size; /* size of each item in the queue */
uint32_t item_num; /* num of items that can be stored in queue */
uint32_t item_front; /* first item in queue */
uint32_t item_tail; /* last item in queue */
uint32_t item_level; /* num of items stored in queue */
} _queue_t;
QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength,
const UBaseType_t uxItemSize,
const uint8_t ucQueueType )
{
DEBUG("%s pid=%d len=%u size=%u type=%u ", __func__,
thread_getpid(), uxQueueLength, uxItemSize, ucQueueType);
uint32_t queue_size = uxQueueLength * uxItemSize;
_queue_t* queue = malloc(sizeof(_queue_t) + queue_size);
mutex_init(&queue->mutex);
queue->type = ucQueueType;
queue->receiving.next = NULL;
queue->sending.next = NULL;
queue->queue = (queue_size) ? (uint8_t*)queue + sizeof(_queue_t) : NULL;
queue->item_num = uxQueueLength;
queue->item_size = uxItemSize;
queue->item_front = 0;
queue->item_tail = 0;
queue->item_level = 0;
DEBUG("queue=%p\n", queue);
return queue;
}
#define queueSEMAPHORE_QUEUE_ITEM_LENGTH ( ( UBaseType_t ) 0 )
QueueHandle_t xQueueCreateCountingSemaphore (const UBaseType_t uxMaxCount,
const UBaseType_t uxInitialCount)
{
_queue_t* queue;
CHECK_PARAM_RET(uxMaxCount != 0, NULL);
CHECK_PARAM_RET(uxInitialCount <= uxMaxCount, NULL);
queue = xQueueGenericCreate(uxMaxCount, queueSEMAPHORE_QUEUE_ITEM_LENGTH,
queueQUEUE_TYPE_COUNTING_SEMAPHORE);
DEBUG("%s pid=%d queue=%p\n", __func__, thread_getpid(), queue);
if (queue != NULL) {
queue->item_level = uxInitialCount;
queue->item_tail = (queue->item_front + queue->item_level) % queue->item_num;
}
return queue;
}
void vQueueDelete( QueueHandle_t xQueue )
{
DEBUG("%s pid=%d queue=%p\n", __func__, thread_getpid(), xQueue);
CHECK_PARAM(xQueue != NULL);
free(xQueue);
}
BaseType_t IRAM_ATTR _queue_generic_send(QueueHandle_t xQueue,
const void * const pvItemToQueue,
const BaseType_t xCopyPosition,
TickType_t xTicksToWait,
BaseType_t * const pxHigherPriorityTaskWoken)
{
DEBUG("%s pid=%d prio=%d queue=%p pos=%d wait=%u woken=%p\n", __func__,
thread_getpid(), sched_threads[thread_getpid()]->priority,
xQueue, xCopyPosition, xTicksToWait, pxHigherPriorityTaskWoken);
_queue_t* queue = (_queue_t*)xQueue;
CHECK_PARAM_RET(queue != NULL, pdFAIL);
while (1) {
taskENTER_CRITICAL(&queue->mutex);
/* is there still space in the queue */
if (queue->item_level < queue->item_num || xCopyPosition == queueOVERWRITE) {
uint32_t write_pos;
/* determin the write position in the queue and update positions */
if (xCopyPosition == queueSEND_TO_BACK) {
write_pos = queue->item_tail;
queue->item_tail = (queue->item_tail + 1) % queue->item_num;
queue->item_level++;
}
else if (xCopyPosition == queueSEND_TO_FRONT) {
queue->item_front = (queue->item_front - 1) % queue->item_num;
queue->item_level++;
write_pos = queue->item_front;
}
else { /* queueOVERWRITE */
write_pos = queue->item_front;
if (queue->item_level == 0) {
queue->item_level++;
}
}
/* if the item has no 0 size, copy it to the according place in queue */
if (queue->item_size && queue->queue && pvItemToQueue) {
memcpy(queue->queue + write_pos * queue->item_size,
pvItemToQueue, queue->item_size);
}
/* unlock waiting receiving thread */
if (queue->receiving.next != NULL) {
list_node_t *next = list_remove_head(&queue->receiving);
thread_t *process = container_of((clist_node_t*)next, thread_t, rq_entry);
uint8_t process_priority = process->priority;
uint8_t my_priority = sched_threads[thread_getpid()]->priority;
if (pxHigherPriorityTaskWoken) {
*pxHigherPriorityTaskWoken = process_priority < my_priority;
}
DEBUG("%s pid=%d queue=%p unlock waiting\n", __func__,
thread_getpid(), xQueue);
sched_set_status(process, STATUS_PENDING);
sched_switch(process_priority);
}
DEBUG("%s pid=%d queue=%p return pdPASS\n", __func__,
thread_getpid(), xQueue);
taskEXIT_CRITICAL(&queue->mutex);
return pdPASS;
}
else if (xTicksToWait == 0) {
/* if there was no space and timeout = 0, return with error */
DEBUG("%s pid=%d queue=%p return errQUEUE_FULL\n", __func__,
thread_getpid(), xQueue);
taskEXIT_CRITICAL(&queue->mutex);
return errQUEUE_FULL;
}
else {
/* suspend the calling thread to wait for space in the queue */
thread_t *me = (thread_t*)sched_active_thread;
sched_set_status(me, STATUS_SEND_BLOCKED);
/* waiting list is sorted by priority */
thread_add_to_list(&queue->sending, me);
DEBUG("%s pid=%d queue=%p suspended calling thread\n", __func__,
thread_getpid(), xQueue);
taskEXIT_CRITICAL(&queue->mutex);
thread_yield_higher();
/* TODO timeout handling with xTicksToWait */
DEBUG("%s pid=%d queue=%p continue calling thread\n", __func__,
thread_getpid(), xQueue);
}
}
return errQUEUE_FULL;
}
BaseType_t IRAM_ATTR _queue_generic_recv (QueueHandle_t xQueue,
void * const pvBuffer,
TickType_t xTicksToWait,
const BaseType_t xJustPeeking,
BaseType_t * const pxHigherPriorityTaskWoken)
{
DEBUG("%s pid=%d prio=%d queue=%p wait=%u peek=%u woken=%p\n", __func__,
thread_getpid(), sched_threads[thread_getpid()]->priority,
xQueue, xTicksToWait, xJustPeeking, pxHigherPriorityTaskWoken);
_queue_t* queue = (_queue_t*)xQueue;
CHECK_PARAM_RET(queue != NULL, pdFAIL);
while (1) {
taskENTER_CRITICAL(&queue->mutex);
if (queue->item_level == 0 && xTicksToWait == 0) {
/* if there was no element in queue and timeout = 0, return with error */
DEBUG("%s pid=%d queue=%p return errQUEUE_EMPTY\n", __func__,
thread_getpid(), xQueue);
taskEXIT_CRITICAL(&queue->mutex);
return errQUEUE_EMPTY;
}
if (queue->item_level > 0) {
/* if the item has no 0 size, copy it from queue to buffer */
if (queue->item_size && queue->item_num && queue->queue && pvBuffer) {
memcpy(pvBuffer,
queue->queue + queue->item_front * queue->item_size,
queue->item_size);
}
/* when only peeking leave the element in queue */
if (xJustPeeking == pdFALSE) {
queue->item_front = (queue->item_front + 1) % queue->item_num;
queue->item_level--;
/* unlock waiting sending thread */
if (queue->sending.next != NULL) {
list_node_t *next = list_remove_head(&queue->sending);
thread_t *process = container_of((clist_node_t*)next,
thread_t, rq_entry);
uint16_t process_priority = process->priority;
uint8_t my_priority = sched_threads[thread_getpid()]->priority;
if (pxHigherPriorityTaskWoken) {
*pxHigherPriorityTaskWoken = process_priority < my_priority;
}
DEBUG("%s pid=%d queue=%p unlock waiting\n", __func__,
thread_getpid(), xQueue);
sched_set_status(process, STATUS_PENDING);
sched_switch(process_priority);
}
}
DEBUG("%s pid=%d queue=%p return pdPASS\n", __func__,
thread_getpid(), xQueue);
taskEXIT_CRITICAL(&queue->mutex);
return pdPASS;
}
else {
/* suspend the calling thread to wait for an item in the queue */
thread_t *me = (thread_t*)sched_active_thread;
sched_set_status(me, STATUS_RECEIVE_BLOCKED);
/* waiting list is sorted by priority */
thread_add_to_list(&queue->receiving, me);
DEBUG("%s pid=%d queue=%p suspended calling thread\n", __func__,
thread_getpid(), xQueue);
taskEXIT_CRITICAL(&queue->mutex);
thread_yield_higher();
/* TODO timeout handling with xTicksToWait */
DEBUG("%s pid=%d queue=%p continue calling thread\n", __func__,
thread_getpid(), xQueue);
}
}
}
BaseType_t IRAM_ATTR xQueueGenericSend( QueueHandle_t xQueue,
const void * const pvItemToQueue,
TickType_t xTicksToWait,
const BaseType_t xCopyPosition )
{
DEBUG("%s pid=%d prio=%d queue=%p wait=%u pos=%d\n", __func__,
thread_getpid(), sched_threads[thread_getpid()]->priority,
xQueue, xTicksToWait, xCopyPosition);
return _queue_generic_send(xQueue, pvItemToQueue, xCopyPosition,
xTicksToWait, NULL);
}
BaseType_t IRAM_ATTR xQueueGenericSendFromISR( QueueHandle_t xQueue,
const void * const pvItemToQueue,
BaseType_t * const pxHigherPriorityTaskWoken,
const BaseType_t xCopyPosition )
{
DEBUG("%s pid=%d prio=%d queue=%p pos=%d woken=%p\n", __func__,
thread_getpid(), sched_threads[thread_getpid()]->priority,
xQueue, xCopyPosition, pxHigherPriorityTaskWoken);
return _queue_generic_send(xQueue, pvItemToQueue, xCopyPosition,
0, pxHigherPriorityTaskWoken);
}
BaseType_t IRAM_ATTR xQueueGenericReceive (QueueHandle_t xQueue,
void * const pvBuffer,
TickType_t xTicksToWait,
const BaseType_t xJustPeeking)
{
DEBUG("%s pid=%d prio=%d queue=%p wait=%u peek=%d\n", __func__,
thread_getpid(), sched_threads[thread_getpid()]->priority,
xQueue, xTicksToWait, xJustPeeking);
return _queue_generic_recv(xQueue, pvBuffer, xTicksToWait,
xJustPeeking, NULL);
}
BaseType_t IRAM_ATTR xQueueReceiveFromISR (QueueHandle_t xQueue,
void * const pvBuffer,
BaseType_t * const pxHigherPriorityTaskWoken)
{
DEBUG("%s pid=%d prio=%d queue=%p woken=%p\n", __func__,
thread_getpid(), sched_threads[thread_getpid()]->priority,
xQueue, pxHigherPriorityTaskWoken);
return _queue_generic_recv(xQueue, pvBuffer, 0,
0, pxHigherPriorityTaskWoken);
}
UBaseType_t uxQueueMessagesWaiting( QueueHandle_t xQueue )
{
_queue_t* queue = (_queue_t*)xQueue;
CHECK_PARAM_RET(queue != NULL, 0);
return queue->item_level;
}
BaseType_t xQueueGiveFromISR (QueueHandle_t xQueue,
BaseType_t * const pxHigherPriorityTaskWoken)
{
ets_printf("%s\n", __func__);
return pdFALSE;
}
#endif /* DOXYGEN */

178
cpu/esp32/freertos/semphr.c Normal file
View File

@ -0,0 +1,178 @@
/*
* 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.
*
* FreeRTOS to RIOT-OS adaption module for source code compatibility
*/
#ifndef DOXYGEN
#define ENABLE_DEBUG (0)
#include "debug.h"
#include <string.h>
#include "esp_common.h"
#include "irq_arch.h"
#include "log.h"
#include "mutex.h"
#include "rmutex.h"
#include "freertos/FreeRTOS.h"
/*
* In FreeRTOS different types of semaphores, mutexes and queues are all
* mapped to a single generic queue type. With all these different types,
* single functions for send, receive, give and take are then used. To be
* able to dsitinguish between these different types in RIOT, we need typed
* objects.
*/
typedef struct {
uint8_t type; /* type of the mutex, MUST be the first element */
mutex_t mutex; /* the mutex */
} _mutex_t;
typedef struct {
uint8_t type; /* type of the mutex, MUST be the first element */
rmutex_t rmutex; /* the mutex */
} _rmutex_t;
SemaphoreHandle_t xSemaphoreCreateMutex(void)
{
_mutex_t* _tmp = (_mutex_t*)malloc (sizeof(_mutex_t));
_tmp->type = queueQUEUE_TYPE_MUTEX;
mutex_init(&_tmp->mutex);
DEBUG("%s mutex=%p\n", __func__, _tmp);
return _tmp;
}
void vSemaphoreDelete( SemaphoreHandle_t xSemaphore )
{
DEBUG("%s mutex=%p\n", __func__, xSemaphore);
CHECK_PARAM(xSemaphore != NULL);
free(xSemaphore);
}
BaseType_t xSemaphoreGive (SemaphoreHandle_t xSemaphore)
{
DEBUG("%s mutex=%p\n", __func__, xSemaphore);
CHECK_PARAM_RET(xSemaphore != NULL, pdFALSE);
uint8_t type = ((_mutex_t*)xSemaphore)->type;
mutex_t* mutex= &((_mutex_t*)xSemaphore)->mutex;
switch (type) {
case queueQUEUE_TYPE_MUTEX:
mutex_unlock(mutex);
break;
case queueQUEUE_TYPE_RECURSIVE_MUTEX:
return xSemaphoreGiveRecursive (xSemaphore);
default:
return xQueueGenericSend(xSemaphore, NULL, 0, queueSEND_TO_BACK);
}
return pdTRUE;
}
BaseType_t xSemaphoreTake (SemaphoreHandle_t xSemaphore,
TickType_t xTicksToWait)
{
DEBUG("%s mutex=%p wait=%u\n", __func__, xSemaphore, xTicksToWait);
CHECK_PARAM_RET(xSemaphore != NULL, pdFALSE);
uint8_t type = ((_mutex_t*)xSemaphore)->type;
mutex_t* mutex= &((_mutex_t*)xSemaphore)->mutex;
switch (type) {
case queueQUEUE_TYPE_MUTEX:
{
if (xTicksToWait == 0) {
return (mutex_trylock(mutex) == 0) ? pdTRUE : pdFALSE;
}
else {
mutex_lock(mutex);
/* TODO timeout handling */
return pdTRUE;
}
break;
}
case queueQUEUE_TYPE_RECURSIVE_MUTEX:
return xSemaphoreTakeRecursive (xSemaphore, xTicksToWait);
default:
return xQueueGenericReceive(xSemaphore, NULL, xTicksToWait, pdFALSE);
}
}
SemaphoreHandle_t xSemaphoreCreateRecursiveMutex(void)
{
_rmutex_t* _tmp = (_rmutex_t*)malloc (sizeof(_rmutex_t));
_tmp->type = queueQUEUE_TYPE_RECURSIVE_MUTEX;
rmutex_init(&_tmp->rmutex);
DEBUG("%s rmutex=%p\n", __func__, _tmp);
return _tmp;
}
BaseType_t xSemaphoreGiveRecursive (SemaphoreHandle_t xSemaphore)
{
DEBUG("%s rmutex=%p\n", __func__, xSemaphore);
CHECK_PARAM_RET(xSemaphore != NULL, pdFALSE);
CHECK_PARAM_RET(((_rmutex_t*)xSemaphore)->type ==
queueQUEUE_TYPE_RECURSIVE_MUTEX, pdFALSE);
rmutex_unlock(&((_rmutex_t*)xSemaphore)->rmutex);
return pdTRUE;
}
BaseType_t xSemaphoreTakeRecursive (SemaphoreHandle_t xSemaphore,
TickType_t xTicksToWait)
{
DEBUG("%s rmutex=%p wait=%u\n", __func__, xSemaphore, xTicksToWait);
CHECK_PARAM_RET(xSemaphore != NULL, pdFALSE);
CHECK_PARAM_RET(((_rmutex_t*)xSemaphore)->type ==
queueQUEUE_TYPE_RECURSIVE_MUTEX, pdFALSE);
BaseType_t ret = pdTRUE;
rmutex_t* rmutex = &((_rmutex_t*)xSemaphore)->rmutex;
if (xTicksToWait == 0) {
ret = (rmutex_trylock(rmutex) == 0) ? pdTRUE : pdFALSE;
}
else {
rmutex_lock(&((_rmutex_t*)xSemaphore)->rmutex);
/* TODO timeout handling */
}
return ret;
}
void vPortCPUAcquireMutex(portMUX_TYPE *mux)
{
DEBUG("%s pid=%d prio=%d mux=%p\n", __func__,
thread_getpid(), sched_threads[thread_getpid()]->priority, mux);
critical_enter();
mutex_lock(mux); /* lock the mutex with interrupts disabled */
critical_exit();
}
void vPortCPUReleaseMutex(portMUX_TYPE *mux)
{
DEBUG("%s pid=%d prio=%d mux=%p\n", __func__,
thread_getpid(), sched_threads[thread_getpid()]->priority, mux);
critical_enter();
mutex_unlock(mux); /* unlock the mutex with interrupts disabled */
critical_exit();
}
#endif /* DOXYGEN */

173
cpu/esp32/freertos/task.c Normal file
View File

@ -0,0 +1,173 @@
/*
* 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.
*
* FreeRTOS to RIOT-OS adaption module for source code compatibility
*/
#ifndef DOXYGEN
#define ENABLE_DEBUG 0
#include "debug.h"
#include <string.h>
#include "esp_common.h"
#include "esp_attr.h"
#include "log.h"
#include "syscalls.h"
#include "thread.h"
#include "xtimer.h"
#include "soc/soc.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#define MHZ 1000000
/**
* @brief Architecture specific data of thread control blocks
*/
typedef struct {
uint32_t saved_int_state;
uint32_t critical_nesting;
} thread_arch_ext_t;
volatile thread_arch_ext_t threads_arch_exts[KERNEL_PID_LAST + 1] = {};
BaseType_t xTaskCreatePinnedToCore (TaskFunction_t pvTaskCode,
const char * const pcName,
const uint32_t usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pvCreatedTask,
const BaseType_t xCoreID)
{
/* FreeRTOS priority values have to be inverted */
uxPriority = SCHED_PRIO_LEVELS - uxPriority - 1;
DEBUG("%s name=%s size=%d prio=%d pvCreatedTask=%p ",
__func__, pcName, usStackDepth, uxPriority, pvCreatedTask);
char* stack = malloc(usStackDepth + sizeof(thread_t));
if (!stack) {
return pdFALSE;
}
kernel_pid_t pid = thread_create(stack,
usStackDepth + sizeof(thread_t),
uxPriority,
THREAD_CREATE_WOUT_YIELD |
THREAD_CREATE_STACKTEST,
(thread_task_func_t)pvTaskCode,
pvParameters, pcName);
DEBUG("pid=%d\n", pid);
if (pvCreatedTask) {
*pvCreatedTask = (TaskHandle_t)(0L + pid);
}
return (pid < 0) ? pdFALSE : pdTRUE;
}
BaseType_t xTaskCreate (TaskFunction_t pvTaskCode,
const char * const pcName,
const uint32_t usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pvCreatedTask)
{
return xTaskCreatePinnedToCore (pvTaskCode,
pcName,
usStackDepth,
pvParameters,
uxPriority,
pvCreatedTask,
PRO_CPU_NUM);
}
void vTaskDelete (TaskHandle_t xTaskToDelete)
{
DEBUG("%s pid=%d task=%p\n", __func__, thread_getpid(), xTaskToDelete);
CHECK_PARAM(xTaskToDelete != NULL);
uint32_t pid = (uint32_t)xTaskToDelete;
/* remove old task from scheduling */
thread_t* thread = (thread_t*)sched_threads[pid];
sched_set_status(thread, STATUS_STOPPED);
sched_threads[pid] = NULL;
sched_num_threads--;
sched_active_thread = NULL;
/* determine the new running task */
sched_run();
}
TaskHandle_t xTaskGetCurrentTaskHandle(void)
{
DEBUG("%s pid=%d\n", __func__, thread_getpid());
uint32_t pid = thread_getpid();
return (TaskHandle_t)pid;
}
void vTaskDelay( const TickType_t xTicksToDelay )
{
uint64_t us = xTicksToDelay * MHZ / xPortGetTickRateHz();
xtimer_usleep(us);
}
TickType_t xTaskGetTickCount (void)
{
return system_get_time() / USEC_PER_MSEC / portTICK_PERIOD_MS;
}
void vTaskEnterCritical( portMUX_TYPE *mux )
{
/* determine calling thread pid (can't fail) */
kernel_pid_t my_pid = thread_getpid();
DEBUG("%s pid=%d prio=%d mux=%p\n", __func__,
my_pid, sched_threads[my_pid]->priority, mux);
/* disable interrupts */
uint32_t state = irq_disable();
/* aquire the mutex with interrupts disabled */
mutex_lock(mux); /* TODO should be only a spin lock */
/* increment nesting counter and save old interrupt level */
threads_arch_exts[my_pid].critical_nesting++;
if (threads_arch_exts[my_pid].critical_nesting == 1) {
threads_arch_exts[my_pid].saved_int_state = state;
}
}
void vTaskExitCritical( portMUX_TYPE *mux )
{
/* determine calling thread pid (can't fail) */
kernel_pid_t my_pid = thread_getpid();
DEBUG("%s pid=%d prio=%d mux=%p\n", __func__,
my_pid, sched_threads[my_pid]->priority, mux);
/* release the mutex with interrupts disabled */
mutex_unlock(mux); /* TODO should be only a spin lock */
/* decrement nesting counter and restore old interrupt level */
if (threads_arch_exts[my_pid].critical_nesting) {
threads_arch_exts[my_pid].critical_nesting--;
if (threads_arch_exts[my_pid].critical_nesting == 0) {
irq_restore(threads_arch_exts[my_pid].saved_int_state);
}
}
}
#endif /* DOXYGEN */

106
cpu/esp32/freertos/timers.c Normal file
View File

@ -0,0 +1,106 @@
/*
* 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.
*
* FreeRTOS to RIOT-OS adaption module for source code compatibility
*/
#ifndef DOXYGEN
#define ENABLE_DEBUG 0
#include "debug.h"
#include <string.h>
#include "esp_common.h"
#include "esp_attr.h"
#include "log.h"
#include "xtimer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/timers.h"
typedef struct {
xtimer_t xtimer; /* xtimer object */
const char* name; /* FreeRTOS timer name */
uint32_t period; /* in us */
bool autoreload; /* FreeRTOS timer reload indicator */
const void* timerid; /* FreeRTOS timer id */
TimerCallbackFunction_t cb; /* FreeRTOS callback function */
} freertos_xtimer_t;
static void IRAM_ATTR _xtimer_callback (void *arg)
{
CHECK_PARAM(arg != NULL);
freertos_xtimer_t* timer = (freertos_xtimer_t*)arg;
if (timer->autoreload) {
xtimer_set(&timer->xtimer, timer->period);
}
if (timer->cb) {
timer->cb(arg);
}
}
TimerHandle_t xTimerCreate (const char * const pcTimerName,
const TickType_t xTimerPeriod,
const UBaseType_t uxAutoReload,
void * const pvTimerID,
TimerCallbackFunction_t pxCallbackFunction)
{
freertos_xtimer_t* timer = malloc(sizeof(freertos_xtimer_t));
if (timer == NULL) {
return NULL;
}
/* FreeRTOS timer parameter */
timer->name = pcTimerName;
timer->period = xTimerPeriod * portTICK_PERIOD_MS * USEC_PER_MSEC;
timer->autoreload = uxAutoReload;
timer->timerid = pvTimerID;
timer->cb = pxCallbackFunction;
/* xtimer parameter */
timer->xtimer.callback = _xtimer_callback;
timer->xtimer.arg = timer;
return timer;
}
BaseType_t xTimerDelete(TimerHandle_t xTimer, TickType_t xBlockTime)
{
CHECK_PARAM_RET(xTimer != NULL, pdFALSE);
freertos_xtimer_t* timer = (freertos_xtimer_t*)xTimer;
xtimer_remove(&timer->xtimer);
free(timer);
return pdTRUE;
}
BaseType_t xTimerStart (TimerHandle_t xTimer, TickType_t xBlockTime)
{
CHECK_PARAM_RET(xTimer != NULL, pdFALSE);
freertos_xtimer_t* timer = (freertos_xtimer_t*)xTimer;
xtimer_set(&timer->xtimer, timer->period);
return pdTRUE;
}
BaseType_t xTimerStop (TimerHandle_t xTimer, TickType_t xBlockTime)
{
CHECK_PARAM_RET(xTimer != NULL, pdFALSE);
freertos_xtimer_t* timer = (freertos_xtimer_t*)xTimer;
xtimer_remove(&timer->xtimer);
return pdTRUE;
}
#endif /* DOXYGEN */

453
cpu/esp32/gen_esp32part.py Executable file
View File

@ -0,0 +1,453 @@
#!/usr/bin/env python
#
# ESP32 partition table generation tool
#
# Converts partition tables to/from CSV and binary formats.
#
# See http://esp-idf.readthedocs.io/en/latest/api-guides/partition-tables.html
# for explanation of partition table structure and uses.
#
# Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http:#www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import print_function, division
import argparse
import os
import re
import struct
import sys
import hashlib
import binascii
MAX_PARTITION_LENGTH = 0xC00 # 3K for partition data (96 entries) leaves 1K in a 4K sector for signature
SHA256_PARTITION_BEGIN = b"\xEB\xEB" + b"\xFF" * 14 # The first 2 bytes are like magic numbers for SHA256 sum
__version__ = '1.0'
quiet = False
sha256sum = True
def status(msg):
""" Print status message to stderr """
if not quiet:
critical(msg)
def critical(msg):
""" Print critical message to stderr """
if not quiet:
sys.stderr.write(msg)
sys.stderr.write('\n')
class PartitionTable(list):
def __init__(self):
super(PartitionTable, self).__init__(self)
@classmethod
def from_csv(cls, csv_contents):
res = PartitionTable()
lines = csv_contents.splitlines()
def expand_vars(f):
f = os.path.expandvars(f)
m = re.match(r'(?<!\\)\$([A-Za-z_][A-Za-z0-9_]*)', f)
if m:
raise InputError("unknown variable '%s'" % m.group(1))
return f
line_num = range(len(lines))
for line_no in line_num:
line = expand_vars(lines[line_no]).strip()
if line.startswith("#") or len(line) == 0:
continue
try:
res.append(PartitionDefinition.from_csv(line))
except InputError as e:
raise InputError("Error at line %d: %s" % (line_no+1, e))
except Exception:
critical("Unexpected error parsing line %d: %s" % (line_no+1, line))
raise
# fix up missing offsets & negative sizes
last_end = 0x5000 # first offset after partition table
for e in res:
if e.offset is None:
pad_to = 0x10000 if e.type == PartitionDefinition.APP_TYPE else 4
if last_end % pad_to != 0:
last_end += pad_to - (last_end % pad_to)
e.offset = last_end
if e.size < 0:
e.size = -e.size - e.offset
last_end = e.offset + e.size
return res
def __getitem__(self, item):
""" Allow partition table access via name as well as by
numeric index. """
if isinstance(item, str):
for x in self:
if x.name == item:
return x
raise ValueError("No partition entry named '%s'" % item)
else:
return super(PartitionTable, self).__getitem__(item)
def verify(self):
# verify each partition individually
for p in self:
p.verify()
# check for overlaps
last = None
for p in sorted(self, key=lambda x: x.offset):
if p.offset < 0x5000:
raise InputError("Partition offset 0x%x is below 0x5000" % p.offset)
if last is not None and p.offset < last.offset + last.size:
raise InputError("Partition at 0x%x overlaps 0x%x-0x%x" % (p.offset,
last.offset,
last.offset+last.size-1))
last = p
def flash_size(self):
""" Return the size that partitions will occupy in flash
(ie the offset the last partition ends at)
"""
try:
last = sorted(self, reverse=True)[0]
except IndexError:
return 0 # empty table!
return last.offset + last.size
@classmethod
def from_binary(cls, b):
sha256 = hashlib.sha256()
result = cls()
for o in range(0, len(b), 32):
data = b[o:o+32]
if len(data) != 32:
raise InputError("Partition table length must be a multiple of 32 bytes")
if data == b'\xFF'*32:
return result # got end marker
if sha256sum and data[:2] == SHA256_PARTITION_BEGIN[:2]: # check only the magic number part
if data[16:] == sha256.digest():
continue # the next iteration will check for the end marker
else:
raise InputError("SHA256 checksums don't match! "
"(computed: 0x%s, parsed: 0x%s)" % (sha256.hexdigest(),
binascii.hexlify(data[16:])))
else:
sha256.update(data)
result.append(PartitionDefinition.from_binary(data))
raise InputError("Partition table is missing an end-of-table marker")
def to_binary(self):
result = b"".join(e.to_binary() for e in self)
# to satisfy Cadacy, was: if sha256sum:
# to satisfy Cadacy, was: result += SHA256_PARTITION_BEGIN + hashlib.sha256(result).digest()
if sha256sum:
result += SHA256_PARTITION_BEGIN + hashlib.sha256(result).digest()
if len(result) >= MAX_PARTITION_LENGTH:
raise InputError("Binary partition table length (%d) longer than max" % len(result))
result += b"\xFF" * (MAX_PARTITION_LENGTH - len(result)) # pad the sector, for signing
return result
def to_csv(self, simple_formatting=False):
rows = ["# Espressif ESP32 Partition Table",
"# Name, Type, SubType, Offset, Size, Flags"]
rows += [x.to_csv(simple_formatting) for x in self]
return "\n".join(rows) + "\n"
class PartitionDefinition(object):
APP_TYPE = 0x00
DATA_TYPE = 0x01
TYPES = {
"app": APP_TYPE,
"data": DATA_TYPE,
}
# Keep this map in sync with esp_partition_subtype_t enum in esp_partition.h
SUBTYPES = {
APP_TYPE: {
"factory": 0x00,
"test": 0x20,
},
DATA_TYPE: {
"ota": 0x00,
"phy": 0x01,
"nvs": 0x02,
"coredump": 0x03,
"esphttpd": 0x80,
"fat": 0x81,
"spiffs": 0x82,
},
}
MAGIC_BYTES = b"\xAA\x50"
ALIGNMENT = {
APP_TYPE: 0x10000,
DATA_TYPE: 0x04,
}
# dictionary maps flag name (as used in CSV flags list, property name)
# to bit set in flags words in binary format
FLAGS = {
"encrypted": 0
}
# add subtypes for the 16 OTA slot values ("ota_XXX, etc.")
for ota_slot in range(16):
SUBTYPES[TYPES["app"]]["ota_%d" % ota_slot] = 0x10 + ota_slot
def __init__(self):
self.name = ""
self.type = None
self.subtype = None
self.offset = None
self.size = None
self.encrypted = False
@classmethod
def from_csv(cls, line):
""" Parse a line from the CSV """
line_w_defaults = line + ",,,," # lazy way to support default fields
fields = [f.strip() for f in line_w_defaults.split(",")]
res = PartitionDefinition()
res.name = fields[0]
res.type = res.parse_type(fields[1])
res.subtype = res.parse_subtype(fields[2])
res.offset = res.parse_address(fields[3])
res.size = res.parse_address(fields[4])
if res.size is None:
raise InputError("Size field can't be empty")
flags = fields[5].split(":")
for flag in flags:
if flag in cls.FLAGS:
setattr(res, flag, True)
elif len(flag) > 0:
raise InputError("CSV flag column contains unknown flag '%s'" % (flag))
return res
def __eq__(self, other):
return self.name == other.name and self.type == other.type \
and self.subtype == other.subtype and self.offset == other.offset \
and self.size == other.size
def __repr__(self):
def maybe_hex(x):
return "0x%x" % x if x is not None else "None"
return "PartitionDefinition('%s', 0x%x, 0x%x, %s, %s)" % (self.name, self.type,
self.subtype or 0,
maybe_hex(self.offset),
maybe_hex(self.size))
def __str__(self):
return "Part '%s' %d/%d @ 0x%x size 0x%x" % (self.name, self.type,
self.subtype, self.offset or -1,
self.size or -1)
def __cmp__(self, other):
return self.offset - other.offset
def parse_type(self, strval):
if strval == "":
raise InputError("Field 'type' can't be left empty.")
return parse_int(strval, self.TYPES)
def parse_subtype(self, strval):
if strval == "":
return 0 # default
return parse_int(strval, self.SUBTYPES.get(self.type, {}))
@classmethod
def parse_address(cls, strval):
if strval == "":
return None # PartitionTable will fill in default
return parse_int(strval, {})
def verify(self):
if self.type is None:
raise ValidationError(self, "Type field is not set")
if self.subtype is None:
raise ValidationError(self, "Subtype field is not set")
if self.offset is None:
raise ValidationError(self, "Offset field is not set")
align = self.ALIGNMENT.get(self.type, 4)
if self.offset % align:
raise ValidationError(self, "Offset 0x%x is not aligned to 0x%x" % (self.offset, align))
if self.size is None:
raise ValidationError(self, "Size field is not set")
STRUCT_FORMAT = "<2sBBLL16sL"
@classmethod
def from_binary(cls, b):
if len(b) != 32:
raise InputError("Partition definition length must be exactly 32 bytes. Got %d bytes." % len(b))
res = cls()
(magic, res.type, res.subtype, res.offset,
res.size, res.name, flags) = struct.unpack(cls.STRUCT_FORMAT, b)
if b"\x00" in res.name: # strip null byte padding from name string
res.name = res.name[:res.name.index(b"\x00")]
res.name = res.name.decode()
if magic != cls.MAGIC_BYTES:
raise InputError("Invalid magic bytes (%r) for partition definition" % magic)
for flag, bit in cls.FLAGS.items():
if flags & (1 << bit):
setattr(res, flag, True)
flags &= ~(1 << bit)
if flags != 0:
critical("WARNING: Partition definition had unknown flag(s) 0x%08x. Newer binary format?" % flags)
return res
def get_flags_list(self):
return [flag for flag in self.FLAGS.keys() if getattr(self, flag)]
def to_binary(self):
flags = sum((1 << self.FLAGS[flag]) for flag in self.get_flags_list())
return struct.pack(self.STRUCT_FORMAT,
self.MAGIC_BYTES,
self.type, self.subtype,
self.offset, self.size,
self.name.encode(),
flags)
def to_csv(self, simple_formatting=False):
def addr_format(a, include_sizes):
if not simple_formatting and include_sizes:
for (val, suffix) in [(0x100000, "M"), (0x400, "K")]:
if a % val == 0:
return "%d%s" % (a // val, suffix)
return "0x%x" % a
def lookup_keyword(t, keywords):
for k, v in keywords.items():
if simple_formatting is False and t == v:
return k
return "%d" % t
def generate_text_flags():
""" colon-delimited list of flags """
return ":".join(self.get_flags_list())
return ",".join([self.name,
lookup_keyword(self.type, self.TYPES),
lookup_keyword(self.subtype, self.SUBTYPES.get(self.type, {})),
addr_format(self.offset, False),
addr_format(self.size, True),
generate_text_flags()])
def parse_int(v, keywords):
"""Generic parser for integer fields - int(x,0) with provision for
k/m/K/M suffixes and 'keyword' value lookup.
"""
try:
for letter, multiplier in [("k", 1024), ("m", 1024*1024)]:
if v.lower().endswith(letter):
return parse_int(v[:-1], keywords) * multiplier
return int(v, 0)
except ValueError:
if len(keywords) == 0:
raise InputError("Invalid field value %s" % v)
try:
return keywords[v.lower()]
except KeyError:
raise InputError("Value '%s' is not valid. Known keywords: %s" % (v, ", ".join(keywords)))
def main():
global quiet
global sha256sum
parser = argparse.ArgumentParser(description='ESP32 partition table utility')
parser.add_argument('--flash-size',
help='Optional flash size limit, checks partition table fits in flash',
nargs='?', choices=['1MB', '2MB', '4MB', '8MB', '16MB'])
parser.add_argument('--disable-sha256sum', help='Disable sha256 checksum for the partition table',
default=False, action='store_true')
parser.add_argument('--verify', '-v', help='Verify partition table fields',
default=True, action='store_false')
parser.add_argument('--quiet', '-q', help="Don't print status messages to stderr",
action='store_true')
parser.add_argument('input',
help='Path to CSV or binary file to parse. Will use stdin if omitted.',
type=argparse.FileType('rb'), default=sys.stdin)
parser.add_argument('output', help='Path to output converted binary or CSV file. Will use '
'stdout if omitted, unless the --display argument is also passed (in '
'which case only the summary is printed.)',
nargs='?',
default='-')
args = parser.parse_args()
quiet = args.quiet
sha256sum = not args.disable_sha256sum
input_arg = args.input.read()
input_is_binary = input_arg[0:2] == PartitionDefinition.MAGIC_BYTES
if input_is_binary:
status("Parsing binary partition input...")
table = PartitionTable.from_binary(input_arg)
else:
input_arg = input_arg.decode()
status("Parsing CSV input...")
table = PartitionTable.from_csv(input_arg)
if args.verify:
status("Verifying table...")
table.verify()
if args.flash_size:
size_mb = int(args.flash_size.replace("MB", ""))
size = size_mb * 1024 * 1024 # flash memory uses honest megabytes!
table_size = table.flash_size()
if size < table_size:
raise InputError("Partitions defined in '%s' occupy %.1fMB of flash (%d bytes) which "
"does not fit in configured flash size %dMB. Change the flash size "
"in menuconfig under the 'Serial Flasher Config' menu." %
(args.input.name, table_size / 1024.0 / 1024.0, table_size, size_mb))
if input_is_binary:
output = table.to_csv()
with sys.stdout if args.output == '-' else open(args.output, 'w') as f:
f.write(output)
else:
output = table.to_binary()
with sys.stdout.buffer if args.output == '-' else open(args.output, 'wb') as f:
f.write(output)
class InputError(RuntimeError):
def __init__(self, e):
super(InputError, self).__init__(e)
class ValidationError(InputError):
def __init__(self, partition, message):
super(ValidationError, self).__init__(
"Partition %s invalid: %s" % (partition.name, message))
if __name__ == '__main__':
try:
main()
except InputError as e:
print(e, file=sys.stderr)
sys.exit(2)

View File

@ -0,0 +1,96 @@
/*
* 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_esp32
* @ingroup drivers_periph_gpio
* @{
*
* @file
* @brief Architecture specific ADC functions ESP32
*
* @author Gunar Schorcht <gunar@schorcht.net>
* @}
*/
#ifndef ADC_ARCH_H
#define ADC_ARCH_H
#ifdef __cplusplus
extern "C" {
#endif
#include "periph/gpio.h"
#include "periph/adc.h"
/**
* @brief Attenuations that can be set for ADC lines
*/
typedef enum {
ADC_ATTENUATION_0_DB = 0, /**< full-range is about 1.1 V (Vref) */
ADC_ATTENUATION_3_DB, /**< full-range is about 1.5 V */
ADC_ATTENUATION_6_DB, /**< full-range is about 2.2 V */
ADC_ATTENUATION_11_DB /**< full-range is about 3.3 V */
} adc_attenuation_t;
/**
* @brief Set the attenuation for the ADC line. Default attenuation is 11 dB.
*
* For each ADC line, an attenuation of the input signal can be defined
* separately. This results in different full ranges of the measurable voltage
* at the input. The attenuation can be set to 0 dB, 3 dB, 6 dB and 11 dB,
* with 11 dB being the standard attenuation. Since an ADC input is measured
* against a reference voltage Vref of 1.1 V, approximately the following
* measurement ranges are given when using a corresponding attenuation:
*
* <center>
*
* Attenuation | Voltage Range | Symbol
* ----------------|-------------------|----------------------
* 0 dB | 0 ... 1.1V (Vref) | ADC_ATTENUATION_0_DB
* 3 dB | 0 ... 1.5V | ADC_ATTENUATION_3_DB
* 6 dB | 0 ... 2.2V | ADC_ATTENUATION_6_DB
* 11 dB (default) | 0 ... 3.3V | ADC_ATTENUATION_11_DB
*
* </center>
*
* Please note: The reference voltage Vref can vary from device to device in
* the range of 1.0V and 1.2V. The Vref of a device can be read with the
* function *adc_vref_to_gpio25* at the pin GPIO 25. The results of the ADC
* input can then be adjusted accordingly.
*
* @param line ADC line for which the attenuation is set
* @param atten Attenuation, see type definition of *adc_attenuation_t
* @return 0 on success
* @return -1 on invalid ADC line
*/
int adc_set_attenuation(adc_t line, adc_attenuation_t atten);
/**
* @brief Output ADC reference voltage to GPIO25
*
* @return 0 on success
* @return -1 on invalid ADC line
*/
int adc_vref_to_gpio25 (void);
/**
* @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 success
* @return -1 on invalid pin
*/
int rtcio_config_sleep_mode (gpio_t pin, bool mode, bool input);
#ifdef __cplusplus
}
#endif
#endif /* ADC_ARCH_H */

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

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

View File

@ -0,0 +1,50 @@
/*
* 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_esp32
* @{
*
* @file
* @brief CPU specific configuration options
*
* @author Gunar Schorcht <gunar@schorcht.net>
*/
#ifndef CPU_CONF_H
#define CPU_CONF_H
#include <stdint.h>
#include "esp_common_log.h"
#include "xtensa_conf.h"
#include "xtensa/xtensa_context.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Stack size configuration
* @{
*/
#define THREAD_EXTRA_STACKSIZE_PRINTF (1024)
#define THREAD_STACKSIZE_DEFAULT (2048)
#define THREAD_STACKSIZE_IDLE (2048)
/** @} */
/**
* Buffer size used for printf functions (maximum length of formatted output).
*/
#define PRINTF_BUFSIZ 256
#ifdef __cplusplus
}
#endif /* CPU_CONF_H */
#endif /* CPU_CONF_H */
/** @} */

View File

@ -0,0 +1,128 @@
/*
* 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_esp32
* @{
*
* @file
* @brief Common helper macros
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
*/
#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 funcion name mappings for source code compatibility with ESP8266 port
* @{
*/
#define system_get_cpu_freq ets_get_cpu_frequency
#define system_update_cpu_freq ets_update_cpu_frequency
/** @} */
/** @} */
/** 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 */

View File

@ -0,0 +1,87 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp32
* @{
*
* @file
* @brief Common log macros
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
*/
#ifndef ESP_COMMON_LOG_H
#define ESP_COMMON_LOG_H
#ifndef DOXYGEN
#ifdef __cplusplus
extern "C" {
#endif
#include "log.h"
#define LOG_TAG(level, tag, ...) do { \
if ((level) <= LOG_LEVEL) log_write_tagged(level, tag, __VA_ARGS__); } while (0U)
/**
* Override LOG_* definitions with a tagged version. By default the function
* name is used tag.
*/
#undef LOG_ERROR
#undef LOG_INFO
#undef LOG_WARNING
#undef LOG_DEBUG
#define LOG_ERROR(fmt, ...) LOG_TAG(LOG_ERROR , __func__, fmt, ##__VA_ARGS__)
#define LOG_WARNING(fmt, ...) LOG_TAG(LOG_WARNING, __func__, fmt, ##__VA_ARGS__)
#define LOG_INFO(fmt, ...) LOG_TAG(LOG_INFO , __func__, fmt, ##__VA_ARGS__)
#define LOG_DEBUG(fmt, ...) LOG_TAG(LOG_DEBUG , __func__, fmt, ##__VA_ARGS__)
/** Tagged LOG_* definitions */
#define LOG_TAG_ERROR(tag, fmt, ...) LOG_TAG(LOG_ERROR , tag, fmt, ##__VA_ARGS__)
#define LOG_TAG_WARNING(tag, fmt, ...) LOG_TAG(LOG_WARNING, tag, fmt, ##__VA_ARGS__)
#define LOG_TAG_INFO(tag, fmt, ...) LOG_TAG(LOG_INFO , tag, fmt, ##__VA_ARGS__)
#define LOG_TAG_DEBUG(tag, fmt, ...) LOG_TAG(LOG_DEBUG , tag, fmt, ##__VA_ARGS__)
/** definitions for source code compatibility with ESP-IDF */
#define ESP_EARLY_LOGE(tag, fmt, ...) LOG_TAG(LOG_ERROR , tag, fmt "\n", ##__VA_ARGS__)
#define ESP_EARLY_LOGW(tag, fmt, ...) LOG_TAG(LOG_WARNING, tag, fmt "\n", ##__VA_ARGS__)
#define ESP_EARLY_LOGI(tag, fmt, ...) LOG_TAG(LOG_INFO , tag, fmt "\n", ##__VA_ARGS__)
/*
#define ESP_EARLY_LOGI(tag, fmt, ...) ets_printf("I (%u) %s: " fmt "\n", \
system_get_time_ms(), tag, ##__VA_ARGS__)
*/
#define ESP_LOGE(tag, fmt, ...) LOG_TAG(LOG_ERROR , tag, fmt "\n", ##__VA_ARGS__)
#define ESP_LOGW(tag, fmt, ...) LOG_TAG(LOG_WARNING, tag, fmt "\n", ##__VA_ARGS__)
#define ESP_LOGI(tag, fmt, ...) LOG_TAG(LOG_INFO , tag, fmt "\n", ##__VA_ARGS__)
#if ENABLE_DEBUG
#define ESP_EARLY_LOGD(tag, fmt, ...) LOG_TAG(LOG_DEBUG, tag, fmt "\n", ##__VA_ARGS__)
#define ESP_EARLY_LOGV(tag, fmt, ...) LOG_TAG(LOG_ALL , tag, fmt "\n", ##__VA_ARGS__)
#define ESP_LOGD(tag, fmt, ...) LOG_TAG(LOG_DEBUG, tag, fmt "\n", ##__VA_ARGS__)
#define ESP_LOGV(tag, fmt, ...) LOG_TAG(LOG_ALL , tag, fmt "\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 */

View File

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

View File

@ -0,0 +1,66 @@
/*
* 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.
*
* FreeRTOS to RIOT-OS adaption module for source code compatibility
*/
#ifndef FREERTOS_FREERTOS_H
#define FREERTOS_FREERTOS_H
#ifndef DOXYGEN
#include "freertos/portmacro.h"
#ifdef __cplusplus
extern "C" {
#endif
#define configMAX_PRIORITIES SCHED_PRIO_LEVELS
#ifndef configASSERT
#define configASSERT assert
#endif
#define portTICK_PERIOD_MS 10
#define portTickType TickType_t
typedef int32_t BaseType_t;
typedef uint32_t UBaseType_t;
typedef uint32_t TickType_t;
uint32_t xPortGetTickRateHz(void);
BaseType_t xPortInIsrContext(void);
/*
* PLESE NOTE: Following definitions were copied directly from the FreeRTOS
* distribution and are under the following copyright:
*
* FreeRTOS V8.2.0 - Copyright (C) 2015 Real Time Engineers Ltd.
* All rights reserved
*
* FreeRTOS is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License (version 2) as published by the
* Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception.
*
* Full license text is available on the following
* link: http://www.freertos.org/a00114.html
*/
#define pdFALSE ( ( BaseType_t ) 0 )
#define pdTRUE ( ( BaseType_t ) 1 )
#define pdPASS ( pdTRUE )
#define pdFAIL ( pdFALSE )
#ifdef __cplusplus
}
#endif
#include "freertos/semphr.h"
#include "freertos/queue.h"
#endif /* DOXYGEN */
#endif /* FREERTOS_FREERTOS_H */

View File

@ -0,0 +1,46 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*
* FreeRTOS to RIOT-OS adaption module for source code compatibility
*/
#ifndef FREERTOS_EVENT_GROUPS_H
#define FREERTOS_EVENT_GROUPS_H
#ifndef DOXYGEN
#include "freertos/FreeRTOS.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef void * EventGroupHandle_t;
typedef TickType_t EventBits_t;
EventGroupHandle_t xEventGroupCreate (void);
void vEventGroupDelete (EventGroupHandle_t xEventGroup);
EventBits_t xEventGroupSetBits (EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToSet);
EventBits_t xEventGroupClearBits (EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToClear );
EventBits_t xEventGroupWaitBits (const EventGroupHandle_t xEventGroup,
const EventBits_t uxBitsToWaitFor,
const BaseType_t xClearOnExit,
const BaseType_t xWaitForAllBits,
TickType_t xTicksToWait);
#ifdef __cplusplus
}
#endif
#endif /* DOXYGEN */
#endif /* FREERTOS_EVENT_GROUPS_H */

View File

@ -0,0 +1,56 @@
/*
* 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.
*
* FreeRTOS to RIOT-OS adaption module for source code compatibility
*/
#ifndef FREERTOS_PORTMACRO_H
#define FREERTOS_PORTMACRO_H
#ifndef DOXYGEN
#include "stdint.h"
#include "mutex.h"
#include "irq.h"
#ifdef __cplusplus
extern "C" {
#endif
#define portBASE_TYPE int32_t
#define portUBASE_TYPE uint32_t
#define portMAX_DELAY 0xFFFFFFFF
#define portMUX_TYPE mutex_t
#define portMUX_INITIALIZER_UNLOCKED MUTEX_INIT
#define portENTER_CRITICAL(pm) mutex_lock(pm)
#define portEXIT_CRITICAL(pm) mutex_unlock(pm)
#define portENTER_CRITICAL_NESTED irq_disable
#define portEXIT_CRITICAL_NESTED irq_restore
#define portENTER_CRITICAL_ISR(mux) vTaskEnterCritical(mux)
#define portEXIT_CRITICAL_ISR(mux) vTaskExitCritical(mux)
#define taskENTER_CRITICAL(mux) portENTER_CRITICAL(mux)
#define taskENTER_CRITICAL_ISR(mux) portENTER_CRITICAL_ISR(mux)
#define taskEXIT_CRITICAL(mux) portEXIT_CRITICAL(mux)
#define taskEXIT_CRITICAL_ISR(mux) portEXIT_CRITICAL_ISR(mux)
#define portYIELD_FROM_ISR thread_yield_higher
#define portNUM_PROCESSORS 2
#define xPortGetCoreID() PRO_CPU_NUM
#ifdef __cplusplus
}
#endif
#endif /* DOXYGEN */
#endif /* FREERTOS_PORTMACRO_H */

View File

@ -0,0 +1,118 @@
/*
* 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.
*
* FreeRTOS to RIOT-OS adaption module for source code compatibility
*/
#ifndef FREERTOS_QUEUE_H
#define FREERTOS_QUEUE_H
#ifndef DOXYGEN
#include "freertos/FreeRTOS.h"
#ifdef __cplusplus
extern "C" {
#endif
#define xQueueHandle QueueHandle_t
typedef void* QueueHandle_t;
QueueHandle_t xQueueGenericCreate (const UBaseType_t uxQueueLength,
const UBaseType_t uxItemSize,
const uint8_t ucQueueType);
QueueHandle_t xQueueCreateCountingSemaphore (const UBaseType_t uxMaxCount,
const UBaseType_t uxInitialCount);
void vQueueDelete (QueueHandle_t xQueue);
BaseType_t xQueueGenericReset (QueueHandle_t xQueue, BaseType_t xNewQueue);
BaseType_t xQueueGenericReceive (QueueHandle_t xQueue,
void * const pvBuffer,
TickType_t xTicksToWait,
const BaseType_t xJustPeeking);
BaseType_t xQueueGenericSend (QueueHandle_t xQueue,
const void * const pvItemToQueue,
TickType_t xTicksToWait,
const BaseType_t xCopyPosition);
BaseType_t xQueueReceiveFromISR (QueueHandle_t xQueue, void * const pvBuffer,
BaseType_t * const pxHigherPriorityTaskWoken);
BaseType_t xQueueGenericSendFromISR (QueueHandle_t xQueue,
const void * const pvItemToQueue,
BaseType_t * const pxHigherPriorityTaskWoken,
const BaseType_t xCopyPosition );
BaseType_t xQueueGiveFromISR (QueueHandle_t xQueue,
BaseType_t * const pxHigherPriorityTaskWoken);
UBaseType_t uxQueueMessagesWaiting( QueueHandle_t xQueue );
/*
* PLESE NOTE: Following definitions were copied directly from the FreeRTOS
* distribution and are under the following copyright:
*
* FreeRTOS V8.2.0 - Copyright (C) 2015 Real Time Engineers Ltd.
* All rights reserved
*
* FreeRTOS is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License (version 2) as published by the
* Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception.
*
* Full license text is available on the following
* link: http://www.freertos.org/a00114.html
*/
#define queueSEND_TO_BACK ( ( BaseType_t ) 0 )
#define queueSEND_TO_FRONT ( ( BaseType_t ) 1 )
#define queueOVERWRITE ( ( BaseType_t ) 2 )
#define queueQUEUE_TYPE_BASE ( ( uint8_t ) 0U )
#define queueQUEUE_TYPE_SET ( ( uint8_t ) 0U )
#define queueQUEUE_TYPE_MUTEX ( ( uint8_t ) 1U )
#define queueQUEUE_TYPE_COUNTING_SEMAPHORE ( ( uint8_t ) 2U )
#define queueQUEUE_TYPE_BINARY_SEMAPHORE ( ( uint8_t ) 3U )
#define queueQUEUE_TYPE_RECURSIVE_MUTEX ( ( uint8_t ) 4U )
#define errQUEUE_EMPTY ( ( BaseType_t ) 0 )
#define errQUEUE_FULL ( ( BaseType_t ) 0 )
#define errQUEUE_BLOCKED ( -4 )
#define errQUEUE_YIELD ( -5 )
#define xQueueCreate( uxQueueLength, uxItemSize ) \
xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) )
#define xQueueReceive( xQueue, pvBuffer, xTicksToWait ) \
xQueueGenericReceive( ( xQueue ), ( pvBuffer ), ( xTicksToWait ), \
pdFALSE )
#define xQueueSend( xQueue, pvItemToQueue, xTicksToWait ) \
xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), \
queueSEND_TO_BACK )
#define xQueueSendToBack( xQueue, pvItemToQueue, xTicksToWait ) \
xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), \
queueSEND_TO_BACK )
#define xQueueSendFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) \
xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), \
( pxHigherPriorityTaskWoken ), \
queueSEND_TO_BACK )
#define xQueueReset( xQueue ) xQueueGenericReset( xQueue, pdFALSE )
#ifdef __cplusplus
}
#endif
#endif /* DOXYGEN */
#endif /* FREERTOS_QUEUE_H */

View File

@ -0,0 +1,81 @@
/*
* 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.
*
* FreeRTOS to RIOT-OS adaption module for source code compatibility
*/
#ifndef FREERTOS_SEMPHR_H
#define FREERTOS_SEMPHR_H
#ifndef DOXYGEN
#include "freertos/FreeRTOS.h"
#include <stdlib.h>
#include "mutex.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef void* SemaphoreHandle_t;
SemaphoreHandle_t xSemaphoreCreateMutex(void);
SemaphoreHandle_t xSemaphoreCreateRecursiveMutex(void);
void vSemaphoreDelete (SemaphoreHandle_t xSemaphore);
BaseType_t xSemaphoreGive (SemaphoreHandle_t xSemaphore);
BaseType_t xSemaphoreTake (SemaphoreHandle_t xSemaphore,
TickType_t xTicksToWait);
BaseType_t xSemaphoreGiveRecursive (SemaphoreHandle_t xSemaphore);
BaseType_t xSemaphoreTakeRecursive (SemaphoreHandle_t xSemaphore,
TickType_t xTicksToWait);
#define vPortCPUInitializeMutex(m) mutex_init(m)
void vPortCPUAcquireMutex (portMUX_TYPE *mux);
void vPortCPUReleaseMutex (portMUX_TYPE *mux);
/*
* PLESE NOTE: Following definitions were copied directly from the FreeRTOS
* distribution and are under the following copyright:
*
* FreeRTOS V8.2.0 - Copyright (C) 2015 Real Time Engineers Ltd.
* All rights reserved
*
* FreeRTOS is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License (version 2) as published by the
* Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception.
*
* Full license text is available on the following
* link: http://www.freertos.org/a00114.html
*/
#define semSEMAPHORE_QUEUE_ITEM_LENGTH ( ( uint8_t ) 0U )
#define xSemaphoreCreateBinary() \
xQueueGenericCreate( ( UBaseType_t ) 1, \
semSEMAPHORE_QUEUE_ITEM_LENGTH, \
queueQUEUE_TYPE_BINARY_SEMAPHORE )
#define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) \
xQueueCreateCountingSemaphore( ( uxMaxCount ), ( uxInitialCount ) )
#define xSemaphoreTakeFromISR( xSemaphore, pxHigherPriorityTaskWoken ) \
xQueueReceiveFromISR( ( QueueHandle_t ) ( xSemaphore ), \
NULL, ( pxHigherPriorityTaskWoken ) )
#define xSemaphoreGiveFromISR( xSemaphore, pxHigherPriorityTaskWoken ) \
xQueueGiveFromISR( ( QueueHandle_t ) ( xSemaphore ), \
( pxHigherPriorityTaskWoken ) )
#ifdef __cplusplus
}
#endif
#endif /* DOXYGEN */
#endif /* FREERTOS_SEMPHR_H */

View File

@ -0,0 +1,59 @@
/*
* 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.
*
* FreeRTOS to RIOT-OS adaption module for source code compatibility
*/
#ifndef FREERTOS_TASK_H
#define FREERTOS_TASK_H
#ifndef DOXYGEN
#include "thread.h"
#ifdef __cplusplus
extern "C" {
#endif
#define xTaskHandle TaskHandle_t
#define tskNO_AFFINITY INT_MAX
typedef void (*TaskFunction_t)(void *);
typedef void* TaskHandle_t;
BaseType_t xTaskCreate (TaskFunction_t pvTaskCode,
const char * const pcName,
const uint32_t usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pvCreatedTask);
BaseType_t xTaskCreatePinnedToCore (TaskFunction_t pvTaskCode,
const char * const pcName,
const uint32_t usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pvCreatedTask,
const BaseType_t xCoreID);
void vTaskDelete (TaskHandle_t xTaskToDelete);
void vTaskDelay (const TickType_t xTicksToDelay);
TaskHandle_t xTaskGetCurrentTaskHandle (void);
void vTaskEnterCritical (portMUX_TYPE *mux);
void vTaskExitCritical (portMUX_TYPE *mux);
TickType_t xTaskGetTickCount (void);
#ifdef __cplusplus
}
#endif
#endif /* DOXYGEN */
#endif /* FREERTOS_TASK_H */

View File

@ -0,0 +1,41 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*
* FreeRTOS to RIOT-OS adaption module for source code compatibility
*/
#ifndef FREERTOS_TIMERS_H
#define FREERTOS_TIMERS_H
#ifndef DOXYGEN
#include "freertos/FreeRTOS.h"
#include "xtimer.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef void* TimerHandle_t;
#define TimerCallbackFunction_t xtimer_callback_t
TimerHandle_t xTimerCreate (const char * const pcTimerName,
const TickType_t xTimerPeriod,
const UBaseType_t uxAutoReload,
void * const pvTimerID,
TimerCallbackFunction_t pxCallbackFunction);
BaseType_t xTimerDelete(TimerHandle_t xTimer, TickType_t xBlockTime);
BaseType_t xTimerStart (TimerHandle_t xTimer, TickType_t xBlockTime);
BaseType_t xTimerStop (TimerHandle_t xTimer, TickType_t xBlockTime);
#ifdef __cplusplus
}
#endif
#endif /* DOXYGEN */
#endif /* FREERTOS_TIMERS_H */

View File

@ -0,0 +1,24 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*
* FreeRTOS to RIOT-OS adaption module for source code compatibility
*/
#ifndef FREERTOS_XTENSA_API_H
#define FREERTOS_XTENSA_API_H
#include "xtensa/xtensa_api.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
#endif /* FREERTOS_XTENSA_API_H */

View File

@ -0,0 +1,129 @@
/*
* 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_esp32
* @ingroup drivers_periph_gpio
* @{
*
* @file
* @brief Architecture specific GPIO functions ESP32
*
* @author Gunar Schorcht <gunar@schorcht.net>
* @}
*/
#ifndef GPIO_ARCH_H
#define GPIO_ARCH_H
#include "periph/gpio.h"
#include "soc/io_mux_reg.h"
#include "soc/gpio_sig_map.h"
#ifndef DOXYGEN
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief 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
/**
* @brief Definition of possible GPIO usage types (for internal use only)
*/
typedef enum
{
_GPIO = 0, /**< pin used as standard GPIO */
_ADC, /**< pin used as ADC input */
_CAN, /**< pin used as CAN signal */
_DAC, /**< pin used as DAC output */
_EMAC, /**< pin used as EMAC signal */
_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;
/**
* @brief Table of GPIO to IOMUX register mappings
*/
extern const uint32_t _gpio_to_iomux_reg[];
#define GPIO_PIN_MUX_REG _gpio_to_iomux_reg
/**
* @brief Set the usage type of the pin
* @param pin GPIO pin
* @param usage GPIO pin usage type
* @return 0 on succes
* -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 succes
* _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 succes
* _NOT_EXIST on error
*/
const char* gpio_get_pin_usage_str(gpio_t pin);
/**
* @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);
#ifdef __cplusplus
}
#endif
#endif /* DOXYGEN */
#endif /* GPIO_ARCH_H */

View File

@ -0,0 +1,105 @@
/*
* 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_esp32
* @{
*
* @file
* @brief Implementation of the kernels irq interface
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
#ifndef IRQ_ARCH_H
#define IRQ_ARCH_H
#include "irq.h"
#include "sched.h"
#include "thread.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Indicates the interrupt nesting depth
*
* The variable is increment on entry into and decremented on exit from an ISR.
*/
extern volatile uint32_t irq_interrupt_nesting;
/**
* @brief fixed allocated CPU interrupt numbers that are used by RIOT
* @{
*/
#define CPU_INUM_GPIO 2 /* level interrupt, low priority = 1 */
#define CPU_INUM_CAN 3 /* level interrupt, low priority = 1 */
#define CPU_INUM_UART 5 /* level interrupt, low priority = 1 */
#define CPU_INUM_RTC 9 /* level interrupt, low priority = 1 */
#define CPU_INUM_I2C 12 /* level interrupt, low priority = 1 */
#define CPU_INUM_WDT 13 /* level interrupt, low priority = 1 */
#define CPU_INUM_SOFTWARE 17 /* level interrupt, low priority = 1 */
#define CPU_INUM_ETH 18 /* level interrupt, low priority = 1 */
#define CPU_INUM_TIMER 19 /* level interrupt, medium priority = 2 */
/** @} */
#if defined(SDK_INT_HANDLING) || defined(DOXYGEN)
/**
* @brief Macros that have to be used on entry into and reset from an ISR
*
* NOTE: since they use a local variable they can be used only in same function
* @{
*/
/** Macro that has to be used at the entry point of an ISR */
#define irq_isr_enter() int _irq_state = irq_disable (); \
irq_interrupt_nesting++;
/** Macro that has to be used at the exit point of an ISR */
#define irq_isr_exit() if (irq_interrupt_nesting) \
irq_interrupt_nesting--; \
irq_restore (_irq_state); \
if (sched_context_switch_request) \
thread_yield();
#else /* SDK_INT_HANDLING */
/* in non SDK task handling all the stuff is done in _frxt_int_enter and _frxt_int_exit */
#define irq_isr_enter() /* int _irq_state = irq_disable (); \
irq_interrupt_nesting++; */
#define irq_isr_exit() /* if (irq_interrupt_nesting) \
irq_interrupt_nesting--; \
irq_restore (_irq_state); */
#endif /* SDK_INT_HANDLING */
/**
* @brief Macros to enter and exit from critical region
*
* NOTE: since they use a local variable they can be used only in same function
* @{
*/
#define critical_enter() int _irq_state = irq_disable ()
#define critical_exit() irq_restore(_irq_state)
/**
* @brief Macros to enter and exit from critical region with state variable
*/
#define critical_enter_var(m) m = irq_disable ()
#define critical_exit_var(m) irq_restore(m)
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* IRQ_ARCH_H */

View File

@ -0,0 +1,47 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp32
* @{
*
* @file
* @brief Log module to realize consistent log messages
*
* @author Gunar Schorcht <gunar@schorcht.net>
*/
#ifndef LOG_MODULE_H
#define LOG_MODULE_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief log_write overridden function
*
* @param[in] level (unused)
* @param[in] format String that the function will print
*/
void log_write(unsigned level, const char *format, ...);
/**
* @brief log_write overridden function, tagged version
*
* @param[in] level Level of the message
* @param[in] tag Additional information like function or module
* @param[in] format String that the function will print
*/
void log_write_tagged(unsigned level, const char *tag, const char *format, ...);
#ifdef __cplusplus
}
#endif
/**@}*/
#endif /* LOG_MODULE_H */

View File

@ -0,0 +1,447 @@
/*
* 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_esp32
* @{
*
* @file
* @brief CPU specific definitions and functions for peripheral handling
*
* @author Gunar Schorcht <gunar@schorcht.net>
*/
#ifndef PERIPH_CPU_H
#define PERIPH_CPU_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @name Power management configuration
* @{
*/
#define PROVIDES_PM_SET_LOWEST
#define PROVIDES_PM_RESTART
#define PROVIDES_PM_OFF
/** @} */
/**
* @brief Length of the CPU_ID in octets
*/
#define CPUID_LEN (7U)
/**
* @brief Available ports on the ESP32
* @{
*/
#define PORT_GPIO 0 /**< port GPIO */
/** @} */
/**
* @brief Definition of a fitting UNDEF value
*/
#define GPIO_UNDEF (0xff)
/**
* @brief Define CPU specific GPIO pin generator macro
*/
#define GPIO_PIN(x, y) ((x << 4) | y)
/**
* @brief Define CPU specific number of GPIO pins
* @{
*/
#define GPIO_PIN_NUMOF 40
#ifndef GPIO_PIN_COUNT
#define GPIO_PIN_COUNT GPIO_PIN_NUMOF
#endif
/** @} */
/**
* @name Predefined GPIO names
* @{
*/
#define GPIO0 (GPIO_PIN(PORT_GPIO,0))
#define GPIO1 (GPIO_PIN(PORT_GPIO,1))
#define GPIO2 (GPIO_PIN(PORT_GPIO,2))
#define GPIO3 (GPIO_PIN(PORT_GPIO,3))
#define GPIO4 (GPIO_PIN(PORT_GPIO,4))
#define GPIO5 (GPIO_PIN(PORT_GPIO,5))
#define GPIO6 (GPIO_PIN(PORT_GPIO,6))
#define GPIO7 (GPIO_PIN(PORT_GPIO,7))
#define GPIO8 (GPIO_PIN(PORT_GPIO,8))
#define GPIO9 (GPIO_PIN(PORT_GPIO,9))
#define GPIO10 (GPIO_PIN(PORT_GPIO,10))
#define GPIO11 (GPIO_PIN(PORT_GPIO,11))
#define GPIO12 (GPIO_PIN(PORT_GPIO,12))
#define GPIO13 (GPIO_PIN(PORT_GPIO,13))
#define GPIO14 (GPIO_PIN(PORT_GPIO,14))
#define GPIO15 (GPIO_PIN(PORT_GPIO,15))
#define GPIO16 (GPIO_PIN(PORT_GPIO,16))
#define GPIO17 (GPIO_PIN(PORT_GPIO,17))
#define GPIO18 (GPIO_PIN(PORT_GPIO,18))
#define GPIO19 (GPIO_PIN(PORT_GPIO,19))
/* GPIO 20 is not available */
#define GPIO21 (GPIO_PIN(PORT_GPIO,21))
#define GPIO22 (GPIO_PIN(PORT_GPIO,22))
#define GPIO23 (GPIO_PIN(PORT_GPIO,23))
/* GPIO 24 is not available */
#define GPIO25 (GPIO_PIN(PORT_GPIO,25))
#define GPIO26 (GPIO_PIN(PORT_GPIO,26))
#define GPIO27 (GPIO_PIN(PORT_GPIO,27))
/* GPIOs 28 ...32 are not available */
#define GPIO32 (GPIO_PIN(PORT_GPIO,32))
#define GPIO33 (GPIO_PIN(PORT_GPIO,33))
/* GPIOs 34 ... 39 can only be used as inputs and do not have pullups/pulldowns */
#define GPIO34 (GPIO_PIN(PORT_GPIO,34))
#define GPIO35 (GPIO_PIN(PORT_GPIO,35))
#define GPIO36 (GPIO_PIN(PORT_GPIO,36))
#define GPIO37 (GPIO_PIN(PORT_GPIO,37))
#define GPIO38 (GPIO_PIN(PORT_GPIO,38))
#define GPIO39 (GPIO_PIN(PORT_GPIO,39))
/** @} */
/**
* @brief Override mode 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;
/** @} */
/**
* @brief Override GPIO modes
*
* @{
*/
#define HAVE_GPIO_MODE_T
typedef enum {
GPIO_IN, /**< input */
GPIO_IN_PD, /**< input with pull-down */
GPIO_IN_PU, /**< input with pull-up */
GPIO_OUT, /**< output */
GPIO_OD, /**< open-drain output */
GPIO_OD_PU, /**< open-drain output with pull-up */
GPIO_IN_OUT, /**< input and output */
GPIO_IN_OD, /**< input and open-drain output */
GPIO_IN_OD_PU /**< input and open-drain output */
} gpio_mode_t;
/** @} */
/**
* @name ADC configuration
*
* ESP32 integrates two 12-bit ADCs (ADC1 and ADC2) capable of measuring up to
* 18 analog signals in total. Most of these ADC channels are either connected
* to a number of intergrated sensors like a Hall sensors, touch sensors and a
* temperature sensor or can be connected with certain GPIOs. Integrated
* sensors are disabled in RIOT's implementation and are not accessible. Thus,
* up to 18 GPIOs, the RTC GPIOs, can be used as ADC inputs:
*
* - ADC1 supports 8 channels: GPIO 32-39
* - ADC2 supports 10 channels: GPIO 0, 2, 4, 12-15, 25-27
*
* For each ADC line, an attenuation of the input signal can be defined
* separately unsing function *adc_set_attenuation*, see file
* ```$RIOTBASE/cpu/esp32/include/adc_arch.h```.
* This results in different full ranges of the measurable voltage
* at the input. The attenuation can be set to 0 dB, 3 dB, 6 dB and 11 dB,
* with 11 dB being the standard attenuation. Since an ADC input is measured
* against a reference voltage Vref of 1.1 V, approximately the following
* measurement ranges are given when using a corresponding attenuation:
*
* <center>
*
* Attenuation | Voltage Range | Symbol
* ----------------|-------------------|----------------------
* 0 dB | 0 ... 1.1V (Vref) | ADC_ATTENUATION_0_DB
* 3 dB | 0 ... 1.5V | ADC_ATTENUATION_3_DB
* 6 dB | 0 ... 2.2V | ADC_ATTENUATION_6_DB
* 11 dB (default) | 0 ... 3.3V | ADC_ATTENUATION_11_DB
*
* </center>
*
* Please note: The reference voltage Vref can vary from device to device in
* the range of 1.0V and 1.2V. The Vref of a device can be read with the
* function *adc_vref_to_gpio25* at the pin GPIO 25, see file
* ```$RIOTBASE/cpu/esp32/include/adc_arch.h```. The results of the ADC
* input can then be adjusted accordingly.
*
* ADC_GPIOS in the board-specific peripheral configuration defines a list of
* GPIOs that can be used as ADC channels. The order of the listed GPIOs
* determines the mapping between the RIOT's ADC lines and the GPIOs.
*
* @note ADC_GPIOS must be defined even if there are no GPIOs that could be
* used as ADC channels on the board. In this case, an empy list hast to be
* defined which just contains the curly braces.
*
* ADC_NUMOF is determined automatically from the ADC_GPIOS definition.
*
* @note As long as the GPIOs listed in ADC_GPIOS are not initialized
* as ADC channels with the *adc_init* function, they can be used for other
* purposes.
*
* @{
*/
/**
* @brief Possible ADC resolution settings
*/
#define HAVE_ADC_RES_T
typedef enum {
ADC_RES_9BIT = 0, /**< ADC resolution: 9 bit */
ADC_RES_10BIT, /**< ADC resolution: 10 bit */
ADC_RES_11BIT, /**< ADC resolution: 11 bit */
ADC_RES_12BIT, /**< ADC resolution: 12 bit */
} adc_res_t;
/** @} */
/**
* @brief Number of ADC cahnnels that could be used at maximum
*
* @note GPIO37 and GPIO38 are usually not broken out on ESP32 modules and are
* therefore not usable. The maximum number of ADC channels (ADC_NUMOF_MAX)
* is therefore set to 16.
*/
#define ADC_NUMOF_MAX 16
/** Number of ADC channels determined from ADC_GPIOS */
extern const unsigned adc_chn_num;
/** @} */
/**
* @name DAC configuration
*
* ESP32 supports 2 DAC lines at GPIO25 and GPIO26. These DACs have a width of
* 8 bits and produce voltages in the range from 0 V to 3.3 V (VDD_A). The 16
* bits DAC values given as parameter of function *dac_set* are down-scaled
* to 8 bit.
*
* DAC_GPIOS in the board-specific peripheral configuration defines a list of
* GPIOs that can be used as DAC channels. The order of the listed GPIOs
* determines the mapping between the RIOT's DAC lines and the GPIOs.
*
* @note DAC_GPIOS must be defined even if there are no GPIOs that could be
* used as DAC channels on the board. In this case, an empy list hast to be
* defined which just contains the curly braces.
*
* DAC_NUMOF is determined automatically from the DAC_GPIOS definition.
*
* @note As long as the GPIOs listed in DAC_GPIOS are not initialized
* as DAC channels with the *dac_init* function, they can be used for other
* purposes.
* @{
*/
/**
* @brief Number of DAC cahnnels that could be used at maximum.
*/
#define DAC_NUMOF_MAX 2
/** Number of DAC channels determined from DAC_GPIOS */
extern const unsigned dac_chn_num;
/** @} */
/**
* @name I2C configuration
*
* ESP32 has two built-in I2C interfaces.
*
* The board-specific configuration of the I2C interface I2C_DEV(n) requires
* the defintion 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.
*
* @{
*/
/** Number of I2C interfaces determined from I2Cn_* definitions */
extern const unsigned i2c_bus_num;
#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 */
/** @} */
/**
* @name PWM configuration
* @{
*
* PWM implementation uses ESP32's high-speed MCPWM modules. ESP32 has 2 such
* modules, each with up to 6 channels (PWM_CHANNEL_NUM_DEV_MAX). Thus, the
* maximum number of PWM devices is 2 and the maximum total number of PWM
* channels is 12.
*
* PWM0_GPIOS and PWM1_GPIOS in the board-specific peripheral configuration
* each define a list of GPIOs that can be used with the respective PWM
* devices as PWM channels. The order of the listed GPIOs determines the
* association between the RIOT PWM channels and the GPIOs.
*
* @note The definition of PWM0_GPIOS and PWM1_GPIOS can be omitted or
* empty. In the latter case, they must at least contain the curly braces.
* The corresponding PWM device can not be used in this case.
*
* PWM_NUMOF is determined automatically from the PWM0_GPIOS and PWM1_GPIOS
* definitions.
*
* @note As long as the GPIOs listed in PWM0_GPIOS and PMW1_GPIOS are not
* initialized as PWM channels with the *pwm_init* function, they can be used
* other purposes.
*/
/**
* @brief Maximum number of channels per PWM device.
*/
#define PWM_CHANNEL_NUM_DEV_MAX (6)
/** Number of PWM devices determined from PWM0_GPIOS and PWM1_GPIOS. */
extern const unsigned pwm_dev_num;
/** @} */
/**
* @name SPI configuration
*
* ESP32 has four SPI controllers:
*
* - controller SPI0 is reserved for accessing flash memory
* - controller SPI1 realizes interface FSPI and shares its signals with SPI0
* - controller SPI2 realizes interface HSPI that can be used for peripherals
* - controller SPI3 realizes interface VSPI that can be used for peripherals
*
* At most three interfaces can be used:
*
* - VSPI with configurable pin definitions
* - HSPI with configurable pin definitions
* - FSPI with fixed pin definitions except the CS signal
*
* All SPI interfaces could be used in quad SPI mode, but RIOT's low level
* device driver doesn't support it.
*
* @note
* - Since the FSPI interface shares its bus signals with the controller
* that implements the flash memory interface, we use the name FSPI for this
* interface. In the technical reference, this interface is misleadingly
* simply referred to as SPI.
* - Since the FSPI interface shares its bus signals with flash
* memory interface and optionally other external memories, you can only use
* this SPI interface to attach external memory with same SPI mode and same
* bus speed but with a different CS.
* - Using FSPI for anything else can disturb flash memory access which
* causes a number of problems. If not really necessary, you should not use
* this interface.
*
* The board-specific configuration of the SPI interface SPI_DEV(n) requires
* the defintion of
*
* SPIn_DEV, the interface which can be VSPI, HSPI, or FSPI,
* SPIn_SCK, the GPIO used as clock signal (fixed for FSPI),
* SPIn_MISO, the GPIO used as MISO signal (fixed for FSPI),
* SPIn_MOSI, the GPIO used as MOSI signal (fixed for FSPI), and
* SPIn_CS0, the GPIO used as CS signal when cs parameter in spi_aquire is GPIO_UNDEF,
*
* where n can be 0, 1 or 2. If they are not defined, the SPI interface
* SPI_DEV(n) is not used.
*
* @note The configuration of the SPI interfaces SPI_DEV(n) must be in
* continuous ascending order of n.
*
* SPI_NUMOF is determined automatically from the board-specific peripheral
* definitions of SPIn_*.
*/
/** Number of SPI interfaces determined from SPI_* definitions */
extern const unsigned spi_bus_num;
#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 */
/** @} */
/**
* @name Timer configuration depenend on which implementation is used
*
* Timers are MCU built-in feature and not board-specific. They are therefore
* configured here.
* @{
*/
#ifdef MODULE_ESP_HW_COUNTER
/** hardware ccount/ccompare registers are used for timer implementation */
#define TIMER_NUMOF (2)
#define TIMER_CHANNELS (1)
#else
/** hardware timer modules are used for timer implementation (default) */
#define TIMER_NUMOF (3)
#define TIMER_CHANNELS (1)
#endif
/** Timer used for system time */
#define TIMER_SYSTEM TIMERG0.hw_timer[0]
/** @} */
/**
* @name UART configuration
*
* ESP32 supports up to three UART devices. UART_DEV(0) has a fixed pin
* configuration and is always available. All ESP32 boards use it as standard
* configuration for the console.
*
* UART_DEV(0).TXD GPIO1
* UART_DEV(0).RXD GPIO3
*
* The pin configuration of UART_DEV(1) and UART_DEV(2) are defined in
* board specific peripheral configuration by
*
* UARTn_TXD, the GPIO used as TxD signal, and
* UARTn_RXD, the GPIO used as RxD signal,
*
* where n can be 2 or 3. If they are not defined, the UART interface
* UART_DEV(n) is not used.
*
* UART_NUMOF is determined automatically from the board-specific peripheral
* definitions of UARTn_TXD and UARTn_RXD.
*
* @{
*/
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* PERIPH_CPU_H */
/** @} */

View File

@ -0,0 +1,173 @@
/*
* 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_esp32
* @{
*
* @file
* @brief SDK configuration compatible to the ESP-IDF
*
* The SDK configuration can be partially overriden by application-specific
* board configuration.
*
* @author Gunar Schorcht <gunar@schorcht.net>
*/
#ifndef SDK_CONF_H
#define SDK_CONF_H
#ifndef DOXYGEN
#ifdef __cplusplus
extern "C" {
#endif
#include "board.h"
/**
* @brief Defines the CPU frequency [vallues = 2, 40, 80, 160 and 240]
*/
#ifndef CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ
#define CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ 80
#endif
/**
* Default console configuration
*
* STDIO_UART_BAUDRATE is used as CONFIG_CONSOLE_UART_BAUDRATE and
* can be overriden 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
/**
* Log output configuration (DO NOT CHANGE)
*/
#ifndef CONFIG_LOG_DEFAULT_LEVEL
#define CONFIG_LOG_DEFAULT_LEVEL LOG_LEVEL
#endif
/**
* ESP32 specific configuration
*
* CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ can be overriden by an application
* specific SDK configuration file.
*/
#ifndef CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ
#define CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ 80
#endif
#define CONFIG_ESP32_RTC_XTAL_BOOTSTRAP_CYCLES 100
#define CONFIG_ESP32_RTC_CLK_CAL_CYCLES 1024
/**
* System specific configuration (DO NOT CHANGE)
*/
#define CONFIG_TRACEMEM_RESERVE_DRAM 0
#define CONFIG_ULP_COPROC_RESERVE_MEM 0
#define CONFIG_SYSTEM_EVENT_QUEUE_SIZE 32
#define CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE 2048
#define CONFIG_NUMBER_OF_UNIVERSAL_MAC_ADDRESS 4
#define CONFIG_NEWLIB_NANO_FORMAT 0
/**
* Bluetooth configuration (DO NOT CHANGE)
*/
#define CONFIG_BT_ENABLED 0
#define CONFIG_BT_RESERVE_DRAM 0
/**
* SPI RAM configuration (DO NOT CHANGE)
*/
#ifdef MODULE_ESP_SPI_RAM
#define CONFIG_SPIRAM_SUPPORT 1
#else
#define CONFIG_SPIRAM_SUPPORT 0
#endif
#define CONFIG_SPIRAM_SPEED_40M 1
#define CONFIG_SPIRAM_SIZE 4194304
#define CONFIG_SPIRAM_BOOT_INIT 1
#define CONFIG_SPIRAM_USE_MALLOC 1
#define CONFIG_SPIRAM_TYPE_ESPPSRAM32 1
#define CONFIG_SPIRAM_MEMTEST 1
#define CONFIG_SPIRAM_CACHE_WORKAROUND 1
#define CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL 16384
#define CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL 32768
/**
* SPI Flash driver configuration (DO NOT CHANGE)
*/
#define CONFIG_SPI_FLASH_ROM_DRIVER_PATCH 1
/**
* Ethernet driver configuration (DO NOT CHANGE)
*/
#define CONFIG_DMA_RX_BUF_NUM 10
#define CONFIG_DMA_TX_BUF_NUM 10
#define CONFIG_EMAC_TASK_PRIORITY 20
/**
* Serial flasher config (DO NOT CHANGE)
*/
#define CONFIG_ESPTOOLPY_FLASHFREQ_40M 1
#if defined(FLASH_MODE_QIO)
#define CONFIG_FLASHMODE_QIO 1
#elif defined(FLASH_MODE_QOUT)
#define CONFIG_FLASHMODE_QOUT 1
#elif defined(FLASH_MODE_DIO)
#define CONFIG_FLASHMODE_DIO 1
#else
#define CONFIG_FLASHMODE_DOUT 1
#endif
/**
* Wi-Fi driver configuration (DO NOT CHANGE)
*/
#define CONFIG_ESP32_WIFI_TX_BUFFER_TYPE 1
#define CONFIG_ESP32_WIFI_STATIC_TX_BUFFER 0
#define CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER 1
#define CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM 48
#define CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM 10
#define CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM 64
#define CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED 1
#define CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED 1
#define CONFIG_ESP32_WIFI_TX_BA_WIN 6
#define CONFIG_ESP32_WIFI_RX_BA_WIN 6
#define CONFIG_ESP32_WIFI_CSI_ENABLED 0
#define CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_0 1
#define CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_1 0
#define CONFIG_ESP32_WIFI_NVS_ENABLED 0
/**
* PHY configuration
*/
#define CONFIG_ESP32_PHY_MAX_TX_POWER 20
#define CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER 20
#define CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION 0
#if MODULE_ESP_IDF_NVS_ENABLED
#define CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE 1
#endif
/**
* EMAC driver configuration (DO NOT CHANGE)
*/
#define CONFIG_EMAC_L2_TO_L3_RX_BUF_MODE 1
#ifdef __cplusplus
}
#endif
#endif /* DOXYGEN */
#endif /* SDK_CONF_H */

748
cpu/esp32/include/stdio.h Normal file
View File

@ -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 <sys/cdefs.h>
#include <stddef.h>
#define __need___va_list
#include <stdarg.h>
/*
* <sys/reent.h> 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 <sys/reent.h>
#include <sys/types.h>
_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 <sys/stdio.h>
#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 */

View File

@ -0,0 +1,336 @@
/*
* 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_esp32
* @{
*
* @file
* @brief This file is a modification of original sys/types.h
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* This file is just a wrapper around sys/types.h to fix missing types
* fsblkcnt_t and fsfilcnt_t needed in statvfs.h
*/
/* 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 <stdint.h>
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 <machine/_types.h>
#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 <sys/_types.h>
#ifdef __i386__
#if defined (GO32) || defined (__MSDOS__)
#define __MS_types__
#endif
#endif
# include <stddef.h>
# include <machine/types.h>
/* 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 */

View File

@ -0,0 +1,68 @@
/*
* 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_esp32
* @{
*
* @file
* @brief Implementation of required system calls
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
#ifndef SYSCALLS_H
#define SYSCALLS_H
#include <stdarg.h>
#include <stdbool.h>
#include <stdlib.h>
#include "esp_common.h"
#ifdef __cplusplus
extern "C" {
#endif
/** Necessary initializations of system call functions */
void syscalls_init (void);
/** System standard printf function */
int printf(const char* format, ...);
/** Determine free heap size */
unsigned int get_free_heap_size (void);
/** Time since boot in us (32bit version) */
uint32_t system_get_time (void);
/** Time since boot in us (64bit version) */
uint64_t system_get_time_64 (void);
/** Time since boot in ms (32bit version) */
uint32_t system_get_time_ms (void);
/** initialize system watchdog timer ans start it */
void system_wdt_init (void);
/** start the initialized system watchdog timer */
void system_wdt_start (void);
/** start the running system watchdog timer */
void system_wdt_stop (void);
/** reset the system watchdog timer */
void system_wdt_feed (void);
#ifdef __cplusplus
}
#endif
#endif /* SYSCALLS_H */

View File

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

44
cpu/esp32/include/tools.h Normal file
View File

@ -0,0 +1,44 @@
/*
* 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_esp32
* @{
*
* @file
* @brief Implementation of some tools
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
#ifndef TOOLS_H
#define TOOLS_H
#include <stdint.h>
#include <inttypes.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Hexdump of memory
* @param[in] addr start address in memory
* @param[in] num number of items to dump
* @param[in] width format of the items
* @param[in] per_line number of items per line
*/
void esp_hexdump (const void* addr, uint32_t num, char width, uint8_t per_line);
#ifdef __cplusplus
}
#endif
#endif /* TOOLS_H */

View File

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

171
cpu/esp32/irq_arch.c Normal file
View File

@ -0,0 +1,171 @@
/*
* 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_esp32
* @{
*
* @file
* @brief Implementation of the kernels irq interface
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
#define ENABLE_DEBUG 0
#include "debug.h"
#include <stdint.h>
#include <stdio.h>
#include "irq.h"
#include "irq_arch.h"
#include "cpu.h"
#include "esp_common.h"
#include "esp_err.h"
#include "esp/common_macros.h"
#include "esp/xtensa_ops.h"
#include "rom/ets_sys.h"
#include "soc/dport_reg.h"
#include "xtensa/xtensa_context.h"
#include "xtensa/xtensa_api.h"
extern unsigned _xtos_set_intlevel(unsigned intlevel);
/**
* @brief Set on entry into and reset on exit from an ISR
*/
volatile uint32_t irq_interrupt_nesting = 0;
/**
* @brief Disable all maskable interrupts
*/
unsigned int IRAM irq_disable(void)
{
uint32_t _saved_intlevel;
/* read and set interrupt level with one asm instruction (RSIL) */
__asm__ volatile ("rsil %0, " XTSTR(XCHAL_NUM_INTLEVELS+1) : "=a" (_saved_intlevel));
/* mask out everything else of the PS register that do not belong to
interrupt level (bits 3..0) */
_saved_intlevel &= 0xf;
DEBUG ("%s new %08x (old %08x)\n", __func__,
XCHAL_NUM_INTLEVELS + 1, _saved_intlevel);
return _saved_intlevel;
}
/**
* @brief Enable all maskable interrupts
*/
unsigned int IRAM irq_enable(void)
{
uint32_t _saved_intlevel;
/* read and set interrupt level with one asm instruction (RSIL) */
__asm__ volatile ("rsil %0, 0" : "=a" (_saved_intlevel));
/* mask out everything else of the PS register that do not belong to
interrupt level (bits 3..0) */
_saved_intlevel &= 0xf;
DEBUG ("%s new %08x (old %08x)\n", __func__, 0, _saved_intlevel);
return _saved_intlevel;
}
/**
* @brief Restore the state of the IRQ flags
*/
void IRAM irq_restore(unsigned int state)
{
/* restore the interrupt level using a rom function, performance is not
important here */
#if 0
__asm__ volatile ("wsr %0, ps; rsync" :: "a" (state));
DEBUG ("%s %02x\n", __func__, state);
#else
unsigned _saved_intlevel = _xtos_set_intlevel(state);
DEBUG ("%s new %08x (old %08x)\n", __func__, state, _saved_intlevel);
#endif
}
/**
* @brief See if the current context is inside an ISR
*/
int IRAM irq_is_in(void)
{
DEBUG("irq_interrupt_nesting = %d\n", irq_interrupt_nesting);
return irq_interrupt_nesting;
}
struct _irq_alloc_table_t {
int src; /* peripheral interrupt source */
uint32_t intr; /* interrupt number */
};
static const struct _irq_alloc_table_t _irq_alloc_table[] = {
{ ETS_FROM_CPU_INTR0_SOURCE, CPU_INUM_SOFTWARE },
{ ETS_TG0_WDT_LEVEL_INTR_SOURCE, CPU_INUM_WDT },
{ ETS_TG0_T0_LEVEL_INTR_SOURCE, CPU_INUM_RTC },
{ ETS_TG0_T1_LEVEL_INTR_SOURCE, CPU_INUM_TIMER },
{ ETS_TG1_T0_LEVEL_INTR_SOURCE, CPU_INUM_TIMER },
{ ETS_TG1_T1_LEVEL_INTR_SOURCE, CPU_INUM_TIMER },
{ ETS_UART0_INTR_SOURCE, CPU_INUM_UART },
{ ETS_UART1_INTR_SOURCE, CPU_INUM_UART },
{ ETS_UART2_INTR_SOURCE, CPU_INUM_UART },
{ ETS_GPIO_INTR_SOURCE, CPU_INUM_GPIO },
{ DPORT_PRO_RTC_CORE_INTR_MAP_REG, CPU_INUM_RTC },
{ ETS_I2C_EXT0_INTR_SOURCE, CPU_INUM_I2C },
{ ETS_I2C_EXT1_INTR_SOURCE, CPU_INUM_I2C },
{ ETS_ETH_MAC_INTR_SOURCE, CPU_INUM_ETH },
{ ETS_CAN_INTR_SOURCE, CPU_INUM_CAN }
};
typedef void (*intr_handler_t)(void *arg);
#define IRQ_ALLOC_TABLE_SIZE (sizeof(_irq_alloc_table)/sizeof(struct _irq_alloc_table_t))
#define ESP_INTR_FLAG_INTRDISABLED (1<<11)
/**
* @brief Emulates the according ESP-IDF function
*
* This function provides a compatible interface to the ESP-IDF function for
* source code compatibility. In difference to ESP-IDF it uses a fix interrupt
* assignment scheme based on a very limited number peripheral interrupt
* signals.
*
* TODO free allocation scheme as in ESP-IDF
*/
esp_err_t esp_intr_alloc(int source, int flags, intr_handler_t handler,
void *arg, void *ret_handle)
{
unsigned i;
for (i = 0; i < IRQ_ALLOC_TABLE_SIZE; i++) {
if (_irq_alloc_table[i].src == source) {
break;
}
}
if (i == IRQ_ALLOC_TABLE_SIZE) {
return ESP_ERR_NOT_FOUND;
}
/* route the interrupt source to the CPU interrupt */
intr_matrix_set(PRO_CPU_NUM, _irq_alloc_table[i].src, _irq_alloc_table[i].intr);
/* set the interrupt handler */
xt_set_interrupt_handler(_irq_alloc_table[i].intr, handler, arg);
/* enable the interrupt if ESP_INTR_FLAG_INTRDISABLED is not set */
if ((flags & ESP_INTR_FLAG_INTRDISABLED) == 0) {
xt_ints_on(BIT(_irq_alloc_table[i].intr));
}
return ESP_OK;
}

1
cpu/esp32/ld/README.md Normal file
View File

@ -0,0 +1 @@
The files in this directory are originally from the [ESP-IDF](https://github.com/espressif/esp-idf) and modified for RIOT-OS. All these files are under the copyright of their respective ownwers and licensed under the Apache License, Version 2.0, [see](https://github.com/espressif/esp-idf/blob/master/LICENSE).

View File

@ -0,0 +1,247 @@
/* Default entry point: */
ENTRY(call_start_cpu0);
SECTIONS
{
/* RTC fast memory holds RTC wake stub code,
including from any source file named rtc_wake_stub*.c
*/
.rtc.text :
{
. = ALIGN(4);
*(.rtc.literal .rtc.text)
*rtc_wake_stub*.o(.literal .text .literal.* .text.*)
} >rtc_iram_seg
/* RTC bss, from any source file named rtc_wake_stub*.c */
.rtc.bss (NOLOAD) :
{
/* part that is initialized if not waking up from deep sleep */
_rtc_bss_start = ABSOLUTE(.);
*rtc_wake_stub*.o(.bss .bss.*)
*rtc_wake_stub*.o(COMMON)
_rtc_bss_end = ABSOLUTE(.);
/* part that saves some data for rtc periph module, this part is
only initialized at power on reset */
_rtc_bss_rtc_start = ABSOLUTE(.);
*(.rtc.bss .rtc.bss.*)
_rtc_bss_rtc_end = ABSOLUTE(.);
} > rtc_slow_seg
/* RTC slow memory holds RTC wake stub
data/rodata, including from any source file
named rtc_wake_stub*.c
*/
.rtc.data :
{
_rtc_data_start = ABSOLUTE(.);
*(.rtc.data)
*(.rtc.rodata)
*rtc_wake_stub*.o(.data .rodata .data.* .rodata.* .bss .bss.*)
_rtc_data_end = ABSOLUTE(.);
} > rtc_slow_seg
/* Send .iram0 code to iram */
.iram0.vectors :
{
/* Vectors go to IRAM */
_init_start = ABSOLUTE(.);
/* Vectors according to builds/RF-2015.2-win32/esp108_v1_2_s5_512int_2/config.html */
. = 0x0;
KEEP(*(.WindowVectors.text));
. = 0x180;
KEEP(*(.Level2InterruptVector.text));
. = 0x1c0;
KEEP(*(.Level3InterruptVector.text));
. = 0x200;
KEEP(*(.Level4InterruptVector.text));
. = 0x240;
KEEP(*(.Level5InterruptVector.text));
. = 0x280;
KEEP(**(.DebugExceptionVector.text));
. = 0x2c0;
KEEP(*(.NMIExceptionVector.text));
. = 0x300;
KEEP(*(.KernelExceptionVector.text));
. = 0x340;
KEEP(*(.UserExceptionVector.text));
. = 0x3C0;
KEEP(*(.DoubleExceptionVector.text));
. = 0x400;
*(.*Vector.literal)
*(.UserEnter.literal);
*(.UserEnter.text);
. = ALIGN (16);
*(.entry.text)
*(.init.literal)
*(.init)
_init_end = ABSOLUTE(.);
/* This goes here, not at top of linker script, so addr2line finds it last,
and uses it in preference to the first symbol in IRAM */
_iram_start = ABSOLUTE(0);
} > iram0_0_seg
.iram0.text :
{
/* Code marked as runnning out of IRAM */
_iram_text_start = ABSOLUTE(.);
*(.iram1 .iram1.*)
*libhal.a:(.literal .text .literal.* .text.*)
*libgcc.a:lib2funcs.o(.literal .text .literal.* .text.*)
*libgcov.a:(.literal .text .literal.* .text.*)
/* *libc.a:(.literal .text .literal.* .text.*) */
/* Xtensa basic functionality written in assembler should be placed in iram */
*xtensa.a:*(.literal .text .literal.* .text.*)
/* ESP-IDF parts that have to run in IRAM */
*esp_idf_heap.a:*(.literal .text .literal.* .text.*)
*esp_idf_spi_flash.a:*(.literal .text .literal.* .text.*)
/* parts of RIOT that should to run in IRAM */
*core.a:*(.literal .text .literal.* .text.*)
/* *spiffs_fs.a:*(.literal .text .literal.* .text.*) */
/* *spiffs.a:*(.literal .text .literal.* .text.*) */
/* part of RIOT ports that should run in IRAM */
*cpu.a:*(.literal .text .literal.* .text.*)
*periph.a:*(.literal .text .literal.* .text.*)
INCLUDE esp32.spiram.rom-functions-iram.ld
_iram_text_end = ABSOLUTE(.);
} > iram0_0_seg
.dram0.data :
{
_data_start = ABSOLUTE(.);
*(.data)
*(.data.*)
*(.gnu.linkonce.d.*)
*(.data1)
*(.sdata)
*(.sdata.*)
*(.gnu.linkonce.s.*)
*(.sdata2)
*(.sdata2.*)
*(.gnu.linkonce.s2.*)
*(.jcr)
*(.dram1 .dram1.*)
*libesp32.a:panic.o(.rodata .rodata.*)
*libphy.a:(.rodata .rodata.*)
*libsoc.a:rtc_clk.o(.rodata .rodata.*)
*libapp_trace.a:(.rodata .rodata.*)
*libgcov.a:(.rodata .rodata.*)
*libheap.a:multi_heap.o(.rodata .rodata.*)
*libheap.a:multi_heap_poisoning.o(.rodata .rodata.*)
INCLUDE esp32.spiram.rom-functions-dram.ld
_data_end = ABSOLUTE(.);
. = ALIGN(4);
} >dram0_0_seg
/* Shared RAM */
.dram0.bss (NOLOAD) :
{
. = ALIGN (8);
_bss_start = ABSOLUTE(.);
*(.dynsbss)
*(.sbss)
*(.sbss.*)
*(.gnu.linkonce.sb.*)
*(.scommon)
*(.sbss2)
*(.sbss2.*)
*(.gnu.linkonce.sb2.*)
*(.dynbss)
*(.bss)
*(.bss.*)
*(.share.mem)
*(.gnu.linkonce.b.*)
*(COMMON)
. = ALIGN (8);
_bss_end = ABSOLUTE(.);
_heap_start = ABSOLUTE(.);
_sheap = ABSOLUTE(.);
} >dram0_0_seg
/* TODO HEAP handling when BT is used
ETS system memory seems to start at 0x3FFE0000 if BT is not used.
This is the top of the heap for the app */
. = 0x3FFE0000;
_heap_top = ABSOLUTE(.);
_eheap = ABSOLUTE(.);
.flash.rodata :
{
_rodata_start = ABSOLUTE(.);
*(.rodata)
*(.rodata.*)
*(.irom1.text) /* catch stray ICACHE_RODATA_ATTR */
*(.gnu.linkonce.r.*)
*(.rodata1)
__XT_EXCEPTION_TABLE_ = ABSOLUTE(.);
*(.xt_except_table)
*(.gcc_except_table .gcc_except_table.*)
*(.gnu.linkonce.e.*)
*(.gnu.version_r)
. = (. + 3) & ~ 3;
__eh_frame = ABSOLUTE(.);
KEEP(*(.eh_frame))
. = (. + 7) & ~ 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.*)))
KEEP (*(.dtors))
/* C++ exception handlers table: */
__XT_EXCEPTION_DESCS_ = ABSOLUTE(.);
*(.xt_except_desc)
*(.gnu.linkonce.h.*)
__XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.);
*(.xt_except_desc_end)
*(.dynamic)
*(.gnu.version_d)
_rodata_end = ABSOLUTE(.);
/* Literals are also RO data. */
_lit4_start = ABSOLUTE(.);
*(*.lit4)
*(.lit4.*)
*(.gnu.linkonce.lit4.*)
_lit4_end = ABSOLUTE(.);
. = ALIGN(4);
_thread_local_start = ABSOLUTE(.);
*(.tdata)
*(.tdata.*)
*(.tbss)
*(.tbss.*)
_thread_local_end = ABSOLUTE(.);
. = ALIGN(4);
} >drom0_0_seg
.flash.text :
{
_stext = .;
_text_start = ABSOLUTE(.);
/* place everything else in iram0_2_seg (cached ROM) */
*(.literal .text .literal.* .text.* .stub)
*(.gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*)
*(.irom0.text) /* catch stray ICACHE_RODATA_ATTR */
*(.fini.literal)
*(.fini)
*(.gnu.version)
_text_end = ABSOLUTE(.);
_etext = .;
/* Similar to _iram_start, this symbol goes here so it is
resolved by addr2line in preference to the first symbol in
the flash.text segment.
*/
_flash_cache_start = ABSOLUTE(0);
} >iram0_2_seg
}

65
cpu/esp32/ld/esp32.ld Normal file
View File

@ -0,0 +1,65 @@
/* ESP32 Linker Script Memory Layout
This file describes the memory layout (memory blocks) as virtual
memory addresses.
esp32.common.ld contains output sections to link compiler output
into these memory blocks.
***
This linker script is passed through the C preprocessor to include
configuration options.
Please use preprocessor features sparingly! Restrict
to simple macros with numeric values, and/or #if/#endif blocks.
*/
MEMORY
{
/* All these values assume the flash cache is on, and have the blocks this uses subtracted from the length
of the various regions. The 'data access port' dram/drom regions map to the same iram/irom regions but
are connected to the data port of the CPU and eg allow bytewise access. */
/* IRAM for PRO cpu. Not sure if happy with this, this is MMU area... */
iram0_0_seg (RX) : org = 0x40080000, len = 0x20000
/* Even though the segment name is iram, it is actually mapped to flash
*/
iram0_2_seg (RX) : org = 0x400D0018, len = 0x330000-0x18
/*
(0x18 offset above is a convenience for the app binary image generation. Flash cache has 64KB pages. The .bin file
which is flashed to the chip has a 0x18 byte file header. Setting this offset makes it simple to meet the flash
cache MMU's constraint that (paddr % 64KB == vaddr % 64KB).)
*/
/* Shared data RAM, excluding memory reserved for ROM bss/data/stack.
Enabling Bluetooth & Trace Memory features in menuconfig will decrease
the amount of RAM available.
Note: Length of this section *should* be 0x50000, and this extra DRAM is available
in heap at runtime. However due to static ROM memory usage at this 176KB mark, the
additional static memory temporarily cannot be used.
*/
dram0_0_seg (RW) : org = 0x3FFB0000,
len = 0x2c200
/* Flash mapped constant data */
drom0_0_seg (R) : org = 0x3F400018, len = 0x400000-0x18
/* (See iram0_2_seg for meaning of 0x18 offset in the above.) */
/* RTC fast memory (executable). Persists over deep sleep.
*/
rtc_iram_seg(RWX) : org = 0x400C0000, len = 0x2000
/* RTC slow memory (data accessible). Persists over deep sleep.
Start of RTC slow memory is reserved for ULP co-processor code + data, if enabled.
*/
rtc_slow_seg(RW) : org = 0x50000000,
len = 0x1000
}

View File

@ -0,0 +1,29 @@
PROVIDE ( UART0 = 0x3ff40000 );
PROVIDE ( SPI1 = 0x3ff42000 );
PROVIDE ( SPI0 = 0x3ff43000 );
PROVIDE ( GPIO = 0x3ff44000 );
PROVIDE ( SIGMADELTA = 0x3ff44f00 );
PROVIDE ( RTCCNTL = 0x3ff48000 );
PROVIDE ( RTCIO = 0x3ff48400 );
PROVIDE ( SENS = 0x3ff48800 );
PROVIDE ( UHCI1 = 0x3ff4C000 );
PROVIDE ( I2S0 = 0x3ff4F000 );
PROVIDE ( UART1 = 0x3ff50000 );
PROVIDE ( I2C0 = 0x3ff53000 );
PROVIDE ( UHCI0 = 0x3ff54000 );
PROVIDE ( RMT = 0x3ff56000 );
PROVIDE ( RMTMEM = 0x3ff56800 );
PROVIDE ( PCNT = 0x3ff57000 );
PROVIDE ( LEDC = 0x3ff59000 );
PROVIDE ( MCPWM0 = 0x3ff5E000 );
PROVIDE ( TIMERG0 = 0x3ff5F000 );
PROVIDE ( TIMERG1 = 0x3ff60000 );
PROVIDE ( SPI2 = 0x3ff64000 );
PROVIDE ( SPI3 = 0x3ff65000 );
PROVIDE ( SYSCON = 0x3ff66000 );
PROVIDE ( I2C1 = 0x3ff67000 );
PROVIDE ( SDMMC = 0x3ff68000 );
PROVIDE ( CAN = 0x3ff6B000 );
PROVIDE ( MCPWM1 = 0x3ff6C000 );
PROVIDE ( I2S1 = 0x3ff6D000 );
PROVIDE ( UART2 = 0x3ff6E000 );

1728
cpu/esp32/ld/esp32.rom.ld Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,98 @@
/*
Address table for printf/scanf family of functions in ESP32 ROM.
These functions are compiled with newlib "nano" format option.
As such, they don's support 64-bit integer formats.
Floating point formats are supported by setting _printf_float and
_scanf_float entries in syscall table. This is done automatically
by startup code.
Generated for ROM with MD5sum:
ab8282ae908fe9e7a63fb2a4ac2df013 eagle.pro.rom.out
*/
PROVIDE ( asiprintf = 0x40056d9c );
PROVIDE ( _asiprintf_r = 0x40056d4c );
PROVIDE ( asniprintf = 0x40056cd8 );
PROVIDE ( _asniprintf_r = 0x40056c64 );
PROVIDE ( asnprintf = 0x40056cd8 );
PROVIDE ( _asnprintf_r = 0x40056c64 );
PROVIDE ( asprintf = 0x40056d9c );
PROVIDE ( _asprintf_r = 0x40056d4c );
PROVIDE ( fiprintf = 0x40056efc );
PROVIDE ( _fiprintf_r = 0x40056ed8 );
PROVIDE ( fiscanf = 0x40058884 );
PROVIDE ( _fiscanf_r = 0x400588b4 );
PROVIDE ( fprintf = 0x40056efc );
PROVIDE ( _fprintf_r = 0x40056ed8 );
PROVIDE ( iprintf = 0x40056978 );
PROVIDE ( _iprintf_r = 0x40056944 );
PROVIDE ( printf = 0x40056978 );
PROVIDE ( _printf_common = 0x40057338 );
PROVIDE ( _printf_float = 0x4000befc );
PROVIDE ( _printf_i = 0x40057404 );
PROVIDE ( _printf_r = 0x40056944 );
PROVIDE ( siprintf = 0x40056c08 );
PROVIDE ( _siprintf_r = 0x40056bbc );
PROVIDE ( sniprintf = 0x40056b4c );
PROVIDE ( _sniprintf_r = 0x40056ae4 );
PROVIDE ( snprintf = 0x40056b4c );
PROVIDE ( _snprintf_r = 0x40056ae4 );
PROVIDE ( sprintf = 0x40056c08 );
PROVIDE ( _sprintf_r = 0x40056bbc );
PROVIDE ( __sprint_r = 0x400577e4 );
PROVIDE ( _svfiprintf_r = 0x40057100 );
PROVIDE ( __svfiscanf_r = 0x40057b08 );
PROVIDE ( _svfprintf_r = 0x40057100 );
PROVIDE ( __svfscanf = 0x40057f04 );
PROVIDE ( __svfscanf_r = 0x40057b08 );
PROVIDE ( vasiprintf = 0x40056eb8 );
PROVIDE ( _vasiprintf_r = 0x40056e80 );
PROVIDE ( vasniprintf = 0x40056e58 );
PROVIDE ( _vasniprintf_r = 0x40056df8 );
PROVIDE ( vasnprintf = 0x40056e58 );
PROVIDE ( _vasnprintf_r = 0x40056df8 );
PROVIDE ( vasprintf = 0x40056eb8 );
PROVIDE ( _vasprintf_r = 0x40056e80 );
PROVIDE ( vfiprintf = 0x40057ae8 );
PROVIDE ( _vfiprintf_r = 0x40057850 );
PROVIDE ( vfiscanf = 0x40057eb8 );
PROVIDE ( _vfiscanf_r = 0x40057f24 );
PROVIDE ( vfprintf = 0x40057ae8 );
PROVIDE ( _vfprintf_r = 0x40057850 );
PROVIDE ( vfscanf = 0x40057eb8 );
PROVIDE ( _vfscanf_r = 0x40057f24 );
PROVIDE ( viprintf = 0x400569b4 );
PROVIDE ( _viprintf_r = 0x400569e4 );
PROVIDE ( viscanf = 0x40058698 );
PROVIDE ( _viscanf_r = 0x400586c8 );
PROVIDE ( vprintf = 0x400569b4 );
PROVIDE ( _vprintf_r = 0x400569e4 );
PROVIDE ( vscanf = 0x40058698 );
PROVIDE ( _vscanf_r = 0x400586c8 );
PROVIDE ( vsiprintf = 0x40056ac4 );
PROVIDE ( _vsiprintf_r = 0x40056a90 );
PROVIDE ( vsiscanf = 0x40058740 );
PROVIDE ( _vsiscanf_r = 0x400586f8 );
PROVIDE ( vsniprintf = 0x40056a68 );
PROVIDE ( _vsniprintf_r = 0x40056a14 );
PROVIDE ( vsnprintf = 0x40056a68 );
PROVIDE ( _vsnprintf_r = 0x40056a14 );
PROVIDE ( vsprintf = 0x40056ac4 );
PROVIDE ( _vsprintf_r = 0x40056a90 );
PROVIDE ( vsscanf = 0x40058740 );
PROVIDE ( _vsscanf_r = 0x400586f8 );
PROVIDE ( fscanf = 0x40058884 );
PROVIDE ( _fscanf_r = 0x400588b4 );
PROVIDE ( iscanf = 0x40058760 );
PROVIDE ( _iscanf_r = 0x4005879c );
PROVIDE ( scanf = 0x40058760 );
PROVIDE ( _scanf_chars = 0x40058384 );
PROVIDE ( _scanf_float = 0x4000bf18 );
PROVIDE ( _scanf_i = 0x4005845c );
PROVIDE ( _scanf_r = 0x4005879c );
PROVIDE ( siscanf = 0x400587d0 );
PROVIDE ( _siscanf_r = 0x40058830 );
PROVIDE ( sscanf = 0x400587d0 );
PROVIDE ( _sscanf_r = 0x40058830 );
PROVIDE ( __ssvfiscanf_r = 0x4005802c );
PROVIDE ( __ssvfscanf_r = 0x4005802c );

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,143 @@
/*
If the Newlib functions in ROM aren't used (eg because the external SPI RAM workaround is active), these functions will
be linked into the application directly instead. Normally, they would end up in flash, which is undesirable because esp-idf
and/or applications may assume that because these functions normally are in ROM, they are accessible even when flash is
inaccessible. To work around this, this ld fragment places these functions in RAM instead. If the ROM functions are used,
these defines do nothing, so they can still be included in that situation.
This file is responsible for placing the rodata segment in DRAM.
*/
*lib_a-utoa.o(.rodata .rodata.*)
*lib_a-longjmp.o(.rodata .rodata.*)
*lib_a-setjmp.o(.rodata .rodata.*)
*lib_a-abs.o(.rodata .rodata.*)
*lib_a-div.o(.rodata .rodata.*)
*lib_a-labs.o(.rodata .rodata.*)
*lib_a-ldiv.o(.rodata .rodata.*)
*lib_a-quorem.o(.rodata .rodata.*)
*lib_a-qsort.o(.rodata .rodata.*)
*lib_a-utoa.o(.rodata .rodata.*)
*lib_a-itoa.o(.rodata .rodata.*)
*lib_a-atoi.o(.rodata .rodata.*)
*lib_a-atol.o(.rodata .rodata.*)
*lib_a-strtol.o(.rodata .rodata.*)
*lib_a-strtoul.o(.rodata .rodata.*)
*lib_a-wcrtomb.o(.rodata .rodata.*)
*lib_a-fvwrite.o(.rodata .rodata.*)
*lib_a-wbuf.o(.rodata .rodata.*)
*lib_a-wsetup.o(.rodata .rodata.*)
*lib_a-fputwc.o(.rodata .rodata.*)
*lib_a-wctomb_r.o(.rodata .rodata.*)
*lib_a-ungetc.o(.rodata .rodata.*)
*lib_a-makebuf.o(.rodata .rodata.*)
*lib_a-fflush.o(.rodata .rodata.*)
*lib_a-refill.o(.rodata .rodata.*)
*lib_a-s_fpclassify.o(.rodata .rodata.*)
*lib_a-locale.o(.rodata .rodata.*)
*lib_a-asctime.o(.rodata .rodata.*)
*lib_a-ctime.o(.rodata .rodata.*)
*lib_a-ctime_r.o(.rodata .rodata.*)
*lib_a-lcltime.o(.rodata .rodata.*)
*lib_a-lcltime_r.o(.rodata .rodata.*)
*lib_a-gmtime.o(.rodata .rodata.*)
*lib_a-gmtime_r.o(.rodata .rodata.*)
*lib_a-strftime.o(.rodata .rodata.*)
*lib_a-mktime.o(.rodata .rodata.*)
*lib_a-syswrite.o(.rodata .rodata.*)
*lib_a-tzset_r.o(.rodata .rodata.*)
*lib_a-tzset.o(.rodata .rodata.*)
*lib_a-toupper.o(.rodata .rodata.*)
*lib_a-tolower.o(.rodata .rodata.*)
*lib_a-toascii.o(.rodata .rodata.*)
*lib_a-systimes.o(.rodata .rodata.*)
*lib_a-time.o(.rodata .rodata.*)
*lib_a-bsd_qsort_r.o(.rodata .rodata.*)
*lib_a-qsort_r.o(.rodata .rodata.*)
*lib_a-gettzinfo.o(.rodata .rodata.*)
*lib_a-strupr.o(.rodata .rodata.*)
*lib_a-asctime_r.o(.rodata .rodata.*)
*lib_a-bzero.o(.rodata .rodata.*)
*lib_a-close.o(.rodata .rodata.*)
*lib_a-creat.o(.rodata .rodata.*)
*lib_a-environ.o(.rodata .rodata.*)
*lib_a-fclose.o(.rodata .rodata.*)
*lib_a-isalnum.o(.rodata .rodata.*)
*lib_a-isalpha.o(.rodata .rodata.*)
*lib_a-isascii.o(.rodata .rodata.*)
*lib_a-isblank.o(.rodata .rodata.*)
*lib_a-iscntrl.o(.rodata .rodata.*)
*lib_a-isdigit.o(.rodata .rodata.*)
*lib_a-isgraph.o(.rodata .rodata.*)
*lib_a-islower.o(.rodata .rodata.*)
*lib_a-isprint.o(.rodata .rodata.*)
*lib_a-ispunct.o(.rodata .rodata.*)
*lib_a-isspace.o(.rodata .rodata.*)
*lib_a-isupper.o(.rodata .rodata.*)
*lib_a-memccpy.o(.rodata .rodata.*)
*lib_a-memchr.o(.rodata .rodata.*)
*lib_a-memcmp.o(.rodata .rodata.*)
*lib_a-memcpy.o(.rodata .rodata.*)
*lib_a-memmove.o(.rodata .rodata.*)
*lib_a-memrchr.o(.rodata .rodata.*)
*lib_a-memset.o(.rodata .rodata.*)
*lib_a-open.o(.rodata .rodata.*)
*lib_a-rand.o(.rodata .rodata.*)
*lib_a-rand_r.o(.rodata .rodata.*)
*lib_a-read.o(.rodata .rodata.*)
*lib_a-rshift.o(.rodata .rodata.*)
*lib_a-sbrk.o(.rodata .rodata.*)
*lib_a-srand.o(.rodata .rodata.*)
*lib_a-strcasecmp.o(.rodata .rodata.*)
*lib_a-strcasestr.o(.rodata .rodata.*)
*lib_a-strcat.o(.rodata .rodata.*)
*lib_a-strchr.o(.rodata .rodata.*)
*lib_a-strcmp.o(.rodata .rodata.*)
*lib_a-strcoll.o(.rodata .rodata.*)
*lib_a-strcpy.o(.rodata .rodata.*)
*lib_a-strcspn.o(.rodata .rodata.*)
*lib_a-strdup.o(.rodata .rodata.*)
*lib_a-strlcat.o(.rodata .rodata.*)
*lib_a-strlcpy.o(.rodata .rodata.*)
*lib_a-strlen.o(.rodata .rodata.*)
*lib_a-strlwr.o(.rodata .rodata.*)
*lib_a-strncasecmp.o(.rodata .rodata.*)
*lib_a-strncat.o(.rodata .rodata.*)
*lib_a-strncmp.o(.rodata .rodata.*)
*lib_a-strncpy.o(.rodata .rodata.*)
*lib_a-strndup.o(.rodata .rodata.*)
*lib_a-strnlen.o(.rodata .rodata.*)
*lib_a-strrchr.o(.rodata .rodata.*)
*lib_a-strsep.o(.rodata .rodata.*)
*lib_a-strspn.o(.rodata .rodata.*)
*lib_a-strstr.o(.rodata .rodata.*)
*lib_a-strtok_r.o(.rodata .rodata.*)
*lib_a-strupr.o(.rodata .rodata.*)
*lib_a-stdio.o(.rodata .rodata.*)
*lib_a-syssbrk.o(.rodata .rodata.*)
*lib_a-sysclose.o(.rodata .rodata.*)
*lib_a-sysopen.o(.rodata .rodata.*)
*creat.o(.rodata .rodata.*)
*lib_a-sysread.o(.rodata .rodata.*)
*lib_a-syswrite.o(.rodata .rodata.*)
*lib_a-impure.o(.rodata .rodata.*)
*lib_a-tzvars.o(.rodata .rodata.*)
*lib_a-sf_nan.o(.rodata .rodata.*)
*lib_a-tzcalc_limits.o(.rodata .rodata.*)
*lib_a-month_lengths.o(.rodata .rodata.*)
*lib_a-timelocal.o(.rodata .rodata.*)
*lib_a-findfp.o(.rodata .rodata.*)
*lock.o(.rodata .rodata.*)
*lib_a-getenv_r.o(.rodata .rodata.*)
*isatty.o(.rodata .rodata.*)
*lib_a-fwalk.o(.rodata .rodata.*)
*lib_a-getenv_r.o(.rodata .rodata.*)
*lib_a-tzlock.o(.rodata .rodata.*)
*lib_a-ctype_.o(.rodata .rodata.*)
*lib_a-sccl.o(.rodata .rodata.*)
*lib_a-strptime.o(.rodata .rodata.*)
*lib_a-envlock.o(.rodata .rodata.*)
*lib_a-raise.o(.rodata .rodata.*)
*lib_a-strdup_r.o(.rodata .rodata.*)
*lib_a-system.o(.rodata .rodata.*)
*lib_a-strndup_r.o(.rodata .rodata.*)

View File

@ -0,0 +1,144 @@
/*
If the Newlib functions in ROM aren't used (eg because the external SPI RAM workaround is active), these functions will
be linked into the application directly instead. Normally, they would end up in flash, which is undesirable because esp-idf
and/or applications may assume that because these functions normally are in ROM, they are accessible even when flash is
inaccessible. To work around this, this ld fragment places these functions in RAM instead. If the ROM functions are used,
these defines do nothing, so they can still be included in that situation.
This file is responsible for placing the literal and text segments in IRAM.
*/
*lib_a-utoa.o(.literal .text .literal.* .text.*)
*lib_a-longjmp.o(.literal .text .literal.* .text.*)
*lib_a-setjmp.o(.literal .text .literal.* .text.*)
*lib_a-abs.o(.literal .text .literal.* .text.*)
*lib_a-div.o(.literal .text .literal.* .text.*)
*lib_a-labs.o(.literal .text .literal.* .text.*)
*lib_a-ldiv.o(.literal .text .literal.* .text.*)
*lib_a-quorem.o(.literal .text .literal.* .text.*)
*lib_a-qsort.o(.literal .text .literal.* .text.*)
*lib_a-utoa.o(.literal .text .literal.* .text.*)
*lib_a-itoa.o(.literal .text .literal.* .text.*)
*lib_a-atoi.o(.literal .text .literal.* .text.*)
*lib_a-atol.o(.literal .text .literal.* .text.*)
*lib_a-strtol.o(.literal .text .literal.* .text.*)
*lib_a-strtoul.o(.literal .text .literal.* .text.*)
*lib_a-wcrtomb.o(.literal .text .literal.* .text.*)
*lib_a-fvwrite.o(.literal .text .literal.* .text.*)
*lib_a-wbuf.o(.literal .text .literal.* .text.*)
*lib_a-wsetup.o(.literal .text .literal.* .text.*)
*lib_a-fputwc.o(.literal .text .literal.* .text.*)
*lib_a-wctomb_r.o(.literal .text .literal.* .text.*)
*lib_a-ungetc.o(.literal .text .literal.* .text.*)
*lib_a-makebuf.o(.literal .text .literal.* .text.*)
*lib_a-fflush.o(.literal .text .literal.* .text.*)
*lib_a-refill.o(.literal .text .literal.* .text.*)
*lib_a-s_fpclassify.o(.literal .text .literal.* .text.*)
*lib_a-locale.o(.literal .text .literal.* .text.*)
*lib_a-asctime.o(.literal .text .literal.* .text.*)
*lib_a-ctime.o(.literal .text .literal.* .text.*)
*lib_a-ctime_r.o(.literal .text .literal.* .text.*)
*lib_a-lcltime.o(.literal .text .literal.* .text.*)
*lib_a-lcltime_r.o(.literal .text .literal.* .text.*)
*lib_a-gmtime.o(.literal .text .literal.* .text.*)
*lib_a-gmtime_r.o(.literal .text .literal.* .text.*)
*lib_a-strftime.o(.literal .text .literal.* .text.*)
*lib_a-mktime.o(.literal .text .literal.* .text.*)
*lib_a-syswrite.o(.literal .text .literal.* .text.*)
*lib_a-tzset_r.o(.literal .text .literal.* .text.*)
*lib_a-tzset.o(.literal .text .literal.* .text.*)
*lib_a-toupper.o(.literal .text .literal.* .text.*)
*lib_a-tolower.o(.literal .text .literal.* .text.*)
*lib_a-toascii.o(.literal .text .literal.* .text.*)
*lib_a-systimes.o(.literal .text .literal.* .text.*)
*lib_a-time.o(.literal .text .literal.* .text.*)
*lib_a-bsd_qsort_r.o(.literal .text .literal.* .text.*)
*lib_a-qsort_r.o(.literal .text .literal.* .text.*)
*lib_a-gettzinfo.o(.literal .text .literal.* .text.*)
*lib_a-strupr.o(.literal .text .literal.* .text.*)
*lib_a-asctime_r.o(.literal .text .literal.* .text.*)
*lib_a-bzero.o(.literal .text .literal.* .text.*)
*lib_a-close.o(.literal .text .literal.* .text.*)
*lib_a-creat.o(.literal .text .literal.* .text.*)
*lib_a-environ.o(.literal .text .literal.* .text.*)
*lib_a-fclose.o(.literal .text .literal.* .text.*)
*lib_a-isalnum.o(.literal .text .literal.* .text.*)
*lib_a-isalpha.o(.literal .text .literal.* .text.*)
*lib_a-isascii.o(.literal .text .literal.* .text.*)
*lib_a-isblank.o(.literal .text .literal.* .text.*)
*lib_a-iscntrl.o(.literal .text .literal.* .text.*)
*lib_a-isdigit.o(.literal .text .literal.* .text.*)
*lib_a-isgraph.o(.literal .text .literal.* .text.*)
*lib_a-islower.o(.literal .text .literal.* .text.*)
*lib_a-isprint.o(.literal .text .literal.* .text.*)
*lib_a-ispunct.o(.literal .text .literal.* .text.*)
*lib_a-isspace.o(.literal .text .literal.* .text.*)
*lib_a-isupper.o(.literal .text .literal.* .text.*)
*lib_a-memccpy.o(.literal .text .literal.* .text.*)
*lib_a-memchr.o(.literal .text .literal.* .text.*)
*lib_a-memcmp.o(.literal .text .literal.* .text.*)
*lib_a-memcpy.o(.literal .text .literal.* .text.*)
*lib_a-memmove.o(.literal .text .literal.* .text.*)
*lib_a-memrchr.o(.literal .text .literal.* .text.*)
*lib_a-memset.o(.literal .text .literal.* .text.*)
*lib_a-open.o(.literal .text .literal.* .text.*)
*lib_a-rand.o(.literal .text .literal.* .text.*)
*lib_a-rand_r.o(.literal .text .literal.* .text.*)
*lib_a-read.o(.literal .text .literal.* .text.*)
*lib_a-rshift.o(.literal .text .literal.* .text.*)
*lib_a-sbrk.o(.literal .text .literal.* .text.*)
*lib_a-srand.o(.literal .text .literal.* .text.*)
*lib_a-strcasecmp.o(.literal .text .literal.* .text.*)
*lib_a-strcasestr.o(.literal .text .literal.* .text.*)
*lib_a-strcat.o(.literal .text .literal.* .text.*)
*lib_a-strchr.o(.literal .text .literal.* .text.*)
*lib_a-strcmp.o(.literal .text .literal.* .text.*)
*lib_a-strcoll.o(.literal .text .literal.* .text.*)
*lib_a-strcpy.o(.literal .text .literal.* .text.*)
*lib_a-strcspn.o(.literal .text .literal.* .text.*)
*lib_a-strdup.o(.literal .text .literal.* .text.*)
*lib_a-strlcat.o(.literal .text .literal.* .text.*)
*lib_a-strlcpy.o(.literal .text .literal.* .text.*)
*lib_a-strlen.o(.literal .text .literal.* .text.*)
*lib_a-strlwr.o(.literal .text .literal.* .text.*)
*lib_a-strncasecmp.o(.literal .text .literal.* .text.*)
*lib_a-strncat.o(.literal .text .literal.* .text.*)
*lib_a-strncmp.o(.literal .text .literal.* .text.*)
*lib_a-strncpy.o(.literal .text .literal.* .text.*)
*lib_a-strndup.o(.literal .text .literal.* .text.*)
*lib_a-strnlen.o(.literal .text .literal.* .text.*)
*lib_a-strrchr.o(.literal .text .literal.* .text.*)
*lib_a-strsep.o(.literal .text .literal.* .text.*)
*lib_a-strspn.o(.literal .text .literal.* .text.*)
*lib_a-strstr.o(.literal .text .literal.* .text.*)
*lib_a-strtok_r.o(.literal .text .literal.* .text.*)
*lib_a-strupr.o(.literal .text .literal.* .text.*)
*lib_a-stdio.o(.literal .text .literal.* .text.*)
*lib_a-syssbrk.o(.literal .text .literal.* .text.*)
*lib_a-sysclose.o(.literal .text .literal.* .text.*)
*lib_a-sysopen.o(.literal .text .literal.* .text.*)
*creat.o(.literal .text .literal.* .text.*)
*lib_a-sysread.o(.literal .text .literal.* .text.*)
*lib_a-syswrite.o(.literal .text .literal.* .text.*)
*lib_a-impure.o(.literal .text .literal.* .text.*)
*lib_a-tzvars.o(.literal .text .literal.* .text.*)
*lib_a-sf_nan.o(.literal .text .literal.* .text.*)
*lib_a-tzcalc_limits.o(.literal .text .literal.* .text.*)
*lib_a-month_lengths.o(.literal .text .literal.* .text.*)
*lib_a-timelocal.o(.literal .text .literal.* .text.*)
*lib_a-findfp.o(.literal .text .literal.* .text.*)
*lock.o(.literal .text .literal.* .text.*)
*lib_a-getenv_r.o(.literal .text .literal.* .text.*)
*isatty.o(.literal .text .literal.* .text.*)
*lib_a-fwalk.o(.literal .text .literal.* .text.*)
*lib_a-getenv_r.o(.literal .text .literal.* .text.*)
*lib_a-tzlock.o(.literal .text .literal.* .text.*)
*lib_a-ctype_.o(.literal .text .literal.* .text.*)
*lib_a-sccl.o(.literal .text .literal.* .text.*)
*lib_a-strptime.o(.literal .text .literal.* .text.*)
*lib_a-envlock.o(.literal .text .literal.* .text.*)
*lib_a-raise.o(.literal .text .literal.* .text.*)
*lib_a-strdup_r.o(.literal .text .literal.* .text.*)
*lib_a-system.o(.literal .text .literal.* .text.*)
*lib_a-strndup_r.o(.literal .text .literal.* .text.*)

99
cpu/esp32/log_module.c Normal file
View File

@ -0,0 +1,99 @@
/*
* 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_esp32
* @{
*
* @file
* @brief Log module to realize consistent log messages
*
* @author Gunar Schorcht <gunar@schorcht.net>
*/
#include <stdio.h>
#include <string.h>
#include "rom/ets_sys.h"
#include "cpu_conf.h"
#include "log.h"
#include "syscalls.h"
extern char _printf_buf[PRINTF_BUFSIZ];
bool _new_line = true;
void log_write(unsigned level, const char *format, ...)
{
if (level == LOG_NONE) {
return;
}
if (_new_line) {
/* if we are in new line, we print the prefix */
char lc = 'U';
switch (level) {
case LOG_ERROR : lc = 'E'; break;
case LOG_WARNING: lc = 'W'; break;
case LOG_INFO : lc = 'I'; break;
case LOG_DEBUG : lc = 'D'; break;
case LOG_ALL : lc = 'V'; break;
}
ets_printf("%c (%u) ", lc, system_get_time_ms());
}
va_list arglist;
va_start(arglist, format);
int ret = vsnprintf(_printf_buf, PRINTF_BUFSIZ, format, arglist);
if (ret > 0) {
ets_printf (_printf_buf);
}
va_end(arglist);
_new_line = (strrchr(format, '\n') != NULL);
}
void log_write_tagged(unsigned level, const char *tag, const char *format, ...)
{
if (level == LOG_NONE) {
return;
}
if (_new_line) {
/* if we are in new line, we print the prefix */
char lc = 'U';
switch (level) {
case LOG_ERROR : lc = 'E'; break;
case LOG_WARNING: lc = 'W'; break;
case LOG_INFO : lc = 'I'; break;
case LOG_DEBUG : lc = 'D'; break;
case LOG_ALL : lc = 'V'; break;
}
#if LOG_TAG_IN_BRACKETS
ets_printf("%c (%u) [%10s]: ", lc, system_get_time_ms(), tag);
#else
ets_printf("%c (%u) %10s: ", lc, system_get_time_ms(), tag);
#endif
}
va_list arglist;
va_start(arglist, format);
int ret = vsnprintf(_printf_buf, PRINTF_BUFSIZ, format, arglist);
if (ret > 0) {
ets_printf (_printf_buf);
}
va_end(arglist);
_new_line = (strrchr(format, '\n') != NULL);
}

View File

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

650
cpu/esp32/periph/adc.c Normal file
View File

@ -0,0 +1,650 @@
/*
* 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_esp32
* @ingroup drivers_periph_adc
* @{
*
* @file
* @brief Low-level ADC driver implementation
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
#define ENABLE_DEBUG 0
#include "debug.h"
#include "esp_common.h"
#include "board.h"
#include "cpu.h"
#include "log.h"
#include "mutex.h"
#include "periph/adc.h"
#include "periph/dac.h"
#include "periph/gpio.h"
#include "adc_arch.h"
#include "gpio_arch.h"
#include "rom/ets_sys.h"
#include "soc/rtc_io_struct.h"
#include "soc/rtc_cntl_struct.h"
#include "soc/sens_reg.h"
#include "soc/sens_struct.h"
#define ADC1_CTRL 0
#define ADC2_CTRL 1
/* RTC pin type (does not correspond to RTC gpio num order) */
typedef enum {
RTCIO_TOUCH0 = 0, /* touch sensor 0 */
RTCIO_TOUCH1, /* touch sensor 1 */
RTCIO_TOUCH2, /* touch sensor 2 */
RTCIO_TOUCH3, /* touch sensor 3 */
RTCIO_TOUCH4, /* touch sensor 4 */
RTCIO_TOUCH5, /* touch sensor 5 */
RTCIO_TOUCH6, /* touch sensor 6 */
RTCIO_TOUCH7, /* touch sensor 7 */
RTCIO_TOUCH8, /* touch sensor 8, 32K_XP */
RTCIO_TOUCH9, /* touch sensor 9, 32K_XN */
RTCIO_ADC_ADC1, /* VDET_1 */
RTCIO_ADC_ADC2, /* VDET_2 */
RTCIO_SENSOR_SENSE1, /* SENSOR_VP */
RTCIO_SENSOR_SENSE2, /* SENSOR_CAPP */
RTCIO_SENSOR_SENSE3, /* SENSOR_CAPN */
RTCIO_SENSOR_SENSE4, /* SENSOR_VN */
RTCIO_DAC1, /* DAC output */
RTCIO_DAC2, /* DAC output */
RTCIO_NA, /* RTC pad not available */
} _rtcio_pin_t;
/* ADC pin hardware information type (for internal use only) */
struct _adc_hw_t {
gpio_t gpio;
uint8_t rtc_gpio;
uint8_t adc_ctrl;
uint8_t adc_channel;
char* pad_name;
};
/* RTC hardware map, the index corresponds to RTC pin type _rtcio_pin_t
(Table 19 in Technical Reference) */
const struct _adc_hw_t _adc_hw[] =
{
/* gpio rtc_gpio adc_ctrl adc_channel, pad_name */
{ GPIO4, 10, ADC2_CTRL, 0, "GPIO4" }, /* RTCIO_TOUCH0 */
{ GPIO0, 11, ADC2_CTRL, 1, "GPIO0" }, /* RTCIO_TOUCH1 */
{ GPIO2, 12, ADC2_CTRL, 2, "GPIO2" }, /* RTCIO_TOUCH2 */
{ GPIO15, 13, ADC2_CTRL, 3, "MTDO" }, /* RTCIO_TOUCH3 */
{ GPIO13, 14, ADC2_CTRL, 4, "MTCK" }, /* RTCIO_TOUCH4 */
{ GPIO12, 15, ADC2_CTRL, 5, "MTDI" }, /* RTCIO_TOUCH5 */
{ GPIO14, 16, ADC2_CTRL, 6, "MTMS" }, /* RTCIO_TOUCH6 */
{ GPIO27, 17, ADC2_CTRL, 7, "GPIO27" }, /* RTCIO_TOUCH7 */
{ GPIO33, 8, ADC1_CTRL, 5, "32K_XN" }, /* RTCIO_TOUCH8 */
{ GPIO32, 9, ADC1_CTRL, 4, "32K_XP" }, /* RTCIO_TOUCH9 */
{ GPIO34, 4, ADC1_CTRL, 6, "VDET_1" }, /* RTCIO_ADC_ADC1 */
{ GPIO35, 5, ADC1_CTRL, 7, "VDET_2" }, /* RTCIO_ADC_ADC2 */
{ GPIO36, 0, ADC1_CTRL, 0, "SENSOR_VP" }, /* RTCIO_SENSOR_SENSE1 */
{ GPIO37, 1, ADC1_CTRL, 1, "SENSOR_CAPP" }, /* RTCIO_SENSOR_SENSE2 */
{ GPIO38, 2, ADC1_CTRL, 2, "SENSOR_CAPN" }, /* RTCIO_SENSOR_SENSE3 */
{ GPIO39, 3, ADC1_CTRL, 3, "SENSOR_VN" }, /* RTCIO_SENSOR_SENSE4 */
{ GPIO25, 6, ADC2_CTRL, 8, "GPIO25" }, /* RTCIO_DAC1 */
{ GPIO26, 7, ADC2_CTRL, 9, "GPIO26" } /* RTCIO_DAC2 */
};
/* maps GPIO pin to RTC pin, this index is used to access ADC hardware table
(Table 19 in Technical Reference) */
const gpio_t _gpio_rtcio_map[] = {
RTCIO_TOUCH1, /* GPIO0 */
RTCIO_NA , /* GPIO1 */
RTCIO_TOUCH2, /* GPIO2 */
RTCIO_NA, /* GPIO3 */
RTCIO_TOUCH0, /* GPIO4 */
RTCIO_NA, /* GPIO5 */
RTCIO_NA, /* GPIO6 */
RTCIO_NA, /* GPIO7 */
RTCIO_NA, /* GPIO8 */
RTCIO_NA, /* GPIO9 */
RTCIO_NA, /* GPIO10 */
RTCIO_NA, /* GPIO11 */
RTCIO_TOUCH5, /* GPIO12 MTDI */
RTCIO_TOUCH4, /* GPIO13 MTCK */
RTCIO_TOUCH6, /* GPIO14 MTMS */
RTCIO_TOUCH3, /* GPIO15 MTDO */
RTCIO_NA, /* GPIO16 */
RTCIO_NA, /* GPIO17 */
RTCIO_NA, /* GPIO18 */
RTCIO_NA, /* GPIO19 */
RTCIO_NA, /* GPIO20 */
RTCIO_NA, /* GPIO21 */
RTCIO_NA, /* GPIO22 */
RTCIO_NA, /* GPIO23 */
RTCIO_NA, /* GPIO24 */
RTCIO_DAC1, /* GPIO25 */
RTCIO_DAC2, /* GPIO26 */
RTCIO_TOUCH7, /* GPIO27 */
RTCIO_NA, /* GPIO28 */
RTCIO_NA, /* GPIO29 */
RTCIO_NA, /* GPIO30 */
RTCIO_NA, /* GPIO31 */
RTCIO_TOUCH9, /* GPIO32 32K_XP */
RTCIO_TOUCH8, /* GPIO33 32K_XN */
RTCIO_ADC_ADC1, /* GPIO34 VDET_1 */
RTCIO_ADC_ADC2, /* GPIO35 VDET_2 */
RTCIO_SENSOR_SENSE1, /* GPIO36 SENSOR_VP */
RTCIO_SENSOR_SENSE2, /* GPIO37 SENSOR_CAPP */
RTCIO_SENSOR_SENSE3, /* GPIO38 SENSOR_CAPN */
RTCIO_SENSOR_SENSE4, /* GPIO39 SENSOR_VN */
};
/** Map of RIOT ADC and DAC lines to GPIOs */
static const uint32_t adc_pins[] = ADC_GPIOS;
static const uint32_t dac_pins[] = DAC_GPIOS;
/** number of ADC and DAC channels */
const unsigned adc_chn_num = (sizeof(adc_pins) / sizeof(adc_pins[0]));
const unsigned dac_chn_num = (sizeof(dac_pins) / sizeof(dac_pins[0]));
#if defined(ADC_GPIOS) || defined(DAC_GPIOS)
/* forward declaration of internal functions */
static void _adc1_ctrl_init(void);
static void _adc2_ctrl_init(void);
static bool _adc1_ctrl_initialized = false;
static bool _adc2_ctrl_initialized = false;
#endif /* defined(ADC_GPIOS) || defined(DAC_GPIOS) */
#if defined(ADC_GPIOS)
static bool _adc_conf_check(void);
static void _adc_module_init(void);
static bool _adc_module_initialized = false;
int adc_init(adc_t line)
{
CHECK_PARAM_RET (line < adc_chn_num, -1)
if (!_adc_module_initialized) {
/* do some configuration checks */
if (!_adc_conf_check()) {
return -1;
}
_adc_module_init();
_adc_module_initialized = true;
}
uint8_t rtcio = _gpio_rtcio_map[adc_pins[line]];
if (_adc_hw[rtcio].adc_ctrl == ADC1_CTRL && !_adc1_ctrl_initialized) {
_adc1_ctrl_init();
}
if (_adc_hw[rtcio].adc_ctrl == ADC2_CTRL && !_adc2_ctrl_initialized) {
_adc2_ctrl_init();
}
/* try to initialize the pin as ADC input */
if (gpio_get_pin_usage(_adc_hw[rtcio].gpio) != _GPIO) {
LOG_TAG_ERROR("adc", "GPIO%d is used for %s and cannot be used as "
"ADC input\n", _adc_hw[rtcio].gpio,
gpio_get_pin_usage_str(_adc_hw[rtcio].gpio));
return -1;
}
uint8_t idx;
/* disable the pad output */
RTCIO.enable_w1tc.val = BIT(_adc_hw[rtcio].rtc_gpio);
/* route pads to RTC and if possible, disable input, pull-up/pull-down */
switch (rtcio) {
case RTCIO_SENSOR_SENSE1: /* GPIO36, RTC0 */
RTCIO.sensor_pads.sense1_mux_sel = 1; /* route to RTC */
RTCIO.sensor_pads.sense1_fun_sel = 0; /* function ADC1_CH0 */
break;
case RTCIO_SENSOR_SENSE2: /* GPIO37, RTC1 */
RTCIO.sensor_pads.sense2_mux_sel = 1; /* route to RTC */
RTCIO.sensor_pads.sense2_fun_sel = 0; /* function ADC1_CH1 */
break;
case RTCIO_SENSOR_SENSE3: /* GPIO38, RTC2 */
RTCIO.sensor_pads.sense3_mux_sel = 1; /* route to RTC */
RTCIO.sensor_pads.sense3_fun_sel = 0; /* function ADC1_CH2 */
break;
case RTCIO_SENSOR_SENSE4: /* GPIO39, RTC3 */
RTCIO.sensor_pads.sense4_mux_sel = 1; /* route to RTC */
RTCIO.sensor_pads.sense4_fun_sel = 0; /* function ADC1_CH3 */
break;
case RTCIO_TOUCH0: /* GPIO4, RTC10 */
case RTCIO_TOUCH1: /* GPIO0, RTC11 */
case RTCIO_TOUCH2: /* GPIO2, RTC12 */
case RTCIO_TOUCH3: /* GPIO15, RTC13 */
case RTCIO_TOUCH4: /* GPIO13, RTC14 */
case RTCIO_TOUCH5: /* GPIO12, RTC15 */
case RTCIO_TOUCH6: /* GPIO14, RTC16 */
case RTCIO_TOUCH7: /* GPIO27, RTC17 */
case RTCIO_TOUCH8: /* GPIO33, RTC8 */
case RTCIO_TOUCH9: /* GPIO32, RTC9 */
idx = rtcio - RTCIO_TOUCH0;
RTCIO.touch_pad[idx].mux_sel = 1; /* route to RTC */
RTCIO.touch_pad[idx].fun_sel = 0; /* function ADC2_CH0..ADC2_CH9 */
RTCIO.touch_pad[idx].fun_ie = 0; /* input disabled */
RTCIO.touch_pad[idx].rue = 0; /* pull-up disabled */
RTCIO.touch_pad[idx].rde = 0; /* pull-down disabled */
RTCIO.touch_pad[idx].xpd = 0; /* touch sensor powered off */
break;
case RTCIO_ADC_ADC1: /* GPIO34, RTC4 */
RTCIO.adc_pad.adc1_mux_sel = 1; /* route to RTC */
RTCIO.adc_pad.adc1_fun_sel = 0; /* function ADC1_CH6 */
break;
case RTCIO_ADC_ADC2: /* GPIO35, RTC5 */
RTCIO.adc_pad.adc2_mux_sel = 1; /* route to RTC */
RTCIO.adc_pad.adc2_fun_sel = 0; /* function ADC1_CH7 */
break;
case RTCIO_DAC1: /* GPIO25, RTC6 */
case RTCIO_DAC2: /* GPIO26, RTC7 */
idx = rtcio - RTCIO_DAC1;
RTCIO.pad_dac[idx].mux_sel = 1; /* route to RTC */
RTCIO.pad_dac[idx].fun_sel = 0; /* function ADC2_CH8, ADC2_CH9 */
RTCIO.pad_dac[idx].fun_ie = 0; /* input disabled */
RTCIO.pad_dac[idx].rue = 0; /* pull-up disabled */
RTCIO.pad_dac[idx].rde = 0; /* pull-down disabled */
RTCIO.pad_dac[idx].xpd_dac = 0; /* DAC powered off */
break;
default: return -1;
}
/* set pin usage type */
gpio_set_pin_usage(_adc_hw[rtcio].gpio, _ADC);
return 0;
}
int adc_sample(adc_t line, adc_res_t res)
{
CHECK_PARAM_RET (line < adc_chn_num, -1)
CHECK_PARAM_RET (res <= ADC_RES_12BIT, -1)
uint8_t rtcio = _gpio_rtcio_map[adc_pins[line]];
if (_adc_hw[rtcio].adc_ctrl == ADC1_CTRL) {
/* set the resolution for the measurement */
SENS.sar_start_force.sar1_bit_width = res;
SENS.sar_read_ctrl.sar1_sample_bit = res;
/* enable the pad in the pad enable bitmap */
SENS.sar_meas_start1.sar1_en_pad = (1 << _adc_hw[rtcio].adc_channel);
while (SENS.sar_slave_addr1.meas_status != 0) {}
/* start measurement by toggling the start bit and wait until the
measurement has been finished */
SENS.sar_meas_start1.meas1_start_sar = 0;
SENS.sar_meas_start1.meas1_start_sar = 1;
while (SENS.sar_meas_start1.meas1_done_sar == 0) {}
/* read out the result and return */
return SENS.sar_meas_start1.meas1_data_sar;
}
else {
/* set the resolution for the measurement */
SENS.sar_start_force.sar2_bit_width = res;
SENS.sar_read_ctrl2.sar2_sample_bit = res;
/* enable the pad in the pad enable bitmap */
SENS.sar_meas_start2.sar2_en_pad = (1 << _adc_hw[rtcio].adc_channel);
/* start measurement by toggling the start bit and wait until the
measurement has been finished */
SENS.sar_meas_start2.meas2_start_sar = 0;
SENS.sar_meas_start2.meas2_start_sar = 1;
while (SENS.sar_meas_start2.meas2_done_sar == 0) {}
/* read out the result and return */
return SENS.sar_meas_start2.meas2_data_sar;
}
}
int adc_set_attenuation(adc_t line, adc_attenuation_t atten)
{
CHECK_PARAM_RET (line < adc_chn_num, -1)
uint8_t rtcio = _gpio_rtcio_map[adc_pins[line]];
if (_adc_hw[rtcio].adc_ctrl == ADC1_CTRL) {
SENS.sar_atten1 &= ~(0x3 << (_adc_hw[rtcio].adc_channel << 1));
SENS.sar_atten1 |= (atten << (_adc_hw[rtcio].adc_channel << 1));
}
else {
SENS.sar_atten2 &= ~(0x3 << (_adc_hw[rtcio].adc_channel << 1));
SENS.sar_atten2 |= (atten << (_adc_hw[rtcio].adc_channel << 1));
}
return 0;
}
int adc_vref_to_gpio25 (void)
{
/* determine ADC line for GPIO25 */
adc_t line = ADC_UNDEF;
for (unsigned i = 0; i < adc_chn_num; i++) { \
if (adc_pins[i] == GPIO25) { \
line = i;
break;
}
}
if (line == ADC_UNDEF) {
LOG_TAG_ERROR("adc", "Have no ADC line for GPIO25\n");
return -1;
}
if (adc_init(line) == 0)
{
uint8_t rtcio = _gpio_rtcio_map[adc_pins[line]];
RTCCNTL.bias_conf.dbg_atten = 0;
RTCCNTL.test_mux.dtest_rtc = 1;
RTCCNTL.test_mux.ent_rtc = 1;
SENS.sar_start_force.sar2_en_test = 1;
SENS.sar_meas_start2.sar2_en_pad = (1 << _adc_hw[rtcio].adc_channel);
LOG_TAG_INFO("adc", "You can now measure Vref at GPIO25\n");
return 0;
}
else {
LOG_TAG_ERROR("adc", "Could not init GPIO25 as Vref output\n");
return -1;
}
}
static bool _adc_conf_check(void)
{
for (unsigned i = 0; i < adc_chn_num; i++) {
if (_gpio_rtcio_map[adc_pins[i]] == RTCIO_NA) {
LOG_TAG_ERROR("adc", "GPIO%d cannot be used as ADC line\n",
adc_pins[i]);
return false;
}
}
return true;
}
static void _adc_module_init(void)
{
RTCIO.enable_w1tc.val = ~0x0;
/* always power on */
SENS.sar_meas_wait2.force_xpd_sar = SENS_FORCE_XPD_SAR_PU;
/* disable temperature sensor */
SENS.sar_tctrl.tsens_power_up_force = 1; /* controlled by SW */
SENS.sar_tctrl.tsens_power_up = 0; /* power down */
}
#endif /* defined(ADC_GPIOS) */
#if defined(DAC_GPIOS)
static bool _dac_conf_check(void);
static bool _dac_module_initialized = false;
int8_t dac_init (dac_t line)
{
CHECK_PARAM_RET (line < dac_chn_num, DAC_NOLINE)
if (!_dac_module_initialized) {
/* do some configuration checks */
if (!_dac_conf_check()) {
return -1;
}
_dac_module_initialized = true;
}
if (!_adc2_ctrl_initialized) {
_adc2_ctrl_init();
}
uint8_t rtcio = _gpio_rtcio_map[dac_pins[line]];
uint8_t idx;
/* try to initialize the pin as DAC ouput */
if (gpio_get_pin_usage(_adc_hw[rtcio].gpio) != _GPIO) {
LOG_TAG_ERROR("dac", "GPIO%d is used for %s and cannot be used as "
"DAC output\n", _adc_hw[rtcio].gpio,
gpio_get_pin_usage_str(_adc_hw[rtcio].gpio));
return DAC_NOLINE;
}
/* disable the output of the pad */
RTCIO.enable_w1tc.val = BIT(_adc_hw[rtcio].rtc_gpio);
switch (rtcio) {
case RTCIO_DAC1: /* GPIO25, RTC6 */
case RTCIO_DAC2: /* GPIO26, RTC7 */
idx = rtcio - RTCIO_DAC1;
RTCIO.pad_dac[idx].mux_sel = 1; /* route to RTC */
RTCIO.pad_dac[idx].fun_sel = 0; /* function ADC2_CH8, ADC2_CH9 */
RTCIO.pad_dac[idx].fun_ie = 0; /* input disabled */
RTCIO.pad_dac[idx].rue = 0; /* pull-up disabled */
RTCIO.pad_dac[idx].rde = 0; /* pull-down disabled */
RTCIO.pad_dac[idx].dac_xpd_force = 1; /* use RTC pad not the FSM*/
RTCIO.pad_dac[idx].xpd_dac = 1; /* DAC powered on */
break;
default: return DAC_NOLINE;
}
/* set pin usage type */
gpio_set_pin_usage(_adc_hw[rtcio].gpio, _DAC);
/* don't use DMA */
SENS.sar_dac_ctrl1.dac_dig_force = 0;
/* disable CW generators and invert DAC signal */
SENS.sar_dac_ctrl1.sw_tone_en = 0;
SENS.sar_dac_ctrl2.dac_cw_en1 = 0;
SENS.sar_dac_ctrl2.dac_cw_en2 = 0;
return DAC_OK;
}
void dac_set (dac_t line, uint16_t value)
{
CHECK_PARAM (line < dac_chn_num);
RTCIO.pad_dac[_gpio_rtcio_map[dac_pins[line]] - RTCIO_DAC1].dac = value >> 8;
}
void dac_poweroff (dac_t line)
{
CHECK_PARAM (line < dac_chn_num);
}
void dac_poweron (dac_t line)
{
CHECK_PARAM (line < dac_chn_num);
}
static bool _dac_conf_check(void)
{
for (unsigned i = 0; i < dac_chn_num; i++) {
if (_gpio_rtcio_map[dac_pins[i]] != RTCIO_DAC1 &&
_gpio_rtcio_map[dac_pins[i]] != RTCIO_DAC2) {
LOG_TAG_ERROR("dac", "GPIO%d cannot be used as DAC line\n",
dac_pins[i]);
return false;
}
}
return true;
}
#endif /* defined(DAC_GPIOS) */
#if defined(ADC_GPIOS) || defined(DAC_GPIOS)
static void _adc1_ctrl_init(void)
{
/* always power on */
SENS.sar_meas_wait2.force_xpd_sar = SENS_FORCE_XPD_SAR_PU;
/* power off LN amp */
SENS.sar_meas_wait2.sar2_rstb_wait = 2;
SENS.sar_meas_ctrl.amp_rst_fb_fsm = 0;
SENS.sar_meas_ctrl.amp_short_ref_fsm = 0;
SENS.sar_meas_ctrl.amp_short_ref_gnd_fsm = 0;
SENS.sar_meas_wait1.sar_amp_wait1 = 1;
SENS.sar_meas_wait1.sar_amp_wait2 = 1;
SENS.sar_meas_wait2.sar_amp_wait3 = 1;
SENS.sar_meas_wait2.force_xpd_amp = SENS_FORCE_XPD_AMP_PD;
/* SAR ADC1 controller configuration */
SENS.sar_read_ctrl.sar1_dig_force = 0; /* SAR ADC1 controlled by RTC */
SENS.sar_meas_start1.meas1_start_force = 1; /* SAR ADC1 started by SW */
SENS.sar_meas_start1.sar1_en_pad_force = 1; /* pad enable bitmap controlled by SW */
SENS.sar_touch_ctrl1.xpd_hall_force = 1; /* XPD HALL is controlled by SW */
SENS.sar_touch_ctrl1.hall_phase_force = 1; /* HALL PHASE is controlled by SW */
SENS.sar_read_ctrl.sar1_data_inv = 1; /* invert data */
SENS.sar_atten1 = 0xffffffff; /* set attenuation to 11 dB for all pads
(input range 0 ... 3,3 V) */
/* power off built-in hall sensor */
RTCIO.hall_sens.xpd_hall = 0;
/* set default resolution */
SENS.sar_start_force.sar1_bit_width = ADC_RES_12BIT;
SENS.sar_read_ctrl.sar1_sample_bit = ADC_RES_12BIT;
_adc1_ctrl_initialized = true;
}
static void _adc2_ctrl_init(void)
{
/* SAR ADC2 controller configuration */
SENS.sar_read_ctrl2.sar2_dig_force = 0; /* SAR ADC2 controlled by RTC not DIG*/
SENS.sar_meas_start2.meas2_start_force = 1; /* SAR ADC2 started by SW */
SENS.sar_meas_start2.sar2_en_pad_force = 1; /* pad enable bitmap controlled by SW */
SENS.sar_read_ctrl2.sar2_data_inv = 1; /* invert data */
SENS.sar_atten2 = 0xffffffff; /* set attenuation to 11 dB for all pads
(input range 0 ... 3,3 V) */
/* set default resolution */
SENS.sar_start_force.sar2_bit_width = ADC_RES_12BIT;
SENS.sar_read_ctrl2.sar2_sample_bit = ADC_RES_12BIT;
_adc2_ctrl_initialized = true;
}
#endif /* defined(ADC_GPIOS) || defined(DAC_GPIOS) */
extern const gpio_t _gpio_rtcio_map[];
int rtcio_config_sleep_mode (gpio_t pin, bool mode, bool input)
{
CHECK_PARAM_RET(pin < GPIO_PIN_NUMOF, -1);
uint8_t rtcio = _gpio_rtcio_map[pin];
uint8_t idx;
/* route pads to RTC and if possible, disable input, pull-up/pull-down */
switch (rtcio) {
case RTCIO_SENSOR_SENSE1: /* GPIO36, RTC0 */
RTCIO.sensor_pads.sense1_mux_sel = 1; /* route to RTC */
RTCIO.sensor_pads.sense1_fun_sel = 0; /* RTC mux function 0 */
RTCIO.sensor_pads.sense1_slp_sel = mode; /* sleep mode */
RTCIO.sensor_pads.sense1_slp_ie = input; /* input enabled */
break;
case RTCIO_SENSOR_SENSE2: /* GPIO37, RTC1 */
RTCIO.sensor_pads.sense2_mux_sel = 1; /* route to RTC */
RTCIO.sensor_pads.sense2_fun_sel = 0; /* RTC mux function 0 */
RTCIO.sensor_pads.sense2_slp_sel = mode; /* sleep mode */
RTCIO.sensor_pads.sense2_slp_ie = input; /* input enabled */
break;
case RTCIO_SENSOR_SENSE3: /* GPIO38, RTC2 */
RTCIO.sensor_pads.sense3_mux_sel = 1; /* route to RTC */
RTCIO.sensor_pads.sense3_fun_sel = 0; /* RTC mux function 0 */
RTCIO.sensor_pads.sense3_slp_sel = mode; /* sleep mode */
RTCIO.sensor_pads.sense3_slp_ie = input; /* input enabled */
break;
case RTCIO_SENSOR_SENSE4: /* GPIO39, RTC3 */
RTCIO.sensor_pads.sense4_mux_sel = 1; /* route to RTC */
RTCIO.sensor_pads.sense4_fun_sel = 0; /* RTC mux function 0 */
RTCIO.sensor_pads.sense4_slp_sel = mode; /* sleep mode */
RTCIO.sensor_pads.sense4_slp_ie = input; /* input enabled */
break;
case RTCIO_TOUCH0: /* GPIO4, RTC10 */
case RTCIO_TOUCH1: /* GPIO0, RTC11 */
case RTCIO_TOUCH2: /* GPIO2, RTC12 */
case RTCIO_TOUCH3: /* GPIO15, RTC13 */
case RTCIO_TOUCH4: /* GPIO13, RTC14 */
case RTCIO_TOUCH5: /* GPIO12, RTC15 */
case RTCIO_TOUCH6: /* GPIO14, RTC16 */
case RTCIO_TOUCH7: /* GPIO27, RTC17 */
case RTCIO_TOUCH8: /* GPIO33, RTC8 */
case RTCIO_TOUCH9: /* GPIO32, RTC9 */
idx = rtcio - RTCIO_TOUCH0;
RTCIO.touch_pad[idx].mux_sel = 1; /* route to RTC */
RTCIO.touch_pad[idx].fun_sel = 0; /* RTC mux function 0 */
RTCIO.touch_pad[idx].slp_sel = mode; /* sleep mode */
RTCIO.touch_pad[idx].slp_ie = input; /* input enabled */
RTCIO.touch_pad[idx].slp_oe = ~input; /* output enabled*/
break;
case RTCIO_ADC_ADC1: /* GPIO34, RTC4 */
RTCIO.adc_pad.adc1_mux_sel = 1; /* route to RTC */
RTCIO.adc_pad.adc1_fun_sel = 0; /* RTC mux function 0 */
RTCIO.adc_pad.adc1_slp_sel = mode; /* sleep mode */
RTCIO.adc_pad.adc1_slp_ie = input; /* input enabled */
break;
case RTCIO_ADC_ADC2: /* GPIO35, RTC5 */
RTCIO.adc_pad.adc2_mux_sel = 1; /* route to RTC */
RTCIO.adc_pad.adc2_fun_sel = 0; /* RTC mux function 0 */
RTCIO.adc_pad.adc2_slp_sel = mode; /* sleep mode */
RTCIO.adc_pad.adc2_slp_ie = input; /* input enabled */
break;
case RTCIO_DAC1: /* GPIO25, RTC6 */
case RTCIO_DAC2: /* GPIO26, RTC7 */
idx = rtcio - RTCIO_DAC1;
RTCIO.pad_dac[idx].mux_sel = 1; /* route to RTC */
RTCIO.pad_dac[idx].fun_sel = 0; /* RTC mux function 0 */
RTCIO.pad_dac[idx].slp_sel = mode; /* sleep mode */
RTCIO.pad_dac[idx].slp_ie = input; /* input enabled */
RTCIO.pad_dac[idx].slp_oe = ~input; /* output enabled*/
break;
default:
LOG_TAG_ERROR("gpio", "GPIO %d is not an RTCIO pin and "
"cannot be used in sleep mode\n", pin);
return -1;
}
return 0;
}
void adc_print_config(void) {
ets_printf("\tADC\t\tpins=[ ");
#if defined(ADC_GPIOS)
for (unsigned i = 0; i < adc_chn_num; i++) {
ets_printf("%d ", adc_pins[i]);
}
#endif /* defined(ADC_GPIOS) */
ets_printf("]\n");
ets_printf("\tDAC\t\tpins=[ ");
#if defined(DAC_GPIOS)
for (unsigned i = 0; i < dac_chn_num; i++) {
ets_printf("%d ", dac_pins[i]);
}
#endif /* defined(DAC_GPIOS) */
ets_printf("]\n");
}

43
cpu/esp32/periph/cpuid.c Normal file
View File

@ -0,0 +1,43 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_esp32
* @ingroup drivers_periph_cpuid
* @{
*
* @file
* @brief Implementation
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
#include <string.h>
#include <stdint.h>
#include "periph/cpuid.h"
#include "soc/efuse_reg.h"
void cpuid_get(void *id)
{
/* since ESP32 has two cores, the default MAC address is used as CPU id */
uint32_t rdata1 = REG_READ(EFUSE_BLK0_RDATA1_REG);
uint32_t rdata2 = REG_READ(EFUSE_BLK0_RDATA2_REG);
uint8_t *tmp = id;
tmp[0] = rdata2 >> 16;
tmp[1] = rdata2 >> 8;
tmp[2] = rdata2;
tmp[3] = rdata1 >> 24;
tmp[4] = rdata1 >> 16;
tmp[5] = rdata1 >> 8;
tmp[6] = rdata1;
}

431
cpu/esp32/periph/flash.c Normal file
View File

@ -0,0 +1,431 @@
/*
* 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_esp32
* @{
*
* @file
* @brief Low-level SPI flash and MTD drive implementation
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
#define ENABLE_DEBUG (0)
#include "debug.h"
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include "esp_common.h"
#include "irq_arch.h"
#include "log.h"
#include "mtd.h"
#include "rom/cache.h"
#include "rom/spi_flash.h"
#include "esp_flash_data_types.h"
#include "esp_partition.h"
#include "esp_spi_flash.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;
mtd_dev_t _flash_dev;
mtd_desc_t _flash_driver;
/* forward declaration of mtd functions */
static int _flash_init (mtd_dev_t *dev);
static int _flash_read (mtd_dev_t *dev, void *buff, uint32_t addr, uint32_t size);
static int _flash_write (mtd_dev_t *dev, const void *buff, uint32_t addr, uint32_t size);
static int _flash_erase (mtd_dev_t *dev, uint32_t addr, uint32_t size);
static int _flash_power (mtd_dev_t *dev, enum mtd_power_state power);
static uint32_t _flash_beg; /* first byte addr of the flash drive in SPI flash */
static uint32_t _flash_end; /* first byte addr after the flash drive in SPI flash */
static uint32_t _flash_size; /* resulting size of the flash drive in SPI flash */
static esp_rom_spiflash_chip_t* _flashchip = NULL;
void spi_flash_drive_init (void)
{
DEBUG("%s\n", __func__);
_flashchip = &g_rom_flashchip;
_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;
/* 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;
}
}
/* map the partition top address to next higher multiple of 0x100000 */
part_top = (part_top + 0x100000) & ~0xfffff;
/*
* 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
LOG_TAG_INFO("spi_flash",
"MTD in SPI flash starts at address 0x%08x\n", part_top);
/* 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) */
_flash_dev.driver = &_flash_driver;
_flash_dev.sector_count = _flash_size / _flashchip->sector_size;
mtd0 = &_flash_dev;
_flash_dev.pages_per_sector = _flashchip->sector_size / _flashchip->page_size;
_flash_dev.page_size = _flashchip->page_size;
DEBUG("%s flashchip chip_size=%d block_size=%d sector_size=%d page_size=%d\n", __func__,
_flashchip->chip_size, _flashchip->block_size,
_flashchip->sector_size, _flashchip->page_size);
DEBUG("%s flash_dev sector_count=%d pages_per_sector=%d page_size=%d\n", __func__,
_flash_dev.sector_count, _flash_dev.pages_per_sector, _flash_dev.page_size);
DEBUG("\n");
}
#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);
/* prepare for write access */
critical_enter();
Cache_Read_Disable(PRO_CPU_NUM);
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;
result = esp_rom_spiflash_read (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 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;
}
result |= esp_rom_spiflash_read (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 (len && result == ESP_ROM_SPIFLASH_RESULT_OK) {
result |= esp_rom_spiflash_read (addr, (uint32_t*)_flash_buf, 4);
memcpy(buff, _flash_buf, len);
}
/* reset read access */
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 */
critical_enter();
Cache_Read_Disable(PRO_CPU_NUM);
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;
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);
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;
}
memcpy(_flash_buf, buff, len_full_words);
result |= esp_rom_spiflash_write (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 (len && result == ESP_ROM_SPIFLASH_RESULT_OK) {
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);
}
/* reset write access */
esp_rom_spiflash_lock();
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_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 */
critical_enter();
Cache_Read_Disable(PRO_CPU_NUM);
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;
while (cnt-- && result == ESP_ROM_SPIFLASH_RESULT_OK) {
result = esp_rom_spiflash_erase_sector (sec++);
}
/* reset write access */
esp_rom_spiflash_lock();
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);
}
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);
return 0;
}
static int _flash_read (mtd_dev_t *dev, void *buff, uint32_t addr, uint32_t size)
{
DEBUG("%s dev=%p addr=%08x size=%u buf=%p\n", __func__, dev, addr, size, buff);
CHECK_PARAM_RET (dev == &_flash_dev, -ENODEV);
CHECK_PARAM_RET (buff != NULL, -ENOTSUP);
/* size must be within the flash address space */
CHECK_PARAM_RET (_flash_beg + addr + size <= _flash_end, -EOVERFLOW);
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)
{
DEBUG("%s dev=%p addr=%08x size=%u buf=%p\n", __func__, dev, addr, size, buff);
CHECK_PARAM_RET (dev == &_flash_dev, -ENODEV);
CHECK_PARAM_RET (buff != NULL, -ENOTSUP);
/* size must be within the flash address space */
CHECK_PARAM_RET (_flash_beg + addr + size <= _flash_end, -EOVERFLOW);
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)
{
DEBUG("%s dev=%p addr=%08x size=%u\n", __func__, dev, addr, size);
CHECK_PARAM_RET (dev == &_flash_dev, -ENODEV);
/* size must be within the flash address space */
CHECK_PARAM_RET (_flash_beg + addr + size <= _flash_end, -EOVERFLOW);
/* size must be a multiple of sector_size && at least one sector */
CHECK_PARAM_RET (size >= _flashchip->sector_size, -ENOTSUP);
CHECK_PARAM_RET (size % _flashchip->sector_size == 0, -ENOTSUP)
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)
{
DEBUG("%s\n", __func__);
return -ENOTSUP;
}

527
cpu/esp32/periph/gpio.c Normal file
View File

@ -0,0 +1,527 @@
/*
* 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_esp32
* @ingroup drivers_periph_gpio
* @{
*
* @file
* @brief Low-level GPIO driver implementation for ESP32
*
* @author Gunar Schorcht <gunar@schorcht.net>
* @}
*/
#define ENABLE_DEBUG (0)
#include "debug.h"
#include <stdbool.h>
#include "log.h"
#include "periph/gpio.h" /* RIOT gpio.h */
#include "esp/common_macros.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/rtc_io_reg.h"
#include "soc/rtc_io_struct.h"
#include "xtensa/xtensa_api.h"
#include "esp_common.h"
#include "adc_arch.h"
#include "gpio_arch.h"
#include "irq_arch.h"
#include "syscalls.h"
#define GPIO_PRO_CPU_INTR_ENA (BIT(2))
/* GPIO to IOMUX register mapping (see Technical Reference, Section 4.12 Register Summary)
https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf */
const uint32_t _gpio_to_iomux_reg[GPIO_PIN_NUMOF] =
{
PERIPHS_IO_MUX_GPIO0_U, /* GPIO0 */
PERIPHS_IO_MUX_U0TXD_U, /* GPIO1 */
PERIPHS_IO_MUX_GPIO2_U, /* GPIO2 */
PERIPHS_IO_MUX_U0RXD_U, /* GPIO3 */
PERIPHS_IO_MUX_GPIO4_U, /* GPIO4 */
PERIPHS_IO_MUX_GPIO5_U, /* GPIO5 */
PERIPHS_IO_MUX_SD_CLK_U, /* GPIO6 used for FLASH */
PERIPHS_IO_MUX_SD_DATA0_U, /* GPIO7 used for FLASH */
PERIPHS_IO_MUX_SD_DATA1_U, /* GPIO8 used for FLASH */
PERIPHS_IO_MUX_SD_DATA2_U, /* GPIO9 used for FLASH in qio or qout mode */
PERIPHS_IO_MUX_SD_DATA3_U, /* GPIO10 used for FLASH in qio or qout mode */
PERIPHS_IO_MUX_SD_CMD_U, /* GPIO11 used for FLASH */
PERIPHS_IO_MUX_MTDI_U, /* GPIO12 used as JTAG for OCD */
PERIPHS_IO_MUX_MTCK_U, /* GPIO13 used as JTAG for OCD */
PERIPHS_IO_MUX_MTMS_U, /* GPIO14 used as JTAG for OCD */
PERIPHS_IO_MUX_MTDO_U, /* GPIO15 used as JTAG for OCD */
PERIPHS_IO_MUX_GPIO16_U, /* GPIO16 used as CS for PSRAM TODO */
PERIPHS_IO_MUX_GPIO17_U, /* GPIO17 */
PERIPHS_IO_MUX_GPIO18_U, /* GPIO18 */
PERIPHS_IO_MUX_GPIO19_U, /* GPIO19 */
0, /* GPIO20 not available */
PERIPHS_IO_MUX_GPIO21_U, /* GPIO21 */
PERIPHS_IO_MUX_GPIO22_U, /* GPIO22 */
PERIPHS_IO_MUX_GPIO23_U, /* GPIO23 */
0, /* GPIO24 not available */
PERIPHS_IO_MUX_GPIO25_U, /* GPIO25 */
PERIPHS_IO_MUX_GPIO26_U, /* GPIO26 */
PERIPHS_IO_MUX_GPIO27_U, /* GPIO27 */
0, /* GPIO28 not available */
0, /* GPIO29 not available */
0, /* GPIO30 not available */
0, /* GPIO31 not available */
PERIPHS_IO_MUX_GPIO32_U, /* GPIO32 */
PERIPHS_IO_MUX_GPIO33_U, /* GPIO33 */
PERIPHS_IO_MUX_GPIO34_U, /* GPIO34 */
PERIPHS_IO_MUX_GPIO35_U, /* GPIO35 */
PERIPHS_IO_MUX_GPIO36_U, /* GPIO36 */
PERIPHS_IO_MUX_GPIO37_U, /* GPIO37 */
PERIPHS_IO_MUX_GPIO38_U, /* GPIO38 */
PERIPHS_IO_MUX_GPIO39_U, /* GPIO39 */
};
/*
* Following table defines which GPIOs have to be handled using RTC_GPIO
* registers since pull-up and pull-down resistors for pads with both GPIO
* and RTC_GPIO functionality can only be controlled via RTC_GPIO registers
* https://www.espressif.com/sites/default/files/documentation/eco_and_workarounds_for_bugs_in_esp32_en.pdf
*
* The table contains the RTC_GPIO num or -1 if it is not an RTC_GPIO pin.
*/
static const int8_t _gpio_to_rtc[GPIO_PIN_NUMOF] = {
11, /* gpio0 */
-1, /* gpio1 */
12, /* gpio2 */
-1, /* gpio3 */
10, /* gpio4 */
-1, /* gpio5 */
-1, /* gpio6 */
-1, /* gpio7 */
-1, /* gpio8 */
-1, /* gpio9 */
-1, /* gpio10 */
-1, /* gpio11 */
15, /* gpio12 */
14, /* gpio13 */
16, /* gpio14 */
13, /* gpio15 */
-1, /* gpio16 */
-1, /* gpio17 */
-1, /* gpio18 */
-1, /* gpio19 */
-1, /* gpio20 */
-1, /* gpio21 */
-1, /* gpio22 */
-1, /* gpio23 */
-1, /* gpio24 */
6, /* gpio25 */
7, /* gpio26 */
17, /* gpio27 */
-1, /* gpio28 */
-1, /* gpio29 */
-1, /* gpio30 */
-1, /* gpio31 */
9, /* gpio32 */
8, /* gpio33 */
4, /* gpio34 */
5, /* gpio35 */
0, /* gpio36 */
1, /* gpio37 */
2, /* gpio38 */
9 /* gpio39 */
};
/**
* @brief Register information type for RTCIO GPIO pins (for internal use only)
*/
struct _rtc_gpio_t {
uint8_t num; /**< RTC_GPIO pin number */
uint32_t reg; /**< register of the RTC_GPIO pin */
uint8_t mux; /**< mux io/rtc bit [0..31] in the register, 32 - no mux */
uint8_t pullup; /**< pullup bit [0..31] in the register, 32 - no pullup */
uint8_t pulldown; /**< pulldown bit [0..31] in the register, 32 - no pulldown */
};
/* Table of RTCIO GPIO pins information */
static const struct _rtc_gpio_t _rtc_gpios[] = {
{ 0, RTC_IO_SENSOR_PADS_REG, 27, 32, 32 }, /* rtc0 (gpio36) - no pullup/pulldown */
{ 1, RTC_IO_SENSOR_PADS_REG, 26, 32, 32 }, /* rtc1 (gpio37) - no pullup/pulldown */
{ 2, RTC_IO_SENSOR_PADS_REG, 25, 32, 32 }, /* rtc2 (gpio38) - no pullup/pulldown */
{ 3, RTC_IO_SENSOR_PADS_REG, 24, 32, 32 }, /* rtc3 (gpio39) - no pullup/pulldown */
{ 4, RTC_IO_ADC_PAD_REG, 29, 32, 32 }, /* rtc4 (gpio34) - no pullup/pulldown */
{ 5, RTC_IO_ADC_PAD_REG, 28, 32, 32 }, /* rtc5 (gpio35) - no pullup/pulldown */
{ 6, RTC_IO_PAD_DAC1_REG, 17, 27, 28 }, /* rtc6 (gpio25) */
{ 7, RTC_IO_PAD_DAC2_REG, 17, 27, 28 }, /* rtc7 (gpio26) */
{ 8, RTC_IO_XTAL_32K_PAD_REG, 18, 27, 28 },/* rtc8 (gpio33) */
{ 9, RTC_IO_XTAL_32K_PAD_REG, 17, 22, 23 },/* rtc9 (gpio32) */
{ 10, RTC_IO_TOUCH_PAD0_REG, 19, 27, 28 }, /* rtc10 (gpio4) */
{ 11, RTC_IO_TOUCH_PAD1_REG, 19, 27, 28 }, /* rtc11 (gpio0) */
{ 12, RTC_IO_TOUCH_PAD2_REG, 19, 27, 28 }, /* rtc12 (gpio2) */
{ 13, RTC_IO_TOUCH_PAD3_REG, 19, 27, 28 }, /* rtc13 (gpio15) */
{ 14, RTC_IO_TOUCH_PAD4_REG, 19, 27, 28 }, /* rtc14 (gpio13) */
{ 15, RTC_IO_TOUCH_PAD5_REG, 19, 27, 28 }, /* rtc15 (gpio12) */
{ 16, RTC_IO_TOUCH_PAD6_REG, 19, 27, 28 }, /* rtc16 (gpio14) */
{ 17, RTC_IO_TOUCH_PAD7_REG, 19, 27, 28 } /* rtc17 (gpio27) */
};
/* Table of the usage type of each GPIO pin */
gpio_pin_usage_t _gpio_pin_usage [GPIO_PIN_NUMOF] = {
_GPIO, /* gpio0 */
_UART, /* gpio1 configured as direct I/O UART0 RxD */
_GPIO, /* gpio2 */
_UART, /* gpio3 configured as direct I/O UART0 TxD */
_GPIO, /* gpio4 */
_GPIO, /* gpio5 configurable as direct I/O VSPI CS0 */
_SPIF, /* gpio6 not configurable, used as SPI SCK */
_SPIF, /* gpio7 not configurable, used as SPI MISO */
_SPIF, /* gpio8 not configurable, used as SPI MOSI */
#if defined(FLASH_MODE_QIO) || defined(FLASH_MODE_QOUT)
/* in qio and qout mode thes pins are used for quad SPI */
_SPIF, /* gpio9 not configurable, used as SPI HD */
_SPIF, /* gpio10 not configurable, used as SPI WP */
#else
/* otherwise these pins can be used as GPIO */
_GPIO, /* gpio9 */
_GPIO, /* gpio10 */
#endif
_SPIF, /* gpio11 not configurable, used as SPI CS0 */
_GPIO, /* gpio12 configurable as direct I/O HSPI MISO */
_GPIO, /* gpio13 configurable as direct I/O HSPI MOSI */
_GPIO, /* gpio14 configurable as direct I/O HSPI SCK */
_GPIO, /* gpio15 configurable as direct I/O HSPI CS0 */
_GPIO, /* gpio16 */
_GPIO, /* gpio17 */
_GPIO, /* gpio18 configurable as direct I/O VSPI SCK */
_GPIO, /* gpio19 configurable as direct I/O VSPI MISO */
_NOT_EXIST, /* gpio20 */
_GPIO, /* gpio21 */
_GPIO, /* gpio22 */
_GPIO, /* gpio23 configurable as direct I/O VSPI MOSI */
_NOT_EXIST, /* gpio24 */
_GPIO, /* gpio25 */
_GPIO, /* gpio26 */
_GPIO, /* gpio27 */
_NOT_EXIST, /* gpio28 */
_NOT_EXIST, /* gpio29 */
_NOT_EXIST, /* gpio30 */
_NOT_EXIST, /* gpio31 */
_GPIO, /* gpio32 */
_GPIO, /* gpio33 */
_GPIO, /* gpio34 */
_GPIO, /* gpio35 */
_GPIO, /* gpio36 */
_GPIO, /* gpio37 */
_GPIO, /* gpio38 */
_GPIO /* gpio39 */
};
/* String representation of usage types */
const char* _gpio_pin_usage_str[] =
{
"GPIO", "ADC", "CAN", "DAC", "EMAC", "I2C", "PWM", "SPI", "SPI Flash", "UART", "N/A"
};
#define FUN_GPIO 2 /* the function number for all GPIOs */
#define GPIO_PIN_SET(b) if (b < 32) GPIO.out_w1ts = BIT(b); else GPIO.out1_w1ts.val = BIT(b-32)
#define GPIO_PIN_CLR(b) if (b < 32) GPIO.out_w1tc = BIT(b); else GPIO.out1_w1tc.val = BIT(b-32)
#define GPIO_REG_BIT_GET(l,h,b) ((b < 32) ? GPIO.l & BIT(b) : GPIO.h.val & BIT(b-32))
#define GPIO_REG_BIT_SET(l,h,b) if (b < 32) GPIO.l |= BIT(b); else GPIO.h.val |= BIT(b-32)
#define GPIO_REG_BIT_CLR(l,h,b) if (b < 32) GPIO.l &= ~BIT(b); else GPIO.h.val &= ~BIT(b-32)
#define GPIO_REG_BIT_XOR(l,h,b) if (b < 32) GPIO.l ^= BIT(b); else GPIO.h.val ^= BIT(b-32)
#define REG_SET_CLR_BIT(c,r,f) if (c) REG_SET_BIT(r,f); else REG_CLR_BIT(r,f)
int gpio_init(gpio_t pin, gpio_mode_t mode)
{
CHECK_PARAM_RET(pin < GPIO_PIN_NUMOF, -1);
/* check if the pin can be used as GPIO or if it is used for something else */
if (_gpio_pin_usage[pin] != _GPIO) {
LOG_TAG_ERROR("gpio", "GPIO%d is already used as %s signal\n", pin,
_gpio_pin_usage_str[_gpio_pin_usage[pin]]);
return -1;
}
/* check additional limitations for GPIOs 34 ... 39 */
if (pin > GPIO33) {
switch (mode) {
case GPIO_OUT:
case GPIO_OD:
case GPIO_OD_PU:
case GPIO_IN_OUT:
case GPIO_IN_OD:
case GPIO_IN_OD_PU:
/* GPIOs 34 ... 39 cannot be used as output */
LOG_TAG_ERROR("gpio",
"GPIO%d can only be used as input\n", pin);
return -1;
case GPIO_IN_PD:
case GPIO_IN_PU:
/* GPIOs 34 ... 39 have no software controlable pullups/pulldowns */
LOG_TAG_ERROR("gpio",
"GPIO%d has no pullups/pulldowns\n", pin);
return -1;
default: break;
}
}
const struct _rtc_gpio_t* rtc = (_gpio_to_rtc[pin] != -1) ?
&_rtc_gpios[_gpio_to_rtc[pin]] : NULL;
/* if pin is a RTC_GPIO, reset RTC function to route RTC pin to GPIO pin */
if (rtc) {
REG_CLR_BIT(rtc->reg, BIT(rtc->mux));
}
switch (mode) {
case GPIO_IN:
case GPIO_IN_PD:
case GPIO_IN_PU:
/* according to Technical Reference it is not necessary
to configure the GPIO matrix to read GPIOs from
GPIO_IN_REG/GPIO_IN1_REG */
#if 0
/* configure the GPIO matrix for the inputs */
GPIO.func_in_sel_cfg[signal].sig_in_sel = 0; /* route through GPIO matrix */
GPIO.func_in_sel_cfg[signal].sig_in_inv = 0; /* do not invert input */
GPIO.func_in_sel_cfg[signal].func_sel = pin; /* connect signal to GPIOx */
#endif
/* disable the output for input-only signals */
GPIO.func_out_sel_cfg[pin].oen_sel = 1;
GPIO_REG_BIT_SET(enable_w1tc, enable1_w1tc, pin);
/* set the FUN_IE bit for input */
REG_SET_BIT(_gpio_to_iomux_reg[pin], FUN_IE);
/* FUN_GPIO / FUN_WPU / FUN_WPD are set later */
break;
case GPIO_OUT:
case GPIO_OD:
case GPIO_OD_PU:
/* configure GPIO signal in the GPIO matrix */
GPIO.func_out_sel_cfg[pin].func_sel = SIG_GPIO_OUT_IDX;
/* enable the output */
GPIO.func_out_sel_cfg[pin].oen_sel = 1;
GPIO_REG_BIT_SET(enable_w1ts, enable1_w1ts, pin);
/* set pad driver to one for open drain outputs */
GPIO.pin[pin].pad_driver = (mode == GPIO_OD || mode == GPIO_OD_PU) ? 1 : 0;
/* set pad driver also for RTC pin if it is a RTC_GPIO */
if (rtc) {
RTCIO.pin[rtc->num].pad_driver = GPIO.pin[pin].pad_driver;
}
/* clear the FUN_IE bit */
REG_CLR_BIT(_gpio_to_iomux_reg[pin], FUN_IE);
/* FUN_GPIO / FUN_WPU / FUN_WPD are set later */
break;
case GPIO_IN_OUT:
case GPIO_IN_OD:
case GPIO_IN_OD_PU:
/* configure GPIO signal in the GPIO matrix */
GPIO.func_out_sel_cfg[pin].func_sel = SIG_GPIO_OUT_IDX;
/* enable the output */
GPIO.func_out_sel_cfg[pin].oen_sel = 1;
GPIO_REG_BIT_SET(enable_w1ts, enable1_w1ts, pin);
/* set pad driver to one for open drain outputs */
GPIO.pin[pin].pad_driver = (mode == GPIO_IN_OD || mode == GPIO_IN_OD_PU) ? 1 : 0;
/* set pad driver also for RTC pin if it is a RTC_GPIO */
if (rtc) {
RTCIO.pin[rtc->num].pad_driver = GPIO.pin[pin].pad_driver;
}
/* enable the input */
REG_SET_BIT(_gpio_to_iomux_reg[pin], FUN_IE);
/* FUN_GPIO / FUN_WPU / FUN_WPD are set later */
break;
}
/* select GPIO as IO_MUX function (FUN_GPIO) */
REG_SET_FIELD(_gpio_to_iomux_reg[pin], MCU_SEL, FUN_GPIO);
/* enable/disable the pull-up resistor (FUN_WPU) */
REG_SET_CLR_BIT(mode == GPIO_IN_PU || mode == GPIO_OD_PU || mode == GPIO_IN_OD_PU,
_gpio_to_iomux_reg[pin], FUN_PU);
/* enable/disable the pull-down resistor (FUN_WPD) */
REG_SET_CLR_BIT(mode == GPIO_IN_PD, _gpio_to_iomux_reg[pin], FUN_PD);
/* handle pull-up/pull-down resistors for RTC_GPIOs */
if (rtc) {
/* enable/disable the pull-up resistor (FUN_WPU) */
REG_SET_CLR_BIT(mode == GPIO_IN_PU || mode == GPIO_OD_PU || mode == GPIO_IN_OD_PU,
rtc->reg, BIT(rtc->pullup));
/* enable/disable the pull-down resistor (FUN_WPD) */
REG_SET_CLR_BIT(mode == GPIO_IN_PD, rtc->reg, BIT(rtc->pulldown));
}
return 0;
}
static gpio_isr_ctx_t gpio_isr_ctx_table [GPIO_PIN_NUMOF] = { };
static bool gpio_int_enabled_table [GPIO_PIN_NUMOF] = { };
void IRAM gpio_int_handler (void* arg)
{
irq_isr_enter();
(void)arg;
for (unsigned i = 0; i < GPIO_PIN_NUMOF; i++) {
if (GPIO_REG_BIT_GET(status, status1, i)) {
GPIO_REG_BIT_SET(status_w1tc, status1_w1tc, i);
if (gpio_int_enabled_table[i] && GPIO.pin[i].int_type) {
gpio_isr_ctx_table[i].cb (gpio_isr_ctx_table[i].arg);
}
}
}
irq_isr_exit();
}
int gpio_init_int(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank,
gpio_cb_t cb, void *arg)
{
if (gpio_init(pin, mode)) {
return -1;
}
gpio_isr_ctx_table[pin].cb = cb;
gpio_isr_ctx_table[pin].arg = arg;
GPIO.pin[pin].int_type = flank;
if (flank != GPIO_NONE) {
gpio_int_enabled_table [pin] = (gpio_isr_ctx_table[pin].cb != NULL);
GPIO.pin[pin].int_ena = GPIO_PRO_CPU_INTR_ENA;
intr_matrix_set(PRO_CPU_NUM, ETS_GPIO_INTR_SOURCE, CPU_INUM_GPIO);
xt_set_interrupt_handler(CPU_INUM_GPIO, gpio_int_handler, NULL);
xt_ints_on(BIT(CPU_INUM_GPIO));
}
return 0;
}
void gpio_irq_enable (gpio_t pin)
{
CHECK_PARAM(pin < GPIO_PIN_NUMOF);
gpio_int_enabled_table [pin] = true;
}
void gpio_irq_disable (gpio_t pin)
{
CHECK_PARAM(pin < GPIO_PIN_NUMOF);
gpio_int_enabled_table [pin] = false;
}
int gpio_read (gpio_t pin)
{
CHECK_PARAM_RET(pin < GPIO_PIN_NUMOF, -1);
return GPIO_REG_BIT_GET(in, in1, pin) ? 1 : 0;
}
void gpio_write (gpio_t pin, int value)
{
DEBUG("%s gpio=%u val=%d\n", __func__, pin, value);
CHECK_PARAM(pin < GPIO_PIN_NUMOF);
if (value) {
GPIO_PIN_SET(pin);
}
else {
GPIO_PIN_CLR(pin);
}
}
void gpio_set (gpio_t pin)
{
DEBUG("%s gpio=%u\n", __func__, pin);
CHECK_PARAM(pin < GPIO_PIN_NUMOF);
GPIO_PIN_SET(pin);
}
void gpio_clear (gpio_t pin)
{
DEBUG("%s gpio=%u\n", __func__, pin);
CHECK_PARAM(pin < GPIO_PIN_NUMOF);
GPIO_PIN_CLR(pin);
}
void gpio_toggle (gpio_t pin)
{
DEBUG("%s gpio=%u\n", __func__, pin);
CHECK_PARAM(pin < GPIO_PIN_NUMOF);
GPIO_REG_BIT_XOR(out, out1, 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)]];
}
void gpio_pullup_dis (gpio_t pin)
{
CHECK_PARAM(pin < GPIO_PIN_NUMOF);
const struct _rtc_gpio_t* rtc = (_gpio_to_rtc[pin] != -1) ?
&_rtc_gpios[_gpio_to_rtc[pin]] : NULL;
REG_CLR_BIT(_gpio_to_iomux_reg[pin], FUN_PU);
if (rtc) {
REG_CLR_BIT(rtc->reg, BIT(rtc->pullup));
}
}
int8_t gpio_is_rtcio (gpio_t pin)
{
return _gpio_to_rtc[pin];
}
int gpio_config_sleep_mode (gpio_t pin, bool mode, bool input)
{
return rtcio_config_sleep_mode (pin, mode, input);
}
int gpio_set_direction(gpio_t pin, gpio_mode_t mode)
{
/* TODO implementation, for the moment we simply initialize the GPIO */
return gpio_init(pin, mode);
}

55
cpu/esp32/periph/hwrng.c Normal file
View File

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

869
cpu/esp32/periph/i2c_hw.c Normal file
View File

@ -0,0 +1,869 @@
/*
* 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_esp32
* @ingroup drivers_periph_i2c
* @{
*
* @file
* @brief Low-level I2C driver implementation for ESP32 SDK
*
* @note The hardware implementation seems to be very poor and faulty.
* I2C commands in the I2C command pipeline are not executed
* sporadically. A number of ACK errors and timeouts caused by
* protocol errors are the result. You should use the hardware
* implementation only if they can be tolerated.
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
#if defined(MODULE_ESP_I2C_HW) /* hardware implementation used */
/**
* PLEASE NOTE:
*
* Some parts of the implementation were inspired by the Espressif IoT
* Development Framework [ESP-IDF](https://github.com/espressif/esp-idf.git)
* implementation of I2C. These partes are marked with an according copyright
* notice.
*/
#define ENABLE_DEBUG (0)
#include "debug.h"
#include <errno.h>
#include <stdbool.h>
#include <string.h>
#include "cpu.h"
#include "log.h"
#include "mutex.h"
#include "periph_conf.h"
#include "periph/gpio.h"
#include "periph/i2c.h"
#include "thread_flags.h"
#include "esp_common.h"
#include "gpio_arch.h"
#include "driver/periph_ctrl.h"
#include "irq_arch.h"
#include "rom/ets_sys.h"
#include "soc/gpio_reg.h"
#include "soc/gpio_sig_map.h"
#include "soc/gpio_struct.h"
#include "soc/i2c_reg.h"
#include "soc/i2c_struct.h"
#include "soc/rtc.h"
#include "soc/soc.h"
#include "syscalls.h"
#include "xtensa/xtensa_api.h"
#if defined(I2C0_SPEED) || defined(I2C1_SPEED)
#undef I2C_CLK_FREQ
#define I2C_CLK_FREQ rtc_clk_apb_freq_get() /* APB_CLK is used */
/* operation codes used for commands */
#define I2C_CMD_RSTART 0
#define I2C_CMD_WRITE 1
#define I2C_CMD_READ 2
#define I2C_CMD_STOP 3
#define I2C_CMD_END 4
/* maximum number of data that can be written / read in one transfer */
#define I2C_MAX_DATA 30
#define I2C_FIFO_USED 1
struct i2c_hw_t {
i2c_dev_t* regs; /* pointer to register data struct of the I2C device */
uint8_t mod; /* peripheral hardware module of the I2C interface */
uint8_t int_src; /* peripheral interrupt source used by the I2C device */
uint8_t pin_scl; /* SCL pin */
uint8_t pin_sda; /* SDA pin */
uint8_t signal_scl_in; /* SCL signal to the controller */
uint8_t signal_scl_out; /* SCL signal from the controller */
uint8_t signal_sda_in; /* SDA signal to the controller */
uint8_t signal_sda_out; /* SDA signal from the controller */
};
static const struct i2c_hw_t _i2c_hw[] = {
#if defined(I2C0_SCL) && defined(I2C0_SDA) && defined(I2C0_SPEED)
{
.regs = &I2C0,
.mod = PERIPH_I2C0_MODULE,
.int_src = ETS_I2C_EXT0_INTR_SOURCE,
.pin_scl = I2C0_SCL,
.pin_sda = I2C0_SDA,
.signal_scl_in = I2CEXT0_SCL_IN_IDX,
.signal_scl_out = I2CEXT0_SCL_OUT_IDX,
.signal_sda_in = I2CEXT0_SDA_IN_IDX,
.signal_sda_out = I2CEXT0_SDA_OUT_IDX,
},
#endif
#if defined(I2C1_SCL) && defined(I2C1_SDA) && defined(I2C1_SPEED)
{
.regs = &I2C1,
.mod = PERIPH_I2C1_MODULE,
.int_src = ETS_I2C_EXT1_INTR_SOURCE,
.pin_scl = I2C1_SCL,
.pin_sda = I2C1_SDA,
.signal_scl_in = I2CEXT1_SCL_IN_IDX,
.signal_scl_out = I2CEXT1_SCL_OUT_IDX,
.signal_sda_in = I2CEXT1_SDA_IN_IDX,
.signal_sda_out = I2CEXT1_SDA_OUT_IDX,
}
#endif
};
struct _i2c_bus_t
{
i2c_speed_t speed; /* bus speed */
uint8_t cmd; /* command index */
uint8_t data; /* index in RAM for data */
mutex_t lock; /* mutex lock */
kernel_pid_t pid; /* PID of thread that triggered a transfer */
uint32_t results; /* results of a transfer */
};
static struct _i2c_bus_t _i2c_bus[] =
{
#if defined(I2C0_SCL) && defined(I2C0_SDA) && defined(I2C0_SPEED)
{
.speed = I2C0_SPEED,
.cmd = 0,
.data = 0,
.lock = MUTEX_INIT
},
#endif
#if defined(I2C1_SCL) && defined(I2C1_SDA) && defined(I2C1_SPEED)
{
.speed = I2C1_SPEED,
.cmd = 0,
.data = 0,
.lock = MUTEX_INIT
},
#endif
};
/* the number of I2C bus devices used */
const unsigned i2c_bus_num = sizeof(_i2c_bus) / sizeof(_i2c_bus[0]);
/* forward declaration of internal functions */
static int _i2c_init_pins (i2c_t dev);
static void _i2c_start_cmd (i2c_t dev);
static void _i2c_stop_cmd (i2c_t dev);
static void _i2c_end_cmd (i2c_t dev);
static void _i2c_write_cmd (i2c_t dev, const uint8_t* data, uint8_t len);
static void _i2c_read_cmd (i2c_t dev, uint8_t* data, uint8_t len, bool last);
static void _i2c_transfer (i2c_t dev);
static void _i2c_reset_hw (i2c_t dev);
static void _i2c_clear_bus (i2c_t dev);
static void _i2c_intr_handler (void *arg);
static inline void _i2c_delay (uint32_t delay);
/* implementation of i2c interface */
void i2c_init(i2c_t dev)
{
CHECK_PARAM (dev < i2c_bus_num)
if (_i2c_bus[dev].speed == I2C_SPEED_FAST_PLUS ||
_i2c_bus[dev].speed == I2C_SPEED_HIGH) {
LOG_TAG_INFO("i2c", "I2C_SPEED_FAST_PLUS and I2C_SPEED_HIGH "
"are not supported\n");
return;
}
i2c_acquire (dev);
_i2c_bus[dev].cmd = 0;
_i2c_bus[dev].data = 0;
DEBUG ("%s scl=%d sda=%d speed=%d\n", __func__,
_i2c_hw[dev].pin_scl, _i2c_hw[dev].pin_sda, _i2c_bus[dev].speed);
/* enable (power on) the according I2C module */
periph_module_enable(_i2c_hw[dev].mod);
/* initialize pins */
if (_i2c_init_pins(dev) != 0) {
return;
}
/* set master mode */
_i2c_hw[dev].regs->ctr.ms_mode = 1;
/* set bit order to MSB first */
_i2c_hw[dev].regs->ctr.tx_lsb_first = 0;
_i2c_hw[dev].regs->ctr.rx_lsb_first = 0;
/* determine the half period of clock in APB clock cycles */
uint32_t half_period = 0;
switch (_i2c_bus[dev].speed) {
case I2C_SPEED_LOW:
/* 10 kbps (period 100 us) */
half_period = (I2C_CLK_FREQ / 10000) >> 1;
break;
case I2C_SPEED_NORMAL:
/* 100 kbps (period 10 us) */
half_period = (I2C_CLK_FREQ / 100000) >> 1;
half_period = half_period * 95 / 100; /* correction factor */
break;
case I2C_SPEED_FAST:
/* 400 kbps (period 2.5 us) */
half_period = (I2C_CLK_FREQ / 400000) >> 1;
half_period = half_period * 82 / 100; /* correction factor */
break;
case I2C_SPEED_FAST_PLUS:
/* 1 Mbps (period 1 us) not working */
half_period = (I2C_CLK_FREQ / 1000000) >> 1;
break;
case I2C_SPEED_HIGH:
/* 3.4 Mbps (period 0.3 us) not working */
half_period = (I2C_CLK_FREQ / 3400000) >> 1;
break;
default:
LOG_TAG_ERROR("i2c", "Invalid speed value in %s\n", __func__);
return;
}
/* set an timeout which is at least 16 times of half cycle */
_i2c_hw[dev].regs->timeout.tout = half_period << 4;
/* timing for SCL (low and high time in APB clock cycles) */
_i2c_hw[dev].regs->scl_low_period.period = half_period;
_i2c_hw[dev].regs->scl_high_period.period = half_period;
/* timing for SDA (sample time after rising edge and hold time after falling edge) */
_i2c_hw[dev].regs->sda_sample.time = half_period >> 1;
_i2c_hw[dev].regs->sda_hold.time = half_period >> 1;
/* timing for START condition (START hold and repeated START setup time) */
_i2c_hw[dev].regs->scl_start_hold.time = half_period >> 1;
_i2c_hw[dev].regs->scl_rstart_setup.time = half_period >> 1;
/* timing for STOP condition (STOP hold and STOP setup time) */
_i2c_hw[dev].regs->scl_stop_hold.time = half_period >> 1;
_i2c_hw[dev].regs->scl_stop_setup.time = half_period >> 1;
/* configure open drain outputs */
_i2c_hw[dev].regs->ctr.scl_force_out = 1;
_i2c_hw[dev].regs->ctr.sda_force_out = 1;
/* sample data during high level */
_i2c_hw[dev].regs->ctr.sample_scl_level = 0;
/* enable non FIFO access and disable slave FIFO address offset */
#if I2C_FIFO_USED
_i2c_hw[dev].regs->fifo_conf.nonfifo_en = 0;
#else
_i2c_hw[dev].regs->fifo_conf.nonfifo_en = 1;
_i2c_hw[dev].regs->fifo_conf.nonfifo_rx_thres = 0;
_i2c_hw[dev].regs->fifo_conf.nonfifo_tx_thres = 0;
_i2c_hw[dev].regs->fifo_conf.rx_fifo_full_thrhd = 0;
_i2c_hw[dev].regs->fifo_conf.tx_fifo_empty_thrhd = 0;
#endif
_i2c_hw[dev].regs->fifo_conf.fifo_addr_cfg_en = 0;
/* route all I2C interrupt sources to same the CPU interrupt */
intr_matrix_set(PRO_CPU_NUM, _i2c_hw[dev].int_src, CPU_INUM_I2C);
/* set the interrupt handler and enable the interrupt */
xt_set_interrupt_handler(CPU_INUM_I2C, _i2c_intr_handler, NULL);
xt_ints_on(BIT(CPU_INUM_I2C));
i2c_release (dev);
return;
}
int i2c_acquire(i2c_t dev)
{
DEBUG ("%s\n", __func__);
CHECK_PARAM_RET (dev < i2c_bus_num, -1)
mutex_lock(&_i2c_bus[dev].lock);
_i2c_reset_hw(dev);
return 0;
}
int i2c_release(i2c_t dev)
{
DEBUG ("%s\n", __func__);
CHECK_PARAM_RET (dev < i2c_bus_num, -1)
_i2c_reset_hw (dev);
mutex_unlock(&_i2c_bus[dev].lock);
return 0;
}
#define _i2c_return_on_error(dev) \
if (_i2c_bus[dev].results & I2C_ARBITRATION_LOST_INT_ENA) { \
LOG_TAG_ERROR("i2c", "arbitration lost dev=%u\n", dev); \
_i2c_reset_hw (dev); \
__asm__ volatile ("isync"); \
return -EAGAIN; \
} \
else if (_i2c_bus[dev].results & I2C_ACK_ERR_INT_ENA) { \
LOG_TAG_ERROR("i2c", "ack error dev=%u\n", dev); \
_i2c_reset_hw (dev); \
__asm__ volatile ("isync"); \
return -EIO; \
} \
else if (_i2c_bus[dev].results & I2C_TIME_OUT_INT_ENA) { \
LOG_TAG_ERROR("i2c", "bus timeout dev=%u\n", dev); \
_i2c_reset_hw (dev); \
__asm__ volatile ("isync"); \
return -ETIMEDOUT; \
}
int i2c_read_bytes(i2c_t dev, uint16_t addr, void *data, size_t len, uint8_t flags)
{
DEBUG ("%s dev=%u addr=%02x data=%p len=%d flags=%01x\n",
__func__, dev, addr, data, len, flags);
CHECK_PARAM_RET (dev < i2c_bus_num, -EINVAL);
CHECK_PARAM_RET (len > 0, -EINVAL);
CHECK_PARAM_RET (data != NULL, -EINVAL);
/* if I2C_NOSTART is not set, START condition and ADDR is used */
if (!(flags & I2C_NOSTART)) {
/* send START condition */
_i2c_start_cmd (dev);
/* address handling */
if (flags & I2C_ADDR10) {
/* prepare 10 bit address bytes */
uint8_t addr10[2];
addr10[0] = 0xf0 | (addr & 0x0300) >> 7 | I2C_READ;
addr10[1] = addr & 0xff;
/* send ADDR with read flag */
_i2c_write_cmd (dev, addr10, 2);
}
else {
/* send ADDR with read flag */
uint8_t addr7 = (addr << 1 | I2C_READ);
_i2c_write_cmd (dev, &addr7, 1);
}
}
/* read data bytes in blocks of I2C_MAX_DATA bytes */
uint32_t off = 0;
/* if len > I2C_MAX_DATA read blocks I2C_MAX_DATA bytes at a time */
while (len > I2C_MAX_DATA) {
/* read one block of data bytes command */
_i2c_read_cmd (dev, data, I2C_MAX_DATA, false);
_i2c_end_cmd (dev);
_i2c_transfer (dev);
_i2c_return_on_error (dev);
/* if transfer was successful, fetch the data from I2C RAM */
for (unsigned i = 0; i < I2C_MAX_DATA; i++) {
#if I2C_FIFO_USED
((uint8_t*)data)[i + off] = _i2c_hw[dev].regs->fifo_data.data;
#else
((uint8_t*)data)[i + off] = _i2c_hw[dev].regs->ram_data[i];
#endif
}
len -= I2C_MAX_DATA;
off += I2C_MAX_DATA;
}
/* read remaining data bytes command with a final NAK */
_i2c_read_cmd (dev, data, len, true);
/* if I2C_NOSTOP flag is not set, send STOP condition is used */
if (!(flags & I2C_NOSTOP)) {
/* send STOP condition */
_i2c_stop_cmd (dev);
}
else {
/* otherwise place end command in pipeline */
_i2c_end_cmd (dev);
}
/* finish operation by executing the command pipeline */
_i2c_transfer (dev);
_i2c_return_on_error (dev);
/* if transfer was successful, fetch data from I2C RAM */
for (unsigned i = 0; i < len; i++) {
#if I2C_FIFO_USED
((uint8_t*)data)[i + off] = _i2c_hw[dev].regs->fifo_data.data;
#else
((uint8_t*)data)[i + off] = _i2c_hw[dev].regs->ram_data[i];
#endif
}
/* return 0 on success */
return 0;
}
int i2c_write_bytes(i2c_t dev, uint16_t addr, const void *data, size_t len, uint8_t flags)
{
DEBUG ("%s dev=%u addr=%02x data=%p len=%d flags=%01x\n",
__func__, dev, addr, data, len, flags);
CHECK_PARAM_RET (dev < i2c_bus_num, -EINVAL);
CHECK_PARAM_RET (len > 0, -EINVAL);
CHECK_PARAM_RET (data != NULL, -EINVAL);
/* if I2C_NOSTART is not set, START condition and ADDR is used */
if (!(flags & I2C_NOSTART)) {
/* send START condition */
_i2c_start_cmd (dev);
/* address handling */
if (flags & I2C_ADDR10) {
/* prepare 10 bit address bytes */
uint8_t addr10[2];
addr10[0] = 0xf0 | (addr & 0x0300) >> 7;
addr10[1] = addr & 0xff;
/* send ADDR without read flag */
_i2c_write_cmd (dev, addr10, 2);
}
else {
/* send ADDR without read flag */
uint8_t addr7 = addr << 1;
_i2c_write_cmd (dev, &addr7, 1);
}
}
/* send data bytes in blocks of I2C_MAX_DATA bytes */
uint32_t off = 0;
/* if len > I2C_MAX_DATA write blocks I2C_MAX_DATA bytes at a time */
while (len > I2C_MAX_DATA) {
/* send on block of data bytes */
_i2c_write_cmd (dev, ((uint8_t*)data) + off, I2C_MAX_DATA);
_i2c_end_cmd (dev);
_i2c_transfer (dev);
_i2c_return_on_error (dev);
len -= I2C_MAX_DATA;
off += I2C_MAX_DATA;
}
/* write remaining data bytes command */
_i2c_write_cmd (dev, ((uint8_t*)data), len);
/* if I2C_NOSTOP flag is not set, send STOP condition is used */
if (!(flags & I2C_NOSTOP)) {
/* send STOP condition */
_i2c_stop_cmd (dev);
}
else {
/* otherwise place end command in pipeline */
_i2c_end_cmd (dev);
}
/* finish operation by executing the command pipeline */
_i2c_transfer (dev);
_i2c_return_on_error (dev);
/* return 0 on success */
return 0;
}
/* internal functions */
static int _i2c_init_pins(i2c_t dev)
{
/*
* reset GPIO usage type if the pins were used already for I2C before to
* make it possible to reinitialize I2C
*/
if (gpio_get_pin_usage(_i2c_hw[dev].pin_scl) == _I2C) {
gpio_set_pin_usage(_i2c_hw[dev].pin_scl, _GPIO);
}
if (gpio_get_pin_usage(_i2c_hw[dev].pin_sda) == _I2C) {
gpio_set_pin_usage(_i2c_hw[dev].pin_sda, _GPIO);
}
/* try to configure SDA and SCL pin as GPIO in open-drain mode with enabled pull-ups */
if (gpio_init (_i2c_hw[dev].pin_scl, GPIO_IN_OD_PU) ||
gpio_init (_i2c_hw[dev].pin_sda, GPIO_IN_OD_PU)) {
return -ENODEV;
}
/* bring signals to high */
gpio_set(_i2c_hw[dev].pin_scl);
gpio_set(_i2c_hw[dev].pin_sda);
/* store the usage type in GPIO table */
gpio_set_pin_usage(_i2c_hw[dev].pin_scl, _I2C);
gpio_set_pin_usage(_i2c_hw[dev].pin_sda, _I2C);
/* connect SCL and SDA pins to output signals through the GPIO matrix */
GPIO.func_out_sel_cfg[_i2c_hw[dev].pin_scl].func_sel = _i2c_hw[dev].signal_scl_out;
GPIO.func_out_sel_cfg[_i2c_hw[dev].pin_sda].func_sel = _i2c_hw[dev].signal_sda_out;
/* connect SCL and SDA input signals to pins through the GPIO matrix */
GPIO.func_in_sel_cfg[_i2c_hw[dev].signal_scl_in].sig_in_sel = 1;
GPIO.func_in_sel_cfg[_i2c_hw[dev].signal_scl_in].sig_in_inv = 0;
GPIO.func_in_sel_cfg[_i2c_hw[dev].signal_scl_in].func_sel = _i2c_hw[dev].pin_scl;
GPIO.func_in_sel_cfg[_i2c_hw[dev].signal_sda_in].sig_in_sel = 1;
GPIO.func_in_sel_cfg[_i2c_hw[dev].signal_sda_in].sig_in_inv = 0;
GPIO.func_in_sel_cfg[_i2c_hw[dev].signal_sda_in].func_sel = _i2c_hw[dev].pin_sda;
return 0;
}
static void _i2c_start_cmd(i2c_t dev)
{
DEBUG ("%s\n", __func__);
/* place START condition command in command queue */
_i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].val = 0;
_i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].op_code = I2C_CMD_RSTART;
/* increment the command counter */
_i2c_bus[dev].cmd++;
}
static void _i2c_stop_cmd (i2c_t dev)
{
DEBUG ("%s\n", __func__);
/* place STOP condition command in command queue */
_i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].val = 0;
_i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].op_code = I2C_CMD_STOP;
/* increment the command counter */
_i2c_bus[dev].cmd++;
}
static void _i2c_end_cmd (i2c_t dev)
{
DEBUG ("%s\n", __func__);
/* place END command for continues data transmission in command queue */
_i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].val = 0;
_i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].op_code = I2C_CMD_END;
/* increment the command counter */
_i2c_bus[dev].cmd++;
}
static void _i2c_write_cmd (i2c_t dev, const uint8_t* data, uint8_t len)
{
DEBUG ("%s dev=%u data=%p len=%d\n", __func__, dev, data, len);
if (_i2c_bus[dev].data + len > I2C_MAX_DATA) {
LOG_TAG_ERROR("i2c", "Maximum number of bytes (32 bytes) that can be "
"sent with on transfer reached\n");
return;
}
/* store the byte in RAM of I2C controller and increment the data counter */
for (int i = 0; i < len; i++) {
#if I2C_FIFO_USED
WRITE_PERI_REG(I2C_DATA_APB_REG(dev), data[i]);
#else
_i2c_hw[dev].regs->ram_data[_i2c_bus[dev].data++] = (uint32_t)data[i];
#endif
}
/* place WRITE command for multiple bytes in command queue */
_i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].val = 0;
_i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].byte_num = len;
_i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].ack_en = 1;
_i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].ack_exp = 0;
_i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].ack_val = 0;
_i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].op_code = I2C_CMD_WRITE;
/* increment the command counter */
_i2c_bus[dev].cmd++;
}
static void _i2c_read_cmd (i2c_t dev, uint8_t* data, uint8_t len, bool last)
{
DEBUG ("%s dev=%u data=%p len=%d\n", __func__, dev, data, len);
if (len < 1 || len > I2C_MAX_DATA) {
/* at least one byte has to be read */
LOG_TAG_ERROR("i2c", "At least one byte has to be read\n");
return;
}
if (len > 1)
{
/* place READ command for len-1 bytes with positive ack in command queue*/
_i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].val = 0;
_i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].byte_num = len-1;
_i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].ack_en = 0;
_i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].ack_exp = 0;
_i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].ack_val = 0;
_i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].op_code = I2C_CMD_READ;
/* increment the command counter */
_i2c_bus[dev].cmd++;
}
/* place READ command for last byte with negative ack in last segment in command queue*/
_i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].val = 0;
_i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].byte_num = 1;
_i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].ack_en = 0;
_i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].ack_exp = 0;
_i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].ack_val = last ? 1 : 0;
_i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].op_code = I2C_CMD_READ;
/* increment the command counter */
_i2c_bus[dev].cmd++;
}
static inline void _i2c_delay (uint32_t cycles)
{
/* produces a delay of 0,0625 us per cycle for -O2 compile option */
/* 1 us = ca. 16 cycles (80 MHz) / 1 us = 32 cycles (160 MHz) */
if (cycles) {
__asm__ volatile ("1: _addi.n %0, %0, -1 \n"
" bnez %0, 1b \n" : "=r" (cycles) : "0" (cycles));
}
}
/* transfer related interrupts handled by the driver */
static const uint32_t transfer_int_mask = I2C_TRANS_COMPLETE_INT_ENA
| I2C_END_DETECT_INT_ENA
| I2C_ACK_ERR_INT_ENA
| I2C_ARBITRATION_LOST_INT_ENA
| I2C_TIME_OUT_INT_ENA;
/* at I2C_SPEED_NORMAL a transfer takes at most 33 byte * 9 clock cycles * 1/100000 s */
#define I2C_TRANSFER_TIMEOUT 3000
#define I2C_THREAD_FLAG BIT (0)
#include "xtimer.h"
void _i2c_transfer_timeout (void *arg)
{
i2c_t dev = (i2c_t)arg;
/* reset the hardware if it I2C got stucked */
_i2c_reset_hw(dev);
/* set result to timeout */
_i2c_bus[dev].results |= I2C_TIME_OUT_INT_ST;
/* wake up the thread that is waiting for the results */
thread_flags_set((thread_t*)thread_get(_i2c_bus[dev].pid), I2C_THREAD_FLAG);
}
/* Transfer of commands in I2C controller command pipeline */
static void _i2c_transfer (i2c_t dev)
{
DEBUG("%s cmd=%d\n", __func__, _i2c_bus[dev].cmd);
#if FIFO_USED
/* reset RX FIFO queue */
_i2c_hw[dev].regs->fifo_conf.rx_fifo_rst = 1;
_i2c_hw[dev].regs->fifo_conf.rx_fifo_rst = 0;
#endif
/* disable and enable all transmission interrupts and clear current status */
_i2c_hw[dev].regs->int_ena.val &= ~transfer_int_mask;
_i2c_hw[dev].regs->int_ena.val |= transfer_int_mask;
_i2c_hw[dev].regs->int_clr.val = transfer_int_mask;
/* set a timer for the case the I2C hardware gets stuck */
xtimer_t i2c_timeout = {};
i2c_timeout.callback = _i2c_transfer_timeout;
i2c_timeout.arg = (void*)dev;
xtimer_set(&i2c_timeout, I2C_TRANSFER_TIMEOUT);
/* start execution of commands in command pipeline registers */
_i2c_bus[dev].pid = thread_getpid();
_i2c_bus[dev].results = 0;
_i2c_hw[dev].regs->ctr.trans_start = 0;
_i2c_hw[dev].regs->ctr.trans_start = 1;
/* wait for transfer results and remove timeout timer*/
thread_flags_wait_one(I2C_THREAD_FLAG);
xtimer_remove(&i2c_timeout);
/* returned from transmission */
DEBUG("%s results=%08x\n", __func__, _i2c_bus[dev].results);
#if FIFO_USED
/* reset TX FIFO queue */
_i2c_hw[dev].regs->fifo_conf.tx_fifo_rst = 1;
_i2c_hw[dev].regs->fifo_conf.tx_fifo_rst = 0;
#endif
/* reset command and data index */
_i2c_bus[dev].cmd = 0;
_i2c_bus[dev].data = 0;
}
static void IRAM_ATTR _i2c_intr_handler (void *arg)
{
/* to satisfy the compiler */
(void)arg;
irq_isr_enter ();
/* all I2C peripheral interrupt sources are routed to the same interrupt,
so we have to use the status register to distinguish interruptees */
for (unsigned dev = 0; dev < i2c_bus_num; dev++) {
/* test for transfer related interrupts */
if (_i2c_hw[dev].regs->int_status.val & transfer_int_mask) {
/* set transfer result */
_i2c_bus[dev].results |= _i2c_hw[dev].regs->int_status.val;
/* disable all interrupts and clear them and left them disabled */
_i2c_hw[dev].regs->int_ena.val &= ~transfer_int_mask;
_i2c_hw[dev].regs->int_clr.val = transfer_int_mask;
/* wake up the thread that is waiting for the results */
thread_flags_set((thread_t*)thread_get(_i2c_bus[dev].pid), I2C_THREAD_FLAG);
}
else if (_i2c_hw[dev].regs->int_status.val) {
/* if there are any other interrupts, clear them */
_i2c_hw[dev].regs->int_clr.val = ~0x0U;
}
}
irq_isr_exit ();
}
#if 1 /* TODO */
/* Some slave devices will die by accident and keep the SDA in low level,
* in this case, master should send several clock to make the slave release
* the bus.
*/
static void _i2c_clear_bus(i2c_t dev)
{
/* reset the usage type in GPIO table */
gpio_set_pin_usage(_i2c_hw[dev].pin_scl, _GPIO);
gpio_set_pin_usage(_i2c_hw[dev].pin_sda, _GPIO);
/* configure SDA and SCL pin as GPIO in open-drain mode temporarily */
gpio_init (_i2c_hw[dev].pin_scl, GPIO_IN_OD_PU);
gpio_init (_i2c_hw[dev].pin_sda, GPIO_IN_OD_PU);
/* master send some clock pulses to make the slave release the bus */
gpio_set (_i2c_hw[dev].pin_scl);
gpio_set (_i2c_hw[dev].pin_sda);
gpio_clear (_i2c_hw[dev].pin_sda);
for (int i = 0; i < 20; i++) {
gpio_toggle(_i2c_hw[dev].pin_scl);
}
gpio_set(_i2c_hw[dev].pin_sda);
/* store the usage type in GPIO table */
gpio_set_pin_usage(_i2c_hw[dev].pin_scl, _I2C);
gpio_set_pin_usage(_i2c_hw[dev].pin_sda, _I2C);
/* connect SCL and SDA pins to output signals through the GPIO matrix */
GPIO.func_out_sel_cfg[_i2c_hw[dev].pin_scl].func_sel = _i2c_hw[dev].signal_scl_out;
GPIO.func_out_sel_cfg[_i2c_hw[dev].pin_sda].func_sel = _i2c_hw[dev].signal_sda_out;
/* connect SCL and SDA input signals to pins through the GPIO matrix */
GPIO.func_in_sel_cfg[_i2c_hw[dev].signal_scl_in].sig_in_sel = 1;
GPIO.func_in_sel_cfg[_i2c_hw[dev].signal_scl_in].sig_in_inv = 0;
GPIO.func_in_sel_cfg[_i2c_hw[dev].signal_scl_in].func_sel = _i2c_hw[dev].pin_scl;
GPIO.func_in_sel_cfg[_i2c_hw[dev].signal_sda_in].sig_in_sel = 1;
GPIO.func_in_sel_cfg[_i2c_hw[dev].signal_sda_in].sig_in_inv = 0;
GPIO.func_in_sel_cfg[_i2c_hw[dev].signal_sda_in].func_sel = _i2c_hw[dev].pin_sda;
return;
}
#endif
/*
* PLEASE NOTE: Following function is from the ESP-IDF and is licensed
* under the Apache License, Version 2.0 (the "License").
* http://www.apache.org/licenses/LICENSE-2.0
*
* Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
*/
static void _i2c_reset_hw (i2c_t dev)
{
/* save current configuration */
uint32_t ctr = _i2c_hw[dev].regs->ctr.val;
uint32_t fifo_conf = _i2c_hw[dev].regs->fifo_conf.val;
uint32_t scl_low_period = _i2c_hw[dev].regs->scl_low_period.val;
uint32_t scl_high_period = _i2c_hw[dev].regs->scl_high_period.val;
uint32_t scl_start_hold = _i2c_hw[dev].regs->scl_start_hold.val;
uint32_t scl_rstart_setup = _i2c_hw[dev].regs->scl_rstart_setup.val;
uint32_t scl_stop_hold = _i2c_hw[dev].regs->scl_stop_hold.val;
uint32_t scl_stop_setup = _i2c_hw[dev].regs->scl_stop_setup.val;
uint32_t sda_hold = _i2c_hw[dev].regs->sda_hold.val;
uint32_t sda_sample = _i2c_hw[dev].regs->sda_sample.val;
uint32_t timeout = _i2c_hw[dev].regs->timeout.val;
uint32_t scl_filter_cfg = _i2c_hw[dev].regs->scl_filter_cfg.val;
uint32_t sda_filter_cfg = _i2c_hw[dev].regs->sda_filter_cfg.val;
/* reset hardware mpdule */
periph_module_disable(_i2c_hw[dev].mod);
_i2c_clear_bus(dev);
periph_module_enable(_i2c_hw[dev].mod);
/* restore configuration */
_i2c_hw[dev].regs->int_ena.val = 0;
_i2c_hw[dev].regs->ctr.val = ctr & (~I2C_TRANS_START_M);
_i2c_hw[dev].regs->fifo_conf.val = fifo_conf;
_i2c_hw[dev].regs->scl_low_period.val = scl_low_period;
_i2c_hw[dev].regs->scl_high_period.val = scl_high_period;
_i2c_hw[dev].regs->scl_start_hold.val = scl_start_hold;
_i2c_hw[dev].regs->scl_rstart_setup.val = scl_rstart_setup;
_i2c_hw[dev].regs->scl_stop_hold.val = scl_stop_hold;
_i2c_hw[dev].regs->scl_stop_setup.val = scl_stop_setup;
_i2c_hw[dev].regs->sda_hold.val = sda_hold;
_i2c_hw[dev].regs->sda_sample.val = sda_sample;
_i2c_hw[dev].regs->timeout.val = timeout;
_i2c_hw[dev].regs->scl_filter_cfg.val = scl_filter_cfg;
_i2c_hw[dev].regs->sda_filter_cfg.val = sda_filter_cfg;
/* disable and clear all interrupt sources */
_i2c_hw[dev].regs->int_ena.val = 0;
_i2c_hw[dev].regs->int_clr.val = ~0x0U;
return;
}
void i2c_print_config(void)
{
for (unsigned bus = 0; bus < i2c_bus_num; bus++) {
ets_printf("\tI2C_DEV(%d)\tscl=%d sda=%d\n",
bus, _i2c_hw[bus].pin_scl, _i2c_hw[bus].pin_sda);
}
}
#else /* defined(I2C0_SPEED) || defined(I2C1_SPEED) */
void i2c_print_config(void)
{
LOG_TAG_INFO("i2c", "no I2C devices\n");
}
#endif /* defined(I2C0_SPEED) || defined(I2C1_SPEED) */
#endif /* MODULE_ESP_I2C_HW */

742
cpu/esp32/periph/i2c_sw.c Normal file
View File

@ -0,0 +1,742 @@
/*
* 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_esp32
* @ingroup drivers_periph_i2c
* @{
*
* @file
* @brief Low-level I2C driver implementation for ESP32 SDK
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
/*
PLEASE NOTE:
Some parts of the implementation bases on the bit-banging implementation as
described in [wikipedia](https://en.wikipedia.org/wiki/I%C2%B2C) as well as
its implementation in [esp-open-rtos](https://github.com/SuperHouse/esp-open-rtos.git).
These parts are under the copyright of their respective owners.
*/
#if defined(MODULE_ESP_I2C_SW) /* software implementation used */
#define ENABLE_DEBUG (0)
#include "debug.h"
#include <errno.h>
#include <stdbool.h>
#include "cpu.h"
#include "log.h"
#include "mutex.h"
#include "periph_conf.h"
#include "periph/gpio.h"
#include "periph/i2c.h"
#include "esp_common.h"
#include "gpio_arch.h"
#include "rom/ets_sys.h"
#include "soc/gpio_reg.h"
#include "soc/gpio_struct.h"
/* only include the code if one of the IC2 interface bus speeds are defined */
#if defined(I2C0_SPEED) || defined(I2C1_SPEED)
/* 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))
typedef struct
{
i2c_speed_t speed;
i2c_t dev;
bool started;
gpio_t scl;
gpio_t sda;
uint32_t scl_bit; /* gpio bit mask for faster access */
uint32_t sda_bit; /* gpio bit mask for faster access */
uint32_t delay;
mutex_t lock;
} _i2c_bus_t;
static _i2c_bus_t _i2c_bus[] =
{
#if defined(I2C0_SCL) && defined(I2C0_SDA) && defined(I2C0_SPEED)
{
.speed = I2C0_SPEED,
.sda = I2C0_SDA,
.scl = I2C0_SCL,
.lock = MUTEX_INIT
},
#endif
#if defined(I2C1_SCL) && defined(I2C1_SDA) && defined(I2C1_SPEED)
{
.speed = I2C1_SPEED,
.sda = I2C1_SDA,
.scl = I2C1_SCL,
.lock = MUTEX_INIT
},
#endif
};
/* the number of I2C bus devices used */
const unsigned i2c_bus_num = sizeof(_i2c_bus) / sizeof(_i2c_bus[0]);
/* to ensure that I2C is always optimized with -O2 to use the defined delays */
#pragma GCC optimize ("O2")
static const uint32_t _i2c_delays[][3] =
{
#if 1
/* 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 = 48 cycles (240) / 32 cycles (160 MHz) / 16 cycles (80 MHz) */
/* values for 240, 160, 80 MHz */
[I2C_SPEED_LOW] = {2380, 1590, 785}, /* 10 kbps (period 100 us) */
[I2C_SPEED_NORMAL] = { 220, 148, 68}, /* 100 kbps (period 10 us) */
[I2C_SPEED_FAST] = { 40, 25, 8}, /* 400 kbps (period 2.5 us) */
[I2C_SPEED_FAST_PLUS] = { 0, 0, 0}, /* 1 Mbps (period 1 us) */
[I2C_SPEED_HIGH] = { 0, 0, 0} /* 3.4 Mbps (period 0.3 us) not working */
#else
/* values specify one half-period and are only valid for -Os option */
/* values for 240, 160, 80 MHz */
[I2C_SPEED_LOW] = {2375, 1585, 780}, /* 10 kbps (period 100 us) */
[I2C_SPEED_NORMAL] = { 220, 137, 62}, /* 100 kbps (period 10 us) */
[I2C_SPEED_FAST] = { 38, 20, 0}, /* 400 kbps (period 2.5 us) */
[I2C_SPEED_FAST_PLUS] = { 0, 0, 0}, /* 1 Mbps (period 1 us) */
[I2C_SPEED_HIGH] = { 0, 0, 0} /* 3.4 Mbps (period 0.3 us) not working */
#endif
};
/* forward declaration of internal functions */
static inline void _i2c_delay (_i2c_bus_t* bus);
static inline bool _i2c_scl_read (_i2c_bus_t* bus);
static inline bool _i2c_sda_read (_i2c_bus_t* bus);
static inline void _i2c_scl_high (_i2c_bus_t* bus);
static inline void _i2c_scl_low (_i2c_bus_t* bus);
static inline void _i2c_sda_high (_i2c_bus_t* bus);
static inline void _i2c_sda_low (_i2c_bus_t* bus);
static int _i2c_start_cond (_i2c_bus_t* bus);
static int _i2c_stop_cond (_i2c_bus_t* bus);
static int _i2c_write_bit (_i2c_bus_t* bus, bool bit);
static int _i2c_read_bit (_i2c_bus_t* bus, bool* bit);
static int _i2c_write_byte (_i2c_bus_t* bus, uint8_t byte);
static int _i2c_read_byte (_i2c_bus_t* bus, uint8_t* byte, bool ack);
static int _i2c_arbitration_lost (_i2c_bus_t* bus, const char* func);
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)
{
CHECK_PARAM (dev < i2c_bus_num)
if (_i2c_bus[dev].speed == I2C_SPEED_HIGH) {
LOG_TAG_INFO("i2c", "I2C_SPEED_HIGH is not supported\n");
return;
}
_i2c_bus[dev].dev = dev;
_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 */
switch (ets_get_cpu_frequency()) {
case 240: _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;
case 80: _i2c_bus[dev].delay = _i2c_delays[_i2c_bus[dev].speed][2]; break;
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);
}
/* try to configure SDA and SCL pin as GPIO in open-drain mode with enabled pull-ups */
if (gpio_init (_i2c_bus[dev].scl, GPIO_IN_OD_PU) ||
gpio_init (_i2c_bus[dev].sda, GPIO_IN_OD_PU)) {
return;
}
/* 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]);
_i2c_scl_high (&_i2c_bus[dev]);
/* clear the bus if necessary (SDA is driven permanently low) */
_i2c_clear (&_i2c_bus[dev]);
return;
}
int i2c_acquire(i2c_t dev)
{
CHECK_PARAM_RET (dev < i2c_bus_num, -1)
mutex_lock(&_i2c_bus[dev].lock);
return 0;
}
int i2c_release(i2c_t dev)
{
CHECK_PARAM_RET (dev < i2c_bus_num, -1)
mutex_unlock(&_i2c_bus[dev].lock);
return 0;
}
int /* IRAM */ i2c_read_bytes(i2c_t dev, uint16_t addr, void *data, size_t len, uint8_t flags)
{
DEBUG ("%s: dev=%u addr=%02x data=%p len=%d flags=%01x\n",
__func__, dev, addr, data, len, flags);
CHECK_PARAM_RET (dev < i2c_bus_num, -EINVAL);
CHECK_PARAM_RET (len > 0, -EINVAL);
CHECK_PARAM_RET (data != NULL, -EINVAL);
_i2c_bus_t* bus = &_i2c_bus[dev];
int res = 0;
/* send START condition and address if I2C_NOSTART is not set */
if (!(flags & I2C_NOSTART)) {
/* START condition */
if ((res = _i2c_start_cond (bus)) != 0) {
return res;
}
/* send 10 bit or 7 bit address */
if (flags & I2C_ADDR10) {
/* prepare 10 bit address bytes */
uint8_t addr1 = 0xf0 | (addr & 0x0300) >> 7 | I2C_READ;
uint8_t addr2 = addr & 0xff;
/* send address bytes wit read flag */
if ((res = _i2c_write_byte (bus, addr1)) != 0 ||
(res = _i2c_write_byte (bus, addr2)) != 0) {
/* abort transfer */
_i2c_abort (bus, __func__);
return res;
}
}
else {
/* send address byte with read flag */
if ((res = _i2c_write_byte (bus, (addr << 1 | I2C_READ))) != 0) {
/* abort transfer */
_i2c_abort (bus, __func__);
return res;
}
}
}
/* receive bytes if send address was successful */
for (unsigned int i = 0; i < len; i++) {
if ((res = _i2c_read_byte (bus, &(((uint8_t*)data)[i]), i < len-1)) != 0) {
/* abort transfer */
_i2c_abort (bus, __func__);
return res;
}
}
/* send STOP condition if I2C_NOSTOP flag is not set */
if (!(flags & I2C_NOSTOP)) {
_i2c_stop_cond (bus);
}
return res;
}
int /* IRAM */ i2c_write_bytes(i2c_t dev, uint16_t addr, const void *data, size_t len, uint8_t flags)
{
DEBUG ("%s: dev=%u addr=%02x data=%p len=%d flags=%01x\n",
__func__, dev, addr, data, len, flags);
CHECK_PARAM_RET (dev < i2c_bus_num, -EINVAL);
CHECK_PARAM_RET (len > 0, -EINVAL);
CHECK_PARAM_RET (data != NULL, -EINVAL);
_i2c_bus_t* bus = &_i2c_bus[dev];
int res = 0;
/* if I2C_NOSTART is not set, send START condition and ADDR */
if (!(flags & I2C_NOSTART)) {
/* START condition */
if ((res = _i2c_start_cond (bus)) != 0) {
return res;
}
/* send 10 bit or 7 bit address */
if (flags & I2C_ADDR10) {
/* prepare 10 bit address bytes */
uint8_t addr1 = 0xf0 | (addr & 0x0300) >> 7;
uint8_t addr2 = addr & 0xff;
/* send address bytes without read flag */
if ((res = _i2c_write_byte (bus, addr1)) != 0 ||
(res = _i2c_write_byte (bus, addr2)) != 0) {
/* abort transfer */
_i2c_abort (bus, __func__);
return res;
}
}
else {
/* send address byte without read flag */
if ((res = _i2c_write_byte (bus, addr << 1)) != 0) {
/* abort transfer */
_i2c_abort (bus, __func__);
return res;
}
}
}
/* send bytes if send address was successful */
for (unsigned int i = 0; i < len; i++) {
if ((res = _i2c_write_byte (bus, ((uint8_t*)data)[i])) != 0) {
/* abort transfer */
_i2c_abort (bus, __func__);
return res;
}
}
/* send STOP condition if I2C_NOSTOP flag is not set */
if (!(flags & I2C_NOSTOP)) {
return _i2c_stop_cond (bus);
}
return res;
}
void i2c_poweron(i2c_t dev)
{
/* since I2C is realized in software there is no device to power on */
/* just return */
}
void i2c_poweroff(i2c_t dev)
{
/* since I2C is realized in software there is no device to power off */
/* just return */
}
/* --- internal functions --- */
static inline void _i2c_delay (_i2c_bus_t* bus)
{
/* produces a delay */
/* ca. 16 cycles = 1 us (80 MHz) or ca. 32 cycles = 1 us (160 MHz) */
uint32_t cycles = bus->delay;
if (cycles) {
__asm__ volatile ("1: _addi.n %0, %0, -1 \n"
" bnez %0, 1b \n" : "=r" (cycles) : "0" (cycles));
}
}
/*
* Please note: SDA and SDL pins are used in GPIO_OD_PU mode
* (open-drain with pull-ups).
*
* Setting a pin which is in open-drain mode leaves the pin floating and
* the signal is pulled up to high. The signal can then be actively driven
* to low by a slave. A read operation returns the current signal at the pin.
*
* Clearing a pin which is in open-drain mode actively drives the signal to
* low.
*/
static inline bool _i2c_scl_read(_i2c_bus_t* bus)
{
/* read SCL status (pin is in open-drain mode and set) */
return GPIO_GET(in, in1, bus->scl);
}
static inline bool _i2c_sda_read(_i2c_bus_t* bus)
{
/* read SDA status (pin is in open-drain mode and set) */
return GPIO_GET(in, in1, bus->sda);
}
static inline void _i2c_scl_high(_i2c_bus_t* bus)
{
/* set SCL signal high (pin is in open-drain mode and pulled-up) */
GPIO_SET(out_w1ts, out1_w1ts, bus->scl);
}
static inline void _i2c_scl_low(_i2c_bus_t* bus)
{
/* set SCL signal low (actively driven to low) */
GPIO_SET(out_w1tc, out1_w1tc, bus->scl);
}
static inline void _i2c_sda_high(_i2c_bus_t* bus)
{
/* set SDA signal high (pin is in open-drain mode and pulled-up) */
GPIO_SET(out_w1ts, out1_w1ts, bus->sda);
}
static inline void _i2c_sda_low(_i2c_bus_t* bus)
{
/* set SDA signal low (actively driven to low) */
GPIO_SET(out_w1tc, out1_w1tc, bus->sda);
}
static void _i2c_clear(_i2c_bus_t* bus)
{
DEBUG("%s: dev=%u\n", __func__, bus->dev);
/**
* Sometimes a slave blocks and drives the SDA line permanently low.
* Send some clock pulses in that case (10 at maximum)
*/
/*
* If SDA is low while SCL is high for 10 half cycles, it is not an
* arbitration lost but a bus lock.
*/
int count = 10;
while (!_i2c_sda_read (bus) && _i2c_scl_read (bus) && count) {
count--;
_i2c_delay (bus);
}
if (count) {
/* was not a bus lock */
return;
}
/* send 10 clock pulses in case of bus lock */
count = 10;
while (!_i2c_sda_read (bus) && count--) {
_i2c_scl_low (bus);
_i2c_delay (bus);
_i2c_scl_high (bus);
_i2c_delay (bus);
}
}
static void _i2c_abort(_i2c_bus_t* bus, const char* func)
{
DEBUG("%s: dev=%u\n", func, bus->dev);
/* reset SCL and SDA to passive HIGH (floating and pulled-up) */
_i2c_sda_high (bus);
_i2c_scl_high (bus);
/* reset repeated start indicator */
bus->started = false;
/* clear the bus if necessary (SDA is driven permanently low) */
_i2c_clear(bus);
}
static /* IRAM */ int _i2c_arbitration_lost (_i2c_bus_t* bus, const char* func)
{
DEBUG("%s: arbitration lost dev=%u\n", func, bus->dev);
/* reset SCL and SDA to passive HIGH (floating and pulled-up) */
_i2c_sda_high (bus);
_i2c_scl_high (bus);
/* reset repeated start indicator */
bus->started = false;
/* clear the bus if necessary (SDA is driven permanently low) */
_i2c_clear(bus);
return -EAGAIN;
}
static /* IRAM */ int _i2c_start_cond(_i2c_bus_t* bus)
{
/*
* send start condition
* on entry: SDA and SCL are set to be floating and pulled-up to high
* on exit : SDA and SCL are actively driven to low
*/
int res = 0;
if (bus->started) {
/* prepare the repeated start condition */
/* SDA = passive HIGH (floating and pulled-up) */
_i2c_sda_high (bus);
/* t_VD;DAT not neccessary */
/* _i2c_delay (bus); */
/* SCL = passive HIGH (floating and pulled-up) */
_i2c_scl_high (bus);
/* clock stretching, wait as long as clock is driven to low by the slave */
uint32_t stretch = I2C_CLOCK_STRETCH;
while (!_i2c_scl_read (bus) && stretch--) {}
if (stretch == 0) {
DEBUG("%s: clock stretching timeout dev=%u\n", __func__, bus->dev);
res = -ETIMEDOUT;
}
/* wait t_SU;STA - set-up time for a repeated START condition */
/* min. in us: 4.7 (SM), 0.6 (FM), 0.26 (FPM), 0.16 (HSM); no max. */
_i2c_delay (bus);
}
/* if SDA is low, arbitration is lost and someone else is driving the bus */
if (!_i2c_sda_read (bus)) {
return _i2c_arbitration_lost (bus, __func__);
}
/* begin the START condition: SDA = active LOW */
_i2c_sda_low (bus);
/* wait t_HD;STA - hold time (repeated) START condition, */
/* max none */
/* min 4.0 us (SM), 0.6 us (FM), 0.26 us (FPM), 0.16 us (HSM) */
_i2c_delay (bus);
/* complete the START condition: SCL = active LOW */
_i2c_scl_low (bus);
/* needed for repeated start condition */
bus->started = true;
return res;
}
static /* IRAM */ int _i2c_stop_cond(_i2c_bus_t* bus)
{
/*
* send stop condition
* on entry: SCL is active low and SDA can be changed
* on exit : SCL and SDA are set to be floating and pulled-up to high
*/
int res = 0;
/* begin the STOP condition: SDA = active LOW */
_i2c_sda_low (bus);
/* wait t_LOW - LOW period of SCL clock */
/* min. in us: 4.7 (SM), 1.3 (FM), 0.5 (FPM), 0.16 (HSM); no max. */
_i2c_delay (bus);
/* SCL = passive HIGH (floating and pulled up) while SDA = active LOW */
_i2c_scl_high (bus);
/* clock stretching, wait as long as clock is driven to low by the slave */
uint32_t stretch = I2C_CLOCK_STRETCH;
while (!_i2c_scl_read (bus) && stretch--) {}
if (stretch == 0) {
DEBUG("%s: clock stretching timeout dev=%u\n", __func__, bus->dev);
res = -ETIMEDOUT;
}
/* wait t_SU;STO - hold time START condition, */
/* min. in us: 4.0 (SM), 0.6 (FM), 0.26 (FPM), 0.16 (HSM); no max. */
_i2c_delay (bus);
/* complete the STOP condition: SDA = passive HIGH (floating and pulled up) */
_i2c_sda_high (bus);
/* reset repeated start indicator */
bus->started = false;
/* wait t_BUF - bus free time between a STOP and a START condition */
/* min. in us: 4.7 (SM), 1.3 (FM), 0.5 (FPM), 0.16 (HSM); no max. */
_i2c_delay (bus);
/* one additional delay */
_i2c_delay (bus);
/* if SDA is low, arbitration is lost and someone else is driving the bus */
if (_i2c_sda_read (bus) == 0) {
return _i2c_arbitration_lost (bus, __func__);
}
return res;
}
static /* IRAM */ int _i2c_write_bit (_i2c_bus_t* bus, bool bit)
{
/*
* send one bit
* on entry: SCL is active low, SDA can be changed
* on exit : SCL is active low, SDA can be changed
*/
int res = 0;
/* SDA = bit */
if (bit) {
_i2c_sda_high (bus);
}
else {
_i2c_sda_low (bus);
}
/* wait t_VD;DAT - data valid time (time until data are valid) */
/* max. in us: 3.45 (SM), 0.9 (FM), 0.45 (FPM); no min */
_i2c_delay (bus);
/* SCL = passive HIGH (floating and pulled-up), SDA value is available */
_i2c_scl_high (bus);
/* wait t_HIGH - time for the slave to read SDA */
/* min. in us: 4 (SM), 0.6 (FM), 0.26 (FPM), 0.09 (HSM); no max. */
_i2c_delay (bus);
/* clock stretching, wait as long as clock is driven low by the slave */
uint32_t stretch = I2C_CLOCK_STRETCH;
while (!_i2c_scl_read (bus) && stretch--) {}
if (stretch == 0) {
DEBUG("%s: clock stretching timeout dev=%u\n", __func__, bus->dev);
res = -ETIMEDOUT;
}
/* if SCL is high, now data is valid */
/* if SDA is high, check that nobody else is driving SDA low */
if (bit && !_i2c_sda_read(bus)) {
return _i2c_arbitration_lost (bus, __func__);
}
/* SCL = active LOW to allow next SDA change */
_i2c_scl_low(bus);
return res;
}
static /* IRAM */ int _i2c_read_bit (_i2c_bus_t* bus, bool* bit)
{
/* read one bit
* on entry: SCL is active low, SDA can be changed
* on exit : SCL is active low, SDA can be changed
*/
int res = 0;
/* SDA = passive HIGH (floating and pulled-up) to let the slave drive data */
_i2c_sda_high (bus);
/* wait t_VD;DAT - data valid time (time until data are valid) */
/* max. in us: 3.45 (SM), 0.9 (FM), 0.45 (FPM); no min */
_i2c_delay (bus);
/* SCL = passive HIGH (floating and pulled-up), SDA value is available */
_i2c_scl_high (bus);
/* clock stretching, wait as long as clock is driven to low by the slave */
uint32_t stretch = I2C_CLOCK_STRETCH;
while (!_i2c_scl_read (bus) && stretch--) {}
if (stretch == 0) {
DEBUG("%s: clock stretching timeout dev=%u\n", __func__, bus->dev);
res = -ETIMEDOUT;
}
/* wait t_HIGH - time for the slave to read SDA */
/* min. in us: 4 (SM), 0.6 (FM), 0.26 (FPM), 0.09 (HSM); no max. */
_i2c_delay (bus);
/* SCL is high, read out bit */
*bit = _i2c_sda_read (bus);
/* SCL = active LOW to allow next SDA change */
_i2c_scl_low(bus);
return res;
}
static /* IRAM */ int _i2c_write_byte (_i2c_bus_t* bus, uint8_t byte)
{
/* send one byte and returns 0 in case of ACK from slave */
/* send the byte from MSB to LSB */
for (unsigned i = 0; i < 8; i++) {
int res = _i2c_write_bit(bus, (byte & 0x80) != 0);
if (res != 0) {
return res;
}
byte = byte << 1;
}
/* read acknowledge bit (low) from slave */
bool bit;
int res = _i2c_read_bit (bus, &bit);
if (res != 0) {
return res;
}
return !bit ? 0 : -EIO;
}
static /* IRAM */ int _i2c_read_byte(_i2c_bus_t* bus, uint8_t *byte, bool ack)
{
bool bit;
/* read the byte */
for (unsigned i = 0; i < 8; i++) {
int res = _i2c_read_bit (bus, &bit);
if (res != 0) {
return res;
}
*byte = (*byte << 1) | bit;
}
/* write acknowledgement flag */
_i2c_write_bit(bus, !ack);
return 0;
}
void i2c_print_config(void)
{
for (unsigned bus = 0; bus < i2c_bus_num; bus++) {
ets_printf("\tI2C_DEV(%d)\tscl=%d sda=%d\n",
bus, _i2c_bus[bus].scl, _i2c_bus[bus].sda);
}
}
#else /* defined(I2C0_SPEED) || defined(I2C1_SPEED) */
void i2c_print_config(void)
{
LOG_TAG_INFO("i2c", "no I2C devices\n");
}
#endif /* defined(I2C0_SPEED) || defined(I2C1_SPEED) */
#endif /* MODULE_ESP_I2C_SW */

105
cpu/esp32/periph/pm.c Normal file
View File

@ -0,0 +1,105 @@
/*
* 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_esp32
* @ingroup drivers_periph_pm
* @{
*
* @file
* @brief Implementation of power management functions
*
* @author Gunar Schorcht <gunar@schorcht.net>
* @}
*/
#define ENABLE_DEBUG (0)
#include "debug.h"
#include "esp_attr.h"
#include "syscalls.h"
#include "rom/rtc.h"
#include "rom/uart.h"
#include "soc/rtc.h"
#include "soc/rtc_cntl_reg.h"
void pm_set_lowest(void)
{
DEBUG ("%s enter to sleep @%u\n", __func__, system_get_time());
#if !defined(QEMU)
/* passive wait for interrupt to leave lowest power mode */
__asm__ volatile ("waiti 0");
/* reset system watchdog timer */
system_wdt_feed();
#endif
DEBUG ("%s exit from sleep @%u\n", __func__, system_get_time());
}
void IRAM_ATTR pm_off(void)
{
DEBUG ("%s\n", __func__);
/* suspend UARTs */
for (int i = 0; i < 3; ++i) {
REG_SET_BIT(UART_FLOW_CONF_REG(i), UART_FORCE_XOFF);
uart_tx_wait_idle(i);
}
/* set all power down flags */
uint32_t pd_flags = RTC_SLEEP_PD_DIG |
RTC_SLEEP_PD_RTC_PERIPH |
RTC_SLEEP_PD_RTC_SLOW_MEM |
RTC_SLEEP_PD_RTC_FAST_MEM |
RTC_SLEEP_PD_RTC_MEM_FOLLOW_CPU |
RTC_SLEEP_PD_VDDSDIO;
rtc_sleep_config_t config = RTC_SLEEP_CONFIG_DEFAULT(pd_flags);
config.wifi_pd_en = 1;
config.rom_mem_pd_en = 1;
config.lslp_meminf_pd = 1;
/* Save current frequency and switch to XTAL */
rtc_cpu_freq_t cpu_freq = rtc_clk_cpu_freq_get();
rtc_clk_cpu_freq_set(RTC_CPU_FREQ_XTAL);
/* set deep sleep duration to forever */
rtc_sleep_set_wakeup_time(rtc_time_get() + ~0x0UL);
/* configure deep sleep */
rtc_sleep_init(config);
rtc_sleep_start(RTC_TIMER_TRIG_EN, 0);
/* Restore CPU frequency */
rtc_clk_cpu_freq_set(cpu_freq);
/* resume UARTs */
for (int i = 0; i < 3; ++i) {
REG_CLR_BIT(UART_FLOW_CONF_REG(i), UART_FORCE_XOFF);
REG_SET_BIT(UART_FLOW_CONF_REG(i), UART_FORCE_XON);
REG_CLR_BIT(UART_FLOW_CONF_REG(i), UART_FORCE_XON);
}
}
extern void esp_restart_noos(void) __attribute__ ((noreturn));
void pm_reboot(void)
{
DEBUG ("%s\n", __func__);
/* suspend and flush UARTs */
for (int i = 0; i < 3; ++i) {
REG_SET_BIT(UART_FLOW_CONF_REG(i), UART_FORCE_XOFF);
uart_tx_wait_idle(i);
}
software_reset();
}

459
cpu/esp32/periph/pwm.c Normal file
View File

@ -0,0 +1,459 @@
/*
* 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_esp32
* @ingroup drivers_periph_pwm
* @{
*
* @file
* @brief Low-level PWM driver implementation
*
* @author Gunar Schorcht <gunar@schorcht.net>
* @}
*/
#define ENABLE_DEBUG 0
#include "debug.h"
#include "board.h"
#include "cpu.h"
#include "log.h"
#include "irq_arch.h"
#include "periph/pwm.h"
#include "periph/gpio.h"
#include "esp_common.h"
#include "gpio_arch.h"
#include "driver/periph_ctrl.h"
#include "rom/ets_sys.h"
#include "soc/gpio_struct.h"
#include "soc/gpio_sig_map.h"
#include "soc/mcpwm_reg.h"
#include "soc/mcpwm_struct.h"
#if defined(PWM0_GPIOS) || defined(PWM1_GPIOS)
#define PWM_NUMOF_MAX (2) /* maximum number of PWM devices */
#define PWM_CLK (160000000UL) /* base clock of PWM devices */
#define PWM_CPS_MAX (10000000UL) /* maximum cycles per second supported */
#define PWM_CPS_MIN (2500UL) /* minumum cycles per second supported */
#define PWM_TIMER_MOD_FREEZE 0 /* timer is disabled */
#define PWM_TIMER_MOD_UP 1 /* timer counts up */
#define PWM_TIMER_MOD_DOWN 2 /* timer counts down */
#define PWM_TIMER_MOD_UP_DOWN 3 /* timer counts up and then down */
#define PWM_TIMER_STOPS_AT_TEZ 0 /* PWM starts, then stops at next TEZ */
#define PWM_TIMER_STOPS_AT_TEP 1 /* PWM starts, then stops at next TEP */
#define PWM_TIMER_RUNS_ON 2 /* PWM runs on */
#define PWM_TIMER_STARTS_STOPS_AT_TEZ 3 /* PWM starts and stops at next TEZ */
#define PWM_TIMER_STARTS_STOPS_AT_TEP 4 /* PWM starts and stops at next TEP */
#define PWM_TIMER_UPDATE_IMMIDIATE 0 /* update period immediatly */
#define PWM_TIMER_UPDATE_AT_TEZ 1 /* update period at TEZ */
#define PWM_TIMER_UPDATE_AT_SYNC 2 /* update period at sync */
#define PWM_TIMER_UPDATE_AT_TEZ_SYNC 3 /* update period at TEZ and sync */
#define PWM_OP_ACTION_NO_CHANGE 0 /* do not change output */
#define PWM_OP_ACTION_LOW 1 /* set the output to high */
#define PWM_OP_ACTION_HIGH 2 /* set the output to low */
#define PWM_OP_ACTION_TOGGLE 3 /* toggle the output */
#define PWM_OP_CHANNEL_A 0 /* operator channel A */
#define PWM_OP_CHANNEL_B 0 /* operator channel B */
/* forward declaration of internal functions */
static void _pwm_start(pwm_t pwm);
static void _pwm_stop(pwm_t pwm);
static bool _pwm_configuration(void);
/* data structure for static configuration of PWM devices */
struct _pwm_hw_t {
mcpwm_dev_t* regs; /* PWM's registers set address */
uint8_t mod; /* PWM's hardware module */
uint8_t int_src; /* PWM's peripheral interrupt source */
uint32_t signal_group; /* PWM's base peripheral signal index */
uint8_t gpio_num; /* number of GPIOs used as channels outputs */
const gpio_t* gpios; /* GPIOs used as channel outputs */
};
#ifdef PWM0_GPIOS
static const gpio_t _pwm_channel_gpios_0[] = PWM0_GPIOS;
#endif
#ifdef PWM1_GPIOS
static const gpio_t _pwm_channel_gpios_1[] = PWM1_GPIOS;
#endif
/* static configuration of PWM devices */
static const struct _pwm_hw_t _pwm_hw[] =
{
#ifdef PWM0_GPIOS
{
.regs = &MCPWM0,
.mod = PERIPH_PWM0_MODULE,
.int_src = ETS_PWM0_INTR_SOURCE,
.signal_group = PWM0_OUT0A_IDX,
.gpio_num = sizeof(_pwm_channel_gpios_0) >> 2,
.gpios = _pwm_channel_gpios_0,
},
#endif
#ifdef PWM1_GPIOS
{
.regs = &MCPWM1,
.mod = PERIPH_PWM1_MODULE,
.int_src = ETS_PWM1_INTR_SOURCE,
.signal_group = PWM1_OUT0A_IDX,
.gpio_num = sizeof(_pwm_channel_gpios_1) >> 2,
.gpios = _pwm_channel_gpios_1,
},
#endif
};
/* the number of PWM devices used */
const unsigned pwm_dev_num = sizeof(_pwm_hw) / sizeof(_pwm_hw[0]);
/* data structure dynamic channel configuration */
typedef struct {
bool used;
uint32_t duty;
} _pwm_chn_t;
/* data structure for dynamic configuration of PWM devices */
struct _pwm_dev_t {
uint16_t res;
uint32_t freq;
pwm_mode_t mode;
uint8_t chn_num;
_pwm_chn_t chn[PWM_CHANNEL_NUM_DEV_MAX];
};
/* dynamic configuration of PWM devices */
static struct _pwm_dev_t _pwm_dev[PWM_NUMOF_MAX] = {};
/* if pwm_init is called first time, it checks the overall pwm configuration */
static bool _pwm_init_first_time = true;
/* Initialize PWM device */
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);
CHECK_PARAM_RET (pwm < pwm_dev_num, 0);
CHECK_PARAM_RET (freq > 0, 0);
if (_pwm_init_first_time) {
if (!_pwm_configuration())
return 0;
}
if (_pwm_hw[pwm].gpio_num == 0) {
LOG_TAG_ERROR("pwm", "PWM device %d has no assigned pins\n", pwm);
return 0;
}
/* reset by disabling and enable the PWM module */
periph_module_disable(_pwm_hw[pwm].mod);
periph_module_enable(_pwm_hw[pwm].mod);
_pwm_dev[pwm].res = res;
_pwm_dev[pwm].freq = freq;
_pwm_dev[pwm].mode = mode;
_pwm_dev[pwm].chn_num = _pwm_hw[pwm].gpio_num;
for (int i = 0; i < _pwm_dev[pwm].chn_num; i++) {
/* initialize channel data */
_pwm_dev[pwm].chn[i].used = false;
_pwm_dev[pwm].chn[i].duty = 0;
/* reset GPIO usage type if the pins were used already for PWM before
to make it possible to reinitialize PWM with new parameters */
if (gpio_get_pin_usage(_pwm_hw[pwm].gpios[i]) == _PWM) {
gpio_set_pin_usage(_pwm_hw[pwm].gpios[i], _GPIO);
}
if (gpio_get_pin_usage(_pwm_hw[pwm].gpios[i]) != _GPIO) {
LOG_TAG_ERROR("pwm", "GPIO%d is used for %s and cannot be used as PWM output\n", i,
gpio_get_pin_usage_str(_pwm_hw[pwm].gpios[i]));
return 0;
}
if (gpio_init(_pwm_hw[pwm].gpios[i], GPIO_OUT) < 0) {
return 0;
}
/* initialize the GPIO and route the PWM signal output to the GPIO */
gpio_set_pin_usage(_pwm_hw[pwm].gpios[i], _GPIO);
gpio_clear (_pwm_hw[pwm].gpios[i]);
GPIO.func_out_sel_cfg[_pwm_hw[pwm].gpios[i]].func_sel = _pwm_hw[pwm].signal_group + i;
}
/* start the PWM device */
_pwm_start(pwm);
return freq;
}
uint8_t pwm_channels(pwm_t pwm)
{
CHECK_PARAM_RET (pwm < pwm_dev_num, 0);
return _pwm_hw[pwm].gpio_num;
}
void pwm_set(pwm_t pwm, uint8_t channel, uint16_t value)
{
DEBUG("%s pwm=%u channel=%u value=%u\n", __func__, pwm, channel, value);
CHECK_PARAM (pwm < pwm_dev_num);
CHECK_PARAM (channel < _pwm_dev[pwm].chn_num);
CHECK_PARAM (value <= _pwm_dev[pwm].res);
uint32_t state = irq_disable();
_pwm_dev[pwm].chn[channel].duty = value;
_pwm_dev[pwm].chn[channel].used = true;
/* determine used operator and operator output */
uint8_t op_idx = channel >> 1;
uint8_t op_out = channel & 0x01;
/* compute and set shadow register (compare) )value of according channel */
uint16_t cmp = 0;
switch (_pwm_dev[pwm].mode) {
case PWM_LEFT: cmp = value;
break;
case PWM_RIGHT: cmp = value - 1;
break;
case PWM_CENTER: cmp = _pwm_hw[pwm].regs->timer[0].period.period - value;
break;
}
_pwm_hw[pwm].regs->channel[op_idx].cmpr_value[op_out].cmpr_val = cmp;
/* set actions for timing events (reset all first) */
_pwm_hw[pwm].regs->channel[op_idx].generator[op_out].val = 0;
if (op_out == 0)
{
/* channel/output A is used -> set actions for channel A */
switch (_pwm_dev[pwm].mode)
{
case PWM_LEFT:
_pwm_hw[pwm].regs->channel[op_idx].generator[op_out].utez = PWM_OP_ACTION_HIGH;
_pwm_hw[pwm].regs->channel[op_idx].generator[op_out].utea = PWM_OP_ACTION_LOW;
break;
case PWM_RIGHT:
_pwm_hw[pwm].regs->channel[op_idx].generator[op_out].dtea = PWM_OP_ACTION_HIGH;
_pwm_hw[pwm].regs->channel[op_idx].generator[op_out].dtep = PWM_OP_ACTION_LOW;
break;
case PWM_CENTER:
_pwm_hw[pwm].regs->channel[op_idx].generator[op_out].utea = PWM_OP_ACTION_HIGH;
_pwm_hw[pwm].regs->channel[op_idx].generator[op_out].dtea = PWM_OP_ACTION_LOW;
break;
}
}
else {
/* channel/output B is used -> set actions for channel B */
switch (_pwm_dev[pwm].mode)
{
case PWM_LEFT:
_pwm_hw[pwm].regs->channel[op_idx].generator[op_out].utez = PWM_OP_ACTION_HIGH;
_pwm_hw[pwm].regs->channel[op_idx].generator[op_out].uteb = PWM_OP_ACTION_LOW;
break;
case PWM_RIGHT:
_pwm_hw[pwm].regs->channel[op_idx].generator[op_out].dteb = PWM_OP_ACTION_HIGH;
_pwm_hw[pwm].regs->channel[op_idx].generator[op_out].dtep = PWM_OP_ACTION_LOW;
break;
case PWM_CENTER:
_pwm_hw[pwm].regs->channel[op_idx].generator[op_out].uteb = PWM_OP_ACTION_HIGH;
_pwm_hw[pwm].regs->channel[op_idx].generator[op_out].dteb = PWM_OP_ACTION_LOW;
break;
}
}
irq_restore(state);
}
void pwm_poweron(pwm_t pwm)
{
CHECK_PARAM (pwm < pwm_dev_num);
periph_module_enable(_pwm_hw[pwm].mod);
_pwm_start(pwm);
}
void pwm_poweroff(pwm_t pwm)
{
CHECK_PARAM (pwm < pwm_dev_num);
_pwm_stop (pwm);
periph_module_disable(_pwm_hw[pwm].mod);
}
static void _pwm_start(pwm_t pwm)
{
pwm_mode_t mode = _pwm_dev[pwm].mode;
uint16_t res = _pwm_dev[pwm].res;
uint32_t freq = _pwm_dev[pwm].freq;
uint32_t period = 0;
/* set timer mode */
switch (mode) {
case PWM_LEFT:
period = res;
_pwm_hw[pwm].regs->timer[0].mode.mode = PWM_TIMER_MOD_UP;
break;
case PWM_RIGHT:
period = res;
_pwm_hw[pwm].regs->timer[0].mode.mode = PWM_TIMER_MOD_DOWN;
break;
case PWM_CENTER:
period = res * 2;
_pwm_hw[pwm].regs->timer[0].mode.mode = PWM_TIMER_MOD_UP_DOWN;
break;
}
uint32_t cps = period * freq;
/* maximum number of timer clock cycles per second (freq*period) must not
be greater than PWM_CPS_MAX, reduce the freq if neccessary and keep
the resolution */
if (cps > PWM_CPS_MAX) {
freq = PWM_CPS_MAX / period;
_pwm_dev[pwm].freq = freq;
DEBUG("%s freq*res was to high, freq was reduced to %d Hz\n",
__func__, freq);
}
/* minimum number of timer clock cycles per second (freq*period) must not
be less than PWM_CPS_MIN, increase the freq if neccessary and keep
the resolution */
else if (cps < PWM_CPS_MIN) {
freq = PWM_CPS_MIN / period;
_pwm_dev[pwm].freq = freq;
DEBUG("%s freq*res was to low, freq was increased to %d Hz\n",
__func__, freq);
}
/* determine a suitable pwm clock prescale */
uint32_t prescale;
if (cps > 1000000) {
/* pwm clock is not scaled,
8 bit timer prescaler can scale down timer clock to 625 kHz */
prescale = 1;
}
else if (cps > 100000) {
/* pwm clock is scaled down to 10 MHz,
8 bit timer prescaler can scale down timer clock to 39,0625 kHz */
prescale = 16;
}
else if (cps > 10000) {
/* pwm clock is scaled down to 1 MHz
8 bit timer prescaler can scale down timer clock to 3,90625 kHz */
prescale = 160;
}
else {
/* pwm clock is scaled down to 640 kHz
8 bit timer prescaler can scale down timer clock to 2,5 kHz */
prescale = 250;
}
_pwm_hw[pwm].regs->clk_cfg.prescale = prescale - 1;
/* set timing parameters (only timer0 is used) */
_pwm_hw[pwm].regs->timer[0].period.prescale = (PWM_CLK / prescale / cps) - 1;
_pwm_hw[pwm].regs->timer[0].period.period = (mode == PWM_CENTER) ? res : res - 1;
_pwm_hw[pwm].regs->timer[0].period.upmethod = PWM_TIMER_UPDATE_IMMIDIATE;
/* start the timer */
_pwm_hw[pwm].regs->timer[0].mode.start = PWM_TIMER_RUNS_ON;
/* set timer sync phase and enable timer sync input */
_pwm_hw[pwm].regs->timer[0].sync.timer_phase = 0;
_pwm_hw[pwm].regs->timer[0].sync.in_en = 1;
/* set the duty for all channels to start them */
for (int i = 0; i < _pwm_dev[pwm].chn_num; i++) {
if (_pwm_dev[pwm].chn[i].used)
pwm_set(pwm, i, _pwm_dev[pwm].chn[i].duty);
}
/* sync all timers */
for (unsigned i = 0; i < pwm_dev_num; i++) {
_pwm_hw[i].regs->timer[0].sync.sync_sw = ~_pwm_hw[i].regs->timer[0].sync.sync_sw;
}
}
static void _pwm_stop(pwm_t pwm)
{
/* disable the timer */
_pwm_hw[pwm].regs->timer[0].mode.mode = PWM_TIMER_MOD_FREEZE;
}
/* do some static initialization and configuration checks */
static bool _pwm_configuration(void)
{
if (pwm_dev_num > PWM_NUMOF_MAX) {
LOG_TAG_ERROR("pwm", "%d PWM devices were defined, only %d PWM are "
"supported\n", pwm_dev_num, PWM_NUMOF_MAX);
return false;
}
for (unsigned i = 0; i < pwm_dev_num; i++) {
if (_pwm_hw[i].gpio_num > PWM_CHANNEL_NUM_DEV_MAX) {
LOG_TAG_ERROR("pwm", "Number of PWM channels of device %d is %d, "
"at maximum only %d channels per PWM device are "
"supported\n",
i, _pwm_hw[i].gpio_num, PWM_CHANNEL_NUM_DEV_MAX);
return false;
}
}
bool multiple_used = false;
for (unsigned i = 0; i < pwm_dev_num; i++) {
for (unsigned j = 0; j < pwm_dev_num; j++) {
if (i != j) {
for (unsigned k = 0; k < _pwm_hw[i].gpio_num >> 2; k++) {
for (unsigned l = 0; l < _pwm_hw[i].gpio_num >> 2; l++) {
if (_pwm_hw[i].gpios[k] == _pwm_hw[j].gpios[l]) {
LOG_TAG_ERROR("pwm", "GPIO%d is used multiple times in "
"PWM devices %d and %d\n",
_pwm_hw[i].gpios[k], i, j);
multiple_used = true;
}
}
}
}
}
}
if (multiple_used) {
return false;
}
return true;
}
void pwm_print_config(void)
{
for (unsigned pwm = 0; pwm < pwm_dev_num; pwm++) {
ets_printf("\tPWM_DEV(%d)\tchannels=[ ", pwm);
for (int i = 0; i < _pwm_hw[pwm].gpio_num; i++) {
ets_printf("%d ", _pwm_hw[pwm].gpios[i]);
}
ets_printf("]\n");
}
}
#else /* defined(PWM0_GPIOS) || defined(PWM1_GPIOS) */
void pwm_print_config(void)
{
LOG_TAG_INFO("pwm", "no PWM devices\n");
}
#endif /* defined(PWM0_GPIOS) || defined(PWM1_GPIOS) */

355
cpu/esp32/periph/rtc.c Normal file
View File

@ -0,0 +1,355 @@
/*
* 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_esp32
* @ingroup drivers_periph_rtc
* @{
*
* @file
* @brief Low-level RTC driver implementation
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
/*
* If RTC_TIMER_USED is 0, the microsecond system timer is used to emulate an
* RTC, otherwise the RTC timer is used. Advantage of using RTC over sytem
* timer is that it also continues in deep sleep and after software reset.
*/
#define RTC_TIMER_USED 1
#define ENABLE_DEBUG (0)
#include "debug.h"
#include "esp_common.h"
#include <string.h>
#include "cpu.h"
#include "esp_attr.h"
#include "log.h"
#include "irq_arch.h"
#include "periph/rtc.h"
#include "rom/ets_sys.h"
#include "soc/dport_reg.h"
#include "soc/rtc_cntl_struct.h"
#include "soc/timer_group_struct.h"
#include "syscalls.h"
#include "xtensa/xtensa_api.h"
/* TODO move to TIMER_SYSTEM definition in periph_cpu.h */
#define TIMER_SYSTEM_GROUP TIMERG0
#define TIMER_SYSTEM_INT_MASK BIT(0)
#define TIMER_SYSTEM_INT_SRC ETS_TG0_T0_LEVEL_INTR_SOURCE
#define TIMER_SYSTEM_CLK_HZ (1000000UL)
/* we can't include soc/rtc.h because of rtc_init declaration conflicts */
extern uint32_t rtc_clk_slow_freq_get_hz(void);
#if RTC_TIMER_USED
#define RTC_TIMER_CLK_HZ rtc_clk_slow_freq_get_hz()
#else /* RTC_TIMER_USED */
#define RTC_TIMER_CLK_HZ TIMER_SYSTEM_CLK_HZ
#endif /* RTC_TIMER_USED */
/* static variables */
static rtc_alarm_cb_t _rtc_alarm_cb = NULL;
static void* _rtc_alarm_arg = NULL;
static time_t _sys_alarm_time = 0;
#define RTC_BSS_ATTR __attribute__((section(".rtc.bss")))
/* save several time stamps */
static uint64_t RTC_BSS_ATTR _rtc_time_init_us;
static uint64_t RTC_BSS_ATTR _rtc_time_init;
static uint64_t RTC_BSS_ATTR _rtc_time_set_us;
static uint64_t RTC_BSS_ATTR _rtc_time_set;
static time_t RTC_BSS_ATTR _sys_time_set;
/* forward declarations */
static time_t _sys_get_time (void);
static uint64_t _rtc_get_time_raw(void);
static void IRAM_ATTR _rtc_timer_handler(void* arg);
void rtc_init(void)
{
if (_rtc_time_init == 0 && _rtc_time_init_us == 0) {
/* only set it new, if it was not set before */
_rtc_time_init = _rtc_get_time_raw();
_rtc_time_init_us = _rtc_get_time_raw();
DEBUG("%s saved rtc_init=%lld rtc_init_us=%lld\n",
__func__, _rtc_time_init, _rtc_time_init_us);
}
#if RTC_TIMER_USED
/* restore microsecond system timer from RTC timer */
uint64_t _rtc_time_now = _rtc_get_time_raw();
uint64_t _sys_time_now = (_rtc_time_now > UINT32_MAX) ?
_rtc_time_now / RTC_TIMER_CLK_HZ * TIMER_SYSTEM_CLK_HZ :
_rtc_time_now * TIMER_SYSTEM_CLK_HZ / RTC_TIMER_CLK_HZ;
/* restore system timer */
TIMER_SYSTEM.load_high = (uint32_t)(_sys_time_now >> 32);
TIMER_SYSTEM.load_low = (uint32_t)(_sys_time_now & 0xffffffff);
TIMER_SYSTEM.reload = 0;
DEBUG("%s restored rtc_init=%lld rtc_init_us=%lld\n",
__func__, _rtc_time_init, _rtc_time_init_us);
#endif
}
void rtc_poweron(void)
{
/* RTC is always on, also in deep sleep mode */
return;
}
void rtc_poweroff(void)
{
/* RTC is always on, also in deep sleep mode */
return;
}
int rtc_set_time(struct tm *ttime)
{
_rtc_time_set_us = system_get_time_64();
_rtc_time_set = _rtc_get_time_raw();
_sys_time_set = mktime (ttime);
DEBUG("%s sys_time_set=%ld sys_time_us=%lld rtc_time_set=%lld\n",
__func__, _sys_time_set, system_get_time_64(), _rtc_time_set);
return 0;
}
int rtc_get_time(struct tm *ttime)
{
time_t _sys_time = _sys_get_time();
DEBUG("%s sys_time=%ld rtc_time=%lld\n", __func__,
_sys_time, _rtc_get_time_raw());
struct tm* _time = localtime(&_sys_time);
if (_time) {
memcpy(ttime, _time, sizeof(struct tm));
return 0;
}
else {
return -1;
}
}
int rtc_get_alarm(struct tm *time)
{
struct tm* _time = localtime(&_sys_alarm_time);
if (_time) {
memcpy(time, _time, sizeof(struct tm));
return 0;
}
else {
return -1;
}
}
int rtc_set_alarm(struct tm *time, rtc_alarm_cb_t cb, void *arg)
{
_rtc_alarm_cb = cb;
_rtc_alarm_arg = arg;
/* determine the offset of alarm time to the set time in seconds */
_sys_alarm_time = mktime(time);
time_t _sys_time_offset = _sys_alarm_time - _sys_time_set;
/*
* RTC doesn't provide alarm functionality in active mode. At least
* the RTC main timer seems not to work. Therefore we always use the
* system timer for alarms. The Advantage of using RTC over sytem timer
* is that it also continues in deep sleep and after software reset.
*/
#if 0 /* TODO should be RTC_TIMER_USED */
/* determine the offset of alarm time to current time in RTC time */
uint64_t _rtc_time_alarm;
_rtc_time_alarm = _rtc_time_set + _sys_time_offset * RTC_TIMER_CLK_HZ;
DEBUG("%s sys=%d sys_alarm=%d rtc=%lld rtc_alarm=%lld\n", __func__,
_sys_get_time(), _sys_time_offset, _rtc_get_time_raw(), _rtc_time_alarm);
/* set the timer value */
RTCCNTL.slp_timer0 = _rtc_time_alarm & 0xffffffff;
RTCCNTL.slp_timer1.slp_val_hi = _rtc_time_alarm >> 32;
DEBUG("%s %08x%08x \n", __func__, RTCCNTL.slp_timer1.slp_val_hi, RTCCNTL.slp_timer0);
/* enable RTC timer alarm */
RTCCNTL.slp_timer1.main_timer_alarm_en = 1;
/* clear and enable RTC timer interrupt */
RTCCNTL.int_clr.rtc_main_timer = 1;
RTCCNTL.int_ena.rtc_main_timer = 1;
/* route all RTC interrupt sources to the same level type interrupt */
intr_matrix_set(PRO_CPU_NUM, DPORT_PRO_RTC_CORE_INTR_MAP_REG, CPU_INUM_RTC);
/* set interrupt handler and enable the CPU interrupt */
xt_set_interrupt_handler(CPU_INUM_RTC, _rtc_timer_handler, NULL);
xt_ints_on(BIT(CPU_INUM_RTC));
#else
/* determine the offset of alarm time to the RTC set time */
uint64_t _rtc_time_alarm;
#if RTC_TIMER_USED
/* convert rtc_time_set to time in us taking care with big numbers */
_rtc_time_alarm = _rtc_time_set_us + _sys_time_offset * TIMER_SYSTEM_CLK_HZ;
DEBUG("%s sys=%ld sys_alarm=%ld rtc_set_us=%lld rtc_us=%lld rtc_alarm_us=%lld\n", __func__,
_sys_get_time(), _sys_time_offset,
_rtc_time_set_us, system_get_time_64(), _rtc_time_alarm);
#else
_rtc_time_alarm = _rtc_time_set + _sys_time_offset * TIMER_SYSTEM_CLK_HZ;
DEBUG("%s sys=%ld sys_alarm=%ld rtc=%lld rtc_alarm=%lld\n", __func__,
_sys_get_time(), _sys_time_offset, _rtc_get_time_raw(), _rtc_time_alarm);
#endif
/* set the timer value */
TIMER_SYSTEM.alarm_high = (uint32_t)(_rtc_time_alarm >> 32);
TIMER_SYSTEM.alarm_low = (uint32_t)(_rtc_time_alarm & 0xffffffff);
/* clear the bit in status and set the bit in interrupt enable */
TIMER_SYSTEM_GROUP.int_clr_timers.val |= TIMER_SYSTEM_INT_MASK;
TIMER_SYSTEM_GROUP.int_ena.val |= TIMER_SYSTEM_INT_MASK;
/* route all timer interrupt sources to the same level type interrupt */
intr_matrix_set(PRO_CPU_NUM, TIMER_SYSTEM_INT_SRC, CPU_INUM_RTC);
/* set interrupt handler and enable the CPU interrupt */
xt_set_interrupt_handler(CPU_INUM_RTC, _rtc_timer_handler, NULL);
xt_ints_on(BIT(CPU_INUM_RTC));
/* enable the timer alarm */
TIMER_SYSTEM.config.level_int_en = 1;
TIMER_SYSTEM.config.alarm_en = 1;
#endif
return 0;
}
void rtc_clear_alarm(void)
{
_rtc_alarm_cb = NULL;
_rtc_alarm_arg = NULL;
#if 0 /* TODO should be RTC_TIMER_USED, see rtc_set_alarm */
/* disable RTC timer alarm and disable the RTC timer interrupt */
RTCCNTL.slp_timer1.main_timer_alarm_en = 0;
RTCCNTL.int_ena.rtc_main_timer = 0;
/* route all RTC interrupt sources to the same level type interrupt */
intr_matrix_set(PRO_CPU_NUM, DPORT_PRO_RTC_CORE_INTR_MAP_REG, CPU_INUM_RTC);
/* disable the the CPU interrupt */
#else
/* reset the bit in interrupt enable */
TIMER_SYSTEM_GROUP.int_ena.val |= TIMER_SYSTEM_INT_MASK;
/* disable the CPU interrupt */
xt_ints_on(BIT(CPU_INUM_RTC));
/* disable the timer alarm */
TIMER_SYSTEM.config.level_int_en = 0;
TIMER_SYSTEM.config.alarm_en = 0;
xt_ints_on(BIT(CPU_INUM_RTC));
#endif
}
static time_t _sys_get_time (void)
{
return _sys_time_set + (_rtc_get_time_raw() - _rtc_time_set) / RTC_TIMER_CLK_HZ;
}
static uint64_t _rtc_get_time_raw(void)
{
#if RTC_TIMER_USED
/* trigger timer register update */
RTCCNTL.time_update.update = 1;
/* wait until values in registers are valid */
while (!RTCCNTL.time_update.valid) { }
/* read the time and return */
uint64_t rtc_time;
rtc_time = RTCCNTL.time0;
rtc_time += ((uint64_t)RTCCNTL.time1.val) << 32;
return rtc_time;
#else
return system_get_time_64();
#endif
}
static void IRAM_ATTR _rtc_timer_handler(void* arg)
{
irq_isr_enter();
#if 0 /* TODO should be RTC_TIMER_USED */
/* check for RTC timer interrupt */
if (RTCCNTL.int_st.rtc_main_timer) {
/* clear the interrupt */
RTCCNTL.int_clr.rtc_main_timer = 1;
/* call back registered function */
if (_rtc_alarm_cb) {
_rtc_alarm_cb(_rtc_alarm_arg);
}
}
/* clear all interrupts */
RTCCNTL.int_clr.val = 0x1ff;
#else
/* check for RTC timer interrupt */
if (TIMER_SYSTEM_GROUP.int_st_timers.val & TIMER_SYSTEM_INT_MASK) {
DEBUG("%s\n", __func__);
/* disable alarms */
TIMER_SYSTEM.config.level_int_en = 0;
TIMER_SYSTEM.config.alarm_en = 0;
/* clear the bit in interrupt enable and status register */
TIMER_SYSTEM_GROUP.int_ena.val &= ~TIMER_SYSTEM_INT_MASK;
TIMER_SYSTEM_GROUP.int_clr_timers.val |= TIMER_SYSTEM_INT_MASK;
/* call back registered function */
if (_rtc_alarm_cb) {
_rtc_alarm_cb(_rtc_alarm_arg);
}
}
#endif
irq_isr_exit();
}

546
cpu/esp32/periph/spi.c Normal file
View File

@ -0,0 +1,546 @@
/*
* 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_esp32
* @ingroup drivers_periph_spi
* @{
*
* @file
* @brief Low-level SPI driver implementation
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
#define ENABLE_DEBUG (0)
#include "debug.h"
#include "esp_common.h"
#include "log.h"
#include <string.h>
#include "cpu.h"
#include "mutex.h"
#include "periph/spi.h"
#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"
#include "gpio_arch.h"
#define SPI_BLOCK_SIZE 64 /* number of bytes per SPI transfer */
#define FSPI (1) /* controller SPI1 realizes interface FSPI */
#define HSPI (2) /* controller SPI2 realizes interface HSPI */
#define VSPI (3) /* controller SPI3 realizes interface VSPI */
/* pins of FSI are fixed */
#define FSPI_SCK GPIO6
#define FSPI_MISO GPIO7
#define FSPI_MOSI GPIO8
/** stucture which decribes all properties of one SPI bus */
struct _spi_bus_t {
spi_dev_t* regs; /* pointer to register data struct of the SPI device */
uint8_t controller; /* number of the controller used */
uint8_t mod; /* peripheral hardware module of the SPI interface */
uint8_t int_src; /* peripheral interrupt source used by the SPI device */
uint8_t pin_sck; /* SCK pin */
uint8_t pin_mosi; /* MOSI pin */
uint8_t pin_miso; /* MISO pin */
uint8_t pin_cs; /* CS pin */
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 */
mutex_t lock; /* mutex for each possible SPI interface */
bool initialized; /* interface already initialized */
bool pins_initialized; /* pins interface initialized */
};
static struct _spi_bus_t _spi[] = {
#ifdef SPI0_DEV
{
.controller = SPI0_DEV,
.pin_cs = SPI0_CS0,
#if SPI0_DEV != FSPI
.pin_sck = SPI0_SCK,
.pin_mosi = SPI0_MOSI,
.pin_miso = SPI0_MISO,
#else
.pin_sck = FSPI_SCK,
.pin_mosi = FSPI_MOSI,
.pin_miso = FSPI_MISO,
#endif
.initialized = false,
.pins_initialized = false,
.lock = MUTEX_INIT
},
#endif
#ifdef SPI1_DEV
{
.controller = SPI1_DEV,
.pin_cs = SPI1_CS0,
#if SPI1_DEV != FSPI
.pin_sck = SPI1_SCK,
.pin_mosi = SPI1_MOSI,
.pin_miso = SPI1_MISO,
#else
.pin_sck = FSPI_SCK,
.pin_mosi = FSPI_MOSI,
.pin_miso = FSPI_MISO,
#endif
.initialized = false,
.pins_initialized = false,
.lock = MUTEX_INIT
},
#endif
#ifdef SPI2_DEV
{
.controller = SPI2_DEV,
.pin_cs = SPI2_CS0,
#if SPI2_DEV != FSPI
.pin_sck = SPI2_SCK,
.pin_mosi = SPI2_MOSI,
.pin_miso = SPI2_MISO,
#else
.pin_sck = FSPI_SCK,
.pin_mosi = FSPI_MOSI,
.pin_miso = FSPI_MISO,
#endif
.initialized = false,
.pins_initialized = false,
.lock = MUTEX_INIT
},
#endif
};
/* the number of SPI bus devices used */
const unsigned spi_bus_num = sizeof(_spi) / sizeof(_spi[0]);
#define CHECK_SPI_DEV(bus) { \
CHECK_PARAM(bus < spi_bus_num); \
if (_spi[bus].regs == NULL) { \
LOG_TAG_ERROR("spi", "SPI_DEV(%d) is not available\n", bus); \
return; \
} \
}
#define CHECK_SPI_DEV_RET(bus,error) { \
CHECK_PARAM_RET(bus < spi_bus_num, error); \
if (_spi[bus].regs == NULL) { \
LOG_TAG_ERROR("spi", "SPI_DEV(%d) is not available\n", bus); \
return error; \
} \
}
/*
* GPIOs that were once initialized as SPI interface pins can not be used
* afterwards for anything else. Therefore, SPI interfaces are not initialized
* until they are used for the first time. The *spi_init* function is just a
* dummy for source code compatibility. The initialization of an SPI interface
* is performed by the *_spi_init_internal* function, which is called either by
* the *spi_init_cs* function or the *spi_acquire* function when the interface
* is used for the first time.
*/
void IRAM_ATTR spi_init (spi_t bus)
{
CHECK_PARAM(bus < spi_bus_num);
switch (_spi[bus].controller) {
case FSPI: _spi[bus].regs = &SPI1;
_spi[bus].mod = PERIPH_SPI_MODULE;
_spi[bus].int_src = ETS_SPI1_INTR_SOURCE;
_spi[bus].signal_sck = SPICLK_OUT_IDX;
_spi[bus].signal_mosi = SPID_OUT_IDX;
_spi[bus].signal_miso = SPIQ_IN_IDX;
break;
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;
default: break;
}
return;
}
/* Internal initialization function when the interface is used the first time */
static void _spi_init_internal (spi_t bus)
{
CHECK_SPI_DEV(bus);
/* avoid multiple initializations */
if (_spi[bus].initialized) {
return;
}
_spi[bus].initialized = true;
DEBUG("%s bus=%u\n", __func__, bus);
/* initialize pins */
spi_init_pins(bus);
/* check whether pins could be initialized, otherwise return */
if (gpio_get_pin_usage(_spi[bus].pin_sck) != _SPI &&
gpio_get_pin_usage(_spi[bus].pin_miso) != _SPI &&
gpio_get_pin_usage(_spi[bus].pin_mosi) != _SPI &&
gpio_get_pin_usage(_spi[bus].pin_cs) != _SPI) {
return;
}
/* enable (power on) the according SPI module */
periph_module_enable(_spi[bus].mod);
/* 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].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].regs->ctrl.wr_bit_order = 0;
_spi[bus].regs->ctrl.rd_bit_order = 0;
/* 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;
_spi[bus].regs->ctrl.wp = 0;
/* aquire 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)
{
CHECK_SPI_DEV(bus);
/* call initialization of the SPI interface if it is not initialized yet */
if (!_spi[bus].initialized) {
_spi_init_internal(bus);
}
/* avoid multiple pin initializations */
if (_spi[bus].pins_initialized) {
return;
}
_spi[bus].pins_initialized = true;
DEBUG("%s bus=%u\n", __func__, bus);
/* in case of SPI_DEV(2) all pins are already initialized
as SPI pins */
if (bus != SPI_DEV(2)) {
/* if not already initialized as SPI, try to initialize the pins */
if (gpio_init (_spi[bus].pin_sck, GPIO_OUT) ||
gpio_init (_spi[bus].pin_mosi, GPIO_OUT) ||
gpio_init (_spi[bus].pin_miso, GPIO_IN)) {
LOG_TAG_ERROR("spi",
"SPI_DEV(%d) pins could not be initialized\n", bus);
return;
}
if (spi_init_cs(bus, _spi[bus].pin_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[bus].pin_sck, _SPI);
gpio_set_pin_usage(_spi[bus].pin_mosi, _SPI);
gpio_set_pin_usage(_spi[bus].pin_miso, _SPI);
/* connect SCK and MOSI pins to the output signal through the GPIO matrix */
GPIO.func_out_sel_cfg[_spi[bus].pin_sck].func_sel = _spi[bus].signal_sck;
GPIO.func_out_sel_cfg[_spi[bus].pin_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[bus].pin_miso;
}
else {
LOG_TAG_WARNING("spi", "Using SPI_DEV(2) is dangerous\n");
}
}
int spi_init_cs(spi_t bus, spi_cs_t cs)
{
DEBUG("%s bus=%u cs=%u\n", __func__, bus, cs);
CHECK_SPI_DEV_RET(bus, SPI_NODEV);
/* call initialization of the SPI interface if it is not initialized yet */
if (!_spi[bus].initialized) {
_spi_init_internal(bus);
}
/* return if pin is already initialized as SPI CS signal */
if (gpio_get_pin_usage(cs) == _SPI) {
return SPI_OK;
}
/* 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);
/* pin cannot be used for anything else */
gpio_set_pin_usage(cs, _SPI);
return SPI_OK;
}
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);
CHECK_SPI_DEV_RET(bus, SPI_NODEV);
/* call initialization of the SPI interface if it is not initialized yet */
if (!_spi[bus].initialized) {
_spi_init_internal(bus);
}
/* if parameter cs is GPIO_UNDEF, the default CS pin is used */
cs = (cs == GPIO_UNDEF) ? _spi[bus].pin_cs : cs;
/* if the CS pin used is not yet initialized, we do it now */
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[bus].lock);
/* set SPI mode, see Table 25 and Section 7.4.2 in Technical Reference */
_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 Technical Reference */
uint32_t spi_clkdiv_pre;
uint32_t spi_clkcnt_N;
switch (clk) {
case SPI_CLK_10MHZ: spi_clkdiv_pre = 2; /* predivides 80 MHz to 40 MHz */
spi_clkcnt_N = 4; /* 4 cycles results into 10 MHz */
break;
case SPI_CLK_5MHZ: spi_clkdiv_pre = 2; /* predivides 80 MHz to 40 MHz */
spi_clkcnt_N = 8; /* 8 cycles results into 5 MHz */
break;
case SPI_CLK_1MHZ: spi_clkdiv_pre = 2; /* predivides 80 MHz to 40 MHz */
spi_clkcnt_N = 40; /* 40 cycles results into 1 MHz */
break;
case SPI_CLK_400KHZ: spi_clkdiv_pre = 20; /* predivides 80 MHz to 4 MHz */
spi_clkcnt_N = 10; /* 10 cycles results into 400 kHz */
break;
case SPI_CLK_100KHZ: spi_clkdiv_pre = 20; /* predivides 80 MHz to 4 MHz */
spi_clkcnt_N = 40; /* 20 cycles results into 100 kHz */
break;
default: spi_clkdiv_pre = 20; /* predivides 80 MHz to 4 MHz */
spi_clkcnt_N = 40; /* 20 cycles results into 100 kHz */
}
/* register values are set to deviders-1 */
spi_clkdiv_pre--;
spi_clkcnt_N--;
DEBUG("%s spi_clkdiv_prev=%u spi_clkcnt_N=%u\n",
__func__, spi_clkdiv_pre, spi_clkcnt_N);
/* SPI clock is derived from APB clock by dividers */
_spi[bus].regs->clock.clk_equ_sysclk = 0;
/* set SPI clock deviders */
_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 IRAM_ATTR spi_release(spi_t bus)
{
CHECK_SPI_DEV(bus);
/* release the bus */
mutex_unlock(&_spi[bus].lock);
}
static const char* _spi_names[] = { "SSPI", "FSPI", "HSPI", "VSPI" };
void spi_print_config(void)
{
for (unsigned bus = 0; bus < spi_bus_num; bus++) {
ets_printf("\tSPI_DEV(%d)\t%s ", bus, _spi_names[_spi[bus].controller]);
ets_printf("sck=%d " , _spi[bus].pin_sck);
ets_printf("miso=%d ", _spi[bus].pin_miso);
ets_printf("mosi=%d ", _spi[bus].pin_mosi);
ets_printf("cs=%d\n" , _spi[bus].pin_cs);
}
}
/*
* Following functions are from the hardware SPI driver of the esp-open-rtos
* project.
*
* Copyright (c) Ruslan V. Uss, 2016
* BSD Licensed as described in the file LICENSE
* https://github.com/SuperHouse/esp-open-rtos/blob/master/LICENSE
*/
inline static void IRAM_ATTR _set_size(uint8_t bus, uint8_t bytes)
{
uint32_t bits = ((uint32_t)bytes << 3) - 1;
_spi[bus].regs->mosi_dlen.val = bits;
_spi[bus].regs->miso_dlen.val = bits;
}
inline static void IRAM_ATTR _wait(uint8_t bus)
{
/* SPI_CMD_REG.SPI_USR is cleared when operation has been finished */
while (_spi[bus].regs->cmd.usr) {}
}
inline static void IRAM_ATTR _start(uint8_t bus)
{
/* set SPI_CMD_REG.SPI_USR to start an operation */
_spi[bus].regs->cmd.usr = 1;
}
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].regs->data_buf, data, len - tail);
if (!tail) {
return;
}
uint32_t last = 0;
uint8_t *offs = (uint8_t *)data + len - tail;
for (uint8_t i = 0; i < tail; i++) {
last = last | (offs[i] << (i * 8));
}
_spi[bus].regs->data_buf[words] = last;
}
static const uint8_t spi_empty_out[SPI_BLOCK_SIZE] = { 0 };
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);
/* transfer one block data */
_wait(bus);
_set_size(bus, len);
_store_data(bus, out ? out : spi_empty_out, len);
_start(bus);
_wait(bus);
if (in) {
memcpy(in, (void *)_spi[bus].regs->data_buf, len);
}
}
void IRAM_ATTR spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont,
const void *out, void *in, size_t len)
{
CHECK_SPI_DEV(bus);
DEBUG("%s bus=%u cs=%u cont=%d out=%p in=%p len=%u\n",
__func__, bus, cs, cont, out, in, len);
if (!len) {
return;
}
#if ENABLE_DEBUG
if (out) {
DEBUG("out = ");
for (size_t i = 0; i < len; i++) {
DEBUG("%02x ", ((const uint8_t *)out)[i]);
}
DEBUG("\n");
}
#endif
gpio_clear (cs != SPI_CS_UNDEF ? cs : _spi[bus].pin_cs);
size_t blocks = len / SPI_BLOCK_SIZE;
uint8_t tail = len % SPI_BLOCK_SIZE;
DEBUG("%s bus=%u cs=%u blocks=%d tail=%d\n",
__func__, bus, cs, blocks, tail);
for (size_t i = 0; i < blocks; i++) {
_spi_buf_transfer(bus,
out ? (const uint8_t *)out + i * SPI_BLOCK_SIZE : NULL,
in ? (uint8_t *)in + i * SPI_BLOCK_SIZE : NULL, SPI_BLOCK_SIZE);
}
if (tail) {
_spi_buf_transfer(bus,
out ? (const uint8_t *)out + blocks * SPI_BLOCK_SIZE : 0,
in ? (uint8_t *)in + blocks * SPI_BLOCK_SIZE : NULL, tail);
}
if (!cont) {
gpio_set (cs != SPI_CS_UNDEF ? cs : _spi[bus].pin_cs);
}
#if ENABLE_DEBUG
if (in) {
DEBUG("in = ");
for (size_t i = 0; i < len; i++) {
DEBUG("%02x ", ((const uint8_t *)in)[i]);
}
DEBUG("\n");
}
#endif
}

623
cpu/esp32/periph/timer.c Normal file
View File

@ -0,0 +1,623 @@
/*
* 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_esp32
* @ingroup drivers_periph_timer
* @{
*
* @file
* @brief Low-level timer driver implementation for ESP32 SDK
*
* @author Gunar Schorcht <gunar@schorcht.net>
* @}
*/
/*
* WARNING! enable debugging will have timing side effects and can lead
* to timer underflows, system crashes or system dead locks in worst case.
*/
#define ENABLE_DEBUG (0)
#include "debug.h"
#include "periph/timer.h"
#include "driver/periph_ctrl.h"
#include "esp/common_macros.h"
#include "rom/ets_sys.h"
#include "soc/rtc.h"
#include "soc/timer_group_struct.h"
#include "xtensa/hal.h"
#include "xtensa/xtensa_api.h"
#include "esp_common.h"
#include "irq_arch.h"
#include "syscalls.h"
#include "xtimer.h"
#define RTC_PLL_480M 480 /* PLL with 480 MHz at maximum */
#define RTC_PLL_320M 320 /* PLL with 480 MHz at maximum */
#ifndef MODULE_ESP_HW_COUNTER
/* hardware timer modules used */
/**
* ESP32 has four 64 bit hardware timers:
* two timer groups TMG0 and TMG1 with 2 timers each
*
* TMG0, timer 0 is used for system time in us and is therefore not
* available as low level timer. Timers have only one channel. Timer device
* are mapped to hardware timer as following:
*
* 0 -> TMG0 timer 1
* 1 -> TMG1 timer 0
* 2 -> TMG1 timer 1
*
* The reason for this mapping is, that if only one timer is needed,
* TMG1 is left disabled. TMG1 is only enabled when more than one
* timer device is needed.
*
* PLEASE NOTE: Don't use ETS timer functions ets_timer_* in and this hardware
* timer implementation together!
*/
#define HW_TIMER_NUMOF 3
#define HW_TIMER_CHANNELS 1
#define HW_TIMER_CLK_DIV (rtc_clk_apb_freq_get() / 1000000)
#define HW_TIMER_CORRECTION (RTC_PLL_320M / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ)
#define HW_TIMER_DELTA_MIN (MAX(HW_TIMER_CORRECTION << 1, 5))
struct hw_timer_regs_t {
/* see Technical Reference, section 17.4 */
struct {
uint32_t unused : 10;
uint32_t ALARM_EN : 1; /* alarms are enabled */
uint32_t LEVEL_INT_EN: 1; /* alarms will generate level type interrupt */
uint32_t EDGE_INT_EN : 1; /* alarms will generate egde type interrupt */
uint32_t DIVIDER : 16; /* timer clock prescale value (basis is ABP) */
uint32_t AUTORELOAD : 1; /* auto-reload on alarms */
uint32_t INCREASE : 1; /* count up */
uint32_t EN : 1; /* timer is enabled */
} CONFIG_REG;
uint32_t LO_REG; /* time-base counter value low 32 bits */
uint32_t HI_REG; /* time-base counter value high 32 bits */
uint32_t UPDATE_REG; /* time-base counter value update trigger */
uint32_t ALARMLO_REG; /* alarm trigger time-base counter value, low 32 bits */
uint32_t ALARMHI_REG; /* alarm trigger time-base counter value, high 32 bits */
uint32_t LOADLO_REG; /* reload value, low 32 bits */
uint32_t LOADHI_REG; /* reload value, high 32 bits */
uint32_t LOAD_REG; /* reload trigger */
};
struct hw_timer_ints_t {
/* see Technical Reference, section 17.4 */
uint32_t INT_ENA_REG; /* interrupt enable bits */
uint32_t INT_RAW_REG; /* raw interrupt status */
uint32_t INT_STA_REG; /* masked interrupt status */
uint32_t INT_CLR_REG; /* interrupt clear bits */
};
struct hw_timer_t {
bool initialized; /* indicates whether timer is already initialized */
bool started; /* indicates whether timer is already started */
timer_isr_ctx_t isr_ctx;
};
struct hw_timer_hw_t {
struct hw_timer_regs_t* regs; /* timer configuration regs */
struct hw_timer_ints_t* int_regs; /* timer interrupt regs */
uint8_t int_mask; /* timer interrupt bit mask in interrupt regs */
uint8_t int_src; /* timer interrupt source */
};
static struct hw_timer_t timers[HW_TIMER_NUMOF] = { };
static const struct hw_timer_hw_t timers_hw[HW_TIMER_NUMOF] =
{
{
.regs = (struct hw_timer_regs_t*)&TIMERG0.hw_timer[1],
.int_regs = (struct hw_timer_ints_t*)&TIMERG0.int_ena,
.int_mask = BIT(1),
.int_src = ETS_TG0_T1_LEVEL_INTR_SOURCE
},
{
.regs = (struct hw_timer_regs_t*)&TIMERG1.hw_timer[0],
.int_regs = (struct hw_timer_ints_t*)&TIMERG1.int_ena,
.int_mask = BIT(0),
.int_src = ETS_TG1_T0_LEVEL_INTR_SOURCE
},
{
.regs = (struct hw_timer_regs_t*)&TIMERG1.hw_timer[1],
.int_regs = (struct hw_timer_ints_t*)&TIMERG1.int_ena,
.int_mask = BIT(1),
.int_src = ETS_TG1_T1_LEVEL_INTR_SOURCE
}
};
/** Latches the current counter value and return only the low part */
static inline uint32_t timer_get_counter_lo(tim_t dev)
{
/* we have to latch the current timer value */
timers_hw[dev].regs->UPDATE_REG = 0;
/* wait until instructions have been finished */
__asm__ volatile ("isync");
/* read high and low part of counter */
return timers_hw[dev].regs->LO_REG;
}
/** Latches the current counter value and return the high and the low part */
static inline void timer_get_counter(tim_t dev, uint32_t* hi, uint32_t* lo)
{
/* parameter check */
if (!hi || !lo) {
return;
}
/* we have to latch the current timer value */
timers_hw[dev].regs->UPDATE_REG = 0;
/* wait until instructions have been finished */
__asm__ volatile ("isync");
/* read high and low part of counter */
*hi = timers_hw[dev].regs->HI_REG;
*lo = timers_hw[dev].regs->LO_REG;
}
void IRAM hw_timer_handler(void* arg)
{
(void)arg;
/* since all timer interrupt sources are routed to the same cpu interrupt */
/* signal, we can't use arg to identify the timer wich caused the it */
irq_isr_enter();
for (unsigned dev = 0; dev < HW_TIMER_NUMOF; dev++) {
/* iterate over all devices and check what interrupt flags are set */
if (timers_hw[dev].int_regs->INT_STA_REG & timers_hw[dev].int_mask) {
DEBUG("%s dev=%d\n", __func__, dev);
/* disable alarms */
timers_hw[dev].regs->CONFIG_REG.LEVEL_INT_EN = 0;
timers_hw[dev].regs->CONFIG_REG.ALARM_EN = 0;
/* clear the bit in interrupt enable and status register */
timers_hw[dev].int_regs->INT_ENA_REG &= ~timers_hw[dev].int_mask;
timers_hw[dev].int_regs->INT_CLR_REG |= timers_hw[dev].int_mask;
/* execute the callback function */
timers[dev].isr_ctx.cb(timers[dev].isr_ctx.arg, 0);
}
}
irq_isr_exit();
}
int timer_init (tim_t dev, unsigned long freq, timer_cb_t cb, void *arg)
{
DEBUG("%s dev=%u freq=%lu cb=%p arg=%p\n", __func__, dev, freq, cb, arg);
CHECK_PARAM_RET (dev < HW_TIMER_NUMOF, -1);
CHECK_PARAM_RET (freq == XTIMER_HZ_BASE, -1);
CHECK_PARAM_RET (cb != NULL, -1);
if (timers[dev].initialized) {
DEBUG("%s timer dev=%u is already initialized (used)\n", __func__, dev);
return -1;
}
/* initialize timer data structure */
timers[dev].initialized = true;
timers[dev].started = false;
timers[dev].isr_ctx.cb = cb;
timers[dev].isr_ctx.arg = arg;
/* route all timer interrupt sources to the same level type interrupt */
intr_matrix_set(PRO_CPU_NUM, timers_hw[dev].int_src, CPU_INUM_TIMER);
/* we have to enable therefore the interrupt here */
xt_set_interrupt_handler(CPU_INUM_TIMER, hw_timer_handler, NULL);
xt_ints_on(BIT(CPU_INUM_TIMER));
if (dev) {
/* if dev > 0 we have to enable TMG1 module */
periph_module_enable(PERIPH_TIMG1_MODULE);
}
/* hardware timer configuration */
timers_hw[dev].regs->CONFIG_REG.EN = 0;
timers_hw[dev].regs->CONFIG_REG.AUTORELOAD = 0;
timers_hw[dev].regs->CONFIG_REG.INCREASE = 1;
timers_hw[dev].regs->CONFIG_REG.DIVIDER = HW_TIMER_CLK_DIV;
timers_hw[dev].regs->CONFIG_REG.EDGE_INT_EN = 0;
timers_hw[dev].regs->CONFIG_REG.LEVEL_INT_EN = 0;
timers_hw[dev].regs->CONFIG_REG.ALARM_EN = 0;
/* start the timer */
timer_start(dev);
return 0;
}
int IRAM timer_set(tim_t dev, int chn, unsigned int delta)
{
DEBUG("%s dev=%u channel=%d delta=%u\n", __func__, dev, chn, delta);
CHECK_PARAM_RET (dev < HW_TIMER_NUMOF, -1);
CHECK_PARAM_RET (chn < HW_TIMER_CHANNELS, -1);
/* disable interrupts */
int state = irq_disable ();
/* disable alarms */
timers_hw[dev].regs->CONFIG_REG.LEVEL_INT_EN = 0;
timers_hw[dev].regs->CONFIG_REG.ALARM_EN = 0;
delta = (delta > HW_TIMER_DELTA_MIN) ? delta : HW_TIMER_DELTA_MIN;
delta = (delta > HW_TIMER_CORRECTION) ? delta - HW_TIMER_CORRECTION : HW_TIMER_CORRECTION;
/* read the current value */
uint32_t count_lo;
uint32_t count_hi;
timer_get_counter(dev, &count_hi, &count_lo);
/* determine the alarm time */
uint64_t alarm;
alarm = count_lo;
alarm += ((uint64_t)count_hi) << 32;
alarm += delta;
timers_hw[dev].regs->ALARMHI_REG = (uint32_t)(alarm >> 32);
timers_hw[dev].regs->ALARMLO_REG = (uint32_t)(alarm & 0xffffffff);
/* enable alarms */
timers_hw[dev].regs->CONFIG_REG.LEVEL_INT_EN = 1;
timers_hw[dev].regs->CONFIG_REG.ALARM_EN = 1;
/* wait until instructions have been finished */
timers_hw[dev].regs->CONFIG_REG.EN = 1;
__asm__ volatile ("isync");
/* clear the bit in status and set the bit in interrupt enable */
timers_hw[dev].int_regs->INT_CLR_REG |= timers_hw[dev].int_mask;
timers_hw[dev].int_regs->INT_ENA_REG |= timers_hw[dev].int_mask;
/* restore interrupts enabled state */
irq_restore (state);
return 0;
}
int IRAM timer_set_absolute(tim_t dev, int chn, unsigned int value)
{
DEBUG("%s dev=%u channel=%d value=%u\n", __func__, dev, chn, value);
return timer_set (dev, chn, value - timer_read(dev));
}
int timer_clear(tim_t dev, int chn)
{
DEBUG("%s dev=%u channel=%d\n", __func__, dev, chn);
CHECK_PARAM_RET (dev < HW_TIMER_NUMOF, -1);
CHECK_PARAM_RET (chn < HW_TIMER_CHANNELS, -1);
/* disable alarms */
timers_hw[dev].regs->CONFIG_REG.LEVEL_INT_EN = 0;
timers_hw[dev].regs->CONFIG_REG.ALARM_EN = 0;
/* clear the bit in interrupt enable and status register */
timers_hw[dev].int_regs->INT_ENA_REG &= ~timers_hw[dev].int_mask;
timers_hw[dev].int_regs->INT_CLR_REG |= timers_hw[dev].int_mask;
return 0;
}
unsigned int IRAM timer_read(tim_t dev)
{
CHECK_PARAM_RET (dev < HW_TIMER_NUMOF, -1);
#if ENABLE_DEBUG
uint32_t count_lo = timer_get_counter_lo(dev);
DEBUG("%s %u\n", __func__, count_lo);
return count_lo;
#else
return timer_get_counter_lo(dev);
#endif
}
void IRAM timer_start(tim_t dev)
{
DEBUG("%s dev=%u @%u\n", __func__, dev, system_get_time());
CHECK_PARAM (dev < HW_TIMER_NUMOF);
timers_hw[dev].regs->CONFIG_REG.EN = 1;
}
void IRAM timer_stop(tim_t dev)
{
DEBUG("%s dev=%u\n", __func__, dev);
CHECK_PARAM (dev < HW_TIMER_NUMOF);
timers_hw[dev].regs->CONFIG_REG.EN = 0;
}
#else /* MODULE_ESP_HW_COUNTER */
/* hardware counter used as timer */
/**
* ESP32 has 3 ccompare registers. Each of them can generate an interrupt
* at different levels:
*
* CCOMPARE INT Level Priority
* 0 6 XCHAL_TIMER0_INTERRUPT 1 low
* 1 15 XCHAL_TIMER1_INTERRUPT 3 medium
* 2 16 XCHAL_TIMER2_INTERRUPT 5 high
*
* PLEASE NOTE: High level interrupts are not disabled in any case. So be
* careful to to use CCOMPARE register 2 and timer num 2, respectively.
* By default, TIMER_NUMOF is therefore set to only 2 in periph_conf.h.
*/
#define HW_TIMER_NUMOF XCHAL_NUM_TIMERS
#define HW_TIMER_CHANNELS 1
#define HW_TIMER_MASK 0xffffffff
#define HW_TIMER_DELTA_MAX 0x00ffffff /* in us */
#define HW_TIMER_DELTA_MASK 0x00ffffff
#define HW_TIMER_DELTA_RSHIFT 24
#define HW_TIMER_CORRECTION (RTC_PLL_480M / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ)
#define HW_TIMER_DELTA_MIN (MAX(HW_TIMER_CORRECTION, 5))
#define US_TO_HW_TIMER_TICKS(t) (t * system_get_cpu_freq())
#define HW_TIMER_TICKS_TO_US(t) (t / system_get_cpu_freq())
struct hw_channel_t {
bool used; /* indicates whether the channel is used */
uint32_t start_time; /* physical time when the timer channel has been started */
uint32_t delta_time; /* timer delta value (delta = cycles * timer_max + remainder) */
uint32_t cycles; /* number of complete max timer cycles */
uint32_t remainder; /* remainder timer value */
};
struct hw_timer_t {
tim_t dev; /* the timer device num */
bool initialized; /* indicates whether timer is already initialized */
bool started; /* indicates whether timer is already started */
timer_isr_ctx_t isr_ctx;
struct hw_channel_t channels[HW_TIMER_CHANNELS];
};
static struct hw_timer_t timers[HW_TIMER_NUMOF] = { };
static const uint8_t timers_int[HW_TIMER_NUMOF] = { XCHAL_TIMER0_INTERRUPT,
XCHAL_TIMER1_INTERRUPT,
XCHAL_TIMER2_INTERRUPT };
static void __timer_channel_start (struct hw_timer_t* timer, struct hw_channel_t* channel);
static void __timer_channel_stop (struct hw_timer_t* timer, struct hw_channel_t* channel);
static uint32_t __hw_timer_ticks_max;
static uint32_t __hw_timer_ticks_min;
void IRAM hw_timer_handler(void* arg)
{
uint32_t dev = (uint32_t)arg;
uint32_t chn = 0;
if (dev >= HW_TIMER_NUMOF && chn >= HW_TIMER_CHANNELS) {
return;
}
irq_isr_enter();
DEBUG("%s arg=%p\n", __func__, arg);
struct hw_timer_t* timer = &timers[dev];
struct hw_channel_t* channel = &timer->channels[chn];
if (channel->cycles) {
channel->cycles--;
xthal_set_ccompare(dev, xthal_get_ccount() + __hw_timer_ticks_max);
}
else if (channel->remainder >= HW_TIMER_DELTA_MIN) {
xthal_set_ccompare (dev, xthal_get_ccount() +
US_TO_HW_TIMER_TICKS(channel->remainder));
channel->remainder = 0;
}
else {
channel->remainder = 0;
channel->used = false;
xt_ints_off(BIT(timers_int[dev]));
xthal_set_ccompare (dev, 0);
timer->isr_ctx.cb(timer->isr_ctx.arg, chn);
}
irq_isr_exit();
}
int timer_init (tim_t dev, unsigned long freq, timer_cb_t cb, void *arg)
{
DEBUG("%s dev=%u freq=%lu cb=%p arg=%p\n", __func__, dev, freq, cb, arg);
CHECK_PARAM_RET (dev < HW_TIMER_NUMOF, -1);
CHECK_PARAM_RET (freq == XTIMER_HZ_BASE, -1);
CHECK_PARAM_RET (cb != NULL, -1);
if (timers[dev].initialized) {
DEBUG("%s timer dev=%u is already initialized (used)\n", __func__, dev);
return -1;
}
timers[dev].dev = dev;
timers[dev].initialized = true;
timers[dev].started = false;
timers[dev].isr_ctx.cb = cb;
timers[dev].isr_ctx.arg = arg;
xt_set_interrupt_handler(timers_int[dev], hw_timer_handler, (void *)dev);
for (int i = 0; i < HW_TIMER_CHANNELS; i++) {
timers[dev].channels[i].used = false;
timers[dev].channels[i].cycles = 0;
timers[dev].channels[i].remainder = 0;
}
timer_start(dev);
return 0;
}
int IRAM timer_set(tim_t dev, int chn, unsigned int delta)
{
DEBUG("%s dev=%u channel=%d delta=%u\n", __func__, dev, chn, delta);
CHECK_PARAM_RET (dev < HW_TIMER_NUMOF, -1);
CHECK_PARAM_RET (chn < HW_TIMER_CHANNELS, -1);
int state = irq_disable ();
struct hw_timer_t* timer = &timers[dev];
struct hw_channel_t* channel = &timer->channels[chn];
/* set delta time and channel used flag */
channel->delta_time = delta > HW_TIMER_CORRECTION ? delta - HW_TIMER_CORRECTION : 0;
channel->used = true;
/* start channel with new delta time */
__timer_channel_start (timer, channel);
irq_restore (state);
return 0;
}
int IRAM timer_set_absolute(tim_t dev, int chn, unsigned int value)
{
DEBUG("%s dev=%u channel=%d value=%u\n", __func__, dev, chn, value);
return timer_set (dev, chn, value - timer_read(dev));
}
int timer_clear(tim_t dev, int chn)
{
DEBUG("%s dev=%u channel=%d\n", __func__, dev, chn);
CHECK_PARAM_RET (dev < HW_TIMER_NUMOF, -1);
CHECK_PARAM_RET (chn < HW_TIMER_CHANNELS, -1);
int state = irq_disable ();
/* stop running timer channel */
__timer_channel_stop (&timers[dev], &timers[dev].channels[chn]);
irq_restore (state);
return 0;
}
unsigned int IRAM timer_read(tim_t dev)
{
(void)dev;
return system_get_time ();
}
void IRAM timer_start(tim_t dev)
{
DEBUG("%s dev=%u @%u\n", __func__, dev, system_get_time());
CHECK_PARAM (dev < HW_TIMER_NUMOF);
CHECK_PARAM (!timers[dev].started);
int state = irq_disable ();
__hw_timer_ticks_max = US_TO_HW_TIMER_TICKS(HW_TIMER_DELTA_MAX);
__hw_timer_ticks_min = US_TO_HW_TIMER_TICKS(HW_TIMER_DELTA_MIN);
struct hw_timer_t* timer = &timers[dev];
timer->started = true;
for (int i = 0; i < HW_TIMER_CHANNELS; i++) {
__timer_channel_start (timer, &timer->channels[i]);
}
irq_restore (state);
}
void IRAM timer_stop(tim_t dev)
{
DEBUG("%s dev=%u\n", __func__, dev);
CHECK_PARAM (dev < HW_TIMER_NUMOF);
int state = irq_disable ();
struct hw_timer_t* timer = &timers[dev];
timer->started = false;
for (int i = 0; i < HW_TIMER_CHANNELS; i++) {
__timer_channel_stop (timer, &timer->channels[i]);
}
irq_restore (state);
}
static void IRAM __timer_channel_start (struct hw_timer_t* timer, struct hw_channel_t* channel)
{
if (!timer->started || !channel->used) {
return;
}
/* save channel starting time */
channel->start_time = timer_read (0);
channel->cycles = channel->delta_time >> HW_TIMER_DELTA_RSHIFT;
channel->remainder = channel->delta_time & HW_TIMER_DELTA_MASK;
DEBUG("%s cycles=%u remainder=%u @%u\n",
__func__, channel->cycles, channel->remainder, system_get_time());
/* start timer either with full cycles, remaining or minimum time */
if (channel->cycles) {
channel->cycles--;
xthal_set_ccompare(timer->dev, xthal_get_ccount() + __hw_timer_ticks_max);
}
else if (channel->remainder > HW_TIMER_DELTA_MIN) {
xthal_set_ccompare(timer->dev, xthal_get_ccount() +
US_TO_HW_TIMER_TICKS(channel->remainder));
channel->remainder = 0;
}
else {
channel->remainder = 0;
xthal_set_ccompare(timer->dev, xthal_get_ccount() + __hw_timer_ticks_min);
}
xt_ints_on(BIT(timers_int[timer->dev]));
}
static void IRAM __timer_channel_stop (struct hw_timer_t* timer, struct hw_channel_t* channel)
{
if (!channel->used) {
return;
}
xt_ints_off(BIT(timers_int[timer->dev]));
/* compute elapsed time */
uint32_t elapsed_time = timer_read (0) - channel->start_time;
if (channel->delta_time > elapsed_time) {
/* compute new delta time if the timer has no been expired */
channel->delta_time -= elapsed_time;
}
else {
/* otherwise deactivate the channel */
channel->used = false;
}
}
#endif /* MODULE_ESP_HW_COUNTER */

341
cpu/esp32/periph/uart.c Normal file
View File

@ -0,0 +1,341 @@
/*
* 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_esp32
* @ingroup drivers_periph_uart
* @{
*
* @file
* @brief Low-level UART driver implementation
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
#define ENABLE_DEBUG (0)
#include "debug.h"
#include "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 "gpio_arch.h"
#include "driver/periph_ctrl.h"
#include "esp/common_macros.h"
#include "rom/ets_sys.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"
#include "xtensa/xtensa_api.h"
#undef UART_CLK_FREQ
#define UART_CLK_FREQ rtc_clk_apb_freq_get() /* APB_CLK is used */
struct uart_hw_t {
uart_dev_t* regs; /* pointer to register data struct of the UART device */
uint8_t pin_txd; /* TxD pin */
uint8_t pin_rxd; /* RxD pin */
uint8_t signal_txd; /* TxD signal from the controller */
uint8_t signal_rxd; /* RxD signal to the controller */
uint32_t baudrate; /* used baudrate */
bool used; /* indicates whether UART is used */
uint8_t int_src; /* peripheral interrupt source used by the UART device */
uart_isr_ctx_t isr_ctx; /* callback functions */
};
/* hardware ressources */
static struct uart_hw_t __uarts[] = {
{
.regs = &UART0,
.pin_txd = GPIO1,
.pin_rxd = GPIO3,
.signal_txd = U0TXD_OUT_IDX,
.signal_rxd = U0RXD_IN_IDX,
.baudrate = STDIO_UART_BAUDRATE,
.used = false,
.int_src = ETS_UART0_INTR_SOURCE
},
#if defined(UART1_TXD) && defined(UART1_RXD)
{ .regs = &UART1,
.pin_txd = UART1_TXD,
.pin_rxd = UART1_RXD,
.signal_txd = U1TXD_OUT_IDX,
.signal_rxd = U1RXD_IN_IDX,
.baudrate = STDIO_UART_BAUDRATE,
.used = false,
.int_src = ETS_UART1_INTR_SOURCE
},
#endif
#if defined(UART2_TXD) && defined(UART2_RXD)
{ .regs = &UART2,
.pin_txd = UART2_TXD,
.pin_rxd = UART2_RXD,
.signal_txd = U2TXD_OUT_IDX,
.signal_rxd = U2RXD_IN_IDX,
.baudrate = STDIO_UART_BAUDRATE,
.used = false,
.int_src = ETS_UART2_INTR_SOURCE
}
#endif
};
/* declaration of external functions */
extern void uart_div_modify(uint8_t uart_no, uint32_t div);
/* forward declaration of internal functions */
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);
void __uart_config (uart_t uart)
{
CHECK_PARAM (uart < UART_NUMOF);
/* 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 {
/* for UART2, we have to control it by registers */
__uarts[uart].regs->conf0.tick_ref_always_on = 1; /* use APB_CLK */
/* compute and set the integral and the decimal part */
uint32_t clk = (UART_CLK_FREQ << 4) / __uarts[uart].baudrate;
__uarts[uart].regs->clk_div.div_int = clk >> 4;
__uarts[uart].regs->clk_div.div_frag = clk & 0xf;
}
/* set 8 data bits */
__uarts[uart].regs->conf0.bit_num = 3;
/* 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);
/* route all UART interrupt sources to same the CPU interrupt */
intr_matrix_set(PRO_CPU_NUM, __uarts[uart].int_src, CPU_INUM_UART);
/* 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));
}
}
int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg)
{
DEBUG("%s uart=%d, rate=%d, rx_cb=%p, arg=%p\n", __func__, uart, baudrate, rx_cb, arg);
CHECK_PARAM_RET (uart < UART_NUMOF, -1);
/* 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;
}
__uarts[uart].baudrate = baudrate;
/* register interrupt context */
__uarts[uart].isr_ctx.rx_cb = rx_cb;
__uarts[uart].isr_ctx.arg = arg;
/* enable and configure the according UART module */
uart_poweron(uart);
return UART_OK;
}
void uart_write(uart_t uart, const uint8_t *data, size_t len)
{
CHECK_PARAM (uart < UART_NUMOF);
for (size_t i = 0; i < len; i++) {
__uart_tx_one_char(uart, data[i]);
}
}
void uart_poweron (uart_t uart)
{
switch (uart) {
#if UART_NUMOF
case 0: periph_module_enable(PERIPH_UART0_MODULE);
__uart_config(uart);
break;
#endif
#if UART_NUMOF > 1
case 1: periph_module_enable(PERIPH_UART1_MODULE);
__uart_config(uart);
break;
#endif
#if UART_NUMOF > 2
case 2: periph_module_enable(PERIPH_UART2_MODULE);
__uart_config(uart);
break;
#endif
default: break;
}
}
void uart_poweroff (uart_t uart)
{
switch (uart) {
#if UART_NUMOF
case 0: periph_module_disable(PERIPH_UART0_MODULE); break;
#endif
#if UART_NUMOF > 1
case 1: periph_module_disable(PERIPH_UART1_MODULE); break;
#endif
#if UART_NUMOF > 2
case 2: periph_module_disable(PERIPH_UART2_MODULE); break;
#endif
default: break;
}
}
void IRAM __uart_intr_handler (void *arg)
{
/* to satisfy the compiler */
(void)arg;
irq_isr_enter ();
/* 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);
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;
}
/* TODO handle other types of interrupts, for the moment just clear them */
__uarts[uart].regs->int_clr.val = ~0x0;
}
}
irq_isr_exit ();
}
/* RX/TX FIFO capacity is 128 byte */
#define UART_FIFO_MAX 127
/* receive one data byte with wait */
static uint8_t IRAM __uart_rx_one_char (uart_t uart)
{
/* 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;
}
/* send one data byte with wait */
static void __uart_tx_one_char(uart_t uart, uint8_t data)
{
/* wait until at least one byte is avaiable in the TX FIFO */
while (__uarts[uart].regs->status.txfifo_cnt >= UART_FIFO_MAX) {}
/* send the byte by placing it in the TX FIFO using MPU */
WRITE_PERI_REG(UART_FIFO_AHB_REG(uart), data);
}
static void __uart_intr_enable(uart_t uart)
{
__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__, __uarts[uart].regs->int_ena.val);
}
/* 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++) {
ets_printf("\tUART_DEV(%d)\ttxd=%d rxd=%d\n", uart,
__uarts[uart].pin_txd, __uarts[uart].pin_rxd);
}
}
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);
/* use APB_CLK */
__uarts[uart].regs->conf0.tick_ref_always_on = 1;
/* compute and set the integral and the decimal part */
uint32_t clk = (UART_CLK_FREQ << 4) / baudrate;
__uarts[uart].regs->clk_div.div_int = clk >> 4;
__uarts[uart].regs->clk_div.div_frag = clk & 0xf;
return UART_OK;
}

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

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

367
cpu/esp32/startup.c Normal file
View File

@ -0,0 +1,367 @@
/*
* 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_esp32
* @{
*
* @file
* @brief Implementation of the CPU initialization
*
* @author Gunar Schorcht <gunar@schorcht.net>
* @}
*/
#define ENABLE_DEBUG (0)
#include "debug.h"
#include "esp_common.h"
#include <stdlib.h>
#include <string.h>
#include <sys/reent.h>
#include "board.h"
#include "esp_attr.h"
#include "exceptions.h"
#include "irq_arch.h"
#include "kernel_defines.h"
#include "kernel_init.h"
#include "log.h"
#include "syscalls.h"
#include "thread_arch.h"
#include "periph/cpuid.h"
#include "periph/init.h"
#include "periph/rtc.h"
#include "driver/periph_ctrl.h"
#include "esp/common_macros.h"
#include "heap/esp_heap_caps_init.h"
#include "rom/cache.h"
#include "rom/ets_sys.h"
#include "rom/rtc.h"
#include "rom/uart.h"
#include "soc/apb_ctrl_reg.h"
#include "soc/cpu.h"
#include "soc/dport_reg.h"
#include "soc/dport_access.h"
#include "soc/rtc.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/rtc_cntl_struct.h"
#include "soc/timer_group_struct.h"
#include "xtensa/core-macros.h"
#include "xtensa/xtensa_api.h"
#include "periph_cpu.h"
#include "tools.h"
#ifdef MODULE_STDIO_UART
#include "stdio_uart.h"
#endif
#define MHZ 1000000UL
#define STRINGIFY(s) STRINGIFY2(s)
#define STRINGIFY2(s) #s
/* following variables are defined in linker script */
extern uint8_t _bss_start;
extern uint8_t _bss_end;
extern uint8_t _sheap;
extern uint8_t _eheap;
extern uint8_t _rtc_bss_start;
extern uint8_t _rtc_bss_end;
extern uint8_t _rtc_bss_rtc_start;
extern uint8_t _rtc_bss_rtc_end;
extern uint8_t _init_start;
/* external esp function declarations */
extern void esp_clk_init(void);
extern void esp_perip_clk_init(void);
extern void esp_reent_init(struct _reent* r);
extern void esp_panic_wdt_stop (void);
extern void spi_ram_init(void);
extern void spi_ram_heap_init(void);
extern uint32_t hwrand (void);
/* forward declarations */
static void system_init(void);
static void do_global_ctors(void);
static void intr_matrix_clear(void);
typedef int32_t esp_err_t;
/**
* @brief CPU startup function
*
* This function is the entry point in the user application. It is called
* after a system reset to startup the system.
*/
NORETURN void IRAM call_start_cpu0 (void)
{
register uint32_t *sp __asm__ ("a1"); (void)sp;
cpu_configure_region_protection();
/* move exception vectors to IRAM */
asm volatile ("wsr %0, vecbase\n" ::"r"(&_init_start));
RESET_REASON reset_reason = rtc_get_reset_reason(PRO_CPU_NUM);
/* reset from panic handler by RWDT or TG0WDT */
if (reset_reason == RTCWDT_SYS_RESET || reset_reason == TG0WDT_SYS_RESET) {
esp_panic_wdt_stop();
}
/* Clear BSS. Please do not attempt to do any complex stuff */
/* (like early logging) before this. */
memset(&_bss_start, 0, (&_bss_end - &_bss_start) * sizeof(_bss_start));
/* if we are not waking up from deep sleep, clear RTC bss */
if (reset_reason != DEEPSLEEP_RESET) {
memset(&_rtc_bss_start, 0, (&_rtc_bss_end - &_rtc_bss_start));
}
/* initialize RTC data after power on */
if (reset_reason == POWERON_RESET || reset_reason == RTCWDT_RTC_RESET) {
memset(&_rtc_bss_rtc_start, 0, (&_rtc_bss_rtc_end - &_rtc_bss_rtc_start));
}
uint8_t cpu_id[CPUID_LEN];
cpuid_get ((void*)cpu_id);
ets_printf("\nStarting ESP32 with ID: ");
for (unsigned i = 0; i < CPUID_LEN; i++) {
ets_printf("%02x", cpu_id[i]);
}
ets_printf("\n\nCurrent clocks in Hz: CPU=%d APB=%d XTAL=%d SLOW=%d\n",
rtc_clk_cpu_freq_value(rtc_clk_cpu_freq_get()),
rtc_clk_apb_freq_get(), rtc_clk_xtal_freq_get()*MHZ,
rtc_clk_slow_freq_get_hz());
#if ENABLE_DEBUG
ets_printf("reset reason: %d\n", reset_reason);
ets_printf("_stack %p\n", sp);
ets_printf("_bss_start %p\n", &_bss_start);
ets_printf("_bss_end %p\n", &_bss_end);
#ifndef MODULE_ESP_IDF_HEAP
ets_printf("_heap_start %p\n", &_sheap);
ets_printf("_heap_end %p\n", &_eheap);
ets_printf("_heap_free %u\n", get_free_heap_size());
#endif /* MODULE_ESP_IDF_HEAP */
#endif /* ENABLE_DEBUG */
ets_printf("PRO cpu is up ");
/* disable APP cpu */
ets_printf("(single core mode, only PRO cpu is used)\n");
DPORT_CLEAR_PERI_REG_MASK(DPORT_APPCPU_CTRL_B_REG, DPORT_APPCPU_CLKGATE_EN);
#ifdef MODULE_ESP_IDF_HEAP
/* init heap */
heap_caps_init();
#ifdef ENABLE_DEBUG
ets_printf("Heap free: %u byte\n", get_free_heap_size());
#endif /* ENABLE_DEBUG */
#endif /* MODULE_ESP_IDF_HEAP */
/* init SPI RAM if enabled */
#if CONFIG_SPIRAM_SUPPORT && CONFIG_SPIRAM_BOOT_INIT
spi_ram_init();
#endif
ets_printf("PRO cpu starts user code\n");
system_init();
UNREACHABLE();
}
#define RTC_FAST_FREQ_8M_MHZ 8000000
#define rtc_select_slow_clk select_rtc_slow_clk
extern uint32_t esp_clk_slowclk_cal_get(void);
extern void IRAM_ATTR rtc_select_slow_clk(rtc_slow_freq_t slow_clk);
static void IRAM system_clk_init (void)
{
/* first initialize RTC with default configuration */
rtc_config_t rtc_cfg = RTC_CONFIG_DEFAULT();
rtc_init_module(rtc_cfg);
/* set FAST_CLK to internal low power clock of 8 MHz */
rtc_clk_fast_freq_set(RTC_FAST_FREQ_8M);
/* set SLOW_CLK to internal low power clock of 150 kHz */
rtc_select_slow_clk(RTC_SLOW_FREQ_RTC);
/* wait until UART is idle to avoid loosing output */
uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM);
ets_printf("Switching system clocks can lead to some unreadable characters\n");
ets_printf("This message is usually not visible at the console\n");
/* determine configured CPU clock frequency from sdk_conf.h */
rtc_cpu_freq_t freq;
switch (CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ) {
case 40: freq = RTC_CPU_FREQ_XTAL; /* derived from external cristal */
break; /* normally 40 MHz */
case 80: freq = RTC_CPU_FREQ_80M; /* derived from PLL */
break;
case 160: freq = RTC_CPU_FREQ_160M; /* derived from PLL */
break;
case 240: freq = RTC_CPU_FREQ_240M; /* derived from PLL */
break;
default: freq = RTC_CPU_FREQ_2M; /* frequencies <= 8 MHz are
set to 2 MHz and handled later */
}
uint32_t freq_before = rtc_clk_cpu_freq_value(rtc_clk_cpu_freq_get()) / MHZ ;
/* set configured CPU frequency */
rtc_clk_cpu_freq_set(freq);
/* Recalculate the ccount to make time calculation correct. */
uint32_t freq_after = CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ;
XTHAL_SET_CCOUNT( XTHAL_GET_CCOUNT() * freq_after / freq_before );
}
extern void IRAM_ATTR thread_yield_isr(void* arg);
static NORETURN void IRAM system_init (void)
{
/* enable cached read from flash */
Cache_Read_Enable(PRO_CPU_NUM);
/* initialize the ISR stack for usage measurements */
thread_isr_stack_init();
/* initialize clocks (CPU_CLK, APB_CLK, SLOW and FAST) */
system_clk_init();
/* disable clocks of peripherals that are not needed at startup */
esp_perip_clk_init();
/* set configured console UART baudrate */
const int uart_clk_freq = rtc_clk_apb_freq_get();
uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM);
uart_div_modify(CONFIG_CONSOLE_UART_NUM,
(uart_clk_freq << 4) / STDIO_UART_BAUDRATE);
/* initialize system call tables of ESP32 rom and newlib */
syscalls_init();
/* initialize the RTC module (restore timer values from RTC RAM) */
rtc_init();
/* install execption handlers */
init_exceptions();
/* clear interrupt matrix */
intr_matrix_clear();
/* systemwide UART initialization */
extern void uart_system_init (void);
uart_system_init();
/* Disable the hold flag of all RTC GPIO pins */
RTCCNTL.hold_force.val = 0;
/* initialize newlib data structure */
esp_reent_init(_GLOBAL_REENT);
_GLOBAL_REENT->_stdin = (FILE*) &__sf_fake_stdin;
_GLOBAL_REENT->_stdout = (FILE*) &__sf_fake_stdout;
_GLOBAL_REENT->_stderr = (FILE*) &__sf_fake_stderr;
/* execute constructors */
do_global_ctors();
/* init watchdogs */
system_wdt_init();
/* init random number generator */
srand(hwrand());
#if defined(MODULE_NEWLIB_SYSCALLS_DEFAULT)
/*
* initialization as it should be called from newlibc (includes the
* execution of stdio_init)
*/
extern void _init(void);
_init();
#elif defined(MODULE_STDIO_UART)
stdio_init();
#endif
/* add SPI RAM to heap if enabled */
#if CONFIG_SPIRAM_SUPPORT && CONFIG_SPIRAM_BOOT_INIT
spi_ram_heap_init();
#endif
/* print some infos */
ets_printf("Used clocks in Hz: CPU=%d APB=%d XTAL=%d FAST=%d SLOW=%d\n",
rtc_clk_cpu_freq_value(rtc_clk_cpu_freq_get()),
rtc_clk_apb_freq_get(), rtc_clk_xtal_freq_get()*MHZ,
RTC_FAST_FREQ_8M_MHZ, rtc_clk_slow_freq_get_hz());
ets_printf("XTAL calibration value: %d\n", esp_clk_slowclk_cal_get());
ets_printf("Heap free: %u bytes\n", get_free_heap_size());
struct tm _sys_time;
rtc_get_time(&_sys_time);
ets_printf("System time: %04d-%02d-%02d %02d:%02d:%02d\n",
_sys_time.tm_year + 1900, _sys_time.tm_mon + 1, _sys_time.tm_mday,
_sys_time.tm_hour, _sys_time.tm_min, _sys_time.tm_sec);
/* init flash drive */
extern void spi_flash_drive_init (void);
spi_flash_drive_init();
/* initialize the board */
board_init();
/* trigger static peripheral initialization */
periph_init();
/* print the board config */
print_board_config();
/* route a software interrupt source to CPU as trigger for thread yields */
intr_matrix_set(PRO_CPU_NUM, ETS_FROM_CPU_INTR0_SOURCE, CPU_INUM_SOFTWARE);
/* set thread yield handler and enable the software interrupt */
xt_set_interrupt_handler(CPU_INUM_SOFTWARE, thread_yield_isr, NULL);
xt_ints_on(BIT(CPU_INUM_SOFTWARE));
/* initialize ESP system event loop */
extern void esp_event_handler_init(void);
esp_event_handler_init();
/* starting RIOT */
ets_printf("Starting RIOT kernel on PRO cpu\n");
kernel_init();
UNREACHABLE();
}
static void do_global_ctors(void)
{
#if 0 /* TODO when real ctors are used exist */
extern uint32_t* __init_array_start;
extern uint32_t* __init_array_end;
for (uint32_t* up = __init_array_end - 1; up >= __init_array_start; --up) {
void (*fp)(void) = (void (*)(void))up;
fp();
}
#endif
}
static void intr_matrix_clear(void)
{
/* attach all peripheral interrupt sources (Technical Reference, Table 7) */
/* to an arbitrary CPU interrupt number (Technical Reference, Table 8) */
for (int i = ETS_WIFI_MAC_INTR_SOURCE; i <= ETS_CACHE_IA_INTR_SOURCE; i++) {
intr_matrix_set(PRO_CPU_NUM, i, ETS_INVALID_INUM);
}
}

581
cpu/esp32/syscalls.c Normal file
View File

@ -0,0 +1,581 @@
/*
* 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_esp32
* @{
*
* @file
* @brief Implementation of required system calls
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
#define ENABLE_DEBUG (0)
#include "debug.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/errno.h>
#include <sys/reent.h>
#include <sys/signal.h>
#include <sys/times.h>
#include <sys/unistd.h>
#include "esp_common.h"
#include "cpu_conf.h"
#include "irq.h"
#include "irq_arch.h"
#include "kernel_defines.h"
#include "log.h"
#include "mutex.h"
#include "rmutex.h"
#include "sched.h"
#include "periph/pm.h"
#include "timex.h"
#include "esp_attr.h"
#include "esp/xtensa_ops.h"
#include "esp/common_macros.h"
#include "rom/ets_sys.h"
#include "rom/libc_stubs.h"
#include "soc/rtc.h"
#include "soc/rtc_cntl_struct.h"
#include "soc/timer_group_reg.h"
#include "soc/timer_group_struct.h"
#include "xtensa/xtensa_api.h"
#include "periph_cpu.h"
#include "syscalls.h"
#ifdef MODULE_ESP_IDF_HEAP
#include "heap/esp_heap_caps.h"
#endif
#define MHZ 1000000UL
#ifdef MODULE_STDIO_UART
#include "stdio_uart.h"
int IRAM putchar(int c)
{
char tmp = c;
if (stdio_write(&tmp, 1) > 0) {
return c;
}
return -EOF;
}
int IRAM getchar(void)
{
char tmp;
if (stdio_read(&tmp, 1) > 0) {
return tmp;
}
return -EOF;
}
#endif /* MODULE_STDIO_UART */
int IRAM puts(const char *s)
{
if (!s) {
return EOF;
}
ets_printf("%s\n", s);
return strlen(s);
}
char _printf_buf[PRINTF_BUFSIZ];
int IRAM printf(const char* format, ...)
{
va_list arglist;
va_start(arglist, format);
int ret = vsnprintf(_printf_buf, PRINTF_BUFSIZ, format, arglist);
if (ret > 0) {
ets_printf (_printf_buf);
}
va_end(arglist);
return ret;
}
/**
* @name Locking functions
*
* Following function implements the lock mechanism in newlib. The only static
* mutex defined here is the _malloc_rmtx to avoid that memory management
* functions try to lock before RIOT's threads are running. All other mutexes
* are allocated dynamically.
*/
static rmutex_t _malloc_rmtx = RMUTEX_INIT;
void IRAM _lock_init(_lock_t *lock)
{
CHECK_PARAM (sched_active_thread != 0);
CHECK_PARAM (lock != NULL);
CHECK_PARAM (*lock != ((_lock_t)&_malloc_rmtx));
mutex_t* mtx = malloc (sizeof(mutex_t));
if (mtx) {
memset (mtx, 0, sizeof(mutex_t));
*lock = (_lock_t)mtx;
}
}
void IRAM _lock_init_recursive(_lock_t *lock)
{
CHECK_PARAM (sched_active_thread != 0);
CHECK_PARAM (lock != NULL);
CHECK_PARAM (*lock != ((_lock_t)&_malloc_rmtx));
rmutex_t* rmtx = malloc (sizeof(rmutex_t));
if (rmtx) {
memset (rmtx, 0, sizeof(rmutex_t));
*lock = (_lock_t)rmtx;
}
}
void IRAM _lock_close(_lock_t *lock)
{
CHECK_PARAM (lock != NULL);
CHECK_PARAM (*lock != ((_lock_t)&_malloc_rmtx));
free ((void*)*lock);
*lock = 0;
}
void IRAM _lock_close_recursive(_lock_t *lock)
{
CHECK_PARAM (lock != NULL);
CHECK_PARAM (*lock != ((_lock_t)&_malloc_rmtx));
free ((void*)*lock);
*lock = 0;
}
void IRAM _lock_acquire(_lock_t *lock)
{
CHECK_PARAM (sched_active_thread != 0);
CHECK_PARAM (lock != NULL && *lock != 0);
mutex_lock ((mutex_t*)*lock);
}
void IRAM _lock_acquire_recursive(_lock_t *lock)
{
CHECK_PARAM (sched_active_thread != 0);
CHECK_PARAM (lock != NULL && *lock != 0);
rmutex_lock ((rmutex_t*)*lock);
}
int IRAM _lock_try_acquire(_lock_t *lock)
{
CHECK_PARAM_RET (sched_active_thread != 0, 0);
CHECK_PARAM_RET (lock != NULL && *lock != 0, 0);
return rmutex_trylock ((rmutex_t*)*lock);
}
int IRAM _lock_try_acquire_recursive(_lock_t *lock)
{
CHECK_PARAM_RET (sched_active_thread != 0, 0);
CHECK_PARAM_RET (lock != NULL && *lock != 0, 0);
return mutex_trylock ((mutex_t*)*lock);
}
void IRAM _lock_release(_lock_t *lock)
{
CHECK_PARAM (sched_active_thread != 0);
CHECK_PARAM (lock != NULL && *lock != 0);
mutex_unlock ((mutex_t*)*lock);
}
void IRAM _lock_release_recursive(_lock_t *lock)
{
CHECK_PARAM (sched_active_thread != 0);
CHECK_PARAM (lock != NULL && *lock != 0);
rmutex_unlock ((rmutex_t*)*lock);
}
/**
* @name Memory allocation functions
*/
#ifdef MODULE_ESP_IDF_HEAP
extern void *heap_caps_malloc_default( size_t size );
extern void *heap_caps_realloc_default( void *ptr, size_t size );
void* IRAM_ATTR _malloc_r(struct _reent *r, size_t size)
{
return heap_caps_malloc_default( size );
}
void IRAM_ATTR _free_r(struct _reent *r, void* ptr)
{
heap_caps_free( ptr );
}
void* IRAM_ATTR _realloc_r(struct _reent *r, void* ptr, size_t size)
{
return heap_caps_realloc_default( ptr, size );
}
void* IRAM_ATTR _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;
}
#ifndef MODULE_NEWLIB_SYSCALLS_DEFAULT
/* this should not happen when MODULE_ESP_IDF_HEAP is activated since heap_caps
doesn't use _sbrk_r to allocate memory blocks */
void* _sbrk_r (struct _reent *r, ptrdiff_t sz)
{
_exit(ENOSYS);
}
#endif /* MODULE_NEWLIB_SYSCALLS_DEFAULT */
#else /* MODULE_ESP_IDF_HEAP */
/* for compatibiliy with ESP-IDF heap functions */
void* IRAM heap_caps_malloc( size_t size, uint32_t caps )
{
(void)caps;
return malloc(size);
}
void* IRAM heap_caps_calloc( size_t n, size_t size, uint32_t caps)
{
(void)caps;
return calloc(n, size);
}
void* IRAM heap_caps_realloc( void *ptr, size_t size )
{
return realloc(ptr, size);
}
extern uint8_t _eheap; /* end of heap (defined in esp32.common.ld) */
extern uint8_t _sheap; /* start of heap (defined in esp32.common.ld) */
#ifdef MODULE_NEWLIB_SYSCALLS_DEFAULT
extern uint8_t *heap_top;
#define _cheap heap_top
#else /* MODULE_NEWLIB_SYSCALLS_DEFAULT */
static uint8_t* _cheap = 0; /* last allocated chunk of heap */
void* IRAM _sbrk_r (struct _reent *r, ptrdiff_t incr)
{
uint8_t* _cheap_old;
/* initial _cheap */
if (_cheap == NULL) {
_cheap = &_sheap;
}
/* save old _cheap */
_cheap_old = _cheap;
/* check whether _cheap + incr overflows the heap */
if (_cheap + incr >= &_eheap) {
r->_errno = ENOMEM;
return (caddr_t)-1;
}
/* set new _cheap */
_cheap += incr;
#if ENABLE_DEBUG
uint32_t remaining = &_eheap - _cheap;
printf ("%s %i byte allocated in %p .. %p, remaining %u\n",
__func__, incr, _cheap_old, _cheap, remaining);
#endif
/* return allocated memory */
return (caddr_t) _cheap_old;
}
#endif /* MODULE_NEWLIB_SYSCALLS_DEFAULT */
#endif /* MODULE_ESP_IDF_HEAP */
unsigned int IRAM get_free_heap_size (void)
{
#if MODULE_ESP_IDF_HEAP
return heap_caps_get_free_size( MALLOC_CAP_DEFAULT );
#else
return &_eheap - ((_cheap) ? _cheap : &_sheap);
#endif
}
/* alias for compatibility with espressif/wifi_libs */
uint32_t esp_get_free_heap_size( void ) __attribute__((alias("get_free_heap_size")));
/**
* @name Other system functions
*/
#ifndef MODULE_NEWLIB_SYSCALLS_DEFAULT
int _getpid_r(struct _reent *r)
{
return sched_active_pid;
}
int _kill_r(struct _reent *r, int pid, int sig)
{
DEBUG("%s: system function not yet implemented\n", __func__);
r->_errno = ESRCH; /* no such process */
return -1;
}
void _exit(int __status)
{
ets_printf("#! exit %d: powering off\n", __status);
pm_off();
while(1);
}
clock_t IRAM_ATTR _times_r(struct _reent *r, struct tms *ptms)
{
ptms->tms_cstime = 0;
ptms->tms_cutime = 0;
ptms->tms_stime = system_get_time() / (US_PER_SEC / CLK_TCK);
ptms->tms_utime = 0;
return ptms->tms_stime / MHZ;
}
#endif /* MODULE_NEWLIB_SYSCALLS_DEFAULT */
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;
}
static int _no_sys_func (struct _reent *r)
{
DEBUG("%s: system function does not exist\n", __func__);
r->_errno = ENOSYS;
return -1;
}
static struct _reent s_reent;
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,
#ifdef MODULE_NEWLIB_SYSCALLS_DEFAULT
._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,
#else /* MODULE_NEWLIB_SYSCALLS_DEFAULT */
._gettimeofday_r = (int (*)(struct _reent *r, struct timeval *, void *))&_no_sys_func,
._open_r = (int (*)(struct _reent *r, const char *, int, int))&_no_sys_func,
._close_r = (int (*)(struct _reent *r, int))&_no_sys_func,
._lseek_r = (int (*)(struct _reent *r, int, int, int))&_no_sys_func,
._fstat_r = (int (*)(struct _reent *r, int, struct stat *))&_no_sys_func,
._stat_r = (int (*)(struct _reent *r, const char*, struct stat *))&_no_sys_func,
._write_r = (int (*)(struct _reent *r, int, const void *, int))&_no_sys_func,
._read_r = (int (*)(struct _reent *r, int, void *, int))&_no_sys_func,
._unlink_r = (int (*)(struct _reent *r, const char*))&_no_sys_func,
#endif /* MODULE_NEWLIB_SYSCALLS_DEFAULT */
._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 */
};
void IRAM syscalls_init (void)
{
/* 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;
_GLOBAL_REENT = &s_reent;
environ = malloc(sizeof(char*));
environ[0] = NULL;
}
uint32_t system_get_time (void)
{
/* latch 64 bit timer value before read */
TIMER_SYSTEM.update = 0;
/* wait until instructions have been finished */
__asm__ volatile ("isync");
return TIMER_SYSTEM.cnt_low;
}
uint32_t system_get_time_ms (void)
{
/* latch 64 bit timer value before read */
TIMER_SYSTEM.update = 0;
/* wait until instructions have been finished */
__asm__ volatile ("isync");
return TIMER_SYSTEM.cnt_low / USEC_PER_MSEC;
}
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 lenght: 3.2 us */
TIMERG0.wdt_config0.cpu_reset_length = 7; /* sys reset signal lenght: 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));
}

382
cpu/esp32/thread_arch.c Normal file
View File

@ -0,0 +1,382 @@
/*
* 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_esp32
* @{
*
* @file
* @brief Implementation of the kernel's architecture dependent thread interface
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
/*
* PLEASE NOTE: Some parts of the code are taken from the FreeRTOS port for
* Xtensa processors from Cadence Design Systems. These parts are marked
* accordingly. For these parts, the following license is valid:
*
* Copyright (c) 2003-2015 Cadence Design Systems, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#define ENABLE_DEBUG (0)
#include "debug.h"
#include <stdio.h>
#include <string.h>
#include "board.h"
#include "cpu.h"
#include "irq.h"
#include "log.h"
#include "thread.h"
#include "sched.h"
#include "esp_common.h"
#include "irq_arch.h"
#include "syscalls.h"
#include "tools.h"
#include "esp/xtensa_ops.h"
#include "rom/ets_sys.h"
#include "soc/dport_reg.h"
#include "xtensa/xtensa_context.h"
/* User exception dispatcher when exiting */
extern void _xt_user_exit(void);
/* Switch context to the highest priority ready task without context save */
extern void _frxt_dispatch(void);
/* Set an flag indicating that a task switch is required on return from interrupt */
extern void _frxt_setup_switch(void);
/* Switch context to the highest priority ready task with context save */
extern void vPortYield(void);
extern void vPortYieldFromInt(void);
/* forward declarations */
NORETURN void task_exit(void);
char* thread_stack_init(thread_task_func_t task_func, void *arg, void *stack_start, int stack_size)
{
/* Stack layout after task stack initialization
*
* +------------------------+
* | | TOP
* | thread_control_block |
* stack_start + stack_size ==> | | top_of_stack+1
* +------------------------+
* top_of_stack ==> | |
* | XT_CP_SA |
* | (optional) |
* | ... | ...
* | cpstored | XT_CPSTORED
* top_of_stack + 1 - XT_CP_SIZE ==> | cpenable | XT_CPENABLE
* (cp_state) +------------------------+
* | |
* | XT_STK_FRAME |
* | | XT_STK_...
* | a2 = arg | XT_STK_A2
* | a1 = sp + XT_STK_FRMSZ | XT_STK_A1
* | a0 = sched_task_exit | XT_STK_A0
* | ps = PS_UM | PS_EXCM | XT_STK_PS
* | pc = task_func | XT_STK_PC
* sp = top_of_stack + 1 - XT_CP_SIZE ==> | exit = _xt_user_exit | XT_STK_EXIT
* - XT_STK_FRMSZ +------------------------+
* | |
* | remaining stack space |
* | available for data |
* stack_start (preallocated var) ==> | | BOTTOM
* +------------------------+
*
* Initialized stack frame represents the registers as set when the
* the task function would have been called.
*
* Registers in a called function
*
* pc - PC at the beginning in the function
* a0 - return address from the function (return address to caller)
* a1 - current stack pointer at the beginning in the function
* a2 - first argument of the function
*/
/* stack is [stack_start+0 ... stack_start+stack_size-1] */
uint8_t *top_of_stack;
uint8_t *sp;
top_of_stack = (uint8_t*)((uint32_t)stack_start + stack_size-1);
/* 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);
/* 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);
#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 for call0 ABI */
exc_frame->pc = (uint32_t)task_func; /* task entry point */
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->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__
/* 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 Register ABI set PS.CALLINC=01 to handle task entry as
call4 return address in a4 and parameter in a6 and */
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
/* Set the initial virtual priority mask value to all 1's. */
exc_frame->vpri = 0xFFFFFFFF;
#endif
#if XCHAL_CP_NUM > 0
/* Init the coprocessor save area (see xtensa_context.h) */
/* No access to TCB here, so derive indirectly. Stack growth is top to bottom. */
/* p = (uint32_t *) xMPUSettings->coproc_area; */
uint32_t *p;
p = (uint32_t *)(((uint32_t) 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;
#endif
/* END - code from FreeRTOS port for Xtensa from Cadence */
DEBUG("%s start=%p size=%d top=%p sp=%p free=%u\n",
__func__, stack_start, stack_size, top_of_stack, sp, sp-(uint8_t*)stack_start);
return (char*)sp;
}
/**
* 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)
{
/* 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();
}
/**
* 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 thread_yield_higher(void)
{
/* reset hardware watchdog */
system_wdt_feed();
/* yield next task */
#if defined(ENABLE_DEBUG) && defined(DEVELHELP)
if (sched_active_thread) {
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()) {
/* 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 {
/* 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", system_get_time(),
sched_active_thread->pid, sched_active_thread->name,
sched_active_thread->sp - sched_active_thread-> stack_start);
}
#endif
return;
}
void thread_stack_print(void)
{
/* Print the current stack to stdout. */
#if defined(DEVELHELP)
volatile thread_t* task = thread_get(sched_active_pid);
if (task) {
char* stack_top = task->stack_start + task->stack_size;
int size = stack_top - task->sp;
printf("Printing current stack of thread %" PRIkernel_pid "\n", thread_getpid());
esp_hexdump((void*)(task->sp), size >> 2, 'w', 8);
}
#else
NOT_SUPPORTED();
#endif
}
void thread_print_stack(void)
{
/* Prints human readable, ps-like thread information for debugging purposes. */
/* because of Xtensa stack structure and call ABI, it is not possible to implement */
NOT_YET_IMPLEMENTED();
return;
}
#ifdef DEVELHELP
extern uint8_t port_IntStack;
extern uint8_t port_IntStackTop;
void thread_isr_stack_init(void)
{
/* code from thread.c, please see the copyright notice there */
/* assign each int of the stack the value of it's address */
uintptr_t *stackmax = (uintptr_t *)&port_IntStackTop;
uintptr_t *stackp = (uintptr_t *)&port_IntStack;
while (stackp < stackmax) {
*stackp = (uintptr_t) stackp;
stackp++;
}
}
int thread_isr_stack_usage(void)
{
return &port_IntStackTop - &port_IntStack -
thread_measure_stack_free((char*)&port_IntStack);
}
void *thread_isr_stack_pointer(void)
{
/* Get the current ISR stack pointer. */
return &port_IntStackTop;
}
void *thread_isr_stack_start(void)
{
/* Get the start of the ISR stack. */
return &port_IntStack;
}
void thread_isr_stack_print(void)
{
printf("Printing current ISR\n");
esp_hexdump(&port_IntStack, &port_IntStackTop-&port_IntStack, 'w', 8);
}
#else /* DEVELHELP */
void thread_isr_stack_init(void) {}
#endif /* DEVELHELP */
static bool _initial_exit = true;
/**
* 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->pid);
(void) irq_disable();
/* remove old task from scheduling */
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();
}
NORETURN void cpu_switch_context_exit(void)
{
/* Switch context to the highest priority ready task without context save */
if (_initial_exit) {
_initial_exit = false;
__asm__ volatile ("call0 _frxt_dispatch");
}
else {
task_exit();
}
UNREACHABLE();
}

60
cpu/esp32/tools.c Normal file
View File

@ -0,0 +1,60 @@
/*
* 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_esp32
* @{
*
* @file
* @brief Implementation of some tools
*
* @author Gunar Schorcht <gunar@schorcht.net>
*
* @}
*/
#include <stdio.h>
#include "esp/common_macros.h"
#include "rom/ets_sys.h"
#include "tools.h"
void esp_hexdump (const void* addr, uint32_t num, char width, uint8_t per_line)
{
uint32_t count = 0;
uint32_t size;
uint8_t* addr8 = (uint8_t*) addr;
uint16_t* addr16 = (uint16_t*)addr;
uint32_t* addr32 = (uint32_t*)addr;
uint64_t* addr64 = (uint64_t*)addr;
switch (width) {
case 'b': size = 1; break;
case 'h': size = 2; break;
case 'w': size = 4; break;
case 'g': size = 8; break;
default : size = 1; break;
}
while (count < num) {
if (count % per_line == 0) {
ets_printf ("%08" PRIx32 ": ", (uint32_t)((uint8_t*)addr+count*size));
}
switch (width) {
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) {
ets_printf ("\n");
}
}
ets_printf ("\n");
}

View File

@ -201,6 +201,21 @@ void auto_init(void)
auto_init_enc28j60();
#endif
#ifdef MODULE_ESP_ETH
extern void auto_init_esp_eth(void);
auto_init_esp_eth();
#endif
#ifdef MODULE_ESP_NOW
extern void auto_init_esp_now(void);
auto_init_esp_now();
#endif
#ifdef MODULE_ESP_WIFI
extern void auto_init_esp_wifi(void);
auto_init_esp_wifi();
#endif
#ifdef MODULE_ETHOS
extern void auto_init_ethos(void);
auto_init_ethos();

View File

@ -53,4 +53,9 @@ void auto_init_candev(void)
extern void auto_init_can_native(void);
auto_init_can_native();
#endif
#ifdef MODULE_ESP_CAN
extern void auto_init_esp_can(void);
auto_init_esp_can();
#endif
}

View File

@ -862,6 +862,18 @@ int gnrc_netif_ipv6_get_iid(gnrc_netif_t *netif, eui64_t *eui64)
case NETDEV_TYPE_NRFMIN:
_create_iid_from_short(netif, eui64);
return 0;
#endif
#if defined(MODULE_ESP_NOW)
case NETDEV_TYPE_RAW:
eui64->uint8[0] = netif->l2addr[0] ^ 0x02;
eui64->uint8[1] = netif->l2addr[1];
eui64->uint8[2] = netif->l2addr[2];
eui64->uint8[3] = 0xff;
eui64->uint8[4] = 0xfe;
eui64->uint8[5] = netif->l2addr[3];
eui64->uint8[6] = netif->l2addr[4];
eui64->uint8[7] = netif->l2addr[5];
return 0;
#endif
default:
(void)eui64;

Some files were not shown because too many files have changed in this diff Show More