From a96aaaaa6b64502dcc015878446d8f97dab21a02 Mon Sep 17 00:00:00 2001 From: Laurence Bank Date: Mon, 23 Sep 2024 08:13:56 +0100 Subject: [PATCH] Added initial ESP-IDF main and ULP I/O wrappers --- esp_idf/c6_ulp_io.inl | 358 ++++++++++++++++++++ esp_idf/{esp_idf_io.inl => esp_main_io.inl} | 4 + esp_idf/s3_ulp_io.inl | 358 ++++++++++++++++++++ 3 files changed, 720 insertions(+) create mode 100644 esp_idf/c6_ulp_io.inl rename esp_idf/{esp_idf_io.inl => esp_main_io.inl} (98%) create mode 100644 esp_idf/s3_ulp_io.inl diff --git a/esp_idf/c6_ulp_io.inl b/esp_idf/c6_ulp_io.inl new file mode 100644 index 0000000..b5ca6df --- /dev/null +++ b/esp_idf/c6_ulp_io.inl @@ -0,0 +1,358 @@ +// +// I/O wrapper functions for the C6/P4 RISC-V ULP +// +#ifndef __ULP_IO__ +#define __ULP_IO__ + +#define INPUT 0 +#define INPUT_PULLUP 1 +#define OUTPUT 2 +#define DISABLED 3 + +#define PROGMEM +#define HIGH 1 +#define LOW 0 +#define PIN_SDA 6 +#define PIN_SCL 7 +#define PIN_SCK 4 +#define PIN_MOSI 6 + +// CPU cycles per bit +#define I2C_DELAY 40 + +void digitalWrite(int iPin, int iState) { + ulp_lp_core_gpio_set_level(iPin, iState); +} +void pinMode(int iPin, int iMode) +{ + ulp_lp_core_gpio_init(iPin); + if (iMode == INPUT || iMode == INPUT_PULLUP) { + ulp_lp_core_gpio_output_disable(iPin); + ulp_lp_core_gpio_input_enable(iPin); + if (iMode == INPUT_PULLUP) { + ulp_lp_core_gpio_pullup_enable(iPin); + } + } else if (iMode == DISABLED) { + ulp_lp_core_gpio_input_disable(iPin); + ulp_lp_core_gpio_output_disable(iPin); + } else { // OUTPUT + ulp_lp_core_gpio_input_disable(iPin); + ulp_lp_core_gpio_output_enable(iPin); + } +} /* pinMode() */ + +int digitalRead(int iPin) +{ + return (int)ulp_lp_core_gpio_get_level(iPin); +} /* digitalRead() */ +void delayMicroseconds(long l) +{ + l *= 40; + while (l) { + __asm__ __volatile__ ("nop"); + l--; + } +} +void _delay(long l) +{ + delayMicroseconds(l*1000); +} +void mymemcpy(void *d, void *s, size_t iLen) +{ + uint8_t *d8 = (uint8_t *)d; + uint8_t *s8 = (uint8_t *)s; + while (iLen) { + *d8++ = *s8++; + iLen--; + } +} /* mymemcpy() */ + +void mymemset(void *d, uint8_t u8, int len) +{ + uint8_t *d8; + if (((intptr_t)d & 3) == 0) { // dword aligned + uint32_t u32, *d32 = (uint32_t *)d; + u32 = u8 | (u8 << 8); + u32 |= (u32 << 16); + while (len >= 4) { + *d32++ = u32; + len -= 4; + } + d = d32; + } + d8 = (uint8_t *)d; + while (len) { + *d8++ = u8; + len--; + } +} /* mymemset() */ +// +// Convert a number into a fixed length, zero-terminated string +// +void i2strf(char *pDest, int iVal, int iDigits) +{ + char *d = pDest; + int i; + pDest[iDigits] = 0; + while (iDigits) { + iDigits--; + i = iVal % 10; + d[iDigits] = '0' + (char)i; + iVal /= 10; + } +} /* i2strf() */ +// +// Convert a number into a zero-terminated string +// +int i2str(char *pDest, int iVal) +{ + char *d = pDest; + int i, iPlaceVal = 10000; + int iDigits = 0; + + if (iVal < 0) { + iDigits++; + *d++ = '-'; + iVal = -iVal; + } + while (iPlaceVal) { + if (iVal >= iPlaceVal) { + i = iVal / iPlaceVal; + *d++ = '0' + (char)i; + iVal -= (i*iPlaceVal); + iDigits++; + } else if (iDigits != 0) { + *d++ = '0'; // non-zeros were already displayed + } + iPlaceVal /= 10; + } + if (d == pDest) // must be zero + *d++ = '0'; + *d++ = 0; // terminator + return (int)(d - pDest - 1); // string length +} /* i2str() */ +// +// Bit Bang SPI +// +void SPIWriteByte(uint8_t uc) +{ + if (uc == 0 || uc == 0xff) { // special case + ulp_lp_core_gpio_set_level(PIN_MOSI, (uc & 1)); + for (int i=0; i<8; i++) { + ulp_lp_core_gpio_set_level(PIN_SCK, 1); // no delays needed since it runs at a slow clock + ulp_lp_core_gpio_set_level(PIN_SCK, 0); + } + } else { + for (int i=0; i<8; i++) { + ulp_lp_core_gpio_set_level(PIN_MOSI, (uc & 0x80)); // msb first + ulp_lp_core_gpio_set_level(PIN_SCK, 1); // no delays needed since it runs at a slow clock + uc <<= 1; + ulp_lp_core_gpio_set_level(PIN_SCK, 0); + } + } +} /* SPIWriteByte() */ +// +// Need to bit-bang I2C +// +uint8_t SDA_READ(void) +{ + return ulp_lp_core_gpio_get_level(PIN_SDA); +} +void SDA_HIGH(void) +{ + ulp_lp_core_gpio_output_disable(PIN_SDA); + ulp_lp_core_gpio_input_enable(PIN_SDA); +} +void SDA_LOW(void) +{ + ulp_lp_core_gpio_input_disable(PIN_SDA); + ulp_lp_core_gpio_output_enable(PIN_SDA); + ulp_lp_core_gpio_set_level(PIN_SDA, 0); +} +void SCL_HIGH(void) +{ + ulp_lp_core_gpio_output_disable(PIN_SCL); + ulp_lp_core_gpio_input_enable(PIN_SCL); +} +void SCL_LOW(void) +{ + ulp_lp_core_gpio_input_disable(PIN_SCL); + ulp_lp_core_gpio_output_enable(PIN_SCL); + ulp_lp_core_gpio_set_level(PIN_SCL, 0); +} +// Transmit a byte and read the ack bit +// if we get a NACK (negative acknowledge) return 0 +// otherwise return 1 for success +// + +int i2cByteOut(uint8_t b) +{ +uint8_t i, ack; + +for (i=0; i<8; i++) { +// my_sleep_us(iDelay); + if (b & 0x80) + SDA_HIGH(); // set data line to 1 + else + SDA_LOW(); // set data line to 0 + b <<= 1; +// my_sleep_us(iDelay); + SCL_HIGH(); // clock high (slave latches data) + ulp_lp_core_delay_cycles(I2C_DELAY); + SCL_LOW(); // clock low + ulp_lp_core_delay_cycles(I2C_DELAY); +} // for i +//my_sleep_us(iDelay); +// read ack bit +SDA_HIGH(); // set data line for reading +//my_sleep_us(iDelay); +SCL_HIGH(); // clock line high +ulp_lp_core_delay_cycles(I2C_DELAY); // DEBUG - delay/2 +ack = SDA_READ(); +//my_sleep_us(iDelay); +SCL_LOW(); // clock low +ulp_lp_core_delay_cycles(I2C_DELAY); // DEBUG - delay/2 +SDA_LOW(); // data low +return (ack == 0); // a low ACK bit means success +} /* i2cByteOut() */ +// +// Receive a byte and read the ack bit +// if we get a NACK (negative acknowledge) return 0 +// otherwise return 1 for success +// +uint8_t i2cByteIn(uint8_t bLast) +{ +uint8_t i; +uint8_t b = 0; + + SDA_HIGH(); // set data line as input + for (i=0; i<8; i++) + { + ulp_lp_core_delay_cycles(I2C_DELAY); // wait for data to settle + SCL_HIGH(); // clock high (slave latches data) + ulp_lp_core_delay_cycles(I2C_DELAY); + b <<= 1; + if (SDA_READ() != 0) // read the data bit + b |= 1; // set data bit + SCL_LOW(); // clock low + } // for i + if (bLast) + SDA_HIGH(); // last byte sends a NACK + else + SDA_LOW(); +// my_sleep_us(iDelay); + SCL_HIGH(); // clock high + ulp_lp_core_delay_cycles(I2C_DELAY); + SCL_LOW(); // clock low to send ack + ulp_lp_core_delay_cycles(I2C_DELAY); +// SDA_HIGH(); + SDA_LOW(); // data low + return b; +} /* i2cByteIn() */ +// +// Send I2C STOP condition +// +void i2cEnd(void) +{ + SDA_LOW(); // data line low + ulp_lp_core_delay_cycles(I2C_DELAY); + SCL_HIGH(); // clock high + ulp_lp_core_delay_cycles(I2C_DELAY); + SDA_HIGH(); // data high + ulp_lp_core_delay_cycles(I2C_DELAY); +} /* i2cEnd() */ + +int i2cBegin(uint8_t addr, uint8_t bRead) +{ + int rc; +// SCL_HIGH(); +// my_sleep_us(iDelay); + SDA_LOW(); // data line low first + ulp_lp_core_delay_cycles(I2C_DELAY); + SCL_LOW(); // then clock line low is a START signal + addr <<= 1; + if (bRead) + addr++; // set read bit + rc = i2cByteOut(addr); // send the slave address and R/W bit + return rc; +} /* i2cBegin() */ +void I2CWrite(uint8_t addr, uint8_t *pData, int iLen) +{ +uint8_t b; +int rc; + + i2cBegin(addr, 0); + rc = 1; + while (iLen && rc == 1) + { + b = *pData++; + rc = i2cByteOut(b); + if (rc == 1) // success + { + iLen--; + } + } // for each byte + i2cEnd(); +//return (rc == 1) ? (iOldLen - iLen) : 0; // 0 indicates bad ack from sending a byte +} /* I2CWrite() */ +int I2CRead(uint8_t addr, uint8_t *pData, int iLen) +{ + i2cBegin(addr, 1); + while (iLen--) + { + *pData++ = i2cByteIn(iLen == 0); + } // for each byte + i2cEnd(); + return 1; +} /* I2CRead() */ + +int I2CTest(uint8_t addr) +{ +int response = 0; + + if (i2cBegin(addr, 0)) // try to write to the given address + { + response = 1; + } + i2cEnd(); +return response; +} /* I2CTest() */ + +void I2CInit(void) +{ + ulp_lp_core_gpio_init(PIN_SDA); + ulp_lp_core_gpio_output_disable(PIN_SDA); + ulp_lp_core_gpio_input_enable(PIN_SDA); + ulp_lp_core_gpio_pullup_enable(PIN_SDA); + ulp_lp_core_gpio_init(PIN_SCL); + ulp_lp_core_gpio_output_disable(PIN_SCL); + ulp_lp_core_gpio_input_enable(PIN_SCL); + ulp_lp_core_gpio_pullup_enable(PIN_SCL); +} +void I2CDeInit(void) +{ + ulp_lp_core_gpio_output_disable(PIN_SDA); + ulp_lp_core_gpio_input_disable(PIN_SDA); + ulp_lp_core_gpio_output_disable(PIN_SCL); + ulp_lp_core_gpio_input_disable(PIN_SCL); +} +// +// Read N bytes starting at a specific I2C internal register +// returns 1 for success, 0 for error +// +void I2CReadRegister(uint8_t iAddr, uint8_t u8Register, uint8_t *pData, int iLen) +{ + I2CWrite(iAddr, &u8Register, 1); + I2CRead(iAddr, pData, iLen); +} /* I2CReadRegister() */ + + +// Init the GPIO pin for output mode +void init_gpio(uint8_t pin) +{ + ulp_lp_core_gpio_init(pin); + ulp_lp_core_gpio_set_output_mode(pin, GPIO_MODE_OUTPUT); + ulp_lp_core_gpio_output_enable(pin); +} /* init_gpio() */ + +#endif // __ULP_IO__ \ No newline at end of file diff --git a/esp_idf/esp_idf_io.inl b/esp_idf/esp_main_io.inl similarity index 98% rename from esp_idf/esp_idf_io.inl rename to esp_idf/esp_main_io.inl index cce1459..bf8d51b 100644 --- a/esp_idf/esp_idf_io.inl +++ b/esp_idf/esp_main_io.inl @@ -1,5 +1,9 @@ // // I/O wrapper functions for the ESP-IDF +// These functions are for the main CPU +// of the ESP32 line of MCUs. Other wrapper +// functions target the 2 different ULP +// CPUs (S2/S3 and C6) // #ifndef __ESP_IDF_IO__ #define __ESP_IDF_IO__ diff --git a/esp_idf/s3_ulp_io.inl b/esp_idf/s3_ulp_io.inl new file mode 100644 index 0000000..a8df797 --- /dev/null +++ b/esp_idf/s3_ulp_io.inl @@ -0,0 +1,358 @@ +// +// I/O wrapper functions for the S2/S3 RISC-V ULP +// +#ifndef __ULP_IO__ +#define __ULP_IO__ + +#define INPUT 0 +#define INPUT_PULLUP 1 +#define OUTPUT 2 +#define DISABLED 3 + +#define PROGMEM +#define HIGH 1 +#define LOW 0 +#define PIN_SDA 6 +#define PIN_SCL 7 + +// Pinout for the LilyGo mini-epaper S3 +#define PIN_BUSY 10 +#define PIN_RST 11 +#define PIN_DC 12 +#define PIN_CS 13 +#define PIN_SCK 14 +#define PIN_MOSI 15 + +// Pinout for my custom ESP32-S3 circuit +//#define PIN_BUSY 12 +//#define PIN_RST 7 +//#define PIN_DC 10 +//#define PIN_CS 11 +//#define PIN_SCK 1 +//#define PIN_MOSI 3 + +// CPU cycles per bit +#define I2C_DELAY 40 + +void digitalWrite(int iPin, int iState) { + ulp_riscv_gpio_output_level(iPin, iState); +} +void pinMode(int iPin, int iMode) +{ + ulp_riscv_gpio_init(iPin); + if (iMode == INPUT || iMode == INPUT_PULLUP) { + ulp_riscv_gpio_output_disable(iPin); + ulp_riscv_gpio_input_enable(iPin); + if (iMode == INPUT_PULLUP) { + ulp_riscv_gpio_pullup(iPin); + } else { + ulp_riscv_gpio_pullup_disable(iPin); + } + } else if (iMode == DISABLED) { + ulp_riscv_gpio_deinit(iPin); + } else { // OUTPUT + ulp_riscv_gpio_input_disable(iPin); + ulp_riscv_gpio_set_output_mode(iPin, RTCIO_MODE_OUTPUT); + ulp_riscv_gpio_output_enable(iPin); + } +} /* pinMode() */ + +int digitalRead(int iPin) +{ + return (int)ulp_riscv_gpio_get_level(iPin); +} /* digitalRead() */ +// +// Bit Bang SPI +// +void SPIWriteByte(uint8_t uc) +{ + if (uc == 0 || uc == 0xff) { // special case + ulp_riscv_gpio_output_level(PIN_MOSI, (uc & 1)); + for (int i=0; i<8; i++) { + ulp_riscv_gpio_output_level(PIN_SCK, 1); // no delays needed since it runs at a slow clock + ulp_riscv_gpio_output_level(PIN_SCK, 0); + } + } else { + for (int i=0; i<8; i++) { + ulp_riscv_gpio_output_level(PIN_MOSI, (uc & 0x80)); // msb first + ulp_riscv_gpio_output_level(PIN_SCK, 1); // no delays needed since it runs at a slow clock + uc <<= 1; + ulp_riscv_gpio_output_level(PIN_SCK, 0); + } + } +} /* SPIWriteByte() */ + +void delayMicroseconds(long l) +{ + l *= 40; + while (l) { + __asm__ __volatile__ ("nop"); + l--; + } +} +void _delay(long l) +{ + delayMicroseconds(l*1000); +} +void mymemcpy(void *d, void *s, size_t iLen) +{ + uint8_t *d8 = (uint8_t *)d; + uint8_t *s8 = (uint8_t *)s; + while (iLen) { + *d8++ = *s8++; + iLen--; + } +} /* mymemcpy() */ + +void mymemset(void *d, uint8_t u8, int len) +{ + uint8_t *d8; + if (((intptr_t)d & 3) == 0) { // dword aligned + uint32_t u32, *d32 = (uint32_t *)d; + u32 = u8 | (u8 << 8); + u32 |= (u32 << 16); + while (len >= 4) { + *d32++ = u32; + len -= 4; + } + d = d32; + } + d8 = (uint8_t *)d; + while (len) { + *d8++ = u8; + len--; + } +} /* mymemset() */ +// +// Convert a number into a fixed length, zero-terminated string +// +void i2strf(char *pDest, int iVal, int iDigits) +{ + char *d = pDest; + int i; + pDest[iDigits] = 0; + while (iDigits) { + iDigits--; + i = iVal % 10; + d[iDigits] = '0' + (char)i; + iVal /= 10; + } +} /* i2strf() */ +// +// Convert a number into a zero-terminated string +// +int i2str(char *pDest, int iVal) +{ + char *d = pDest; + int i, iPlaceVal = 10000; + int iDigits = 0; + + if (iVal < 0) { + iDigits++; + *d++ = '-'; + iVal = -iVal; + } + while (iPlaceVal) { + if (iVal >= iPlaceVal) { + i = iVal / iPlaceVal; + *d++ = '0' + (char)i; + iVal -= (i*iPlaceVal); + iDigits++; + } else if (iDigits != 0) { + *d++ = '0'; // non-zeros were already displayed + } + iPlaceVal /= 10; + } + if (d == pDest) // must be zero + *d++ = '0'; + *d++ = 0; // terminator + return (int)(d - pDest - 1); // string length +} /* i2str() */ +// +// Need to bit-bang I2C +// +uint8_t SDA_READ(void) +{ + return ulp_riscv_gpio_get_level(PIN_SDA); +} +void SDA_HIGH(void) +{ + ulp_riscv_gpio_output_disable(PIN_SDA); + ulp_riscv_gpio_input_enable(PIN_SDA); +} +void SDA_LOW(void) +{ + ulp_riscv_gpio_input_disable(PIN_SDA); + ulp_riscv_gpio_output_enable(PIN_SDA); + ulp_riscv_gpio_output_level(PIN_SDA, 0); +} +void SCL_HIGH(void) +{ + ulp_riscv_gpio_output_disable(PIN_SCL); + ulp_riscv_gpio_input_enable(PIN_SCL); +} +void SCL_LOW(void) +{ + ulp_riscv_gpio_input_disable(PIN_SCL); + ulp_riscv_gpio_output_enable(PIN_SCL); + ulp_riscv_gpio_output_level(PIN_SDA, 0); +} +// Transmit a byte and read the ack bit +// if we get a NACK (negative acknowledge) return 0 +// otherwise return 1 for success +// + +int i2cByteOut(uint8_t b) +{ +uint8_t i, ack; + +for (i=0; i<8; i++) { +// my_sleep_us(iDelay); + if (b & 0x80) + SDA_HIGH(); // set data line to 1 + else + SDA_LOW(); // set data line to 0 + b <<= 1; +// my_sleep_us(iDelay); + SCL_HIGH(); // clock high (slave latches data) + ulp_riscv_delay_cycles(I2C_DELAY); + SCL_LOW(); // clock low + ulp_riscv_delay_cycles(I2C_DELAY); +} // for i +//my_sleep_us(iDelay); +// read ack bit +SDA_HIGH(); // set data line for reading +//my_sleep_us(iDelay); +SCL_HIGH(); // clock line high +ulp_riscv_delay_cycles(I2C_DELAY); // DEBUG - delay/2 +ack = SDA_READ(); +//my_sleep_us(iDelay); +SCL_LOW(); // clock low +ulp_riscv_delay_cycles(I2C_DELAY); // DEBUG - delay/2 +SDA_LOW(); // data low +return (ack == 0); // a low ACK bit means success +} /* i2cByteOut() */ +// +// Receive a byte and read the ack bit +// if we get a NACK (negative acknowledge) return 0 +// otherwise return 1 for success +// +uint8_t i2cByteIn(uint8_t bLast) +{ +uint8_t i; +uint8_t b = 0; + + SDA_HIGH(); // set data line as input + for (i=0; i<8; i++) + { + ulp_riscv_delay_cycles(I2C_DELAY); // wait for data to settle + SCL_HIGH(); // clock high (slave latches data) + ulp_riscv_delay_cycles(I2C_DELAY); + b <<= 1; + if (SDA_READ() != 0) // read the data bit + b |= 1; // set data bit + SCL_LOW(); // clock low + } // for i + if (bLast) + SDA_HIGH(); // last byte sends a NACK + else + SDA_LOW(); +// my_sleep_us(iDelay); + SCL_HIGH(); // clock high + ulp_riscv_delay_cycles(I2C_DELAY); + SCL_LOW(); // clock low to send ack + ulp_riscv_delay_cycles(I2C_DELAY); +// SDA_HIGH(); + SDA_LOW(); // data low + return b; +} /* i2cByteIn() */ +// +// Send I2C STOP condition +// +void i2cEnd(void) +{ + SDA_LOW(); // data line low + ulp_riscv_delay_cycles(I2C_DELAY); + SCL_HIGH(); // clock high + ulp_riscv_delay_cycles(I2C_DELAY); + SDA_HIGH(); // data high + ulp_riscv_delay_cycles(I2C_DELAY); +} /* i2cEnd() */ + +int i2cBegin(uint8_t addr, uint8_t bRead) +{ + int rc; +// SCL_HIGH(); +// my_sleep_us(iDelay); + SDA_LOW(); // data line low first + ulp_riscv_delay_cycles(I2C_DELAY); + SCL_LOW(); // then clock line low is a START signal + addr <<= 1; + if (bRead) + addr++; // set read bit + rc = i2cByteOut(addr); // send the slave address and R/W bit + return rc; +} /* i2cBegin() */ +void I2CWrite(uint8_t addr, uint8_t *pData, int iLen) +{ +uint8_t b; +int rc; + + i2cBegin(addr, 0); + rc = 1; + while (iLen && rc == 1) + { + b = *pData++; + rc = i2cByteOut(b); + if (rc == 1) // success + { + iLen--; + } + } // for each byte + i2cEnd(); +//return (rc == 1) ? (iOldLen - iLen) : 0; // 0 indicates bad ack from sending a byte +} /* I2CWrite() */ +int I2CRead(uint8_t addr, uint8_t *pData, int iLen) +{ + i2cBegin(addr, 1); + while (iLen--) + { + *pData++ = i2cByteIn(iLen == 0); + } // for each byte + i2cEnd(); + return 1; +} /* I2CRead() */ + +int I2CTest(uint8_t addr) +{ +int response = 0; + + if (i2cBegin(addr, 0)) // try to write to the given address + { + response = 1; + } + i2cEnd(); +return response; +} /* I2CTest() */ + +void I2CInit(void) +{ + pinMode(PIN_SDA, INPUT_PULLUP); + pinMode(PIN_SCL, INPUT_PULLUP); +} +void I2CDeInit(void) +{ + pinMode(PIN_SDA, DISABLED); + pinMode(PIN_SCL, DISABLED); +} +// +// Read N bytes starting at a specific I2C internal register +// returns 1 for success, 0 for error +// +void I2CReadRegister(uint8_t iAddr, uint8_t u8Register, uint8_t *pData, int iLen) +{ + I2CWrite(iAddr, &u8Register, 1); + I2CRead(iAddr, pData, iLen); +} /* I2CReadRegister() */ + +#endif // __ULP_IO__