← index #1681PR #4213
Related · high · value 0.326
QUERY · ISSUE

stmhal: ADC class only uses ADC1 hardware

openby blmorrisopened 2015-12-04updated 2024-08-28
enhancementportsport-stm32

This issue appears not to be very minor for the pyboard or any board using 64- or 100-pin versions of the STM32F405, so it could be low-priority or 'won't fix'. However, I encountered this running MicroPython on an Olimex STM32-E407 dev board, which uses a 144-pin STM32F407. On this board, much of the GPIO from ports A, B, and C are connected to / utilized by on-board peripherals (like an Ethernet PHY). Most of the ADC input pins that are broken out to headers are on Port F, but those ADC pins are only connected internally to ADC3, so not accessible via the current ADC class:

>>> pf6 = pyb.ADC('PF6')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: pin F6 does not have ADC capabilities
>>> 

Since I am already customizing the firmware a bit to get it running on the Olimex board, I think that I can solve my immediate problem by hacking a few lines in adc.c to use ADC3 instead of ADC1. I don't have a more generic solution yet, so I just wanted to raise the issue before I forgot about it.

The biggest potential benefit that I could see in enabling the ADC2 and ADC3 hardware for lower pin-count chips would be to enable high-speed interleaved ADC reads on those pins connected to all three instances, but it would take some serious recoding to allow this, for an application that may see relatively little use.

CANDIDATE · PULL REQUEST

docs: Specify new machine.ADC and machine.ADCBlock classes

mergedby dpgeorgeopened 2018-10-04updated 2022-01-21
docs

Based on the discussion in #3943 here is my proposal for a revised machine.ADC class which is intended to be implemented by all ports to make the ADC class more standardised. The changes here include a new machine.ADCBlock class as well, which is used to allow further configuration of ADC if a port supports it.

In summary the new interface is:

ADC(id, *, sample_ns, gain_mult, gain_div) # create ADC object
ADC.init(sample_ns, gain_mult, gain_div) # initialise
val = ADC.read_u16() # read value between 0-65535
val = ADC.read_v() # read voltage, float
val = ADC.read_mv() # read mV, integer

ADCBlock(id, *, bits) # create ADC peripheral object
ADCBlock.init(bits) # initialise
adc = ADCBlock.connect(channel) # connect to a channel
adc = ADCBlock.connect(source) # connect up a pin or other object
adc = ADCBlock.connect(channel, source) # connect a channel to a pin

The API is split up this way into two classes (ADC and ADCBlock) so that basic and common functionality is easily available in the ADC class, and extended functionality is in ADCBlock. A given port only needs to implement ADC if that is all it can do, and already that gives 80% of the functionality. The ADC class is the one that would be used most of the time.

If a port wants to expose more control, and the user needs to use it, then the ADCBlock class is there for that. It allows to specify the resolution, and which channels are connected to which pins.

All existing ports should be able to fit into this model without a problem. In particular the gain settings can be used by nrf and esp32. stm32 can use the sample time.

It should be possible to update the ports in a backwards-compatible way to this new API because most of the methods are new and don't clash with existing ones. ADCBlock is completely new so won't clash with anything.

There is room for extensions to this API: ADC can have additional methods like read_u24() and read_uv(), and ADCBlock might get methods to read internal channels, like voltage references and temperature.

Note: it might be easier to read the diff of this PR in split view.

Keyboard

j / / n
next pair
k / / p
previous pair
1 / / h
show query pane
2 / / l
show candidate pane
c
copy suggested comment
r
toggle reasoning
g i
go to index
?
show this help
esc
close overlays

press ? or esc to close

copied