ULP ADC Sampling
Posted: Sun Jan 31, 2021 5:08 pm
Hey folks! I'm pretty new to micropython, but I have been loving it so far. However, I have been programming the ESP32 through the ESP IDF for a little bit longer, so I'm a little more familiar with interacting with it through C. I've been messing with the ULP coprocessor and found Thomas Waldmann's awesome library for assembling instructions in python! This has worked great so far and I wrote a short set of instructions for sampling the ADC and saving the measurement to memory. I wrote a class to abstract this and now have a way faster method of sampling one ADC pin that runs in parallel.
I am running into trouble with configuring the adc though. All the samples I get from this are 9 bits or less. Strangely configuring width through the micropython adc interface does actually change the width but they are 9,8,7, and 6 bit values instead of the configured 12,11,10 or 9. Reading ESP documentation, I wonder if this is because ad1_ulp_enable() is not being called.
I know a small amount about the ESP32 low level operations, but I'm starting to get lost trying to solve this problem. I would appreciate a second set of eyes to point out obvious mistake, or someone who know the granularity of working with the ESP32. I'm also curious if there is a way to call the ad1_ulp_enable() function.
Code: Select all
"""This module creates an abstraction for an ADC sampling program
running concurrently on the ESP32 ULP coprocessor"""
from esp32 import ULP
from machine import mem32, ADC, Pin
from esp32_ulp.__main__ import src_to_binary
class ULP_ADC:
SOURCE = """\
data: .long 0
entry: move r3, data # load data address into r3
adc r2, 0, 4 # measure voltage with ADC1 pad 3 and store result in r2
st r2, r3, 0 # store r2 in data [r3 + 0]
halt # halt ULP co-processor until woken up again
"""
ULP_MEM_BASE = 0x50000000
ULP_DATA_MASK = 0xffff # ULP data is only in lower 16 bits
LOAD_ADDR = 0
ENTRY_ADDR = 4
def __init__(self, gpio):
self.binary = src_to_binary(ULP_ADC.SOURCE)
# we only use this object to configure the ADC.
# we don't actually need it for sampling
self.ulp = ULP()
self.gpio = gpio
def start(self, wakeup_period_cyc, atten, width):
self.ulp.set_wakeup_period(0, wakeup_period_cyc) # use timer 0
self.ulp.load_binary(ULP_ADC.LOAD_ADDR, self.binary)
mem32[ULP_ADC.ULP_MEM_BASE + ULP_ADC.LOAD_ADDR] = 0x1000
self.ulp.run(ULP_ADC.ENTRY_ADDR)
self.adc = ADC(Pin(self.gpio))
self.adc.atten(atten)
self.adc.width(width)
def get_reading(self):
return mem32[ULP_ADC.ULP_MEM_BASE + ULP_ADC.LOAD_ADDR] & ULP_ADC.ULP_DATA_MASK
I know a small amount about the ESP32 low level operations, but I'm starting to get lost trying to solve this problem. I would appreciate a second set of eyes to point out obvious mistake, or someone who know the granularity of working with the ESP32. I'm also curious if there is a way to call the ad1_ulp_enable() function.