STM32学习笔记详解-输出比较与输入捕获

针对STM32F103RC学习过程中遇到的较为复杂、难以理解的地方,此处对其进行详细分析,以避免学习笔记中容易出错、混淆、看不懂的地方

基于正点原子ALIENTEKmini版,由于其手册对于部分模块的讲解有些模糊,此处记录一些个人理解,便于日后重温

概念

输出比较是对于已有的信号,自定义要求,当计数器满足要求时,便输出有效,否则输出无效(可以是电平,也可以是模拟信号)

PWM是输出比较的特例,通过定时器产生规律的信号,例如三角波、锯齿波、正弦波等,而一般输出比较仅产生矩形波

输入捕获与输出比较对立,是检查输入引脚获取到的信号,通过通用/高级定时器TIMx,对其时间相关的参数进行测定,例如脉宽、频率、周期、占空比等。

主要依靠对输入信号的边沿(上升/下降)进行抓取,通过定时器进行计数,来达到其目的。

除此之外,输入捕获还有自带的滤波降噪等简单的信号处理。

学习目的

了解输入捕获的应用场景,如何配置相关的寄存器,如何对已有的信号进行分析,并得到其信息

接下来以一个小实验为例,实验目标:

    1、通过PWM产生一个规律的三角波信号(具体电压值、频率等无严格要求),并通过一个LED展示(由亮到灭,再由灭到亮)

    2、通过按键产生电平序列,获得不规则的矩形波

    3、对于获得的矩形波,测量其脉宽,并通过串口输出展示,得到按键按下的时间

分析过程

1、PWM原理及设计

PWM脉宽调制,将单一的电平信号,按照其占空比,输出一个模拟电量,可用其设计产生一些规律的信号,例如矩形波、三角波、锯齿波等

其原理可自行百度

PWM输出模式只能由高级定时器TIM1/TIM8产生,配置为PWM模式,我们设置CH1输出

需要配置的寄存器有:

        外设时钟使能寄存器RCC_APB2ENR:将输出通道的时钟使能

        端口配置寄存器GPIOx_CRH/CRL:输出通道对应的IO口设置为复用输出

        捕获/比较模式寄存器TIMx_CCMRn:设置PWM模式、启用预装载

        捕获/比较使能寄存器TIMx_CCER:OC1输出使能

        捕获比较寄存器TIMx_CCRn:输出比较值

        刹车和死区寄存器TIMx_BDTR:主输出使能

        自动重装载寄存器TIMx_ARR:设置计数值

        预分频器TIMx_PSC:设置预分频

        控制寄存器TIMx_CR1:自动重装载预装载允许

PWM频率 = Tclk1 / ((arr+1)*(prer+1)),输出频率应与之接近

占空比γ = TIM1_CCRn / ARR

输出电压U = γ*标准电压Vcc

 

2、输入捕获原理及设计

对于已有信号,检测其有效起始点(上升/下降沿),启动计时直至有效终止点,获取信号的周期、频率、脉宽、占空比等信息

输入捕获可由通用定时器TIMx产生,配置为输入捕获模式,我们设置CH3输入

需要配置的寄存器有:

        外设时钟使能寄存器RCC_APB2ENR:将输出通道的时钟使能、TIMx时钟使能

        端口配置寄存器GPIOx_CRH/CRL:输出通道对应的IO口设置为电平输入

        捕获/比较模式寄存器TIMx_CCMRn:设置滤波器,映射到TI3

        捕获/比较使能寄存器TIMx_CCER:上升/下降沿捕获

        DMA/中断使能寄存器TIMx_DIER:允许捕获中断、允许更新中断

        捕获比较寄存器TIMx_CCRn:输入捕获值

        自动重装载寄存器TIMx_ARR:设置计数值

        预分频器TIMx_PSC:设置预分频

        控制寄存器TIMx_CR1:使能定时器

由于STM32计数器非常快,所以对于按键来说,按下的时间很容易因为远大于超时值,导致计数器溢出,因此需要对以下情况进行处理,并以此编写输入捕获中断程序

 

如上图所示,分为五种可能性,因此需要对所有情况进行分析处理

①②为正常的开始和结束,分别需要开始计时直至终止,和停止计时、发送数据并等待下一次开始

③是处于无效的区段,此时可以任凭计数器计时(反正每次开始都会清空一次计数),但是此时不进行任何数据处理或发送

④当有效时间较长时,会导致计数器溢出,此时我们可以通过对溢出计次,然后清空计数器接着计数,直到最后有效电平结束后,对时间进行一个统一折算

⑤当时间非常长时,不仅计数器溢出,而且溢出次数过多,导致存储溢出次数的寄存器都不够用时,我们只能通过扩大寄存器容量,或者再添加一级寄存器,用于存储“溢出次数寄存器”的溢出次数,否则只能进行强制停止,将最大数据发出,并重新等待下一次上升沿

脉宽Pulsewidth = 溢出次数*最大计时+最后一次计时

代码编写

主要用来对比学习,注意哪些寄存器需要配置,如何配置,以及寄存器的配置顺序,仅供参考

1、PWM输出部分

