1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-04-22 21:23:07 +03:00

Merge pull request #199 from Links2004/esp8266

SPI improvement and bugfix
This commit is contained in:
Ivan Grokhotkov 2015-05-08 08:27:49 +03:00
commit 5969f56f5e
4 changed files with 188 additions and 68 deletions

View File

@ -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 ####

View File

@ -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

View File

@ -17,22 +17,35 @@
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(){}
SPIClass::SPIClass() {
}
void SPIClass::begin(){
pinMode(SCK, SPECIAL);
pinMode(MISO, SPECIAL);
pinMode(MOSI, SPECIAL);
void SPIClass::begin() {
pinMode(SCK, SPECIAL); ///< GPIO14
pinMode(MISO, SPECIAL); ///< GPIO12
pinMode(MOSI, SPECIAL); ///< GPIO13
GPMUX = 0x105;
GPMUX = 0x105; // note crash if SPI flash Frequency < 40MHz
SPI1C = 0;
SPI1CLK = SPI_CLOCK_DIV16;//1MHz
setFrequency(1000000); ///< 1MHz
SPI1U = SPIUMOSI | SPIUDUPLEX | SPIUSSE;
SPI1U1 = (7 << SPILMOSI) | (7 << SPILMISO);
SPI1C1 = 0;
@ -45,35 +58,146 @@ void SPIClass::end() {
}
void SPIClass::beginTransaction(SPISettings settings) {
setClockDivider(settings._clock);
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) {
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(&reg);
//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, &reg, 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, &reg, 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;
}
uint8_t SPIClass::transfer(uint8_t data) {
while(SPI1CMD & SPIBUSY);
while(SPI1CMD & SPIBUSY)
;
SPI1W0 = data;
SPI1CMD |= SPIBUSY;
while(SPI1CMD & SPIBUSY);
return (uint8_t)(SPI1W0 & 0xff);
while(SPI1CMD & SPIBUSY)
;
return (uint8_t) (SPI1W0 & 0xff);
}
uint16_t SPIClass::transfer16(uint16_t data) {

View File

@ -24,16 +24,10 @@
#include <Arduino.h>
#include <stdlib.h>
#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 @@ 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);