单片机论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 312|回复: 1
收起左侧

stm32 ADC实验(DMA方式)

[复制链接]
wan12345 发表于 2019-7-12 13:20 | 显示全部楼层 |阅读模式
笔者之前用STM32的ADC外设通过DMA通道来传输数据,通常有两种方法,一个是直接通过DMA通道传输数据,二是通过定时器触发。相较而言,后者在实际中比较常使用,因为ADC读取传感器数据需要一段时间,通常我们不会让处理器一直让ADC外设读取数据,而是根据ADC的读取时间和实际项目所需来使用定时器触发,有些传感器如室内温度我们不需要经常读取,我们可以200ms读取一次,在STM32处理器中就很方便了,直接通过设置定时器触发达到此效果。如果用的处理器没有这个功能,我们可以通过定时器产生的时隙划分合适的时间,在需要的时间端允许ADC外设读取,该做法也是可行的。

下面,列出主要的配置代码和注意事项:

//ADC配置,红色为注意

void  Adc_Init(void)
{     

ADC_InitTypeDef ADC_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE );

    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;        //两个ADC工作在独立模式
    ADC_InitStructure.ADC_ScanConvMode = ENABLE;            //扫描多通道模式
//    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;        //连续转换模式
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;        // 定时器触发不使能连续
    //禁止外部触发转换模式,软件触发
    //ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;   
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_CC2;   
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;    //AD数据右对齐
    ADC_InitStructure.ADC_NbrOfChannel = 2;                    //顺序进行规则转换的ADC通道的数目
    ADC_Init(ADC1, &ADC_InitStructure);

    //规则采样通道次序与采样时间
        ADC_RegularChannelConfig(ADC1, ADC_Channel_17, 2, ADC_SampleTime_239Cycles5);   
    ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 1, ADC_SampleTime_239Cycles5);   

    ADC_TempSensorVrefintCmd(ENABLE);                         //使能内部温度传感器

    ADC_DMACmd(ADC1, ENABLE);                                 //ADC命令,和DMA关联

    ADC_Cmd(ADC1, ENABLE);                                    //使能ADC      

    ADC_ResetCalibration(ADC1);                                //复位ADC校准寄存器
    while(ADC_GetResetCalibrationStatus(ADC1));                //等待校准寄存器复位完成

    ADC_StartCalibration(ADC1);                                //开始ADC校准
    while(ADC_GetCalibrationStatus(ADC1));                    //等待校准完成

    //ADC_SoftwareStartConvCmd(ADC1, ENABLE);                    //注释掉软件启动AD转换
    ADC_ExternalTrigConvCmd(ADC1, ENABLE);  //使能外部定时器触发  
}   

//DMA配置

void MYDMA_Config(void)
{

          DMA_InitTypeDef  DMA_InitStruct;

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE );

      //DMA init;  Using DMA1 channel 1
      DMA_DeInit(DMA1_Channel1);                                //复位DMA1的第1通道
      DMA_InitStruct.DMA_PeripheralBaseAddr = (u32)&ADC1->DR;    //DMA对应的外设基地址
      DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //转换结果16bits
      DMA_InitStruct.DMA_MemoryBaseAddr = (u32)ADC_TempValue;
      DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;           //DMA的转换模式是SRC模式,外设to内存
      DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;                 //M2M模式禁止,memory to memory
      DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;  //DMA搬运的数据16bits

      //接收一次数据后,目标内存地址后移,用来采集多个数据的
      DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
    //接收一次数据后,设备地址是否后移
      DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

      //转换模式:常用循环缓存模式。如果M2M开启了,则这个模式失效
      //另一种是Normal模式:不循环,仅一次DMA
      //DMA_InitStruct.DMA_Mode  = DMA_Mode_Circular;
    DMA_InitStruct.DMA_Mode  = DMA_Mode_Normal;

      DMA_InitStruct.DMA_Priority = DMA_Priority_High;             //DMA优先级,高
      DMA_InitStruct.DMA_BufferSize = 8;                          //DMA缓存大小
      DMA_Init(DMA1_Channel1,&DMA_InitStruct);

        DMA_ClearITPendingBit(DMA1_IT_TC1);
        DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);                //开启DMA1CH1中断

       DMA_Cmd(DMA1_Channel1, ENABLE);
}

//定时器配置,设置想要的触发频率

void TIM2_Int_Init(u16 arr,u16 psc)
{
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    TIM_OCInitTypeDef TIM_OCInitStructure;
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //时钟使能

    //定时器TIM3初始化
    TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值   
    TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位

    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse = 1000;
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
    //TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); //使能指定的TIM2中断,允许更新中断  不需要中断。是用定时器的PWM触发
    TIM_OC2Init(TIM2, & TIM_OCInitStructure);   
    //中断优先级NVIC设置
    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;  //TIM2中断
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先占优先级0级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
    NVIC_Init(&NVIC_InitStructure);  //初始化NVIC寄存器
    TIM_Cmd(TIM2, ENABLE);  //使能TIMx                     
}

//中断优先级配置

   void NVIC_Configuration(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);                //优先级分组4,所有位均用于抢占优先级

    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);





评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

回复

使用道具 举报

一梦红尘 发表于 2019-7-13 21:11 | 显示全部楼层
不错,写的挺详细
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|单片机论坛 |51黑电子论坛技术交流 QQ 管理员QQ:125739409;技术交流QQ群636986012

Powered by 单片机教程网

快速回复 返回顶部 返回列表