diff --git a/CMake/Modules/FindSystem.Device.Adc.cmake b/CMake/Modules/FindSystem.Device.Adc.cmake index 9b756bd98b..5f8e53c7ee 100644 --- a/CMake/Modules/FindSystem.Device.Adc.cmake +++ b/CMake/Modules/FindSystem.Device.Adc.cmake @@ -17,7 +17,6 @@ list(APPEND System.Device.Adc_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/src/System.Device # source files set(System.Device.Adc_SRCS - sys_dev_adc_native.cpp sys_dev_adc_native_System_Device_Adc_AdcChannel.cpp diff --git a/CMake/binutils.ESP32.cmake b/CMake/binutils.ESP32.cmake index f7303fdd5d..5cbf98a3e0 100644 --- a/CMake/binutils.ESP32.cmake +++ b/CMake/binutils.ESP32.cmake @@ -687,6 +687,7 @@ macro(nf_add_idf_as_library) esp_netif esp_eth esp_psram + esp_adc littlefs ) @@ -702,6 +703,7 @@ macro(nf_add_idf_as_library) idf::esp_netif idf::esp_eth idf::esp_psram + idf::esp_adc idf::littlefs ) diff --git a/CMake/riscv-esp32c6.json b/CMake/riscv-esp32c6.json index b6b7f7f3dc..ecfa772e9e 100644 --- a/CMake/riscv-esp32c6.json +++ b/CMake/riscv-esp32c6.json @@ -25,7 +25,7 @@ "SUPPORT_ANY_BASE_CONVERSION": "ON", "API_System.Net": "ON", "API_System.Math": "ON", - "API_System.Device.Adc": "OFF", + "API_System.Device.Adc": "ON", "API_System.Device.Gpio": "ON", "API_System.Device.I2c": "ON", "API_System.Device.I2c.Slave": "ON", diff --git a/CMake/riscv-esp32h2.json b/CMake/riscv-esp32h2.json index b538afd256..1190c40783 100644 --- a/CMake/riscv-esp32h2.json +++ b/CMake/riscv-esp32h2.json @@ -25,7 +25,7 @@ "SUPPORT_ANY_BASE_CONVERSION": "ON", "API_System.Net": "ON", "API_System.Math": "ON", - "API_System.Device.Adc": "OFF", + "API_System.Device.Adc": "ON", "API_System.Device.Gpio": "ON", "API_System.Device.I2c": "ON", "API_System.Device.I2c.Slave": "ON", diff --git a/targets/ESP32/_common/ESP32_C6_DeviceMapping.cpp b/targets/ESP32/_common/ESP32_C6_DeviceMapping.cpp index a318d6eec6..cdf589018d 100644 --- a/targets/ESP32/_common/ESP32_C6_DeviceMapping.cpp +++ b/targets/ESP32/_common/ESP32_C6_DeviceMapping.cpp @@ -23,10 +23,7 @@ int8_t Esp32_SPI_DevicePinMap[MAX_SPI_DEVICES][Esp32SpiPin_Max] = { // others assign as NONE because the default pins can be shared with serial flash and PSRAM int8_t Esp32_SERIAL_DevicePinMap[UART_NUM_MAX][Esp32SerialPin_Max] = { // COM 1 - pins 21, 20 - {UART_NUM_0_TXD_DIRECT_GPIO_NUM, - UART_NUM_0_RXD_DIRECT_GPIO_NUM, - UART_PIN_NO_CHANGE, - UART_PIN_NO_CHANGE}, + {UART_NUM_0_TXD_DIRECT_GPIO_NUM, UART_NUM_0_RXD_DIRECT_GPIO_NUM, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE}, #if defined(UART_NUM_2) // COM 2 - all set to UART_PIN_NO_CHANGE @@ -56,15 +53,18 @@ int8_t Esp32_LED_DevicePinMap[6] = { -1, // 6 }; -// We use "ADC1" for 20 logical channels -// Mapped to ESP32 controllers +// We use "ADC1" for 7 logical channels +// Mapped to ESP32_C6 controllers // ESP32 ADC1 channels 0 - 6 -// " ADC1 channel 7 - Internal Temperature sensor ? -// " ADC1 channel 8 - vdd33 ? -// TODO review ADC channels for ESP32_C3 , Internal Temperature sensor (VP) & Vdd33 int8_t Esp32_ADC_DevicePinMap[7] = { - // 0 1 2 3 4 5 - 0, 1, 2, 3, 4, 5, 6}; + // 0 1 2 3 4 5 + 0, + 1, + 2, + 3, + 4, + 5, + 6}; // I2S // 1 device I2S1 diff --git a/targets/ESP32/_common/ESP32_H2_DeviceMapping.cpp b/targets/ESP32/_common/ESP32_H2_DeviceMapping.cpp index b1413c95e0..712d0da350 100644 --- a/targets/ESP32/_common/ESP32_H2_DeviceMapping.cpp +++ b/targets/ESP32/_common/ESP32_H2_DeviceMapping.cpp @@ -23,10 +23,7 @@ int8_t Esp32_SPI_DevicePinMap[MAX_SPI_DEVICES][Esp32SpiPin_Max] = { // others assign as NONE because the default pins can be shared with serial flash and PSRAM int8_t Esp32_SERIAL_DevicePinMap[UART_NUM_MAX][Esp32SerialPin_Max] = { // COM 1 - pins 21, 20 - {UART_NUM_0_TXD_DIRECT_GPIO_NUM, - UART_NUM_0_RXD_DIRECT_GPIO_NUM, - UART_PIN_NO_CHANGE, - UART_PIN_NO_CHANGE}, + {UART_NUM_0_TXD_DIRECT_GPIO_NUM, UART_NUM_0_RXD_DIRECT_GPIO_NUM, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE}, #if defined(UART_NUM_2) // COM 2 - all set to UART_PIN_NO_CHANGE @@ -56,18 +53,12 @@ int8_t Esp32_LED_DevicePinMap[TARGET_LED_NUM_PINS] = { -1, // 6 -1, // 7 -1, // 8 - }; +}; -// We use "ADC1" for 20 logical channels -// Mapped to ESP32 controllers -// ESP32 ADC1 channels 0 - 7 -// " ADC1 channel 8 - Internal Temperature sensor (VP) -// " ADC1 channel 9 - Internal Hall Sensor (VN) -// " ADC2 channels 10 - 19 -// TODO review ADC channels for ESP32_C3 -int8_t Esp32_ADC_DevicePinMap[TARGET_ADC_NUM_PINS] = { - // 0 1 2 3 4 5 - }; +// We use "ADC1" for 5 logical channels +// Mapped to ESP32_H2 controllers +// ESP32_H2 ADC1 channels 1 - 5 +int8_t Esp32_ADC_DevicePinMap[TARGET_ADC_NUM_PINS] = {1, 2, 3, 4, 5}; // I2S // 1 device I2S1 diff --git a/targets/ESP32/_common/ESP32_P4_DeviceMapping.cpp b/targets/ESP32/_common/ESP32_P4_DeviceMapping.cpp index d214a22663..d9055a04b9 100644 --- a/targets/ESP32/_common/ESP32_P4_DeviceMapping.cpp +++ b/targets/ESP32/_common/ESP32_P4_DeviceMapping.cpp @@ -24,10 +24,7 @@ int8_t Esp32_SPI_DevicePinMap[MAX_SPI_DEVICES][Esp32SpiPin_Max] = { // others assign as NONE because the default pins can be shared with serial flash and PSRAM int8_t Esp32_SERIAL_DevicePinMap[UART_NUM_MAX][Esp32SerialPin_Max] = { // COM 1 - pins 21, 20 - {UART_NUM_0_TXD_DIRECT_GPIO_NUM, - UART_NUM_0_RXD_DIRECT_GPIO_NUM, - UART_PIN_NO_CHANGE, - UART_PIN_NO_CHANGE}, + {UART_NUM_0_TXD_DIRECT_GPIO_NUM, UART_NUM_0_RXD_DIRECT_GPIO_NUM, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE}, #if defined(UART_NUM_2) // COM 2 - all set to UART_PIN_NO_CHANGE @@ -59,15 +56,28 @@ int8_t Esp32_LED_DevicePinMap[8] = { -1 // 8 }; +// ESP32_P4 ADC1 with 8 channels (GPIO 16,17,18,19,20,21,22,23) +// plus 6 channels (GPIO 49,50,51,52,53,54) + // ESP32P4 ADC1 channels 0 - 9 // ADC2 channels 10 - 19 int8_t Esp32_ADC_DevicePinMap[TARGET_ADC_NUM_PINS] = { // ADC1 - ADC1_CHANNEL_0_GPIO_NUM, ADC1_CHANNEL_1_GPIO_NUM, ADC1_CHANNEL_2_GPIO_NUM, ADC1_CHANNEL_3_GPIO_NUM, - ADC1_CHANNEL_4_GPIO_NUM, ADC1_CHANNEL_5_GPIO_NUM, ADC1_CHANNEL_6_GPIO_NUM, ADC1_CHANNEL_7_GPIO_NUM, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, // ADC2 - ADC2_CHANNEL_0_GPIO_NUM, ADC2_CHANNEL_1_GPIO_NUM, ADC2_CHANNEL_2_GPIO_NUM, ADC2_CHANNEL_3_GPIO_NUM, - ADC2_CHANNEL_4_GPIO_NUM, ADC2_CHANNEL_5_GPIO_NUM}; + 49, + 50, + 51, + 52, + 53, + 54}; // I2S // 1 device I2S1 diff --git a/targets/ESP32/_include/esp32_idf.h b/targets/ESP32/_include/esp32_idf.h index 0662e6ca2f..9f34fbb9e0 100644 --- a/targets/ESP32/_include/esp32_idf.h +++ b/targets/ESP32/_include/esp32_idf.h @@ -63,8 +63,13 @@ #include #include +#if defined(CONFIG_IDF_TARGET_ESP32) +// Use legacy ADC driver for ESP32 for now as the new one also requires the new I2S driver due to dependency because of +// internal DAC other ESP32 variants don't have DAC so use the new ADC driver #include -// #include +#else +#include +#endif #if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) #include diff --git a/targets/ESP32/_nanoCLR/System.Device.Adc/sys_dev_adc_native_System_Device_Adc_AdcChannel.cpp b/targets/ESP32/_nanoCLR/System.Device.Adc/sys_dev_adc_native_System_Device_Adc_AdcChannel.cpp index 8c0a7542e2..e9c39459e6 100644 --- a/targets/ESP32/_nanoCLR/System.Device.Adc/sys_dev_adc_native_System_Device_Adc_AdcChannel.cpp +++ b/targets/ESP32/_nanoCLR/System.Device.Adc/sys_dev_adc_native_System_Device_Adc_AdcChannel.cpp @@ -5,10 +5,12 @@ // #include +#include + +// The ESP32 still uses the legacy ADC driver for now as dependency between ADC and I2S due to internal DAC etc +#if defined(CONFIG_IDF_TARGET_ESP32) -#if defined(IDF_TARGET_ESP32) extern "C" uint8_t temprature_sens_read(); -#endif HRESULT Library_sys_dev_adc_native_System_Device_Adc_AdcChannel::NativeReadValue___I4(CLR_RT_StackFrame &stack) { @@ -33,13 +35,14 @@ HRESULT Library_sys_dev_adc_native_System_Device_Adc_AdcChannel::NativeReadValue switch (channelNumber) { -#if defined(IDF_TARGET_ESP32) +#if defined(CONFIG_IDF_TARGET_ESP32) case 8: - reading = temperature_sens_read(); + reading = temprature_sens_read(); break; case 9: - reading = hall_sensor_read(); + // Hall sensor no longer available in IDF 5.x + NANOCLR_SET_AND_LEAVE(CLR_E_INVALID_PARAMETER); break; #endif @@ -53,7 +56,8 @@ HRESULT Library_sys_dev_adc_native_System_Device_Adc_AdcChannel::NativeReadValue { // Adjust channel number for ADC2 channelNumber -= CONFIG_SOC_ADC_MAX_CHANNEL_NUM; - esp_err_t result = adc2_get_raw((adc2_channel_t)channelNumber, (adc_bits_width_t)SOC_ADC_RTC_MAX_BITWIDTH, &reading); + esp_err_t result = + adc2_get_raw((adc2_channel_t)channelNumber, (adc_bits_width_t)SOC_ADC_RTC_MAX_BITWIDTH, &reading); if (result != ESP_OK) { @@ -81,3 +85,79 @@ HRESULT Library_sys_dev_adc_native_System_Device_Adc_AdcChannel::NativeDisposeCh NANOCLR_NOCLEANUP_NOLABEL(); } + +#else // !defined(CONFIG_IDF_TARGET_ESP32) + +adc_oneshot_unit_handle_t GetAdcHandle(adc_unit_t unit); + +HRESULT Library_sys_dev_adc_native_System_Device_Adc_AdcChannel::NativeReadValue___I4(CLR_RT_StackFrame &stack) +{ + NANOCLR_HEADER(); + + int channelNumber; + adc_unit_t adcNumber; + int reading = 0; + + // get a pointer to the managed object instance and check that it's not NULL + CLR_RT_HeapBlock *pThis = stack.This(); + FAULT_ON_NULL(pThis); + + // Get channel from _channelNumber field + channelNumber = pThis[FIELD___channelNumber].NumericByRef().s4; + if (channelNumber < 0 || channelNumber > TARGET_ADC_NUM_PINS) + { + NANOCLR_SET_AND_LEAVE(CLR_E_INVALID_PARAMETER); + } + + // Calculate internal ADC number based on channel number, 0->(CONFIG_SOC_ADC_MAX_CHANNEL_NUM - 1) + adcNumber = channelNumber < CONFIG_SOC_ADC_MAX_CHANNEL_NUM ? ADC_UNIT_1 : ADC_UNIT_2; + +#if defined(CONFIG_IDF_TARGET_ESP32) + // Handle internal channels for ESP32 only + if (adcNumber == ADC_UNIT_1 && (channelNumber == 8 || channelNumber == 9)) + { + if (channelNumber == 8) + { + // reading = temperature_sens_read(); + reading = 0; + } + else + { + // Hall sensor no longer available + NANOCLR_SET_AND_LEAVE(CLR_E_INVALID_PARAMETER); + } + } + else +#endif + { + if (adcNumber == ADC_UNIT_2) + { + // Adjust channel number for ADC2 + channelNumber -= CONFIG_SOC_ADC_MAX_CHANNEL_NUM; + } + + // Read the value + if (adc_oneshot_read(GetAdcHandle(adcNumber), (adc_channel_t)channelNumber, &reading) != ESP_OK) + { + NANOCLR_SET_AND_LEAVE(CLR_E_PIN_UNAVAILABLE); + } + } + + // Return value to the managed application + stack.SetResult_I4(reading); + + NANOCLR_NOCLEANUP(); +} + +HRESULT Library_sys_dev_adc_native_System_Device_Adc_AdcChannel::NativeDisposeChannel___VOID(CLR_RT_StackFrame &stack) +{ + (void)stack; + + NANOCLR_HEADER(); + + // left empty on purpose, nothing to do here + + NANOCLR_NOCLEANUP_NOLABEL(); +} + +#endif // !defined(CONFIG_IDF_TARGET_ESP32) diff --git a/targets/ESP32/_nanoCLR/System.Device.Adc/sys_dev_adc_native_System_Device_Adc_AdcController.cpp b/targets/ESP32/_nanoCLR/System.Device.Adc/sys_dev_adc_native_System_Device_Adc_AdcController.cpp index 5b6c800b58..46c148da4c 100644 --- a/targets/ESP32/_nanoCLR/System.Device.Adc/sys_dev_adc_native_System_Device_Adc_AdcController.cpp +++ b/targets/ESP32/_nanoCLR/System.Device.Adc/sys_dev_adc_native_System_Device_Adc_AdcController.cpp @@ -24,13 +24,28 @@ // VN - GPIO39 ( channel 3 ) // From managed code we treat all ADC channels as 1 logical ADC unit (ADC1) -// Logical channels -// ADC1 / 0 - 7 - 8 channels - ( GPIO 32 - 39 ) 0=36, 1=37, 2=38, 3=39, 4=32, 5=33, 6=34, 7=35 -// 8 - Internal Temperture sensor -// 9 - Internal Hall Sensor -// ADC2 / 10 - 19 - 10 Channels -( GPIOs 0, 2, 4, 12 - 15 and 25 - 27. ) -// Note: ESP32-H2 and ESP32-C6 have only 1 ADC and limited number of channels +// ESP32 ADC1 with 8 channels (GPIO 36,37,38,39,32,33,34,35) +// plus 2 channels (Temp sensor, Hall sensor) ( no longer available with IDF 5.x) +// plus 10 channels (GPIO 4,0,2,12,13,14,15,27,25,26) +// ESP32_S2 ADC1 with 10 channels (GPIO 1,2,3,4,5,6,7,8,9,10) +// plus 10 channels (GPIO 11,12,13,14, XTAL_32K_P, XTAL_32K_N, DAC_1, DAC_2, 19, 20) +// ESP32_S3 ADC1 with 10 channels (GPIO 1,2,3,4,5,6,7,8,9,10) +// plus 10 channels (GPIO 11,12,13,14,15,16,17,18,19,20) +// ESP32_P4 ADC1 with 8 channels (GPIO 16,17,18,19,20,21,22,23) +// plus 6 channels (GPIO 49,50,51,52,53,54) +// ESP32_C3 ADC1 with 5 channels (GPIO 0,1,2,3,4) +// plus 1 channel (GPIO 5) +// ESP32_C6 ADC1 with 7 channels (GPIO X32K_P(0),X32K_N(1),2,3,4,5,6) +// ESP32_H2 ADC1 with 5 channels (GPIO 1,2,3,4,5) + +// Config parameters from sdkconfig +// CONFIG_SOC_ADC_SUPPORTED +// CONFIG_SOC_ADC_PERIPH_NUM +// CONFIG_SOC_ADC_MAX_CHANNEL_NUM + +// The ESP32 still uses the legacy ADC driver for now as dependency between ADC and I2S due to internal DAC etc +#if defined(CONFIG_IDF_TARGET_ESP32) HRESULT Library_sys_dev_adc_native_System_Device_Adc_AdcController::NativeOpenChannel___VOID__I4( CLR_RT_StackFrame &stack) @@ -162,3 +177,173 @@ HRESULT Library_sys_dev_adc_native_System_Device_Adc_AdcController::NativeInit__ NANOCLR_NOCLEANUP_NOLABEL(); } + +#else + +adc_oneshot_unit_handle_t adc_handles[SOC_ADC_PERIPH_NUM] = {NULL}; + +adc_oneshot_unit_handle_t GetAdcHandle(adc_unit_t unit) +{ + if (unit < 0 || unit >= SOC_ADC_PERIPH_NUM) + { + return NULL; + } + + return adc_handles[unit]; +} + +void AdcController_Initialize(adc_unit_t unit) +{ + if (adc_handles[unit] != NULL) + { + // already initialized + return; + } + + adc_oneshot_unit_init_cfg_t init_config1 = {}; + init_config1.unit_id = unit; + init_config1.ulp_mode = ADC_ULP_MODE_DISABLE; + + ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config1, &adc_handles[unit])); +} + +void AdcController_delete(adc_unit_t unit) +{ + if (adc_handles[unit] != NULL) + { + ESP_ERROR_CHECK(adc_oneshot_del_unit(adc_handles[unit])); + adc_handles[unit] = NULL; + } +} + +HRESULT Library_sys_dev_adc_native_System_Device_Adc_AdcController::NativeOpenChannel___VOID__I4( + CLR_RT_StackFrame &stack) +{ + NANOCLR_HEADER(); + + int channelNumber; + adc_unit_t adcUnit; + esp_err_t result; + + // Set up channel configuration + adc_oneshot_chan_cfg_t config = {}; + + config.bitwidth = ADC_BITWIDTH_12; + config.atten = ADC_ATTEN_DB_12; + + // get a pointer to the managed object instance and check that it's not NULL + CLR_RT_HeapBlock *pThis = stack.This(); + FAULT_ON_NULL(pThis); + + // Get channel from argument + channelNumber = stack.Arg1().NumericByRef().s4; + + if (channelNumber < ADC_CHANNEL_0 || channelNumber > TARGET_ADC_NUM_PINS) + { + NANOCLR_SET_AND_LEAVE(CLR_E_INVALID_PARAMETER); + } + + // Get ADC device number from channel + adcUnit = channelNumber < CONFIG_SOC_ADC_MAX_CHANNEL_NUM ? ADC_UNIT_1 : ADC_UNIT_2; + +#if (CONFIG_SOC_ADC_PERIPH_NUM == 1) + // Only ADC1 supported + if (adcUnit == ADC_UNIT_2) + { + NANOCLR_SET_AND_LEAVE(CLR_E_INVALID_PARAMETER); + } +#endif + + // Initialize ADC unit if required + AdcController_Initialize(adcUnit); + + if (adcUnit == ADC_UNIT_2) + { + // Adjust channel number for ADC2 + channelNumber -= CONFIG_SOC_ADC_MAX_CHANNEL_NUM; + } + +#if defined(CONFIG_IDF_TARGET_ESP32) + // Handle internal channels for ESP32 only, no longer used with IDF 5.x + if (adcUnit == ADC_UNIT_1 && (channelNumber == 8 || channelNumber == 9)) + { + NANOCLR_SET_AND_LEAVE(CLR_E_INVALID_PARAMETER); + } +#endif + + result = adc_oneshot_config_channel(GetAdcHandle(adcUnit), (adc_channel_t)channelNumber, &config); + if (result != ESP_OK) + { + NANOCLR_SET_AND_LEAVE(CLR_E_PIN_UNAVAILABLE); + } + + NANOCLR_NOCLEANUP(); +} + +HRESULT Library_sys_dev_adc_native_System_Device_Adc_AdcController::NativeGetChannelCount___I4(CLR_RT_StackFrame &stack) +{ + NANOCLR_HEADER(); + + int channelCount = CONFIG_SOC_ADC_MAX_CHANNEL_NUM * CONFIG_SOC_ADC_PERIPH_NUM; + + // Return value to the managed application + stack.SetResult_I4(channelCount); + + NANOCLR_NOCLEANUP_NOLABEL(); +} + +HRESULT Library_sys_dev_adc_native_System_Device_Adc_AdcController::NativeGetMaxValue___I4(CLR_RT_StackFrame &stack) +{ + NANOCLR_HEADER(); + + // Currently fixed 12 bit so return 4095 + stack.SetResult_I4(4095); + + NANOCLR_NOCLEANUP_NOLABEL(); +} + +HRESULT Library_sys_dev_adc_native_System_Device_Adc_AdcController::NativeGetMinValue___I4(CLR_RT_StackFrame &stack) +{ + NANOCLR_HEADER(); + + // Return 0 for now, is this signed ? + stack.SetResult_I4(0); + + NANOCLR_NOCLEANUP_NOLABEL(); +} + +HRESULT Library_sys_dev_adc_native_System_Device_Adc_AdcController::NativeIsChannelModeSupported___BOOLEAN__I4( + CLR_RT_StackFrame &stack) +{ + NANOCLR_HEADER(); + + int mode = stack.Arg1().NumericByRef().s4; + + // Only support Single ended mode for now + stack.SetResult_Boolean((mode == (int)AdcChannelMode_SingleEnded)); + + NANOCLR_NOCLEANUP_NOLABEL(); +} + +HRESULT Library_sys_dev_adc_native_System_Device_Adc_AdcController::NativeGetResolutionInBits___I4( + CLR_RT_StackFrame &stack) +{ + NANOCLR_HEADER(); + + // Fixed at 12 bit for now + stack.SetResult_I4(12); + + NANOCLR_NOCLEANUP_NOLABEL(); +} + +HRESULT Library_sys_dev_adc_native_System_Device_Adc_AdcController::NativeInit___VOID(CLR_RT_StackFrame &stack) +{ + (void)stack; + + NANOCLR_HEADER(); + + // all required initialization for ADC are already handled + + NANOCLR_NOCLEANUP_NOLABEL(); +} +#endif // #if defined(CONFIG_IDF_TARGET_ESP32) \ No newline at end of file