打印
[应用相关]

STM32 多通道ADC采集详解(DMA模式和非DMA模式)

[复制链接]
1029|32
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
1.采集到的值如何转化计算?

  STM32系列芯片大都是12位只有少部分是16位的,如:F373芯片。

  12位分辨率意味着我们采集电压的精度可以达到:Vref / 4096。

   采集电压= Vref * ADC_DR / 4096;

   VREF:参考电压

   ADC_DR:读取到ADC数据寄存器的值


使用特权

评论回复
沙发
hehhehee|  楼主 | 2022-1-25 23:54 | 只看该作者
2.什么是通道扫描模式?

扫描模式用于多通道采集时,一轮采集为根据通道设定的顺序依次采集每个开启的通道。但要注意的是每个通道采集到的值都是存放在寄存器ADCx-> DR中,而只有当一轮采集完即所有设定的通道都采集完后采集完成标志才会被置位,所以不使用DMA传输方式的时候会出现采集到的数据被覆盖的现象。

使用特权

评论回复
板凳
hehhehee|  楼主 | 2022-1-25 23:55 | 只看该作者
3.采集周期,转换周期

转换时间=采样时间+12.5个时钟周期;

采样时间根据ADC_RegularChannelConfig()配置。

使用特权

评论回复
地板
hehhehee|  楼主 | 2022-1-25 23:56 | 只看该作者
4.连续转换模式

将所有开启的通道采集转换完一轮后自动开启下一轮的采集转换。

使用特权

评论回复
5
hehhehee|  楼主 | 2022-1-25 23:56 | 只看该作者
5.一次采集完成后会触发中断吗?

ADC使能中断的话当通道规则组都采集转化完成一轮后会产生中断ADC1_2_IRQHandler(void)

使用特权

评论回复
6
hehhehee|  楼主 | 2022-1-25 23:57 | 只看该作者
6.规则组跟注入组

规则组是程序按照配置规则转换,注入组是根据外部触发中断来转换,在执行规则通道组扫描转换时,如有例外处理则可启用注入通道组的转换。

使用特权

评论回复
7
hehhehee|  楼主 | 2022-1-25 23:57 | 只看该作者
DMA相关问题:
1.什么是DMA循环模式?

完成一轮DMA传输后自动开启下一轮传输。

使用特权

评论回复
8
hehhehee|  楼主 | 2022-1-25 23:58 | 只看该作者
2.怎么算一轮DMA传输完成?

使用特权

评论回复
9
hehhehee|  楼主 | 2022-1-25 23:58 | 只看该作者

使用特权

评论回复
10
hehhehee|  楼主 | 2022-1-27 17:56 | 只看该作者

使用特权

评论回复
11
hehhehee|  楼主 | 2022-1-27 17:58 | 只看该作者
3.ADC怎么触发DMA?

传输请求函数触发

例如:

ADC_DMACmd(ADC1,Enable);

使用特权

评论回复
12
hehhehee|  楼主 | 2022-1-27 17:59 | 只看该作者
4.DMA有哪些传输方向?

内存  到  内存

内存  到  外设

外设  到  内存

使用特权

评论回复
13
hehhehee|  楼主 | 2022-1-27 18:00 | 只看该作者
5.DMA传输会触发哪些中断?

使用特权

评论回复
14
hehhehee|  楼主 | 2022-1-27 21:57 | 只看该作者
以下为ADC多通道采集(非DMA模式和DMA模式)的示例

使用特权

评论回复
15
hehhehee|  楼主 | 2022-1-27 21:59 | 只看该作者
非DMA模式(配置16个通道)

void adc_gpio_init(void)
{
    GPIO_InitTypeDef t_gpio;
   
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE);//使能 GPIOA GPIOB GPIOC 时钟
    t_gpio.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;//GPIOA
    t_gpio.GPIO_Mode=GPIO_Mode_AIN;//模拟输入模式
    GPIO_Init(GPIOA,&t_gpio);//初始化GPIOA
   
    t_gpio.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1;//GPIOB
    GPIO_Init(GPIOB,&t_gpio);//初始化GPIOB
   
    t_gpio.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;//GPIOC  
    GPIO_Init(GPIOC,&t_gpio);//初始化GPIOC      
}


void adc_init(void)
{
    ADC_InitTypeDef t_adc;
   
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);//使能ADC1时钟
   
    t_adc.ADC_Mode = ADC_Mode_Independent;               //独立模式:ADC1与ADC2独立
    t_adc.ADC_ScanConvMode = DISABLE;                      //禁用通道扫描
    t_adc.ADC_ContinuousConvMode = DISABLE;                //禁用连续转换
    t_adc.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//不使用外部触发
    t_adc.ADC_DataAlign = ADC_DataAlign_Right;             //数据位右对齐
    t_adc.ADC_NbrOfChannel = 1;                            //转换通道数为1
    ADC_Init(ADC1,&t_adc);                                 //初始化ADC1
   
    RCC_ADCCLKConfig(RCC_PCLK2_Div8);//配置ADC时钟为PCLK2的8分频

    ADC_Cmd(ADC1,ENABLE); //使能ADC1
        
    ADC_ResetCalibration(ADC1);         
    while(ADC_GetResetCalibrationStatus(ADC1));     
    ADC_StartCalibration(ADC1); //校准
    while(ADC_GetCalibrationStatus(ADC1)){};
}

