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; //选择EXTI11
EXTI_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通道的数目,只有PA0
ADC_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); //清除标志位EOC
Data = 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; //时钟源分频,则为72MHz
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; //向上计数
TIM_TimeBaseInitStruct.TIM_Prescaler = 7200 - 1; //预分频器,计数分辨率为0.1ms
TIM_TimeBaseInitStruct.TIM_Period = 10000 - 1; //重装载值为10000,则计数周期为1s
TIM_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); //配置CH2
TIM_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通道的数目,只有PA0
ADC_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); //清除标志位EOC
Data = 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通道的数目,只有PA0
ADC_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); //监测通道0
ADC_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); //清除标志位EOC
printf("结果寄存器数值为:%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)
{
}
}