diff --git a/04-adc/README.md b/04-adc/README.md index d30e078..f1c1e68 100644 --- a/04-adc/README.md +++ b/04-adc/README.md @@ -54,11 +54,44 @@ The ESP32-S3 has two ADCs on the package that both perform in the same basic fas ![ESP32-S3 SAR ADC diagram](esp32sdcadc.png) -There are two ADCs on the ESP32-S3, being ADC1 and ADC2. They can both be operated in a low power mode (by driving them with the real-time clock controller) or in a high power mode (using the digital controller). ADC2 is shared among many peripherals such as WiFi, and so it has an "arbiter" hardware component that controls who gets to use it at which times and which controller drives it. ADC1 appears to be dedicated to userland programs running on the CPU, so it does not have a separate hardware arbiter, it has a much simpler mux device that selects between the RTC and Digital controller based on a hardware register. +There are two ADCs on the ESP32-S3, being ADC1 and ADC2. These are two distinct hardware units - they are not part of the CPU or the ULP, they are distinct peripherals. They can both be operated in a low power mode (by driving them with the real-time clock controller) or in a high power mode (using the digital controller). ADC2 is shared among many peripherals such as WiFi, and so it has an "arbiter" hardware component that controls who gets to use it at which times and which controller drives it. ADC1 appears to be dedicated to userland programs running on the CPU, so it does not have a separate hardware arbiter, it has a much simpler mux device that selects between the RTC and Digital controller based on a hardware register. + +### Arduino HAL be Dummy Thicc + +As previously mentioned, the HAL is quite thick here. The HAL hides the grand majority of the complexity I'm about to talk about, underneath the simple concepts of `oneshot` and `continuous` sampling modes. The source in this project uses the `oneshot` sampling mechanism to pull ADC values from pin 1 on request: + +```arduino +int adcvalue = analogRead(PIN_ANALOG_IN); +``` + +... This engages the RTC controller on ADC1 to pull a single value and return it immediately. There is a second mode of operation, [analogContinuous](https://docs.espressif.com/projects/arduino-esp32/en/latest/api/adc.html#adc-continuous-mode), wherein the ADC is configured to continually sample values, store the results, and call a user callback function when data is ready. + +```arduino + // Optional for ESP32: Set the resolution to 9-12 bits (default is 12 bits) + analogContinuousSetWidth(12); + + // Optional: Set different attenaution (default is ADC_11db) + analogContinuousSetAtten(ADC_11db); + + // Setup ADC Continuous with following input: + // array of pins, count of the pins, how many conversions per pin in one cycle will happen, sampling frequency, callback function + analogContinuous(adc_pins, adc_pins_count, CONVERSIONS_PER_PIN, 20000, &adcComplete); + + // Start ADC Continuous conversions + analogContinuousStart(); +``` + +The demo code I worked with didn't use the continuous mode, but I can definitely see utility for it. + +As usual the Arduino HAL makes this all really easy and hides a bunch of complexity from us. Part of the whole reason I'm doing these projects is as a rejection of abstraction layers between me and the computer, so, I'm not willing to stop there. Let's grab a shovel and dig into what's actually happening. ### ADC hardware registers -The ADC hardware registers are mapped into memory between `0x6000_8000` and `0x6000_8FFF`. These memory addresses are memory mapped in from the low power mode peripherals in the address controller. Basically, when something requests data from those memory regions, the memory address controller redirects the request out to the appropriate peripheral. The peripheral in question then manages the requested item, which is usually stored in a hardware register inside the peripheral. The ADC registers that are of most interest to this particular explanation are: +All of the ADC hardware is controlled by writing values to the ADC hardware registers. Once these are changed, the ADC hardware sees the change and starts acting on the request. + +The ADC hardware registers are mapped into memory between `0x6000_8000` and `0x6000_8FFF`. These memory addresses are memory mapped in from the low power mode peripherals in the address controller, and are part of the shared instruction/data bus. Basically, when something requests data from those memory regions, the memory address controller redirects the request out to the appropriate peripheral on behalf of whoever is requesting that memory. The peripheral in question then manages the requested item, which is usually stored in a hardware register inside the peripheral. Memory mapped registers are a really common way of controlling peripherals and reduces the complexity of connections between components in the package. + +The ADC registers that are of most interest to this particular explanation are: | ADC | Register | Address | Purpose | |-----|----------|---------|---------|