ADC 同步规则双ADC多通道采集
#include "bsp_adc.h"
#include <stdio.h>
// 定义一个数组用于存储 ADC 转换后的值,这里准备存储两个 ADC 转换结果
uint32_t adc_value[2];
// 定时器配置函数,用于配置定时器产生触发 ADC 转换的信号
void timer_config(void)
{
// 定义定时器输出比较参数结构体
timer_oc_parameter_struct timer_ocintpara;
// 定义定时器参数结构体
timer_parameter_struct timer_initpara;
// TIMER1 配置
// 初始化定时器参数结构体为默认值
timer_struct_para_init(&timer_initpara);
// 设置定时器预分频器,这里将定时器时钟频率分频为原来的 5400 分之一
timer_initpara.prescaler = 5399;
// 设置定时器对齐模式为边沿对齐模式
timer_initpara.alignedmode = TIMER_COUNTER_EDGE;
// 设置定时器计数方向为向上计数
timer_initpara.counterdirection = TIMER_COUNTER_UP;
// 设置定时器周期,计数到 9999 后产生更新事件
timer_initpara.period = 9999;
// 设置定时器时钟分频因子为 1
timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
// 设置重复计数器值为 0
timer_initpara.repetitioncounter = 0;
// 根据上述配置初始化 TIMER1
timer_init(TIMER1, &timer_initpara);
// CH0 配置为 PWM 模式 1
// 初始化定时器输出比较参数结构体为默认值
timer_channel_output_struct_para_init(&timer_ocintpara);
// 设置输出比较极性为高电平有效
timer_ocintpara.ocpolarity = TIMER_OC_POLARITY_HIGH;
// 使能定时器通道输出
timer_ocintpara.outputstate = TIMER_CCX_ENABLE;
// 根据上述配置初始化 TIMER1 的通道 1
timer_channel_output_config(TIMER1, TIMER_CH_1, &timer_ocintpara);
// 设置 TIMER1 通道 1 的脉冲值,用于控制 PWM 占空比
timer_channel_output_pulse_value_config(TIMER1, TIMER_CH_1, 3999);
// 设置 TIMER1 通道 1 的输出模式为 PWM 模式 1
timer_channel_output_mode_config(TIMER1, TIMER_CH_1, TIMER_OC_MODE_PWM1);
// 禁用 TIMER1 通道 1 的输出比较影子寄存器
timer_channel_output_shadow_config(TIMER1, TIMER_CH_1, TIMER_OC_SHADOW_DISABLE);
}
// ADC 初始化函数,调用多个子函数完成 ADC 的初始化工作
void adc_init()
{
// 配置 ADC 系统时钟
adc_rcu_config();
// 配置 ADC 相关的 GPIO 引脚
adc_gpio_config();
// 配置定时器
timer_config();
// 配置 ADC 的 DMA 功能
adc_dma_config();
// 配置 ADC 本身的参数
adc_config();
}
// 配置 ADC 系统时钟的函数
void adc_rcu_config(void)
{
// 使能 GPIOA 时钟,因为 ADC 引脚连接在 GPIOA 上
rcu_periph_clock_enable(RCU_GPIOA);
// 使能 ADC0 时钟
rcu_periph_clock_enable(RCU_ADC0);
// 使能 ADC1 时钟
rcu_periph_clock_enable(RCU_ADC1);
// 使能 DMA0 时钟,用于 ADC 数据的 DMA 传输
rcu_periph_clock_enable(RCU_DMA0);
// 使能定时器 1 时钟,用于触发 ADC 转换
rcu_periph_clock_enable(RCU_TIMER1);
// 配置 ADC 时钟,将 APB2 时钟分频为原来的 8 分之一作为 ADC 时钟
rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV8);
}
// 配置 ADC 相关 GPIO 引脚的函数
void adc_gpio_config(void)
{
// 将 GPIOA 的引脚 4 和 5 配置为模拟输入模式,速度为 50MHz
gpio_init(GPIOA, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ, GPIO_PIN_4 | GPIO_PIN_5);
}
/*!
\brief configure the DMA peripheral
\param[in] none
\param[out] none
\retval none
*/
#if 1
// 配置 ADC 的 DMA 功能的函数
void adc_dma_config(void)
{
// 定义 DMA 参数结构体
dma_parameter_struct dma_data_parameter;
// 对 DMA0 的通道 0 进行去初始化操作
dma_deinit(DMA0, DMA_CH0);
// 初始化 DMA 为单次数据传输模式
// 设置 DMA 外设地址为 ADC0 的数据寄存器地址
dma_data_parameter.periph_addr = (uint32_t)(&ADC_RDATA(ADC0));
// 禁用外设地址自增
dma_data_parameter.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
// 设置 DMA 内存地址为存储 ADC 转换结果的数组地址
dma_data_parameter.memory_addr = (uint32_t)(adc_value);
// 使能内存地址自增
dma_data_parameter.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
// 设置外设数据宽度为 32 位
dma_data_parameter.periph_width = DMA_PERIPHERAL_WIDTH_32BIT;
// 设置内存数据宽度为 32 位
dma_data_parameter.memory_width = DMA_MEMORY_WIDTH_32BIT;
// 设置 DMA 传输方向为从外设到内存
dma_data_parameter.direction = DMA_PERIPHERAL_TO_MEMORY;
// 设置 DMA 传输的数据数量为 1
dma_data_parameter.number = 1;
// 设置 DMA 通道优先级为高
dma_data_parameter.priority = DMA_PRIORITY_HIGH;
// 根据上述配置初始化 DMA0 的通道 0
dma_init(DMA0, DMA_CH0, &dma_data_parameter);
// 使能 DMA0 通道 0 的循环模式
dma_circulation_enable(DMA0, DMA_CH0);
// 使能 DMA0 的通道 0
dma_channel_enable(DMA0, DMA_CH0);
}
/*!
\brief configure the ADC peripheral
\param[in] none
\param[out] none
\retval none
*/
// 配置 ADC 本身参数的函数
void adc_config(void)
{
// 对 ADC0 和 ADC1 进行复位操作
adc_deinit(ADC0);
adc_deinit(ADC1);
// 使能 ADC0 和 ADC1 的扫描模式
adc_special_function_config(ADC0, ADC_SCAN_MODE, ENABLE);
adc_special_function_config(ADC1, ADC_SCAN_MODE, ENABLE);
// 配置 ADC0 的规则通道触发源为 TIMER1 的通道 1
adc_external_trigger_source_config(ADC0, ADC_REGULAR_CHANNEL, ADC0_1_EXTTRIG_REGULAR_T1_CH1);
// 配置 ADC1 的规则通道不使用外部触发
adc_external_trigger_source_config(ADC1, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE);
// 配置 ADC0 和 ADC1 的数据对齐方式为右对齐
adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT);
adc_data_alignment_config(ADC1, ADC_DATAALIGN_RIGHT);
// 配置 ADC 为双规则并行模式
adc_mode_config(ADC_DAUL_REGULAL_PARALLEL);
// 配置 ADC0 和 ADC1 的规则通道长度为 1
adc_channel_length_config(ADC0, ADC_REGULAR_CHANNEL, 1);
adc_channel_length_config(ADC1, ADC_REGULAR_CHANNEL, 1);
// 配置 ADC0 的规则通道 0 为通道 4,采样时间为 55.5 个 ADC 时钟周期
adc_regular_channel_config(ADC0, 0, ADC_CHANNEL_4, ADC_SAMPLETIME_55POINT5);
// 配置 ADC1 的规则通道 0 为通道 5,采样时间为 55.5 个 ADC 时钟周期
adc_regular_channel_config(ADC1, 0, ADC_CHANNEL_5, ADC_SAMPLETIME_55POINT5);
// 使能 ADC0 和 ADC1 的规则通道外部触发
adc_external_trigger_config(ADC0, ADC_REGULAR_CHANNEL, ENABLE);
adc_external_trigger_config(ADC1, ADC_REGULAR_CHANNEL, ENABLE);
// 使能 ADC0
adc_enable(ADC0);
// 延时 1ms 等待 ADC 稳定
delay_1ms(1);
// 对 ADC0 进行校准
adc_calibration_enable(ADC0);
// 使能 ADC1
adc_enable(ADC1);
// 延时 1ms 等待 ADC 稳定
delay_1ms(1);
// 对 ADC1 进行校准
adc_calibration_enable(ADC1);
// 使能 ADC0 的 DMA 功能
adc_dma_mode_enable(ADC0);
}
#else
// 另一种 DMA 配置方式
void adc_dma_config(void)
{
// 定义 DMA 参数结构体
dma_parameter_struct dma_data_parameter;
// 对 DMA0 的通道 0 进行去初始化操作
dma_deinit(DMA0, DMA_CH0);
// 初始化 DMA 数据传输模式
// 设置 DMA 外设地址为 ADC0 的数据寄存器地址
dma_data_parameter.periph_addr = (uint32_t)(&ADC_RDATA(ADC0));
// 禁用外设地址自增
dma_data_parameter.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
// 设置 DMA 内存地址为存储 ADC 转换结果的数组地址
dma_data_parameter.memory_addr = (uint32_t)(&adc_value);
// 使能内存地址自增
dma_data_parameter.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
// 设置外设数据宽度为 32 位
dma_data_parameter.periph_width = DMA_PERIPHERAL_WIDTH_32BIT;
// 设置内存数据宽度为 32 位
dma_data_parameter.memory_width = DMA_MEMORY_WIDTH_32BIT;
// 设置 DMA 传输方向为从外设到内存
dma_data_parameter.direction = DMA_PERIPHERAL_TO_MEMORY;
// 设置 DMA 传输的数据数量为 2
dma_data_parameter.number = 2;
// 设置 DMA 通道优先级为高
dma_data_parameter.priority = DMA_PRIORITY_HIGH;
// 根据上述配置初始化 DMA0 的通道 0
dma_init(DMA0, DMA_CH0, &dma_data_parameter);
// 使能 DMA0 通道 0 的循环模式
dma_circulation_enable(DMA0, DMA_CH0);
// 使能 DMA0 的通道 0
dma_channel_enable(DMA0, DMA_CH0);
}
// 另一种 ADC 配置方式
void adc_config(void)
{
// 对 ADC0 和 ADC1 进行复位操作
adc_deinit(ADC0);
adc_deinit(ADC1);
// 配置 ADC 为双规则并行模式
adc_mode_config(ADC_DAUL_REGULAL_PARALLEL);
// 使能 ADC0 和 ADC1 的扫描模式
adc_special_function_config(ADC0, ADC_SCAN_MODE, ENABLE);
adc_special_function_config(ADC1, ADC_SCAN_MODE, ENABLE);
// 配置 ADC0 和 ADC1 的数据对齐方式为右对齐
adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT);
adc_data_alignment_config(ADC1, ADC_DATAALIGN_RIGHT);
// 配置 ADC0 和 ADC1 的规则通道长度为 2
adc_channel_length_config(ADC0, ADC_REGULAR_CHANNEL, 2);
adc_channel_length_config(ADC1, ADC_REGULAR_CHANNEL, 2);
// 配置 ADC0 的规则通道 0 为通道 4,采样时间为 55.5 个 ADC 时钟周期
adc_regular_channel_config(ADC0, 0, ADC_CHANNEL_4, ADC_SAMPLETIME_55POINT5);
// 配置 ADC0 的规则通道 1 为通道 5,采样时间为 55.5 个 ADC 时钟周期
adc_regular_channel_config(ADC0, 1, ADC_CHANNEL_5, ADC_SAMPLETIME_55POINT5);
// 配置 ADC1 的规则通道 0 为通道 5,采样时间为 55.5 个 ADC 时钟周期
adc_regular_channel_config(ADC1, 0, ADC_CHANNEL_5, ADC_SAMPLETIME_55POINT5);
// 配置 ADC1 的规则通道 1 为通道 4,采样时间为 55.5 个 ADC 时钟周期
adc_regular_channel_config(ADC1, 1, ADC_CHANNEL_4, ADC_SAMPLETIME_55POINT5);
// 配置 ADC0 的规则通道触发源为 TIMER1 的通道 1
adc_external_trigger_source_config(ADC0, ADC_REGULAR_CHANNEL, ADC0_1_EXTTRIG_REGULAR_T1_CH1);
// 配置 ADC1 的规则通道不使用外部触发
adc_external_trigger_source_config(ADC1, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE);
// 使能 ADC0 和 ADC1 的规则通道外部触发
adc_external_trigger_config(ADC0, ADC_REGULAR_CHANNEL, ENABLE);
adc_external_trigger_config(ADC1, ADC_REGULAR_CHANNEL, ENABLE);
// 使能 ADC0
adc_enable(ADC0);
// 对 ADC0 进行校准
adc_calibration_enable(ADC0);
// 使能 ADC1
adc_enable(ADC1);
// 延时 1ms 等待 ADC 稳定
delay_1ms(1);
// 对 ADC1 进行校准
adc_calibration_enable(ADC1);
// 使能 ADC0 的 DMA 功能
adc_dma_mode_enable(ADC0);
}
#endif
// 计算功率并进行比较的函数
void calculate_power_and_compare()
{
// 定义电压、电流和功率变量
float voltage1, current1, power1;
float voltage2, current2, power2;
// 定义存储高 16 位和低 16 位数据的变量
uint16_t h_raw, l_raw;
// 提取 adc_value[0] 的高 16 位数据
h_raw = (uint16_t)((adc_value[0]) >> 16);
// 提取 adc_value[0] 的低 16 位数据
l_raw = (uint16_t)((adc_value[0]) & 0xffff);
// 根据 ADC 转换结果计算电压值,假设参考电压为 3.3V,放大倍数为 3
voltage1 = ((h_raw * 3.3) / 4096) * 3;
// 根据 ADC 转换结果计算电流值,假设参考电压为 3.3V,放大倍数为 2
current1 = ((l_raw * 3.3) / 4096) * 2;
// 对电流值进行处理,减去偏移量并除以灵敏度
current1 = (current1 - 1.65) / 0.132;
// 计算功率
power1 = voltage1 * current1;
// printf("value0:%x, high: %x low:%x\n", adc_value[0], h_raw, l_raw);
// 以下代码被注释掉,可用于处理 adc_value[1] 的数据
// h_raw = (uint16_t)((adc_value[1]) >> 16);
// l_raw = (uint16_t)((adc_value[1]) & 0xffff);
// voltage2 = ((h_raw * 3.3) / 4096) * 3;
// current2 = ((l_raw * 3.3) / 4096) * 2;
// current2 = (current2 - 1.65) / 0.132;
// power2 = voltage2 * current2;
// printf("value1:%x, high: %x low:%x\n", adc_value[1], h_raw, l_raw);
// 输出计算结果
printf("Voltage: %f V, Current: %f A, Power: %f W\n", voltage1, current1, power1);
// printf("adc_value[1] - Voltage
这是测量两个io的电压,如果使用 “另一种 DMA 配置方式和ADC配置”是双通道采集数据,但是这里面有一个误区,
也就是adc_value[0]:
高16bit是ADC1(ADC_CHANNEL_5采集)
低16bit是ADC0(ADC_CHANNEL_4采集)
adc_regular_channel_config(ADC0, 0, ADC_CHANNEL_4, ADC_SAMPLETIME_55POINT5);
adc_regular_channel_config(ADC1, 0, ADC_CHANNEL_5, ADC_SAMPLETIME_55POINT5);
注意:adc_value[1]的高16bit是ADC1(ADC_CHANNEL_4采集),
低16bit是ADC0(ADC_CHANNEL_5采集)
adc_regular_channel_config(ADC0, 1, ADC_CHANNEL_5, ADC_SAMPLETIME_55POINT5);
adc_regular_channel_config(ADC1, 1, ADC_CHANNEL_4, ADC_SAMPLETIME_55POINT5);
因为 规则不变,只是ADC0采集的通道变了,所以
l_raw = (uint16_t)((adc_value[1]) >> 16); // 提取 adc_value[1] 的低 16 位数据,来自 ADC0(ADC_CHANNEL_5 采集) h_raw = (uint16_t)((adc_value[1]) & 0xffff); voltage2 = ((h_raw * 3.3) / 4096) * 3; current2 = ((l_raw * 3.3) / 4096) * 2; current2 = (current2 - 1.65) / 0.132; power2 = voltage2 * current2;
当 TIMER1
的通道 1 产生触发信号时,ADC0
开始对配置的通道(ADC_CHANNEL_4
和 ADC_CHANNEL_5
)进行采样和转换。转换完成后,转换结果会被存储在 ADC0
的数据寄存器中。由于 ADC0
的 DMA 功能已被使能,DMA 会自动将数据寄存器中的数据读取出来,并按照配置的内存地址和数据宽度,将数据传输到 adc_value
数组中。具体来说:
-
adc_value[0]
的高 16 位存储ADC1
对ADC_CHANNEL_5
的采样结果,低 16 位存储ADC0
对ADC_CHANNEL_4
的采样结果。 -
adc_value[1]
的高 16 位存储ADC1
对ADC_CHANNEL_4
的采样结果,低 16 位存储ADC0
对ADC_CHANNEL_5
的采样结果。