{"id":790,"date":"2024-07-09T13:12:09","date_gmt":"2024-07-09T11:12:09","guid":{"rendered":"https:\/\/redero.fr\/?p=790"},"modified":"2025-09-19T21:54:00","modified_gmt":"2025-09-19T19:54:00","slug":"zephyr-adc-api-for-kittens","status":"publish","type":"post","link":"https:\/\/redero.fr\/?p=790&lang=en","title":{"rendered":"Zephyr ADC API for kittens"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Analog to Digital Converters! Those versatile hardware blocks are typically used to retrieve data from analog sensors or board voltage rails.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">All the information you need about the Zephyr ADC functions can be found on the <a href=\"https:\/\/docs.zephyrproject.org\/latest\/hardware\/peripherals\/adc.html\" target=\"_blank\" rel=\"noreferrer noopener\">ADC API documentation<\/a>. Let\u2019s have an overview of this API and create a new code sample.<\/p>\n\n\n\n\n\n<h2 class=\"wp-block-heading\">Overview<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In order to read an analog input, you will need:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>To initialize the ADC inputs, with correct hardware pinmux and options,<\/li>\n\n\n\n<li>To run a read function, like adc_read, adc_read_dt or adc_read_async.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">A Zephyr ADC read function requires 2 inputs:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>An ADC hardware device, as device or as device tree spec,<\/li>\n\n\n\n<li>An object of type <a href=\"https:\/\/docs.zephyrproject.org\/latest\/hardware\/peripherals\/adc.html#c.adc_sequence\" target=\"_blank\" rel=\"noreferrer noopener\">adc_sequence<\/a> telling the function what to retrieve from said ADC device and how.<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-large wp-duotone-default-filter\"><img decoding=\"async\" src=\"https:\/\/kroki.io\/nomnoml\/svg\/eNpNzDEOgzAMQNHdp2AsvRJCkeOaNsI41E5QBx--dKjE-KWnP-GDkvO7sxIH0AtVWXy45dI29HUMMPYqvZWqMUA92By3XYo-A3JfFrZ7wHTdpLr_tAcUbWwHSupnEIpkpPXk_GmG6f_xGeYv6EE0AQ==\" alt=\"[adc_sequence|\nchannels (bitmask)|\nresolution| \noversampling|\nbuffer*|\n[adc_sequence_options|\ninterval_us|\ncallback*|\nextra_samplings]\n]\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">In order to create this adc_sequence object, two options:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Automatically initiate it from device tree parameters with <a href=\"https:\/\/docs.zephyrproject.org\/latest\/hardware\/peripherals\/adc.html#c.adc_sequence_options\">adc_sequence_init_dt<\/a>. This function takes a device tree adc_dt_spec object as an input, and fills in the adc_sequence object channels, resolution and oversampling fields with the corresponding zephyr,parameter from the device tree channel description:<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code has-medium-font-size\"><code>&amp;adc0 {\n    &#091;...] \n    channel@0 {\n        reg = &lt;0&gt;;\n        zephyr,gain = \"ADC_GAIN_1\";\n        zephyr,reference = \"ADC_REF_INTERNAL\";\n        zephyr,acquisition-time = &lt;ADC_ACQ_TIME_DEFAULT&gt;;\n        <strong>zephyr,resolution = &lt;12&gt;;<\/strong>\n        <strong>zephyr,oversampling = &lt;0&gt;;<\/strong>\n    };\n};<\/code><\/pre>\n\n\n\n<p class=\"has-text-align-right has-small-font-size wp-block-paragraph\" style=\"margin-top:0;margin-bottom:0\"><em>Device tree channel description<\/em><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Manually create and instantiate the adc_sequence object.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Recipe for an adc_sequence object<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Multichannel ADC reading<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">In order to configure a sequence with several channels, it is necessary to initialize the sequence channels parameter manually.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The resolution and oversampling parameters will be applied to all channels.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">All ADC channels have to belong to the same ADC hardware driver.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">ADC channels are selected by the channels parameters in the adc_sequence object. This parameter is a bitmask.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For example, for and ADC with 3 channels 0, 3 and 5:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&amp;adc0 {\n\u2002\u2002\u2002\u2002\u2002\u2002channel@0 {\n\u2002\u2002\u2002\u2002\u2002\u2002\u2002\u2002\u2002\u2002\u2002\u2002reg = &lt;0&gt;;\n\u2002\u2002\u2002\u2002\u2002\u2002\u2002\u2002\u2002\u2002\u2002\u2002&#091;...]\n\u2002\u2002\u2002\u2002\u2002\u2002};\n\u2002\u2002\u2002\u2002\u2002\u2002channel@3 {\n\u2002\u2002\u2002\u2002\u2002\u2002\u2002\u2002\u2002\u2002\u2002\u2002reg = &lt;3&gt;;\n\u2002\u2002\u2002\u2002\u2002\u2002\u2002\u2002\u2002\u2002\u2002\u2002&#091;...]\n\u2002\u2002\u2002\u2002\u2002\u2002};\n\u2002\u2002\u2002\u2002\u2002\u2002channel@5 {\n\u2002\u2002\u2002\u2002\u2002\u2002\u2002\u2002\u2002\u2002\u2002\u2002reg = &lt;5&gt;;\n\u2002\u2002\u2002\u2002\u2002\u2002\u2002\u2002\u2002\u2002\u2002\u2002&#091;...]\n\u2002\u2002\u2002\u2002\u2002\u2002};\n};<\/code><\/pre>\n\n\n\n<p class=\"has-text-align-right has-small-font-size wp-block-paragraph\" style=\"margin-top:0;margin-bottom:0\"><em>Device tree example channels description<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Bitmask is 0b101001 = 0x29.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Number of samples<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The number of samples to be read on each channel in a single read function call is selected by the extra_samplings parameter.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This parameters is: number of samples, minus one. <\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>By default, extra_samplings = 0, 1 sample is selected<\/li>\n\n\n\n<li>To select 8 samples: extra_samplings = 7<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">When several samples are selected, the function sleeps options.interval_us between each sample. This value cannot be optimally fast or precise because it is based on kernel system timer, around 10-50 kHz (20us-100us precision). For audio quality, it is necessary to bypass the kernel and configure a hardware timer.<\/p>\n\n\n\n<div class=\"wp-block-group has-global-padding is-layout-constrained wp-container-core-group-is-layout-8f6441c8 wp-block-group-is-layout-constrained\">\n<div class=\"tenor-gif-embed\" data-postid=\"23378358\" data-share-method=\"host\" data-aspect-ratio=\"1\" data-width=\"100%\"><a href=\"https:\/\/tenor.com\/view\/cat-cats-mic-cat-mic-microphone-gif-23378358\">Cat Cats GIF<\/a>from <a href=\"https:\/\/tenor.com\/search\/cat-gifs\">Cat GIFs<\/a><\/div> <script type=\"text\/javascript\" async src=\"https:\/\/tenor.com\/embed.js\"><\/script>\n\n\n\n<p class=\"has-text-align-right has-small-font-size wp-block-paragraph\" style=\"margin-top:0;margin-bottom:0\"><em>It means no analog microphones capability yet&#8230;<\/em><\/p>\n<\/div>\n\n\n\n<details class=\"wp-block-details is-layout-flow wp-block-details-is-layout-flow\"><summary>\u24d8 How to get the current systick<\/summary>\n<p class=\"wp-block-paragraph\">Menuconfig will give the systick and system clock information.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>west build -t menuconfig<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li>SYS_CLOCK_TICKS_PER_SEC(=10000) System tick frequency (in ticks\/second)<\/li>\n\n\n\n<li>SYS_CLOCK_HW_CYCLES_PER_SEC (=216000000) System clock&rsquo;s h\/w timer frequency<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n<\/details>\n\n\n\n<p class=\"wp-block-paragraph\">\u24d8 Oversampling is not available when several sample reads are enabled.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Buffer!<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">All this data needs a nice, big buffer to store it. This buffer is linked in the adc_sequence object, as a (void*).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It has to be a big enough buffer. To compute its size, multiply:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Size of read data read (usually uint16_t, because most ADC are 12 or 16 bits)<\/li>\n\n\n\n<li>Number of samples per channel (1 + extra_samplings)<\/li>\n\n\n\n<li>Number of channels<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">For instance, a 12-bits ADC with 3 active channels and 6 simultaneous read will have to be 36 bytes wide, or 18 uint16_t.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized wp-duotone-default-filter\"><img decoding=\"async\" src=\"https:\/\/kroki.io\/nomnoml\/svg\/eNpNzsEOwiAQBNA7X7FH7Qk0evNLmqZZcKukW6gsNMbw8dKDidfJ5M30eHej0KtQcFSVe2IIxAIH6_OCMh_hBtoabbQ2VSWSyCX7GFpsTlBBxY2S4LKyDw8IMQNu6BktN82WaaLUgfgPwflaVf-_NsZ1l6QqHzKlDXkssrta6_YEmS26uWtoaBa9c8Lxt7T3LoMavvCWQok=\" alt=\"[adc_sequence|\nchannels (bitmask)|\nresolution| \noversampling|\nbuffer*|\n[adc_sequence_options|\ninterval_us|\ncallback*|\nextra_samplings]\n]\" style=\"width:376px;height:auto\"\/><\/figure>\n\n\n\n<p class=\"has-text-align-left has-small-font-size wp-block-paragraph\" style=\"margin-top:0;margin-bottom:0\"><em>ADC sequence configuration<\/em><\/p>\n\n\n\n<figure class=\"wp-block-image size-large wp-duotone-default-filter\"><img decoding=\"async\" src=\"https:\/\/kroki.io\/packetdiag\/svg\/eNpN0E0KgzAQhuG9p8gFBjIz-dFKVz1CD1BExUiLdiF0UXr3JoPtuH0I38vk2fX3cRvmbjLvyph-fbzmYUvmbDC0GZZ1GG9pnKe0ZYvUVhkt4MlcUrdYY662PCNgFSziwKtQkQBRhYvU0Ki4IpinD9tejAD3df710AF6NSliAIxq0sQasFGTKlkgVJMuEdChIV1yQHvD_-8MQFFNulQDNWrSZQuMatLl_EmsJl12wIdG7n6-sbpbLw==\" alt=\"packetdiag {\n  colwidth = 16;\n  node_height = 72;\n\n  0-1: Chan0  S0;\n  2-3: Chan0  S1;\n  4-5: Chan0  S2;\n  6-7: Chan0  S3;\n  8-9: Chan0  S4;\n  10-11: Chan0  S5;\n  12-13: Chan3  S0;\n  14-15: Chan3  S1;\n  16-17: Chan3  S2;\n  18-19: Chan3  S3;\n  20-21: Chan3  S4;\n  22-23: Chan3  S5;\n  24-25: Chan5  S0;\n  26-27: Chan5  S1;\n  28-29: Chan5  S2;\n  30-31: Chan5  S3;\n  32-33: Chan5  S4;\n  34-35: Chan5  S5;\n}\"\/><\/figure>\n\n\n\n<p class=\"has-text-align-left has-small-font-size wp-block-paragraph\" style=\"margin-top:0px;margin-bottom:0px\"><em>Buffer with 6 uint16_t samples and 3 channels (0, 3 and 5) (unit = byte)<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">When the read function returns, the data will be available in the buffer.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Custom callback<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">adc_sequence.options has a callback field, empty by default. It allows to call an optional function every time a value is read.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Timing considerations<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">DMA use is selected by project options, and then, works by magic. ADC with DMA is not available on every hardware.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In my case, I have an STM32 target, DMA use is enabled in prj.conf.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CONFIG_DMA=y<br>CONFIG_ADC_STM32_DMA=y<\/code><\/pre>\n\n\n\n<p class=\"has-text-align-right has-small-font-size wp-block-paragraph\" style=\"margin-top:0;margin-bottom:0\"><em>prj.conf<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In order to know what it does exactly, refer to the driver implementation in drivers\/adc\/adc_stm32.c.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">DMA activation can be checked with devmem shell, or with debugger by stepping into the driver implementation.<\/p>\n\n\n\n<details class=\"wp-block-details is-layout-flow wp-block-details-is-layout-flow\"><summary>Side note: debugging with devmem<\/summary>\n<p class=\"wp-block-paragraph\">Devmem proves very useful if you plan to customize or debug the ADC driver implementation itself: it allows to verify easily that the Zephyr driver has correctly set up the SoC register options.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The reference manual for our STM32 MCU tells us that our ADC1 uses DMA2, Channel 0, Stream 0.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full wp-duotone-default-filter\"><img decoding=\"async\" width=\"863\" height=\"374\" fetchpriority=\"low\" src=\"https:\/\/redero.fr\/wp-content\/uploads\/2024\/07\/dma2_map.png\" alt=\"\" class=\"wp-image-939\" srcset=\"https:\/\/redero.fr\/wp-content\/uploads\/2024\/07\/dma2_map.png 863w, https:\/\/redero.fr\/wp-content\/uploads\/2024\/07\/dma2_map-300x130.png 300w, https:\/\/redero.fr\/wp-content\/uploads\/2024\/07\/dma2_map-768x333.png 768w\" sizes=\"(max-width: 863px) 100vw, 863px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">In the register boundary addresses table, I find my DMA2 registers start address: 0x40026400<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In the DMA registers detail section, I find out that my stream is configured by the DMA_SxCR register, and channel selection is bits 27:25.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large wp-duotone-default-filter\"><img decoding=\"async\" width=\"1024\" height=\"353\" fetchpriority=\"low\" src=\"https:\/\/redero.fr\/wp-content\/uploads\/2024\/07\/dma_config_stream-1024x353.png\" alt=\"\" class=\"wp-image-944\" srcset=\"https:\/\/redero.fr\/wp-content\/uploads\/2024\/07\/dma_config_stream-1024x353.png 1024w, https:\/\/redero.fr\/wp-content\/uploads\/2024\/07\/dma_config_stream-300x104.png 300w, https:\/\/redero.fr\/wp-content\/uploads\/2024\/07\/dma_config_stream-768x265.png 768w, https:\/\/redero.fr\/wp-content\/uploads\/2024\/07\/dma_config_stream.png 1359w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">DMA_SxCR: Address offset: 0x10 + 0x18 * x, (x = 0 to 7)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">DMA2_S0CR address is:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">0x40026400 + 0x10 + 0x18*0 = 0x40026410<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ devmem 0x40026410\nRead value 0x2c10<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This value is not all zeros: I have some parameters here, such as the TCIE bit 4 set to 1, enabling the Transfer Complete interrupt. Bits 27:25 are set to 0b000: channel 0 is selected. DAC DMA looks correctly set up.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Keep in mind that this example depends on SoC and SoC architecture, you will have to dig into your favorite manufacturer\u2019s documentation.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n<\/details>\n\n\n\n<p class=\"wp-block-paragraph\">In this case, adc_read triggers one sequence. Processor is sleeping between reads, thanks to DMA automation. After reading a sequence, it returns.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large wp-duotone-default-filter\"><img decoding=\"async\" src=\"https:\/\/kroki.io\/seqdiag\/svg\/eNrtkD0OwjAMhfee4qkTHZILlPQiCFWmsWgkJ4Kk7YK4O7UQAtZODAwe3p-Hr_DVBzrjVgGRQgJMBx8JB6ETCxxq8kOfmXx9bNeSZtpZ3Y-O5igUL8LYkQiGkVJp3pO9MTpRaa1FSBPnhaSfi-rf_YsXFzW-uWSe5pyeE-ec3h_iVohoq_sDLrK8Sw==\" alt=\"seqdiag {\n  main  -&gt; dma [label = &quot;adc_read&quot;];\n  dma  -&gt; adc [label = &quot;read sample (all chans)&quot;];\n  dma <-- adc;\n  ... interval_us ...\n  dma  -&gt; adc [label = &quot;read sample (all chans)&quot;];\n  dma <-- adc;\n  ... interval_us ...\n  dma  -&gt; adc [label = &quot;read sample (all chans)&quot;];\n  dma <-- adc; \n  main <-- dma [label = &quot;return&quot;];\n  === ===\n  main  -&gt; dma [label = &quot;adc_read&quot;];\n  dma  -&gt; adc [label = &quot;read sample (all chans)&quot;];\n  dma <-- adc;\n  ... interval_us ...\n  dma  -&gt; adc [label = &quot;read sample (all chans)&quot;];\n  dma <-- adc;\n  ... interval_us ...\n  dma  -&gt; adc [label = &quot;read sample (all chans)&quot;];\n  dma <-- adc; \n  main <-- dma [label = &quot;return&quot;] ;\n}\"\/><\/figure>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">With custom callbacks on top:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large wp-duotone-default-filter\"><img decoding=\"async\" src=\"https:\/\/kroki.io\/seqdiag\/svg\/eNrNkEsOwjAMRPecYtQVLJIL8LkIQpWbWBCRpG3SsEHcnRhUKDdgOZ73LMuZR-vojPsKCOQioA6wgXD01LHHHg1Z0yYm25y2FZJOmDpdMNIjUxg8Y03ew1wo5s1bEVRVxdSiI3NdeJnHwtGw7ofJ9THrmfmaO6U-5nyAzGonUWsNFydON_JtyZL_8MrXayX_vjbxVFKULY8nQBJ3hQ==\" alt=\"\nseqdiag {\n  main  -&gt; dma [label = &quot;adc_read&quot;];\n  dma  -&gt; adc [label = &quot;read sample (all chans)&quot;];\n  adc --&gt; callback [label = &quot;sequence.options.callback&quot;];\n  adc <-- callback;\n  dma <-- adc;\n  ... interval_us ...\n  dma  -&gt; adc [label = &quot;read sample (all chans)&quot;];\n  adc --&gt; callback [label = &quot;sequence.options.callback&quot;];\n  adc <-- callback;\n  dma <-- adc;\n  main <-- dma [label = &quot;return&quot;];\n}\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">To make it infinite, configure a thread, and use <a href=\"https:\/\/docs.zephyrproject.org\/latest\/hardware\/peripherals\/adc.html#c.adc_read_async\">adc_read_async<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">And now, a code sample<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/github.com\/everedero\/zephyr\/tree\/sample_multi_adc\">https:\/\/github.com\/everedero\/zephyr\/tree\/sample_multi_adc<\/a>, in file samples\/drivers\/adc\/adc_multichannel\/src\/main.c<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In this example, main reads 32 samples from 6 different channels in ADC1.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It features a custom callback called adc_callback printing the buffer each time a new sample is read, so every 32 readings.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It runs the sequence 3 times with a 100 msec sleep between each.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large wp-duotone-default-filter\"><img decoding=\"async\" src=\"https:\/\/kroki.io\/seqdiag\/svg\/eNrFUcsKwkAMvPsVgyc9dKkKXrT-iEhJt0GL2-1jWxHEf3djrdZbL-ItycxkhsRxlWZ0xG0C5JRZINghzQl7QwkbRJhSquOaKZ0eNp4kmHD8dMARHI7y0jBmZAz0iaybdxKhBl6iPZCQPg90jquWrWZVlE1WWKd6zke5DYK3sg8gM49Jq5RCZhuuL2Ti1kk_LuX6K-Pvt-K1dnbFajmXctPfXCjfN6-5aWvbLYmiCIswRO6kHPunp9Uw2nW8K_5me38AWoO57A==\" alt=\"\nseqdiag {\n  main  -&gt; dma [label = &quot;adc_read&quot;];\n  dma  -&gt; adc [label = &quot;read sample (all chans)&quot;];\n  adc --&gt; callback [label = &quot;sequence.options.callback&quot;];\n  adc <-- callback;\n  dma <-- adc;\n  ... interval_us ...\n  dma  -&gt; adc [label = &quot;read sample (6 chans)&quot;];\n  dma <-- adc;\n  ... interval_us ...\n  dma  -&gt; adc [label = &quot;read sample (6 chans)&quot;];\n  dma <-- adc; \n  ... (x 32) ...;\n  main <-- dma [label = &quot;return&quot;];\n  === 100 ms ===\n  main  -&gt; dma [label = &quot;adc_read&quot;];\n  ... (read sample x32) ...;\n  main <-- dma [label = &quot;return&quot;] ;\n  === 100 ms ===\n  main  -&gt; dma [label = &quot;adc_read&quot;];\n  ... (read sample x32) ...;\n  main <-- dma [label = &quot;return&quot;] ;\n}\"\/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Sampling with DMA<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Here is a zoom-in on the trace while retrieving 32 samples from 2 channels. Notice all this time spent in the idle thread while hardware is working hard:<\/p>\n\n\n\n<figure class=\"wp-block-gallery has-nested-images columns-default wp-block-gallery-2 is-layout-flex wp-block-gallery-is-layout-flex\">\n<figure class=\"wp-block-image size-full wp-duotone-unset-1\"><a href=\"https:\/\/redero.fr\/wp-content\/uploads\/2024\/07\/recording_with_dma.png\" target=\"_blank\" rel=\"noopener\"><img loading=\"lazy\" decoding=\"async\" width=\"1022\" height=\"280\" data-id=\"840\" src=\"https:\/\/redero.fr\/wp-content\/uploads\/2024\/07\/recording_with_dma.png\" alt=\"\" class=\"wp-image-840\" srcset=\"https:\/\/redero.fr\/wp-content\/uploads\/2024\/07\/recording_with_dma.png 1022w, https:\/\/redero.fr\/wp-content\/uploads\/2024\/07\/recording_with_dma-300x82.png 300w, https:\/\/redero.fr\/wp-content\/uploads\/2024\/07\/recording_with_dma-768x210.png 768w\" sizes=\"auto, (max-width: 1022px) 100vw, 1022px\" \/><\/a><\/figure>\n<\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The ISR shown here is dma_stm32_irq_handler, in drivers\/dma\/dma_stm32.c.<\/p>\n\n\n\n<p class=\"has-medium-font-size wp-block-paragraph\">\u24d8 To know how this beautiful trace was made, refer to my tracing how-to: <a href=\"https:\/\/redero.fr\/?p=649&amp;lang=en\">https:\/\/redero.fr\/?p=649&amp;lang=en<\/a><\/p>\n\n\n\n<div class=\"wp-block-group has-global-padding is-layout-constrained wp-container-core-group-is-layout-8f6441c8 wp-block-group-is-layout-constrained\">\n<div class=\"tenor-gif-embed\" data-postid=\"23378152\" data-share-method=\"host\" data-aspect-ratio=\"1\" data-width=\"100%\"><a href=\"https:\/\/tenor.com\/view\/cat-cats-lazy-lazy-cat-cat-lazy-gif-23378152\">Cat Cats GIF<\/a>from <a href=\"https:\/\/tenor.com\/search\/cat-gifs\">Cat GIFs<\/a><\/div> <script type=\"text\/javascript\" async src=\"https:\/\/tenor.com\/embed.js\"><\/script>\n\n\n\n<p class=\"has-text-align-right has-small-font-size wp-block-paragraph\" style=\"margin-top:0;margin-bottom:0\"><em>Spending time in idle thread<\/em><\/p>\n<\/div>\n\n\n\n<pre class=\"wp-block-verse has-text-align-center has-vivid-green-cyan-color has-secondary-background-color has-text-color has-background has-link-color has-small-font-size wp-elements-37aa7aa7e113bae62a2eed0912ef5bac\">Zephyr version used here: V3.7.0<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Let\u2019s dive into the ADC API description with diagrams, pictures and codes samples!<\/p>\n","protected":false},"author":1,"featured_media":800,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[49],"tags":[73,60,47],"class_list":["post-790","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog_en","tag-adc","tag-stm32-en","tag-zephyr-en"],"_links":{"self":[{"href":"https:\/\/redero.fr\/index.php?rest_route=\/wp\/v2\/posts\/790","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/redero.fr\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/redero.fr\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/redero.fr\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/redero.fr\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=790"}],"version-history":[{"count":92,"href":"https:\/\/redero.fr\/index.php?rest_route=\/wp\/v2\/posts\/790\/revisions"}],"predecessor-version":[{"id":1300,"href":"https:\/\/redero.fr\/index.php?rest_route=\/wp\/v2\/posts\/790\/revisions\/1300"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/redero.fr\/index.php?rest_route=\/wp\/v2\/media\/800"}],"wp:attachment":[{"href":"https:\/\/redero.fr\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=790"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/redero.fr\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=790"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/redero.fr\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=790"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}