Driver: ADC¶
1 功能概述¶
该sample演示了外设模块ADC单次转换及多次转换功能。
2 环境要求¶
PAN1080 EVB一块
Micro USB线一条(用于供电和查看串口打印Log)
硬件接线:
使用USB线,将PC USB与EVB MicroUSB(USB->UART)相连
使用杜邦线将EVB上的:
UART1 TX与P06相连
UART1 RX与P07相连
PC软件: 串口调试助手(UartAssist)或终端工具(SecureCRT),波特率921600
3 编译和烧录¶
项目位置:zephyr\samples_panchip\drivers\adc
目前可使用ZAL工具或quick build脚本进行编译和下载。
脚本位置:quick_build_samples\drivers\adc.bat
打开脚本后默认会编译项目,编译完成时,可输入字符进行后续下载等操作:
Input the keyword to continue:
'b' build 编译项目
'r' make clean and rebuild 重新编译项目
'f' flash download 下载
'e' erase chip 擦除芯片
'o' open project by VS Code 打开 `VS Code`,可查看源码,执行编译下载等
others exit 退出
wait input:
4 演示说明¶
4.1环境说明¶
采集通道:ADC_CHANNEL1 (P30),像采集通道输入电压
ADC的换算方法(理想状态)
低压档(0~1.2V):N=V / 2 * 4096
高压档(0~VDD):N=V / VDD * 4096
其中N为ADC输出Code,V为ADC采样电压。
每5s执行一次例程
4.2 ADC单次转换功能¶
配置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; }
读取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*/
软件通过公式计算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)); /*打印采样电压*/
观测输入电压是否大致与采样电压一致
note: 采样电压是放大了100倍后的值,单位是v,例如252,那么就是2.52v
4.3 ADC多次转换功能¶
配置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; }
设置回调函数
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;
}
}
}
读取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]); }
软件通过公式计算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)); /*打印采样电压*/
观测输入电压是否大致与采样电压一致
note: 采样电压是放大了100倍后的值,单位是v,例如252,那么就是2.52v
5 开发说明¶
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;
}