cortex_m3_stm32嵌入式学习笔记(十):输入捕捉实验(定时器的输入捕捉)

输入捕获模式可以用来测量脉冲宽度或者测量频率。 STM32 的定时器,除了 TIM6 和 TIM7,其他定时器都有输入捕获功能。 STM32 的输入捕获,简单的说就是通过检测 TIMx_CHx 上的边沿信号,在边沿信号发生跳变(比如上升沿/下降沿)的时候,将当前定时器的值(
TIMx_CNT)存放到对应的通道的捕获/比较寄存器( TIMx_CCRx)里面,完成一次捕获。同时还可以配置捕获时是否触发中断/DMA 等。

本章我们用到 TIM2_CH1 来捕获高电平脉宽,也就是要先设置输入捕获为上升沿检测,记录发生上升沿的时候
TIM2_CNT 的值。然后配置捕获信号为下降沿捕获,当下降沿到来时,发生捕获,并记录此时的 TIM2_CNT 值。这样,前后两次 TIM2_CNT 之差,就是高电平的脉宽,同时 TIM2 的计数频率我们是知道的,从而可以计算出高电平脉宽的准确时间。

至于为什么TIM2_CH1来测量WK_UP的脉宽。。看图:

显然,TIM_CH1是连在PA0上的。。

本节实验是在上节PWM的基础上改动的

timer.c

#include "timer.h"
#include "led.h"
#include "sys.h"
#include "usart.h"
//PWM 输出初始化
//arr:自动重装值
//psc:时钟预分频数
void TIM1_PWM_Init(u16 arr,u16 psc)
{
	 GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);//
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);  //使能GPIO外设时钟使能

   //设置该引脚为复用输出功能,输出TIM1 CH2的PWM脉冲波形
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //TIM_CH2
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值	 80K
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值  不分频
	TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位

	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
	TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
	TIM_OC1Init(TIM1, &TIM_OCInitStructure);  //根据TIM_OCInitStruct中指定的参数初始化外设TIMx

  TIM_CtrlPWMOutputs(TIM1,ENABLE);	//MOE 主输出使能	

	TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);  //CH1预装载使能	 

	TIM_ARRPreloadConfig(TIM1, ENABLE); //使能TIMx在ARR上的预装载寄存器

	TIM_Cmd(TIM1, ENABLE);  //使能TIM1

}
void TIM2_Cap_Init(u16 arr,u16 psc)
{
	TIM_ICInitTypeDef  TIM2_ICInitStructure;
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
 	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);	//使能TIM2时钟
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  //使能GPIOA时钟

	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0;  //PA0 清除之前设置
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0 输入
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	GPIO_ResetBits(GPIOA,GPIO_Pin_0);						 //PA0 下拉

	//初始化定时器2 TIM2
	TIM_TimeBaseStructure.TIM_Period = arr; //设定计数器自动重装值
	TIM_TimeBaseStructure.TIM_Prescaler =psc; 	//预分频器
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位

	//初始化TIM2输入捕获参数
	TIM2_ICInitStructure.TIM_Channel = TIM_Channel_1; //CC1S=01 	选择输入端 IC1映射到TI1上
  	TIM2_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;	//上升沿捕获
  	TIM2_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1上
  	TIM2_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;	 //配置输入分频,不分频
  	TIM2_ICInitStructure.TIM_ICFilter = 0x00;//IC1F=0000 配置输入滤波器 不滤波
  	TIM_ICInit(TIM2, &TIM2_ICInitStructure);

	//中断分组初始化
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;  //TIM2中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;  //先占优先级2级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  //从优先级0级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器 

	TIM_ITConfig(TIM2,TIM_IT_Update|TIM_IT_CC1,ENABLE);//允许更新中断 ,允许CC1IE捕获中断	

  TIM_Cmd(TIM2,ENABLE ); 	//使能定时器2

}
u8 TIM2CH1_CAPTURE_STA=0; //输入捕获状态
//bit7:捕获完成标志
//bit6:捕获到高点平标志
//bit5~0:捕获到高电平后定时器溢出的次数

u32 TIM2CH1_CAPTURE_VAL;//输入捕获值
void TIM2_IRQHandler(void)
{ 

 	if((TIM2CH1_CAPTURE_STA&0X80)==0)//还未成功捕获
	{
		if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)

		{
			if(TIM2CH1_CAPTURE_STA&0X40)//已经捕获到高电平了
			{
				if((TIM2CH1_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
				{
					TIM2CH1_CAPTURE_STA|=0X80;//标记成功捕获了一次
					TIM2CH1_CAPTURE_VAL=0XFFFF;
				}else TIM2CH1_CAPTURE_STA++;
			}
		}
	if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET)//捕获1发生捕获事件
		{
			if(TIM2CH1_CAPTURE_STA&0X40)		//捕获到一个下降沿
			{
				TIM2CH1_CAPTURE_STA|=0X80;		//标记成功捕获到一次上升沿
				TIM2CH1_CAPTURE_VAL=TIM_GetCapture1(TIM2);
		   		TIM_OC1PolarityConfig(TIM2,TIM_ICPolarity_Rising); //CC1P=0 设置为上升沿捕获
			}else  								//还未开始,第一次捕获上升沿
			{
				TIM2CH1_CAPTURE_STA=0;			//清空
				TIM2CH1_CAPTURE_VAL=0;
	 			TIM_SetCounter(TIM2,0);
				TIM2CH1_CAPTURE_STA|=0X40;		//标记捕获到了上升沿
		   		TIM_OC1PolarityConfig(TIM2,TIM_ICPolarity_Falling);		//CC1P=1 设置为下降沿捕获
			}
		}
 	}

    TIM_ClearITPendingBit(TIM2, TIM_IT_CC1|TIM_IT_Update); //清除中断标志位

}

前两个初始化的函数没什么好说的,看最后那个中断服务函数。分析一下测量脉冲的原理:

先看一下这个变量

u8 TIM2CH1_CAPTURE_STA=0; //输入捕获状态
//bit7:捕获完成标志
//bit6:捕获到高点平标志
//bit5~0:捕获到高电平后定时器溢出的次数

首先当第一次触发捕获中断的,程序进入中断服务函数,判断是否捕获完成,很显然还没完成(因为TIM2CH1_CAPTURE_STA初始值为0),然后进入捕捉中断服务的if语句

因为在上面的初始化函数中设置的上升沿捕捉,而第一次进来TIM2CH1_CAPTURE_STA&0X40很明显等于0,这是开始改动捕捉方式,改为下降沿捕捉(对称式捕捉?。。),最关键的是令TIM2CH1_CAPTURE_STA的第6为1,ok等待下一次捕捉中断触发就可以算一次完整的捕捉了,当然这里还有一个问题,有可能你在等的时候定时器溢出了。。这时候我们就借助更新中断(我觉得叫它溢出中断更形象啊)来统计溢出次数,溢出次数很巧妙的记在了TIM2CH1_CAPTURE_STA的0-5位,但如果时间还长TIM2CH1_CAPTURE_STA的第5位也溢出了怎么办?。。没办法,强制结束本次捕捉吧

timer.h

#ifndef _TIMER_
#define _TIMER_
#include "sys.h"
void TIM1_PWM_Init(u16 arr,u16 psc);
void TIM2_Cap_Init(u16 arr,u16 psc);
#endif

主函数

保留了PWM输出

#include "led.h"
#include "delay.h"
#include "usart.h"
#include "timer.h"
#include "sys.h"
extern u8 TIM2CH1_CAPTURE_STA;
extern u16 TIM2CH1_CAPTURE_VAL;
void init(void)
{
	NVIC_Configuration();
	delay_init();
	uart_init(9600);
	LED_Init();
	TIM1_PWM_Init(899,0);//PWM频率80KHz
	TIM2_Cap_Init(0xFFFF,72-1);//以1MHz的频率计数
}
int main(void)
{
	u32 temp=0;
	init();
	while(1)
	{
		delay_ms(10);
		TIM_SetCompare1(TIM1,TIM_GetCapture1(TIM1)+1);
		if(TIM_GetCapture1(TIM1)==300)TIM_SetCompare1(TIM1,0);
		if(TIM2CH1_CAPTURE_STA&0X80)//成功捕获到了一次高电平
		{
			temp=TIM2CH1_CAPTURE_STA&0X3F;
			temp*=65536;					//溢出时间总和
			temp+=TIM2CH1_CAPTURE_VAL;		//得到总的高电平时间
			printf("HIGH:%d us\r\n",temp);	//打印总的高点平时间
 			TIM2CH1_CAPTURE_STA=0;			//开启下一次捕获
 		}
	}
}

时间: 2024-12-21 18:34:41

cortex_m3_stm32嵌入式学习笔记(十):输入捕捉实验(定时器的输入捕捉)的相关文章

cortex_m3_stm32嵌入式学习笔记(八):定时器中断实验

STM32 的定时器功能十分强大,有 TIME1 和 TIME8 等高级定时器,也有 TIME2~TIME5 等通用定时器,还有 TIME6 和TIME7 等基本定时器. 本节学习通用定时器 TIM3 STM32 的通用定时器可以被用于:测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和 PWM)等. STM3 的通用 TIMx (TIM2. TIM3. TIM4 和 TIM5)定时器功能包括: 1)16 位向上.向下.向上/向下自动装载计数器( TIMx_CNT). 2)16 位可

cortex_m3_stm32嵌入式学习笔记(二十三):红外遥控实验(输入捕捉+解码)

红外遥控是一种无线.非接触控制技术,具有抗干扰能力强,信息传输可靠,功耗低,成本低,易实现等显著优点,被诸多电子设备特别是家用电器广泛采用,并越来越多的应用到计算机系统中. 红外遥控的编码方式目前广泛使用的是: PWM(脉冲宽度调制)的 NEC 协议和 PhilipsPPM(脉冲位置调制) 的 RC-5 协议的.ALIENTEK MiniSTM32 开发板配套的遥控器使用的是NEC 协议,其特征如下: 1. 8 位地址和 8 位指令长度: 2.地址和命令 2 次传输(确保可靠性) 3. PWM

cortex_m3_stm32嵌入式学习笔记(十九):DMA实验(高速传输)

DMA,全称为: Direct Memory Access,即直接存储器访问. DMA 传输方式无需 CPU 直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为 RAM 与 I/O 设备开辟一条直接传送数据的通路, 能使 CPU 的效率大为提高. 即DMA传输前,CPU要把总线控制权交给DMA控制器,而在结束DMA传输后,DMA控制器应立即把总线控制权再交回给CPU. 一个完整的DMA传输过程必须经过下面的4个步骤. 1.DMA请求 CPU对DMA控制器初始化,并向I/O接

cortex_m3_stm32嵌入式学习笔记(十六):ADC实验(模数转换)

之前没学过数模电,对A/D D/A转换一窍不通,也百度了很多资料大都深奥难懂..算了,先自以为是一下吧,等以后学了专业课再说..(寒假回家一定要学..恩 就这么决定了)看了那么多资料,感觉 A/D转换就是将电压(或者是其他模拟量:如 压力,图像等)转换为数字,D/A就是反过来,而ADC就是A/D转换器,他可以采集外部电压转化为数字.本节实验通过ADC采集外部电压转换为数字显示在屏幕上. STM32 拥有 1~3 个 ADC( STM32F101/102 系列只有 1 个 ADC),这些 ADC

cortex_m3_stm32嵌入式学习笔记(十五):待机唤醒实验(WK_UP外部中断)

很多单片机都有低功耗模式, STM32 也不例外.在系统或电源复位以后,微控制器处于运行状态.运行状态下的 HCLK 为 CPU 提供时钟,内核执行程序代码.当 CPU 不需继续运行时,可以利用多个低功耗模式来节省功耗,例如等待某个外部事件时.用户需要根据最低电源消耗,最快速启动时间和可用的唤醒源等条件,选定一个最佳的低功耗模式. STM32 的低功耗模式有 3 种: 1)睡眠模式( CM3 内核停止,外设仍然运行) 2)停止模式(所有时钟都停止) 3)待机模式( 1.8V 内核电源关闭) 在这

cortex_m3_stm32嵌入式学习笔记(十八):DAC实验(数模转换)

STM32 的 DAC 模块(数字/模拟转换模块)是 12 位数字输入,电压输出型的 DAC. DAC可以配置为 8 位或 12 位模式,也可以与 DMA 控制器配合使用. DAC 工作在 12 位模式时,数据可以设置成左对齐或右对齐. DAC 模块有 2 个输出通道,每个通道都有单独的转换器.在双 DAC 模式下, 2 个通道可以独立地进行转换,也可以同时进行转换并同步地更新 2 个通道的输出. 本节实验,我们将利用按键(或 USMART) 控制 STM32 内部 DAC1来输出电压,通过 A

cortex_m3_stm32嵌入式学习笔记(二):独立按键实验(IO输入)

上一个也就是第一个实验做的是关于LED的,属于IO口的输出使用,这一节实验是独立按键的使用,即IO 口的输入使用 ministm32 开发板上一共有3个独立按键,分别为 KEY0  KEY1 WK_UP 原理图如下: 注意: KEY0 KEY1 是低电平有效(即它们为低电平时代表按键按下)而 WK_UP 是高电平有效,为什么呢..很明显,这个问题要分析上面的原理图才能知道的,好吧本渣没学过数模电电路也就勉强70多分(半本书..还没学完),就硬着头皮来分析一下吧(对错可不保证啊..QAQ):很明显

cortex_m3_stm32嵌入式学习笔记(二十):IIC实验(I2C串行总线)

IIC(Inter- Integrated Circuit)总线是一种由 PHILIPS 公司开发的两线式串行总线,用于连接微控制器及其外围设备.它是由数据线SDA 和时钟SCL构成的串行总线,可发送和接收数据.在 CPU 与被控 IC 之间. IC 与 IC 之间进行双向传送, 高速 IIC 总线一般可达 400kbps 以上. ALIENTEK MiniSTM32 开发板板载的 EEPROM 芯片型号为 24C02.该芯片的总容量是 256个字节,该芯片通过 IIC 总线与外部连接,我们本章

cortex_m3_stm32嵌入式学习笔记(二十二):触摸屏实验(触摸屏驱动)

目前最常用的触摸屏有两种:电阻式触摸屏与电容式触摸屏. 电阻式触摸屏 在 Iphone 面世之前,几乎清一色的都是使用电阻式触摸屏, 电阻式触摸屏利用压力感应进行触点检测控制,需要直接应力接触, 通过检测电阻来定位触摸位置. 电阻触摸屏的主要部分是一块与显示器表面非常配合的电阻薄膜屏,这是一种多层的复合薄膜,它以一层玻璃或硬塑料平板作为基层,表面涂有一层透明氧化金属(透明的导电电阻)导电层,上面再盖有一层外表面硬化处理.光滑防擦的塑料层.它的内表面也涂有一层涂层.在他们之间有许多细小的(小于 1