STM32分别利用软件/定时器TGRO信号触发ADC采样,包括规则组和注入组的配置方法

ADC做为一种模数转换功能,在实际应用中很是经常使用,那么也经常有各类个性化需求,包括对其转换开始时间、顺序等的要求等。STM32也提供了多种ADC触发方式来知足要求,包括软件触发、定时器触发和外部触发等等。数组

本文简单介绍软件触发,重点说明定时器触发。同时,网上的资料多数是介绍规则组的配置,而本文把规则组和注入组都进行说明,方便你们学习和比较。过程当中也会说起本身的一些思考。函数

1. ADC简介

1.1ADC通道和转换时间

STM32f103有3个AD模数转换器,每一个ADC都有18个通道,能够测量16个外部和2个内部模拟量。最大理论转换频率为1Mhz,也就是转换时间为1us(在 ADCCLK = 14Mhz,采样周期为1.5个时钟周期时,由于总时间是采样时间+12.5个时钟周期)。而实际用的时候最短转换时间约为1.17 u s u s us,这是由于挂载的时钟频率为72MHz,分频为6时ADC时钟为12MHz,能够知足低于14MHz,最大时钟超过14Mhz会致使ADC转换准确度下降。学习

每一个ADC的各个通道对应接口如图:
在这里插入图片描述
es5

STM32的ADC是12位精度的,即数据最大为4096spa

STM32的ADC转换有两种通道,规则通道和注入通道,注入通道能够抢占式地打断规则通道的采样,执行注入通道采样后,再执行以前的规则通道采样,和中断相似。3d

1.2ADC触发方式、模式等的设置(ADC_CR2)

STM32的ADC能够由外部事件触发(例如定时器更新、捕获,EXTI线)和软件触发(即在配置相关寄存器时,直接开启采样)。这里能够看一下参考手册里的说明:
在这里插入图片描述
在这里插入图片描述

code

在配置转换的时候有一些模式,这里进行一下说明:blog

单次模式、连续模式:ADC单通道要求进行一次ADC转换时配置为单次模式使能,这样ADC的这个通道转换一次后就中止转换。要求进行连续ADC转换时配置为连续模式使能,这样ADC的这个通道转换一次后接着进行下一次。
扫描模式:对应多个ADC通道的状况,每一个通道依次进行转换。
教程

上面的各类内容,包括触发方式、转换模式等的设置,都在ADC_CR2寄存器里体现,详情能够去参考手册里看看寄存器的说明。接口

1.3ADC的数据存储(ADC_DR和ADC_JDRx)

1.3.1 规则通道

ADC在规则通道存储数据时,只有一个数据寄存器ADC_DR
在这里插入图片描述
寄存器中有16位来存放数据,而ADC精度是12位,因此涉及到数据左或右u对齐的问题。

在连续屡次ADC转换时,因为只有一个数据寄存器,规则通道的数据会被下一次转换的数据覆盖,因此ADC经常和DMA一块儿使用,利用DMA模式将转换的数据,传输在一个数组中,程序对数组读操做就能够获得转换的结果。

1.3.2 注入通道

注入通道的每一个通道都有本身的数据寄存器,不过一样涉及到数据对齐方式的设置。
在这里插入图片描述

1.4工做流程

STM32的ADC在单次转换模式下,只执行一次转换,首先要置为ADC_CR2 寄存器的ADON 位,该模式能够经过软件触发启动,也能够经过外部触发启动(均适用于规则通道和注入通道),这时CONT 位为0。以规则通道为例,转换开始后,SWSTART位(开始规则通道转换位)自动清除,一旦所选择的通道转换完成,转换结果将被存在ADC_DR 寄存器,EOC (转换结束)标志将被置位,若是设置了EOCIE ,则会产生中断。而后ADC将中止,直到下次启动。

2 软件触发方式

软件触发方式是最基本的ADC触发方式,这里我用规则组来作。配置的时候配置成不使用外部触发,而且在单次转换下,每次转换时都须要对启动规则组转换对应的位置位即调用对应的函数,这个后面结合着代码来讲。这里我把ADC部分的配置贴上来。

