当前文档版本为 v0.8.0,您可以访问当前页面的 开发中 版本以获取最近可能的更新。

Driver: ADC

1 功能概述

该sample演示了外设模块ADC单次转换及多次转换功能。

DMA功能参考第 6 小节。

2 环境要求

  • PAN1080 EVB 一块

  • USB-TypeC 线一条(用于供电和查看串口打印 Log)

  • 硬件接线:

    • 使用USB线,将 PC USB 与 EVB USB-TypeC(USB->UART)相连

    • 根据 EVB 核心板芯片的封装不同,使用杜邦线将 EVB 底板上的:

      • TX0 与 P00 相连, RX0 与 P01 相连(若 EVB 板芯片为 QFN32 或 LQFP64 封装)

      • TX0 与 P30 相连, RX0 与 P31 相连(若 EVB 板芯片为 QFN48 封装)

  • PC 软件: 串口调试助手(UartAssist)或终端工具(SecureCRT),波特率921600

3 编译和烧录

例程位置:zephyr\samples_panchip\drivers\adc

使用 ZAL 工具可以对其进行编译、烧录、打开 VS Code 调试等操作。关于 ZAL 工具的详细介绍请参考:Zephyr APP Launcher 工具介绍

4 演示说明

4.1环境说明

  • 采集通道:ADC_CHANNEL6 (P20),像采集通道输入电压

  • ADC的换算方法(理想状态)

      低压档(0~1.2V):N=V / 2 * 4096
    
      高压档(0~VDD):N=V / VDD * 4096
    
      其中N为ADC输出Code,V为ADC采样电压。
    
  • 每5s执行一次例程

4.2 ADC单次转换功能

  1. 配置adc序列信息并adc初始化。

    const struct adc_sequence sequence = {
        .channels = BIT(ADC_CHANNEL_ID),				/*通道选择*/
        .buffer = m_sample_buffer,						/*结果缓存buffer*/
        .buffer_size = sizeof(m_sample_buffer),			/*buffer大小,不能小于次数*/
        .resolution = ADC_RESOLUTION,					/*我们固定为12bit adc*/
    };
    
    const struct device *adc_dev = init_adc();	/*初始化adc模块*/
    
    if (!adc_dev) {
        return -ENODEV;
    }
    
  2. 读取adc转换值。

    ret = adc_read(adc_dev, &sequence);	/*读取adc转换的code*/
    if (ret != 0) {
        printk("adc_read() failed with code %d\n", ret);
    }
    printk("adc sampling data: 0x%x\n", m_sample_buffer[0]); /*打印获取code*/
    
  3. 软件通过公式计算adc 采样电压,电压值放大100倍后打印

    v_adc = ADC_REF_VOLTAGE * adc_value / 4096.0f;
    printk("The voltage value after amplified by 100 times is %d\n", (uint32_t)(v_adc*100)); /*打印采样电压*/
    
  4. 观测输入电压是否大致与采样电压一致

    note: 采样电压是放大了100倍后的值,单位是v,例如252,那么就是2.52v

4.3 ADC多次转换功能

  1. 配置adc序列信息并adc初始化。

    const struct adc_sequence_options options = {
        .callback        = repeated_samplings_callback,	/*中断回调函数*/
        .extra_samplings = ADC_BUFFER_SIZE / 2,			/*转换次数*/
        .interval_us     = 0,							/*间隔时间*/
        .user_data       = user_data,					/*用户数据*/
    };
    
    const struct adc_sequence sequence = {
        .options     = &options,						/*增加附加序列信息*/
        .channels = BIT(ADC_CHANNEL_ID),				/*通道选择*/
        .buffer = m_sample_buffer,						/*结果缓存buffer*/
        .buffer_size = sizeof(m_sample_buffer),			/*buffer大小,不能小于次数*/
        .resolution = ADC_RESOLUTION,					/*我们固定为12bit adc*/
    };
    
    const struct device *adc_dev = init_adc();	/*初始化adc模块*/
    
    if (!adc_dev) {
        return -ENODEV;
    }
    
  2. 设置回调函数

