笔记10:STM32F407ADC简介+定时器触发ADC1 您所在的位置:网站首页 中断的触发方式有哪些 笔记10:STM32F407ADC简介+定时器触发ADC1

笔记10:STM32F407ADC简介+定时器触发ADC1

2024-07-08 17:59| 来源: 网络整理| 查看: 265

ADC,即Anolog to Digital Converter,模拟信号到数字信号的转换。 模拟信号:连续的信号,就像连续函数一样的 数字信号:离散的信号,数字信号,就像点一样。 举例:若我们要将3.3V的交流电压,正弦信号转化为数字信号,若3.3V用数字信号100表示,则1.65V则用熟悉信号50表示。 一. 采集信号范围:0-3.3V

ADC 输入范围为: VREF- ≤ VIN ≤ VREF+。由 VREF-、 VREF+ 、 VDDA 、 VSSA、这四个外部引脚决定.在设计时,已经将VREF-与VSSA(模拟地)接在一起,而VREF+=3.3V,因此输入范围为0-3.3V。 在这里插入图片描述 问题:若想要测超出这个范围的电压怎么办?举例,测量-10V —10V 对输入信号进行外部处理。 -10V->0V 10V->3.3V 在这里插入图片描述 在这里插入图片描述 电阻的确定:先确定其中两个,再确定最后一个。

二. 3路18通道

STM32F407有三路ADC,可配置 12 位、 10 位、 8 位或 6 位分辨率,分别是ADC1,ADC2,ADC3,每一路有18个通道,有16个外部可以看到的通道,有两个内部通道。但是并不是说有三路,每路18个通道就总共有48个(不同)通道,三路有些通道是一样的,加起来外部总的有23个通到可以使用。对于ADC1来说,只有PA6,PB0,PB1,PC2没有被复用过,可以纯净的用作ADC1,其他的I/O口已经在其他地方使用,可能采集的信号会有杂波,使用被复用过的I/O口时,可以在使用之前做一个小测试,将I/O单独接入0V或者3.3V,观察能否采集到这两个极值,如果能的话,就说明基本不会产生杂波影响。(野火版的) 在这里插入图片描述(野火版F407 ADC与I/O的对应) 在这里插入图片描述 (正点原子版F407与I/O的对应)

通道类型:规则通道和注入通道 在这里插入图片描述 什么是注入通道,什么是规则通道? 注入通道:可以理解为插入,插队的意思,是一种不安分的通道。它是一种在规则通道转换的时候强行插入要转换的一种。如果在规则通道转换过程中,有注入通道插队,那么就要先转换完注入通道,等注入通道转换完成后,再回到规则通道的转换流程。这点跟中断程序很像。因此注入通道只有在规则通道存在时才会出现。 规则通道就是很规矩的意思,我们平时一般使用的就是这个通道,或者应该说我们用到的都是这个通道 三 .通道的优先顺序 通道的优先顺序由以下寄存器控制,详见STM32F4XX中文参考手册 在这里插入图片描述

规则序列通道的优先顺序 在这里插入图片描述 说明:SQR1的SQL决定需要控制的通道数,取值范围在1-16。若你想让通道0优先顺序为1,则你在SQR3的SQ1[4:0]位写入0x00就好了。

注入通道的优先顺序 在这里插入图片描述 四.触发源:告诉ADC开始采集的这个信号就叫触发 在这里插入图片描述 详见STM32F4XX中文参考手册: 在这里插入图片描述 五.转换时间

转换时间=采样周期(自己设置,最大为3,也是最快的)+分辨率个周期(12位分辨率就是12个周期) ADC的周期:ADC_ CLK(挂在APB2总线下) : ADC模拟电路时钟,最大值为36M,由PCLK2提供,还可分频,2/4/6/8 , ADC_ CCR的ADCPRE[1:0]设置。PCLK2= 84M。有关ADC_ CLK时钟的具体描述参考datasheet:5.3.21 数字时钟: RCC APB2ENR ,用于访问寄存器

采样时间 采样时间: ADC需要若干个ADC_ CLK周期完成对输入的模拟量进行采样,采样的周期数可通过ADC采样时间寄存器ADC SMPR1和ADC_ _SMPR2中的SMP[2:0]位设置,

