From 163858dc448f04564e39c9bb5b71d2695eadfc0d Mon Sep 17 00:00:00 2001 From: Markus Sattler Date: Thu, 7 May 2015 19:25:25 +0200 Subject: [PATCH 1/5] fix use SPI.beginTransaction crash --- cores/esp8266/Esp.cpp | 7 +++ libraries/SPI/SPI.cpp | 130 +++++++++++++++++++++++++++--------------- libraries/SPI/SPI.h | 34 ++--------- 3 files changed, 95 insertions(+), 76 deletions(-) diff --git a/cores/esp8266/Esp.cpp b/cores/esp8266/Esp.cpp index 1ed9a09cb..af6d99468 100644 --- a/cores/esp8266/Esp.cpp +++ b/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/libraries/SPI/SPI.cpp b/libraries/SPI/SPI.cpp index 347259f4d..9292c8895 100644 --- a/libraries/SPI/SPI.cpp +++ b/libraries/SPI/SPI.cpp @@ -1,79 +1,117 @@ /* - 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 struct { + uint32_t divider; + uint32_t regValue; +} spiClockDiv_t; + +static const spiClockDiv_t spiClockDiv[] = { + { 0, (0x80000000) }, ///< @80Mhz = 80 MHz @160Mhz = 160 MHz + { 2, (0x00001001) }, ///< @80Mhz = 40 MHz @160Mhz = 80 MHz + { 4, (0x00041001) }, ///< @80Mhz = 20 MHz @160Mhz = 40 MHz + { 6, (0x000fffc0) }, ///< @80Mhz = 16 MHz @160Mhz = 32 MHz + { 8, (0x000c1001) }, ///< @80Mhz = 10 MHz @160Mhz = 20 MHz + { 10, (0x00101001) }, ///< @80Mhz = 8 MHz @160Mhz = 16 MHz + { 16, (0x001c1001) }, ///< @80Mhz = 5 MHz @160Mhz = 10 MHz + { 20, (0x00241001) }, ///< @80Mhz = 4 MHz @160Mhz = 8 MHz + { 40, (0x004c1001) }, ///< @80Mhz = 2 MHz @160Mhz = 4 MHz + { 80, (0x009c1001) }, ///< @80Mhz = 1 MHz @160Mhz = 2 MHz + { 160, (0x013c1001) }, ///< @80Mhz = 500 KHz @160Mhz = 1 MHz + { 320, (0x027c1001) }, ///< @80Mhz = 250 KHz @160Mhz = 500 KHz + { 640, (0x04fc1001) } ///< @80Mhz = 125 KHz @160Mhz = 250 KHz +}; + +static const uint8_t spiClockDiv_count = (sizeof(spiClockDiv) / sizeof(spiClockDiv_t)); SPIClass SPI; -SPIClass::SPIClass(){} +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; +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) { - + // todo find way to set } 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); + } +} + +void SPIClass::setFrequency(uint32_t freq) { + uint8_t i = 0; + // find the best match + if(freq < F_CPU) { + for(i = 1; i < (spiClockDiv_count-1); i++) { + if(freq >= (F_CPU/spiClockDiv[i].divider)) { + break; + } + } + } + setClockDivider(spiClockDiv[i].regValue); } 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/libraries/SPI/SPI.h b/libraries/SPI/SPI.h index 5ff10d3a3..3daf57c62 100644 --- a/libraries/SPI/SPI.h +++ b/libraries/SPI/SPI.h @@ -26,35 +26,8 @@ #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 -#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 -#define SPI_CLOCK_DIV32 0x013c1001 //500 KHz -#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 -#define SPI_CLOCK_DIV16 0x013c1001 //1 MHz -#define SPI_CLOCK_DIV32 0x027c1001 //500 KHz -#define SPI_CLOCK_DIV64 0x04fc1001 //250 KHz -#endif + + const uint8_t SPI_MODE0 = 0x00; const uint8_t SPI_MODE1 = 0x04; @@ -63,7 +36,7 @@ const uint8_t SPI_MODE3 = 0x0C; 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 +50,7 @@ public: 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); From ee5def9c8c6ca3375a5b4f38b1df3adbdc882281 Mon Sep 17 00:00:00 2001 From: Markus Sattler Date: Thu, 7 May 2015 19:48:55 +0200 Subject: [PATCH 2/5] add CPHA support for SPI - tested with logic analyzer --- libraries/SPI/SPI.cpp | 24 ++++++++++++++++++++++-- libraries/SPI/SPI.h | 13 ++++--------- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/libraries/SPI/SPI.cpp b/libraries/SPI/SPI.cpp index 9292c8895..6e5cfb7cd 100644 --- a/libraries/SPI/SPI.cpp +++ b/libraries/SPI/SPI.cpp @@ -54,7 +54,7 @@ void SPIClass::begin() { pinMode(MISO, SPECIAL); ///< GPIO12 pinMode(MOSI, SPECIAL); ///< GPIO13 - GPMUX = 0x105; // note crash if spi flash Frequency < 40MHz + GPMUX = 0x105; // note crash if SPI flash Frequency < 40MHz SPI1C = 0; setFrequency(1000000); ///< 1Mhz SPI1U = SPIUMOSI | SPIUDUPLEX | SPIUSSE; @@ -78,7 +78,27 @@ void SPIClass::endTransaction() { } void SPIClass::setDataMode(uint8_t dataMode) { - // todo find way to set + +/** + 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) { diff --git a/libraries/SPI/SPI.h b/libraries/SPI/SPI.h index 3daf57c62..e8e9b2c92 100644 --- a/libraries/SPI/SPI.h +++ b/libraries/SPI/SPI.h @@ -24,15 +24,10 @@ #include #include -#define FCPU80 80000000L - - - - -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: From 7f4a10062b74db309434348220d8e415622ab1db Mon Sep 17 00:00:00 2001 From: Markus Sattler Date: Thu, 7 May 2015 20:48:59 +0200 Subject: [PATCH 3/5] update README add some docu and todo --- README.md | 3 ++- libraries/SPI/SPI.cpp | 45 +++++++++++++++++++++++++++++-------------- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 0f01ba86d..bf6cba3b3 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/libraries/SPI/SPI.cpp b/libraries/SPI/SPI.cpp index 6e5cfb7cd..8cd8f8514 100644 --- a/libraries/SPI/SPI.cpp +++ b/libraries/SPI/SPI.cpp @@ -21,27 +21,39 @@ #include "SPI.h" #include "HardwareSerial.h" + typedef struct { uint32_t divider; - uint32_t regValue; + union { + uint32_t regValue; + struct { + unsigned regL :6; + unsigned regH :6; + unsigned regN :6; + unsigned regPre :13; + unsigned regEQU :1; + }; + }; } spiClockDiv_t; +// todo find way of calculation for the divider static const spiClockDiv_t spiClockDiv[] = { - { 0, (0x80000000) }, ///< @80Mhz = 80 MHz @160Mhz = 160 MHz - { 2, (0x00001001) }, ///< @80Mhz = 40 MHz @160Mhz = 80 MHz - { 4, (0x00041001) }, ///< @80Mhz = 20 MHz @160Mhz = 40 MHz - { 6, (0x000fffc0) }, ///< @80Mhz = 16 MHz @160Mhz = 32 MHz - { 8, (0x000c1001) }, ///< @80Mhz = 10 MHz @160Mhz = 20 MHz - { 10, (0x00101001) }, ///< @80Mhz = 8 MHz @160Mhz = 16 MHz - { 16, (0x001c1001) }, ///< @80Mhz = 5 MHz @160Mhz = 10 MHz - { 20, (0x00241001) }, ///< @80Mhz = 4 MHz @160Mhz = 8 MHz - { 40, (0x004c1001) }, ///< @80Mhz = 2 MHz @160Mhz = 4 MHz - { 80, (0x009c1001) }, ///< @80Mhz = 1 MHz @160Mhz = 2 MHz - { 160, (0x013c1001) }, ///< @80Mhz = 500 KHz @160Mhz = 1 MHz - { 320, (0x027c1001) }, ///< @80Mhz = 250 KHz @160Mhz = 500 KHz - { 640, (0x04fc1001) } ///< @80Mhz = 125 KHz @160Mhz = 250 KHz + { 0, (0x80000000) }, ///< [0] EQU: 1 Pre: 0 N: 0 H: 0 L: 0 Div: 0 @80Mhz = 80 MHz @160Mhz = 160 MHz + { 2, (0x00001001) }, ///< [1] EQU: 0 Pre: 0 N: 1 H: 0 L: 1 Div: 2 @80Mhz = 40 MHz @160Mhz = 80 MHz + { 4, (0x00041001) }, ///< [2] EQU: 0 Pre: 1 N: 1 H: 0 L: 1 Div: 4 @80Mhz = 20 MHz @160Mhz = 40 MHz + { 6, (0x000fffc0) }, ///< [3] EQU: 0 Pre: 3 N: 63 H: 63 L: 0 Div: 6 @80Mhz = 16 MHz @160Mhz = 32 MHz + { 8, (0x000c1001) }, ///< [4] EQU: 0 Pre: 3 N: 1 H: 0 L: 1 Div: 8 @80Mhz = 10 MHz @160Mhz = 20 MHz + { 10, (0x00101001) }, ///< [5] EQU: 0 Pre: 4 N: 1 H: 0 L: 1 Div: 10 @80Mhz = 8 MHz @160Mhz = 16 MHz + { 16, (0x001c1001) }, ///< [6] EQU: 0 Pre: 7 N: 1 H: 0 L: 1 Div: 16 @80Mhz = 5 MHz @160Mhz = 10 MHz + { 20, (0x00241001) }, ///< [7] EQU: 0 Pre: 9 N: 1 H: 0 L: 1 Div: 20 @80Mhz = 4 MHz @160Mhz = 8 MHz + { 40, (0x004c1001) }, ///< [8] EQU: 0 Pre: 19 N: 1 H: 0 L: 1 Div: 40 @80Mhz = 2 MHz @160Mhz = 4 MHz + { 80, (0x009c1001) }, ///< [9] EQU: 0 Pre: 39 N: 1 H: 0 L: 1 Div: 80 @80Mhz = 1 MHz @160Mhz = 2 MHz + { 160, (0x013c1001) }, ///< [10] EQU: 0 Pre: 79 N: 1 H: 0 L: 1 Div: 160 @80Mhz = 500 KHz @160Mhz = 1 MHz + { 320, (0x027c1001) }, ///< [11] EQU: 0 Pre: 159 N: 1 H: 0 L: 1 Div: 320 @80Mhz = 250 KHz @160Mhz = 500 KHz + { 640, (0x04fc1001) } ///< [12] EQU: 0 Pre: 319 N: 1 H: 0 L: 1 Div: 640 @80Mhz = 125 KHz @160Mhz = 250 KHz }; + static const uint8_t spiClockDiv_count = (sizeof(spiClockDiv) / sizeof(spiClockDiv_t)); SPIClass SPI; @@ -53,6 +65,11 @@ void SPIClass::begin() { pinMode(SCK, SPECIAL); ///< GPIO14 pinMode(MISO, SPECIAL); ///< GPIO12 pinMode(MOSI, SPECIAL); ///< GPIO13 +/* + for(uint8_t i = 0; i < (spiClockDiv_count); i++) { + os_printf("[%d]\t EQU: %d\t Pre: %d\t N: %d\t H: %d\t L: %d\t Div: %d - %d\n", i, spiClockDiv[i].regEQU, spiClockDiv[i].regPre, spiClockDiv[i].regN, spiClockDiv[i].regH, spiClockDiv[i].regL, spiClockDiv[i].divider ); + } +*/ GPMUX = 0x105; // note crash if SPI flash Frequency < 40MHz SPI1C = 0; From 49fc1980fe636b399c3baee4f37bedd4e9399c26 Mon Sep 17 00:00:00 2001 From: Markus Sattler Date: Thu, 7 May 2015 22:16:51 +0200 Subject: [PATCH 4/5] add back SPI_CLOCK_DIVx defines may needed by some library Note: better use SPI.setFrequency() --- libraries/SPI/SPI.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/libraries/SPI/SPI.h b/libraries/SPI/SPI.h index e8e9b2c92..a39f3d042 100644 --- a/libraries/SPI/SPI.h +++ b/libraries/SPI/SPI.h @@ -24,6 +24,25 @@ #include #include +// 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_DIV4 0x00241001 //4 MHz +#define SPI_CLOCK_DIV8 0x004c1001 //2 MHz +#define SPI_CLOCK_DIV16 0x009c1001 //1 MHz +#define SPI_CLOCK_DIV32 0x013c1001 //500 KHz +#define SPI_CLOCK_DIV64 0x027c1001 //250 KHz +#define SPI_CLOCK_DIV128 0x04fc1001 //125 KHz +#else +#define SPI_CLOCK_DIV2 0x00241001 //8 MHz +#define SPI_CLOCK_DIV4 0x004c1001 //4 MHz +#define SPI_CLOCK_DIV8 0x009c1001 //2 MHz +#define SPI_CLOCK_DIV16 0x013c1001 //1 MHz +#define SPI_CLOCK_DIV32 0x027c1001 //500 KHz +#define SPI_CLOCK_DIV64 0x04fc1001 //250 KHz +#endif + 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 From d7a88c3ea388eb802109ea8c629f2c183d1c6ed9 Mon Sep 17 00:00:00 2001 From: Markus Sattler Date: Fri, 8 May 2015 00:48:08 +0200 Subject: [PATCH 5/5] use a function to calculate best match clock register for SPI TEST: Frequency: 1000000 -> [0x009C1001] EQU: 0 Pre: 39 N: 1 H: 0 L: 1 - Back Calculated Frequency: 1000000 Frequency: 4000000 -> [0x00001001] EQU: 0 Pre: 0 N: 1 H: 0 L: 1 - Back Calculated Frequency: 40000000 Frequency: 2000000 -> [0x00041001] EQU: 0 Pre: 1 N: 1 H: 0 L: 1 - Back Calculated Frequency: 20000000 Frequency: 1600000 -> [0x00004002] EQU: 0 Pre: 0 N: 4 H: 0 L: 2 - Back Calculated Frequency: 16000000 Frequency: 1610000 -> [0x00004002] EQU: 0 Pre: 0 N: 4 H: 0 L: 2 - Back Calculated Frequency: 16000000 Frequency: 1000000 -> [0x000C1001] EQU: 0 Pre: 3 N: 1 H: 0 L: 1 - Back Calculated Frequency: 10000000 Frequency: 8000000 -> [0x00101001] EQU: 0 Pre: 4 N: 1 H: 0 L: 1 - Back Calculated Frequency: 8000000 Frequency: 5000000 -> [0x001C1001] EQU: 0 Pre: 7 N: 1 H: 0 L: 1 - Back Calculated Frequency: 5000000 Frequency: 4000000 -> [0x00241001] EQU: 0 Pre: 9 N: 1 H: 0 L: 1 - Back Calculated Frequency: 4000000 Frequency: 2000000 -> [0x004C1001] EQU: 0 Pre: 19 N: 1 H: 0 L: 1 - Back Calculated Frequency: 2000000 Frequency: 1000000 -> [0x009C1001] EQU: 0 Pre: 39 N: 1 H: 0 L: 1 - Back Calculated Frequency: 1000000 Frequency: 500000 -> [0x013C1001] EQU: 0 Pre: 79 N: 1 H: 0 L: 1 - Back Calculated Frequency: 500000 Frequency: 250000 -> [0x027C1001] EQU: 0 Pre: 159 N: 1 H: 0 L: 1 - Back Calculated Frequency: 250000 Frequency: 125000 -> [0x04FC1001] EQU: 0 Pre: 319 N: 1 H: 0 L: 1 - Back Calculated Frequency: 125000 Frequency: 6666666 -> [0x00141001] EQU: 0 Pre: 5 N: 1 H: 0 L: 1 - Back Calculated Frequency: 6666666 Frequency: 6000000 -> [0x00181001] EQU: 0 Pre: 6 N: 1 H: 0 L: 1 - Back Calculated Frequency: 5714285 Frequency: 3000000 -> [0x00202001] EQU: 0 Pre: 8 N: 2 H: 0 L: 1 - Back Calculated Frequency: 2962962 Frequency: 100 -> [0x7FFFF020] EQU: 0 Pre: 8191 N: 63 H: 0 L: 32 - Back Calculated Frequency: 152 Frequency: 125000 -> [0x04FC1001] EQU: 0 Pre: 319 N: 1 H: 0 L: 1 - Back Calculated Frequency: 125000 Frequency: 16457 -> [0x25F81001] EQU: 0 Pre: 2430 N: 1 H: 0 L: 1 - Back Calculated Frequency: 16454 --- libraries/SPI/SPI.cpp | 155 +++++++++++++++++++++++++++--------------- 1 file changed, 102 insertions(+), 53 deletions(-) diff --git a/libraries/SPI/SPI.cpp b/libraries/SPI/SPI.cpp index 8cd8f8514..2523e689f 100644 --- a/libraries/SPI/SPI.cpp +++ b/libraries/SPI/SPI.cpp @@ -22,39 +22,16 @@ #include "SPI.h" #include "HardwareSerial.h" -typedef struct { - uint32_t divider; - union { - uint32_t regValue; - struct { - unsigned regL :6; - unsigned regH :6; - unsigned regN :6; - unsigned regPre :13; - unsigned regEQU :1; - }; +typedef union { + uint32_t regValue; + struct { + unsigned regL :6; + unsigned regH :6; + unsigned regN :6; + unsigned regPre :13; + unsigned regEQU :1; }; -} spiClockDiv_t; - -// todo find way of calculation for the divider -static const spiClockDiv_t spiClockDiv[] = { - { 0, (0x80000000) }, ///< [0] EQU: 1 Pre: 0 N: 0 H: 0 L: 0 Div: 0 @80Mhz = 80 MHz @160Mhz = 160 MHz - { 2, (0x00001001) }, ///< [1] EQU: 0 Pre: 0 N: 1 H: 0 L: 1 Div: 2 @80Mhz = 40 MHz @160Mhz = 80 MHz - { 4, (0x00041001) }, ///< [2] EQU: 0 Pre: 1 N: 1 H: 0 L: 1 Div: 4 @80Mhz = 20 MHz @160Mhz = 40 MHz - { 6, (0x000fffc0) }, ///< [3] EQU: 0 Pre: 3 N: 63 H: 63 L: 0 Div: 6 @80Mhz = 16 MHz @160Mhz = 32 MHz - { 8, (0x000c1001) }, ///< [4] EQU: 0 Pre: 3 N: 1 H: 0 L: 1 Div: 8 @80Mhz = 10 MHz @160Mhz = 20 MHz - { 10, (0x00101001) }, ///< [5] EQU: 0 Pre: 4 N: 1 H: 0 L: 1 Div: 10 @80Mhz = 8 MHz @160Mhz = 16 MHz - { 16, (0x001c1001) }, ///< [6] EQU: 0 Pre: 7 N: 1 H: 0 L: 1 Div: 16 @80Mhz = 5 MHz @160Mhz = 10 MHz - { 20, (0x00241001) }, ///< [7] EQU: 0 Pre: 9 N: 1 H: 0 L: 1 Div: 20 @80Mhz = 4 MHz @160Mhz = 8 MHz - { 40, (0x004c1001) }, ///< [8] EQU: 0 Pre: 19 N: 1 H: 0 L: 1 Div: 40 @80Mhz = 2 MHz @160Mhz = 4 MHz - { 80, (0x009c1001) }, ///< [9] EQU: 0 Pre: 39 N: 1 H: 0 L: 1 Div: 80 @80Mhz = 1 MHz @160Mhz = 2 MHz - { 160, (0x013c1001) }, ///< [10] EQU: 0 Pre: 79 N: 1 H: 0 L: 1 Div: 160 @80Mhz = 500 KHz @160Mhz = 1 MHz - { 320, (0x027c1001) }, ///< [11] EQU: 0 Pre: 159 N: 1 H: 0 L: 1 Div: 320 @80Mhz = 250 KHz @160Mhz = 500 KHz - { 640, (0x04fc1001) } ///< [12] EQU: 0 Pre: 319 N: 1 H: 0 L: 1 Div: 640 @80Mhz = 125 KHz @160Mhz = 250 KHz -}; - - -static const uint8_t spiClockDiv_count = (sizeof(spiClockDiv) / sizeof(spiClockDiv_t)); +} spiClk_t; SPIClass SPI; @@ -65,15 +42,10 @@ void SPIClass::begin() { pinMode(SCK, SPECIAL); ///< GPIO14 pinMode(MISO, SPECIAL); ///< GPIO12 pinMode(MOSI, SPECIAL); ///< GPIO13 -/* - for(uint8_t i = 0; i < (spiClockDiv_count); i++) { - os_printf("[%d]\t EQU: %d\t Pre: %d\t N: %d\t H: %d\t L: %d\t Div: %d - %d\n", i, spiClockDiv[i].regEQU, spiClockDiv[i].regPre, spiClockDiv[i].regN, spiClockDiv[i].regH, spiClockDiv[i].regL, spiClockDiv[i].divider ); - } -*/ GPMUX = 0x105; // note crash if SPI flash Frequency < 40MHz SPI1C = 0; - setFrequency(1000000); ///< 1Mhz + setFrequency(1000000); ///< 1MHz SPI1U = SPIUMOSI | SPIUDUPLEX | SPIUSSE; SPI1U1 = (7 << SPILMOSI) | (7 << SPILMISO); SPI1C1 = 0; @@ -96,15 +68,15 @@ 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 -*/ + /** + 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) + bool CPOL = (dataMode & 0x10); ///< CPOL (Clock Polarity) + bool CPHA = (dataMode & 0x01); ///< CPHA (Clock Phase) if(CPHA) { SPI1U |= (SPIUSME); @@ -126,17 +98,92 @@ void SPIClass::setBitOrder(uint8_t bitOrder) { } } +/** + * 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) { - uint8_t i = 0; + 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 - if(freq < F_CPU) { - for(i = 1; i < (spiClockDiv_count-1); i++) { - if(freq >= (F_CPU/spiClockDiv[i].divider)) { + 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++; } - setClockDivider(spiClockDiv[i].regValue); + + // 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) { @@ -144,10 +191,12 @@ void SPIClass::setClockDivider(uint32_t clockDiv) { } uint8_t SPIClass::transfer(uint8_t data) { - while(SPI1CMD & SPIBUSY); + while(SPI1CMD & SPIBUSY) + ; SPI1W0 = data; SPI1CMD |= SPIBUSY; - while(SPI1CMD & SPIBUSY); + while(SPI1CMD & SPIBUSY) + ; return (uint8_t) (SPI1W0 & 0xff); }