打印
[Cortex-M0技术交流]

STM32F051x ADC+TIM+DMA详解!世上最详!

[复制链接]
17553|15
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 xiaxuepiaopiao 于 2013-8-5 21:35 编辑

       闲来无事,玩玩去年申请过来的STM32f0Discovery,决定试试 ADC+TIM+DMA方式。具体方法是每隔10s钟TIM触发一次AD转换,之后从DMA读走数据。讲讲ADC中最重要编程的部分,看看下面这张ADC结构图:

      ADC可选择外部触发,有5个信号,TIM1_TRGO,TIM1_CC4,...TIM15_TRGO,TIM1_CC4容易理解,就是TIM1输出捕获4通道触发(PWM模式下)。那TRGO是什么呢?Trigger Output,触发输出,定时器均有Trigger Output信号(自己看看定时器的结构图),以下事情可以触发TRGO信号:(摘自stm32f0xx.tim.c   TIM_SelectOutputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_TRGOSource)函数)
  *   - For all TIMx
  *            @arg TIM_TRGOSource_Reset:  The UG bit in the TIM_EGR register is used as the trigger output (TRGO).
  *            @arg TIM_TRGOSource_Enable: The Counter Enable CEN is used as the trigger output (TRGO).
  *            @arg TIM_TRGOSource_Update: The update event is selected as the trigger output (TRGO).
  *
  *   - For all TIMx except TIM6
  *            @arg TIM_TRGOSource_OC1: The trigger output sends a positive pulse when the CC1IF flag
  *                                     is to be set, as soon as a capture or compare match occurs (TRGO).
  *            @arg TIM_TRGOSource_OC1Ref: OC1REF signal is used as the trigger output (TRGO).
  *            @arg TIM_TRGOSource_OC2Ref: OC2REF signal is used as the trigger output (TRGO).
  *            @arg TIM_TRGOSource_OC3Ref: OC3REF signal is used as the trigger output (TRGO).
  *            @arg TIM_TRGOSource_OC4Ref: OC4REF signal is used as the trigger output (TRGO).
即复位、使能、输出捕获均能引起TRGO事件。所以若想利用TIM的TRGO引发ADC转换,记得必须加上:
TIM_SelectOutputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_TRGOSource)函数选择定时器的TRGO。当如果你想利用TIM1_CC4触发,就不必使用该函数,因为CC4不在TRGO行列内!
       上面有TIM_TRGOSource_OC1和TIM_TRGOSource_OC1Ref,OCxRef和OCx有啥区别,这里,有许多PWM模式知识要将。看看下面这张结构图:

      OCxRef参看信号由PWM模式决定,OCxRef再经过极性选择得到OCx,OCx必须被使能输出才得到PWM。PWM有模式1和模式2。PWM mode1为:当计数值cnt<预定值ccr时,输出有效电平(可简单视为高电平),cnt=ccr时,触发PWM,输出无效电平(低电平);mode2相反,cnt<ccr,输出高,cnt>ccr,输出低;必须说说有两种计数模式,向上计数和向下计数;向上计数,从0向上递增;向下计数,从0xfff向下递减。所以若选择PWM模式1且向上计数,输出极性的变化必定是从高到低,即OCxRef是从有效电平到无效电平跳变。其他组合方式自己算算。极性选择,若选择TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High,表明输出OCx与OCxRef同相,若为Low表明反向。总输出极性=PWM模式选择 && PWM计数方式 && 极性选择。
      讲程序,TIM15 10s触发一次ADC有3种方法。1法:10s进入TIM中断,在中断中软件触发ADC。技术含量低,不采用,效率也低。2法:定时器配置为10s计时,ADC配置为ADC_ExternalTrigConv_T15_TRGO,TIM15的TRGO选择为TIM_TRGOSource_Update(即计数溢出事件);3法:TIM15配置为PWM,周期10s,向上计数,PWM pulse值设定为1,ADC配置为ADC_ExternalTrigConv_T15_TRGO,TIM15的TRGO选择为TIM_TRGOSource_OC1。
     2法如下:
