Skip to content

Commit

Permalink
Merge pull request #15 from esp8266/esp8266
Browse files Browse the repository at this point in the history
pull latest changes
  • Loading branch information
ficeto committed May 8, 2015
2 parents fa1b4d5 + 6988ba6 commit 7be94cb
Show file tree
Hide file tree
Showing 7 changed files with 211 additions and 71 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 ####

Expand Down
13 changes: 11 additions & 2 deletions hardware/esp8266com/esp8266/cores/esp8266/Arduino.h
Original file line number Diff line number Diff line change
Expand Up @@ -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() )
Expand Down
7 changes: 7 additions & 0 deletions hardware/esp8266com/esp8266/cores/esp8266/Esp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions hardware/esp8266com/esp8266/cores/esp8266/Esp.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
218 changes: 171 additions & 47 deletions hardware/esp8266com/esp8266/libraries/SPI/SPI.cpp
Original file line number Diff line number Diff line change
@@ -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(&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;
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) {
Expand Down
30 changes: 9 additions & 21 deletions hardware/esp8266com/esp8266/libraries/SPI/SPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,30 +24,17 @@
#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
#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
Expand All @@ -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;
Expand All @@ -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);
Expand Down

0 comments on commit 7be94cb

Please sign in to comment.