|
| 1 | +/* |
| 2 | +* The MIT License (MIT) |
| 3 | +* |
| 4 | +* Copyright (c) 2015 David Ogilvy (MetalPhreak) |
| 5 | +* Modified 2016 by Radomir Dopieralski |
| 6 | +* |
| 7 | +* Permission is hereby granted, free of charge, to any person obtaining a copy |
| 8 | +* of this software and associated documentation files (the "Software"), to deal |
| 9 | +* in the Software without restriction, including without limitation the rights |
| 10 | +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 11 | +* copies of the Software, and to permit persons to whom the Software is |
| 12 | +* furnished to do so, subject to the following conditions: |
| 13 | +* |
| 14 | +* The above copyright notice and this permission notice shall be included in all |
| 15 | +* copies or substantial portions of the Software. |
| 16 | +* |
| 17 | +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 18 | +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 19 | +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 20 | +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 21 | +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 22 | +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| 23 | +* SOFTWARE. |
| 24 | +*/ |
| 25 | + |
| 26 | +#include "hspi.h" |
| 27 | + |
| 28 | +/* |
| 29 | +Wrapper to setup HSPI/SPI GPIO pins and default SPI clock |
| 30 | + spi_no - SPI (0) or HSPI (1) |
| 31 | +Not used in Micropython. |
| 32 | +*/ |
| 33 | +void spi_init(uint8_t spi_no) { |
| 34 | + spi_init_gpio(spi_no, SPI_CLK_USE_DIV); |
| 35 | + spi_clock(spi_no, SPI_CLK_PREDIV, SPI_CLK_CNTDIV); |
| 36 | + spi_tx_byte_order(spi_no, SPI_BYTE_ORDER_HIGH_TO_LOW); |
| 37 | + spi_rx_byte_order(spi_no, SPI_BYTE_ORDER_HIGH_TO_LOW); |
| 38 | + |
| 39 | + SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_CS_SETUP|SPI_CS_HOLD); |
| 40 | + CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_FLASH_MODE); |
| 41 | +} |
| 42 | + |
| 43 | + |
| 44 | +/* |
| 45 | +Configures SPI mode parameters for clock edge and clock polarity. |
| 46 | + spi_no - SPI (0) or HSPI (1) |
| 47 | + spi_cpha - (0) Data is valid on clock leading edge |
| 48 | + (1) Data is valid on clock trailing edge |
| 49 | + spi_cpol - (0) Clock is low when inactive |
| 50 | + (1) Clock is high when inactive |
| 51 | +For Micropython this version is different from original. |
| 52 | +*/ |
| 53 | +void spi_mode(uint8_t spi_no, uint8_t spi_cpha, uint8_t spi_cpol) { |
| 54 | + if (spi_cpol) { |
| 55 | + SET_PERI_REG_MASK(SPI_PIN(HSPI), SPI_IDLE_EDGE); |
| 56 | + } else { |
| 57 | + CLEAR_PERI_REG_MASK(SPI_PIN(HSPI), SPI_IDLE_EDGE); |
| 58 | + } |
| 59 | + if (spi_cpha == spi_cpol) { |
| 60 | + // Mode 3 - MOSI is set on falling edge of clock |
| 61 | + // Mode 0 - MOSI is set on falling edge of clock |
| 62 | + CLEAR_PERI_REG_MASK(SPI_USER(HSPI), SPI_CK_OUT_EDGE); |
| 63 | + SET_PERI_REG_MASK(SPI_USER(HSPI), SPI_CK_I_EDGE); |
| 64 | + } else { |
| 65 | + // Mode 2 - MOSI is set on rising edge of clock |
| 66 | + // Mode 1 - MOSI is set on rising edge of clock |
| 67 | + SET_PERI_REG_MASK(SPI_USER(HSPI), SPI_CK_OUT_EDGE); |
| 68 | + CLEAR_PERI_REG_MASK(SPI_USER(HSPI), SPI_CK_I_EDGE); |
| 69 | + } |
| 70 | +} |
| 71 | + |
| 72 | + |
| 73 | +/* |
| 74 | +Initialise the GPIO pins for use as SPI pins. |
| 75 | + spi_no - SPI (0) or HSPI (1) |
| 76 | + sysclk_as_spiclk - |
| 77 | + SPI_CLK_80MHZ_NODIV (1) if using 80MHz for SPI clock. |
| 78 | + SPI_CLK_USE_DIV (0) if using divider for lower speed. |
| 79 | +*/ |
| 80 | +void spi_init_gpio(uint8_t spi_no, uint8_t sysclk_as_spiclk) { |
| 81 | + uint32_t clock_div_flag = 0; |
| 82 | + if (sysclk_as_spiclk) { |
| 83 | + clock_div_flag = 0x0001; |
| 84 | + } |
| 85 | + if (spi_no == SPI) { |
| 86 | + // Set bit 8 if 80MHz sysclock required |
| 87 | + WRITE_PERI_REG(PERIPHS_IO_MUX, 0x005 | (clock_div_flag<<8)); |
| 88 | + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, 1); |
| 89 | + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CMD_U, 1); |
| 90 | + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA0_U, 1); |
| 91 | + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA1_U, 1); |
| 92 | + } else if (spi_no == HSPI) { |
| 93 | + // Set bit 9 if 80MHz sysclock required |
| 94 | + WRITE_PERI_REG(PERIPHS_IO_MUX, 0x105 | (clock_div_flag<<9)); |
| 95 | + // GPIO12 is HSPI MISO pin (Master Data In) |
| 96 | + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, 2); |
| 97 | + // GPIO13 is HSPI MOSI pin (Master Data Out) |
| 98 | + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, 2); |
| 99 | + // GPIO14 is HSPI CLK pin (Clock) |
| 100 | + PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, 2); |
| 101 | + // GPIO15 is HSPI CS pin (Chip Select / Slave Select) |
| 102 | + // In Micropython, we are handling CS ourself in drivers. |
| 103 | + // PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U, 2); |
| 104 | + } |
| 105 | +} |
| 106 | + |
| 107 | + |
| 108 | +/* |
| 109 | +Set up the control registers for the SPI clock |
| 110 | + spi_no - SPI (0) or HSPI (1) |
| 111 | + prediv - predivider value (actual division value) |
| 112 | + cntdiv - postdivider value (actual division value) |
| 113 | +Set either divider to 0 to disable all division (80MHz sysclock) |
| 114 | +*/ |
| 115 | +void spi_clock(uint8_t spi_no, uint16_t prediv, uint8_t cntdiv) { |
| 116 | + if (prediv == 0 || cntdiv == 0) { |
| 117 | + WRITE_PERI_REG(SPI_CLOCK(spi_no), SPI_CLK_EQU_SYSCLK); |
| 118 | + } else { |
| 119 | + WRITE_PERI_REG(SPI_CLOCK(spi_no), |
| 120 | + (((prediv - 1) & SPI_CLKDIV_PRE) << SPI_CLKDIV_PRE_S) | |
| 121 | + (((cntdiv - 1) & SPI_CLKCNT_N) << SPI_CLKCNT_N_S) | |
| 122 | + (((cntdiv >> 1) & SPI_CLKCNT_H) << SPI_CLKCNT_H_S) | |
| 123 | + ((0 & SPI_CLKCNT_L) << SPI_CLKCNT_L_S) |
| 124 | + ); |
| 125 | + } |
| 126 | +} |
| 127 | + |
| 128 | + |
| 129 | +/* |
| 130 | +Setup the byte order for shifting data out of buffer |
| 131 | + spi_no - SPI (0) or HSPI (1) |
| 132 | + byte_order - |
| 133 | + SPI_BYTE_ORDER_HIGH_TO_LOW (1) |
| 134 | + Data is sent out starting with Bit31 and down to Bit0 |
| 135 | + SPI_BYTE_ORDER_LOW_TO_HIGH (0) |
| 136 | + Data is sent out starting with the lowest BYTE, from MSB to LSB, |
| 137 | + followed by the second lowest BYTE, from MSB to LSB, followed by |
| 138 | + the second highest BYTE, from MSB to LSB, followed by the highest |
| 139 | + BYTE, from MSB to LSB 0xABCDEFGH would be sent as 0xGHEFCDAB. |
| 140 | +*/ |
| 141 | +void spi_tx_byte_order(uint8_t spi_no, uint8_t byte_order) { |
| 142 | + if (byte_order) { |
| 143 | + SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_WR_BYTE_ORDER); |
| 144 | + } else { |
| 145 | + CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_WR_BYTE_ORDER); |
| 146 | + } |
| 147 | +} |
| 148 | + |
| 149 | + |
| 150 | +/* |
| 151 | +Setup the byte order for shifting data into buffer |
| 152 | + spi_no - SPI (0) or HSPI (1) |
| 153 | + byte_order - |
| 154 | + SPI_BYTE_ORDER_HIGH_TO_LOW (1) |
| 155 | + Data is read in starting with Bit31 and down to Bit0 |
| 156 | + SPI_BYTE_ORDER_LOW_TO_HIGH (0) |
| 157 | + Data is read in starting with the lowest BYTE, from MSB to LSB, |
| 158 | + followed by the second lowest BYTE, from MSB to LSB, followed by |
| 159 | + the second highest BYTE, from MSB to LSB, followed by the highest |
| 160 | + BYTE, from MSB to LSB 0xABCDEFGH would be read as 0xGHEFCDAB |
| 161 | +*/ |
| 162 | +void spi_rx_byte_order(uint8_t spi_no, uint8_t byte_order) { |
| 163 | + if (byte_order) { |
| 164 | + SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_RD_BYTE_ORDER); |
| 165 | + } else { |
| 166 | + CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_RD_BYTE_ORDER); |
| 167 | + } |
| 168 | +} |
| 169 | + |
| 170 | + |
| 171 | +/* |
| 172 | +SPI transaction function |
| 173 | + spi_no - SPI (0) or HSPI (1) |
| 174 | + cmd_bits - actual number of bits to transmit |
| 175 | + cmd_data - command data |
| 176 | + addr_bits - actual number of bits to transmit |
| 177 | + addr_data - address data |
| 178 | + dout_bits - actual number of bits to transmit |
| 179 | + dout_data - output data |
| 180 | + din_bits - actual number of bits to receive |
| 181 | +Returns: read data - uint32_t containing read in data only if RX was set |
| 182 | + 0 - something went wrong (or actual read data was 0) |
| 183 | + 1 - data sent ok (or actual read data is 1) |
| 184 | +Note: all data is assumed to be stored in the lower bits of the data variables |
| 185 | +(for anything <32 bits). |
| 186 | +*/ |
| 187 | +uint32_t spi_transaction(uint8_t spi_no, uint8_t cmd_bits, uint16_t cmd_data, |
| 188 | + uint32_t addr_bits, uint32_t addr_data, |
| 189 | + uint32_t dout_bits, uint32_t dout_data, |
| 190 | + uint32_t din_bits, uint32_t dummy_bits) { |
| 191 | + while (spi_busy(spi_no)) {}; // Wait for SPI to be ready |
| 192 | + |
| 193 | +// Enable SPI Functions |
| 194 | + // Disable MOSI, MISO, ADDR, COMMAND, DUMMY in case previously set. |
| 195 | + CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MOSI | SPI_USR_MISO | |
| 196 | + SPI_USR_COMMAND | SPI_USR_ADDR | SPI_USR_DUMMY); |
| 197 | + |
| 198 | + // Enable functions based on number of bits. 0 bits = disabled. |
| 199 | + // This is rather inefficient but allows for a very generic function. |
| 200 | + // CMD ADDR and MOSI are set below to save on an extra if statement. |
| 201 | + if (din_bits) { |
| 202 | + SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MISO); |
| 203 | + } |
| 204 | + if (dummy_bits) { |
| 205 | + SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_DUMMY); |
| 206 | + } |
| 207 | + |
| 208 | +// Setup Bitlengths |
| 209 | + WRITE_PERI_REG(SPI_USER1(spi_no), |
| 210 | + // Number of bits in Address |
| 211 | + ((addr_bits - 1) & SPI_USR_ADDR_BITLEN) << SPI_USR_ADDR_BITLEN_S | |
| 212 | + // Number of bits to Send |
| 213 | + ((dout_bits - 1) & SPI_USR_MOSI_BITLEN) << SPI_USR_MOSI_BITLEN_S | |
| 214 | + // Number of bits to receive |
| 215 | + ((din_bits - 1) & SPI_USR_MISO_BITLEN) << SPI_USR_MISO_BITLEN_S | |
| 216 | + // Number of Dummy bits to insert |
| 217 | + ((dummy_bits - 1) & SPI_USR_DUMMY_CYCLELEN) << SPI_USR_DUMMY_CYCLELEN_S); |
| 218 | + |
| 219 | +// Setup Command Data |
| 220 | + if (cmd_bits) { |
| 221 | + // Enable COMMAND function in SPI module |
| 222 | + SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_COMMAND); |
| 223 | + // Align command data to high bits |
| 224 | + uint16_t command = cmd_data << (16-cmd_bits); |
| 225 | + // Swap byte order |
| 226 | + command = ((command>>8)&0xff) | ((command<<8)&0xff00); |
| 227 | + WRITE_PERI_REG(SPI_USER2(spi_no), ( |
| 228 | + (((cmd_bits - 1) & SPI_USR_COMMAND_BITLEN) << SPI_USR_COMMAND_BITLEN_S) | |
| 229 | + (command & SPI_USR_COMMAND_VALUE) |
| 230 | + )); |
| 231 | + } |
| 232 | + |
| 233 | +// Setup Address Data |
| 234 | + if (addr_bits) { |
| 235 | + // Enable ADDRess function in SPI module |
| 236 | + SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_ADDR); |
| 237 | + // Align address data to high bits |
| 238 | + WRITE_PERI_REG(SPI_ADDR(spi_no), addr_data << (32 - addr_bits)); |
| 239 | + } |
| 240 | + |
| 241 | +// Setup DOUT data |
| 242 | + if (dout_bits) { |
| 243 | + // Enable MOSI function in SPI module |
| 244 | + SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MOSI); |
| 245 | + // Copy data to W0 |
| 246 | + if (READ_PERI_REG(SPI_USER(spi_no))&SPI_WR_BYTE_ORDER) { |
| 247 | + WRITE_PERI_REG(SPI_W0(spi_no), dout_data << (32 - dout_bits)); |
| 248 | + } else { |
| 249 | + uint8_t dout_extra_bits = dout_bits%8; |
| 250 | + |
| 251 | + if (dout_extra_bits) { |
| 252 | + // If your data isn't a byte multiple (8/16/24/32 bits) and you |
| 253 | + // don't have SPI_WR_BYTE_ORDER set, you need this to move the |
| 254 | + // non-8bit remainder to the MSBs. Not sure if there's even a use |
| 255 | + // case for this, but it's here if you need it... For example, |
| 256 | + // 0xDA4 12 bits without SPI_WR_BYTE_ORDER would usually be output |
| 257 | + // as if it were 0x0DA4, of which 0xA4, and then 0x0 would be |
| 258 | + // shifted out (first 8 bits of low byte, then 4 MSB bits of high |
| 259 | + // byte - ie reverse byte order). |
| 260 | + // The code below shifts it out as 0xA4 followed by 0xD as you |
| 261 | + // might require. |
| 262 | + WRITE_PERI_REG(SPI_W0(spi_no), ( |
| 263 | + (0xFFFFFFFF << (dout_bits - dout_extra_bits) & dout_data) |
| 264 | + << (8-dout_extra_bits) | |
| 265 | + ((0xFFFFFFFF >> (32 - (dout_bits - dout_extra_bits))) |
| 266 | + & dout_data) |
| 267 | + )); |
| 268 | + } else { |
| 269 | + WRITE_PERI_REG(SPI_W0(spi_no), dout_data); |
| 270 | + } |
| 271 | + } |
| 272 | +} |
| 273 | + |
| 274 | +// Begin SPI Transaction |
| 275 | + SET_PERI_REG_MASK(SPI_CMD(spi_no), SPI_USR); |
| 276 | + |
| 277 | +// Return DIN data |
| 278 | + if (din_bits) { |
| 279 | + while (spi_busy(spi_no)) {}; // Wait for SPI transaction to complete |
| 280 | + if (READ_PERI_REG(SPI_USER(spi_no))&SPI_RD_BYTE_ORDER) { |
| 281 | + // Assuming data in is written to MSB. TBC |
| 282 | + return READ_PERI_REG(SPI_W0(spi_no)) >> (32 - din_bits); |
| 283 | + } else { |
| 284 | + // Read in the same way as DOUT is sent. Note existing contents of |
| 285 | + // SPI_W0 remain unless overwritten! |
| 286 | + return READ_PERI_REG(SPI_W0(spi_no)); |
| 287 | + } |
| 288 | + return 0; // Something went wrong |
| 289 | + } |
| 290 | + |
| 291 | + // Transaction completed |
| 292 | + return 1; // Success |
| 293 | +} |
| 294 | + |
| 295 | + |
| 296 | +/* |
| 297 | +Just do minimal work needed to send 8 bits. |
| 298 | +*/ |
| 299 | +inline void spi_tx8fast(uint8_t spi_no, uint8_t dout_data) { |
| 300 | + while (spi_busy(spi_no)) {}; // Wait for SPI to be ready |
| 301 | + |
| 302 | +// Enable SPI Functions |
| 303 | + // Disable MOSI, MISO, ADDR, COMMAND, DUMMY in case previously set. |
| 304 | + CLEAR_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MOSI | SPI_USR_MISO | |
| 305 | + SPI_USR_COMMAND | SPI_USR_ADDR | SPI_USR_DUMMY); |
| 306 | + |
| 307 | +// Setup Bitlengths |
| 308 | + WRITE_PERI_REG(SPI_USER1(spi_no), |
| 309 | + // Number of bits to Send |
| 310 | + ((8 - 1) & SPI_USR_MOSI_BITLEN) << SPI_USR_MOSI_BITLEN_S | |
| 311 | + // Number of bits to receive |
| 312 | + ((8 - 1) & SPI_USR_MISO_BITLEN) << SPI_USR_MISO_BITLEN_S); |
| 313 | + |
| 314 | + |
| 315 | +// Setup DOUT data |
| 316 | + // Enable MOSI function in SPI module |
| 317 | + SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_MOSI); |
| 318 | + // Copy data to W0 |
| 319 | + if (READ_PERI_REG(SPI_USER(spi_no)) & SPI_WR_BYTE_ORDER) { |
| 320 | + WRITE_PERI_REG(SPI_W0(spi_no), dout_data << (32 - 8)); |
| 321 | + } else { |
| 322 | + WRITE_PERI_REG(SPI_W0(spi_no), dout_data); |
| 323 | + } |
| 324 | + |
| 325 | +// Begin SPI Transaction |
| 326 | + SET_PERI_REG_MASK(SPI_CMD(spi_no), SPI_USR); |
| 327 | +} |
0 commit comments