static enum adc_action repeated_samplings_callback(const struct device *dev,
						   const struct adc_sequence *sequence,
						   uint16_t sampling_index)
{
	++m_samplings_done;
	if (m_samplings_done == 1U) {
		/* After first sampling continue normally. */
		return ADC_ACTION_CONTINUE;
	} else {
		/*
		 * The second sampling is repeated 9 times (the samples are
		 * written in the same place), then the sequence is finished
		 * prematurely.
		 */
		if (m_samplings_done < ADC_BUFFER_SIZE / 2) {
            /*这里选择continue,code会依次存放在buffer中*/
            /*如果选择repeat,那么只会存放在buffer的固定第二个数据中*/
			return ADC_ACTION_CONTINUE;
		} else {
			convert_finish = true;
			return ADC_ACTION_FINISH;
		}
	}
}
  1. 读取adc转换值并计算平均值。

    ret = adc_read(adc_dev, &sequence);	/*读取adc转换的code*/
    if (ret != 0) {
        printk("adc_read() failed with code %d\n", ret);
    }
    
    for (uint8_t i = 0; i < ADC_BUFFER_SIZE / 2; i++) {
        avg_code += m_sample_buffer[i];
        printk("adc sampling data: 0x%x\n", m_sample_buffer[i]);
    }
    
  2. 软件通过公式计算adc 采样电压,电压值放大100倍后打印

    v_adc = ADC_REF_VOLTAGE * adc_value / 4096.0f;
    printk("The voltage value after amplified by 100 times is %d\n", (uint32_t)(v_adc*100)); /*打印采样电压*/
    
  3. 观测输入电压是否大致与采样电压一致

    note: 采样电压是放大了100倍后的值,单位是v,例如252,那么就是2.52v

5 开发说明

5.1 启用ADC模块

在prj.conf文件中添加“CONFIG_ADC=y”启用ADC模块。
CONFIG_ADC=y

5.2 初始化ADC

int ret;
const struct device *adc_dev = device_get_binding(DT_LABEL(DT_INST(0, panchip_pan_adc))); /*获取adc设备*/

if (!adc_dev) {
    printk("Cannot get ADC device\n");
}

ret = adc_channel_setup(adc_dev, &m_channel_cfg); /*初始化adc模块*/
if (ret != 0) {
    printk("Set up ADC channel failed with code %d\n", ret);
}

5.3 ADC数据结构及参数含义

  • 通道配置

struct adc_channel_cfg {
	/** Gain selection. */
	enum adc_gain gain;

	/** Reference selection. */
	enum adc_reference reference;

	/**
	 * Acquisition time.
	 * Use the ADC_ACQ_TIME macro to compose the value for this field or
	 * pass ADC_ACQ_TIME_DEFAULT to use the default setting for a given
	 * hardware (e.g. when the hardware does not allow to configure the
	 * acquisition time).
	 * Particular drivers do not necessarily support all the possible units.
	 * Value range is 0-16383 for a given unit.
	 */
	uint16_t acquisition_time;

	/**
	 * Channel identifier.
	 * This value primarily identifies the channel within the ADC API - when
	 * a read request is done, the corresponding bit in the "channels" field
	 * of the "adc_sequence" structure must be set to include this channel
	 * in the sampling.
	 * For hardware that does not allow selection of analog inputs for given
	 * channels, but rather have dedicated ones, this value also selects the
	 * physical ADC input to be used in the sampling. Otherwise, when it is
	 * needed to explicitly select an analog input for the channel, or two
	 * inputs when the channel is a differential one, the selection is done
	 * in "input_positive" and "input_negative" fields.
	 * Particular drivers indicate which one of the above two cases they
	 * support by selecting or not a special hidden Kconfig option named
	 * ADC_CONFIGURABLE_INPUTS. If this option is not selected, the macro
	 * CONFIG_ADC_CONFIGURABLE_INPUTS is not defined and consequently the
	 * mentioned two fields are not present in this structure.
	 * While this API allows identifiers from range 0-31, particular drivers
	 * may support only a limited number of channel identifiers (dependent
	 * on the underlying hardware capabilities or configured via a dedicated
	 * Kconfig option).
	 */
	uint8_t channel_id   : 5;
	/** Channel type: single-ended or differential. */
	uint8_t differential : 1;

#ifdef CONFIG_ADC_CONFIGURABLE_INPUTS
	/**
	 * Positive ADC input.
	 * This is a driver dependent value that identifies an ADC input to be
	 * associated with the channel.
	 */
	uint8_t input_positive;