(1)GPIO设置
void GPIOConfig()
{   
  GPIO_InitTypeDef        GPIO_InitStructure;
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);//使能外设GPIO时钟
  GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_0 |GPIO_Pin_1;//ADC_CH1 ADC_CH2
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速率
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;//模式为模拟输入输出
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//上拉设置
  GPIO_Init(GPIOA,&GPIO_InitStructure);
}
(2)ADC配置
void ADCConfig()
{
  ADC_InitTypeDef        ADC_InitStructure;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
  
  ADC_DeInit(ADC1);
  ADC_StructInit(&ADC_InitStructure);
  ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right;//右对齐
  ADC_InitStructure.ADC_ExternalTrigConvEdge=ADC_ExternalTrigConvEdge_Rising;//上升沿触发
  ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_T15_TRGO;//TIM15触发
// ADC_InitStructure.ADC_ExternalTrigConv =  ADC_ExternalTrigConv_T1_CC4;//选择TIM1 Compare&Capture4 作为触发源
  ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; //12位精度
  ADC_InitStructure.ADC_ContinuousConvMode=DISABLE;//连续模式禁止 要由定时器触发
  ADC_InitStructure.ADC_ScanDirection = ADC_ScanDirection_Upward; //CH0-18
  ADC_Init(ADC1, &ADC_InitStructure);
  
  ADC_OverrunModeCmd(ADC1,ENABLE);//数据覆盖方式,只保留最新的转换数据
  ADC_ChannelConfig(ADC1,ADC_Channel_0|ADC_Channel_1,ADC_SampleTime_239_5Cycles);
  ADC_GetCalibrationFactor(ADC1);        // 开始ADC校准
  ADC_Cmd(ADC1,ENABLE);
  while(ADC_GetFlagStatus(ADC1, ADC_FLAG_ADEN)==RESET);   /* 等待ADC准备好 */
  
  ADC_DMACmd(ADC1,ENABLE);//使能DMA
  ADC_DMARequestModeConfig(ADC1,ADC_DMAMode_Circular);//DMA循环模式
}
(3)TIM15配置
void TIM15Config()
{  
  //(1)选择TIM15 Update事件作为触发源(计数器溢出)
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM15, ENABLE);
//配置基础
  TIM_DeInit(TIM15);
  TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
  TIM_TimeBaseStructure.TIM_ClockDivision=0;//48M
  TIM_TimeBaseStructure.TIM_Prescaler=48000;//48k 1Tick=1ms
  TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
  TIM_TimeBaseStructure.TIM_Period=1000*10;//10s周期
  TIM_TimeBaseStructure.TIM_RepetitionCounter=0;//发生RepetitionCounter+1次溢出事件后中断
  TIM_TimeBaseInit(TIM15,&TIM_TimeBaseStructure);
  TIM_SelectOutputTrigger(TIM15,TIM_TRGOSource_Update);//TIM15的Update事件作为外部TRGO
  TIM_ClearFlag(TIM15,TIM_FLAG_Update); //防止一上电进入中断 貌似可以不用??
  TIM_Cmd(TIM15,ENABLE);//使能TIM3
}
(4)DMA配置
void DMAConfig()
{
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1 , ENABLE);//使能DMA时钟
  DMA_InitTypeDef        DMA_InitStructure;
  DMA_DeInit(DMA1_Channel1);
  DMA_InitStructure.DMA_PeripheralBaseAddr = (INT32U)&(ADC1->DR);//外设基地址 ADC1数据寄存器地址
  DMA_InitStructure.DMA_MemoryBaseAddr =(INT32U)ADCResult;//内存基地址:数组ADCResult地址
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//方向:外设为源地址 即外设-》内存
  DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE;//BUFFER_SIZE是宏定义,为2
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//DMA外设地址不自加
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
  DMA_Init(DMA1_Channel1, &DMA_InitStructure);
  /* DMA1 Channel1 enable */
  DMA_Cmd(DMA1_Channel1, ENABLE);
}
(5)main函数
#define BUFFER_SIZE 2
INT16U ADCResult[BUFFER_SIZE];
INT16U ADC1ConvertedValue[BUFFER_SIZE];
void main()
{
  int i;
  GPIOConfig();
  ADCConfig();
  DMAConfig();
  TIM15Config();
  ADC_StartOfConversion(ADC1);
  //A conversion either starts immediately (software trigger configuration)or once a hardware trigger event
  //  occurs (hardware trigger configuration).
  
  while (1)
  {
    while((DMA_GetFlagStatus(DMA1_FLAG_TC1)) == RESET );
    DMA_ClearFlag(DMA1_FLAG_TC1);
    for(i=0;i<BUFFER_SIZE;i++)
    {
      ADC1ConvertedValue=ADCResult*3000/0x0fff;
    }
    asm("nop");  
  }
}
ADC_StartOfConversion(ADC1)必须加入,看看注释就知道以及本帖图一ADC结构图就知道。
#################################################################
3法程序如下:
只有TIM15配置不同,多了PWM配置。
void TIM15Config()
{  
   //(2)选择T15 TIM_TRGOSource_OC1 作为触发
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM15, ENABLE);
  
  TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
  TIM_OCInitTypeDef       TIM_OCInitStructure;
   
  TIM_DeInit(TIM15);
  TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
  TIM_OCStructInit(&TIM_OCInitStructure);  
  // Time base configuration /
  TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;  //48M
  TIM_TimeBaseStructure.TIM_Prescaler = 48000; //48k 1Tick=1ms
  TIM_TimeBaseStructure.TIM_Period = 1000*10;//周期10s
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
  TIM_TimeBaseInit(TIM15, &TIM_TimeBaseStructure);
  TIM_SelectOutputTrigger(TIM15,TIM_TRGOSource_OC1);//TIM15的OC1事件作为外部TRGO
  TIM_ClearFlag(TIM15,TIM_FLAG_CC1);
  // Output Compare PWM Mode configuration
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
  //PWM模式1- 在向上计数时,一旦TIMx_CNT<TIMx_CCR1时通道1为有效电平,否则为无效电平;
  //PWM模式2- 在向上计数时,一旦TIMx_CNT<TIMx_CCR1时通道2为无效电平,否则为有效电平;
  
  TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;//OC与OCRef同相
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;           
  TIM_OCInitStructure.TIM_Pulse = 0x01;
  TIM_OC1Init(TIM15, &TIM_OCInitStructure);
  //TIM1 enable counter //
  TIM_Cmd(TIM15, ENABLE);
  //Main Output Enable //
  TIM_CtrlPWMOutputs(TIM15, ENABLE);
}
#########################################################
     如果选择TIM1_CC4触发ADC,TIM配置中
