{"id":966,"date":"2024-07-22T13:12:39","date_gmt":"2024-07-22T11:12:39","guid":{"rendered":"https:\/\/redero.fr\/?p=966"},"modified":"2025-09-19T21:53:44","modified_gmt":"2025-09-19T19:53:44","slug":"lapi-des-adc-explique-aux-chatons","status":"publish","type":"post","link":"https:\/\/redero.fr\/?p=966","title":{"rendered":"L\u2019API des ADC, expliqu\u00e9 aux chatons"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Les convertisseurs analogiques num\u00e9riques, ou CAN en fran\u00e7ais, ou ADC comme \u00ab\u00a0Analog to Digital Converters\u00a0\u00bb en anglais &#8211; terme que j\u2019utiliserai pour ne pas confondre CAN et bus CAN &#8211; sont des blocs hardware polyvalents et forts utiles, par exemple pour r\u00e9cup\u00e9rer de la donn\u00e9e de capteurs analogiques ou surveiller des tensions d\u2019alimentation de la carte.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Toutes les fonctions pour invoquer les ADC via Zephyr sont document\u00e9es dans <a href=\"https:\/\/docs.zephyrproject.org\/latest\/hardware\/peripherals\/adc.html\" target=\"_blank\" rel=\"noreferrer noopener\">la page de doc de l\u2019API<\/a>. Regardons cette API en d\u00e9tail pour pouvoir \u00e9crire un exemple simplifi\u00e9.<\/p>\n\n\n\n\n\n<h2 class=\"wp-block-heading\">Pr\u00e9sentation<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Pour lire une entr\u00e9e analogique, il faut:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Initialiser les entr\u00e9es ADC, avec les configurations correctes de pinmux et autres options,<\/li>\n\n\n\n<li>Lancer une fonction de lecture, telle que adc_read, adc_read_dt ou adc_read_async.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Ces fonctions n\u00e9cessitent deux param\u00e8tres d\u2019entr\u00e9es:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Un device ADC hardware, donn\u00e9 sous forme d\u2019objet device vers sa description dans le device tree,<\/li>\n\n\n\n<li>Un objet de type <a href=\"https:\/\/docs.zephyrproject.org\/latest\/hardware\/peripherals\/adc.html#c.adc_sequence\" target=\"_blank\" rel=\"noreferrer noopener\">adc_sequence<\/a>, qui d\u00e9crit \u00e0 la fonction ce qu\u2019il faut r\u00e9cup\u00e9rer depuis le bloc ADC et comment. <\/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\">Pour cr\u00e9er cet objet adc_sequence, 2 choix :<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>L\u2019instancier automatiquement depuis les param\u00e8tres dans le device tree avec la fonction <a href=\"https:\/\/docs.zephyrproject.org\/latest\/hardware\/peripherals\/adc.html#c.adc_sequence_options\">adc_sequence_init_dt<\/a>. Celle-ci prends un objet adc_dt_spec en entr\u00e9e, et remplis les champs \u00ab\u00a0channels\u00a0\u00bb, \u00ab\u00a0resolution\u00a0\u00bb et \u00ab\u00a0oversampling\u00a0\u00bb de l\u2019objet adc_sequence en recopiant ce qui est donn\u00e9 dans le device tree sous la forme zephyr,parameter:<\/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>Description d\u2019un channel dans le device tree<\/em><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Cr\u00e9er un objet adc_sequence manuellement en s\u00e9lectionnant ses propres param\u00e8tres.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Recette de cuisine d\u2019un objet adc_sequence<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Lecture de plusieurs canaux d\u2019un m\u00eame bloc ADC<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Pour configurer la lecture simultan\u00e9e de plusieurs canaux d\u2019un ADC, il faut forc\u00e9ment initialiser l\u2019objet manuellement, car adc_sequence_init_dt configure la lecture d\u2019un seul canal simultan\u00e9.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Les m\u00eames param\u00e8tres de r\u00e9solution et d\u2019oversampling seront appliqu\u00e9s \u00e0 tous les canaux s\u00e9lectionn\u00e9s.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Tous les canaux ADC lus doivent appartenir au m\u00eame bloc ADC hardware.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Les canaux sont s\u00e9lectionn\u00e9s dans le param\u00e8tre \u00ab\u00a0channels\u00a0\u00bb de l\u2019objet adc_sequence. Ce param\u00e8tre est sous forme de bitmask.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Par exemple, pour un ADC avec 3 canaux 0, 3 et 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>Exemple de multicanaux dans le device tree<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Le bitmask est 0b101001 = 0x29.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Nombre d\u2019\u00e9chantillons<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Le nombre d\u2019\u00e9chantillons \u00e0 lire sur chaque canal en un m\u00eame appel de la fonction \u00ab\u00a0read\u00a0\u00bb est d\u00e9termin\u00e9 par le param\u00e8tre \u00ab\u00a0extra_samplings\u00a0\u00bb.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ce param\u00e8tre correspond au nombre d\u2019\u00e9chantillons voulus, moins 1.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Par d\u00e9faut, extra_samplings = 0, 1 seul \u00e9chantillon par canal sera mesur\u00e9<\/li>\n\n\n\n<li>Pour en s\u00e9lectionner 8: extra_samplings = 7<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Lorsque plusieurs \u00e9chantillons sont s\u00e9lectionn\u00e9s, la fonction \u00ab\u00a0read\u00a0\u00bb s\u2019endort pendant un intervalle de temps de options.interval_us entre deux lectures d\u2019\u00e9chantillon. Cette valeur peux ne pas \u00eatre aussi pr\u00e9cise ou rapide que ce que permet le hardware, puisqu\u2019elle est bas\u00e9e sur le timer du kernel, autour de 10-50 kHz (pr\u00e9cision de 20us-100us). Pour de la qualit\u00e9 audio, par exemple, il sera n\u00e9cessaire de contourner la limitation et de configurer un d\u00e9clenchement de lecture par un timer hardware.<\/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>Les microphones analogiques ne sont pas encore au programme<\/em>&#8230;<\/p>\n<\/div>\n\n\n\n<details class=\"wp-block-details is-layout-flow wp-block-details-is-layout-flow\"><summary>\u24d8 Obtenir la fr\u00e9quence du systick<\/summary>\n<p class=\"wp-block-paragraph\">Menuconfig donne les valeurs de fr\u00e9quence de systick et d\u2019horloge syst\u00e8me.<\/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 Le sur-\u00e9chantillonage automatique n\u2019est pas disponible si la lecture simultan\u00e9e de plusieurs \u00e9chantillons est s\u00e9lectionn\u00e9e.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Buffer!<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Toute cette donn\u00e9e doit \u00eatre stock\u00e9e dans un buffer suffisamment spacieux. L\u2019adresse du buffer est donn\u00e9e \u00e0 l\u2019objet adc_sequence object sous forme de (void*).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Pour obtenir la taille du buffer n\u00e9cessaire, multiplier:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>La taille de stockage d\u2019un \u00e9chantillon (g\u00e9n\u00e9ralement uint16_t, car la plupart des ADC ont des r\u00e9solution de 12 ou 16 bits)<\/li>\n\n\n\n<li>Le nombre d\u2019\u00e9chantillons \u00e0 stocker (1 + extra_samplings)<\/li>\n\n\n\n<li>Le nombre de canaux lus simultan\u00e9ment<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Par exemple, pour un ADC 12-bits avec 3 canaux s\u00e9lectionn\u00e9s et 6 \u00e9chantillons \u00e0 lire, le buffer devra \u00eatre de 36 octets, l\u2019\u00e9quivalent de 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 contenant 6 \u00e9chantillons uint16_t et 3 canaux (0, 3 et 5) (unit\u00e9 = octet)<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Lorsque la fonction de lecture s\u2019ach\u00e8ve, la donn\u00e9e est disponible dans le buffer.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Callback customis\u00e9<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">adc_sequence.options contient un champ \u00ab\u00a0callback\u00a0\u00bb, vide par d\u00e9faut. Il permet d\u2019appeler une fonction optionnelle chaque fois qu\u2019un \u00e9chantillon est lu.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Consid\u00e9rations temporelles<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">L\u2019utilisation de DMA est s\u00e9lectionn\u00e9 dans les options du projet, et fonctionne comme par magie. Le DMA ADC n\u2019est pas disponible sur toutes les plate-formes.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dans mon cas, j\u2019ai une cible STM32, et je peux activer la lecture d\u2019ADC avec DMA dans mon 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\">Pour savoir ce que ce changement implique pr\u00e9cis\u00e9ment, il faut jeter un oeil \u00e0 l\u2019impl\u00e9mentation du driver dans drivers\/adc\/adc_stm32.c.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">L\u2019activation du DMA peut \u00eatre v\u00e9rifi\u00e9e avec la commande devmem du shell, ou avec un debuggueur, en mettant un point d\u2019arr\u00eat dans l\u2019impl\u00e9mentation du driver.<\/p>\n\n\n\n<details class=\"wp-block-details is-layout-flow wp-block-details-is-layout-flow\"><summary>Digression sur devmem<\/summary>\n<p class=\"wp-block-paragraph\">Devmem est fort utile pour ceux qui envisagent de d\u00e9bugguer ou de customiser les impl\u00e9mentations de drivers. Il permet de v\u00e9rifier facilement que le driver a correctement initialis\u00e9 les diff\u00e9rentes options de registre.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Le manuel de r\u00e9f\u00e9rence de notre STM32 nous dit que l\u2019ADC1 utilises le 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\">Dans la table des registres, l\u2019adresse de d\u00e9part du DMA2  est : 0x40026400<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dans la page de d\u00e9tail des registres du DMA, il est dit que le stream X est configur\u00e9 par le registre DMA_SxCR register, et que le channel auquel ce stream est connect\u00e9 est donn\u00e9 par les octets 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\">L\u2019adresse de DMA2_S0CR est :<\/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\">Cette valeur n\u2019est pas z\u00e9ro: j\u2019ai des param\u00e8tres s\u00e9lectionn\u00e9s ici, comme le bit 4 TCIE, ce qui active l\u2019interruption lorsque le transfert est termin\u00e9. Let octets 27:25 sont 0b000 : le channel 0 est bel est bien s\u00e9lectionn\u00e9. Tout a l\u2019air en ordre.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Cet exemple est enti\u00e8rement d\u00e9pendant du SoC et de son architecture, il est n\u00e9cessaire de se r\u00e9f\u00e9rer \u00e0 la documentation du fabriquant.<\/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\">Dans ce cas, adc_read d\u00e9clenche une s\u00e9quence. Le processeur dort entre les lectures, puisque le DMA s\u2019occupe de r\u00e9cup\u00e9rer les \u00e9chantillons automatiquement. Apr\u00e8s lecture de la s\u00e9quence, la fonction read redonne la main.<\/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\">Avec un callback customis\u00e9:<\/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\">Pour avoir une lecture continue des ADC, il sera n\u00e9cessaire de configurer un thread et d\u2019utiliser <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\">\u00c9crivons un exemple<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">L\u2019exemple de code est ici: <a href=\"https:\/\/github.com\/everedero\/zephyr\/tree\/sample_multi_adc\">https:\/\/github.com\/everedero\/zephyr\/tree\/sample_multi_adc<\/a>, dans le fichier samples\/drivers\/adc\/adc_multichannel\/src\/main.c<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dans cet exemple, la fonction main lit 32 \u00e9chantillons depuis 6 canaux diff\u00e9rents de l\u2019ADC num\u00e9ro 1.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Il comporte une fonction callback intitul\u00e9e \u00ab\u00a0adc_callback\u00a0\u00bb qui imprime le buffer chaque fois qu\u2019un nouvel \u00e9chantillon est lu, donc toutes les 32\u202flectures.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Il r\u00e9p\u00e8te cette s\u00e9quence de \u00ab\u00a0read\u00a0\u00bb 3 fois, avec un intervalle de 100 msec entre chaque.<\/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\">\u00c9chantillonage avec le DMA<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Voici une visualisation partielle d\u2019une trace d\u2019un exemple o\u00f9 32 \u00e9chantillons sont lus sur 2 canaux. Il est int\u00e9ressant de voir tout le temps pass\u00e9 dans le processus \u00ab\u00a0idle\u00a0\u00bb pendant que le hardware lis les entr\u00e9es ADC:<\/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\">L\u2019interruption \u00ab\u00a0ISR context 1\u00a0\u00bb montr\u00e9e ici correspond \u00e0  \u00ab\u00a0dma_stm32_irq_handler\u00a0\u00bb, dans drivers\/dma\/dma_stm32.c.<\/p>\n\n\n\n<p class=\"has-medium-font-size wp-block-paragraph\">\u24d8 Le tutoriel pour cr\u00e9er des trace comme celle-ci se trouve ici: <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>Je suis dans un processus idle<\/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-8b42f6e275577d6a617810d87f31d878\">Version de Zephyr du pr\u00e9sent document: V3.7.0<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Une description de l\u2019API Zephyr d\u00e9di\u00e9e aux ADC, avec des diagrammes et des images.<\/p>\n","protected":false},"author":1,"featured_media":800,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[37],"tags":[79,54,25],"class_list":["post-966","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog","tag-adc-fr","tag-stm32","tag-zephyr"],"_links":{"self":[{"href":"https:\/\/redero.fr\/index.php?rest_route=\/wp\/v2\/posts\/966","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=966"}],"version-history":[{"count":19,"href":"https:\/\/redero.fr\/index.php?rest_route=\/wp\/v2\/posts\/966\/revisions"}],"predecessor-version":[{"id":1296,"href":"https:\/\/redero.fr\/index.php?rest_route=\/wp\/v2\/posts\/966\/revisions\/1296"}],"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=966"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/redero.fr\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=966"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/redero.fr\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=966"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}