uint16_t read_adc_value(uint8_t ch)
{
    uint16_t adc_value = 0;
   
    ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_7Cycles5 );   //设置ADC1通道ch的转换周期为7.5个采样周期,采样次序为1

    ADC_SoftwareStartConvCmd(ADC1, ENABLE);//使能软件触发
   
    while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC )){};//等待转换完成
        
    adc_value = ADC_GetConversionValue(ADC1); //获取转换值
  
    return adc_value;
}

使用特权

评论回复
16
hehhehee|  楼主 | 2022-1-27 22:00 | 只看该作者
DMA模式


ADC配置(配置4个通道)

使用特权

评论回复
17
hehhehee|  楼主 | 2022-1-27 22:03 | 只看该作者
void ADC_Multichannel_Config(void)
{
    ADC_InitTypeDef  ADC_InitStructure;
   
    ADC_DeInit(ADC1);  //将外设 ADC1 的全部寄存器重设为缺省值
   
    /* ADC1 configuration ------------------------------------------------------*/
   
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//ADC工作模式:ADC1和ADC2工作在独立模式
    ADC_InitStructure.ADC_ScanConvMode =ENABLE; //ADC为扫描模式
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //ADC为连续转换模式
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //外部触发转换关闭
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐
    ADC_InitStructure.ADC_NbrOfChannel = 4; //顺序进行规则转换的ADC通道的数目
    ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
   
    /* ADC1 regular channel11 configuration */
    //设置指定ADC的规则组通道,设置它们的转化顺序和采样时间
    //ADC1,ADC通道x,规则采样顺序值为y,采样时间为239.5周期
   
    ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5 );
    ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_239Cycles5 );
    ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 3, ADC_SampleTime_239Cycles5 );
    ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 4, ADC_SampleTime_239Cycles5);
    // 开启ADC的DMA支持(要实现DMA功能,还需独立配置DMA通道等参数)     
    ADC_DMACmd(ADC1, ENABLE);
    /* Enable ADC1 */   
    ADC_Cmd(ADC1, ENABLE);    //使能指定的ADC1
     /* Enable ADC1 reset calibaration register */      
    ADC_ResetCalibration(ADC1);  //复位指定的ADC1的校准寄存器
   
    /* Enable ADC1 reset calibaration register */      
    ADC_ResetCalibration(ADC1);  //复位指定的ADC1的校准寄存器
    /* Start ADC1 calibaration */
    ADC_StartCalibration(ADC1);  //开始指定ADC1的校准状态
    /* Check the end of ADC1 calibration */
    while(ADC_GetCalibrationStatus(ADC1));
    //获取指定ADC1的校准程序,设置状态则等待
}

使用特权

评论回复
18
hehhehee|  楼主 | 2022-1-27 22:04 | 只看该作者
DMA配置(ADC1传输到内存变量ADC_Value)

void DMA_Configuration(void) 
{
    /* ADC1  DMA1 Channel Config */  
    DMA_InitTypeDef DMA_InitStructure;
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA时钟

    DMA_DeInit(DMA1_Channel1);   //将DMA的通道1寄存器重设为缺省值:ADC1连接DMA通道1
    DMA_InitStructure.DMA_PeripheralBaseAddr =  (u32)&ADC1->DR;  //DMA外设ADC基地址
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC_Value;      //DMA内存基地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;  //内存作为数据传输的目的地
    DMA_InitStructure.DMA_BufferSize = N*4;  //此值为完整一轮DMA传输的次数
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //外设地址不变
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  //内存地址递增
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //外设数据位宽度16位,即DMA传输尺寸
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //数据宽度16位
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;  //工作在循环缓存模式,一轮结束后自动开始下轮传输
    DMA_InitStructure.DMA_Priority = DMA_Priority_High; //DMA通道 x拥有高优先级
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //DMA通道x禁止内存到内存
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);  //根据DMA_InitStruct中参数DMA通道
}

使用特权

评论回复
19
hehhehee|  楼主 | 2022-1-27 22:06 | 只看该作者
DMA配置(ADC1传输到内存变量ADC_Value)
void DMA_Configuration(void) 
{
    /* ADC1  DMA1 Channel Config */  
    DMA_InitTypeDef DMA_InitStructure;
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA时钟

    DMA_DeInit(DMA1_Channel1);   //将DMA的通道1寄存器重设为缺省值:ADC1连接DMA通道1
    DMA_InitStructure.DMA_PeripheralBaseAddr =  (u32)&ADC1->DR;  //DMA外设ADC基地址
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC_Value;      //DMA内存基地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;  //内存作为数据传输的目的地
    DMA_InitStructure.DMA_BufferSize = N*4;  //此值为完整一轮DMA传输的次数
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //外设地址不变
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  //内存地址递增
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //外设数据位宽度16位,即DMA传输尺寸
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //数据宽度16位
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;  //工作在循环缓存模式,一轮结束后自动开始下轮传输
    DMA_InitStructure.DMA_Priority = DMA_Priority_High; //DMA通道 x拥有高优先级
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //DMA通道x禁止内存到内存
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);  //根据DMA_InitStruct中参数DMA通道
}

使用特权

评论回复
20
逢dududu必shu| | 2022-1-27 22:21 | 只看该作者
源码下载 https://download.csdn.net/download/wangluo515/14158061
改程序已经在正点原子开发板上验证;

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

70

主题

837

帖子

0

粉丝