#!/usr/bin/env python3

from machine import ADC
from array import array
from time import ticks_us, ticks_diff
import micropython

class FastADC:
    # _ADC_BASE = const(0x4004c000)   # rp2040
    _ADC_BASE = const(0x400a0000)  # rp2350
    _ADC_CS   = const(0x00 >> 2)
    _ADC_FCS  = const(0x08 >> 2)
    _ADC_FIFO = const(0x0c >> 2)

    def __init__(self, adc_pin):
        self.adc = ADC(adc_pin)

    @micropython.viper
    def adc_on(self):
        ADC: ptr32      = ptr32(_ADC_BASE)
        #ADC[FastADC._ADC_FCS]   =   (1<<0)      # EN write result to the FIFO after each conversion.
        ADC[_ADC_FCS]   =   (1<<0)
        ADC[_ADC_CS]    = ( (1<<3)      # START_MANY Continuously perform conversions.
                        |   (1<<0) )    # EN Power on ADC and enable its clock.

    @micropython.viper
    def adc_off(self):
        ADC: ptr32      = ptr32(_ADC_BASE)
        ADC[_ADC_FCS]   = 0
        ADC[_ADC_CS]    = 0

    @micropython.viper
    def get_samples(self, values: ptr16, length: int):
        FIFOEMPTY: int  = (1<<8)        # prepare mask
        ADC: ptr32      = ptr32(_ADC_BASE)
        self.adc_on()
        index: int = 0
        start: int = ticks_us()

        while (ADC[_ADC_FCS] & FIFOEMPTY) == 0:
            ADC[_ADC_FIFO]

        while index < length:
            while ADC[_ADC_FCS] & FIFOEMPTY: pass
            values[index] = ADC[_ADC_FIFO]
            index += 1
        stop: int = ticks_us()
        self.adc_off()
        samplingTime_us=float(ticks_diff(stop, start)) / float(length)
        return values,samplingTime_us

    @staticmethod
    def show_results(dt):
        print(f'sampling time:      {dt:10} µs')
        print(f'sampling frequency: {1e6/dt:10} Hz')

# Usage example:
if __name__ == "__main__":
    fast_adc = FastADC(0)  # Initialize with ADC pin 0
    length = 1000
    values = array('H', (0 for _ in range(length)))
    for n in range(0,10):
      values,dt = fast_adc.get_samples(values, length)
      FastADC.show_results(dt)