jonyzhu 发表于 2014-8-5 13:24:21

使用 STM32F303VCT6 的 ADC

本帖最后由 jonyzhu 于 2014-8-5 13:29 编辑

这篇帖子将介绍如何使用 STM32F303VCT6 的 ADC 功能,属于[记录我的 STM32 示波器的研发经历]的副产品。STM32F3 系列的模拟电路部分功能比较强(相对于ST其它的MCU),其中STM32F303VCT6 最高主频 72MHz,256K Flash,48K RAM;最牛的,是它的72MHz的ADC时钟,以及6bit分辨率下8个时钟周期的采样+转换时间,这相当于9M/s的采样率了 。

而 STM32F3 Discovery 开发板的价格也非常便宜,作为学习的起点是个不错的选择。


系统时钟的设置
上面刚刚说到Discovery开发板便宜,额,其实是有代价的:没有外部晶振,开发板上STM32F303VCT6的时钟是那个做ST-Link2的STM32F103C8T6的MCO给的8MHz。。。
所以,你最好是把官网提供的固件程序里面的system_stm32f30x.c文件拷贝到自己项目里面用,而不要用外设示例程序里面的,否则,你可能就会和我刚开始一样:在下载程序到MCU时提示你丢失目标MCU。如果你不幸干了这事儿,请去ST官网下载 STM32 ST-LINK Utility 来恢复。
Discovery 固件程序里面的system_stm32f30x.c也没有什么秘密,主要是里面定义了这个宏:
#define PLL_SOURCE_HSE_BYPASS
然后,剩下的代码自己就知道如何正确的配置时钟了。

ADC的设置
GPIO的设置
需要配置采样通道对应的GPIO为模拟端口。顺便说一句:为了省电,应该将所有不用的IO都设置成模拟端口。
因为要双ADC交替采样,所以下面这2个IO口需要连接在一起。
        GPIO_InitTypeDef         gs;
        RCC_AHB1PeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
        gs.GPIO_Mode = GPIO_Mode_AN;
        gs.GPIO_OType = GPIO_OType_PP;
        gs.GPIO_PuPd = GPIO_PuPd_NOPULL;
        gs.GPIO_Speed = GPIO_Speed_50MHz;
        gs.GPIO_Pin = GPIO_Pin_3;
        GPIO_Init(GPIOA, &gs);
        gs.GPIO_Pin = GPIO_Pin_7;
        GPIO_Init(GPIOA, &gs);
ADC 时钟
STM32F3 的ADC时钟比其它系列的要复杂(越复杂越强嘛),它可以有2个时钟源:



[*]PLL输出。这个设置的目的是可以实现不受HCLK影响的自由时钟频率配置,分频系数可以是:1, 2, 4, 6, 8, 12, 16, 32, 64, 128, 256 等等。缺点是,不保证和内部TIM的同步(也不是什么大问题,可以配置ADC的触发方式和TIM同步就可以了,当然,外部触发会慢一个时钟。
[*]HCLK。这个设置可以保证ADC时钟和TIM时钟肯定是同步的。缺点是分频系数选择少:1,2,4。没了。

从制作示波器的角度看,PLL其实更加合适,因为你用片上RAM做存储,数据深度不大的情况下,要看低频波形,还是调低ADC频率比较方便。
摘录对应的代码(完整的设置代码在最后):
ADC_CommonInitTypeDef cs;
RCC_ADCCLKConfig(RCC_ADC12PLLCLK_Div1);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_ADC12, ENABLE);
cs.ADC_Clock = ADC_Clock_AsynClkMode;

规则通道的采样模式
STM32的ADC通道可以分为规则通道(Regualar Channel)和注入通道(Injected Channel)两种。从灵活性上面来说,注入通道更灵活,可以随意插入正在进行采样的规则通道序列中,也可以以交替模式安排4个ADC里面的每个采样通道按照触发顺序依次采样。更重要的,注入模式中每个采样通道都有独立的数据寄存器(STM32F3有4个ADC,每个ADC的注入模式最多有4个通道,所以一共对应有4X4=16个数据寄存器),从而不会发生Overrun的情况。
然而就示波器的应用来说,规则通道更合适,因为它可以达到最大的采样率,且配置简单。最重要的:它可以用DMA!注入通道的交替触发采样是无法用DMA的(坑爹啊)!


双ADC的交替采样模式下,有几个参数是需要精心设计的:

[*]采样周期。共分8挡,最短1.5个ADC时钟周期,最长601个时钟周期。采样周期加上前面讲的PLL时钟分频系数,可以搭配出多种不同的采样率,在捕获低频信号时会非常有用。
[*]转换周期。这个参数和分辨率有关。采样周期+转换周期=总周期,采样频率/总周期=采样率。就这么简单。
[*]通道间采样间隔。别忘了我们可是双ADC采样,ADC1和ADC2在对同一个通道进行交替采样,在ADC1完成采样(注意,是采样,不是转换)后,要等几个(最少1个)ADC时钟周期才让开始ADC2的采样。

仍然以6bit分辨率为例:采样周期=1.5,转换周期=6.5,总周期=1.5+6.5=8,通道间采样间隔=3 时,ADC1在 t0 时刻采样,然后ADC2就会在 t0+1.5+3=t0+4.5 的时刻采样,然后如此继续下去。
这样,相当于8个ADC时钟周期里面,采样了2次(虽然2次并不是严格对齐的),72MHz/(8/2)=72MHz/4 = 18M/s采样率。

我为什么老抓住6bit分辨率不放?因为我的显示屏只有320X240啊,垂直不能同时显示2个8bit分辨率的波形啊!!!而且我SPI的显示屏啊,刷新率是硬伤啊,6bit分辨率意味着我能少刷几个像素啊!!!哎。。。(我后来都想整单色LCD了,那刷新率杠杠的,不过没关系,我还有STM32F429i Discovery,哈哈哈!)
Common 设置:
cs.ADC_Mode= ADC_Mode_Interleave;
cs.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
cs.ADC_DMAMode = ADC_DMAMode_OneShot;
cs.ADC_TwoSamplingDelay = 2;//2就是3个周期的通道间间隔
ADC_CommonInit(ADC1, &cs);   //双通道模式下,只需配置Master ADC
每个 ADC 的设置:
        ADC_StructInit(&adcs);
        ADC_VoltageRegulatorCmd(ADC1, ENABLE);
        ADC_VoltageRegulatorCmd(ADC2, ENABLE);
        osDelay(1); //延迟 1ms,需要用你自己的延时函数替换。其实2us就足够了,CMSIS_RTOS 没有那么精细,1ms算了。
        ADC_SelectCalibrationMode(ADC1, ADC_CalibrationMode_Single); ADC_StartCalibration(ADC1);
        ADC_SelectCalibrationMode(ADC2, ADC_CalibrationMode_Single); ADC_StartCalibration(ADC2);
        while(ADC_GetCalibrationStatus(ADC1) != RESET );
        while(ADC_GetCalibrationStatus(ADC2) != RESET );
        adcs.ADC_ContinuousConvMode = ADC_ContinuousConvMode_Enable;
        adcs.ADC_Resolution = ADC_Resolution_6b;
        adcs.ADC_ExternalTrigConvEvent = ADC_ExternalTrigConvEvent_7;         
        adcs.ADC_ExternalTrigEventEdge = ADC_ExternalTrigEventEdge_None;
        adcs.ADC_DataAlign = ADC_DataAlign_Right;
        adcs.ADC_OverrunMode = ADC_OverrunMode_Enable;
        adcs.ADC_AutoInjMode = ADC_AutoInjec_Disable;
        adcs.ADC_NbrOfRegChannel = 1;
        ADC_Init(ADC1, &adcs);
        ADC_Init(ADC2, &adcs);
        ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 1, ADC_SampleTime_1Cycles5);
        ADC_RegularChannelConfig(ADC2, ADC_Channel_4, 1, ADC_SampleTime_1Cycles5);
        ADC_Cmd(ADC1, ENABLE);
        ADC_Cmd(ADC2, ENABLE);

DMA的设置
ADC转换那么快,用CPU等中断再往数组里面存那是不现实的哇!(我用注入交替触发模式试过,用中断存数组大约只有200K/s的速率)
ADC1、2做双交替采样的DMA设置,有2个选择:

[*]只用1个DMA通道,一次在ADC2的EOC事件后一次读取2个采样结果到一个数组中
[*]用2个DMA通道,分别读取2个ADC的采样结果到2个数组中

我选了第二个,原因么:

[*]我不缺DMA
[*]以后从双采样切换到独立采样(也就是双通道示波器变身四通道示波器)时,比较自然
DMA里面需要确定你的采样存储深度,让DMA在读取完这么多组数据后自动停止,发送给LCD去显示。当然,你也可以让DMA继续采样来提高捕获率或者实现余辉效果,不过那需要内存支持,算法就复杂了,我还没有搞到那么深。等以后用到那个地步了,可能我也不会选择 soc 的 ADC 了。
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1|RCC_AHBPeriph_DMA2, ENABLE);
        ds.DMA_PeripheralBaseAddr         = (uint32_t)&ADC1->DR;
        ds.DMA_MemoryBaseAddr                 = (uint32_t)&appADCValue1;
        ds.DMA_BufferSize                         = ADCDeepth;
        ds.DMA_DIR                                         = DMA_DIR_PeripheralSRC;
        ds.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte;
        ds.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;
        ds.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable;
        ds.DMA_MemoryInc                         = DMA_MemoryInc_Enable;
        ds.DMA_Mode                                 = DMA_Mode_Circular;
        ds.DMA_Priority                         = DMA_Priority_VeryHigh;
        ds.DMA_M2M                                         = DMA_M2M_Disable;
        DMA_Init(DMA1_Channel1, &ds);
        ds.DMA_PeripheralBaseAddr         = (uint32_t)&ADC2->DR;
        ds.DMA_MemoryBaseAddr                 = (uint32_t)&appADCValue2;
        DMA_Init(DMA2_Channel1, &ds);
        DMA_Cmd(DMA1_Channel1, ENABLE);
        DMA_Cmd(DMA2_Channel1, ENABLE);
        DMA_ITConfig(DMA2_Channel1, DMA_IT_TC, ENABLE);
        ns.NVIC_IRQChannel = DMA2_Channel1_IRQn;
        ns.NVIC_IRQChannelPreemptionPriority = 4;
        ns.NVIC_IRQChannelSubPriority = 8;
        ns.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&ns);

好了,最后启动 Master ADC的转换就开始了 ADC_StartConversion(ADC1);

最后,把上面的代码整合一下,看个全貌(不是全部工程,但应该更容易应用到你自己到项目中去,因为少了很多乱七八糟的和我个人环境设置有关的东西):
#define ADCDeepth 300
uint8_t appADCValue1;
uint8_t appADCValue2;

void ADCInit(void){
        GPIO_InitTypeDef         gs;
        ADC_CommonInitTypeDef cs;
        ADC_InitTypeDef         adcs;
        DMA_InitTypeDef         ds;
        NVIC_InitTypeDef         ns;

        RCC_AHB1PeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
        gs.GPIO_Mode = GPIO_Mode_AN;
        gs.GPIO_OType = GPIO_OType_PP;
        gs.GPIO_PuPd = GPIO_PuPd_NOPULL;
        gs.GPIO_Speed = GPIO_Speed_50MHz;
        gs.GPIO_Pin = GPIO_Pin_3;
        GPIO_Init(GPIOA, &gs);
        gs.GPIO_Pin = GPIO_Pin_7;
        GPIO_Init(GPIOA, &gs);

        RCC_ADCCLKConfig(RCC_ADC12PLLCLK_Div1);
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_ADC12, ENABLE);
        cs.ADC_Clock = ADC_Clock_AsynClkMode;
        cs.ADC_Mode= ADC_Mode_Interleave;
        cs.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
        cs.ADC_DMAMode = ADC_DMAMode_OneShot;
        cs.ADC_TwoSamplingDelay = 2;//2就是3个周期的通道间间隔
        ADC_CommonInit(ADC1, &cs);    //双通道模式下,只需配置Master ADC

        ADC_StructInit(&adcs);
        ADC_VoltageRegulatorCmd(ADC1, ENABLE);
        ADC_VoltageRegulatorCmd(ADC2, ENABLE);
        osDelay(1); //延迟 1ms,需要用你自己的延时函数替换。其实2us就足够了,CMSIS_RTOS 没有那么精细,1ms算了。
        ADC_SelectCalibrationMode(ADC1, ADC_CalibrationMode_Single); ADC_StartCalibration(ADC1);
        ADC_SelectCalibrationMode(ADC2, ADC_CalibrationMode_Single); ADC_StartCalibration(ADC2);
        while(ADC_GetCalibrationStatus(ADC1) != RESET );
        while(ADC_GetCalibrationStatus(ADC2) != RESET );
        adcs.ADC_ContinuousConvMode = ADC_ContinuousConvMode_Enable;
        adcs.ADC_Resolution = ADC_Resolution_6b;
        adcs.ADC_ExternalTrigConvEvent = ADC_ExternalTrigConvEvent_7;         
        adcs.ADC_ExternalTrigEventEdge = ADC_ExternalTrigEventEdge_None;
        adcs.ADC_DataAlign = ADC_DataAlign_Right;
        adcs.ADC_OverrunMode = ADC_OverrunMode_Enable;
        adcs.ADC_AutoInjMode = ADC_AutoInjec_Disable;
        adcs.ADC_NbrOfRegChannel = 1;
        ADC_Init(ADC1, &adcs);
        ADC_Init(ADC2, &adcs);
        ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 1, ADC_SampleTime_1Cycles5);
        ADC_RegularChannelConfig(ADC2, ADC_Channel_4, 1, ADC_SampleTime_1Cycles5);
        ADC_Cmd(ADC1, ENABLE);
        ADC_Cmd(ADC2, ENABLE);

        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1|RCC_AHBPeriph_DMA2, ENABLE);
        ds.DMA_PeripheralBaseAddr         = (uint32_t)&ADC1->DR;
        ds.DMA_MemoryBaseAddr                 = (uint32_t)&appADCValue1;
        ds.DMA_BufferSize                         = ADCDeepth;
        ds.DMA_DIR                                         = DMA_DIR_PeripheralSRC;
        ds.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte;
        ds.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;
        ds.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable;
        ds.DMA_MemoryInc                         = DMA_MemoryInc_Enable;
        ds.DMA_Mode                                 = DMA_Mode_Circular;
        ds.DMA_Priority                         = DMA_Priority_VeryHigh;
        ds.DMA_M2M                                         = DMA_M2M_Disable;
        DMA_Init(DMA1_Channel1, &ds);
        ds.DMA_PeripheralBaseAddr         = (uint32_t)&ADC2->DR;
        ds.DMA_MemoryBaseAddr                 = (uint32_t)&appADCValue2;
        DMA_Init(DMA2_Channel1, &ds);
        DMA_Cmd(DMA1_Channel1, ENABLE);
        DMA_Cmd(DMA2_Channel1, ENABLE);
        DMA_ITConfig(DMA2_Channel1, DMA_IT_TC, ENABLE);
        ns.NVIC_IRQChannel = DMA2_Channel1_IRQn;
        ns.NVIC_IRQChannelPreemptionPriority = 4;
        ns.NVIC_IRQChannelSubPriority = 8;
        ns.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&ns);
}
void DMA2_Channel1_IRQHandler(void){
        if(DMA_GetITStatus(DMA2_IT_TC1)!=RESET){
                DMA_ClearITPendingBit(DMA2_IT_TC1);
                //数据拿到了,接下来看你的了!
        }
}

star_tale 发表于 2014-8-5 13:34:03

分享精神值得表扬

30zero 发表于 2014-8-9 12:23:40


分享精神值得表扬+1 刚好准备做这芯片的开发

30zero 发表于 2014-8-9 15:18:45

楼主能分享一下程序,然后小弟研究研究吗

kxb 发表于 2014-8-10 09:32:53

谢谢分享经验

jonyzhu 发表于 2014-8-11 14:13:54

30zero 发表于 2014-8-9 15:18
楼主能分享一下程序,然后小弟研究研究吗

上面就是程序,我特地从项目里面摘出来的;否则全是自定义的配置,直接给出来你们是根本编译不了的。

minier 发表于 2014-9-6 06:57:31

顶楼主!!这是ADC综合运用的导向贴!赞一个

30zero 发表于 2014-9-15 14:59:32

jonyzhu 发表于 2014-8-11 14:13
上面就是程序,我特地从项目里面摘出来的;否则全是自定义的配置,直接给出来你们是根本编译不了的。 ...

楼主,我想请教一下,最近在学校搞个检测项目,也是用到 STM32F303VCT6。需要多路ADC检测,问题是ADC12_IN6,ADC34_IN1,这些通道怎么开启呢?

jonyzhu 发表于 2014-9-15 16:47:33

30zero 发表于 2014-9-15 14:59
楼主,我想请教一下,最近在学校搞个检测项目,也是用到 STM32F303VCT6。需要多路ADC检测,问题是ADC12_I ...


[*]IO 配置为 GPIO_Mode_AN
[*]ADC_RegularChannelConfig(ADC1, %你的采样通道%, 1, ADC_SampleTime_1Cycles5)

30zero 发表于 2014-9-17 21:17:41

jonyzhu 发表于 2014-9-15 16:47
[*]IO 配置为 GPIO_Mode_AN
[*]ADC_RegularChannelConfig(ADC1, %你的采样通道%, 1, ADC_SampleTime_1 ...

请问STM32F303VCT6的ADC连续采样多路该怎么配置了?我搞了一天都搞不懂。希望你们帮助我。配置为:
void ADC_Config(void)
{
        ADC_InitTypeDef ADC_InitStructure;
        ADC_CommonInitTypeDef ADC_CommonInitStructure;
       
        RCC_ADCCLKConfig(RCC_ADC12PLLCLK_Div1|RCC_ADC34PLLCLK_Div1);
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_ADC12|RCC_AHBPeriph_ADC34, ENABLE);
       
        ADC_GPIO_Config();
        ADC_DMA_Config();
       
        ADC_StructInit(&ADC_InitStructure);
       
        ADC_VoltageRegulatorCmd(ADC1, ENABLE);
        ADC_VoltageRegulatorCmd(ADC2, ENABLE);
        ADC_VoltageRegulatorCmd(ADC3, ENABLE);
        ADC_VoltageRegulatorCmd(ADC4, ENABLE);
       
        Delay(10);
       
        ADC_SelectCalibrationMode(ADC1, ADC_CalibrationMode_Single);
        ADC_StartCalibration(ADC1);

        ADC_SelectCalibrationMode(ADC2, ADC_CalibrationMode_Single);
        ADC_StartCalibration(ADC2);
       
        ADC_SelectCalibrationMode(ADC3, ADC_CalibrationMode_Single);
        ADC_StartCalibration(ADC3);
       
        ADC_SelectCalibrationMode(ADC4, ADC_CalibrationMode_Single);
        ADC_StartCalibration(ADC4);

        while(ADC_GetCalibrationStatus(ADC1) != RESET );
        calibration_value_1 = ADC_GetCalibrationValue(ADC1);

        while(ADC_GetCalibrationStatus(ADC2) != RESET );
        calibration_value_2 = ADC_GetCalibrationValue(ADC2);

        while(ADC_GetCalibrationStatus(ADC3) != RESET ){}
        calibration_value_3 = ADC_GetCalibrationValue(ADC3);

        while(ADC_GetCalibrationStatus(ADC4) != RESET ){}
        calibration_value_4 = ADC_GetCalibrationValue(ADC4);
       
        /* ADC Dual mode configuration */
        ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Interleave;                                                                  
        ADC_CommonInitStructure.ADC_Clock = ADC_Clock_AsynClkMode;                     
        ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_1;            
        ADC_CommonInitStructure.ADC_DMAMode = ADC_DMAMode_Circular;               
        ADC_CommonInitStructure.ADC_TwoSamplingDelay = 2;         
        ADC_CommonInit(ADC1, &ADC_CommonInitStructure);

        /* 配置ADC1在连续模式下分辨率为12bits*/
        ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;//配置ADC的转化分辨率
        ADC_InitStructure.ADC_ContinuousConvMode = ADC_ContinuousConvMode_Enable;//配置选择连续采样或单次采样
        ADC_InitStructure.ADC_ExternalTrigConvEvent = ADC_ExternalTrigConvEvent_0;//ADC内部边沿触发               
        ADC_InitStructure.ADC_ExternalTrigEventEdge = ADC_ExternalTrigEventEdge_None;
        ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//设置ADC是左对齐还是右对齐
        ADC_InitStructure.ADC_OverrunMode = ADC_OverrunMode_Disable;   
        ADC_InitStructure.ADC_AutoInjMode = ADC_AutoInjec_Disable;
        ADC_InitStructure.ADC_NbrOfRegChannel = 1;
               
        ADC_Init(ADC1, &ADC_InitStructure);
        ADC_Init(ADC2, &ADC_InitStructure);
        ADC_Init(ADC3, &ADC_InitStructure);
        ADC_Init(ADC4, &ADC_InitStructure);
       
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_19Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 2, ADC_SampleTime_19Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 3, ADC_SampleTime_19Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 4, ADC_SampleTime_19Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 5, ADC_SampleTime_19Cycles5);

        ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 6, ADC_SampleTime_19Cycles5);
        ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 7, ADC_SampleTime_19Cycles5);
        ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 8, ADC_SampleTime_19Cycles5);
        ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 9, ADC_SampleTime_19Cycles5);
        ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 10, ADC_SampleTime_19Cycles5);
          
ADC_RegularChannelConfig(ADC2, ADC_Channel_1, 11, ADC_SampleTime_19Cycles5);
ADC_RegularChannelConfig(ADC2, ADC_Channel_2, 12, ADC_SampleTime_19Cycles5);
ADC_RegularChannelConfig(ADC2, ADC_Channel_3, 13, ADC_SampleTime_19Cycles5);
ADC_RegularChannelConfig(ADC2, ADC_Channel_4, 14, ADC_SampleTime_19Cycles5);
ADC_RegularChannelConfig(ADC2, ADC_Channel_5, 15, ADC_SampleTime_19Cycles5);
ADC_RegularChannelConfig(ADC2, ADC_Channel_11, 16, ADC_SampleTime_19Cycles5);
ADC_RegularChannelConfig(ADC2, ADC_Channel_12, 17, ADC_SampleTime_19Cycles5);

ADC_RegularChannelConfig(ADC3, ADC_Channel_1, 18, ADC_SampleTime_19Cycles5);
ADC_RegularChannelConfig(ADC3, ADC_Channel_2, 19, ADC_SampleTime_19Cycles5);
ADC_RegularChannelConfig(ADC3, ADC_Channel_3, 20, ADC_SampleTime_19Cycles5);
ADC_RegularChannelConfig(ADC3, ADC_Channel_5, 21, ADC_SampleTime_19Cycles5);
ADC_RegularChannelConfig(ADC3, ADC_Channel_12, 22, ADC_SampleTime_19Cycles5);
ADC_RegularChannelConfig(ADC3, ADC_Channel_13, 23, ADC_SampleTime_19Cycles5);
ADC_RegularChannelConfig(ADC3, ADC_Channel_14, 24, ADC_SampleTime_19Cycles5);
ADC_RegularChannelConfig(ADC3, ADC_Channel_15, 25, ADC_SampleTime_19Cycles5);
ADC_RegularChannelConfig(ADC3, ADC_Channel_16, 26, ADC_SampleTime_19Cycles5);

ADC_RegularChannelConfig(ADC4, ADC_Channel_1, 27, ADC_SampleTime_19Cycles5);
ADC_RegularChannelConfig(ADC4, ADC_Channel_2, 28, ADC_SampleTime_19Cycles5);
ADC_RegularChannelConfig(ADC4, ADC_Channel_3, 29, ADC_SampleTime_19Cycles5);
ADC_RegularChannelConfig(ADC4, ADC_Channel_4, 30, ADC_SampleTime_19Cycles5);
ADC_RegularChannelConfig(ADC4, ADC_Channel_5, 31, ADC_SampleTime_19Cycles5);
ADC_RegularChannelConfig(ADC4, ADC_Channel_12, 32, ADC_SampleTime_19Cycles5);
ADC_RegularChannelConfig(ADC4, ADC_Channel_13, 33, ADC_SampleTime_19Cycles5);

        ADC_RegularChannelConfig(ADC4, ADC_Channel_6, 34, ADC_SampleTime_19Cycles5);
        ADC_RegularChannelConfig(ADC4, ADC_Channel_7, 35, ADC_SampleTime_19Cycles5);
        ADC_RegularChannelConfig(ADC4, ADC_Channel_8, 36, ADC_SampleTime_19Cycles5);
        ADC_RegularChannelConfig(ADC4, ADC_Channel_9, 37, ADC_SampleTime_19Cycles5);
        ADC_RegularChannelConfig(ADC4, ADC_Channel_10, 38, ADC_SampleTime_19Cycles5);
        ADC_RegularChannelConfig(ADC4, ADC_Channel_11, 39, ADC_SampleTime_19Cycles5);

        ADC_DMAConfig(ADC1, ADC_DMAMode_Circular);//配置DMA循环模式
        ADC_DMAConfig(ADC2, ADC_DMAMode_Circular);//配置DMA循环模式
        ADC_DMAConfig(ADC3, ADC_DMAMode_Circular);//配置DMA循环模式
        ADC_DMAConfig(ADC4, ADC_DMAMode_Circular);//配置DMA循环模式
       
        ADC_DMACmd(ADC1, ENABLE);        /* Enable ADC_DMA */
        ADC_DMACmd(ADC2, ENABLE);        /* Enable ADC_DMA */
        ADC_DMACmd(ADC3, ENABLE);        /* Enable ADC_DMA */
        ADC_DMACmd(ADC4, ENABLE);        /* Enable ADC_DMA */
       
        ADC_Cmd(ADC1, ENABLE);
        ADC_Cmd(ADC2, ENABLE);
        ADC_Cmd(ADC3, ENABLE);
        ADC_Cmd(ADC4, ENABLE);
       
        while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_RDY));
        while(!ADC_GetFlagStatus(ADC2, ADC_FLAG_RDY));
        while(!ADC_GetFlagStatus(ADC3, ADC_FLAG_RDY));
        while(!ADC_GetFlagStatus(ADC4, ADC_FLAG_RDY));

//DMA_Cmd(DMA1_Channel1, ENABLE);//DMA频道使能
//DMA_Cmd(DMA1_Channel1, ENABLE);//DMA频道使能
//DMA_Cmd(DMA1_Channel1, ENABLE);//DMA频道使能
        ADC_StartConversion(ADC1);
        ADC_StartConversion(ADC2);
        ADC_StartConversion(ADC3);
        ADC_StartConversion(ADC4);
}

void ADC_DMA_Config(void)
{
        DMA_InitTypeDef DMA_InitStruct;
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
       
        /* DMA1 Channel1 Config */
        DMA_DeInit(DMA1_Channel1);//选择通道
        DMA_InitStruct.DMA_PeripheralBaseAddr = ADC_CDR_ADDRESS;//设置外设地址
        DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)&ADC_ConvertedValue;//设置内存映射地址
        DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;//外设作为数据传输的来源
        DMA_InitStruct.DMA_BufferSize = 39;//缓冲为39
        DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//关外设地址映射
        DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;//关内存地址计数
        DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//字节 外设数据宽度为32位
        DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
        DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;//循环模式
        DMA_InitStruct.DMA_Priority = DMA_Priority_High;//高优先级
        DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;//关内存到内存
        DMA_Init(DMA1_Channel1, &DMA_InitStruct);
}

jonyzhu 发表于 2014-9-17 22:47:22

30zero 发表于 2014-9-17 21:17
请问STM32F303VCT6的ADC连续采样多路该怎么配置了?我搞了一天都搞不懂。希望你们帮助我。配置为:
void...

ADC_InitStructure.ADC_NbrOfRegChannel = 1;
这里的数字不能瞎写,你需要采样几个通道就是几。例如,你 ADC1 采样 10 个通道,你就要写 10。“1” 是你从别人代码里面拷贝过来的,看过手册,搞明白每行代码的用途才行。

hyghyg1234 发表于 2014-9-17 22:58:21

写的很好,谢谢。

30zero 发表于 2014-9-22 22:14:16

楼主,我的多路采样ADC还是不行{:3_60:}我ADC1、ADC2、ADC3、ADC4都要用起来,楼主能教导一下吗?{:cry:}下午是我配置的ADC1和ADC12通道采样都正常的程序:
void ADC_Configuration(void)
{
        ADC_InitTypeDef ADC_InitStructure;
        ADC_CommonInitTypeDef ADC_CommonInitStructure;
        /* Configure the ADC clock */
        RCC_ADCCLKConfig(RCC_ADC12PLLCLK_Div2|RCC_ADC34PLLCLK_Div2);

        /* Enable ADC1 clock */
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_ADC12|RCC_AHBPeriph_ADC34, ENABLE);
       
        ADC_CommonInitStructure.ADC_Clock             = ADC_Clock_AsynClkMode;
        ADC_CommonInitStructure.ADC_DMAAccessMode   = ADC_DMAAccessMode_Disabled;
        ADC_CommonInitStructure.ADC_DMAMode         = ADC_DMAMode_OneShot;
        ADC_CommonInitStructure.ADC_Mode            =ADC_Mode_Independent;
        ADC_CommonInitStructure.ADC_TwoSamplingDelay= 2;
        ADC_CommonInit(ADC1,&ADC_CommonInitStructure);
       
        ADC_StructInit(&ADC_InitStructure);
        /* Calibration procedure */
        ADC_VoltageRegulatorCmd(ADC1, ENABLE);

        /* Insert delay equal to 10 */
        Delay(10);

        ADC_SelectCalibrationMode(ADC1, ADC_CalibrationMode_Single);ADC_StartCalibration(ADC1);

        while(ADC_GetCalibrationStatus(ADC1) != RESET );

        ADC_InitStructure.ADC_ContinuousConvMode      = ADC_ContinuousConvMode_Enable;
        ADC_InitStructure.ADC_DataAlign               = ADC_DataAlign_Right;
        ADC_InitStructure.ADC_ExternalTrigConvEvent   = ADC_ExternalTrigConvEvent_1;
        ADC_InitStructure.ADC_ExternalTrigEventEdge   = ADC_ExternalTrigEventEdge_None;
        ADC_InitStructure.ADC_NbrOfRegChannel         = 10;
        ADC_InitStructure.ADC_OverrunMode             =   ADC_OverrunMode_Disable;
        ADC_InitStructure.ADC_AutoInjMode             = ADC_AutoInjec_Disable;
        ADC_InitStructure.ADC_Resolution            = ADC_Resolution_12b;
        ADC_Init(ADC1, &ADC_InitStructure);

        /* ADC1 Regular Channel1 Configuration */
        ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_1Cycles5);
        ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 2, ADC_SampleTime_1Cycles5);
        ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 3, ADC_SampleTime_1Cycles5);
        ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 4, ADC_SampleTime_1Cycles5);
        ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 5, ADC_SampleTime_1Cycles5);
        ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 6, ADC_SampleTime_1Cycles5);//PC0
        ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 7, ADC_SampleTime_1Cycles5);//PC1
        ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 8, ADC_SampleTime_1Cycles5);//PC2
        ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 9, ADC_SampleTime_1Cycles5);//PC3
        ADC_RegularChannelConfig(ADC1, ADC_Channel_10,10, ADC_SampleTime_1Cycles5);//PF2
       
        ADC_Cmd(ADC1, ENABLE);             /* Enable ADC1                        */

        DMA1_Configuration();
        ADC_DMAConfig(ADC1,ADC_DMAMode_Circular);
        ADC_DMACmd(ADC1, ENABLE);             /* Enable ADC1's DMA interface      */

        /* Start ADC1 calibaration */
        ADC_StartCalibration(ADC1);
       
        while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_RDY));
        ADC_StartConversion(ADC1);/* Start ADC1 Software Conversion*/
}

void DMA1_Configuration(void)
{
        DMA_InitTypeDef DMA_InitStruct;
        /* ADC Channel configuration */
       
        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
       
        DMA_DeInit(DMA1_Channel1);
        DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->DR); //外设首地址
        DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)&ADC_ConvertedValue0;//自己定义的存储区
        DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;    //数据传输外设到内存
        DMA_InitStruct.DMA_BufferSize = 10;//数据大小10
        DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器地址自动增加地址禁止
        DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;    //内存地址自动增加

        DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //外设数据大小

        DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//内存数据大小,同上
        DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;       //DMA循环传输
        DMA_InitStruct.DMA_Priority = DMA_Priority_High; //高优先级

        DMA_Init(DMA1_Channel1, &DMA_InitStruct);
        DMA_Cmd(DMA1_Channel1, ENABLE);      //使能DMA1_Stream        
}
但就不知道怎么添加其他通道进行采样,ADC2、ADC3、ADC4的一些通道进行采样,希望你能帮我这个小菜鸟{:dizzy:}

jonyzhu 发表于 2014-9-23 11:47:24

30zero 发表于 2014-9-22 22:14
楼主,我的多路采样ADC还是不行我ADC1、ADC2、ADC3、ADC4都要用起来,楼主能教导一下吗?下 ...

需要配置 ADC2、3、4 的 DMA 通道。参见手册v3版本,第184~187页:

wx85105157 发表于 2014-10-7 22:51:31

本帖最后由 wx85105157 于 2014-10-7 22:53 编辑

四ADC交错采样岂不是可以做到单通道最高32M的采样率?

jonyzhu 发表于 2014-10-7 22:55:18

wx85105157 发表于 2014-10-7 22:51
四ADC交错采样岂不是可以做到单通道最高32M的采样率?

DMA 会成为瓶颈的;而且也没有办法控制 ADC34 能够准确的在 ADC12 开始之后的1/4个采样+转换周期开始,所以,放弃吧。

wx85105157 发表于 2014-10-7 23:03:42

jonyzhu 发表于 2014-10-7 22:55
DMA 会成为瓶颈的;而且也没有办法控制 ADC34 能够准确的在 ADC12 开始之后的1/4个采样+转换周期开始, ...

定时器触发做不到么?

TongIC 发表于 2014-12-4 23:09:41

在STM32F103中 ADC_InitStructure.ADC_ScanConvMode = ENABLE ;        //扫描模式用于多通道采集
在STM32F30x中为什么没有看到这句?难道不用配置,直接使用?
在F103中有个Scan的寄存器位,但是在F30x中却没有找到?

jonyzhu 发表于 2014-12-5 15:32:20

TongIC 发表于 2014-12-4 23:09
在STM32F103中 ADC_InitStructure.ADC_ScanConvMode = ENABLE ;        //扫描模式用于多通道采集
在STM32F30x中 ...

ADC_InitStructure.ADC_NbrOfRegChannel= 10;
STM32F3 是通过指定采样通道的数量来设置多通道采样的。ADC_NbrOfRegChannel = 1,就相当于单通道(1个ADC);大于 1,就相当于 STM32F1 的 ADC_InitStructure.ADC_ScanConvMode = ENABLE ;

TongIC 发表于 2014-12-5 21:00:16

jonyzhu 发表于 2014-12-5 15:32
STM32F3 是通过指定采样通道的数量来设置多通道采样的。ADC_NbrOfRegChannel = 1,就相当于单通道(1个A ...

非常感谢,楼主,能说说你是看参考手册的哪个部分有介绍关于《指定采样通道的数量来设置多通道采样的》么?再次感谢!

TongIC 发表于 2014-12-5 21:58:53

jonyzhu 发表于 2014-12-5 15:32
STM32F3 是通过指定采样通道的数量来设置多通道采样的。ADC_NbrOfRegChannel = 1,就相当于单通道(1个A ...

还有一个 ADC_CommonInitStructure.ADC_TwoSamplingDelay= 2; 这个参数具体是做什么用的,能产生什么样的影响呢?

jonyzhu 发表于 2014-12-6 11:15:54

TongIC 发表于 2014-12-5 21:58
还有一个 ADC_CommonInitStructure.ADC_TwoSamplingDelay= 2; 这个参数具体是做什么用的,能产生什么样 ...

决定采样的“均匀”程度。数值是同一 ADC 外设中不同通道的采样时钟间隔,同一外设的不同通道不可以重叠采样。

TongIC 发表于 2014-12-7 14:47:41

jonyzhu 发表于 2014-12-6 11:15
决定采样的“均匀”程度。数值是同一 ADC 外设中不同通道的采样时钟间隔,同一外设的不同通道不可以重叠 ...

比如ADC1通道 1和通道2采集电压,这个值就是当通道1转化完成后等待多少后启动通道2采集?是同一个ADC,这样做的好处是什么?能保证数据采集的稳定正确?谢谢楼主!

wz18th 发表于 2015-4-6 16:00:12

想请教一下楼主,STM32F303的差分ADC该如何配置?
我试着配了一下,采出的数据不对。

void Init_ADC(void)
{
/* Configure the ADC clock */
RCC_ADCCLKConfig(RCC_ADC12PLLCLK_Div2);

/* Enable ADC1 clock */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_ADC12, ENABLE);
   /* ADC Channel configuration */
/* GPIOC Periph clock enable */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);

/* Configure ADC Channel7 as analog input */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
GPIO_Init(GPIOA, &GPIO_InitStructure);

ADC_DeInit(ADC1);

ADC_StructInit(&ADC_InitStructure);

/* Calibration procedure */
ADC_VoltageRegulatorCmd(ADC1, ENABLE);

/* Insert delay equal to 10 祍 */
Delay_systick(10);

ADC_SelectCalibrationMode(ADC1, ADC_CalibrationMode_Differential);
ADC_StartCalibration(ADC1);

while(ADC_GetCalibrationStatus(ADC1) != RESET );
ADC_GetCalibrationValue(ADC1);

ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;                                                                  
ADC_CommonInitStructure.ADC_Clock = ADC_Clock_AsynClkMode;                  
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;            
ADC_CommonInitStructure.ADC_DMAMode = ADC_DMAMode_OneShot;                  
ADC_CommonInitStructure.ADC_TwoSamplingDelay = 0;
ADC_CommonInit(ADC1, &ADC_CommonInitStructure);

ADC_InitStructure.ADC_ContinuousConvMode = ADC_ContinuousConvMode_Disable;
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ExternalTrigConvEvent = ADC_ExternalTrigConvEvent_0;         
ADC_InitStructure.ADC_ExternalTrigEventEdge = ADC_ExternalTrigEventEdge_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_OverrunMode = ADC_OverrunMode_Disable;   
ADC_InitStructure.ADC_AutoInjMode = ADC_AutoInjec_Disable;
ADC_InitStructure.ADC_NbrOfRegChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);

/* ADC1 regular channel7 configuration */
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_7Cycles5);
    /* Select the differetiel mode for Channel 7 */
ADC_SelectDifferentialMode(ADC1, ADC_Channel_1, ENABLE);
ADC_SelectDifferentialMode(ADC1, ADC_Channel_3, ENABLE);
   
/* Enable ADC1 */
ADC_Cmd(ADC1, ENABLE);

/* wait for ADRDY */
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_RDY));

ADC_StartConversion(ADC1);
}

nydxsydt0 发表于 2015-7-9 11:12:42

谢谢分享经验

奋斗的小傲 发表于 2016-6-7 18:42:17

谢谢分享

cpholr1 发表于 2017-3-24 00:43:26

wz18th 发表于 2015-4-6 16:00
想请教一下楼主,STM32F303的差分ADC该如何配置?
我试着配了一下,采出的数据不对。



感谢24楼和楼主。


搞了两天,终于有数据了。

cpholr1 发表于 2017-3-24 00:57:05

楼主整合代码的第12行有错。。。函数是AHB1内容是AHB这个程序难道没有经过实际验证吗?

jonyzhu 发表于 2017-4-12 15:48:35

cpholr1 发表于 2017-3-24 00:57
楼主整合代码的第12行有错。。。函数是AHB1内容是AHB这个程序难道没有经过实际验证吗? ...

应该是 RCC_AHBPeriphClockCmd,而不是 RCC_AHB1PeriphClockCmd。代码是从更大的工程里面挑选出来的,去掉了无关的部分,这样可以把 ADC 这块看得更清楚,挑选摘录代码的时候,应该是出现了笔误。
只是帖子发布时间比较久了,好像已经不能修改了。

eleqian 发表于 2017-7-7 13:54:23

最近也在调f3

eleqian 发表于 2017-7-7 13:56:49

不小心点了发送,楼主成功避过了adc的坑,errata写着用交错模式时不能使用单dma传输,否则会overrun。

jsszdfdn 发表于 2022-4-2 15:02:58

jonyzhu 发表于 2014-9-23 11:47
需要配置 ADC2、3、4 的 DMA 通道。参见手册v3版本,第184~187页:
(引用自14楼)

楼主你好 :有 STM303的编程手册么 ?

cg4900 发表于 2023-11-7 13:18:18

进来学习的,准备用F303来做

TINXPST 发表于 2023-11-8 08:45:09

STM32全系列中,ADC综合性能超过F303有哪几个型号?
页: [1]
查看完整版本: 使用 STM32F303VCT6 的 ADC