赞
踩
1、STM32F407ZGT6
2、STM32CubeMx软件
3、keil5
内容简述:
通篇文章将涉及以下内容,如有错误,欢迎指出:
1、ADC基本原理
2、轮询、中断、DMA方式下的ADC采集
ADC 即模拟数字转换器,英文详称 Analog-to-digital converter,可以将外部的模拟信号转换为数字信号,是模拟信号数字化的必要器件。STM32F407有3个ADC,最高12位分辨率,最多16个外部通道,ADC1还有3个内部测量通道,可以测量内部温度、参考电压和备用电池电压。
STM32F407 的 ADC 主要特性我们可以总结为以下几条:
单个ADC的内部功能结构如图1-1所示,核心是图中的“模数转换器”。
ADC是MCU上的模拟部分,模拟部分的供电有4个引脚,典型的供电方案如图1-2所示。
(1) VDDA是模拟部分工作电源。如果要保证ADC精度,可以使用独立的模拟电源输入,但是VDDA最高不能超过4V。如图6-2所示。
注:VREF+为3.3V,那么ADC的输入电压不应该超过这个值。
(2)VSS是模拟地电源,与数字电源共地。
(3)VREF+ 是ADC转换的正参考电压。如果要保证ADC精度,可以使用单独的参考电压芯片输出的精密参考电压, VREF+不能超过VDDA,最低值为1.8V。一般情况下,将VREF+与VDDA连接。
注:
在测量电压时,如果VDDA接的是3.3V,那么VERF+一定注意不能接到大于3.3V上。
(4) VREF-是将ADC的负参考电压,必须与VSSA连接。
ADC转换电压的输入范围是在VERF-与 VERF+之间,因为VREF-必须与VSS连接,也就是VREF-总是0,所以STM32F407的片上ADC只能转换正电压,这与某些独立的ADC芯片可转换正负范围的电压是不同的。
每个ADC单元有16个外部输入引脚,对应与16个ADC输入复用引脚,即图6-1中的ADCx_IN0至ADCx_IN15。每个ADC单元的模拟输入复用引脚如图1-3所示。
ADC1单元还有以下3个内部输入使用通道16-18。
选择的多个模拟输入通道可以分为两组:规则通道和注入通道。每个组的通道构成一个转换序列。
规则转换序列最多可设置16个通道,一个规则转换序列规定了多路复用转换时的顺序。例如,选择了IN0、IN1、IN2共三个通道作为规则通道,定义的规则转换序列可以是IN0、IN1、IN2,也可以是IN0、IN3、IN2,甚至是IN0、IN31、IN0。
注入通道就是可以在规则通道转换过程中插入进行转换的通道,类似于中断的现象,可以通过图1-4进行理解。
注入转换序列最多可以设置成4个注入通道,也可以像规则转换序列那样设置转换顺序。每个注入通道还可以设置一个数据的偏移量,每次转换结果自动减去这个偏移量,所以转换结果可以是负数。例如,设置偏移量为信号的直流分量,每次转换自动减去直流分量。
规则通道和注入通道由单独的触发源,有以下3类启动或触发转换的方式。
ADC转换需要时钟信号ADCCLK驱动,ADCCLK由PCLK2经过分频产生(见图1-5),最少2分频,最多8分频(见图1-6)。STM32F407的PCLK2的最高频率为84MHz,所以ADCCLK的最高频率为42MHz(不同芯片的频率不同)。
我们可以设置在N个ADCCLK周期内对信号进行采样,N的值最小为3,最大为480(见图1-7), 在允许的情况下,尽量选大一点的会使ADC 更稳定、更精确。
ADC 总转换时间的计算公式如下:
TCONV = 采样时间 + 12 个周期
注:之所以要加12个周期,是因为ADC在开始精确转换之前,需要一段稳定时间(个人理解,如理解有误还望指出)
以STM32F407的ADC1的通道1为例,如果 Clock Prescaler设置成“PCLK2 divided by 4”,Sampling Time设置成15 Cycles,那么ADC时钟频率为
42MHz / 4 = 10.5MHz
一个周期转换所需要的时间为频率的倒数,即此时的频率为 1 / 10.5 MHz 。
转换周期为
15 + 12 = 27 周期
转换时间为
一次转换需要的总周期数 * 一个周期转换所需时间 = 27 * ( 1 / 10.5 Mhz ) = 2.57 微秒
其他情况,以此推算。
ADC完成转换后将结果存放进数据寄存器,规则通道和注入通道有不同的数据寄存器。规则通道只有一个数据寄存器ADC_DR,只有低16位有效,在多通道转换时,如果前一通道转换结束后,ADC_DR的数据未被及时读出,下一个转换通道的转换结果就会覆盖上一次结果的数据。所以,在多通道转换时,一般EOC中断里及时读取数据或通过DMA将数据传输到内存里。
注入通道有4个数据寄存器,分别对应4个注入通道的转换结果。 规则数据寄存器和注入数据寄存器都是低16位有效,因为转换结果数据对多12位有效。可以设置数据左对齐或右对齐,一般使用右对齐,如图1-8。
注:如图1-9所示, 在二进制中左移n位,数据扩大2的n次方倍,所以左对齐直接读出的数值会比实际值大16倍(左移4位,2的4次方)。
规则和注入组转换结束时能产生中断,当模拟看门狗状态位被设置时也能产生中断,它们在 ADC_SR 中都有独立的中断使能位。
ADC转换的结果是一个数字量,与实际的模拟电压之间的计算关系 由VERF+和转换精度位数确定。例如,转换精度位12位,VERF+ 为3.3V,ADC转换结果位12位数字量对应的整数为X,则实际电压为
Voltage = (3.3 * X) / 4096 V(该公式必须理解)
比如ADC采集电压采集到的数值为2000,则实际对应的电压应该为
Voltage = (3.3 * 2000)/ 4096 = 1.61 V
STM32F407有3个ADC。这三个ADC可以独立工作,也可以组成双重或三种工作模式。在多重工作模式下,ADC1是主器件,是必须使用的;双重模式就是使用的ADC1和ADC2,不能使用ADC1和ADC3;三种模式就是3个ADC都使用。
多重模式就是使用住期间ADC1的触发信号去交替触发或同步触发其他ADC启动转换。例如,对于三分量模拟输出的振动传感器,需要对X、Y、Z这3个方向的振动信号同步采集,以合成一个三维空间中的振动矢量,这时就需要使用3个ADC对3路信号同步采集,而不能使用一个ADC对3路信号通过多路复用方式进行采集。
多重ADC有多种工作模式,可以交替触发,也可以同步触发。为避免过于复杂,我们仅以双重ADC同步触发为例,说明多重ADC的工作原理和使用方法。 三种ADC和其他工作模式的原理参见STM32F407参考手册。
设置ADC1和ADC2双重工作模式,为ADC1设置的触发源同时也触发ADC2,以实现两个ADC同步转换。在多重模式下,有一个专门的32位数据寄存器ADC_CDR,用于存储多重模式下的转换结果数据。在多重模式下,ADC_CDR的高16位存储ADC2的规则转数据,ADC_CDR的低16位存储ADC1的规则转换结果数据。
在多重模式下,使用DMA进行数据传输有3种模式,其中DMA模式2适用于双重ADC的数据传输。双重ADC是,DMA模式2的工作特点是:每发送一个DMA请求,就以字的形式传输 表示ADC2和ADC1转换结果的32位数据,其中高16位是ADC2的转换结果,低16位是ADC1的转换结果,相当于将ADC_CDR的数据在一个DMA请求时传输出去。
ADC的驱动程序有两个头文件:头文件stm32f4xx_hal_adc.h是ADC模块总体设置和常规通道相关的函数和定义;文件stm32f4xx_hal_adc_ex.h是注入通道和多重ADC模式相关的函数和定义。表2-1 是文件stm32f4xx_hal_adc.h中的一些主要函数
表2-1 文件stm32f4xx_hal_adc.h中的一些主要的函数
分组 | 函数名 | 功能描述 |
---|---|---|
初始化和配置 | HAL_ADC_Init() | ADC的初始化,设置ADC的总体参数 |
初始化和配置 | HAL_ADC_MspInit() | ADC初始化的MSP弱函数,在HAL_ADC_Init()里面被调用 |
初始化和配置 | HAL_ADC_ConfigChannel() | ADC常规通道配置,一次配置一个通道 |
初始化和配置 | HAL_ADC_AnalogWDGConfig() | 模拟看门狗配置 |
初始化和配置 | HAL_ADC_GetState() | 返回ADC的当前状态 |
初始化和配置 | HAL_ADC_GetError() | 返回ADC的错误码 |
软件启动和转换 | HAL_ADC_Start() | 启动ADC,开始常规通道的转换 |
软件启动和转换 | HAL_ADC_Stop() | 停止常规通道的转换,并停止ADC |
软件启动和转换 | HAL_ADC_PollForConversion() | 轮询方式等待ADC常规通道转换完成 |
软件启动和转换 | HAL_TIM_Base_GetState() | 获取基础定时器的当前状态 |
中断方式转换 | HAL_ADC_Start_IT() | 开启中断,开始ADC常规通道的转换 |
软件启动和转换 | HAL_ADC_Stop_IT() | 关闭中断,停止ADC常规通道的转换 |
软件启动和转换 | HAL_ADC_IRQHandler() | ADC中断ISR里调用的ADC中断处理通用处理函数 |
中断方式转换 | HAL_ADC_Start_DMA() | 开启ADC的DMA请求,开始ADC常规通道的转换 |
软件启动和转换 | HAL_ADC_Stop_DMA() | 停止ADC的DMA请求,停止ADC常规通道的转换 |
下面介绍ADC的三种启动方式,应熟练掌握。
函数HAL_ADC_Start()用于以软件方式启动ADC常规通道的转换,软件启动转换后,需要调用函数HAL_ADC_PollForConversion()查询转换是否完成,转换完成后可用函数HAL_ADC_GetValue()读出常规转换结果寄存器的32位数据。若要再次转换,需要再次使用这3个函数启动转换、查询转换是否完成、读出转换结果。使用函数HAL_ADC_Stop()停止ADC常规通道转换。
这种软件启动转换的模式适用于单通道、低采样率的ADC转换。这几个函数原型定义如下:
HAL_StatusTypeDef HAL_ADC_Start(ADC_HandleTypeDef* hadc); //软件启动转换
HAL_StatusTypeDef HAL_ADC_Stop(ADC_HandleTypeDef* hadc); //停止转换
HAL_StatusTypeDef HAL_ADC_PollForConversion(ADC_HandleTypeDef* hadc, uint32_t Timeout);
uint32_t HAL_ADC_GetValue(ADC_HandleTypeDef* hadc); //读取转换结果寄存器的32位数据
其中,参数hadc是ADC外设对象指针,Timeout是超时等待时间(单位是ms)。
当ADC设置为用定时器或外部信号触发转换时,函数HAL_ADC_Start_IT()用于启动转换,这会开启ADC的中断。当ADC转换完成时会触发中断,在中断服务程序里,可以用HAL_ADC_GetValue()读取转换结果寄存器里的数据。函数HAL_ADC_Stop_IT()可以关闭中断,停止ADC转换。开启和停止ADC中断方式转换的两个函数的原型定义如下:
HAL_StatusTypeDef HAL_ADC_Start_IT(ADC_HandleTypeDef* hadc);
HAL_StatusTypeDef HAL_ADC_Stop_IT(ADC_HandleTypeDef* hadc);
ADC1、ADC2和ADC3共用一个中断号,ISR名称是ADC_IRQHandler()。ADC有4个中断事件源,中断事件类型的宏定义如下(在stm32f4xx_hal_adc.h中):
#define ADC_IT_EOC ((uint32_t)ADC_CR1_EOCIE) //规则通道转换结果结束(EOC)事件
#define ADC_IT_AWD ((uint32_t)ADC_CR1_AWDIE) //模拟看门狗触发事件
#define ADC_IT_JEOC ((uint32_t)ADC_CR1_JEOCIE) //注入通道转换结束事件
#define ADC_IT_OVR ((uint32_t)ADC_CR1_OVRIE) //数据溢出事件、即转换结果未被及时读出
ADC中断通用处理函数是HAL_ADC_IRQHandler(),它内部会判断中断事件类型,并调用相应的回调函数。ADC的4个中断事件类型及其对应的回调函数如表6-2所示。
表6-2 ADC的中断时间类型及其对应的回调函数
中断事件类型 | 中断事件 | 回调函数 |
---|---|---|
ADC_IT_EOC | 规则通道转换结果结束(EOC)事件 | HAL_ADC_ConvCpltCallback() |
ADC_IT_AWD | 模拟看门狗触发事件 | HAL_ADC_LevelOutOfWindowCallback() |
ADC_IT_JEOC | 转入通道转换结束事件 | HAL_ADCEx_InjectedConvCpltCallback() |
ADC_IT_OVR | 数据溢出事件、即转换结果未被及时读出 | HAL_ADC_ErrorCallback() |
用户可以设置为在转换完一个通道后就产生EOC事件,也可以设置为转换完规则通道组的所有通道之后产生EOC事件。但是规则组只有一个转换结果寄存器,如果有多个转换通道,设置为 转换完规则组的所有通道之后产生EOC,会导致数据溢出。一般设置为在转换外一个通道后就产生EOC事件(如图2-1所示)。所以,中断方式转换适用于单通道或采样频率不高的场合。
图2-1 规则通道转换结果结束(EOC)事件
ADC只有一个DMA请求,方向是外设到存储器。DMA在ADC中是非常有用,它可以处理多通道、高采样频率的情况。函数HAL_ADC_Start_DMA()以DM方式启动ADC,其原型定义如下
HAL_StatusTypeDef HAL_ADC_Start_DMA(ADC_HandleTypeDef* hadc, uint32_t* pData, uint32_t Length);
其中,参数hadc是ADC外设对象指针;参数pData是uint32_t类型缓冲区指针,因为ADC转换结果寄存器是32位的,所以DMA数据宽度是32位;参数Length是缓冲区长度,单位是字(4字节)。
停止DMA方式采集的函数是HAL_ADC_Stop_DMA();其原型定义如下:
HAL_StatusTypeDef HAL_ADC_Stop_DMA(ADC_HandleTypeDef* hadc);
DMA流的主要中断事件与ADC的回调函数之间的关系如表6-3所示。一个外设使用DMA传输方式时,DMA流的事件中断一般使用外设的事件中断回调函数。
表6-3 DMA流中断事件类型和关联的回调函数*
DMA流中断事件类型宏 | DMA流中断事件类型 | 关联的回调函数名称 |
---|---|---|
DMA_IT_TC | 传输完成中断 | HAL_ADC_ConvCpltCallback() |
DMA_IT_HT | 传输半完成中断 | HAL_ADC_ConvHalfCpltCallback() |
ADC_IT_JEOC | 传输错误中断 | HAL_ADC_ErrorCallback() |
注:注入通道没有DMA方式。
在该示例中,使用ADC2的IN5通道采集电位器的电压,通过串口进行显示,采用软件方式启动ADC转换,在mian()函数的while循环,每隔约500ms转换一次。
1、ADC配置
ADC1的模式设置界面如图3-1 所示(本示例用到的是ADC2,但是考虑到ADC1功能更全,故 部分是按ADC1进行讲解,ADC2同样适用),各个复选框的意义如下。
图3-1 ADC的通道选择
在图3-2所示的界面中设置ADC1的参数,参数分为多个组。
图3-2 ADC2的参数设置
(1) ADCs_Common_Settings组,具体包括如下参数。
Mode:模式。只启用了一个ADC时,只能选择Independent mode(独立模式)。如果启用2个或3个ADC,会出现双重或三重工作模式的选项。
(2) ADC_Settings组,具体包括如下参数。
(3)ADC_Regular_ConversionMode组,具体包括如下参数。
图3-3 外部触发中断设置
2、串口设置
本实例中用到的是USART1,STM32CubeMx的配置如下。
图3-5 串口设置
因为此时用到了串口将数据发打印出来,所以要先进行“串口重定向”,之所以这样做是将它们的出流重定向到其他设备或接口,如串口、LCD 显示屏等。这样可以通过串口或其他方式输出调试信息、变量值等。
/* 重定义fputc函数(需包含 stdio.h 头文件) */
int fputc(int ch, FILE*f)
{
while (!(USART1->SR & (1 << 7)));
USART1->DR = ch;
return ch;
}
注:如果使用串口2,只需要将 “串口重定向” 程序中 “ USART1” 改为 “ USART2 ”,其他不变。
在该示例中,采用软件起启动方式进行ADC转换,所以在while循环中每隔约500ms进行一次轮换。使用HAL_ADC_Start(&hadc2)启动转换,然后在函数HAL_ADC_PollForConversion()轮询转换是否完成,再用函数HAL_ADC_GetValue()读出转换结果数据,返回一个32位的值val(因为本示例中设置的数据寄存器为右对齐,所以只有低12位有效,可以直接用)。通过printf将val、转换后的电压打印出来,然后用HAL_ADC_Stop()函数停止ADC转换。后面通过HAL_Delay()函数实现500毫秒阻塞式延时。
注:
为了便于观察,程序中加了一行HAL_GPIO_TogglePin(GPIOF,GPIO_PIN_10); 翻转LED灯的代码。
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_ADC_Start(&hadc2); /* 开启ADC转换 */
if(HAL_ADC_PollForConversion(&hadc2,200)==HAL_OK){ /* 判断ADC转换是否完成 */
uint32_t val=HAL_ADC_GetValue(&hadc2); /* 获取ADC采集到的数值 */
printf("val = %-7d V = %5.2f\n",val, val*3.3/4096); /* 将数值和转换出的电压打印出来 */
}
HAL_ADC_Stop(&hadc2); /* 关闭ADC转换 */
HAL_GPIO_TogglePin(GPIOF,GPIO_PIN_10); /* 翻转LED灯 */
HAL_Delay(500); /* 延时500ms */
}
注:
(1)函数HAL_ADC_Stop()停止ADC的规则通道转换。没有必要每次转换结束后调用此函数停止ADC,因为停止后再启动ADC需要经过一段稳定时间才能开始精确转换。
(2)在实际应用时,如果需要一直转换通常不采用上述模式。因为本次没有选择 Continuous Conversion Mode (连续转换模式),所以用HAL_ADC_Start()函数开启ADC后只转换一次,需要将HAL_ADC_Start()放入while(1)循环里不断开启才能实现连续转换。
连续转换时 将Continuous Conversion Mode 使能,然后在初始化的时候用HAL_ADC_Start()开启一次ADC转换(此时不需要调用HAL_ADC_Stop()关闭ADC转换)。在回调函数里HAL_ADC_PollForConversion()里进行读取数值,即可实现连续转换。
用一根杜邦线将开发板上的PA5引脚引出,将其分别放到GND和3.3V上(注意,测量电压不要超过3.3V!原理看前文VERF+的讲解),转换结果如下,通过图3-6的实际操作和图3-7的串口检测,能够看到不断是将杜邦线接到3.3V上还是将杜邦线接到GND上,串口都能及时的反馈出数值。
图3-6 接线图
图3-7 串口显示图
前面的示例使用软件触发方式进行ADC转换,每次转换之后延时500ms,采样周期大约是500ms。如果要求精确周期性进行ADC转换,这种方式的周期肯定是不够精确的。ADC可以使用定时器的触发输出(TRGO)信号或捕获比较事件作为ADC转换启动信号,而TRGO信号可以设置为定时器的更新事件(UEV)信号,也就是定时溢出信号,这样,每次ADC的采样间隔就是精确的。
在本次示例中,我们使用TIM3的TRGO信号作为ADC1的外部触发信号,TIM3的定时周期为500ms,ADC2以中断模式启动转换,在ADC的转换完成中断里读取转换结果数据。
1、ADC2的设置
ADC1的输入通道仍然只选择IN5,参数设置部分只需要修改外部触发源,如图4-1所示,主要有两个参数的设置。
使用定时器的TRGO信号周期性地启动ADC转换时,应该开启ADC的全局中断,在转换完成事件(ADC_IT_EOC)中断里读取转换结果。因此,在NVIC Settings页面开启ADC1的全局中断,并设置ADC1中断的抢占优先级1,ADC中端配置的结果如图 所示。
注:ADC1、ADC2、ADC3共用一个中断号。
图4-2 ADC全局中断设置
2、TIM3的设置
STM32F407ZGT6的TIM3在总线APB1上,定时器时钟信号频率为84MHz,TIM3的模式和配置界面如图4-2所示。模式设置部分只需设置Clock Source 为Internal Clock,启动TIM3即可。
Trigger Output(TRGO)Parameters组用于设置TRGO信号,主/从模式(Master/Slave Mode)设置为Disable,即禁用主/从模式。触发事件选择(Trigger Event Selection)设置为Updata Event,也让就是以UEV事件信号作为TRGOx信号。
这样,ADC1在TIM3的TRGO信号的每个上跳沿启动一次ADC转换,就可以实现周期性的ADC转换,转换周期由TIM3的定时周期决定。无需开启TIM3的全局中断,TRGO信号也是正常输出的。
关于定时器定时不了解的可以看这篇文章 HAL库STM32常用外设教程(四)—— 定时器 基本定时
图4-3 TIM3设置
(1)因为本示例中用到了“串口重定向”,具体程序见3.2中的代码,此处不再赘述。
(2)以中断方式启动了ADC2的模数转换过程,启动了定时器TIM3的基本定时功能。
HAL_ADC_Start_IT(&hadc2); /* 开启ADC转换,中断模式 */
HAL_TIM_Base_Start(&htim3); /* 启动定时器 */
(3)HAL_ADC_ConvCpltCallback() 函数是HAL库提供的ADC转换完成的回调函数,ADC_HandleTypeDef* hadc 是传递给回调函数的ADC句柄,其中包含有关ADC配置和状态的信息。
注:
“%-7d”用法
①%7d:表示输出的整数字段宽度为7个字符,如果实际整数的宽度不足7个字符,将在左侧用空格填充,使得输出整数右对齐。
② %-7d:表示输出的整数字段宽度为7个字符,如果实际整数的宽度不足7个字符,将在右侧用空格填充,使得输出整数左对齐。
%5.2f中的“5.2”表示总的输出宽度为5个字符,其中有2位小数。
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
if(hadc->Instance == ADC2){
uint32_t val=HAL_ADC_GetValue(&hadc2); /* 获取ADC采集到的数值 */
printf("val = %-7d V = %5.2f\n",val, val*3.3/4096); /* 将数值和转换出的电压打印出来 */
}
}
在开发板上进行测试,将图 在串口里进行打印,从打印的时间可以看出,每间隔500ms就会进行一次打印,采集到的电压通过串口观察也是正确的。
图4-3 结果显示
前面两个示例都只有一个通道,在转换结束后可以计时读出结果数据寄存器的内容。当规则转换组有多个通道时,应该使用扫描转换模式(Scan Conversion Mode),ADC在转换完一个通道后立即转换下一个通道,知道规则组内的通道序列转换完。规则转换只有一个转换结果数据寄存器,虽然可以设置在每个通道转换完之后就产生EOC事件中断,但是在多通道情况下,在EOC事件中断里读取转换结果数据可能是来不及的,更谈不上对数据进行显示和处理。
如果规则转换组有多个输入通道,应该使用DMA,使转换结果数据通过DMA传输自动保存到缓存中,在一个规则组转换结束后在对数据进行处理,或者在采集多次数据后再处理。
本次示例中用到了三个规则组输入通道,使用扫描模式,通过DMA方式传输ADC转换结果数据。和第四节一样依然通过定时器TIM3定时500ms 去触发。此处不再赘述。
1、ADC1通道配置
在ADC1的模式设置中,选择3个输入通道,如图5-1所示。本次示例使用了3个内部输入通道, Temperature Sensor Channel是内部的温度传感器通道, Vrefint Channel是内部参考电压通道, Vbat Channel,备用电源VBAT的通道
在ADC_Settings参数组,开启扫描转换模式(Scan Conversion Mode)和DMA连续请求(DMA Continous Requests)。这两个参数的意义见3.1节的解释。
图5-1 ADC1参数设置(1)
在ADC_Regular_ConversionMode参数组设置转换个数为3,下面会自动生成3个Rank的设置,分别设置每个Rank的输入通道和采样时间——每个通道的采样时间可以不一样,3个Rank里面模拟通道的出现顺序就是规则组转换的顺序,如图5-2所示。
注意,ADC_Setting组里面的参数End of Conversion Selection的设定置不变,仍然是在每个通道转换完成后产生EOC信号。
图5-2 ADC1参数设置(2)
ADC只有一个DMA请求,为这个DMA请求配置DMA流DMA2 Stream 0,设置DMA传输属性参数,设置界面如图5-3 所示。DMA传输方向自动设置为Peripheral To Memory(外设到存储器)。在DMA Request Setting 组中将Mode(工作模式)设置为Circular(循环模式),将外设和存储器的数据宽度都设置为Word——因为ADC转换结果数据寄存器是32位的,存储器设置为地址自增加。
图5-3 ADC1的DMA设置
(1)因为本示例中用到了“串口重定向”,具体程序见3.2中的代码,此处不再赘述。
(2)相关参数定义,此处定义了一个数组去存储ADC采集到的数值。
uint32_t dmaDataBuffer[3]; /* ADC数据存放数组 */
(3)开启DMA转换并且开启定时器3。
HAL_ADC_Start_DMA(&hadc1,dmaDataBuffer,3); /* 以DMA方式开启ADC转换 */
HAL_TIM_Base_Start(&htim3); /* 启动定时器3 */
(4)在回调函数里面读取参数。
在开发板上进行测试,将图5在串口里进行打印,从打印的时间可以看出,每间隔500ms就会进行一次打印,可以采集到两路电压值和一路ADC采集的原始数据。
本章介绍了ADC的原理,讲解了ADC采集的三种方式,这三种方式在其他外设中也是同样适用的,一种是轮询、一种是中断、一种是DMA方式,三种方式应该熟练掌握并知道其中区别。本人在实际应用中用到的DMA的方式比较多。
参考书籍和文章:
1、《STM32Cube高效开发教程(基础篇)》王维波
2、《STM32F4xx中文参考手册》
3、《STM32F407 探索者开发指南》
4、STM32对HAL库的ADC(多通道DMA)
5、STM32使用ADC获取内部温度传感器数据输出(直接读取/DMA两种方式实现)
6、STM32CubeMX ADC参数配置页中文注解
生活不能等别人来安排,要自己去争取和奋斗;而不论结果是喜是悲,但能够慰藉的是,你总不枉在这世界上活了一场。
——《人生》
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。