diff options
Diffstat (limited to 'esp8266/hspi.c')
-rw-r--r-- | esp8266/hspi.c | 327 |
1 files changed, 327 insertions, 0 deletions
diff --git a/esp8266/hspi.c b/esp8266/hspi.c new file mode 100644 index 0000000000..7315dc8a12 --- /dev/null +++ b/esp8266/hspi.c @@ -0,0 +1,327 @@ +/* +* The MIT License (MIT) +* +* Copyright (c) 2015 David Ogilvy (MetalPhreak) +* Modified 2016 by Radomir Dopieralski +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#include "hspi.h" + +/* +Wrapper to setup HSPI/SPI GPIO pins and default SPI clock + spi_no - SPI (0) or HSPI (1) +Not used in Micropython. +*/ +void spi_init(uint8_t spi_no) { + spi_init_gpio(spi_no, SPI_CLK_USE_DIV); + spi_clock(spi_no, SPI_CLK_PREDIV, SPI_CLK_CNTDIV); + spi_tx_byte_order(spi_no, SPI_BYTE_ORDER_HIGH_TO_LOW); + spi_rx_byte_order(spi_no, SPI_BYTE_ORDER_HIGH_TO_LOW); + + SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_CS_SETUP|SPI_CS_HOLD); + CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_FLASH_MODE); +} + + +/* +Configures SPI mode parameters for clock edge and clock polarity. + spi_no - SPI (0) or HSPI (1) + spi_cpha - (0) Data is valid on clock leading edge + (1) Data is valid on clock trailing edge + spi_cpol - (0) Clock is low when inactive + (1) Clock is high when inactive +For Micropython this version is different from original. +*/ +void spi_mode(uint8_t spi_no, uint8_t spi_cpha, uint8_t spi_cpol) { + if (spi_cpol) { + SET_PERI_REG_MASK(SPI_PIN(HSPI), SPI_IDLE_EDGE); + } else { + CLEAR_PERI_REG_MASK(SPI_PIN(HSPI), SPI_IDLE_EDGE); + } + if (spi_cpha == spi_cpol) { + // Mode 3 - MOSI is set on falling edge of clock + // Mode 0 - MOSI is set on falling edge of clock + CLEAR_PERI_REG_MASK(SPI_USER(HSPI), SPI_CK_OUT_EDGE); + SET_PERI_REG_MASK(SPI_USER(HSPI), SPI_CK_I_EDGE); + } else { + // Mode 2 - MOSI is set on rising edge of clock + // Mode 1 - MOSI is set on rising edge of clock + SET_PERI_REG_MASK(SPI_USER(HSPI), SPI_CK_OUT_EDGE); + CLEAR_PERI_REG_MASK(SPI_USER(HSPI), SPI_CK_I_EDGE); + } +} + + +/* +Initialise the GPIO pins for use as SPI pins. + spi_no - SPI (0) or HSPI (1) + sysclk_as_spiclk - + SPI_CLK_80MHZ_NODIV (1) if using 80MHz for SPI clock. + SPI_CLK_USE_DIV (0) if using divider for lower speed. +*/ +void spi_init_gpio(uint8_t spi_no, uint8_t sysclk_as_spiclk) { + uint32_t clock_div_flag = 0; + if (sysclk_as_spiclk) { + clock_div_flag = 0x0001; + } + if (spi_no == SPI) { + // Set bit 8 if 80MHz sysclock required + WRITE_PERI_REG(PERIPHS_IO_MUX, 0x005 | (clock_div_flag<<8)); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, 1); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CMD_U, 1); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA0_U, 1); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA1_U, 1); + } else if (spi_no == HSPI) { + // Set bit 9 if 80MHz sysclock required + WRITE_PERI_REG(PERIPHS_IO_MUX, 0x105 | (clock_div_flag<<9)); + // GPIO12 is HSPI MISO pin (Master Data In) + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, 2); + // GPIO13 is HSPI MOSI pin (Master Data Out) + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, 2); + // GPIO14 is HSPI CLK pin (Clock) + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, 2); + // GPIO15 is HSPI CS pin (Chip Select / Slave Select) + // In Micropython, we are handling CS ourself in drivers. + // PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, 2); + } +} + + +/* +Set up the control registers for the SPI clock + spi_no - SPI (0) or HSPI (1) + prediv - predivider value (actual division value) + cntdiv - postdivider value (actual division value) +Set either divider to 0 to disable all division (80MHz sysclock) +*/ +void spi_clock(uint8_t spi_no, uint16_t prediv, uint8_t cntdiv) { + if (prediv == 0 || cntdiv == 0) { + WRITE_PERI_REG(SPI_CLOCK(spi_no), SPI_CLK_EQU_SYSCLK); + } else { + WRITE_PERI_REG(SPI_CLOCK(spi_no), + (((prediv - 1) & SPI_CLKDIV_PRE) << SPI_CLKDIV_PRE_S) | + (((cntdiv - 1) & SPI_CLKCNT_N) << SPI_CLKCNT_N_S) | + (((cntdiv >> 1) & SPI_CLKCNT_H) << SPI_CLKCNT_H_S) | + ((0 & SPI_CLKCNT_L) << SPI_CLKCNT_L_S) + ); + } +} + + +/* +Setup the byte order for shifting data out of buffer + spi_no - SPI (0) or HSPI (1) + byte_order - + SPI_BYTE_ORDER_HIGH_TO_LOW (1) + Data is sent out starting with Bit31 and down to Bit0 + SPI_BYTE_ORDER_LOW_TO_HIGH (0) + Data is sent out starting with the lowest BYTE, from MSB to LSB, + followed by the second lowest BYTE, from MSB to LSB, followed by + the second highest BYTE, from MSB to LSB, followed by the highest + BYTE, from MSB to LSB 0xABCDEFGH would be sent as 0xGHEFCDAB. +*/ +void spi_tx_byte_order(uint8_t spi_no, uint8_t byte_order) { + if (byte_order) { + SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_WR_BYTE_ORDER); + } else { + CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_WR_BYTE_ORDER); + } +} + + +/* +Setup the byte order for shifting data into buffer + spi_no - SPI (0) or HSPI (1) + byte_order - + SPI_BYTE_ORDER_HIGH_TO_LOW (1) + Data is read in starting with Bit31 and down to Bit0 + SPI_BYTE_ORDER_LOW_TO_HIGH (0) + Data is read in starting with the lowest BYTE, from MSB to LSB, + followed by the second lowest BYTE, from MSB to LSB, followed by + the second highest BYTE, from MSB to LSB, followed by the highest + BYTE, from MSB to LSB 0xABCDEFGH would be read as 0xGHEFCDAB +*/ +void spi_rx_byte_order(uint8_t spi_no, uint8_t byte_order) { + if (byte_order) { + SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_RD_BYTE_ORDER); + } else { + CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_RD_BYTE_ORDER); + } +} + + +/* +SPI transaction function + spi_no - SPI (0) or HSPI (1) + cmd_bits - actual number of bits to transmit + cmd_data - command data + addr_bits - actual number of bits to transmit + addr_data - address data + dout_bits - actual number of bits to transmit + dout_data - output data + din_bits - actual number of bits to receive +Returns: read data - uint32_t containing read in data only if RX was set + 0 - something went wrong (or actual read data was 0) + 1 - data sent ok (or actual read data is 1) +Note: all data is assumed to be stored in the lower bits of the data variables +(for anything <32 bits). +*/ +uint32_t spi_transaction(uint8_t spi_no, uint8_t cmd_bits, uint16_t cmd_data, + uint32_t addr_bits, uint32_t addr_data, + uint32_t dout_bits, uint32_t dout_data, + uint32_t din_bits, uint32_t dummy_bits) { + while (spi_busy(spi_no)) {}; // Wait for SPI to be ready + +// Enable SPI Functions + // Disable MOSI, MISO, ADDR, COMMAND, DUMMY in case previously set. + CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MOSI | SPI_USR_MISO | + SPI_USR_COMMAND | SPI_USR_ADDR | SPI_USR_DUMMY); + + // Enable functions based on number of bits. 0 bits = disabled. + // This is rather inefficient but allows for a very generic function. + // CMD ADDR and MOSI are set below to save on an extra if statement. + if (din_bits) { + SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MISO); + } + if (dummy_bits) { + SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_DUMMY); + } + +// Setup Bitlengths + WRITE_PERI_REG(SPI_USER1(spi_no), + // Number of bits in Address + ((addr_bits - 1) & SPI_USR_ADDR_BITLEN) << SPI_USR_ADDR_BITLEN_S | + // Number of bits to Send + ((dout_bits - 1) & SPI_USR_MOSI_BITLEN) << SPI_USR_MOSI_BITLEN_S | + // Number of bits to receive + ((din_bits - 1) & SPI_USR_MISO_BITLEN) << SPI_USR_MISO_BITLEN_S | + // Number of Dummy bits to insert + ((dummy_bits - 1) & SPI_USR_DUMMY_CYCLELEN) << SPI_USR_DUMMY_CYCLELEN_S); + +// Setup Command Data + if (cmd_bits) { + // Enable COMMAND function in SPI module + SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_COMMAND); + // Align command data to high bits + uint16_t command = cmd_data << (16-cmd_bits); + // Swap byte order + command = ((command>>8)&0xff) | ((command<<8)&0xff00); + WRITE_PERI_REG(SPI_USER2(spi_no), ( + (((cmd_bits - 1) & SPI_USR_COMMAND_BITLEN) << SPI_USR_COMMAND_BITLEN_S) | + (command & SPI_USR_COMMAND_VALUE) + )); + } + +// Setup Address Data + if (addr_bits) { + // Enable ADDRess function in SPI module + SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_ADDR); + // Align address data to high bits + WRITE_PERI_REG(SPI_ADDR(spi_no), addr_data << (32 - addr_bits)); + } + +// Setup DOUT data + if (dout_bits) { + // Enable MOSI function in SPI module + SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MOSI); + // Copy data to W0 + if (READ_PERI_REG(SPI_USER(spi_no))&SPI_WR_BYTE_ORDER) { + WRITE_PERI_REG(SPI_W0(spi_no), dout_data << (32 - dout_bits)); + } else { + uint8_t dout_extra_bits = dout_bits%8; + + if (dout_extra_bits) { + // If your data isn't a byte multiple (8/16/24/32 bits) and you + // don't have SPI_WR_BYTE_ORDER set, you need this to move the + // non-8bit remainder to the MSBs. Not sure if there's even a use + // case for this, but it's here if you need it... For example, + // 0xDA4 12 bits without SPI_WR_BYTE_ORDER would usually be output + // as if it were 0x0DA4, of which 0xA4, and then 0x0 would be + // shifted out (first 8 bits of low byte, then 4 MSB bits of high + // byte - ie reverse byte order). + // The code below shifts it out as 0xA4 followed by 0xD as you + // might require. + WRITE_PERI_REG(SPI_W0(spi_no), ( + (0xFFFFFFFF << (dout_bits - dout_extra_bits) & dout_data) + << (8-dout_extra_bits) | + ((0xFFFFFFFF >> (32 - (dout_bits - dout_extra_bits))) + & dout_data) + )); + } else { + WRITE_PERI_REG(SPI_W0(spi_no), dout_data); + } + } +} + +// Begin SPI Transaction + SET_PERI_REG_MASK(SPI_CMD(spi_no), SPI_USR); + +// Return DIN data + if (din_bits) { + while (spi_busy(spi_no)) {}; // Wait for SPI transaction to complete + if (READ_PERI_REG(SPI_USER(spi_no))&SPI_RD_BYTE_ORDER) { + // Assuming data in is written to MSB. TBC + return READ_PERI_REG(SPI_W0(spi_no)) >> (32 - din_bits); + } else { + // Read in the same way as DOUT is sent. Note existing contents of + // SPI_W0 remain unless overwritten! + return READ_PERI_REG(SPI_W0(spi_no)); + } + return 0; // Something went wrong + } + + // Transaction completed + return 1; // Success +} + + +/* +Just do minimal work needed to send 8 bits. +*/ +inline void spi_tx8fast(uint8_t spi_no, uint8_t dout_data) { + while (spi_busy(spi_no)) {}; // Wait for SPI to be ready + +// Enable SPI Functions + // Disable MOSI, MISO, ADDR, COMMAND, DUMMY in case previously set. + CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MOSI | SPI_USR_MISO | + SPI_USR_COMMAND | SPI_USR_ADDR | SPI_USR_DUMMY); + +// Setup Bitlengths + WRITE_PERI_REG(SPI_USER1(spi_no), + // Number of bits to Send + ((8 - 1) & SPI_USR_MOSI_BITLEN) << SPI_USR_MOSI_BITLEN_S | + // Number of bits to receive + ((8 - 1) & SPI_USR_MISO_BITLEN) << SPI_USR_MISO_BITLEN_S); + + +// Setup DOUT data + // Enable MOSI function in SPI module + SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MOSI); + // Copy data to W0 + if (READ_PERI_REG(SPI_USER(spi_no)) & SPI_WR_BYTE_ORDER) { + WRITE_PERI_REG(SPI_W0(spi_no), dout_data << (32 - 8)); + } else { + WRITE_PERI_REG(SPI_W0(spi_no), dout_data); + } + +// Begin SPI Transaction + SET_PERI_REG_MASK(SPI_CMD(spi_no), SPI_USR); +} |