Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spi0command #6674

Merged
merged 23 commits into from
Nov 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
71d348c
precache() - preload code into the flash cache.
ChocolateFrogsNuts Oct 10, 2019
acde951
Fix missing include
ChocolateFrogsNuts Oct 10, 2019
482310d
Merge branch 'master' into precache
ChocolateFrogsNuts Oct 10, 2019
e74736c
Make precache extern "C"
ChocolateFrogsNuts Oct 11, 2019
fdbe442
Merge branch 'precache' of https://github.com/ChocolateFrogsNuts/Ardu…
ChocolateFrogsNuts Oct 11, 2019
2cbdb41
Attempt 2 at making precache extern "C"
ChocolateFrogsNuts Oct 11, 2019
0a7862a
Fix calculation of number of cache lines to preload
ChocolateFrogsNuts Oct 11, 2019
8e4f9d0
SPI0Command - A utility function for generic SPI commands on SPI0
ChocolateFrogsNuts Oct 11, 2019
e2c6505
Clean some trailing spaces
ChocolateFrogsNuts Oct 11, 2019
d9d07cb
Upgrade _SPI0Command to _SPICommand
ChocolateFrogsNuts Oct 14, 2019
34084e9
Tidy up a comment thats no longer applicable
ChocolateFrogsNuts Oct 15, 2019
adadaeb
Merge branch 'master' into spi0command
ChocolateFrogsNuts Oct 26, 2019
a2d1400
Merge branch 'master' into spi0command
ChocolateFrogsNuts Oct 27, 2019
63cbbf7
Comments, formatting and variable renames
ChocolateFrogsNuts Oct 28, 2019
abcb709
put SPI0Command in namespace experimental
ChocolateFrogsNuts Oct 28, 2019
7b2710c
Add a comment noting that the code has only been tested on bus 0
ChocolateFrogsNuts Oct 28, 2019
78af7d9
Merge branch 'master' into spi0command
devyte Oct 29, 2019
97ce07c
Replace use of memcpy with for loops in _SPICommand()
ChocolateFrogsNuts Nov 1, 2019
0890556
Merge branch 'master' into spi0command
ChocolateFrogsNuts Nov 1, 2019
a40577c
Typo fix
ChocolateFrogsNuts Nov 1, 2019
55b3a59
Merge branch 'spi0command' of https://github.com/ChocolateFrogsNuts/A…
ChocolateFrogsNuts Nov 1, 2019
71a14a4
Merge branch 'master' into spi0command
ChocolateFrogsNuts Nov 3, 2019
2d7c43b
Move the SpiOpResult enum into experimental namespace
ChocolateFrogsNuts Nov 5, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
191 changes: 191 additions & 0 deletions cores/esp8266/core_esp8266_spi_utils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
/*
core_esp8266_spi_utils.cpp

Copyright (c) 2019 Mike Nix. 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 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
*/

#include <stdint.h>
#include <string.h>

// register names
#include "esp8266_peri.h"

// for flashchip
#include "spi_flash.h"

// for PRECACHE_*
#include "core_esp8266_features.h"

#include "spi_utils.h"

extern "C" uint32_t Wait_SPI_Idle(SpiFlashChip *fc);

