Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

samd/adc_dac: Implememt adc.read_timed() and dac.write_timed(). #9624

Open
wants to merge 13 commits into
base: master
Choose a base branch
from

Conversation

robert-hh
Copy link
Contributor

@robert-hh robert-hh commented Oct 14, 2022

@dpgeorge As requested, this is the separate PR for ADC-timed and DAC-timed. Below
is a short documentation.

ADC Constructor:

ADC(Pin, *, average=16, bits=12, vref=3, callback=None)

The callback keyword option is used for timed ADC sampling. The callback is executed when all data has been sampled.

ADC Methods:

adc.read_u16()

Read single value from the Analog Pin using the bits and average setting from the constructors.

adc.read_timed(data, freq)

Read adc values into the data buffer at a supplied frequency. The buffer must be pre-allocated. Values are stored as 16 bit quantities in the binary range given by the bits option. If bits=12, the value range is 0-4095.
The voltage range is defined by the vref option.
If in the constructor a callback was defined, it will be called after all data has been read. Alternatively, the method busy() can be used to tell, if the capture has finished.

adc.busy()

busy() returns True while the data acquisition using read_timed() is ongoing, False otherwise.

adc.deinit()

Deinitialize as ADC object and release the resources used by it, especially the ADC channel and the timer used for read_timed().

DAC Constructor

DAC(id, *, vref=3, callback=None)

The resolution of the DAC is 12 bit for SAMD51 and 10 bit for SAMD21. SAMD21 devices have 1 DAC channel at GPIO PA02, accepting only 0 as id. SAMD51 devices have 2 DAC channels at GPIO PA02 and PA05 with values 0 and 1 for the id.

DAC Methods

dac.write(value)

Write a single value to the selected DAC output. The value range is 0-1023 for SAMD21 and 0-4095 for SAMD51. The voltage range depends on the vref setting.

dac.write_timed(data, freq [, count=1])

The call to dac_timed() allows to output a series of analogue values at a given rate. data must be a buffer with 16 bit values in the range of the DAC (10 bit of 12 bit). freq may have a range of 1Hz to ~200kHz for SAMD21 and 1 Hz to ~500kHz for SAMD51. The optional argument count specifies,
how often data output will be repeated. The range is 1 - 2**32. If count == 0, the data output will be repeated until stopped by a call to deinit(). If the data has been output count times, a callback will
be called, if given.

dac.busy()

Tells, whether a dac.write_timed() activity is ongoing. It returns True if yes, False otherwise.

dac.deinit()

Deinitialize the DAC and release the resources used by it, especially the DMA channel and the Timer. On most SAMD21 boards, there is just one timer available for dac.write_timed() and adc.read_timed_into(). So they cannot run both at the same time, and releasing the timer may be important. The DAC driver consumes a substantial amount of current. Calling deinit()
will reduce that as well.

@robert-hh
Copy link
Contributor Author

Just a conflict resolved after rebase.

@robert-hh
Copy link
Contributor Author

@dpgeorge You mentioned about this PR that you would have to consider a proper API for that ADC/DAC timed feature. I guess you did not have time and it got lost under all other requests you have. The actual PR's API is close to that of the STM32 port.

@robert-hh
Copy link
Contributor Author

Re-based and Re-tested. Still fine.

@github-actions
Copy link

github-actions bot commented Oct 24, 2023

Code size report:

   bare-arm:    +0 +0.000% 
minimal x86:    +0 +0.000% 
   unix x64:    +0 +0.000% standard
      stm32:    +0 +0.000% PYBV10
     mimxrt:    +0 +0.000% TEENSY40
        rp2:    +0 +0.000% RPI_PICO
       samd: +2396 +0.903% ADAFRUIT_ITSYBITSY_M4_EXPRESS[incl +36(data) +304(bss)]

@codecov
Copy link

codecov bot commented Oct 24, 2023

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 98.39%. Comparing base (c1a6b95) to head (5efb48e).

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #9624   +/-   ##
=======================================
  Coverage   98.39%   98.39%           
=======================================
  Files         161      161           
  Lines       21204    21204           
=======================================
  Hits        20864    20864           
  Misses        340      340           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@robert-hh robert-hh force-pushed the samd_adc_dac branch 2 times, most recently from 1cf57d7 to d59e450 Compare October 24, 2023 09:41