ADC_ SMPR2控制的是通道0~9,ADC_ SMPR1控制的是通道10~17。每个通道可以分别用不同的时间采样。其中采样周期最小是3个,即如果我们要达到最快的采样, 那么应该设置采样周期为3个周期,这里说的周期就是1/ADC_ CLK。

在这里插入图片描述 举例:最短的转换时间:Tconv=采样时间+12个周期(12) PCLK2=84M,ADC_CLK=84/4=21M Tconv =3+12 = 15周期 = 15/21us=0.71us 六.转换方式

在这里插入图片描述 七.数据寄存器 一切准备就绪后,ADC转换后的数据根据转换组的不同,规则组的数据放在ADC_ DR寄存器,注入组的数据放在JDRx。如果是使用双重或者三重模式那规矩组的数据:是存放在通用规则寄存器ADC_ CDR内的。 在这里插入图片描述

规则数据寄存器:ADC_ DR寄存器 在这里插入图片描述 1.1. 只有16位有效数据,用于存放单个ADC模式转换完成数据 1.2. 根据ADC_CR2:ALIGN位选择数据对齐方式 1.3. 只有一个规则通道数据寄存器,由于数据都是存放在这个数据寄存器中,如果不及时取走,数据就会被覆盖,因此多通道的时候最好使用中断或着DMA 模式。我们可以将每一个通道的数据放入不同数组,再通过DMA得到数据。注入数据寄存器 在这里插入图片描述 2.1. 只有16位有效数据,用于存放单个ADC模式转换完成数据 2.2. 根据ADC_CR2:ALIGN位选择数据对齐方式 2.3. 有四个这样的寄存器ADC_CDR 在这里插入图片描述

在这里插入图片描述 1-32位有效,双重或者三重ADC使用,必须配合DMA 八.ADC的中断 涉及到的寄存器: 1 ADC _ SR , ADC_ CR1 2 ADC_ HTR , ADC_ LTR

在这里插入图片描述 看门狗中断是用来保护模拟信号范围的。举例:若采集到的信号电压范围为1V-3V,那么我们可以设置阈值上限电压为3V,阈值下限电压为1V,这样超出这个数据范围之外的错误电压信号就可以报警产生中断了。 模拟看门狗如何保护多个通道: 在这里插入图片描述 九.模拟量计算: 怎么根据数据量算出模拟量

1-电压输入范围为: 0~3.3V

2-分辨率为12位

3-最小精度为: 3.3/2^12

4-设数字量为X ,则有模拟量Y =(3.31 2^12)*X

十. 单通道ADC1+定时器3触发并在LCD上显示波形和幅值的程序源码 adc.h文件

#ifndef __ADC_H #define __ADC_H #include "sys.h" #define TableSize 320 void Adc1_Init(void); void ADC_IRQHandler(void); #endif

adc.c文件