namespace experimental {

/*
* critical part of SPICommand.
* Kept in a separate function to aid with precaching
* PRECACHE_* saves having to make the function IRAM_ATTR.
*
* spiIfNum needs to be volatile to keep the optimiser from
* deciding it can be treated as a constant (due to this being a
* static function only called with spiIfNum set to 0)
*
* Note: if porting to ESP32 mosi/miso bits are set in 2 registers, not 1.
*/
static SpiOpResult PRECACHE_ATTR
_SPICommand(volatile uint32_t spiIfNum,
uint32_t spic,uint32_t spiu,uint32_t spiu1,uint32_t spiu2,
uint32_t *data,uint32_t writeWords,uint32_t readWords)
{
if (spiIfNum>1)
return SPI_RESULT_ERR;

// force SPI register access via base+offest.
// Prevents loading individual address constants from flash.
uint32_t *spibase = (uint32_t*)(spiIfNum ? &(SPI1CMD) : &(SPI0CMD));
#define SPIREG(reg) (*((volatile uint32_t *)(spibase+(&(reg) - &(SPI0CMD)))))

// preload any constants and functions we need into variables
// Everything defined here must be volatile or the optimizer can
// treat them as constants, resulting in the flash reads we're
// trying to avoid
uint32_t (* volatile Wait_SPI_Idlep)(SpiFlashChip *) = Wait_SPI_Idle;
volatile SpiFlashChip *fchip=flashchip;
volatile uint32_t spicmdusr=SPICMDUSR;

if (!spiIfNum) {
// Only need to precache when using SPI0
PRECACHE_START();
Wait_SPI_Idlep((SpiFlashChip *)fchip);
}

// preserve essential controller state such as incoming/outgoing
// data lengths and IO mode.
uint32_t oldSPI0U = SPIREG(SPI0U);
uint32_t oldSPI0U2= SPIREG(SPI0U2);
uint32_t oldSPI0C = SPIREG(SPI0C);

//SPI0S &= ~(SPISE|SPISBE|SPISSE|SPISCD);
SPIREG(SPI0C) = spic;
SPIREG(SPI0U) = spiu;
SPIREG(SPI0U1)= spiu1;
SPIREG(SPI0U2)= spiu2;

if (writeWords>0) {
// copy the outgoing data to the SPI hardware
uint32_t *src=data;
volatile uint32_t *dst=&SPIREG(SPI0W0);
for (uint32_t i=0; i<writeWords; i++)
*dst++ = *src++;
}

// Start the transfer
SPIREG(SPI0CMD) = spicmdusr;

// wait for the command to complete (typically only 1-3 iterations)
uint32_t timeout = 1000;
while ((SPIREG(SPI0CMD) & spicmdusr) && timeout--);

if ((readWords>0) && (timeout>0)) {
// copy the response back to the buffer
uint32_t *dst=data;
volatile uint32_t *src=&SPIREG(SPI0W0);
for (uint32_t i=0; i<readWords; i++)
*dst++ = *src++;
}

// Restore saved registers
SPIREG(SPI0U) = oldSPI0U;
SPIREG(SPI0U2)= oldSPI0U2;
SPIREG(SPI0C) = oldSPI0C;

PRECACHE_END();
return (timeout>0 ? SPI_RESULT_OK : SPI_RESULT_TIMEOUT);
}


/* SPI0Command: send a custom SPI command.
* This part calculates register values and passes them to _SPI0Command().
* Parameters:
* cmd The command byte (first 8 bits) to send to the SPI device
* *data The buffer containing the outgoing data for the SPI bus.
* The data is expected to be mosi_bits long, and the buffer
* is overwritten by the incoming bus data, which will be
* miso_bits long.
* mosi_bits
* Number of bits to be sent after the command byte.
* miso_bits
* Number of bits to read from the SPI bus after the outgoing
* data has been sent.
*
* Note: This code has only been tested with SPI bus 0, but should work
* equally well with other busses. The ESP8266 has bus 0 and 1,
* newer chips may have more one day.
*/
SpiOpResult SPI0Command(uint8_t cmd, uint32_t *data, uint32_t mosi_bits, uint32_t miso_bits) {
if (mosi_bits>(64*8))
return SPI_RESULT_ERR;
if (miso_bits>(64*8))
return SPI_RESULT_ERR;

// Calculate the number of data words (aka registers) that need to be copied
// to/from the SPI controller.
uint32_t mosi_words=mosi_bits/32;
uint32_t miso_words=miso_bits/32;
if (mosi_bits % 32 != 0)
mosi_words++;
if (miso_bits % 32 != 0)
miso_words++;

// Select user defined command mode in the controller
uint32_t spiu=SPIUCOMMAND; //SPI_USR_COMMAND

// Set the command byte to send
uint32_t spiu2 = ((7 & SPIMCOMMAND)<<SPILCOMMAND) | cmd;

uint32_t spiu1 = 0;
if (mosi_bits>0) {
// set the number of outgoing data bits to send
spiu1 |= ((mosi_bits-1) & SPIMMOSI) << SPILMOSI;
spiu |= SPIUMOSI; // SPI_USR_MOSI
}
if (miso_bits>0) {
// set the number of incoming bits to read
spiu1 |= ((miso_bits-1) & SPIMMISO) << SPILMISO;
spiu |= SPIUMISO; // SPI_USR_MISO
}

uint32_t spic = SPI0C;
// Select the most basic IO mode for maximum compatibility
// Some flash commands are only available in this mode.
spic &= ~(SPICQIO | SPICDIO | SPICQOUT | SPICDOUT | SPICAHB | SPICFASTRD);
spic |= (SPICRESANDRES | SPICSHARE | SPICWPR | SPIC2BSE);

SpiOpResult rc =_SPICommand(0,spic,spiu,spiu1,spiu2,data,mosi_words,miso_words);

if (rc==SPI_RESULT_OK) {
// clear any bits we did not read in the last word.
if (miso_bits % 32) {
data[miso_bits/32] &= ~(0xFFFFFFFF << (miso_bits % 32));
}
}
return rc;
}

} // namespace experimental
46 changes: 46 additions & 0 deletions cores/esp8266/spi_utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
spi_utils.h - SPI utility function
Copyright (c) 2015 Ivan Grokhotkov. All right reserved.

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.

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
*/


#ifndef SPI_UTILS_H
#define SPI_UTILS_H


#ifdef __cplusplus
extern "C" {
#endif

#include <stdint.h>

namespace experimental {
typedef enum {
SPI_RESULT_OK,
SPI_RESULT_ERR,
SPI_RESULT_TIMEOUT
} SpiOpResult;

SpiOpResult SPI0Command(uint8_t cmd, uint32_t *data, uint32_t mosi_bits, uint32_t miso_bits);
}

#ifdef __cplusplus
}
#endif


#endif //SPI_UTILS_H