1.ADC初始化流程
<1>建立GPIO、ADC初始化结构体
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
<2>打开相应的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
<3>配置GPIO、ADC初始化结构体并初始化
/*GPIO结构体配置 略*/
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //独立模式
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_InitStructure.ADC_NbrOfChannel = 1; //转换通道数 1
ADC_Init(ADC1, &ADC_InitStructure);
<4>设置ADC工作时钟
/* 配置ADC时钟为8分频,即9M */
RCC_ADCCLKConfig(RCC_PCLK2_Div8);
<5>设置ADC转换通道、顺序、及采样时间
ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 1, ADC_SampleTime_13Cycles5);
<6>如果使能中断,要配置使能ADC转换完成中断,中断中读取数据;否则不需要
/* 配置为转换结束后产生中断 在中断中读取信息 */
ADC_ITConfig(ADCx, ADC_IT_EOC,ENABLE);
<7>使能ADC,开启校准寄存器,等待校准完成。
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1); //初始化ADC校准器
while(ADC_GetResetCalibrationStatus(ADC1)); //等待ADC校准寄存器初始化完成
ADC_StartCalibration(ADC1); //开始校准
while(ADC_GetCalibrationStatus(ADC1)); //等待校准完成
2.ADC初始化结构体
typedef struct
{
uint32_t ADC_Mode; // ADC 工作模式选择
FunctionalState ADC_ScanConvMode; // ADC 扫描(多通道)或者单次(单通道)模式选择
FunctionalState ADC_ContinuousConvMode; // ADC 单次转换或者连续转换选择
uint32_t ADC_ExternalTrigConv; // ADC 转换触发信号选择
uint32_t ADC_DataAlign; // ADC 数据寄存器对齐格式
uint8_t ADC_NbrOfChannel; // ADC 采集通道数
} ADC_InitTypeDef;
/ADC模式选择宏定义*/
#define ADC_Mode_Independent ((uint32_t)0x00000000)
#define ADC_Mode_RegInjecSimult ((uint32_t)0x00010000)
#define ADC_Mode_RegSimult_AlterTrig ((uint32_t)0x00020000)
#define ADC_Mode_InjecSimult_FastInterl ((uint32_t)0x00030000)
#define ADC_Mode_InjecSimult_SlowInterl ((uint32_t)0x00040000)
#define ADC_Mode_InjecSimult ((uint32_t)0x00050000)
#define ADC_Mode_RegSimult ((uint32_t)0x00060000)
#define ADC_Mode_FastInterl ((uint32_t)0x00070000)
#define ADC_Mode_SlowInterl ((uint32_t)0x00080000)
#define ADC_Mode_AlterTrig ((uint32_t)0x00090000)
/*ADC_external_trigger_sources_for_regular_channels_conversion */
#define ADC_ExternalTrigConv_T1_CC1 ((uint32_t)0x00000000) /*!< For ADC1 and ADC2 */
#define ADC_ExternalTrigConv_T1_CC2 ((uint32_t)0x00020000) /*!< For ADC1 and ADC2 */
#define ADC_ExternalTrigConv_T2_CC2 ((uint32_t)0x00060000) /*!< For ADC1 and ADC2 */
#define ADC_ExternalTrigConv_T3_TRGO ((uint32_t)0x00080000) /*!< For ADC1 and ADC2 */
#define ADC_ExternalTrigConv_T4_CC4 ((uint32_t)0x000A0000) /*!< For ADC1 and ADC2 */
#define ADC_ExternalTrigConv_Ext_IT11_TIM8_TRGO ((uint32_t)0x000C0000) /*!< For ADC1 and ADC2 */
#define ADC_ExternalTrigConv_T1_CC3 ((uint32_t)0x00040000) /*!< For ADC1, ADC2 and ADC3 */
#define ADC_ExternalTrigConv_None ((uint32_t)0x000E0000) /*!< For ADC1, ADC2 and ADC3 */
#define ADC_ExternalTrigConv_T3_CC1 ((uint32_t)0x00000000) /*!< For ADC3 only */
#define ADC_ExternalTrigConv_T2_CC3 ((uint32_t)0x00020000) /*!< For ADC3 only */
#define ADC_ExternalTrigConv_T8_CC1 ((uint32_t)0x00060000) /*!< For ADC3 only */
#define ADC_ExternalTrigConv_T8_TRGO ((uint32_t)0x00080000) /*!< For ADC3 only */
#define ADC_ExternalTrigConv_T5_CC1 ((uint32_t)0x000A0000) /*!< For ADC3 only */
#define ADC_ExternalTrigConv_T5_CC3 ((uint32_t)0x000C0000) /*!< For ADC3 only */
/*group ADC_data_align*/
#define ADC_DataAlign_Right ((uint32_t)0x00000000)
#define ADC_DataAlign_Left ((uint32_t)0x00000800)
/*ADC通道选择宏定义*/
#define ADC_Channel_0 ((uint8_t)0x00)
#define ADC_Channel_1 ((uint8_t)0x01)
#define ADC_Channel_2 ((uint8_t)0x02)
#define ADC_Channel_3 ((uint8_t)0x03)
#define ADC_Channel_4 ((uint8_t)0x04)
#define ADC_Channel_5 ((uint8_t)0x05)
#define ADC_Channel_6 ((uint8_t)0x06)
#define ADC_Channel_7 ((uint8_t)0x07)
#define ADC_Channel_8 ((uint8_t)0x08)
#define ADC_Channel_9 ((uint8_t)0x09)
#define ADC_Channel_10 ((uint8_t)0x0A)
#define ADC_Channel_11 ((uint8_t)0x0B)
#define ADC_Channel_12 ((uint8_t)0x0C)
#define ADC_Channel_13 ((uint8_t)0x0D)
#define ADC_Channel_14 ((uint8_t)0x0E)
#define ADC_Channel_15 ((uint8_t)0x0F)
#define ADC_Channel_16 ((uint8_t)0x10)
#define ADC_Channel_17 ((uint8_t)0x11)
#define ADC_Channel_TempSensor ((uint8_t)ADC_Channel_16)
#define ADC_Channel_Vrefint ((uint8_t)ADC_Channel_17)
/*ADC采样时间*/
#define ADC_SampleTime_1Cycles5 ((uint8_t)0x00)
#define ADC_SampleTime_7Cycles5 ((uint8_t)0x01)
#define ADC_SampleTime_13Cycles5 ((uint8_t)0x02)
#define ADC_SampleTime_28Cycles5 ((uint8_t)0x03)
#define ADC_SampleTime_41Cycles5 ((uint8_t)0x04)
#define ADC_SampleTime_55Cycles5 ((uint8_t)0x05)
#define ADC_SampleTime_71Cycles5 ((uint8_t)0x06)
#define ADC_SampleTime_239Cycles5 ((uint8_t)0x07)
/* ADC_external_trigger_sources_for_injected_channels_conversion */
#define ADC_ExternalTrigInjecConv_T2_TRGO ((uint32_t)0x00002000) /*!< For ADC1 and ADC2 */
#define ADC_ExternalTrigInjecConv_T2_CC1 ((uint32_t)0x00003000) /*!< For ADC1 and ADC2 */
#define ADC_ExternalTrigInjecConv_T3_CC4 ((uint32_t)0x00004000) /*!< For ADC1 and ADC2 */
#define ADC_ExternalTrigInjecConv_T4_TRGO ((uint32_t)0x00005000) /*!< For ADC1 and ADC2 */
#define ADC_ExternalTrigInjecConv_Ext_IT15_TIM8_CC4 ((uint32_t)0x00006000) /*!< For ADC1 and ADC2 */
#define ADC_ExternalTrigInjecConv_T1_TRGO ((uint32_t)0x00000000) /*!< For ADC1, ADC2 and ADC3 */
#define ADC_ExternalTrigInjecConv_T1_CC4 ((uint32_t)0x00001000) /*!< For ADC1, ADC2 and ADC3 */
#define ADC_ExternalTrigInjecConv_None ((uint32_t)0x00007000) /*!< For ADC1, ADC2 and ADC3 */
#define ADC_ExternalTrigInjecConv_T4_CC3 ((uint32_t)0x00002000) /*!< For ADC3 only */
#define ADC_ExternalTrigInjecConv_T8_CC2 ((uint32_t)0x00003000) /*!< For ADC3 only */
#define ADC_ExternalTrigInjecConv_T8_CC4 ((uint32_t)0x00004000) /*!< For ADC3 only */
#define ADC_ExternalTrigInjecConv_T5_TRGO ((uint32_t)0x00005000) /*!< For ADC3 only */
#define ADC_ExternalTrigInjecConv_T5_CC4 ((uint32_t)0x00006000) /*!< For ADC3 only */
/* ADC_injected_channel_selection */
#define ADC_InjectedChannel_1 ((uint8_t)0x14)
#define ADC_InjectedChannel_2 ((uint8_t)0x18)
#define ADC_InjectedChannel_3 ((uint8_t)0x1C)
#define ADC_InjectedChannel_4 ((uint8_t)0x20)
/* ADC_analog_watchdog_selection */
#define ADC_AnalogWatchdog_SingleRegEnable ((uint32_t)0x00800200)
#define ADC_AnalogWatchdog_SingleInjecEnable ((uint32_t)0x00400200)
#define ADC_AnalogWatchdog_SingleRegOrInjecEnable ((uint32_t)0x00C00200)
#define ADC_AnalogWatchdog_AllRegEnable ((uint32_t)0x00800000)
#define ADC_AnalogWatchdog_AllInjecEnable ((uint32_t)0x00400000)
#define ADC_AnalogWatchdog_AllRegAllInjecEnable ((uint32_t)0x00C00000)
#define ADC_AnalogWatchdog_None ((uint32_t)0x00000000)
/* ADC中断选择 */
#define ADC_IT_EOC ((uint16_t)0x0220)
#define ADC_IT_AWD ((uint16_t)0x0140)
#define ADC_IT_JEOC ((uint16_t)0x0480)
/* ADC_flags_definition */
#define ADC_FLAG_AWD ((uint8_t)0x01)
#define ADC_FLAG_EOC ((uint8_t)0x02)
#define ADC_FLAG_JEOC ((uint8_t)0x04)
#define ADC_FLAG_JSTRT ((uint8_t)0x08)
#define ADC_FLAG_STRT ((uint8_t)0x10)
ADC中的宏定义确实很多,但是相信我,绝大多数都用的到。
3.ADC单次采样初始化例程
void adc_1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOB, &GPIO_InitStructure);
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //独立模式
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_InitStructure.ADC_NbrOfChannel = 1; //转换通道数 1
ADC_Init(ADC1, &ADC_InitStructure);
/* 配置ADC时钟为8分频,即9M */
RCC_ADCCLKConfig(RCC_PCLK2_Div8);
ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 1, ADC_SampleTime_13Cycles5);
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1); //初始化ADC校准器
while(ADC_GetResetCalibrationStatus(ADC1)); //等待ADC校准寄存器初始化完成
ADC_StartCalibration(ADC1); //开始校准
while(ADC_GetCalibrationStatus(ADC1)); //等待校准完成
}
u16 get_adc(void)
{
u16 value;
/*读取ADC的值一定要调用此函数,因为前面配置为软件触发*/
ADC_SoftwareStartConvCmd(ADC1,ENABLE);
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
value = ADC_GetConversionValue(ADC1);
return value;
}
ADC_InitStructure.ADC_ExternalTrigConv 配置为 ADC_ExternalTrigConv_None后,读取ADC的值时一定要调用ADC_SoftwareStartConvCmd(ADCx,ENABLE),因为前面配置为软件触发,单次采样模式下,末次采样前都需要触发操作,否则ADC不会做出工作。
单次采样可概括为:
-
1、单次转换模式下,ADC只执行一次转换。
-
2、 一旦选择通道的转换完成:
-
- 如果一个规则通道被转换: ─ 转换数据被储存在16位ADC_DR寄存器中 ─ EOC(转换结束)标志被设置 ─ 如果设置了EOCIE,则产生中断。
-
- 如果一个注入通道被转换: ─ 转换数据被储存在16位的ADC_DRJ1寄存器中 ─ JEOC(注入转换结束)标志被设置 ─ 如果设置了JEOCIE位,则产生中断。
-
3、然后ADC停止
注意:不要让ADC时钟超过14MHz,否则可能不准。
4.ADC连续采样,中断发送数据例程
void adc_1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
NVIC_InitTypeDef NVIC_InitStruct ;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOB, &GPIO_InitStructure);
NVIC_InitStruct.NVIC_IRQChannel = ADC_IRQn ;
/* 配置抢占优先级 */
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1 ;
/* 配置子优先级 */
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1 ;
/* 使能中断通道 */
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE ;
/* 调用初始化函数 */
NVIC_Init(&NVIC_InitStruct) ;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent //独立模式
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //禁止扫面,单通道不需要扫描
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //连续转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //不用外部触发
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //右对齐
ADC_InitStructure.ADC_NbrOfChannel = 1; //转换通道数 1
ADC_Init(ADC1, &ADC_InitStructure);
/* 配置ADC时钟为8分频,即9M */
RCC_ADCCLKConfig(RCC_PCLK2_Div8);
ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 1, ADC_SampleTime_13Cycles5);
/* 配置为转换结束后产生中断 在中断中读取信息 */
ADC_ITConfig(ADCx, ADC_IT_EOC,ENABLE);
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1); //初始化ADC校准器
while(ADC_GetResetCalibrationStatus(ADC1)); //等待ADC校准寄存器初始化完成
ADC_StartCalibration(ADC1); //开始校准
while(ADC_GetCalibrationStatus(ADC1)); //等待校准完成
ADC_SoftwareStartConvCmd(ADC1,ENABLE); //软件触发,ADC启动
}
extern uint16_t resurt;
/*关于中断函数的名称,不同的系列是不同的,这一点着实*/
void ADC1_2_IRQHandler(void)
{
/* 判断产生中断请求 */
while(ADC_GetITStatus(ADCx, ADC_IT_EOC) == SET)
/*EOC转换结束标志,在转换结束后被置位*/
resurt=ADC_GetConversionValue(ADCx);
/* 清除中断标志 */
ADC_ClearITPendingBit(ADCx, ADC_IT_EOC);
}
注意:
- 不要让ADC时钟超过14MHz,否则可能不准。
- 扫描模式在最后一个通道转换结束才会产生中断。
5.ADC的一些补充
1、ADC的基础知识
STM32F10x ADC特点
- 12位(位数决定精度)逐次逼近型的模拟数字转换器。
- 最多带3个ADC控制器(每个控制器有多个通道)
- 最多支持18个通道,可最多测量16个外部和2个内部信号源。
- 支持单次和连续转换模式
- 转换结束,注入转换结束,和发生模拟看门狗事件时产生中断。
- 通道0到通道n的自动扫描模式
- 自动校准
- 采样间隔可以按通道编程
- 规则通道和注入通道均有外部触发选项
- 转换结果支持左对齐或右对齐方式存储在16位数据寄存器
- ADC转换时间:最大转换速率 1us。(最大转换速度为1MHz,在ADCCLK=14M,采样周期为1.5个ADC时钟下得到。)
- ADC供电要求:2.4V-3.6V(通常0-3.3)
- DMA功能(仅ADC1有)
2、关于ADC的时钟:
1 HIS RC
2 HIE OSE
3、关于ADC的采样率
每个通道都可以单独配置为不同的采样时间,单个通道的转换时间计算公式为:
Tconv = Sampling time + 12.5 cycles.
例如:
当ADC时钟为14MHz并且软件配置采样时间为1.5cycles(即1.5个ADC时钟):
Tconv = 1.5 + 12.5 = 14cycles = 1us
(14 cycles即是14个ADC时钟,14 / 14MHz = 1us,即1us)
4、问题总结
ADC的分辨率,可选的分辨率有12位、10位、8位和6位。分辨率越高,AD转换数据精度越高,转换时间也越长;分辨率越低,AD转换数据精度越低,转换时间也越短。默认是12位的分辨率,数据左对齐需要除以16。
ADC时钟是有PCLK2分频而来,分频系数决定ADC时钟频率,可选的分频系数为2、4、6和8。ADC最大时钟配置为36MHz。
每个转换通道都可以单独配置采样的时间周期,单个通道最大转换速率为1us。
ADC的采样周期跟与采样的目标相关,采集有频率的目标需要目标频率设置采样周期,采样周期越长,精度越高,但是时间越长。
使用ADC_RegularChannelConfig函数可以配置通道采集的先后顺序,配合DMA的缓存数组可以确定哪一个通道存放在哪一个内存。
DMA_BufferSize 表示的是有几个缓存,如果DMA_PeripheralDataSize 和DMA_MemoryDataSize都设置的是半字,那1个DMA_BufferSize 就表示1个半字缓存,注意DMA_PeripheralDataSize 和DMA_MemoryDataSize的配置必须一致。
扫描模式用于多通道采集时,一轮采集为根据通道设定的顺序依次采集每个开启的通道。但要注意的是每个通道采集到的值都是存放在寄存器ADCx-> DR中,而只有当一轮采集完即所有设定的通道都采集完后采集完成标志才会被置位,所以不使用DMA传输方式的时候会出现采集到的数据被覆盖的现象。aSize 和DMA_MemoryDataSize都设置的是半字,那1个DMA_BufferSize 就表示1个半字缓存,注意
DMA_PeripheralDataSize 和DMA_MemoryDataSize的配置必须一致。扫描模式用于多通道采集时,一轮采集为根据通道设定的顺序依次采集每个开启的通道。但要注意的是每个通道采集到的值都是存放在寄存器ADCx-> DR中,而只有当一轮采集完即所有设定的通道都采集完后采集完成标志才会被置位,所以不使用DMA传输方式的时候会出现采集到的数据被覆盖的现象。
规则通道组中可以安排最多16个通道,而注入通道组可以安排最多4个通道。规则组是程序按照配置规则转换,注入组是根据外部触发中断来转换,在执行规则通道组扫描转换时,如有例外处理则可启用注入通道组的转换。