@robert-hh robert-hh force-pushed the samd_adc_dac branch 3 times, most recently from 4070f1d to 3fea431 Compare November 6, 2023 14:19
Used for allocation of DMA channels. It will be needed for planned
modules and methods like adc_timed(), dac_timed(), I2S.

It includes management code for DMA IRQ handlers, similar to what
was made for Sercom.

Signed-off-by: robert-hh <robert@hammelrath.com>
These functions are use to allocate, free and configure a set
of TC counter instances. The SAMxx MCU have between 3 to 5 (SAMD21) and
4 to 8 (SAMD51) TC instances. Two of them are used for the µs counter,
the remaining 1 - 6 instances are administered here for use by
various functions, like timed DMA transfers.

Signed-off-by: robert-hh <robert@hammelrath.com>
Call:
dac.write_timed(data, freq [, count])
dac.deinit()

Working range for dac_timed():

SAMD21: 1 Hz - 100 kHz (1 MHz clock, 10 bit)
SAMD51: 1 Hz - ~500 kHz (8 MHz clock, 12 bit)

The buffer has to be a byte array or a halfword array,
and the data is sent once.

The default for count is 1. If set to a value > 0, the data will be
transmitted count times. If set to 0 or  < 0, the date will be
transmitted until deliberately stopped. The playback
can be stopped with dac.deinit().

dac.deinit() just releases the timer and DMA channel needed by
dac_timed(). The DAC object itself does not have to be released.

Signed-off-by: robert-hh <robert@hammelrath.com>
Call: adc.read_timed(buffer, freq)

buffer must be preallocated. The size determines the number of 16 bit
words to be read. The numeric range of the results is that of the raw
ADC. The call returns immediately, and the data transfer is done by DMA.
The caller must wait sufficiently long until the data is sampled
and can be noticed by a callback. No internal checks are made for
a too-high freq value.

Read speeds depends on Average and bit length setting:

SAMD21: Max. 350kS/s (8 bit, Average 1)
SAMD51: Max. 1 MS/s (8 bit, Average 1)
Signed-off-by: robert-hh <robert@hammelrath.com>
The callback is called when a dac_timed() sequence finishes. It will be
reset with callback=None or omitting the callback option in the
constructor.

Side change: Set the clock freq. to 48Mhz.

Signed-off-by: robert-hh <robert@hammelrath.com>
Enabling a callback that will be called when a adc.read_timed_into() run
is finished. That's especially useful with slow sampling rates and/or
many samples, avoiding to guess the sampling time.
Raise an error is adc.read_u16() is called while a read_timed_into()
is active.

Other ADC changes:
- SAMD51: use ADC1 if both ADC1 and ADC0 are available at a Pin.

Signed-off-by: robert-hh <robert@hammelrath.com>
These return True, while a timed action is ongoing.

Side change:
Reorder some code in machine_dac.c and do not reset DAC twice.

Signed-off-by: robert-hh <robert@hammelrath.com>
Signed-off-by: robert-hh <robert@hammelrath.com>
Signed-off-by: robert-hh <robert@hammelrath.com>
Since the two channels of a SAMD51 are not completely independent,
dac.deinit() now clears both channels, and both channels have to
be re-instantiated after a deinit().

Side change:
- rearrange some code lines.

Signed-off-by: robert-hh <robert@hammelrath.com>
Both together require ~1.9k of flash space, including the DMA-manager
and the TC-manager. adc.read_timed() uses ~700 bytes, dac.write_timed()
~600 bytes.

Signed-off-by: robert-hh <robert@hammelrath.com>
To leave no half-initialized device if init fails.

Signed-off-by: robert-hh <robert@hammelrath.com>
After machine.ADC has been moved to extmod/machine_adc.c.
Adding adc.read_timed() and adc.busy() to extmod/machine_adc.c with
a corresponding flag to enable them.
ADC/DAC timed are by default enabled only at all SAMD51 devices and
at SAMD21 devices with an external flash for the file system.

Side changes:
- Fix a type in pin_af.c, preventing to use a second ADC channel.
- Remove a duplicate definition in mcu/samd21/mpconfigmcu.h.

Signed-off-by: robert-hh <robert@hammelrath.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants