发新帖本帖赏金 20.00元(功能说明)我要提问
123下一页
返回列表
打印
[STM32F1]

一文搞定STM32F103双通道ADC_DMA采集,可拓展多通道

[复制链接]
4176|55
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 呐咯密密 于 2021-2-5 17:23 编辑

#申请原创#@21小跑堂
对于STM32F103来说,它的ADC是12位,一共18个通道,其中16个外部通道,2个内部通道。支持单次,连续以及间断模式扫描。
12位ADC
指的是STM32F103的ADC分辨率具有12位,位数越高采集到的ADC越精准。12位是相对于二进制数来说,也就是“111111111111”,转换为十进制就是4095,其实是0-4095,实际上是4096个数,STM32F103的引脚电压是0-3.3V,12位的ADC就会把0-3.3V切割成4096份。这样转换器得到的ADC值便可以转换为相应电压,设转换器采集到的ADC值为x,实际所求电压为y。那么公式为:y=x/4096*3.3V。
16个外部通道:
f103的芯片上有16个引脚是接到模拟电压上可以进行电压检测的,这16个通道会分给3个转换器,这三个转换器是独立的,也就是我们常见的ADC1,ADC2,ADC3。引脚和通道的对应关系可通过手册或者芯片引脚定义查询。

因为手册的引脚定义表太大,我这里就给出一个开发板原理图上的芯片引脚图。在图中可以看到哪个引脚有ADC通道。
这里挑一个典型进行说明:
比如PC0 标注的是ADC123_IN10,说明该引脚可以作为ADC引脚,而123说明该引脚可以被ADC1.ADC2,ADC3三个转化器共用。10对应上面所说的18个通道中的10通道。
ADC的使用:
我在使用该应用的时候是因为项目需要有一个有线遥控器连接控制板,遥控器上按键很多,如果采用外部中断或者按键扫描这种模式,那么每一个按键都需要连接控制板,造成IO口资源的浪费,同时连接线也会更粗,更贵。于是我们采用双排按键,每一排按键对应一个ADC的IO口,根据每个按键按下的电压不同来判断哪个按键被按下。
废话到此结束,让我们开始吧:
我们采用固件库的方式编写代码,那么就先初始化ADC的端口:
 GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA ,ENABLE);
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
        GPIO_Init(GPIOA, &GPIO_InitStructure);

        GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_5;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
        GPIO_Init(GPIOA, &GPIO_InitStructure);


首先还是走老路,初始化所用的GPIO,此次所用的是PA4和PA5。通过手册可以看到这两个口对应的是ADC1和ADC2,对应通道是通道4和通道5。

于是选择ADC1的通道4和通道5对ADC进行初始化:
ADC_InitTypeDef ADC_InitStructure;        
RCC_APB2PeriphClockCmd( RCC_APB2Periph_ADC1 ,ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);//12M  最大14M
 ADC_DeInit(ADC1);  //将外设 ADC1 的全部寄存器重设为缺省值
        ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
        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 = 2;//扫描通道数
        ADC_Init(ADC1, &ADC_InitStructure);
        /* ADC1 regular channel10 configuration */
       
        ADC_RegularChannelConfig(ADC1, ADC_Channel_4,1, ADC_SampleTime_71Cycles5);
        ADC_RegularChannelConfig(ADC1, ADC_Channel_5,2, ADC_SampleTime_71Cycles5);          
                  
        /* Enable ADC1 */
        ADC_Cmd(ADC1, ENABLE);
        ADC_DMACmd(ADC1, ENABLE);
        /* Enable ADC1 reset calibaration register */              
        ADC_ResetCalibration(ADC1);          //复位指定的ADC1的校准寄存器           
        /* Check the end of ADC1 reset calibration register */           
        while(ADC_GetResetCalibrationStatus(ADC1));//获取ADC1复位校准寄存器的状态,设置状态则等待
        /* Start ADC1 calibaration */           
        ADC_StartCalibration(ADC1);                //开始指定ADC1的校准状态         
        /* Check the end of ADC1 calibration */         
        while(ADC_GetCalibrationStatus(ADC1));     //获取指定ADC1的校准程序,设置状态则等待
        /* Start ADC1 Software Conversion */
        ADC_SoftwareStartConvCmd(ADC1, ENABLE);
       
        ADC_SoftwareStartConvCmd(ADC1, ENABLE);
        DMA_Cmd(DMA1_Channel1, ENABLE);  //启动DMA通道


参数 ADC_Mode :是用来设置 ADC 的模式  ,ADC的模式很多(如下图),这里选择独立模式:ADC_Mode_Independent

参数ADC_ScanConvMode :规定了模数转换工作在扫描模式(多通道)还是单次(单通道)模式。可以设置这个参数为 ENABLE 或者 DISABLE。
参数ADC_ContinuousConvMode :规定了模数转换工作在连续还是单次模式。可以设置这个参数为 ENABLE 或者 DISABLE。
参数ADC_ExternalTrigConv :定义了使用外部触发来启动规则通道的模数转换 ,可取值如下图

参数ADC_DataAlign :规定了 ADC 数据向左边对齐还是向右边对齐。

参数ADC_NbreOfChannel :规定了顺序进行规则转换的 ADC 通道的数目 。我这里只用了双通道,所以为2.


函数ADC_RegularChannelConfig():这一步比较重要,上面我们初始化了ADC,但是并未指定引脚,也就是规则组通道,这里设置有规则组通道和转化顺序以及采样时间。根据上面所说,我们选择ADC1,通道4和5,顺序是通道4第一个采样,通道5第二个采样。ADC_SampleTime 设定了通道的 ADC 采样时间,可选参数如下:

DMA设置:
 DMA_InitTypeDef DMA_InitStructure;

        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//使能DMA时钟
        DMA_DeInit(DMA1_Channel1);
        DMA_InitStructure.DMA_PeripheralBaseAddr =(u32)&ADC1->DR;  //DMA外设ADC基地
        DMA_InitStructure.DMA_MemoryBaseAddr =(u32)&ADCConvertedValue;//内存地址
        DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//dma传输方向单向
        DMA_InitStructure.DMA_BufferSize = 100*2;//设置DMA在传输时缓冲区的长度 word
        DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//设置DMA的外设递增模式,一个外设
        DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//设置DMA的内存递增模式,
        DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//外设数据字长
        DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//内存数据字长
        DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//设置DMA的传输模式:连续不断的循环模式
        DMA_InitStructure.DMA_Priority = DMA_Priority_High;//设置DMA的优先级别
        DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//设置DMA的2个memory中的变量互相访问
        DMA_Init(DMA1_Channel1, &DMA_InitStructure);
       
        DMA_Cmd(DMA1_Channel1, ENABLE);

因为我在代码中做了备注,这里踢几个人比较重要的点:
参数DMA_BufferSize :设置DMA在传输时缓冲区的长度 ,这里要提一下我的数据储存数组u16 ADCConvertedValue[100][2];因为采用两个通道,所以需要两个缓冲保存数据,100*2=200,DMA在运行是就会采集200个ADC值保存在数组中,100个通道4的值,100个通道5的值。取值处理会在后面介绍。
此项设置完之后DMA就回不停地采集这两个ADC通道中的ADC值并保存在数组中。我们只需要在需要的时候读取数组中的值便可。
读取ADC值并做处理
   int sum;
                int sum1=0;
                int sum2=0;
        
                int value1[10];
                int value2[10];
                int isSorted,temp1,temp2,k,p;
                float a;
                int i,j,c;
                float ADC_Value[2];//用来保存经过转换得到的电压值
        
     for(c=0;c<10;c++){
                        for(i=0;i<2;i++)
                        {
                                sum=0;
                                for(j=0;j<100;j++)
                                {
                                        sum+=ADCConvertedValue[j][i];
                                }
                                ADC_Value[i]=(float)sum*3.3/4096/100;//求平均值并转换成电压值

                        }
                        value1[c]=(int)(ADC_Value[0]*100);//4通道电压值扩大100
                        value2[c]=(int)(ADC_Value[1]*100);//5通道电压值扩大100
                }
                        



使用特权

评论回复

打赏榜单

21小跑堂 打赏了 20.00 元 2021-02-20
理由:恭喜通过原创文章审核!请多多加油哦!

评论
鸡蛋鸭蛋荷包蛋 2023-2-6 16:56 回复TA
以前没搞明白ADC的 扫描模式是怎么应用的,看了这篇介绍,瞬间就明白了,很不错,楼主加油 
13838345631 2021-3-8 11:36 回复TA
不错,学到了 
沙发
elephant00| | 2021-2-5 17:21 | 只看该作者
很厉害的样子,支持一下

使用特权

评论回复
板凳
caigang13| | 2021-2-5 17:59 | 只看该作者
不错,学习了。

使用特权

评论回复
地板
gygp| | 2021-2-9 21:44 | 只看该作者
                     

使用特权

评论回复
5
Daisyduxy| | 2021-3-2 09:13 | 只看该作者
学习学习

使用特权

评论回复
6
ETjason| | 2021-3-2 09:23 | 只看该作者
总结的不错

使用特权

评论回复
7
两只袜子| | 2021-3-2 09:26 | 只看该作者
总结的不错,学习学习

使用特权

评论回复
8
磨砂| | 2021-3-2 13:55 | 只看该作者
图片解释的很详细啊

使用特权

评论回复
9
晓伍| | 2021-3-2 13:57 | 只看该作者
dma的内存可以设置成递减模式吗

使用特权

评论回复
10
八层楼| | 2021-3-2 14:00 | 只看该作者
最多一次多少通道啊

使用特权

评论回复
11
观海| | 2021-3-2 14:00 | 只看该作者
16个都可用吗

使用特权

评论回复
12
guanjiaer| | 2021-3-2 14:01 | 只看该作者
总结的非常到位啊

使用特权

评论回复
13
17883657069| | 2021-3-3 09:16 | 只看该作者
谢谢楼主分享

使用特权

评论回复
14
amina22| | 2022-5-25 16:40 | 只看该作者

使用特权

评论回复
15
kkzz| | 2022-5-28 16:44 | 只看该作者
ADC_DMA配置简单多了。  

使用特权

评论回复
16
sesefadou| | 2022-5-29 09:08 | 只看该作者
STM32F103支持几个通道的转换呢?

使用特权

评论回复
17
jonas222| | 2022-5-29 10:19 | 只看该作者
最高的采样率可以到多大?

使用特权

评论回复
18
xiaoyaozt| | 2022-5-29 11:56 | 只看该作者
DMA采样可以,效果很好。

使用特权

评论回复
19
burgessmaggie| | 2022-5-29 17:11 | 只看该作者
多通道如何dma?

使用特权

评论回复
20
quickman| | 2022-5-29 18:03 | 只看该作者
ADC不同通道之间会存在串扰。

使用特权

评论回复
发新帖 本帖赏金 20.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:苏州澜宭自动化科技嵌入式工程师
简介:本人从事磁编码器研发工作,负责开发2500线增量式磁编码器以及17位、23位绝对值式磁编码器,拥有多年嵌入式开发经验,精通STM32、GD32、N32等多种品牌单片机,熟练使用单片机各种外设。

493

主题

3877

帖子

47

粉丝