#include "adc.h" #include "delay.h" #include "timer.h" u32 ADC_EndFlag; float Sintable[TableSize]; void Adc1_Init(void) { ADC_InitTypeDef ADC_InitStruct; GPIO_InitTypeDef GPIO_InitStructure; ADC_CommonInitTypeDef ADC_CommonInitStruct; NVIC_InitTypeDef NVIC_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); //使能 GPIOA 时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //使能 ADC1 时钟 GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1; //选择ADC1通道1 GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AN; //设置成模拟输入模式 GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_NOPULL; //不使用上拉和下拉 GPIO_Init(GPIOA,&GPIO_InitStructure); RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,ENABLE); //ADC1复位 RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,DISABLE); //复位结束 ADC_CommonInitStruct.ADC_Mode=ADC_Mode_Independent; //ADC独立工作模式 ADC_CommonInitStruct.ADC_Prescaler=ADC_Prescaler_Div4; //ADC1挂在APB2=84M,因此ADC时钟频率21MHZ ADC_CommonInitStruct.ADC_DMAAccessMode=ADC_DMAAccessMode_Disabled; //ADC1通道不采用直接访问DMA模式 ADC_CommonInitStruct.ADC_TwoSamplingDelay=ADC_TwoSamplingDelay_5Cycles; //两个采样时间之间的延迟,双重或者多重才有效 ADC_CommonInit(&ADC_CommonInitStruct); ADC_InitStruct.ADC_Resolution=ADC_Resolution_12b; //12位分辨率 ADC_InitStruct.ADC_ScanConvMode=DISABLE; //采用单通道,因此不需要开启扫描模式 ADC_InitStruct.ADC_ContinuousConvMode=DISABLE; //不开启连续采集,否则除开第一次后面的数据采集就不是定时器采集了 ADC_InitStruct.ADC_ExternalTrigConvEdge=ADC_ExternalTrigConvEdge_Rising; //上升沿触发有效 ADC_InitStruct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_T3_TRGO; //定时器TIM3触发 ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right; //数据右对齐 ADC_InitStruct.ADC_NbrOfConversion = 1; //转换通道1个,这个通道是指每个ADC的通道数 ADC_Init(ADC1,&ADC_InitStruct); ADC_ITConfig(ADC1,ADC_IT_EOC,ENABLE); NVIC_InitStructure.NVIC_IRQChannel=ADC_IRQn; //选择TIM3的定时器中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x03; //抢占优先级为3 NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x02; //子优先级为2 NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; NVIC_Init(&NVIC_InitStructure); ADC_Cmd(ADC1, ENABLE); //开启AD转换器 ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_144Cycles ); //设置指定 ADC 的规则组通道1(PA1),第一个采集,采样时间144个周期,一个周期为1/21us } void ADC_IRQHandler(void) { static u32 n=0; if(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)!=RESET) //等待转换结束 { Sintable[n++]=ADC_GetConversionValue(ADC1)*3.3f/4096; //返回最近一次 ADC1 规则组的转换结果 //将读取到的数字量存入数组中 } if(n==TableSize) { n=0; ADC_EndFlag=1; ADC_ITConfig(ADC1,ADC_IT_EOC,DISABLE); //采集TableSize个数据后关闭ADC中断 } ADC_ClearITPendingBit(ADC1,ADC_FLAG_EOC); //清除中断标志位 }

timer.c文件

#include "timer.h" #include "math.h" #include "adc.h" void TIM3_Int_Init(u16 arr,u16 psc) { TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); //使能TIM3上的APB1时钟 TIM_TimeBaseInitStruct.TIM_Prescaler=psc; //TIM3的定时器分频系数 TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up; //TIM3为向下计数方式 TIM_TimeBaseInitStruct.TIM_Period=arr; //TIM3的自动重装载值 TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1; //时钟分频因子 TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct); TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update); //定时器2更新触发DAC TIM_Cmd(TIM3,ENABLE); //使能定时器3 }

mian.c文件

#include "sys.h" #include "lcd.h" #include "adc.h" #include "delay.h" #include "usart.h" #include "led.h" #include "timer.h" extern float Sintable[TableSize]; extern u32 SINtable[TableSize]; extern u32 ADC_EndFlag; void WaveformDisplay() //在LCD屏幕上显示波型 { u32 i; u32 m=1; for(i=1;i float max1=0; u32 i=0; for(i=0;i float max1=0; u16 adcx; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置系统中断优先级分组2 delay_init(168); //初始化延时函数 uart_init(115200); //初始化串口波特率为115200 LED_Init(); //初始化LED LCD_Init(); //初始化LCD接口 Adc1_Init(); //初始化ADC TIM3_Int_Init(4,83); //5us定时器开始触发 LCD_Display_Dir(1); //横屏显示 POINT_COLOR=RED; while(1) { if(ADC_EndFlag==1) { LCD_Clear(WHITE); WaveformDisplay(); LCD_ShowString(30,150,200,16,16,"ADC1_CH1_VOL:0.000V"); //这里显示了小数点 max1=MAX(); adcx=max1; //赋值整数部分给 adcx 变量,因为 adcx 为 u16 整型 LCD_ShowxNum(134,150,adcx,1,16,0); //显示电压值的整数部分 max1-=adcx; //把已经显示的整数部分去掉,留下小数部分,比如 3.1111-3=0.1111 max1*=1000; //小数部分乘以 1000,例如: 0.1111 就转换为 111.1,保留三位小数。 LCD_ShowxNum(150,150,max1,3,16,0X80); //显示小数部分 ADC_EndFlag=0; ADC_ITConfig(ADC1,ADC_IT_EOC,ENABLE); //开启ADC中断,实现实时采集 } LED0=!LED0; delay_ms(50); } }

本文为观看野火视频学习总结文章



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有