stm32专题二十四:ADC + DMA 多通道 规则同步
2021-10-28 来源:eefocus
ADC + DMA多通道采集
其实和单通道采集差不多,这里我们采集6路ADC,主要是修改一下DMA的Memory地址增量,代码如下:
bsp_adc.c
#include 'bsp_adc.h'
extern __IO uint16_t ADC_ConvertedValue[NOFCHANEL];
static void ADCx_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 打开 ADC IO端口时钟
ADC_GPIO_APBxClock_FUN(ADC_GPIO_CLK, ENABLE);
// 配置 ADC IO 引脚模式
// 必须为模拟输入
GPIO_InitStructure.GPIO_Pin = ADC_PIN1| ADC_PIN2 | ADC_PIN3 | ADC_PIN4 | ADC_PIN5 | ADC_PIN6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
// 初始化 ADC IO
GPIO_Init(ADC_PORT, &GPIO_InitStructure);
}
/**
* @brief ADC - DMA配置
* @note 注意,ADC - DMA只能使用ADC1 或 ADC3,ADC2无DMA功能
*/
static void ADCx_DMA_Config(void)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_DeInit(ADC_DMA_CHANNEL);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&(ADC_x->DR));
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADC_ConvertedValue;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = NOFCHANEL; // 改
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // 改
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(ADC_DMA_CHANNEL, &DMA_InitStructure);
DMA_Cmd(ADC_DMA_CHANNEL , ENABLE);
}
static void ADCx_Mode_Config(void)
{
ADC_InitTypeDef ADC_InitStruct;
// 打开ADC的时钟
ADC_APBxClock_FUN(ADC_CLK, ENABLE);
ADC_InitStruct.ADC_Mode = ADC_Mode_Independent; // 独立模式
ADC_InitStruct.ADC_ScanConvMode = ENABLE; // 使用扫描模式
ADC_InitStruct.ADC_ContinuousConvMode = ENABLE; // 连续转换
/* 不使用外部触发 */
ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStruct.ADC_NbrOfChannel = NOFCHANEL;
ADC_Init(ADC_x, &ADC_InitStruct);
RCC_ADCCLKConfig(RCC_PCLK2_Div8); // 配置ADC时钟为8分频 ADCCLK = 9M
// 配置ADC 通道的转换顺序和采样时间
ADC_RegularChannelConfig(ADC_x, ADC_CHANNEL1, 1, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC_x, ADC_CHANNEL2, 2, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC_x, ADC_CHANNEL3, 3, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC_x, ADC_CHANNEL4, 4, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC_x, ADC_CHANNEL5, 5, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC_x, ADC_CHANNEL6, 6, ADC_SampleTime_55Cycles5);
/* ADC - DMA设置要在使能ADC完成 */
ADCx_DMA_Config(); // 配置ADC - DMA
ADC_DMACmd(ADC_x, ENABLE); // 使能 ADC DMA 请求
ADC_Cmd(ADC_x, ENABLE); // 使能ADC
ADC_ResetCalibration(ADC_x); // 初始化ADC 校准寄存器
while(ADC_GetResetCalibrationStatus(ADC_x)); // 等待校准寄存器初始化完成
ADC_StartCalibration(ADC_x); // ADC开始校准
while(ADC_GetCalibrationStatus(ADC_x)); // 等待校准完成
ADC_SoftwareStartConvCmd(ADC_x, ENABLE); // 使用软件触发
}
/**
* @brief ADC初始化
* @param 无
* @retval 无
*/
void ADCx_Init(void)
{
ADCx_GPIO_Config();
ADCx_Mode_Config();
}
bsp_adc.h
#ifndef __BSP_ADC_H
#define __BSP_ADC_H
#include 'stm32f10x.h'
// 注意:用作ADC采集的IO必须没有复用,否则采集电压会有影响
/********************ADC1输入通道(引脚)配置**************************/
#define ADC_APBxClock_FUN RCC_APB2PeriphClockCmd
#define ADC_CLK RCC_APB2Periph_ADC1
#define ADC_GPIO_APBxClock_FUN RCC_APB2PeriphClockCmd
#define ADC_GPIO_CLK RCC_APB2Periph_GPIOC
#define ADC_PORT GPIOC
// 注意
// 1-PC0 在指南者里面接的是蜂鸣器,默认被拉低
// 2-PC0 在指南者里面接的是SPI FLASH的 片选,默认被拉高
// 所以 PC0 做 ADC 转换通道的时候,结果可能会有误差
// 转换通道个数
#define NOFCHANEL 6
#define ADC_PIN1 GPIO_Pin_0
#define ADC_CHANNEL1 ADC_Channel_10
#define ADC_PIN2 GPIO_Pin_1
#define ADC_CHANNEL2 ADC_Channel_11
#define ADC_PIN3 GPIO_Pin_2
#define ADC_CHANNEL3 ADC_Channel_12
#define ADC_PIN4 GPIO_Pin_3
#define ADC_CHANNEL4 ADC_Channel_13
#define ADC_PIN5 GPIO_Pin_4
#define ADC_CHANNEL5 ADC_Channel_14
#define ADC_PIN6 GPIO_Pin_5
#define ADC_CHANNEL6 ADC_Channel_15
// ADC1 对应 DMA1通道1,ADC3对应DMA2通道5,ADC2没有DMA功能
#define ADC_x ADC1
#define ADC_DMA_CHANNEL DMA1_Channel1
#define ADC_DMA_CLK RCC_AHBPeriph_DMA1
void ADCx_Init(void);
#endif /* __BSP_ADC_H */
测试结果如下(PC0接3.3v,PC5接地):
双ADC规则同步模式:
选用ADC1和ADC2,ADC1有一个通道PC1,ADC2有一个通道PC4。其中,ADC1设置为软件触发,而ADC2其实是由ADC1的触发信号一起触发的,根据中文参考手册,要设置为外部触发。其他配置见代码:
bsp_adc.c
#include 'bsp_adc.h'
extern __IO uint32_t ADC_ConvertedValue[NOFCHANEL];
static void ADCx_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// ADCx_1 GPIO 初始化
ADCx_1_GPIO_APBxClock_FUN ( ADCx_1_GPIO_CLK, ENABLE );
GPIO_InitStructure.GPIO_Pin = ADCx_1_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(ADCx_1_PORT, &GPIO_InitStructure);
// ADCx_2 GPIO 初始化
ADCx_1_GPIO_APBxClock_FUN ( ADCx_2_GPIO_CLK, ENABLE );
GPIO_InitStructure.GPIO_Pin = ADCx_2_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(ADCx_2_PORT, &GPIO_InitStructure);
}
/**
* @brief ADC - DMA配置
* @note 注意,ADC - DMA只能使用ADC1 或 ADC3,ADC2无DMA功能
*/
static void ADCx_DMA_Config(void)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_DeInit(ADC_DMA_CHANNEL);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&(ADCx_1->DR));
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADC_ConvertedValue;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = NOFCHANEL;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(ADC_DMA_CHANNEL, &DMA_InitStructure);
DMA_Cmd(ADC_DMA_CHANNEL , ENABLE);
}
static void ADCx_Mode_Config(void)
{
ADC_InitTypeDef ADC_InitStructure;
// 打开 ADC1 和 ADC2 的时钟
ADCx_1_APBxClock_FUN ( ADCx_1_CLK, ENABLE );
ADCx_2_APBxClock_FUN ( ADCx_2_CLK, ENABLE );
ADCx_DMA_Config();
/* ----------------ADCx_1 模式配置--------------------- */
// 双ADC的规则同步
ADC_InitStructure.ADC_Mode = ADC_Mode_RegSimult;
// 扫描模式
ADC_InitStructure.ADC_ScanConvMode = ENABLE ;
// 连续转换模式
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
// 不用外部触发转换,软件开启即可
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
// 转换结果右对齐
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
// 转换通道个数
ADC_InitStructure.ADC_NbrOfChannel = NOFCHANEL;
// 初始化ADC
ADC_Init(ADCx_1, &ADC_InitStructure);
// 配置ADC时钟N狿CLK2的8分频,即9MHz
RCC_ADCCLKConfig(RCC_PCLK2_Div8);
// 配置ADC 通道的转换顺序和采样时间
ADC_RegularChannelConfig(ADCx_1, ADCx_1_CHANNEL, 1,
ADC_SampleTime_239Cycles5);
// 使能ADC DMA 请求
ADC_DMACmd(ADCx_1, ENABLE);
/* ----------------ADCx_2 模式配置--------------------- */
// 双ADC的规则同步
ADC_InitStructure.ADC_Mode = ADC_Mode_RegSimult;
// 扫描模式
ADC_InitStructure.ADC_ScanConvMode = ENABLE ;
// 连续转换模式
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
// 不用外部触发转换,软件开启即可
ADC_InitStructure.ADC_ExternalTrigConv =
ADC_ExternalTrigConv_None;
// 转换结果右对齐
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
// 转换通道个数
ADC_InitStructure.ADC_NbrOfChannel = NOFCHANEL;
// 初始化ADC
ADC_Init(ADCx_2, &ADC_InitStructure);
// 配置ADC时钟为PCLK2的8分频,即9MHz
RCC_ADCCLKConfig(RCC_PCLK2_Div8);
// 配置ADC 通道的转换顺序和采样时间
ADC_RegularChannelConfig(ADCx_2, ADCx_2_CHANNEL, 1,
ADC_SampleTime_239Cycles5);
/* 使能ADCx_2的外部触发转换 */
ADC_ExternalTrigConvCmd(ADC2, ENABLE);
/* ----------------ADCx_1 校准--------------------- */
// 开启ADC ,并开始转换
ADC_Cmd(ADCx_1, ENABLE);
// 初始化ADC 校准寄存器
ADC_ResetCalibration(ADCx_1);
// 等待校准寄存器初始化完成
while(ADC_GetResetCalibrationStatus(ADCx_1));
// ADC开始校准
ADC_StartCalibration(ADCx_1);
// 等待校准完成
while(ADC_GetCalibrationStatus(ADCx_1));
/* ----------------ADCx_2 校准--------------------- */
// 开启ADC ,并开始转换
ADC_Cmd(ADCx_2, ENABLE);
// 初始化ADC 校准寄存器
ADC_ResetCalibration(ADCx_2);
// 等待校准寄存器初始化完成
while(ADC_GetResetCalibrationStatus(ADCx_2));
// ADC开始校准
ADC_StartCalibration(ADCx_2);
// 等待校准完成
while(ADC_GetCalibrationStatus(ADCx_2));
// 由于没有采用外部触发,所以使用软件触发ADC转换
ADC_SoftwareStartConvCmd(ADCx_1, ENABLE);
}
/**
* @brief ADC初始化
* @param 无
* @retval 无
*/
void ADCx_Init(void)
{
ADCx_GPIO_Config();
ADCx_Mode_Config();
}
bsp_adc.h
#ifndef __BSP_ADC_H
#define __BSP_ADC_H
#include 'stm32f10x.h'
// 双模式时,ADC1和ADC2转换的数据都存放在ADC1的数据寄存器,
// ADC1的在低十六位,ADC2的在高十六位
// 双ADC模式的第一个ADC,必须是ADC1
#define ADCx_1 ADC1
#define ADCx_1_APBxClock_FUN RCC_APB2PeriphClockCmd
#define ADCx_1_CLK RCC_APB2Periph_ADC1
#define ADCx_1_GPIO_APBxClock_FUN RCC_APB2PeriphClockCmd
#define ADCx_1_GPIO_CLK RCC_APB2Periph_GPIOC
#define ADCx_1_PORT GPIOC
#define ADCx_1_PIN GPIO_Pin_1
#define ADCx_1_CHANNEL ADC_Channel_11
// 双ADC模式的第二个ADC,必须是ADC2
#define ADCx_2 ADC2
#define ADCx_2_APBxClock_FUN RCC_APB2PeriphClockCmd
#define ADCx_2_CLK RCC_APB2Periph_ADC2
#define ADCx_2_GPIO_APBxClock_FUN RCC_APB2PeriphClockCmd
#define ADCx_2_GPIO_CLK RCC_APB2Periph_GPIOC
#define ADCx_2_PORT GPIOC
#define ADCx_2_PIN GPIO_Pin_4
#define ADCx_2_CHANNEL ADC_Channel_14
#define NOFCHANEL 1
// ADC1 对应 DMA1通道1,ADC3对应DMA2通道5,ADC2没有DMA功能
#define ADC_DMA_CHANNEL DMA1_Channel1
void ADCx_Init(void);
#endif /* __BSP_ADC_H */
然后在main.c中进行测试:
#include 'stm32f10x.h'
#include 'bsp_led.h'
#include 'bsp_usart.h'
#include 'bsp_adc.h'
#include __IO uint32_t ADC_ConvertedValue[NOFCHANEL] = {0}; // 局部变量,用于保存转换计算后的电压值 float ADC_ConvertedValueLocal[NOFCHANEL]; void Delay(__IO uint32_t nCount) { for(; nCount != 0; nCount--); } int main(void) { uint16_t temp0 = 0, temp1 = 0; LED_GPIO_Config(); USART_config(); ADCx_Init(); printf('----这是一个双ADC规则同步采集实验----tn'); while (1) { // 取出ADC1数据寄存器的高16位,这个是ADC2的转换数据 temp0 = (ADC_ConvertedValue[0]&0XFFFF0000) >> 16; // 取出ADC1数据寄存器的低16位,这个是ADC1的转换数据 temp1 = (ADC_ConvertedValue[0]&0XFFFF); ADC_ConvertedValueLocal[0] =(float) temp0/4096*3.3; ADC_ConvertedValueLocal[1] =(float) temp1/4096*3.3; printf('rn ADCx_1 value = %f V rn', ADC_ConvertedValueLocal[1]); printf('rn ADCx_2 value = %f V rn', ADC_ConvertedValueLocal[0]); printf('rnrn'); Delay(0xffffee); } } 实际测试结果如下: