.. include:: ../substitutions.rst .. include:: stdout.rst ADC === The phyCORE-RT1170 features two 12-bit LPADC (Low-Power Analog-to-Digital Converter) instances, LPADC1 and LPADC2. Each ADC capable pin can be muxed to one or more LPADC channels via the SoC's IOMUX. The |product-cb| carrier board routes the LPADC interface out at pin GPIO_AD_12, which is connected to the analog output of U12 (MAX4372FEUK+T), a current-sense amplifier. For detailed hardware-level information, refer to the *10.2 ADC* chapter in the |hw-manual-link|. Reading a Channel via Shell ---------------------------- The example configures LPADC1 channel 3A on pin GPIO_AD_12, entirely from devicetree, and exposes it through a single shell command. On the |product-cb| carrier board, this reads the sensed-current output of U12: .. code-block:: console :substitutions: |target-prompt| adc_read ADC raw value: 2048 = 900 mV Each invocation performs one conversion and prints both the raw sample and the converted millivolt value. The channel is configured on first use, so the very first call takes care of `adc_channel_setup_dt()` before sampling. ADC Channel Configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~ The channel is defined once, at build time, using the ``zephyr,user`` devicetree node's `io-channels` property: .. code-block:: dts / { zephyr,user { io-channels = <&lpadc1 0>; }; }; &lpadc1 { status = "okay"; #address-cells = <1>; #size-cells = <0>; channel@0 { reg = <0>; zephyr,gain = "ADC_GAIN_1"; zephyr,reference = "ADC_REF_EXTERNAL0"; zephyr,vref-mv = <1800>; zephyr,acquisition-time = ; zephyr,resolution = <12>; zephyr,input-positive = ; }; }; The `struct adc_dt_spec` for this channel is retrieved with `ADC_DT_SPEC_GET_BY_IDX()`, which pulls the device, channel ID, gain, reference, resolution and acquisition time straight out of the devicetree node above, so no manual configuration is required in the source code. .. note:: The mcux-lpadc driver only accepts `ADC_REF_EXTERNAL0` or `ADC_REF_EXTERNAL1` as `zephyr,reference`; `ADC_REF_INTERNAL` is rejected outright. Channel Setup on First Use ~~~~~~~~~~~~~~~~~~~~~~~~~~ The channel is configured with ``adc_channel_setup_dt()`` the first time the command runs, and the result is cached in a static flag so that subsequent invocations skip straight to sampling. Reading the Channel ~~~~~~~~~~~~~~~~~~~~ Each call to `adc_read` performs one conversion: - ``adc_sequence_init_dt()`` prepares an `adc_sequence` from the devicetree-defined resolution. - ``adc_read_dt()`` triggers the conversion and stores the raw result. - ``adc_raw_to_millivolts_dt()`` converts the raw value to millivolts using the channel's configured gain and reference voltage. The raw value, and the converted millivolt reading when available, are printed to the shell. The mcux-lpadc driver writes exactly one ``uint16_t`` per sample into the sequence buffer, regardless of the configured resolution. Declaring the sample buffer as a wider type, such as ``uint32_t``, leaves the upper bytes as uninitialized stack contents and produces nonsensical raw and millivolt readings. Source Code ~~~~~~~~~~~ .. code-block:: c #include #include #include #include #include LOG_MODULE_REGISTER(adc_read, CONFIG_APP_LOG_LEVEL); /* * Get ADC channel configuration from the devicetree zephyr,user node's * io-channels property. This is mandatory. */ #if !DT_NODE_HAS_PROP(DT_PATH(zephyr_user), io_channels) #error "Unsupported board: io-channels not defined for the zephyr,user node" #endif static const struct adc_dt_spec adc_chan0 = ADC_DT_SPEC_GET_BY_IDX(DT_PATH(zephyr_user), 0); static bool adc_chan0_configured; static int cmd_adc_read(const struct shell *sh, size_t argc, char **argv) { int ret; uint16_t buf; int32_t val_mv; struct adc_sequence sequence = { .buffer = &buf, /* buffer size in bytes, not number of samples */ .buffer_size = sizeof(buf), }; ARG_UNUSED(argc); ARG_UNUSED(argv); if (!adc_is_ready_dt(&adc_chan0)) { shell_error(sh, "%s is not ready", adc_chan0.dev->name); return -ENODEV; } if (!adc_chan0_configured) { ret = adc_channel_setup_dt(&adc_chan0); if (ret < 0) { shell_error(sh, "Unable to configure %s channel %d", adc_chan0.dev->name, adc_chan0.channel_id); return ret; } adc_chan0_configured = true; } ret = adc_sequence_init_dt(&adc_chan0, &sequence); if (ret < 0) { shell_error(sh, "Unable to initialize ADC sequence: %d", ret); return ret; } ret = adc_read_dt(&adc_chan0, &sequence); if (ret < 0) { shell_error(sh, "Failed to read %s: %d", adc_chan0.dev->name, ret); return ret; } val_mv = (int32_t)buf; ret = adc_raw_to_millivolts_dt(&adc_chan0, &val_mv); if (ret < 0) { shell_print(sh, "ADC raw value: %u (mV conversion not available)", buf); } else { shell_print(sh, "ADC raw value: %u = %d mV", buf, val_mv); } return 0; } SHELL_CMD_ARG_REGISTER(adc_read, NULL, "Read the example ADC channel (GPIO_AD_12, LPADC1 CH3A)", cmd_adc_read, 1, 0);