	/**
	 * Negative ADC input (used only for differential channels).
	 * This is a driver dependent value that identifies an ADC input to be
	 * associated with the channel.
	 */
	uint8_t input_negative;
#endif /* CONFIG_ADC_CONFIGURABLE_INPUTS */
};
  • adc采样序列参数

struct adc_sequence {
	/**
	 * Pointer to a structure defining additional options for the sequence.
	 * If NULL, the sequence consists of a single sampling.
	 */
	const struct adc_sequence_options *options;

	/**
	 * Bit-mask indicating the channels to be included in each sampling
	 * of this sequence.
	 * All selected channels must be configured with adc_channel_setup()
	 * before they are used in a sequence.
	 */
	uint32_t channels;

	/**
	 * Pointer to a buffer where the samples are to be written. Samples
	 * from subsequent samplings are written sequentially in the buffer.
	 * The number of samples written for each sampling is determined by
	 * the number of channels selected in the "channels" field.
	 * The buffer must be of an appropriate size, taking into account
	 * the number of selected channels and the ADC resolution used,
	 * as well as the number of samplings contained in the sequence.
	 */
	void *buffer;

	/**
	 * Specifies the actual size of the buffer pointed by the "buffer"
	 * field (in bytes). The driver must ensure that samples are not
	 * written beyond the limit and it must return an error if the buffer
	 * turns out to be not large enough to hold all the requested samples.
	 */
	size_t buffer_size;

	/**
	 * ADC resolution.
	 * For single-ended channels the sample values are from range:
	 *   0 .. 2^resolution - 1,
	 * for differential ones:
	 *   - 2^(resolution-1) .. 2^(resolution-1) - 1.
	 */
	uint8_t resolution;

	/**
	 * Oversampling setting.
	 * Each sample is averaged from 2^oversampling conversion results.
	 * This feature may be unsupported by a given ADC hardware, or in
	 * a specific mode (e.g. when sampling multiple channels).
	 */
	uint8_t oversampling;

	/**
	 * Perform calibration before the reading is taken if requested.
	 *
	 * The impact of channel configuration on the calibration
	 * process is specific to the underlying hardware.  ADC
	 * implementations that do not support calibration should
	 * ignore this flag.
	 */
	bool calibrate;
};
  • adc采样附加选项参数

struct adc_sequence_options {
	/**
	 * Interval between consecutive samplings (in microseconds), 0 means
	 * sample as fast as possible, without involving any timer.
	 * The accuracy of this interval is dependent on the implementation of
	 * a given driver. The default routine that handles the intervals uses
	 * a kernel timer for this purpose, thus, it has the accuracy of the
	 * kernel's system clock. Particular drivers may use some dedicated
	 * hardware timers and achieve a better precision.
	 */
	uint32_t interval_us;

	/**
	 * Callback function to be called after each sampling is done.
	 * Optional - set to NULL if it is not needed.
	 */
	adc_sequence_callback callback;

	/**
	 * Pointer to user data. It can be used to associate the sequence
	 * with any other data that is needed in the callback function.
	 */
	void *user_data;

	/**
	 * Number of extra samplings to perform (the total number of samplings
	 * is 1 + extra_samplings).
	 */
	uint16_t extra_samplings;
};

