上节对ADC基础知识进行了学习,这节在了解一下ADC相关的结构体。
在标准库函数中基本上对于外设都有一个初始化结构体xx_InitTypeDef
(其中xx为外设名,例如之前使用过的GPIO_InitTypeDef
),结构体成员用来设置外设的工作模式,通过标准库函数xx_Init()
调用将这些设定的参数送入外摄对应的寄存器。
对于ADC来说也是这样的操作,先看一下ADC_InitTypeDef
结构体的定义:
ADC_InitTypeDef结构体
typedef struct {
uint32_t ADC_Resolution; //设置ADC分辨率选择
FunctionalState ADC_ScanConvMode; //设置ADC扫描选择
FunctionalState ADC_ContinuousConvMode; //设置ADC连续转换模式选择
uint32_t ADC_ExternalTrigConvEdge; //设置ADC外部触发极性
uint32_t ADC_ExternalTrigConv; //设置ADC外部触发选择
uint32_t ADC_DataAlign; //设置输出数据对齐方式
uint8_t ADC_NbrOfChannel; //设置转换通道数目
} ADC_InitTypeDef;
ADC_Resolution
:用于配置ADC的分辨率,可选的分辨率有12 位、10 位、8 位和 6 位。分辨率越高, AD转换数据精度越高,转换时间也越长;分辨率越低,AD转换数据精度越低,转换时间也越短。
ADC_Resolution_12b //12位精度
ADC_Resolution_10b //10位精度
ADC_Resolution_8b //8位精度c
ADC_Resolution_6b //6位精度
ADC_ScanConvMode
:该成员主要用于配置ADC是否使用扫描,可选参数为ENABLE和DISABLE,如果只用到一个通道,配置为DISABLE,如果时多个通道,则选择ENABLE。
ADC_ContinuousConvMode
:用于配置启动自动连续转换还是单次转换,可选参数为ENABLE(连续转换)和DISABLE(单次转换)。如果选择为单次转换,那么每次转换完成后,都需要手动控制才能重新启动转换。
ADC_ExternalTrigConvEdge
:设置外部触发极性,如果使用外部触发,可选有:
ADC_ExternalTrigConvEdge_None //禁止触发检测
ADC_ExternalTrigConvEdge_Rising //上升沿触发检测
ADC_ExternalTrigConvEdge_Falling //下降沿触发检测
ADC_ExternalTrigConvEdge_RisingFalling //上升下降沿均检测
ADC_ExternalTrigConv
:外部触发模式选择,可选的触发条件如下图-4,常用的一般使用软件自动触发:
ADC_DataAlign
:设置转换结果数据对其模式,可选有右对齐ADC_DataAlign_Right
或者左对齐ADC_DataAlign_Left
,这里我们选择右对齐。
ADC_NbrOfChannel
:设置AD转换通道数目。
ADC_CommonInitTypeDef结构体
除了ADC_InitTypeDef
初始化结构体外,还有ADC_CommonInitTypeDef通用初始化结构,ADC_CommonInitTypeDef结构体内容决定了三个ADC共用的工作环境。
typedef struct{
uint32_t ADC_Mode; //ADC模式选择
uint32_t ADC_Prescaler; //ADC分频系数
uint32_t ADC_DMAAccessMode; //DMA模式配置
uint32_t ADC_TwoSamplingDelay; //采样延迟
}ADC_CommonInitTypeDef;
ADC_Mode
:ADC工作模式选择在前面有学习过,有独立模式、双重模式以及三重模式;
ADC_Prescaler
:ADC时钟分频系数选择,ADC时钟是由PCLK2分频而来,分频系数决定ADC时钟频率,可选的分频系数位2、4、6和8,在之前也学习过ADC的最大时钟配置为36MHz。
ADC_DMAAccessMode
:DMA模式设置,在双重或者三重模式下才需要设置,可以设置三种模式,分别为:
ADC_DMAAccessMode_Disabled //独立模式下选择
ADC_DMAAccessMode_1 //模式1
ADC_DMAAccessMode_2 //模式2
ADC_DMAAccessMode_3 //模式3
ADC_TwoSamplingDelay
:2个采样阶段之前的延迟,仅适用于双重或三重交错模式。
在F429实验板上,提供了一个板载贴片滑动变阻器,供我们调试ADC时使用,原理图如下:
滑动变阻器动触点连接到STM32的ADC通道引脚(GPIOC_3),固定触点一边接0V,一边接3.3V,因此旋转滑动变阻器调节旋钮时,动触点的电压会在0~3.3V之间变化。
根据以上电路设计,设计个模拟量采集程序,具体需要实现如下步骤:
这里需要注意,没用使用到DMA进行数据传输,而是使用中断方式进行读取。
bsp_adc.h
#ifndef __BSP_ADC_H_
#define __BSP_ADC_H_
#include "stm32f4xx.h"
#define R_ADC_IRQ ADC_IRQn
#define R_ADC_INT_FUNCTION ADC_IRQHandler
#define R_ADC_GPIO_PORT GPIOC
#define R_ADC_GPIO_PIN GPIO_Pin_3
#define R_ADC_GPIO_CLK RCC_AHB1Periph_GPIOC
#define R_ADC ADC1
#define R_ADC_CLK RCC_APB2Periph_ADC1
#define R_ADC_CHANNEL ADC_Channel_13
void R_ADC_Init(void);
#endif
bsp_adc.c
#include "bsp_adc.h"
/** @brief 初始化ADC GPIO引脚
* @parm 无
* @retval 无
*/
static void R_ADC_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(R_ADC_GPIO_CLK,ENABLE); //开启ADC外设引脚时钟
GPIO_InitStructure.GPIO_Pin = R_ADC_GPIO_PIN; //配置引脚位3引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //配置引脚为模拟输入
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //配置为无上下拉
GPIO_Init(R_ADC_GPIO_PORT,&GPIO_InitStructure); //初始化引脚
}
/** @brief 配置ADC引脚工作模式
* @parm 无
* @retval 无
*/
static void R_ADC_Mode_Config(void)
{
ADC_InitTypeDef ADC_InitStructure;
ADC_CommonInitTypeDef ADC_CommonInitStructure;
RCC_APB2PeriphClockCmd(R_ADC_CLK,ENABLE);
//-----------------ADC Common结构体参数初始化--------------//
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; //设置模式为独立模式
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4; //设置为4分频
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; //禁止DMA直接访问模式
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_10Cycles; //设置采样间隔周期为10个周期
ADC_CommonInit(&ADC_CommonInitStructure);
//-----------------ADC Init结构体参数初始化--------------//
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; //设置ADC采样分辨率为12位
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //多通道下才会用到扫描模式,这里直接禁止
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //设置为连续转换
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; //禁用外部边沿触发
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //设置为右对齐
ADC_InitStructure.ADC_NbrOfConversion = 1; //转换通道只有1个
ADC_Init(R_ADC,&ADC_InitStructure);
ADC_RegularChannelConfig(R_ADC,R_ADC_CHANNEL,1,ADC_SampleTime_56Cycles); //配置ADC通道转换顺序为1,第一个转换,才压根时间为56个时钟周期
ADC_ITConfig(R_ADC,ADC_IT_EOC,ENABLE); //ADC转换结束产生中断,在中断服务程序中读取转换数值
ADC_Cmd(R_ADC,ENABLE); //使能ADC
ADC_SoftwareStartConv(R_ADC); //开始ADC转换,由软件触发
}
static void R_ADC_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //设置中断向量组为1
NVIC_InitStructure.NVIC_IRQChannel = R_ADC_IRQ; //配置中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //设置主优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //设置此优先级为1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //通道使能
NVIC_Init(&NVIC_InitStructure); //初始化中断
}
void R_ADC_Init(void)
{
R_ADC_GPIO_Config();
R_ADC_Mode_Config();
R_ADC_NVIC_Config();
}
main.c
#include "stm32f4xx.h"
#include "bsp_usart_dma.h"
#include "bsp_systick.h"
#include "bsp_adc.h"
#include
__IO uint16_t ADC_Value;
float ADC_Vol = 0;
int main(void)
{
DEBUG_USART1_Config();
R_ADC_Init();
SysTick_Init();
printf("\r\n---------------ADC实验(中断模式)----------------\r\n");
while(1)
{
Delay_ms(1000);
printf("\r\n ADC数据(未转换) = 0x%04X \r\n",ADC_Value);
ADC_Vol =(float)(ADC_Value*3.3/4096); // 读取转换的 AD 值
printf("\r\n ADC数据(已转换) = %.2f V \r\n",ADC_Vol);
}
}
stm32f4xx_it.c
//此处记得在头文件中声明函数
extern __IO uint16_t ADC_ConvertedValue;
void ADC_IRQHandler(void)
{
if (ADC_GetITStatus(R_ADC,ADC_IT_EOC)==SET) //获取ADC中断状态
{
ADC_ConvertedValue = ADC_GetConversionValue(R_ADC); // 等待转换完成后,读取ADC的转换值
}
ADC_ClearITPendingBit(R_ADC,ADC_IT_EOC); //清除状态寄存器
}
输出结果如下,调整电位器会发现数值在0V~3.3V之间变化。
单通道相对比较简单,这里先了解一下,后面在学习一下多通道并且启用DMA传输模式。