TIM_SelectOutputTrigger函数不必有(也不应该有,第二个参数没合适的)。。。官网例程中TIM就有CC4触发的例子

相关帖子

沙发
springvirus| | 2013-9-17 17:15 | 只看该作者
学习了

使用特权

评论回复
板凳
缥缈九哥| | 2013-9-18 12:47 | 只看该作者
好像ST的只有 DMA,没有FIFO。

使用特权

评论回复
地板
wgsxsm| | 2013-10-24 00:38 | 只看该作者
学习了

使用特权

评论回复
5
天罡星lmy| | 2014-1-16 22:21 | 只看该作者
mark

使用特权

评论回复
6
杨开路| | 2014-4-17 16:27 | 只看该作者
楼主!!! ADC转化怎么应用中断读取数据啊???

使用特权

评论回复
7
dontium| | 2014-4-22 12:06 | 只看该作者
写得很好!

使用特权

评论回复
8
xuan309170083| | 2014-4-28 21:18 | 只看该作者
MARK~

使用特权

评论回复
9
zhu51231| | 2014-9-26 10:19 | 只看该作者
mark

使用特权

评论回复
10
lcr12| | 2014-12-10 09:21 | 只看该作者
用官方库函数的写法

使用特权

评论回复
11
wuyong2k| | 2015-6-26 18:53 | 只看该作者
学习学习!!!!

使用特权

评论回复
12
liukai19911123| | 2015-12-21 14:36 | 只看该作者
f051可能和 f031不一样 没仔细看f051的   
f031的 TIM1的TRGO信号是可以选择的,配置为CC4也需要在初始化里选择一下

使用特权

评论回复
13
yu515301489| | 2016-3-7 13:03 | 只看该作者
mark!

使用特权

评论回复
14
zhanghl45| | 2016-5-24 18:45 | 只看该作者
学习了!

使用特权

评论回复
15
@lonely| | 2016-8-29 18:07 | 只看该作者
liukai19911123 发表于 2015-12-21 14:36
f051可能和 f031不一样 没仔细看f051的   
f031的 TIM1的TRGO信号是可以选择的,配置为CC4也需要在初始化里 ...

楼主能帮我看看为什么TIM1_CC4触发不成功的原因吗?

https://bbs.21ic.com/icview-1624856-1-1.html

使用特权

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

本版积分规则

1

主题

1

帖子

0

粉丝