5.4 ADC API接口

  • 接口总览

__subsystem struct adc_driver_api {
	adc_api_channel_setup channel_setup;
	adc_api_read          read;
#ifdef CONFIG_ADC_ASYNC
	adc_api_read_async    read_async;
#endif
	uint16_t ref_internal;	/* mV */
};
  • adc初始化模块配置

/**
 * @brief Configure an ADC channel.
 *
 * It is required to call this function and configure each channel before it is
 * selected for a read request.
 *
 * @param dev          Pointer to the device structure for the driver instance.
 * @param channel_cfg  Channel configuration.
 *
 * @retval 0       On success.
 * @retval -EINVAL If a parameter with an invalid value has been provided.
 */
__syscall int adc_channel_setup(const struct device *dev,
				const struct adc_channel_cfg *channel_cfg);
  • adc读操作

/**
 * @brief Set a read request.
 *
 * @param dev       Pointer to the device structure for the driver instance.
 * @param sequence  Structure specifying requested sequence of samplings.
 *
 * If invoked from user mode, any sequence struct options for callback must
 * be NULL.
 *
 * @retval 0        On success.
 * @retval -EINVAL  If a parameter with an invalid value has been provided.
 * @retval -ENOMEM  If the provided buffer is to small to hold the results
 *                  of all requested samplings.
 * @retval -ENOTSUP If the requested mode of operation is not supported.
 * @retval -EBUSY   If another sampling was triggered while the previous one
 *                  was still in progress. This may occur only when samplings
 *                  are done with intervals, and it indicates that the selected
 *                  interval was too small. All requested samples are written
 *                  in the buffer, but at least some of them were taken with
 *                  an extra delay compared to what was scheduled.
 */
__syscall int adc_read(const struct device *dev,
		       const struct adc_sequence *sequence);
  • adc异步读(一般用于多个ADC操作)

/**
 * @brief Set an asynchronous read request.
 *
 * @note This function is available only if @kconfig{CONFIG_ADC_ASYNC}
 * is selected.
 *
 * If invoked from user mode, any sequence struct options for callback must
 * be NULL.
 *
 * @param dev       Pointer to the device structure for the driver instance.
 * @param sequence  Structure specifying requested sequence of samplings.
 * @param async     Pointer to a valid and ready to be signaled struct
 *                  k_poll_signal. (Note: if NULL this function will not notify
 *                  the end of the transaction, and whether it went successfully
 *                  or not).
 *
 * @returns 0 on success, negative error code otherwise.
 *          See adc_read() for a list of possible error codes.
 *
 */
__syscall int adc_read_async(const struct device *dev,
			     const struct adc_sequence *sequence,
			     struct k_poll_signal *async);
  • adc获取参考电压(这里我们固定是1.2V)

/**
 * @brief Get the internal reference voltage.
 *
 * Returns the voltage corresponding to @ref ADC_REF_INTERNAL,
 * measured in millivolts.
 *
 * @return a positive value is the reference voltage value.  Returns
 * zero if reference voltage information is not available.
 */
static inline uint16_t adc_ref_internal(const struct device *dev)
{
	const struct adc_driver_api *api =
				(const struct adc_driver_api *)dev->api;

	return api->ref_internal;
}

6 ADC DMA配置说明

  • CONFIG_ADC_PANCHIP_DMA=y

该配置使能ADC DMA feature。

  • CONFIG_ADC_PANCHIP_DMA_TIMEOUT=1000

该配置为ADC DMA读取一次时的超时时间,单位为ms。

  • ADC配置注意点 需要确认extra_samplings+1等于实际采样的次数,否则采样次数不对会导致不会触发DMA中断,从而导致ADC DMA read超时。

7 RAM/Flash资源使用情况

Memory region         Used Size  Region Size  %age Used
FLASH:       25488 B       256 KB      9.72%
SRAM:        5728 B        64 KB      8.74%