void Adc_Init()
{
 ADC_InitTypeDef ADC_InitStructure; 
 GPIO_InitTypeDef GPIO_InitStructure;
 
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1,ENABLE );   //使能ADC1通道时钟
 RCC_ADCCLKConfig(RCC_PCLK2_Div6);//分频因子为6
 
 //RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); 
 
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//模拟输入引脚
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 GPIO_Init(GPIOA, &GPIO_InitStructure);
 
 ADC_DeInit(ADC1);  //复位ADC1,将外设 ADC1 的所有寄存器重设为缺省值
 ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工做模式:ADC1和ADC2工做在独立模式
 ADC_InitStructure.ADC_ScanConvMode = DISABLE;
 ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件而不是外部触发启动
 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐
 ADC_InitStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的ADC通道的数目
 ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
 
//DMACmd(ADC1, ENABLE);//开启ADC的DMA支持
 ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1
 
 ADC_ResetCalibration(ADC1); //使能复位校准  
 while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
  ADC_StartCalibration(ADC1);  //开启AD校准
  while(ADC_GetCalibrationStatus(ADC1));  //等待校准结束
}

以上就是单通道、软件触发模式下规则组的配置状况,基本说明在注释里都包括了。那么配置完毕之后,何时开始一次转换呢?咱们看调用的函数

u16 Get_Adc(u8 ch)
{
 ADC_RegularChannelConfig(ADC1,ch,1,ADC_SampleTime_239Cycles5);
  ADC_SoftwareStartConvCmd(ADC1,ENABLE);
 while(!ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC));
  return ADC_GetConversionValue(ADC1);
}

咱们每调用一次这个函数,就执行一次ADC转换,并返回转换的结果值。那么咱们来分析一下这个函数里的语句:
ADC_RegularChannelConfig这个函数决定了在多通道采样的状况下,对每一个通道的采样顺序、采样时间的配置,ch即通道名,1是第一个进行转换。
ADC_SoftwareStartConvCmd这个函数对寄存器ADC_CR2的SWSTART位进行操做,执行完此句之后就真正开始一次ADC转换。

仔细思考的一些同窗可能会有一些疑惑,好比ADC_SoftwareStartConvCmd函数使能和ADC_Cmd是什么关系?状况是这样的,前者操做的是ADC_CR2的SWSTART位,后者操做的是ADC_CR2的ADON位,后者至关于电源,当ADON低位时,操做SWSTART位无效;当ADON高位时,置SWSTART位能够开启一次转换。

其实这里我在实验的时候遇到了两个问题,一个是执行完ADC_SoftwareStartConvCmd这条语句后寄存器被置位从而执行转换,可是却没有将对应的位清零的语句,那么为何只执行了一次而不是反复在转换呢?另外一个问题是当转换完成后EOC被置位,一样没有该位清零的语句,那么这个转换完成位就一直是高位,后面再判断的时候就老是转换完成了,这显然是不合逻辑的,问题出在哪里呢?

这个问题我上网查了不少教程和咨询,都没有被说起,通过了很长时间的思索之后,最后在参考手册的寄存器描述里找到了很是细节的答案,手册里是这么描述的:
在这里插入图片描述

第一行写到,转换开始后硬件立刻清除此位。也就是说为了让转换开始而置的位在转换一开始就被清楚了,方便后面的配置。就让我豁然开朗。

对于第二个问题,在参考手册里也找到了答案:
在这里插入图片描述

EOC转换结束标志位在读取本次ADC转换结果数据的时候被自动清除,这样一样有利于后面程序的执行。

3定时器TRGO触发ADC

3.1使用规则组配置

在规则组配置过程时,ADC配置部分基本内容和软件触发相同,仅有下面这些改动

  1. 触发方式修改:ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO; 也就是把本来的ADC_ExternalTrigConv_None改掉
  2. 使能外部触发: ADC_ExternalTrigConvCmd(ADC1,ENABLE);
  3. 同时我发现,这时不用配置ADC_SoftwareStartConvCmd就也能够正常工做了,我不太清除这个函数究竟是使能软件转换开启的仍是使能整个规则组转换开启的,如今倾向于前者,有比较清楚的大佬能够给解释一下。

