在以往学习中,使用ADC采集都是规则单通道软件启动采集的那种方式,这种方式也仅限于学习。在真正的项目中会采集很多路ADC,显示上面方式不合理,这时候就可以使用ADC+DMA进行多路采集,Nice!!!
STM32 ADC 简介
ADC通道与GPIO管脚对应表
DMA简介
更多理论内容可以查看中文参考数据手册
知道以上东西可以撸代码了。
使用固件库配置还是挺"容易"的。看配置代码就理解了。
DMA初始化
:
static void ADC_DMA_Init(void)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; //ADC外设数据寄存器地址作为基地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)g_stTempInfo.ADC_ValTab; //存储数据内存地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //传输方向为外设到内存
DMA_InitStructure.DMA_BufferSize = 4; //单位是下面的HalfWord 16bit
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //用到多通道ADC, 所以使能自动增加,一个通道转换完的数据放在g_stTempInfo.ADC_ValTab[0],
//下一个通道数据就自动存放在g_stTempInfo.ADC_ValTab[1]
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(DMA1_Channel1, &DMA_InitStructure);
/* Enable DMA1 Channel1 */
DMA_Cmd(DMA1_Channel1, ENABLE); //使能DMA通道1
}
模拟输入引脚初始化
:
static void GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //模拟输入引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
PA5对应ADC1的通道5,PB0对应ADC1的通道8
ADC初始化
:
static void ADC_Init(void)
{
ADC_InitTypeDef ADC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE ); //使能ADC1通道时钟
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //72M/6=12,ADC最大时间不能超过14M
ADC_DeInit(ADC1);
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1工作在独立模式
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数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 2; //顺序进行规则转换的ADC通道的数目
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1,ADC_Channel_5, 1, ADC_SampleTime_239Cycles5); //ADC1通道5配置->PA5 第3个参数为转换顺序,转换后的值存在g_stTempInfo.ADC_ValTab[0]中
ADC_RegularChannelConfig(ADC1,ADC_Channel_8, 2, ADC_SampleTime_239Cycles5); //ADC1通道8配置->PB0 第3个参数为转换顺序,转换后的值存在g_stTempInfo.ADC_ValTab[1]中
ADC_DMACmd(ADC1, ENABLE); //使能ADC1的DMA功能
ADC_Cmd(ADC1, ENABLE); //使能ADC1
ADC_ResetCalibration(ADC1); //重置指定的ADC1的校准寄存器
while(ADC_GetResetCalibrationStatus(ADC1)); //获取ADC1重置校准寄存器的状态,设置状态则等待
ADC_StartCalibration(ADC1); //开始指定ADC1的校准状态
while(ADC_GetCalibrationStatus(ADC1)); //获取指定ADC1的校准程序,设置状态则等待
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能
}
初始化GPIO、DMA、ADC
:
void MyADC_Init(void)
{
GPIO_Init();
ADC_DMA_Init(); //ADC DMA初始化
ADC_Init(); //ADC初始化
}
获取多个通道的ADC值
:
void GetAverageAdcVal(stADCTempInfo *TempInfo,u8 n)
{
u8 x = 0;
u32 ledVal = 0;
u32 machineVal = 0;
if(TempInfo == NULL)
{
printf("指针为空\r\n");
return;
}
for(x = 0; x < n; x++)
{
while(!DMA_GetFlagStatus(DMA1_FLAG_TC1)); //等待DMA传输完成
ledVal += TempInfo->ADC_ValTab[0];
machineVal += TempInfo->ADC_ValTab[1];
delay_ms(1);
}
ledVal = ledVal / n;
TempInfo->averageLedAdcVal = ledVal;
machineVal = machineVal / n;
TempInfo->averageMachineAdcVal = machineVal;
}