【STM32】实现ADC等间隔采样:使用定时器触发ADC转换,并使用DMA进行数据搬运

1,319次阅读
没有评论

共计 4982 个字符,预计需要花费 13 分钟才能阅读完成。

进行定时采样的方法大致有三种:
1、使用定时器中断每隔一定时间进行 ADC 转换,这样每次都必须读 ADC 的数据寄存器,如果采样率很低到时可以用。

2、把 ADC 设置成连续转换模式,同时对应的 DMA 通道开启循环模式,这样 ADC 就一直在进行数据采集然后通过 DMA 把数据搬运至内存(一个 U16 变量)。
然后再加一个定时中断,定时读取那个 U16 变量,存入你的采样数组。

3、使用定时器触发 ADC 转换的功能,然后使用 DMA 进行数据的搬运。这样只要设置好定时器的触发间隔,就能实现 ADC 定时采样转换的功能,
然后可以在程序的死循环中一直检测 DMA 转换完成标志,然后进行数据的读取,或者使能 DMA 转换完成中断,这样每次转换完成就会产生中断。

第三种方法较于第二种方法的好处在于它由定时器触发 ADC 采样,那样 ADC 的采样间隔会更加均匀,方便闭环控制、数据滤波等操作。
本文示意采用第三种方法的配置过程。


// 一次采集多少数据
u16 ADC_BUFFER[BufferSize];
u8 ADC_DMA_FINISHED;

void TIM5_Init(void)
{
    // 设置触发 ADC 的定时器 5 通道 1
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_OCInitTypeDef TIM_OCInitStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);

    TIM_TimeBaseStructure.TIM_Period=999;// 设置 1ms 一次 TIM5 比较的周期 = 1 KHz
    TIM_TimeBaseStructure.TIM_Prescaler=83;// 系统主频 84M,这里分频 83,相当于 1000K 的定时器 10 时钟
    TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM5,&TIM_TimeBaseStructure);

    TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;// 下面详细说明
    TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;//TIM_OutputState_Disable;
    TIM_OCInitStructure.TIM_Pulse=499;
    TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_Low;// 如果是 PWM1 要为 Low,PWM2 则为 High
    TIM_OC1Init(TIM5,&TIM_OCInitStructure); // channel 1

    TIM_SelectOutputTrigger(TIM5, TIM_TRGOSource_OC1Ref);  // 选择 TIM5 的 OC1 事件更新为触发源
}
// 初始化 ADC    with DMA                                                           
void  ADC_DMA_Init(void)
{    
  GPIO_InitTypeDef  GPIO_InitStructure;
    ADC_CommonInitTypeDef ADC_CommonInitStructure;
    ADC_InitTypeDef       ADC_InitStructure;
    DMA_InitTypeDef  DMA_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    TIM5_Init();
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);// 使能 GPIOC 时钟
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); // 使能 ADC1 时钟
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);//DMA2 时钟使能 

  // 先初始化 ADC1 通道 10 IO 口
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;//PC0 通道 10
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;// 模拟输入
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;// 不带上下拉
  GPIO_Init(GPIOC, &GPIO_InitStructure);// 初始化  

    RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,ENABLE);   //ADC1 复位
    RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,DISABLE);    // 复位结束   

  ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;// 独立模式
  ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;// 两个采样阶段之间的延迟 5 个时钟  47.6 ns*5 = 238.1 ns
  ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; //DMA 失能
  ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4;// 预分频 4 分频。ADCCLK=PCLK2/4=84/4=21Mhz,ADC 时钟最好不要超过 36Mhz 
  ADC_CommonInit(&ADC_CommonInitStructure);// 初始化

  ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;//12 位模式
  ADC_InitStructure.ADC_ScanConvMode = DISABLE;// 非扫描模式  
  ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;// 关闭连续转换;如果开启,后续的转换就会永不停歇(除非 CONT 清 0)ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;// 上升沿触发检测
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T5_CC1;
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;// 右对齐   
  ADC_InitStructure.ADC_NbrOfConversion = 1;// 1 个转换在规则序列中 也就是只转换规则序列 1 
  ADC_Init(ADC1, &ADC_InitStructure);//ADC 初始化

    ADC_RegularChannelConfig(ADC1,ADC_Channel_10,1,ADC_SampleTime_480Cycles); // 不间断连续(最大)采样间隔大约 23.5 us -->42KHz

    DMA_DeInit(DMA2_Stream0);
    while (DMA_GetCmdStatus(DMA2_Stream0) != DISABLE){}// 等待 DMA 可配置 

  /* 配置 DMA Stream */
  DMA_InitStructure.DMA_Channel = DMA_Channel_0;  // 通道选择
  DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&ADC1->DR;//DMA 外设地址
  DMA_InitStructure.DMA_Memory0BaseAddr = (u32)ADC_BUFFER;//DMA 存储器 0 地址
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;// 外设到存储器模式
  DMA_InitStructure.DMA_BufferSize = BufferSize;// 数据传输量 
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;// 外设非增量模式
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;// 存储器增量模式
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;// 外设数据长度:16 位
  DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_HalfWord;// 存储器数据长度:16 位
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;// 不使用循环模式,普通模式
  DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;// 中等优先级
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;         
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;// 存储器突发单次传输
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;// 外设突发单次传输
  DMA_Init(DMA2_Stream0, &DMA_InitStructure);// 初始化 DMA Stream

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

    DMA_ITConfig(DMA2_Stream0,DMA_IT_TC,ENABLE);        // 使能传输完成中断

}                 

void Start_Sample()
{TIM_Cmd(TIM5,ENABLE);
        ADC_Cmd(ADC1, ENABLE);// 打开 AD 转换器 失能 ADC_ADON=1
        ADC_DMACmd(ADC1,ENABLE);
        DMA_Cmd(DMA2_Stream0, ENABLE); // 使能 DMA
        ADC_DMA_FINISHED = 0;
        //ADC_SoftwareStartConv(ADC1);
}
void Stop_Sample()
{TIM_Cmd(TIM5,DISABLE);
        ADC_Cmd(ADC1, DISABLE);// 关闭 AD 转换器    失能 ADC_ADON=0
        ADC_DMACmd(ADC1,DISABLE);
        DMA_Cmd(DMA2_Stream0, DISABLE); // 失能 DMA
      ADC_DMA_FINISHED = 1;
}
void DMA2_Stream0_IRQHandler(void)
{if(DMA_GetITStatus(DMA2_Stream0,DMA_IT_TCIF0)!=RESET)
    {DMA_ClearITPendingBit(DMA2_Stream0,DMA_IT_TCIF0);
        Stop_Sample();}

}

void ADC2Voltage(u16* adc,float*vol)
{
    u16 i;

    for(i=0;i
正文完
 0
评论(没有评论)