而后就是配置定时器的内容了,注意选择让定时去产生更新触发。
整个配置部分代码以下:

void Adc_Init()
{
 ADC_InitTypeDef ADC_InitStructure; 
 GPIO_InitTypeDef GPIO_InitStructure;
 
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1,ENABLE );   //使能ADC1通道时钟
 RCC_ADCCLKConfig(RCC_PCLK2_Div6);//分频因子改成6
 //RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); 
 
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//模拟输入引脚
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 GPIO_Init(GPIOA, &GPIO_InitStructure);
 
 ADC_DeInit(ADC1);  //复位ADC1,将外设 ADC1 的所有寄存器重设为缺省值
 ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工做模式:ADC1和ADC2工做在独立模式
 ADC_InitStructure.ADC_ScanConvMode = DISABLE;
 ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO;
 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐
 ADC_InitStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的ADC通道的数目
 ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
 
 ADC_ExternalTrigConvCmd(ADC1,ENABLE);
  // ADC_DMACmd(ADC1, ENABLE);//开启ADC的DMA支持
 
 ADC_RegularChannelConfig(ADC1,ADC_Channel_1,1,ADC_SampleTime_239Cycles5);
 ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1
 
 ADC_ResetCalibration(ADC1); //使能复位校准  
  while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
  ADC_StartCalibration(ADC1);  //开启AD校准
  while(ADC_GetCalibrationStatus(ADC1));  //等待校准结束
 // ADC_SoftwareStartConvCmd(ADC1, ENABLE);  //使能指定的ADC1的软件转换启动功能
} 

void TIM3_init(u16 arr,u16 psc)
{ NVIC_InitTypeDef NVIC_InitStructure;
 TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
 //TIM_OCInitTypeDef  TIM_OCInitStructure;
 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);//           
                                                        
 TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值  80K
 TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来做为TIMx时钟频率除数的预分频值  不分频
 TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
 TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
 
 TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update);
 //TIM_ITConfig(TIM3, TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,容许更新中断
 // TIM_ClearITPendingBit(TIM3, TIM_IT_Update); //清中断以避免一启用中断后当即产生中断
 TIM_Cmd(TIM3, ENABLE);  //使能TIM1
   }

而后调用ADC_GetConversionValue(ADC1)函数就能够获得ADC转换的值了。经过配置定时器的arr和psc值,能够实现任意时间间隔的ADC转换。

这里要注意的一个点就是,若是你不须要定时器中断,那就不要使能定时器中断,不然你使能了中断却又没有写中断服务函数的话,程序会跑飞。我看不少人分享的代码里都使能了定时器中断但也不用,实在不能理解。

3.2使用注入组配置

通常来讲,用到注入组了确定是以前已经有了规则组了,否则就不必让它做为注入组了。在规则组和注入组混合使用的状况下,规则组的配置没有任何变化,注入组的配置以下:

ADC_InjectedSequencerLengthConfig(ADC1, 2);  //注入组有两个通道
    ADC_InjectedChannelConfig(ADC1, ADC_Channel_3, 1,ADC_SampleTime_13Cycles5);
    ADC_InjectedChannelConfig(ADC1, ADC_Channel_6, 2,ADC_SampleTime_13Cycles5);//对两个注入组的通道进行设置
    ADC_ExternalTrigInjectedConvConfig(ADC1, ADC_ExternalTrigInjecConv_T1_TRGO); 
    ADC_ExternalTrigInjectedConvCmd(ADC1, ENABLE);	//使能 ADC1的经外部触发启动注入组转换功能
  //ADC_ITConfig(ADC1,ADC_IT_JEOC,ENABLE);		//注入组ADC转换结束的中断
    ADC_AutoInjectedConvCmd(ADC1, DISABLE); 		//使能或者失能指定 ADC 在规则组转化后自动开始注入组转换

