电子说
ADC可以将现实世界中连续变化的模拟量,如温度、压力、流量、速度、光强等,转换成离散的数字量,输入到计算机中进行处理。按照原理不同,ADC可以分为积分型、逐次逼近型(SAR)、并行比较型、Σ-∆调制型、电容阵列逐次比较型及压频变换型。逐次逼近型(SAR)ADC由比较器和DA转换器构成,它从最高有效MSB位开始通过逐次比较,按顺序以位位单位对输入电压与内置DA转换器输出电压进行比较,经过多次比较输出相应的数字值。逐次逼近型(SAR)ADC的电路规模属于中等,它的优点是速度较快、功耗低、成本适中。
ADC简介
CKS32F4xx系列产品内嵌3个12位SAR型ADC,每个ADC多达19个复用通道,可测量来自16个外部、2个内部和VBAT通道的信号,具有独立模式、双重模式和三种模式,并支持单次、连续、扫描或间断采样模式下进行A/D转换,对于不同AD转换要求几乎都有合适的模式可选,转换的结果可以按照左对齐或右对齐的方式存储在16位数据寄存器中。下图是CKS32F4xx系列产品ADC的结构框图,每个模块的具体描述和技术参数可以参阅《CKS32F4xx参考手册》。
ADC主要特性
▲ 分辨率支持12位,10位,8位,6位,可软件配置
▲ 支持转换结束产生中断、包括规则通道和注入通道
▲ 支持单次、连续、间隔转换模式
▲ 可独立设置各通道采样时间
▲ 规则通道转换和注入通道转换均有外部触发选项,支持软件或者硬件触发转换
▲ 支持模拟看门狗
▲ 支持双重或者三重模式(具有2个或以上ADC的器件)
▲ 支持双重或者三重模式下可配置的DMA数据存储
▲ 可配置双重或者三重交替采样模式下的转换延迟时间间隔
▲ ADC供电要求:全速模式下为2.4V到3.6V,低速模式下为1.8V
▲ ADC输入电压范围:Vref-≤Vin≤Vref+
▲ 规则通道转换期间可产生DMA请求
固件库中与ADC相关的主要API
CKS32F4xx系列固件库中与ADC相关的API定义在cks32f4xx_adc.h和cks32f4xx_adc.c两个文件中,前者为头文件,包含寄存器地址、常量定义、API函数声明,后者为API的具体实现。CKS32F4xx系列产品最多有3个ADC,编号分别为ADC1、ADC2、ADC3,大多数API的第一个参数为ADC_TypeDef*ADCx,表示操作的ADC实体编号。下表列出了常用的API函数:
API函数原型 | 函数功能 |
void ADC_DeInit(void) | 复位ADC外设寄存器 |
void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct) | 根据ADC_InitStruct中参数初始化ADC |
void ADC_StructInit(ADC_InitTypeDef* ADC_InitStruct) | 复位结构体成员 |
void ADC_CommonInit(ADC_CommonInitTypeDef* ADC_CommonInitStruct) | 根据结构体参数初始化ADC外设 |
void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState) | 使能或者禁止ADC外设 |
void ADC_TempSensorVrefintCmd(FunctionalState NewState) | 使能或者禁止温度传感器通道 |
void ADC_VBATCmd(FunctionalState NewState) | 使能或者禁止VBAT通道 |
void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime) | 设置ADC规则通道的参数和采样时间 |
void ADC_SoftwareStartConv(ADC_TypeDef* ADCx) | 软件启动ADC转换 |
FlagStatus ADC_GetSoftwareStartConvStatus(ADC_TypeDef* ADCx) | 得到ADC转换状态 |
void ADC_EOCOnEachRegularChannelCmd(ADC_TypeDef* ADCx, FunctionalState NewState) | 使能或者禁止每个规则转换通道的EOC |
void ADC_ContinuousModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState) | 使能或者禁止ADC的连续转换模式 |
void ADC_DiscModeChannelCountConfig(ADC_TypeDef* ADCx, uint8_t Number) | 配置ADC规则组通道的间断模式 |
void ADC_DiscModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState) | 使能或者禁止ADC的规则组通道 |
uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx) | 得到规则通道最后一次转换的数值 |
void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState) | 使能或者禁止ADC的DMA请求 |
void ADC_InjectedChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime) | 设置ADC注入通道的参数和采样时间 |
void ADC_InjectedSequencerLengthConfig(ADC_TypeDef* ADCx, uint8_t Length) | 配置注入通道序列长度 |
void ADC_SetInjectedOffset(ADC_TypeDef* ADCx, uint8_t ADC_InjectedChannel, uint16_t Offset) | 设置注入通道转换值偏移 |
void ADC_ExternalTrigInjectedConvConfig(ADC_TypeDef* ADCx, uint32_t ADC_ExternalTrigInjecConv) | 配置注入通道的外部触发 |
void ADC_SoftwareStartInjectedConv(ADC_TypeDef* ADCx) | 配置注入通道转换 |
void ADC_InjectedDiscModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState) | 使能注入通道组的间断模式 |
void ADC_ITConfig(ADC_TypeDef* ADCx, uint16_t ADC_IT, FunctionalState NewState) | 使能或者禁止ADC中断 |
FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG) | 检测ADC特定标志是否设置 |
void ADC_ClearFlag(ADC_TypeDef* ADCx, uint8_t ADC_FLAG) | 清除ADC标志 |
ITStatus ADC_GetITStatus(ADC_TypeDef* ADCx, uint16_t ADC_IT) | 通过相应标志检测是否发生相应中断 |
void ADC_ClearITPendingBit(ADC_TypeDef* ADCx, uint16_t ADC_IT) | 清除中断挂起位 |
实现ADC单通道电压采集
CKS32F4xx系列的ADC功能繁多,我们设计几个实验尽量完整的展示ADC的功能。本次课堂主要介绍比较基础实用的单通道电压采集,实现开发板上引脚电压的采集并通过串口打印至PC端串口调试助手。单通道电压采集适用AD转换完成中断,在中断服务函数中读取数据,不使用DMA传输,在后期多通道采集实验中再使用DMA传输。相关核心代码实现如下:
(1)ADC宏定义
// ADC GPIO 宏定义 #define TEMP_ADC_GPIO_PORT GPIOB #define TEMP_ADC_GPIO_PIN GPIO_Pin_0 #define TEMP_ADC_GPIO_CLK RCC_AHB1Periph_GPIOB // ADC 序号宏定义 #define TEMP_ADC ADC1 #define TEMP_ADC_CLK RCC_APB2Periph_ADC1 #define TEMP_ADC_CHANNEL ADC_Channel_8 // ADC 中断宏定义 #define Temp_ADC_IRQ ADC_IRQn #define Temp_ADC_INT_FUNCTION ADC_IRQHandler
(2)ADC GPIO初始化
static void Temp_ADC_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_AHB1PeriphClockCmd(TEMP_ADC_GPIO_CLK, ENABLE);// 使能 GPIO 时钟 GPIO_InitStructure.GPIO_Pin = TEMP_ADC_GPIO_PIN; // 配置 IO GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; // 配置为模拟输入 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ; //不上拉不下拉 GPIO_Init(TEMP_ADC_GPIO_PORT, GPIO_InitStructure); }
使用到GPIO时候都必须开启对应的GPIO时钟,GPIO用于AD转换功能必须配置为模拟输入模式。
(3)配置ADC工作模式
static void Temp_ADC_Mode_Config(void) { ADC_InitTypeDef ADC_InitStructure; ADC_CommonInitTypeDef ADC_CommonInitStructure; RCC_APB2PeriphClockCmd(TEMP_ADC_CLK , ENABLE); // 开启ADC时钟 // -------------------ADC Common 结构体 参数 初始化------------------------ ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; // 独立ADC模式 ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4;// 时钟为fpclk x分频 ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;// 禁止DMA直接访问模式 ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles; // 采样时间间隔 ADC_CommonInit( ADC_CommonInitStructure); // -------------------ADC Init 结构体 参数 初始化-------------------------- ADC_StructInit( ADC_InitStructure); ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; // ADC 分辨率 ADC_InitStructure.ADC_ScanConvMode = DISABLE; // 禁止扫描模式,多通道采集才需要 ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; // 连续转换 ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;//禁止外部边沿触发 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1; //外部触发通道,本例子使用软件触发,此值随便赋值即可 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //数据右对齐 ADC_InitStructure.ADC_NbrOfConversion = 1; //转换通道 1个 ADC_Init(TEMP_ADC, ADC_InitStructure); // 配置 ADC 通道转换顺序为1,第一个转换,采样时间为3个时钟周期 ADC_RegularChannelConfig(TEMP_ADC, TEMP_ADC_CHANNEL, 1, ADC_SampleTime_56Cycles); // ADC 转换结束产生中断,在中断服务程序中读取转换值 ADC_ITConfig(TEMP_ADC, ADC_IT_EOC, ENABLE); ADC_Cmd(TEMP_ADC, ENABLE); // 使能ADC ADC_SoftwareStartConv(TEMP_ADC);//开始ADC转换,软件触发 }
首先,使用ADC_InitTypeDef和ADC_CommonInitTypeDef结构体分别定义一个ADC初始化和ADC通用类型变量。调用RCC_APB2PeriphClockCmd()开启ADC时钟。接下来使用ADC_CommonInitTypeDef结构体变量ADC_CommonInitStructure来配置ADC为独立模式、分频系数为2、不需要设置DMA模式、20个周期的采样延迟,并调用ADC_CommonInit函数完成ADC通用工作环境配置。我们使用ADC_InitTypeDef结构体变量ADC_InitStructure来配置ADC1为12位分辨率、单通道采集不需要扫描、启动连续转换、使用内部软件触发无需外部触发事件、使用右对齐数据格式转换通道为1,并调用ADC_Init函数完成ADC1工作环境配置。
ADC_RegularChannelConfifig函数用来绑定ADC通道转换顺序和时间。它接收4个形参,第一个形参选择ADC外设,可为ADC1、ADC2或ADC3;第二个形参通道选择,总共可选18个通道;第三个形参为转换顺序,可选为1到16;第四个形参为采样周期选择,采样周期越短,ADC转换数据输出周期就越短但数据精度也越低,采样周期越长,ADC转换数据输出周期就越长同时数据精度越高。PC3对应ADC通道ADC_Channel_13,这里我们选择ADC_SampleTime_56Cycles即56周期的采样时间。
利用ADC转换完成中断可以非常方便的保证我们读取到的数据是转换完成后的数据而不用担心该数据可能是ADC正在转换时“不稳定”数据。我们使用ADC_ITConfifig函数使能ADC转换完成中断,并在中断服务函数中读取转换结果数据。ADC_Cmd函数控制ADC转换启动和停止。
最后使用软件触发调用ADC_SoftwareStartConvCmd函数进行使能配置。
(4)在Temp_ADC_NVIC_Config函数中配置ADC转换完成中断的优先级分组和优先级。
static void Temp_ADC_NVIC_Config(void) { NVIC_InitTypeDef NVIC_InitStructure; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); // 配置优先级分组 NVIC_InitStructure.NVIC_IRQChannel = Temp_ADC_IRQ; // 配置中断优先级 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init( NVIC_InitStructure); }
(5)ADC中断服务程序
void ADC_IRQHandler(void) { if(ADC_GetITStatus(TEMP_ADC,ADC_IT_EOC)==SET) { ADC_ConvertedValue = ADC_GetConversionValue(TEMP_ADC); // 读取ADC的转换值 } ADC_ClearITPendingBit(TEMP_ADC,ADC_IT_EOC); }
中断服务函数一般定义在cks32f4xx_it.c文件内,我们使能了ADC转换完成中断,因此在ADC转换完成后就会进入中断服务函数,在中断服务函数内直接读取ADC转换结果保存在变量ADC_ConvertedValue(在main.c中定义)中。ADC_GetConversionValue函数是获取ADC转换结果值的库函数,只有一个形参为ADC外设,可选为ADC1、ADC2或ADC3,该函数返回一个16位的ADC转换结果值。
(6)Main程序
int main(void) { Debug_USART_Config(); //初始化USART1 Temp_Init();while(1) { ADC_Vol =(float) ADC_ConvertedValue/4096*(float)3.3; // 读取转换的AD值 printf("rn The current AD value = %f V rn",ADC_Vol); Delay(0xffffee); } }
主函数先配置调试串口相关参数,接下来调用Temp_Init函数进行ADC初始化配置并启动ADC。在ADC中断服务函数中把AD转换结果保存在变量ADC_ConvertedValue中,可以计算出对应的IO口电压值,最后把相关数据打印至串口调试助手。
来源:中科芯MCU
全部0条评论
快来发表一下你的评论吧 !