diff --git a/README.md b/README.md index 0f01ba86d8..bf6cba3b36 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,8 @@ else they default to pins 4(SDA) and 5(SCL). #### SPI #### -SPI library supports the entire Arduino SPI API including transactions, including setting phase and polarity. +SPI library supports the entire Arduino SPI API including transactions, including setting phase (CPHA). +Setting the Clock polarity (CPOL) is not supported, yet (SPI_MODE2 and SPI_MODE3 not working). #### ESP-specific APIs #### diff --git a/hardware/esp8266com/esp8266/cores/esp8266/Arduino.h b/hardware/esp8266com/esp8266/cores/esp8266/Arduino.h index 6d039683ba..51aa65602c 100644 --- a/hardware/esp8266com/esp8266/cores/esp8266/Arduino.h +++ b/hardware/esp8266com/esp8266/cores/esp8266/Arduino.h @@ -124,8 +124,17 @@ void timer1_write(uint32_t ticks); //maximum ticks 8388607 void ets_intr_lock(); void ets_intr_unlock(); -#define interrupts() ets_intr_unlock(); -#define noInterrupts() ets_intr_lock(); +// level (0-15), +// level 15 will disable ALL interrupts, +// level 0 will disable most software interrupts +// +#define xt_disable_interrupts(state, level) __asm__ __volatile__("rsil %0," __STRINGIFY(level) "; esync; isync; dsync" : "=a" (state)) +#define xt_enable_interrupts(state) __asm__ __volatile__("wsr %0,ps; esync" :: "a" (state) : "memory") + +extern uint32_t interruptsState; + +#define interrupts() xt_enable_interrupts(interruptsState) +#define noInterrupts() xt_disable_interrupts(interruptsState, 15) #define clockCyclesPerMicrosecond() ( F_CPU / 1000000L ) #define clockCyclesToMicroseconds(a) ( (a) / clockCyclesPerMicrosecond() ) diff --git a/hardware/esp8266com/esp8266/cores/esp8266/Esp.cpp b/hardware/esp8266com/esp8266/cores/esp8266/Esp.cpp index 1ed9a09cb4..af6d99468c 100644 --- a/hardware/esp8266com/esp8266/cores/esp8266/Esp.cpp +++ b/hardware/esp8266com/esp8266/cores/esp8266/Esp.cpp @@ -227,6 +227,13 @@ FlashMode_t EspClass::getFlashChipMode(void) */ uint32_t EspClass::getFlashChipSizeByChipId(void) { uint32_t chipId = getFlashChipId(); + /** + * Chip ID + * 00 - always 00 (Chip ID use only 3 byte) + * 17 - ? looks like 2^xx is size in Byte ? //todo: find docu to this + * 40 - ? may be Speed ? //todo: find docu to this + * C8 - manufacturer ID + */ switch(chipId) { // GigaDevice diff --git a/hardware/esp8266com/esp8266/cores/esp8266/Esp.h b/hardware/esp8266com/esp8266/cores/esp8266/Esp.h index b3d836c798..9750dd312f 100644 --- a/hardware/esp8266com/esp8266/cores/esp8266/Esp.h +++ b/hardware/esp8266com/esp8266/cores/esp8266/Esp.h @@ -95,8 +95,16 @@ class EspClass { FlashMode_t getFlashChipMode(void); uint32_t getFlashChipSizeByChipId(void); + inline uint32_t getCycleCount(void); }; +uint32_t EspClass::getCycleCount(void) +{ + uint32_t ccount; + __asm__ __volatile__("rsr %0,ccount":"=a" (ccount)); + return ccount; +} + extern EspClass ESP; #endif //ESP_H diff --git a/hardware/esp8266com/esp8266/cores/esp8266/core_esp8266_wiring_digital.c b/hardware/esp8266com/esp8266/cores/esp8266/core_esp8266_wiring_digital.c index 0e673549bb..54c2b557b1 100644 --- a/hardware/esp8266com/esp8266/cores/esp8266/core_esp8266_wiring_digital.c +++ b/hardware/esp8266com/esp8266/cores/esp8266/core_esp8266_wiring_digital.c @@ -139,6 +139,9 @@ extern void __detachInterrupt(uint8_t pin) { } } +// stored state for the noInterrupts/interrupts methods +uint32_t interruptsState = 0; + void initPins() { //Disable UART interrupts system_set_os_print(0); diff --git a/hardware/esp8266com/esp8266/libraries/SPI/SPI.cpp b/hardware/esp8266com/esp8266/libraries/SPI/SPI.cpp index 347259f4d3..2523e689f1 100644 --- a/hardware/esp8266com/esp8266/libraries/SPI/SPI.cpp +++ b/hardware/esp8266com/esp8266/libraries/SPI/SPI.cpp @@ -1,79 +1,203 @@ /* - SPI.cpp - SPI library for esp8266 + SPI.cpp - SPI library for esp8266 - Copyright (c) 2015 Hristo Gochkov. All rights reserved. - This file is part of the esp8266 core for Arduino environment. + Copyright (c) 2015 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ #include "SPI.h" +#include "HardwareSerial.h" + +typedef union { + uint32_t regValue; + struct { + unsigned regL :6; + unsigned regH :6; + unsigned regN :6; + unsigned regPre :13; + unsigned regEQU :1; + }; +} spiClk_t; SPIClass SPI; -SPIClass::SPIClass(){} - -void SPIClass::begin(){ - pinMode(SCK, SPECIAL); - pinMode(MISO, SPECIAL); - pinMode(MOSI, SPECIAL); - - GPMUX = 0x105; - SPI1C = 0; - SPI1CLK = SPI_CLOCK_DIV16;//1MHz - SPI1U = SPIUMOSI | SPIUDUPLEX | SPIUSSE; - SPI1U1 = (7 << SPILMOSI) | (7 << SPILMISO); - SPI1C1 = 0; +SPIClass::SPIClass() { +} + +void SPIClass::begin() { + pinMode(SCK, SPECIAL); ///< GPIO14 + pinMode(MISO, SPECIAL); ///< GPIO12 + pinMode(MOSI, SPECIAL); ///< GPIO13 + + GPMUX = 0x105; // note crash if SPI flash Frequency < 40MHz + SPI1C = 0; + setFrequency(1000000); ///< 1MHz + SPI1U = SPIUMOSI | SPIUDUPLEX | SPIUSSE; + SPI1U1 = (7 << SPILMOSI) | (7 << SPILMISO); + SPI1C1 = 0; } void SPIClass::end() { - pinMode(SCK, INPUT); - pinMode(MISO, INPUT); - pinMode(MOSI, INPUT); + pinMode(SCK, INPUT); + pinMode(MISO, INPUT); + pinMode(MOSI, INPUT); } void SPIClass::beginTransaction(SPISettings settings) { - setClockDivider(settings._clock); - setBitOrder(settings._bitOrder); - setDataMode(settings._dataMode); + setFrequency(settings._clock); + setBitOrder(settings._bitOrder); + setDataMode(settings._dataMode); } -void SPIClass::endTransaction() {} +void SPIClass::endTransaction() { +} void SPIClass::setDataMode(uint8_t dataMode) { - + + /** + SPI_MODE0 0x00 - CPOL: 0 CPHA: 0 + SPI_MODE1 0x01 - CPOL: 0 CPHA: 1 + SPI_MODE2 0x10 - CPOL: 1 CPHA: 0 + SPI_MODE3 0x11 - CPOL: 1 CPHA: 1 + */ + + bool CPOL = (dataMode & 0x10); ///< CPOL (Clock Polarity) + bool CPHA = (dataMode & 0x01); ///< CPHA (Clock Phase) + + if(CPHA) { + SPI1U |= (SPIUSME); + } else { + SPI1U &= ~(SPIUSME); + } + + if(CPOL) { + //todo How set CPOL??? + } + } void SPIClass::setBitOrder(uint8_t bitOrder) { - if (bitOrder == MSBFIRST) { - SPI1C &= ~(SPICWBO | SPICRBO); - } else { - SPI1C |= (SPICWBO | SPICRBO); - } + if(bitOrder == MSBFIRST) { + SPI1C &= ~(SPICWBO | SPICRBO); + } else { + SPI1C |= (SPICWBO | SPICRBO); + } +} + +/** + * calculate the Frequency based on the register value + * @param reg + * @return + */ +static uint32_t ClkRegToFreq(spiClk_t * reg) { + return (F_CPU / ((reg->regPre + 1) * (reg->regN + 1))); +} + +void SPIClass::setFrequency(uint32_t freq) { + static uint32_t lastSetFrequency = 0; + static uint32_t lastSetRegister = 0; + + if(freq >= F_CPU) { + setClockDivider(0x80000000); + return; + } + + if(lastSetFrequency == freq && lastSetRegister == SPI1CLK) { + // do nothing (speed optimization) + return; + } + + const spiClk_t minFreqReg = { 0x7FFFF000 }; + uint32_t minFreq = ClkRegToFreq((spiClk_t*) &minFreqReg); + if(freq < minFreq) { + freq = minFreq; + } + + uint8_t calN = 1; + + spiClk_t bestReg = { 0 }; + int32_t bestFreq = 0; + + // find the best match + while(calN <= 0x3F) { // 0x3F max for N + + spiClk_t reg = { 0 }; + int32_t calFreq; + int32_t calPre; + int8_t calPreVari = -2; + + reg.regN = calN; + + while(calPreVari++ <= 1) { // test different variants for Pre (we calculate in int so we miss the decimals, testing is the easyest and fastest way) + calPre = (((F_CPU / (reg.regN + 1)) / freq) - 1) + calPreVari; + if(calPre > 0x1FFF) { + reg.regPre = 0x1FFF; // 8191 + } else if(calPre <= 0) { + reg.regPre = 0; + } else { + reg.regPre = calPre; + } + + reg.regL = ((reg.regN + 1) / 2); + // reg.regH = (reg.regN - reg.regL); + + // test calculation + calFreq = ClkRegToFreq(®); + //os_printf("-----[0x%08X][%d]\t EQU: %d\t Pre: %d\t N: %d\t H: %d\t L: %d = %d\n", reg.regValue, freq, reg.regEQU, reg.regPre, reg.regN, reg.regH, reg.regL, calFreq); + + if(calFreq == (int32_t) freq) { + // accurate match use it! + memcpy(&bestReg, ®, sizeof(bestReg)); + break; + } else if(calFreq < (int32_t) freq) { + // never go over the requested frequency + if(abs(freq - calFreq) < abs(freq - bestFreq)) { + bestFreq = calFreq; + memcpy(&bestReg, ®, sizeof(bestReg)); + } + } + } + if(calFreq == (int32_t) freq) { + // accurate match use it! + break; + } + calN++; + } + + // os_printf("[0x%08X][%d]\t EQU: %d\t Pre: %d\t N: %d\t H: %d\t L: %d\t - Real Frequency: %d\n", bestReg.regValue, freq, bestReg.regEQU, bestReg.regPre, bestReg.regN, bestReg.regH, bestReg.regL, ClkRegToFreq(&bestReg)); + + setClockDivider(bestReg.regValue); + lastSetRegister = SPI1CLK; + lastSetFrequency = freq; + } void SPIClass::setClockDivider(uint32_t clockDiv) { - SPI1CLK = clockDiv; + SPI1CLK = clockDiv; } uint8_t SPIClass::transfer(uint8_t data) { - while(SPI1CMD & SPIBUSY); - SPI1W0 = data; - SPI1CMD |= SPIBUSY; - while(SPI1CMD & SPIBUSY); - return (uint8_t)(SPI1W0 & 0xff); + while(SPI1CMD & SPIBUSY) + ; + SPI1W0 = data; + SPI1CMD |= SPIBUSY; + while(SPI1CMD & SPIBUSY) + ; + return (uint8_t) (SPI1W0 & 0xff); } uint16_t SPIClass::transfer16(uint16_t data) { diff --git a/hardware/esp8266com/esp8266/libraries/SPI/SPI.h b/hardware/esp8266com/esp8266/libraries/SPI/SPI.h index 5ff10d3a3a..a39f3d0420 100644 --- a/hardware/esp8266com/esp8266/libraries/SPI/SPI.h +++ b/hardware/esp8266com/esp8266/libraries/SPI/SPI.h @@ -24,16 +24,10 @@ #include #include -#define FCPU80 80000000L - -#if F_CPU == FCPU80 -#define SPI_CLOCK_DIV80M 0x80000000 //80 MHz -#define SPI_CLOCK_DIV40M 0x00001001 //40 MHz -#define SPI_CLOCK_DIV20M 0x00041001 //20 MHz -#define SPI_CLOCK_DIV16M 0x000fffc0 //16 MHz -#define SPI_CLOCK_DIV10M 0x000c1001 //10 MHz +// This defines are not representing the real Divider of the ESP8266 +// the Defines match to an AVR Arduino on 16MHz for better compatibility +#if F_CPU == 80000000L #define SPI_CLOCK_DIV2 0x00101001 //8 MHz -#define SPI_CLOCK_DIV5M 0x001c1001 //5 MHz #define SPI_CLOCK_DIV4 0x00241001 //4 MHz #define SPI_CLOCK_DIV8 0x004c1001 //2 MHz #define SPI_CLOCK_DIV16 0x009c1001 //1 MHz @@ -41,13 +35,6 @@ #define SPI_CLOCK_DIV64 0x027c1001 //250 KHz #define SPI_CLOCK_DIV128 0x04fc1001 //125 KHz #else -#define SPI_CLOCK_DIV160M 0x80000000 //160 MHz -#define SPI_CLOCK_DIV80M 0x00001001 //80 MHz -#define SPI_CLOCK_DIV40M 0x00041001 //40 MHz -#define SPI_CLOCK_DIV32M 0x000fffc0 //32 MHz -#define SPI_CLOCK_DIV20M 0x000c1001 //20 MHz -#define SPI_CLOCK_DIV16M 0x00101001 //16 MHz -#define SPI_CLOCK_DIV10M 0x001c1001 //10 MHz #define SPI_CLOCK_DIV2 0x00241001 //8 MHz #define SPI_CLOCK_DIV4 0x004c1001 //4 MHz #define SPI_CLOCK_DIV8 0x009c1001 //2 MHz @@ -56,14 +43,14 @@ #define SPI_CLOCK_DIV64 0x04fc1001 //250 KHz #endif -const uint8_t SPI_MODE0 = 0x00; -const uint8_t SPI_MODE1 = 0x04; -const uint8_t SPI_MODE2 = 0x08; -const uint8_t SPI_MODE3 = 0x0C; +const uint8_t SPI_MODE0 = 0x00; ///< CPOL: 0 CPHA: 0 +const uint8_t SPI_MODE1 = 0x01; ///< CPOL: 0 CPHA: 1 +const uint8_t SPI_MODE2 = 0x10; ///< CPOL: 1 CPHA: 0 +const uint8_t SPI_MODE3 = 0x11; ///< CPOL: 1 CPHA: 1 class SPISettings { public: - SPISettings() :_clock(SPI_CLOCK_DIV16), _bitOrder(LSBFIRST), _dataMode(SPI_MODE0){} + SPISettings() :_clock(1000000), _bitOrder(LSBFIRST), _dataMode(SPI_MODE0){} SPISettings(uint32_t clock, uint8_t bitOrder, uint8_t dataMode) :_clock(clock), _bitOrder(bitOrder), _dataMode(dataMode){} uint32_t _clock; uint8_t _bitOrder; @@ -77,6 +64,7 @@ class SPIClass { void end(); void setBitOrder(uint8_t bitOrder); void setDataMode(uint8_t dataMode); + void setFrequency(uint32_t freq); void setClockDivider(uint32_t clockDiv); void beginTransaction(SPISettings settings); uint8_t transfer(uint8_t data);