16:(标准库)ADC三:使用外部触发启动ADC/模拟看门狗
使用外部触发启动ADC
- 1、外部中断线EXTI11触发ADC
- 2、外部定时器TIM2_CH2触发ADC
- 3、ADC中模拟看门狗的使用
1、外部中断线EXTI11触发ADC
ADC的触发方式有很多,一般情况都是使用软件触发反式启动ADC转换。除了软件触发方式还能使用外部事件触发启动ADC转换。如下图所示:
①EXTI.c文件的代码如下:
#include "EXTI.h"/*** 初始化外部中断线EXTI11,将配置为上升沿触发事件模式*/
void EXTI11_Init(void)
{/* 1、使能GPIOA时钟,AFIO时钟 */RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);/* 2、配置PA11为下拉输入 */GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;GPIO_Init(GPIOA, &GPIO_InitStructure);/* 3、配置EXTI11为上升沿触发事件 */GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource11); //选择PA11通道进行外部中断EXTI_InitTypeDef EXTI_InitStructure;EXTI_InitStructure.EXTI_Line = EXTI_Line11; //选择EXTI11EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Event; //触发事件模式EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; //上升沿触发EXTI_InitStructure.EXTI_LineCmd = ENABLE;EXTI_Init(&EXTI_InitStructure);/* 4、配置NVIC *///事件无需配置NVIC
// NVIC_InitTypeDef NVIC_InitStructure;
// NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
// NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
// NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
// NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
// NVIC_Init(&NVIC_InitStructure);
}
②ADC.c文件的代码如下:
#include "ADC.h"
#include "UART.h"/*** ADC1初始化函数*/
void ADC1_Init(void)
{/* 1、使能GPIOA和ADC时钟 */RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIOA时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //使能ADC1时钟RCC_ADCCLKConfig(RCC_PCLK2_Div6); //对ADC时钟源分频/* 2、对PA0(ADC1的通道0引脚)进行引脚配置 */GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //PA0 作为模拟通道输入引脚GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚GPIO_Init(GPIOA, &GPIO_InitStructure);/* 3、配置ADC1工作模式 */ADC_InitTypeDef ADC_InitStructure;ADC_DeInit(ADC1); //将外设ADC1的全部寄存器重设为缺省值ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC1和ADC2工作在独立模式ADC_InitStructure.ADC_ScanConvMode = DISABLE; //非扫描(单通道)模式ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //非连续模式
// ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //使用软件触发ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_Ext_IT11_TIM8_TRGO; //使用外部中断线EXTI11触发ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC1数据右对齐ADC_InitStructure.ADC_NbrOfChannel = 1; //转换的ADC通道的数目,只有PA0ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器/* 4、配置规则组*/ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5); //ADC1,ADC通道,盒子序列1,采样时间为55.5周期/* 5、使能EOC中断,NVIC的配置 */ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);NVIC_InitTypeDef NVIC_InitStruct;NVIC_InitStruct.NVIC_IRQChannel = ADC1_2_IRQn;NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStruct);/* 使能ADC的外部触发转换 */ADC_ExternalTrigConvCmd(ADC1,ENABLE); //这一步很重要,不要忘记ADC_Cmd(ADC1, ENABLE); //使能ADC1/* 5、ADC校准 */ADC_ResetCalibration(ADC1); //使能复位校准while (ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束ADC_StartCalibration(ADC1); //开启AD校准while (ADC_GetCalibrationStatus(ADC1)); //等待校准结束// /* 6、软件触发 */
// ADC_SoftwareStartConvCmd(ADC1, ENABLE); //启动ADC转换
}/*** ADC单通道转换完成的中断服务函数*/
uint16_t Data = 0;
uint8_t Satatus_Flag = 0;
void ADC1_2_IRQHandler (void)
{if(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == SET)//转换完成标志位EOC置1{ADC_ClearFlag(ADC1, ADC_FLAG_EOC); //清除标志位EOCData = ADC_GetConversionValue(ADC1); //获取结果寄存器中的数据Satatus_Flag = 1; //转换完成标志位}
}
③ADC.h文件的代码如下:
#ifndef __ADC_H
#define __ADC_H
#include "stm32f10x.h" extern uint16_t Data;
extern uint8_t Satatus_Flag;
void ADC1_Init(void);#endif
④主函数main.c文件的代码如下:
#include "stm32f10x.h"
#include "UART.h"
#include "ADC.h"
#include "EXTI.h"int main(void)
{float ADC_Value = 0;UART1_Init();ADC1_Init();EXTI11_Init();printf("ADC电压测量\r\n");while(1){if(Satatus_Flag){Satatus_Flag = 0;printf("结果寄存器数值为:%d\r\n",Data);ADC_Value = Data * 3.3 / 4095; //将二进制计数为电压电压值printf("测量到的电压为:%0.2f\r\n",ADC_Value); }}
}
2、外部定时器TIM2_CH2触发ADC
①TIM.c文件的代码如下:
#include "TIM.h"/*** 定时器TIM2的初始化:使用PWM模式1产生上升沿触发ADC*/
void TIM2_Init(void)
{/* 1、使能TIM2的时钟 */RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);/* 2,对时基单元的初始化 */TIM_InternalClockConfig(TIM2); //选择内部时钟源TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1; //时钟源分频,则为72MHzTIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; //向上计数TIM_TimeBaseInitStruct.TIM_Prescaler = 7200 - 1; //预分频器,计数分辨率为0.1msTIM_TimeBaseInitStruct.TIM_Period = 10000 - 1; //重装载值为10000,则计数周期为1sTIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0; //重复计数器(高级定时器才配置)TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct); /* 3、对TIM2的输出比较单元CH2进行配置 */TIM_OCInitTypeDef TIM_OCInitStruct;TIM_OCStructInit(&TIM_OCInitStruct); //给结构体默认初始值TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1; //选择PWM1模式TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High; //有效值配置极性为低电平TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; //通道使能TIM_OCInitStruct.TIM_Pulse = 5000; //CCR的初始值TIM_OC2Init(TIM2, &TIM_OCInitStruct); //配置CH2TIM_Cmd(TIM2,ENABLE); //使能定时器}
②ADC.c文件的代码如下:
#include "ADC.h"/*** ADC1初始化函数*/
void ADC1_Init(void)
{/* 1、使能GPIOA和ADC时钟 */RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIOA时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //使能ADC1时钟RCC_ADCCLKConfig(RCC_PCLK2_Div6); //对ADC时钟源分频/* 2、对PA0(ADC1的通道0引脚)进行引脚配置 */GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //PA0 作为模拟通道输入引脚GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚GPIO_Init(GPIOA, &GPIO_InitStructure);/* 3、配置ADC1工作模式 */ADC_InitTypeDef ADC_InitStructure;ADC_DeInit(ADC1); //将外设ADC1的全部寄存器重设为缺省值ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC1和ADC2工作在独立模式ADC_InitStructure.ADC_ScanConvMode = DISABLE; //非扫描(单通道)模式ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //非连续模式
// ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //使用软件触发ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_CC2; //使用TIM2_CH2触发ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC1数据右对齐ADC_InitStructure.ADC_NbrOfChannel = 1; //转换的ADC通道的数目,只有PA0ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器/* 4、配置规则组*/ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5); //ADC1,ADC通道,盒子序列1,采样时间为55.5周期/* 5、使能EOC中断,NVIC的配置 */ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);NVIC_InitTypeDef NVIC_InitStruct;NVIC_InitStruct.NVIC_IRQChannel = ADC1_2_IRQn;NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStruct);/* 使能ADC的外部触发转换 */ADC_ExternalTrigConvCmd(ADC1,ENABLE);//这一步很重要,不要忘记ADC_Cmd(ADC1, ENABLE); //使能ADC1/* 5、ADC校准 */ADC_ResetCalibration(ADC1); //使能复位校准while (ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束ADC_StartCalibration(ADC1); //开启AD校准while (ADC_GetCalibrationStatus(ADC1)); //等待校准结束// /* 6、软件触发 */
// ADC_SoftwareStartConvCmd(ADC1, ENABLE); //启动ADC转换
}/*** ADC单通道转换完成的中断服务函数*/
uint16_t Data = 0;
uint8_t Satatus_Flag = 0;
void ADC1_2_IRQHandler (void)
{if(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == SET)//转换完成标志位EOC置1{ADC_ClearFlag(ADC1, ADC_FLAG_EOC); //清除标志位EOCData = ADC_GetConversionValue(ADC1);//获取结果寄存器中的数据Satatus_Flag = 1; //转换完成标志位}
}
③主函数main.c文件的代码如下:
#include "stm32f10x.h"
#include "UART.h"
#include "ADC.h"
#include "TIM.h"int main(void)
{float ADC_Value = 0;UART1_Init();ADC1_Init();TIM2_Init();printf("ADC电压测量\r\n");while(1){if(Satatus_Flag){Satatus_Flag = 0;printf("结果寄存器数值为:%d\r\n",Data);ADC_Value = Data * 3.3 / 4095; //将二进制计数为电压电压值printf("测量到的电压为:%0.2f\r\n",ADC_Value); }}
}
使用定时器的PWM模式触发,每隔1s触发一次,因为定时器的定时器周期配置的为1s。
3、ADC中模拟看门狗的使用
如图:在ADC中有一个模拟看门狗,用于监测着结果寄存器的数值。用户可以设定一个寄存器的上限值和下限值,当寄存器的值大于上限值/小于下限值时,看门狗就会将标志位AWD置1,若开启了模拟看门狗中断,则会进入ADC中断。
①ADC.c文件的代码如下:
#include "ADC.h"
#include "UART.h"/*** ADC1初始化函数*/
void ADC1_Init(void)
{/* 1、使能GPIO和ADC时钟 */RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIOA时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //使能ADC1时钟RCC_ADCCLKConfig(RCC_PCLK2_Div6);//对ADC时钟源分频/* 2、对PA0(ADC1的通道0引脚)进行引脚配置 */GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //PA0 作为模拟通道输入引脚GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚GPIO_Init(GPIOA, &GPIO_InitStructure);/* 3、配置ADC1工作模式 */ADC_InitTypeDef ADC_InitStructure;ADC_DeInit(ADC1); //将外设ADC1的全部寄存器重设为缺省值ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC1和ADC2工作在独立模式ADC_InitStructure.ADC_ScanConvMode = DISABLE; //非扫描(单通道)模式ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //连续模式ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件而不是外部触发启动ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC1数据右对齐ADC_InitStructure.ADC_NbrOfChannel = 1; //转换的ADC通道的数目,只有PA0ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器/* 4、配置规则组*/ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5); //ADC1,ADC通道,盒子序列1,采样时间为55.5周期/* 5、配置模拟看门狗:上限为3.0v,下限为2.0v */ADC_AnalogWatchdogThresholdsConfig(ADC1,0x09B1,0x0EBA); //配置监测的上下限,注意:监测的是结果寄存器的值ADC_AnalogWatchdogSingleChannelConfig(ADC1,ADC_Channel_0); //监测通道0ADC_AnalogWatchdogCmd(ADC1,ADC_AnalogWatchdog_SingleRegEnable); //使能单个规则组模拟看门狗/* 6、使能EOC和AWD中断,NVIC的配置 */ADC_ITConfig(ADC1, ADC_IT_AWD, ENABLE); //使能看门狗中断ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE); //使能ADC转换完成中断NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);NVIC_InitTypeDef NVIC_InitStruct;NVIC_InitStruct.NVIC_IRQChannel = ADC1_2_IRQn;NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStruct);ADC_Cmd(ADC1, ENABLE); //使能ADC1/* 7、ADC校准 */ADC_ResetCalibration(ADC1); //使能复位校准while (ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束ADC_StartCalibration(ADC1); //开启AD校准while (ADC_GetCalibrationStatus(ADC1)); //等待校准结束/* 8、软件触发 */ADC_SoftwareStartConvCmd(ADC1, ENABLE); //启动ADC转换
}/*** ADC单通道转换完成和模拟看门狗的中断范围函数*/
void ADC1_2_IRQHandler (void)
{/* 转换完成 */if(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)) //转换完成标志位EOC置1{ADC_ClearFlag(ADC1, ADC_FLAG_EOC); //清除标志位EOCprintf("结果寄存器数值为:%d\r\n",ADC_GetConversionValue(ADC1)); //打印结果寄存器的值printf("测量到的电压为:%0.2f\r\n",ADC_GetConversionValue(ADC1) * 3.3 / 4095); //打印电压值}/* 模拟看门狗监测到超过阈值 */if(ADC_GetFlagStatus(ADC1, ADC_FLAG_AWD)){ADC_ClearFlag(ADC1, ADC_FLAG_AWD); //清除标志位AWD//LED翻转 }
}
②主函数main.c文件的代码如下:
#include "stm32f10x.h"
#include "ADC.h"int main(void)
{ADC1_Init();printf("ADC电压监测\r\n");while(1){}
}