在对实际应用过程中,ADC的采集大多是多个通道同时采集的,比如同时采集多个传感器的数据,就可能需要我们配置多个通道的ADC采集了,而多通道的ADC采集大多用到了DMA,笼统的讲通过DMA来传输数据不经过CPU,可以有效的为CPU减负,我们在使用时只需要通过CPU完成相应的初始化,而传输本身呢,是由DMA来进行的,而对于采集到的不同通道的数据我们只需要将其放入指定的数组便可以获得到不同通道具体的数据了。
DMA通道配置过程:
1、在DMA_CPARx寄存器中设置外设寄存起的地址,发生外设数据请求时,这个地址将是数据传输的源或目标。
2、在DMA_CMARx寄存器中设置数据存储寄存器的地址,发生外设数据请求时,传输的数据将从这个地址读出或写入这个地址。
3、在DMA_CNDTRx寄存器中设置要传输的数据量。在每个数据传输后,这个数值递减。
4、在DMA_CCRx寄存器的PL[1:0]位中设置优先级。
5、在DMA_CCRx寄存器中设置数据传输的方向、循环模式、外设和存储器的增量模式、外设和存储器的数据宽度、传输一半产生中断,或
传输完成产生中断。
6、设置DMA_CCRx寄存器的ENABLE位,启动该通道。
具体的配置过程我们通过代码来 展示
首先定义一个数组以便我们存放数据,数组大小按照实际需求即可。
u16 ADC_convered[2]={0,0};
然后是io口的初始化(我这儿把ADC的时钟也直接使能了)
static void ADC_GPIO_CONFIG(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1 , ENABLE ); //使能
ADC1通道时钟
//PA1 作为模拟通道输入引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;//初始化两个GPIO
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
再接下来便是重头戏了——DMA和ADC的初始化(有些部分是需要配置多个通道的)
static void ADC_MODE_CONFIG_(void)
{
DMA_InitTypeDef DMA_InitInstructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
DMA_DeInit(DMA1_Channel1);//复位
DMA_InitInstructure.DMA_PeripheralBaseAddr =(u32)(&(ADC1->DR));//配置外设的基址,取adc
数据寄存器的地址
DMA_InitInstructure.DMA_MemoryBaseAddr = (u32)ADC_convered;//把DMA的数据存入数组
DMA_InitInstructure.DMA_DIR = DMA_DIR_PeripheralSRC;//外设到DMA
DMA_InitInstructure.DMA_BufferSize = 2;//2个通道
DMA_InitInstructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//
DMA_InitInstructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitInstructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitInstructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitInstructure.DMA_Mode = DMA_Mode_Circular;//不断地传输,有数据就传输
DMA_InitInstructure.DMA_Priority = DMA_Priority_High;//DMA优先级
DMA_InitInstructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1,&DMA_InitInstructure);
DMA_Cmd(DMA1_Channel1,ENABLE);//使能DMA请求
ADC_InitTypeDef ADC_InitStructure;
ADC_DeInit(ADC1); //复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在独立
模式
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_InitStruct中指定的参数初始化外设ADCx的寄
存器
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
//设置指定ADC的规则组通道,一个序列,采样时间
ADC_RegularChannelConfig(ADC1, ADC_Channel_1,1, ADC_SampleTime_239Cycles5 );
//ADC1,ADC1通道1,采样时间为239.5周期
ADC_RegularChannelConfig(ADC1, ADC_Channel_2,2, ADC_SampleTime_239Cycles5 );
//ADC1,ADC1通道2,采样时间为239.5周期
ADC_DMACmd(ADC1,ENABLE);
ADC_Cmd(ADC1,ENABLE);
ADC_ResetCalibration(ADC1); //使能复位校准
while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
ADC_StartCalibration(ADC1); //开启AD校准
while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能
//while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束
}
然后把初始化的程序整到一块,方便调用
void Adc_Init()
{
ADC_GPIO_CONFIG();
ADC_MODE_CONFIG_();
}
接下来便是主函数部分了,主函数主要看要实现什么样的功能,初始化之后其实也就是把采集到的数据展示或设置阈值再进行相应的操作。
extern u16 ADC_convered[2];
int main(void)
{
delay_init();
//uart_init(9600);//串口初始化为9600,串口打印需要对其进行初始化
float temp[2];
delay_init();
Oled_Init();
Adc_Init();
float value;
float value1;
char buf[5];
char buf1[5];
while(1)
{
temp[0]=(float)ADC_convered[0]/4096*3.3;
temp[1]=(float)ADC_convered[1]/4096*3.3;//结果-电压转换
//printf("\r\n THE AD1 is = %f V \r\n",temp[0]);
//printf("\r\n THE AD2 is = %f V \r\n",temp[1]);
value = temp[0];
sprintf(buf,"%f",value);
Oled_Display_String(0,0,buf);//显示电压值
value1 = temp[1];
sprintf(buf1,"%f",value1);
Oled_Display_String(2,0,buf1);//显示电压值
//Oled_Display_String(4,0,buf1);//
delay_ms(500);//延时了一小会儿,要不数据变化太快,看的心烦
//我这儿是把电压值显示在了oled上
}
}
如有错误还请指出,谢谢