若是是软件触发方式的注入组,就把触发方式ADC_ExternalTrigInjectedConvConfig改变一下,同时不用使能外部触发启动功能,而使能ADC_SoftwareStartInjectedConvCmd(ADC1, ENABLE)便可。
同时也别忘了,每一个通道对应的GPIO端口也要进行初始化配置。

在读取注入组的转换数据时,使用的函数变成了ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_1);
它包括了两个入口参数,一个是ADCx,另外一个是注入通道的顺序序号,这时由于注入组每一个通道都有本身的数据寄存器,因此能够读指定通道的数据。
以上就是关于ADC触发部分介绍的内容了,但愿你们读了会有些收获!

最后参考了一些别人的总结,列出了ADC部分的各类函数

ADC_DeInit 将外设 ADCx 的所有寄存器重设为缺省值
ADC_Init 根据 ADC_InitStruct 中指定的参数初始化外设 ADCx 的寄存器
ADC_StructInit 把 ADC_InitStruct 中的每个参数按缺省值填入
ADC_Cmd 使能或者失能指定的 ADC
ADC_DMACmd 使能或者失能指定的 ADC 的 DMA 请求
ADC_ITConfig 使能或者失能指定的 ADC 的中断
ADC_ResetCalibration 重置指定的 ADC 的校准寄存器
ADC_GetResetCalibrationStatus 获取 ADC 重置校准寄存器的状态
ADC_StartCalibration 开始指定 ADC 的校准程序
ADC_GetCalibrationStatus 获取指定 ADC 的校准状态
ADC_SoftwareStartConvCmd 使能或者失能指定的 ADC 的软件转换启动功能
ADC_GetSoftwareStartConvStatus 获取 ADC 软件转换启动状态
ADC_DiscModeChannelCountConfig 对 ADC 规则组通道配置间断模式
ADC_DiscModeCmd 使能或者失能指定的 ADC 规则组通道的间断模式
ADC_RegularChannelConfig 设置指定 ADC 的规则组通道,设置它们的转化顺序和采样时间
ADC_ExternalTrigConvConfig 使能或者失能 ADCx 的经外部触发启动转换功能
ADC_GetConversionValue 返回最近一次 ADCx 规则组的转换结果
ADC_GetDuelModeConversionValue 返回最近一次双 ADC 模式下的转换结果
ADC_AutoInjectedConvCmd 使能或者失能指定 ADC 在规则组转化后自动开始注入组转换
ADC_InjectedDiscModeCmd 使能或者失能指定 ADC 的注入组间断模式
ADC_ExternalTrigInjectedConvConfig 配置 ADCx 的外部触发启动注入组转换功能
ADC_ExternalTrigInjectedConvCmd 使能或者失能 ADCx 的经外部触发启动注入组转换功能
ADC_SoftwareStartinjectedConvCmd 使能或者失能 ADCx 软件启动注入组转换功能
ADC_GetsoftwareStartinjectedConvStatus 获取指定 ADC 的软件启动注入组转换状态
ADC_InjectedChannleConfig 设置指定 ADC 的注入组通道,设置它们的转化顺序和采样时间
ADC_InjectedSequencerLengthConfig 设置注入组通道的转换序列长度
ADC_SetinjectedOffset 设置注入组通道的转换偏移值
ADC_GetInjectedConversionValue 返回 ADC 指定注入通道的转换结果
ADC_AnalogWatchdogCmd 使能或者失能指定单个/全体,规则/注入组通道上的模拟看门狗
ADC_AnalogWatchdongThresholdsConfig 设置模拟看门狗的高/低阈值
ADC_AnalogWatchdongSingleChannelConfig对单个 ADC 通道设置模拟看门狗
ADC_TampSensorVrefintCmd 使能或者失能温度传感器和内部参考电压通道
ADC_GetFlagStatus 检查制定 ADC 标志位置 1 与否
ADC_ClearFlag 清除 ADCx 的待处理标志位
ADC_GetITStatus 检查指定的 ADC 中断是否发生
ADC_ClearITPendingBit 清除 ADCx 的中断待处理位
相关文章
相关标签/搜索