| extern void *callback_up; |
| |
| // #include "stdint.h" |
| // #include "stdlib.h" |
| #include "pin_map.h" |
| |
| #define NOPULL 0 |
| #define GPIO_INPUT 0 |
| #define GPIO_OUTPUT 1 |
| |
| #define ESP8266_REG(addr) *((volatile uint32_t *)(0x60000000+(addr))) |
| #define ESP8266_CLOCK 80000000UL |
| #define HSPI_REG(offset) ESP8266_REG(0x100 + offset) |
| #define PINMUX_REG(offset) ESP8266_REG(0x800 + offset) |
| |
| #define GP_MUX PINMUX_REG(0x00) |
| |
| #define HSPI_FUNCTION 2 |
| #define GPIO_FUNCTION 3 |
| |
| // These are the labeled pin numbers on the NodeMCU module, |
| // not the ESP8266 GPIO numbers |
| #define HSPI_SCK_PIN 5 |
| #define HSPI_MISO_PIN 6 |
| #define HSPI_MOSI_PIN 7 |
| #define HSPI_HW_CS_PIN 8 |
| |
| |
| static void pin_function_select(int pinnum, int function) |
| { |
| volatile uint32_t *pinmux = (uint32_t *)pin_mux[pinnum]; |
| ets_delay_us(2000); |
| if (function & 4) { |
| function = 0x10 | (function & ~4); |
| } |
| *pinmux = (*pinmux & ~0x130) | (function << 4); |
| } |
| |
| #define HSPI_CMD HSPI_REG(0x00) |
| // #define HSPIA HSPI_REG(0x04) |
| #define HSPI_CTRL HSPI_REG(0x08) |
| #define HSPI_CTRL1 HSPI_REG(0x0C) |
| // #define HSPI_RS HSPI_REG(0x10) |
| // #define HSPI_C2 HSPI_REG(0x14) |
| #define HSPI_CLK HSPI_REG(0x18) |
| #define HSPI_USR HSPI_REG(0x1C) |
| #define HSPI_USR1 HSPI_REG(0x20) |
| // #define HSPI_USR2 HSPI_REG(0x24) |
| // #define HSPI_WS HSPI_REG(0x28) |
| #define HSPI_POL HSPI_REG(0x2C) |
| // #define HSPI_S HSPI_REG(0x30) /* Total of 4 registers */ |
| #define HSPI_DATA HSPI_REG(0x40) /* Total of 16 registers */ |
| // #define HSPI_E0 HSPI_REG(0xF0) /* Total of 4 registers */ |
| |
| // CMD register bits |
| #define SPI_BUSY (1 << 18) |
| |
| // USR1 register bits and masks |
| // #define SPI_LEN_COMMAND 28 //4 bit in SPIxU2 default 7 (8bit) |
| // #define SPI_LEN_ADDR 26 //6 bit in SPIxU1 default:23 (24bit) |
| // #define SPI_LEN_DUMMY 0 //8 bit in SPIxU1 default:0 (0 cycles) |
| |
| #define SPI_MISO_SHIFT 8 //9 bit in SPIxU1 default:0 (1bit) |
| #define SPI_MOSI_SHIFT 17 //9 bit in SPIxU1 default:0 (1bit) |
| |
| // #define SPI_MASK_COMMAND 0xF |
| // #define SPI_MASK_ADDR 0x3F |
| // #define SPI_MASK_DUMMY 0xFF |
| |
| #define SPI_MASK_MISO 0x1FF |
| #define SPI_MASK_MOSI 0x1FF |
| |
| #define SPI_CPOL (1 << 29) |
| |
| #define SPI_WR_BIT_ORDER (1 << 26) |
| #define SPI_RD_BIT_ORDER (1 << 25) |
| // #define SPI_QIO_MODE (1 << 24) |
| // #define SPI_DIO_MODE (1 << 23) |
| // #define SPI_TWO_BYTE_STATUS_EN (1 << 22) |
| // #define SPI_WP_REG (1 << 21) |
| // #define SPI_QOUT_MODE (1 << 20) |
| // #define SPI_SHARE_BUS (1 << 19) |
| // #define SPI_HOLD_MODE (1 << 18) |
| // #define SPI_ENABLE_AHB (1 << 17) |
| // #define SPI_SST_AAI (1 << 16) |
| // #define SPI_RESANDRES (1 << 15) |
| // #define SPI_DOUT_MODE (1 << 14) |
| // #define SPI_FASTRD_MODE (1 << 13) |
| |
| |
| #define SPI_USR_COMMAND (1 << 31) |
| // #define SPI_USR_ADDR (1 << 30) |
| #define SPI_USR_MISO (1 << 28) // MISO phase |
| #define SPI_USR_MOSI (1 << 27) //MOSI phase |
| // #define SPI_USR_DUMMY_IDLE (1 << 26) //SPI_USR_DUMMY_IDLE |
| // #define SPI_USR_DOUT_HIGHPART (1 << 25) //MOSI phase uses W8-W15 |
| // #define SPI_USR_DIN_HIGHPART (1 << 24) //MISO phase uses W8-W15 |
| // #define SPI_USR_PREP_HOLD (1 << 23) |
| // #define SPI_USR_CMD_HOLD (1 << 22) |
| // #define SPI_USR_ADDR_HOLD (1 << 21) |
| // #define SPI_USR_DUMMY_HOLD (1 << 20) |
| // #define SPI_USR_DIN_HOLD (1 << 19) |
| // #define SPI_USR_DOUT_HOLD (1 << 18) |
| // #define SPI_USR_HOLD_POL (1 << 17) |
| // #define SPI_SIO (1 << 16) |
| // #define SPI_FWRITE_QIO (1 << 15) |
| // #define SPI_FWRITE_DIO (1 << 14) |
| // #define SPI_FWRITE_QUAD (1 << 13) |
| // #define SPI_FWRITE_DUAL (1 << 12) |
| // #define SPI_WR_BYTE_ORDER (1 << 11) |
| // #define SPI_RD_BYTE_ORDER (1 << 10) |
| // #define SPI_AHB_ENDIAN_MODE 0x3 |
| // #define SPI_AHB_ENDIAN_MODE_S_S 8 |
| #define SPI_CK_OUT_EDGE (1 << 7) // 0 for falling, 1 for rising |
| #define SPI_CK_I_EDGE (1 << 6) // 0 for falling, 1 for rising |
| #define SPI_CS_SETUP (1 << 5) // |
| #define SPI_CS_HOLD (1 << 4) |
| // #define SPIUAHBUCMD (1 << 3) //SPI_AHB_USR_COMMAND |
| // #define SPIUAHBUCMD4B (1 << 1) //SPI_AHB_USR_COMMAND_4BYTE |
| #define SPI_DOUTDIN (1 << 0) |
| |
| typedef union { |
| uint32_t regValue; |
| struct { |
| unsigned regL :6; |
| unsigned regH :6; |
| unsigned regN :6; |
| unsigned regPre :13; |
| unsigned regEQU :1; |
| }; |
| } spiClk_t; |
| |
| static uint32_t ClkRegToFreq(spiClk_t * reg) { |
| return (ESP8266_CLOCK / ((reg->regPre + 1) * (reg->regN + 1))); |
| } |
| |
| static void setClockDivider(uint32_t clockDiv) { |
| if(clockDiv == 0x80000000) { |
| GP_MUX |= (1 << 9); // Set bit 9 if sysclock required |
| } else { |
| GP_MUX &= ~(1 << 9); |
| } |
| HSPI_CLK = clockDiv; |
| } |
| |
| void spi_setFrequency(uint32_t freq) { |
| static uint32_t lastSetFrequency = 0; |
| static uint32_t lastSetRegister = 0; |
| |
| if(freq >= ESP8266_CLOCK) { |
| setClockDivider(0x80000000); |
| return; |
| } |
| |
| if(lastSetFrequency == freq && lastSetRegister == HSPI_CLK) { |
| // do nothing (speed optimization) |
| return; |
| } |
| |
| const spiClk_t minFreqReg = { 0x7FFFF000 }; |
| uint32_t minFreq = ClkRegToFreq((spiClk_t*) &minFreqReg); |
| if(freq < minFreq) { |
| // use minimum possible clock |
| setClockDivider(minFreqReg.regValue); |
| lastSetRegister = HSPI_CLK; |
| lastSetFrequency = freq; |
| return; |
| } |
| |
| 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 easiest and fastest way) |
| calPre = (((ESP8266_CLOCK / (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(®); |
| //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! |
| bestReg.regValue = reg.regValue; |
| break; |
| } else if(calFreq < (int32_t) freq) { |
| // never go over the requested frequency |
| if(abs(freq - calFreq) < abs(freq - bestFreq)) { |
| bestFreq = calFreq; |
| bestReg.regValue = reg.regValue; |
| } |
| } |
| } |
| 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 = HSPI_CLK; |
| lastSetFrequency = freq; |
| } |
| |
| // csGPIO is -1 for hardware controlled chip select, otherwise it is the |
| // chip select GPIO pin number |
| |
| static int spi_csGPIO; |
| |
| void spi_open(int csGPIO, uint32_t clock, uint8_t msbfirst, uint8_t dataMode) |
| { |
| spi_csGPIO = csGPIO; |
| |
| HSPI_CTRL = 0; |
| // setFrequency(1000000); ///< 1MHz |
| HSPI_USR = SPI_USR_MOSI | SPI_DOUTDIN | SPI_CK_I_EDGE; |
| HSPI_USR1 = (7 << SPI_MOSI_SHIFT) | (7 << SPI_MISO_SHIFT); |
| HSPI_CTRL1 = 0; |
| |
| spi_setFrequency(clock); |
| |
| if(msbfirst) { |
| HSPI_CTRL &= ~(SPI_WR_BIT_ORDER | SPI_RD_BIT_ORDER); |
| } else { |
| HSPI_CTRL |= (SPI_WR_BIT_ORDER | SPI_RD_BIT_ORDER); |
| } |
| |
| ets_delay_us(1000); |
| // Clock phase |
| if(dataMode & 0x01) { |
| HSPI_USR |= (SPI_CK_OUT_EDGE); |
| } else { |
| HSPI_USR &= ~(SPI_CK_OUT_EDGE); |
| } |
| |
| ets_delay_us(1000); |
| // Clock polarity |
| if (dataMode & 0x02) { |
| HSPI_POL |= SPI_CPOL; |
| } else { |
| HSPI_POL &= ~SPI_CPOL; |
| } |
| |
| pin_function_select(HSPI_SCK_PIN, HSPI_FUNCTION); |
| pin_function_select(HSPI_MISO_PIN, HSPI_FUNCTION); |
| pin_function_select(HSPI_MOSI_PIN, HSPI_FUNCTION); |
| |
| if(spi_csGPIO == -1) { |
| pin_function_select(HSPI_HW_CS_PIN, HSPI_FUNCTION); |
| HSPI_USR |= (SPI_CS_SETUP | SPI_CS_HOLD); |
| } else { |
| if (spi_csGPIO == HSPI_HW_CS_PIN) { |
| pin_function_select(HSPI_HW_CS_PIN, GPIO_FUNCTION); |
| } |
| HSPI_USR &= ~(SPI_CS_SETUP | SPI_CS_HOLD); |
| platform_gpio_mode(spi_csGPIO, GPIO_OUTPUT, NOPULL); |
| platform_gpio_write(spi_csGPIO, 1); |
| } |
| } |
| |
| void spi_close() |
| { |
| pin_function_select(HSPI_SCK_PIN, GPIO_FUNCTION); |
| pin_function_select(HSPI_MISO_PIN, GPIO_FUNCTION); |
| pin_function_select(HSPI_MOSI_PIN, GPIO_INPUT); |
| if(spi_csGPIO == -1) { |
| pin_function_select(HSPI_HW_CS_PIN, GPIO_FUNCTION); |
| } else { |
| platform_gpio_mode(spi_csGPIO, GPIO_INPUT, NOPULL); |
| } |
| } |
| |
| static inline |
| void spi_wait_while_busy() |
| { |
| while(HSPI_CMD & SPI_BUSY) {} |
| } |
| |
| void spi_begin() |
| { |
| if (spi_csGPIO != -1) { |
| platform_gpio_write(spi_csGPIO, 0); |
| } |
| } |
| |
| void spi_end() |
| { |
| if (spi_csGPIO != -1) { |
| platform_gpio_write(spi_csGPIO, 1); |
| } |
| } |
| |
| static inline void setDataBits(uint16_t bits) |
| { |
| const uint32_t mask = ~((SPI_MASK_MOSI << SPI_MOSI_SHIFT) | (SPI_MASK_MISO << SPI_MISO_SHIFT)); |
| HSPI_USR1 = ((HSPI_USR1 & mask) | (((bits-1) << SPI_MOSI_SHIFT) | ((bits-1) << SPI_MISO_SHIFT))); |
| } |
| |
| int spi_bits_in(int num) |
| { |
| spi_wait_while_busy(); |
| // Set in/out Bits to transfer |
| |
| setDataBits(num); |
| HSPI_DATA = 0xffffffff; |
| |
| HSPI_CMD |= SPI_BUSY; |
| spi_wait_while_busy(); |
| |
| // XXX this might only work for MSB first |
| if (num == 32) { |
| return HSPI_DATA; |
| } else { |
| return (HSPI_DATA >> (8-num)) & ((1<<num)-1); |
| } |
| } |
| |
| // in and out must to be 32-bit aligned |
| void spi_transfer(uint32_t remaining, uint8_t * in, uint32_t * out) |
| { |
| while(remaining) { |
| int this_size = remaining > 64 ? 64 : remaining; |
| remaining -= this_size; |
| |
| spi_wait_while_busy(); |
| // Set in/out Bits to transfer |
| |
| setDataBits(this_size * 8); |
| |
| volatile uint32_t * fifoPtr = &HSPI_DATA; |
| uint8_t dataSize = ((this_size + 3) / 4); |
| |
| if(out) { |
| while(dataSize--) { |
| *fifoPtr++ = *out++; |
| } |
| } else { |
| // Send dummy data if no real data to send |
| while(dataSize--) { |
| *fifoPtr++ = 0xffffffff; |
| } |
| } |
| |
| HSPI_CMD |= SPI_BUSY; |
| spi_wait_while_busy(); |
| |
| if(in) { |
| volatile uint8_t * fifoPtr8 = (volatile uint8_t *) &HSPI_DATA; |
| dataSize = this_size; |
| while(dataSize--) { |
| *in++ = *fifoPtr8++; |
| } |
| } |
| } |
| } |