/*
定时器TIM1_CH1 PWM输出初始化
psc  预分频值
arr  计数器值
*/
void TIM1_PWM_Init(u16 arr,u16 psc)
{
	RCC->APB2ENR|=1<<11;            //使能TIM1时钟
	GPIOA->CRH&=0XFFFFFFF0;         
	GPIOA->CRH|=0X0000000B;         //PD2复用推挽输出
	
	TIM1->ARR=arr;                  //设置计数值
	TIM1->PSC=psc;                  //设置预分频
	TIM1->CCMR1|=7<<4;              //设置输出比较模式
	TIM1->CCMR1|=1<<3;
	TIM1->CCER|=1<<0;               //输出比较使能
	
	TIM1->BDTR|=1<<15;              //主输出使能
	TIM1->CR1|=0X0081;              //允许自动重装,使能计数器
}

2、输入捕获部分

//定时器TIM2_CH3 输入捕获初始化
void TIM2_Cap_Init(u16 arr,u16 psc)
{
	RCC->APB1ENR|=1<<0;            //使能TIM2时钟
	RCC->APB2ENR|=1<<2;            //使能PA2时钟
	GPIOA->CRL&=0XFFFFF0FF;        
	GPIOA->CRL|=0X00000800;        //TIM2_CH3对应PA2,设置上拉/下拉输入
	GPIOA->ODR|=1<<2;              //默认上拉
	
	TIM2->ARR=arr;                 //设置计数器值
	TIM2->PSC=psc;                 //设置预分频
	
	TIM2->CCMR2|=1<<4;             //设置滤波器
	TIM2->CCMR2|=1<<0;             //映射到中断TI3
	
	TIM2->CCER|=1<<8;              //下降沿捕获
	TIM2->CCER|=1<<9;              
	
	TIM2->DIER|=1<<3;              //允许捕获中断3
	TIM2->DIER|=1<<0;              //允许更新中断3

	TIM2->CR1|=1<<0;               //使能计数器
	MY_NVIC_Init(2,0,TIM2_IRQn,2); //中断初始化
}

/*
捕获状态
为了减少变量和调用方便,我们将其假设为一个寄存器使用
[7]:是否完成捕获,0表示未完成,1表示完成
[6]:捕获低电平,0表示未捕获低电平,1表示捕获低电平
[5:0]:捕获高电平后,计数器溢出次数
*/
u8 STATE=0;

//捕获值
u16 VALUE=0;

//输入捕获TIM2中断服务程序
void TIM2_IRQHandler(void)
{
	if((STATE&0X80)==0)    //未完成,不能发出去
	{
		if((TIM2->SR&0X01)&&(STATE&0x40))   //低电平有效期间(按键一直按下),而计数器溢出(时间太长了)
		{
			if((STATE&0X3F)==0X3F)          //持续时间非常长,导致溢出计数都不够用了,不能再继续++了,则强行打断
			{
				STATE|=0X80;                //强行完成捕获
				VALUE=0XFFFF;               //value赋最大值代表最后一次溢出
			}
			else STATE++;
		}
		if(TIM2->SR&0X08)  //捕获到东西了
		{
			if(STATE&0X40) //已经捕获过低电平了(说明此次捕获为上升沿)
			{
				STATE|=0X80;          //完成捕获了
				VALUE=TIM2->CCR3;     //将计数值传递给value
				TIM2->CCER|=1<<9;     //等待下降沿
			}
			else if((STATE&0X40)==0)  //未捕获过低电平(说明此次捕获为下降沿)
			{
				VALUE=0;              //清空捕获值
				STATE=0X40;           //已经捕获过低电平了
				TIM2->CNT=0;          //清空计数器
				TIM2->CCER&=~(1<<9);  //等待上升沿
			}
		}
	}
	TIM2->SR=0;   //清空中断标志位,上述任一情况发生均需要清空标志位,如果无情况发生则原本就为0
}

3、主函数部分

extern u8 STATE;
extern u16 VALUE;

int main()
{
	u16 led0pwmval=0;
	u8 dir=1;
	u32 time=0;
	
	Stm32_Clock_Init(9);         //一堆初始化
	delay_init(72);
	uart_init(72,9600);
	LED_Init();
	KEY_Init();
	
	LED0=1;
	LED1=0;
	while(WKUP==0);              //为了做区别,按键按下则程序正式开始
	TIM2_Cap_Init(0XFFFF,71);    //以1MHz计数
	TIM1_PWM_Init(899,0);        //PWM启动
	while(1)
	{
		delay_ms(10);
		if(dir)led0pwmval++;         //亮暗交替轮回
		else led0pwmval--;
		if(led0pwmval>300)dir=0;
		if(led0pwmval==0)dir=1;
		TIM1->CCR1=led0pwmval;       //传值给通道CH1,点亮LED0
		if(STATE&0X80)
		{
			time=STATE&0X3F;         //溢出时间总和
			time*=65536;
			time+=VALUE;             //按键时长
			printf("按键按下的时间为:%d us\r\n",time); 
			STATE=0;                              //清空状态及计数   
		}
	}
}

实验结果

在结果之前,需要注意以下事宜:

        1、端口:PWM对应CH1,输入捕获对应CH3

        2、对于结果显示部分,LED应与CH1相连,按键应与CH3相连,串口连接至电脑

        3、按键、LED及串口通信的软硬件设计此处略过,如果有不懂,参照     STM32学习笔记(未更新)

实验结果:LED由亮变暗,再由暗变亮,串口工具显示固定值时间数据

串口展示按键按下的时间,如下图所示,圈出部分为最大计时值,按下时间太长则显示此数值

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值