完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
一、ADC介绍
12位ADC是一种逐次逼近型模拟数字转换器。它有多达18个通道,可测量16个外部和2个内部信号源。各通道的A/D转换可以单次、连续、扫描或间断模式执行。ADC的结果可以左对齐或右对齐方式存储在16位数据寄存器中。 二、框图分析 按照顺序分析: 1、电压输入范围:ADC 输入范围为:VREF- ≤ VIN ≤ VREF+。由 VREF-、VREF+ 、VDDA、 VSSA、这四个外部引脚决定。一般把 VSSA 和 VREF-接地,把 VREF+和 VDDA 接 3V3,得到ADC 的输入电压范围为:0~3.3V。 2、输入通道:可分为注入通道和规则通道 前者常用。关于输入通道查询,可以参考《STM32F103xCDE_数据手册》 下面是总结好的表格:F103x 系列 对应的引脚几乎完全一样 下面是ZET6的 3、 转换顺序: 规则序列 : 通过控制ADC规则序列寄存器 ADC_SQR1 、ADC_SQR2、ADC_SQR3 来配置规则通道的转换顺序, 如果通道 16 想第一次转换,那么在 SQ1[4:0] 写 16 即可。SQR2 控制着规则序列中的第 7 到第12 个转换,对应的位为:SQ7[4:0]~SQ12[4:0],如果通道 1 想第 8 个转换,则 SQ8[4:0] 写 1 即可。SQR1 控制着规则序列中的第 13 到第 16 个转换,对应位为:SQ13[4:0]~SQ16[4:0],如果通道 6 想第 10 个转换,则 SQ10[4:0] 写 6 即可。具体使用多少个通道,由 SQR1 的位L[3:0]决定,最多 16 个通道。 注入序列: ADC注入序列寄存器ADC_JSQR 。注入序列寄存器 JSQR 只有一个,最多支持 4 个通道,具体多少个由 JSQR 的 JL[2:0]决定。如果 JL 的 值小于 4 的话,则 JSQR 跟 SQR 决定转换顺序的设置不一样,第一次转换的不是 JSQR1[4:0],而是 JCQRx[4:0] ,x = (4-JL),跟 SQR 刚好相反。如果 JL=00(1个转换),那么转换的顺序是从 JSQR4[4:0]开始,而不是从 JSQR1[4:0]开始,这个要注意,编程的时候不要搞错。当 JL 等于 4 时,跟 SQR 一样。 4、触发源选择 通道配置完成,转换顺序也已经确定,下面就是触发源的选择。通过ADC控制寄存器 1(ADC_CR1)、ADC控制寄存器 2(ADC_CR2),选择触发源: 5、转换时间 ADC 时钟: ADC 输入时钟 ADC_CLK 由 PCLK2 经过分频产生,最大是 14M,分频因子由 RCC 时钟配置寄存器 RCC_CFGR 的位 15:14 ADCPRE[1:0]设置,可以是 2/4/6/8 分频,一般我们设置 PCLK2=HCLK=72M。一般选择8分频。 采样时间: 采样的周期数可通过 ADC 采样时间寄存器 ADC_SMPR1 和 ADC_SMPR2 中的SMP[2:0]位设置,ADC_SMPR2 控制的是通道 0~9,ADC_SMPR1 控制的是通道 10~17。每个通道可以分别用不同的时间采样。其中采样周期最小是 1.5 个,即如果我们要达到最快的采样,那么应该设置采样周期为 1.5个周期,这里说的周期就是 1/ADC_CLK。 ADC 的转换时间跟 ADC 的输入时钟和采样时间有关公式为:Tconv = 采样时间 + 12.5 个周期。当 ADCLK = 14MHZ (最高),采样时间设置为 1.5 周期(最快),那么总 的转换时间(最短)Tconv = 1.5 周期 + 12.5 周期 = 14 周期 = 1us。一般我们设置 PCLK2=72M,经过 ADC 预分频器能分频到最大的时钟只能是 12M,采样周期设置为 1.5 个周期,算出最短的转换时间为 1.17us(常用1.17us)。 6、数据寄存器:一切准备就绪后,ADC 转换后的数据根据转换组的不同,规则组的数据放在 ADC_DR寄存器,注入组的数据放在 JDRx。 规则数据寄存器 : 注入数据寄存器: 7、中断: 转换结束中断: 数据转换结束后,可以产生中断,中断分为三种:规则通道转换结束中断,注入转换通道转换结束中断,模拟看门狗中断。可以根据相应的中断标志位,编写对应的中断服务函数。事件是否发生,可以访问ADC状态寄存器(ADC_SR) 模拟看门狗中断: 当被 ADC 转换的模拟电压低于低阈值或者高于高阈值时,就会产生中断,前提是我们开启了模拟看门狗中断,其中低阈值和高阈值由 ADC_LTR 和 ADC_HTR 设置。例如我们设置高阈值是 2.5V,那么模拟电压超过 2.5V 的时候,就会产生模拟看门狗中断,反之低阈值也一样。 三、编程相关 在编程的时候,我们只需要配置对用的结构体即可: ADC_InitTypeDef 结构体 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_Mode:配置 ADC 的模式,当使用一个 ADC 时是独立模式,使用两个 ADC 时 是双模式,在双模式下还有很多细分模式可选。 ScanConvMode:可选参数为 ENABLE 和 DISABLE,配置是否使用扫描。如果是单通 道 AD 转 换 使 用 DISABLE , 如 果 是 多 通 道 AD 转 换 使 用 ENABLE 。 ADC_ContinuousConvMode:可选参数为 ENABLE 和 DISABLE,配置是启动自动连 续转换还是单次转换。使用 ENABLE 配置为使能自动连续转换;使用 DISABLE 配置为单 次转换,转换一次后停止需要手动控制才重新启动转换。 ADC_ExternalTrigConv:外部触发选择,框图中中列举了很多外部触发条件,可根据 项目需求配置触发来源。一般使用软件自动触发。 ADC_DataAlign:转换结果数据对齐模式,可选右对齐 ADC_DataAlign_Right 或者左 对齐 ADC_DataAlign_Left。一般选择右对齐模式。不然读取数据 还得处理。 ADC_NbrOfChannel:AD 转换通道数目,根据实际设置即可。具体的通道数和通道的 转换顺序是配置规则序列或注入序列寄存器 实例一:《中断单通道读取ADC》 编程要点 初始 ADC 用到的 GPIO; 设置 ADC 的工作参数并初始化; 设置 ADC 工作时钟; 设置 ADC 转换通道顺序及采样时间; 配置使能 ADC 转换完成中断,在中断内读取转换完数据; 使能 ADC; 使能软件触发 ADC 转换。 ADC 转换结果数据使用中断方式读取,这里没有使用 DMA 进行数据传输。 1.初始 ADC 用到的 GPIO void ADCx_GPIO_Config(void) { //以PA1为例 GPIO_InitTypeDef GPIO_InitStruct; //开启GPIO时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //选择模拟模式 GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN; GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1; GPIO_Init(ADCx_GPIO_PORT,&GPIO_InitStruct); } 2.配置ADC void ADCx_Config(void) { //ADC1 Channel_1 ADC_InitTypeDef ADC_InitStruct; //开启ADC时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE); ADC_InitStruct.ADC_Mode = ADC_Mode_Independent; // 只使用一个ADC,属于单模式 ADC_InitStruct.ADC_ScanConvMode = DISABLE; //只有一个通道,关闭扫描模式 ADC_InitStruct.ADC_ContinuousConvMode = ENABLE; // 连续转换模式 ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;// 不用外部触发转换,软件启即可 ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right; // 转换结果右对齐 ADC_InitStruct.ADC_NbrOfChannel = 1; // 转换通道个数 //初始化 ADC_Init(ADC1,&ADC_InitStruct); //配置ADC时钟N狿CLK2的8分频,即9MHz RCC_ADCCLKConfig(RCC_PCLK2_Div8); // 配置ADC 通道的转换顺序和采样时间 ADC_RegularChannelConfig(ADC1,ADC_1_Channel,1,ADC_SampleTime_55Cycles5); // ADC 转换结束产生中断,在中断服务程序中读取转换值 ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE); // 开启ADC ,并开始转换 ADC_Cmd(ADC1,ENABLE); ADC_ResetCalibration(ADC1); // 等待校准寄存器初始化完成 while(ADC_GetResetCalibrationStatus(ADC1)); // ADC开始校准 ADC_StartCalibration(ADC1); // 等待校准完成 while(ADC_GetCalibrationStatus(ADC1)); // 由于没有采用外部触发,所以使用软件触发ADC转换 ADC_SoftwareStartConvCmd(ADC1,ENABLE); } 3.配置NVIC void ADC_NVIC_Config(void) { NVIC_InitTypeDef NVIC_InitStructure; // 优先级分组 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); // 配置中断优先级 NVIC_InitStructure.NVIC_IRQChannel = ADC1_2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } 4.编写中断服务函数 void ADC1_2_IRQHandler(void) { uint8_t i = 0; if (ADC_GetITStatus(ADC_x,ADC_IT_EOC)==SET) { //读取ADC的转换值 ADC_Convert_Value =(float)ADC_GetConversionValue(ADC_x)/4096*3.3; ADC_ClearITPendingBit(ADC_x,ADC_IT_EOC); //清除转换完成标志位,等待下一次 } } 5.测试函数 uint16_t ADC_USART_Value = 0; uint16_t ADC_USART_Value2 = 0; void _ADC_IT_Init(void) { ADCx_GPIO_Config(); ADCx_Config(); ADC_NVIC_Config(); } void delay(uint32_t count) { while(count--); } int main(void) { LED_GPIO_Config(); USARTx_Config(); ADC_IT_Init(); printf("rn ----这是一个ADC多通道采集实验----rn"); while(1) { ADC_USART_Value 2=(float) ADC_USART_Value ; printf("rn The current AD value = 0x%04X rn", ADC_USART_Value; printf("rn The current AD value = %f V rn",ADC_USART_Value2); printf("rnrn"); delay(0x3fffff); } } 实例二:《DMA多通道读取ADC》 编程要点
DMA1 的通道 1 DMA2 的通道 5 1 .初始 ADC 用到的 GPIO void ADCx_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStruct; RCC_APB2PeriphClockCmd(ADCx_GPIO_CLK,ENABLE); GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN; GPIO_InitStruct.GPIO_Pin = ADC_1_Pin| ADC_1_Pin| //ADC_2_Pin和SUART冲突 因此删除 ADC_3_Pin| ADC_4_Pin| ADC_5_Pin| ADC_6_Pin| ADC_7_Pin; GPIO_Init(ADCx_GPIO_PORT,&GPIO_InitStruct); } 2.初始 DMA void ADC_DMA_Config(void) { DMA_InitTypeDef DMA_InitStruct; RCC_AHBPeriphClockCmd(DMA_CLK,ENABLE); //外设基址为:ADC 数据寄存器地址 DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)(&(ADC_x->DR)); // 存储器地址 DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)ADC_USART_Value; // 数据源来自外设 DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC; //缓冲区大小,应该等于数据目的地的大小 DMA_InitStruct.DMA_BufferSize = BUFFERSIZE; // 外设寄存器只有一个,地址不用递增 DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //存储器地址递增 DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; //外设数据大小为半字,即两个字节 DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //内存数据大小也为半字,跟外设数据大小相同 DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //循环传输模式 DMA_InitStruct.DMA_Mode = DMA_Mode_Circular; //DMA传输通道优先级为高,当使用一个DMA通道时,优先级设置不影响 DMA_InitStruct.DMA_Priority = DMA_Priority_High; //禁止存储器到存储器模式,因为是从外设到存储器 DMA_InitStruct.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA_CHANNEL,&DMA_InitStruct); DMA_Cmd(DMA_CHANNEL,ENABLE); } 3.初始 ADC void ADCx_Config(void) { ADC_InitTypeDef ADC_InitStruct; //开启ADC时钟 RCC_APB2PeriphClockCmd(ADCx_CLK,ENABLE); ADC_InitStruct.ADC_Mode = ADC_Mode_Independent; // 只使用一个ADC,属于单模式 ADC_InitStruct.ADC_ScanConvMode = ENABLE; //6通道开启扫描 ADC_InitStruct.ADC_ContinuousConvMode = ENABLE;// 连续转换模式 ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;// 不用外部触发转换,软件开启即可 ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right; // 转换结果右对齐 ADC_InitStruct.ADC_NbrOfChannel = ADC_num; // 转换通道个数 //初始化 ADC_Init(ADC_x,&ADC_InitStruct); //配置ADC时钟N狿CLK2的8分频,即9MHz RCC_ADCCLKConfig(RCC_PCLK2_Div8); // 配置ADC 通道的转换顺序和采样时间 ADC_RegularChannelConfig(ADC_x,ADC_1_Channel,1,ADC_SampleTime_55Cycles5); ADC_RegularChannelConfig(ADC_x,ADC_4_Channel,2,ADC_SampleTime_55Cycles5); //A2 A3用于串口 ADC_RegularChannelConfig(ADC_x,ADC_5_Channel,3,ADC_SampleTime_55Cycles5); ADC_RegularChannelConfig(ADC_x,ADC_6_Channel,4,ADC_SampleTime_55Cycles5); ADC_RegularChannelConfig(ADC_x,ADC_7_Channel,5,ADC_SampleTime_55Cycles5); // 使能ADC DMA 请求 ADC_DMACmd(ADC_x, ENABLE); // 开启ADC ,并开始转换 ADC_Cmd(ADC_x,ENABLE); ADC_ResetCalibration(ADC_x); // 等待校准寄存器初始化完成 while(ADC_GetResetCalibrationStatus(ADC_x)); // ADC开始校准 ADC_StartCalibration(ADC_x); // 等待校准完成 while(ADC_GetCalibrationStatus(ADC_x)); // 由于没有采用外部触发,所以使用软件触发ADC转换 ADC_SoftwareStartConvCmd(ADC_x,ENABLE); } void ADCx_DMA_Init(void) { ADCx_GPIO_Config(); ADC_DMA_Config(); ADCx_Config(); } 4.测试函数 void delay(uint32_t count) { while(count--); } int main(void) { LED_GPIO_Config(); USARTx_Config(); ADCx_DMA_Init(); //ADC_IT_Init(); printf("rn ----这是一个ADC多通道采集实验----rn"); while(1) { printf("r PA1 value = %f V rn",(float)ADC_USART_Value[0]/4096*3.3); printf("r PA4 value = %f V rn",(float)ADC_USART_Value[1]/4096*3.3); printf("r PA5 value = %f V rn",(float)ADC_USART_Value[2]/4096*3.3); printf("r PA6 value = %f V rn",(float)ADC_USART_Value[3]/4096*3.3); printf("r PA7 value = %f V rnnnnn",(float)ADC_USART_Value[4]/4096*3.3); delay(0x3fffff); } } 头文件 #include "bsp_dma.h" #ifndef _BSP_DMA_H_ #define _BSP_DMA_H_ #include "stm32f10x.h" #define BUFFERSIZE 5 #define DMA_CHANNEL DMA1_Channel1 //手册 #define DMA_CLK RCC_AHBPeriph_DMA1 void ADC_DMA_Config(void); #endif /*_BSP_DMA_H_*/ #include "bsp_adc.h" #ifndef _BSP_ADC_H_ #define _BSP_ADC_H_ #include "stm32f10x.h" #define ADC_num 5 #define ADC_x ADC1 #define ADCx_CLK RCC_APB2Periph_ADC1 #define ADCx_GPIO_PORT GPIOA #define ADCx_GPIO_CLK RCC_APB2Periph_GPIOA #define ADC_1_Pin GPIO_Pin_1 #define ADC_2_Pin GPIO_Pin_2 #define ADC_3_Pin GPIO_Pin_3 #define ADC_4_Pin GPIO_Pin_4 #define ADC_5_Pin GPIO_Pin_5 #define ADC_6_Pin GPIO_Pin_6 #define ADC_7_Pin GPIO_Pin_7 #define ADC_1_Channel ADC_Channel_1 #define ADC_2_Channel ADC_Channel_2 #define ADC_3_Channel ADC_Channel_3 #define ADC_4_Channel ADC_Channel_4 #define ADC_5_Channel ADC_Channel_5 #define ADC_6_Channel ADC_Channel_6 #define ADC_7_Channel ADC_Channel_7 extern uint16_t ADC_USART_Value[ADC_num]; extern float ADC_Convert_Value[ADC_num]; void ADCx_GPIO_Config(void); void ADCx_Config(void); void ADC_DMA_Config(void); void ADCx_DMA_Init(void); #endif /*_BSP_ADC_H_*/ |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1649 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1566 浏览 1 评论
994 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
694 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1613 浏览 2 评论
1872浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
656浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
526浏览 3评论
543浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
515浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-27 21:09 , Processed in 0.621753 second(s), Total 46, Slave 40 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号