Why a converter
A microcontroller is a system which works on bits and bit streams more than analog signals, anyway those signals are still widely used in many fields especially in audio and power applications. An Analog to Digital Converter (or ADC) is a system that converts such kind of signals in a sequence of discrete values directly interpretable by the microcontroller. Such kind of converter is widely used in embedded systems to deal with those peripherals which works with analog signals like potentiometers, analog microphones or sensors.
- Analog sensors which produces a voltage level proportional to the measured property, like industrial temperature sensor which usually are made with a calibrated Diode.
- A 2-axis joystick you probably have seen on the PlayStation controller. Each axis is actually a potentiometer and it is possible to read it using two channels of the STM32 ADC. We wrote an article about that.
- The guitar pickups are actually pressure sensor which are able to convert air pressure variation produced by chords vibration into an electric signal.
Once a signal have been sampled with an ADC and it is possible to manipulate it digitally. Considering the guitar pickup example, after the conversion it is possible to use the microcontroller to apply digital sound effects as example equipping the guitar with a small PCB which mounts a micro and few electroncs. Cool right? Yeah, and the coolest part is that someone created a prototype base on this idea (take a look if you want).
The Analog to Digital Convert
The theory related to the ADC is particularly large and there is a lot of literature about this topic. The converters are indeed largely used in many field which go from the Laboratory Test Equipment to Microelectromechanical systems. Because of that there are many implementation and variants of them to match all the different needs. In this chapter we would just explain few concept you should know to deal with ADC.
An ADC converts an analog continuous-time signal in a digital discrete-time signal. As example in the next figure we can spot a time continuous analog sinusoid in blue and the a digital version of the same signal in orange.
First notion we want to introduce here is the Resolution which represent the number of discrete values that the ADC can produce. The sampled value are stored in form of binary code thus usually the ADC has usually a resolution which is a power of 2 and which is often expressed in bits more than in absolute value. In the previous example the ADC was able to produce up to 8 different value (from 0 to 7 or in binary from 0b0 to 0b111): in such case we can state that this ADC has a 3-bit resolution.
Extending this notion, an ADC having a resolution in bit equal to n will generate sampled values in the range
Full scale voltage range
An ADC is able to sample only voltages in a specific range. Usually ADC are able to convert only voltages which goes from reference ground to a maximum positive value which is known as Full scale voltage range (or even FSR). Anyway, a more general definition of full scale range would be
To avoid sensitivity reduction as well as distortion or noise introduction, the input of an ADC is a clean conductor with no protection diodes nor conditioning circuitry and this means that user shall pay attention to provide signals which amplitude is coerced in the proper range. To measure signals which are out of range indeed the signal shall be usually conditioned by external circuitry. An example of conditioning circuitry are that used in the vertical channels of oscilloscopes which are even configurable allowing to select between Alternate and Direct Coupling as well as tune the FSR.
Back to the previous example, the value 0b111 correspond to a 3.5V input and 0b0 to 0V. Thus we can state the ADC has a full scale range of 3.5V.
While the FSR represent the range of input voltage, the Resolution represent the range in which lays the output binary code. The ratio of these two quantities provides an useful information about the “weight” of the LSb of the output binary value in term of voltage. This new quantity is known as Voltage resolution
Considering the previous example the voltage resolution is
As seen in the previous example, the LSb of the binary code has a value of half volt. This means we will not see any output variation if input is 0.5V or 0.9V: both will be converted as 0b001.
Converting a continuous quantity in a discrete one there is always an information loss. Such information loss is much more greater the smallest is the resolution of our ADC. The quantization error can be evaluated computing the difference between an input value and its quantized value. In the previous figure we have marked the quantization error for the 5th sample which is marked by a dark red arrow. That error is more evident but note that there is an error on every sample. In some cases this error is very small, almost negligible. Anyway it is clear the quantization error has an upper bound which is actually the voltage resolution
There a couple of action we can take to reduce the quantization error side effect: one is to reduce the voltage resolution which means to have an higher resolution, the other is to exploit the whole convertion range sampling wide signals which overlap the range as much as possible. Let us clarify this with an example considering a 4-bit ADC with a FSR of 3.0V. In such
case we have
Let us now consider that our input is a signal in range 0-1.2V. In such scenario we are not using the whole input range and we are using more or less only 3-bits as
and the next figure shows which would be the effect of quantization on the sampled waveform
If we amplify the input signal with an external circuitry trying to overlap the whole input range we would make a better use of our digital representation space. Indeed, in such case we will use all the 16 symbols (instead of only 7) to represent the sampled version of our signal. The next figure shows the result of such operation: we can see the quantization error is still there but we we have a lower information loss.
If the quantization error is still too big for our application another solution could be to increase the representation space of our digital output or in other word to use an ADC having a better resolution.
The ADC samples the signal each while. Usually the time elapsed between two samples remains constant in time and takes the name of Sampling time or, considering its reciprocal, we have a frequency named Sampling Rate. In our example the sampling time is represented by the pink arrow. Note that usually sampling frequency is measured as Samples per second (S/s) and its multiple (kS/s, MS/S or GS/s).
In certain cases having a constant sampling cadency over time is a requirement of the application and in those scenario the ADC is usually triggered by an external entity which gives time like a timer. An example of application which requires such precision is audio sampling whereas you often would to convert an analog audio signal in a Waveform Audio File Format (or wav) which is usually a PCM having a constant bitrate (usually 44.1kHz, 48kHz or 96kHz).
Note that maximum sampling rate sets a constraint on input signal maximum frequency we are able to perceive: we are blind on frequencies greater than half of the sampling frequency as Nyquist states.
The STM32 ADC peripheral
Working principle of a SAR ADC
Every STM32 equips a 12-bit resolution Successive Approximation ADC. In figure 4 we can see a principle diagram of such ADC. In the scheme we can spot:
- A Sample and Hold circuit which samples the input voltage and keep it unchanged until the end of conversion. This circuit requires a certain Sampling Time usually expressed in clock cycles to do its job.
- A Digital to Analog Converter which purpose is to convert a binary code into a voltage (its behaviour is the the dual of the ADC).
- A Comparator which purpose is to compare its inputs. The result of comparison is a logic level that indicate if the DAC output is greater of smaller than S/H output.
- The Successive Approximation Register which is a complex logic which scope is to provide an approximated binary code to the DAC and, when conversion is over, the output of conversion.
The conversion happens as follows:
- The input is sampled in m clock cycles by the sample and hold.
- The SAR set the bit D11 to ‘1’
- The DAC receives thus 0b1000 0000 0000 generating an output voltage
- The comparator compares its two inputs. If the S/H output is greater that the DAC output the D11 remains to ‘1’ otherwise it is changed to ‘0’
- The SAR set the bit D10 to ‘1’ while D11 depends by the previous cycle.
- The DAC receives a new value which better approximate the input generating a new output voltage
- The comparator compares its two input. If the S/H output is greater that the DAC output the D10 remains to ‘1’ otherwise it is changed to ‘0’
- …. the loop continues on remaining bits
When all the bit have been processed the SAR contains a bit code which better approximate the input value. Note that the each bit of the SAR requires is determined in a clock cycle, thus the total time required by SAR to perform a conversion is equal to the resolution of the ADC expressed in bits. The total number of clock cycles required is thus
Multiplexed input and sequences
Each STM32 ADC has many input sources usually named channel, anyway the ADC can sample one channel at time thus the channels are multiplexed. These channels are identified with a progressive number starting from 0, the first 16 channels are usually assigned to external sources (e.g. are connectable to the outside world through the GPIO peripheral). Other channels are internal sources like the VBAT (e.g. the voltage of the backup battery usually used to keep up the Real Time Clock) or like the VTEMP (e.g. the voltage of internal temperature sensor).
External inputs are assigned to some specific GPIO which shall be configured in Analog Mode to ensure the proper connection with the ADC. You can find information about these assignments exploring the Datasheet of the STM32 and you should also take a look to the article Using STM32’s GPIO with ChibiOS’ PAL Driver if you didn’t.
To guarantee flexibility the ADC is able to convert also group of channels organized is a user defined sequence. The sequence can be configured acting on some registers known as ADC Sequence Registers. Usually those registers are three (ADCx_SQR1, ADCx_SQR2 and ADCx_SQR3) and the maximum sequence length is 16 anyway this can vary across different microcontroller even if working principle remains quite the same. Each sequence register is splitted in many bit fields representing an element of the sequence and in the first register there is also a bit field representing the length of the whole sequence (actually length minus one, as instance if we store 3 in it we are stating the whole sequence length is 4).
The sequence can be composed in a completely arbitrary way and user can even repeat the same channel more than once. To configure it we have to populate these bitfield with the numeric identifier of the channel. Thus if as example we would to set a sequence like ADC_CHANNEL_IN0, ADC_CHANNEL_IN14 and ADC_CHANNEL_IN3 we should set
If you are not familiar with shift and bitwise operators you should definitely read Registers and bit masks which is a short article that explains how to deal with read-modify-write paradigm and registers in general.
We will see later that ChibiOS ADC driver exposes some helper macros and constants to compose the sequence registers.
Sampling time and trigger
The ADC requires to be triggered to start a conversion.
Once triggered the STM32 ADC will sample the whole sequence converting subsequently all the channel of the sequence at the maximum rate.
This mean there is no pause between the conversion of one channel and the next one in the sequence. We can anyway influence the sampling time of the whole sequence acting on the sampling time of each sequence element. This can be done basically through two parameters:
- The ADC resolution which can be configured through one of its control register (usually ADCx_CR1). Note that available resolutions are limited (usually 6-bit, 8-bit, 10-bit or 12-bit);
- The ADC S/H circuit sample time which can be configured independently on each channel acting on some registers (usually 2) known as ADC Sample Rate Register (ADCx_SMPR1 and ADCx_SMPR2)
Sample rate registers are splitted in many bit fields each one representing the sampling rate of a channel (SMP0 is the rate of ADC_CHANNEL_IN0, SMP1 of ADC_CHANNEL_IN1, and so on).
The available sample rate values are limited and possible values depends on specific driver version (as example 3, 15, 28, 56 cycles). The total sample time of each channel can be computed with next formula
The sample time of the whole sequence is the sum of time required to sample each channel. As instance let us assume that
- the sequence is again ADC_CHANNEL_IN0, ADC_CHANNEL_IN14 and ADC_CHANNEL_IN3
- ADC resolution is 12-bit
- the sample rate for ADC_CHANNEL_IN0 is 3 cycle
- the sample rate for ADC_CHANNEL_IN14 is 15 cycle
- the sample rate for ADC_CHANNEL_IN3 is 15 cycle
In such scenario the number of clock cycles will be 3 + 12 + 15 + 12 + 15 + 12 (plus a fraction of a clock cycle due to an eventual misalignment between ADC clock and trigger).
The STM32 ADC can be triggered in Software by writing a trigger bit in a control register (usually the SWSTART bit in the ADCx_CR2). It can also be triggered by an external signal which can be injected by the outside world or generated by an internal timer: this option is largely used when sampling shall be performed with precise timing. For this purpose there are certain General Purpose Timer of the STM32 which have no output channel but only internal lines just to be used as trigger for the ADC and DAC peripherals: such timers are usually the TIM6 and TIM7.
The behaviour of trigger depends on the Conversion Mode of the ADC. If the ADC is configured as Single Conversion Mode (on trigger event) it converts a sequence then stops. If it is configured in Continuous Conversion Mode the ADC starts a new conversion as soon as it finishes one.
The ChibiOS ADC driver
The ChibiOS ADC driver exploits all the feature of the STM32 ADC to cover a large number of scenarios offering both blocking and non-blocking functions, fully configurable sample groups, circular/linear conversion modes and configurable trigger.
Each API of the ADC Driver starts with the prefix “adc”. Function names are camel-case, pre-processor constants uppercase and variables lowercase.
Different driver same approach
Comparing the ADC driver with previously presented Serial Driver we can notice that there are some certitudes in ChibiOS: one of these is for sure the consistency of design patterns. The ADC driver is organized like every other simple driver of ChibiOS\HAL:
- The whole driver subsystem can be enabled disabled through the proper switch in the halconf.h. The switch name is HAL_USE_ADC.
- To use the driver we have then to assign it an ADC peripheral acting on mcuconf.h.
- Assigning a peripheral to the driver a new object will become available: ADCD1 on ADC 1 assignation, ADCD2 on ADC 2 and so on.
- Each ADCDx object represent a driver which implements a Finite State Machine.
- A driver to be used shall be initialized but this is done automatically on halInit();
- Each driver operation (e.g. a conversion) can be done only if driver has been properly started. This requires a call to adcStart().
- The adcStart() receives as usual two parameters: a pointer to the driver and a pointer to its configuration.
- If the driver is not used it can be stopped through the adcStop().
The following figure represent the state machine of the driver.
The ADC driver offers both synchronous and asynchronous conversion function. We already introduced these concept but let me recall them.
A blocking function blocks the execution of the calling thread until the operation is completed. If the function has some internal wait they are not polled, instead the calling thread is suspended and the RTOS executes other threads. Such functions are also know as synchronous functions as the calling thread remains in sync with the operation.
A non-blocking function launches the operation and returns to the calling thread which continues to be executed. Such operation is executed in background, usually in hardware, launching a interrupt on operation complete event. Such IRQ usually is exposed by driver API in form of a callback. Such function are also know as asynchronous functions.
The synchronous version of the ADC conversion is the adcConvert
the asynchronous one the adcStartConvert
Both the conversion functions receives the same parameters. Let us take an overview of them:
First parameter is, as usual, a pointer to the driver. Remember that the driver shall be be already started and configured through the adcStart before to call a conversion function: this is a common pattern in ChibiOS which I use to call start-do-stop.
Anyway ADC configuration itself is very poor and, in certain driver version, is even empty. This would sound quite strange (especially because we have spent a whole chapter talking about features and configuration of STM32 ADC). This take us to the second parameter: the ADCConversionGroup. Most of the ADC configuration is indeed demanded to the conversion functions and it is confined in this configuration structure. The logical motivation of this design is that configurations like trigger source, sequence or sample rate are related to the conversion more than to the driver itself.
The ADCConversionGroup contains all the configuration related to the conversion group like the trigger source, the sequence, the sample rate and the conversion mode.
Let me now jump to the fourth parameter which is named depth and is a size. The conversion function allow to sample the same conversion group more than once and depth represent the number of times we want to sample the conversion group. Thus if we consider the conversion group lenght (M) the first dimension we should consider the depth (N) the second.
Thus a conversion function need a buffer which is actually a matrix M by N which lead us to the third parameter which is actually a pointer to that buffer. The user shall guarantee that buffer is properly sized to avoid overflow. Note that the sample buffer type is specific of ChibiOS and this allow abstraction on Hardware differences. In case this case adcsample_t is a 16-bit unsigned integer. The data alignment depends on certain configuration we can provide through the ADCConversionGroup.
There are some restriction about depth: it shall be at least one and it shall be an even number. The first one is almost obvious but to understand the second we should understand how asynchronous conversion works. When we launch the adcStartConvert we have also to provide a callback through the ADCConversionGroup. This callback is also know as end callback (end_cb) and is called in twice specific moments of the conversion:
- when the ADC has filled the first half buffer
- when the ADC has filled the whole buffer.
This is very useful in circular mode when the conversion continues indefinitely and the matrix is used as a circular buffer. Let us clarify this considering a scenario in which:
- the sequence is ADC_CHANNEL_IN0, ADC_CHANNEL_IN14 and ADC_CHANNEL_IN3,
- conversion depth is 10,
- timing are same of the previous example,
- conversion is asynchronous (e.g. we have used the adcStartConvert),
- trigger is software (e.g. conversion starts on adcStartConvert)
In such scenario the buffer is a 3×10 matrix. On call the adcStartConvert configures the ADC triggering the conversion and promptly returns to calling thread which continues its execution. From here an out while kernel is executing threads ADC is converting continuously relying on DMA to fill memory with converted data: this means the only way to get in sync with conversion is to use the end callback. As said this callback will be triggered when ADC (more precisely DMA) has filled the first half buffer: in this case the ADC has performed 5 conversion and it is going to fill the second half buffer: through the callback when the ADC is doing that we can operate on the first half. Next callback will be on the completion of second half buffer conversion: here the ADC will start to convert overwriting the first half buffer and we can operate on the second one. This solution allows us to operate in real time on sampled data as long as our operations inside the callback are faster the ADC half buffer conversion.
Note that in case of linear conversion callback is triggered only in the end of the whole conversion. Note also that such kind of callback receives certain parameter like a pointer to the driver, a pointer to the buffer currently filled and the size of the converted buffer: this means in circular mode we will receive a pointer to the current half buffer and NxM/2 as size, in case of linear mode we will receive the pointer to the whole buffer and NxM as size.
Back to the circular buffer scenario, it is clear that API requires an additional function to stop the ongoing conversion which could potentially last indefinitely. Such function is the adcStopConversion
Conversion Group configuration
At the end of the day, the most difficult part in ADC usage, is to properly configure the conversion group. This is particularly tricky because STM32 ADC has many variants depending on sub-family and in certain case on specific Microcontroller. ChibiOS\HAL implements many different versions of this driver to resolve hardware differences. Details related to ChibiOS\HAL design can be found in this article.
Anyway, currently there are 4 different drivers version plus a couple of specific ADC driver implementation to cover all the hardware differences across the STM32 subfamilies. From the user point of view, a different driver implementation means different ADC register organizations, different bit mask. By the way remember that all the hardware differences are confined in configuration structures. In this case you will have different ADCConversionGroup and ADCConfig while the ADC API itself remains constant.
In what follow we are going to take a deep look in the ADCConversionGroup of the drivers ADCv2 which supports the STM32 “advanced” ADC found on F2, F4 and F7 sub-families. This walkthrough, the testhal demos and the STM32 reference manual should be far enough to understand how to use other driver versions also.
First step is to identify which driver version is the proper one for you STM32. In case you want to learn how to do take a look here, otherwise for a quick reference:
- STM32F0xx and STM32L0xx use ADCv1;
- STM32F1xx uses a platform specific ADC driver;
- STM32F2xx, STM32F4xx and STM32F7xx use ADCv2;
- STM32F3xx and STM32L4xx use ADCv3;
- STM32F37x and STM32F38x use a platform specific ADC driver;
- STM32H7xx uses ADCv4.
Now, the easiest way to use ADC is to copy some snippets of code from a testhal demo. Such demos are under the folder [chibios_root]\testhal\STM32. In such directory you can find demos for many every STM32 subfamily. Anyway, it is not guaranteed that you will find runs on your development board.
As example let us consider the STM32F4 sub-family: reading the readme.txt of the demo [chibios_root]\testhal\STM32\STM32F4xx\ADC we would notice that this demo has been created for STM32F4 Discovery thus will not run on a STM32 Nucleo-64 F401RE. Nevertheless, the ADCConversionGroup used in this demo is compliant with any STM32F4 MCU as well as with STM32F2xx and STM32F7xx families. Thus steps to port this demo are:
- Clone the default demo for STM32 Nucleo-64 F401RE;
- Enable the ADC driver (halconf.h) and assign a peripheral to it (mcuconf.h);
- Copy from testhal demo the whole main or snippet of code related to ADC.
- Resolve eventual differences like different identifier of LED line.
Usually the demos have more than a configuration: this allows user to make comparison and understand how configuration works. Now considering the proposed demo it has a couple of configuration. The first one is the simpler and samples 1 channel linearly triggered by software.
This configuration is extremely simple but has a lot of field many of which are empty. What if the user has to modify this configuration to achieve a custom behaviour?
To create some custom configuration, it is required to investigate on the meaning of each ADCConversionGroup field taking a look to how structure is defined in the ChibiOS LLD code. More in detail this structure is defined in the file hal_adc_lld.h and each driver version has is own file. As example the path of LLD header for ADCv2 is [chibios_root]\os\hal\ports\STM32\LLD\ADCv2\hal_adc_lld.h. As counter example, the one for STM32F37x is [chibios_root]\os\hal\ports\STM32\STM32F37x\hal_adc_lld.h.
Back to ADCv2 we have
The first four fields are common across every driver version:
- circular, is a boolean which programs the ADC to work in circular mode when true or linear otherwise. In linear mode the ADC converts a group one time than stops; In circular mode, the ADC converts group continuously using the sample buffer in a circular way.
- num_channels, is an unsigned representing the length of the sequence. This value is used internally to configure the LEN bit field of the ADCx_SQR1 register. Remember that the maximum number depends on hardware as each sequence elements is actually a bit field in the sequence registers.
- end_cb, is that callback, the one executed when the conversion is completed and, in case of circular buffer, on half buffer.
- error_cb, which is a callback triggered on ADC hardware error.
The other fields are instead hardware dependent and thus can change between different version of the ADC driver. Basically these fields represent the value of some ADC registers. Some of them can be ignored in most of the scenario and left to zero, others requires close attention:
- cr1, cr2, … crx are the control registers. The driver configures them partially but we can use them to change some configuration like ADC resolution, data alignment or trigger source.
- sqr1, sqr2, … sqrx are the sequence registers.
- smpr1, smpr2, … smpr3 are the sample rate registers.
To compose registers value we can use the bitmasks already defined in the CMSIS header files. Such files contains all the definition of every bit field of every register of our MCU. It is possible to find under [chibios_root]\chibios_trunk\os\common\ext\ST\. Each MCU has is own CMSIS header file as the register map changes with microcontroller. As example the file for the STM32F401RE is [chibios_root]\chibios_trunk\os\common\ext\ST\STM32F4xx\stm32f401xe.h.
What follows are some bit masks definition for the ADC registers. Note that has some ChibiOS drivers already uses those bitmasks internally, the proper file for the micro in use is already included.
The ChibiOS/HAL drivers often offer some additional helper. As example ADCv2 defines identifiers for the input channel channels.
We can notice that the first 16 channel are called generically ADC_CHANNEL_INx which is by the way the same name adopted in the STM32 documentation. Such channels can be connected to the outside world through GPIO and this can be done configuring PIN as Analog Mode. If you do not remember how to configure GPIO through PAL take a look back to the article Using STM32’s GPIO with ChibiOS’ PAL Driver. The others instead are extra channels connected to internal peripherals.
We also talked about a limited number of options for the ADCx_SMPRx bit fields. These values are defined in ChibiOS ADC driver as well and, with reference to ADCv2, they are an unsigned integer in range 0 to 7 : this explains why each bit field as a lenght of 3-bit
Lastly the driver offers some macros to build sequence with ease
Note that macro perfectly shows to which register belongs each bit field. As example ADC_SQR2_SQ9_N states that the ninth element of the sequence is a piece of the ADC Sequence Register 2.
To conclude let us consider the already considered scenario in which
- the sequence is again ADC_CHANNEL_IN0, ADC_CHANNEL_IN14 and ADC_CHANNEL_IN3
- ADC resolution is 12-bit
- the sample rate for ADC_CHANNEL_IN0 is 3 cycle
- the sample rate for ADC_CHANNEL_IN14 is 15 cycle
- the sample rate for ADC_CHANNEL_IN3 is 15 cycle
- trigger is software
- mode is linear
In such case the ADCConversionGroup would be
Note that we do not have to configure the LEN bit field in the ADC_SQR1 because the driver already does it internally starting from the second parameter of the conversion group (num_channels).
To conclude note that resolution can be configured acting on bit24 and bit25 of the ADC_CR1 and when those bit are set to 0 ADC resolution is set to 12-bit. There are many other configuration we can achieve acting on control registers. User interested to this should take a look to the microcontroller Reference Manual to understand how to handle those register.
A commonly used “advanced” configuration is the external ADC trigger which allows to trigger each conversion group with an extremely precise timing using a general purpose timer. Such kind of configuration is widely used in audio application where certain formats like PCM require that audio signal is sampled with a specific cadency (44.1kHz, 48kHz, 96 kHz, …). ChibiOS already provides a demo ADC+GPT under testhal. Moreover on the download page of this website you can also find a porting of this demo for the STM32 Nucleo-64 F401RE.
Further readings and Hands-on
We have already planned a collection of example and exercises for the ADC driver. If you are interested in follow us on Facebook to be updated on our articles. Anyway, at this moment you could refer to the ADC demo under testhal to give it a try.
Under testhal you can find also the ADC-GPT demo which shows how to trigger ADC in hardware using a timer. Remember that those demos are written for specific board: as example many STM32F4xx demos have been written for STM32F4 Discovery. Anyway, in case you are using a STM32 Nucleo-64 F401RE, I have ported the ADC-GPT and you can find it in the Download page of this website. Anyway for quick reference
You should also take a look to the demo for Joystick which uses ADC to sample voltage across two potentiometer representing the two axis as well the slider potentiometer
Previous and next
This article is part of a series of articles which are meant to be tutorials. I have composed them to be read in sequence. Here the previous and next article of this series:
- Using STM32 USART with ChibiOS Serial Driver (Previous)
- PWM in hardware with STM32 Timer and ChibiOS (Next)
- Why a converter
- The Analog to Digital Convert
- The STM32 ADC peripheral
- The ChibiOS ADC driver
- Further readings and Hands-on
- Previous and next