10.定时器各功能分析及编码

知识汇总:

STM32的定时器有三种,高级定时器,通用定时器,基本定时器

就是功能多与少的差别,下面来逐个解释功能:在此之前,需要对几个概念有认知

几个概念:

1.定时器时钟频率:

        和定时器每计数一次所花时间有关,其倒数就是时钟周期

2.计数值:

        例如是自增的,就是每隔一个时钟周期就会自加1,就是所谓的计数了

3.重载值:

        就是计数值计数到多少就会溢出的那个值,一般计数值自加自加,直到等于重载值时,计数器又会从零开始自加。例如设置重载值是5000,打开定时器,计数器(对应计数值)就开始自增,0,1,2,3.。。。。直到等于5000,然后计数器又从0开始自增,0,1,2,。。。5000

4.重复值:

        重复值就是计数器自加到重载值这个重复过程的次数(溢出次数),这个比较少用可以不管。

5.比较值:

        这个值一般比重载值要小,简单地说就是定时器有一个相关的IO口,当计数值小于比较值的时候,这个IO口就输出高电平,因为计数值一直自增嘛,总有一天会大于比较值的,当计数值大于比较值的时候,这个IO口就输出低电平,比较值主要就是这个作用,可以想象这个比较值是控制一个输出波形占空比的关键。

6.stm32f40x下定时器时钟频率:

这个可把我坑惨了,在写输入捕获的时候发现怎么捕获时间误差那么大,后来排查了好一阵,发现好家伙,原来APBx下的定时器时钟频率是该APBx时钟频率的两倍。

所以附属于APB1的定时器的时钟频率是84M,附属于APB2的定时器的时钟频率是168M频率(这一次编写代码的TIM1就是APB2下的时钟)

至于原理,没什么道理就是手册讲了的,如果APB的分频不是1,那么定时器的频率就是APB的两倍:

(还是得认真看手册!)

下面正式进入

功能解释:

定时中断:

        很显然,就是配置好时钟频率、重载值,打开中断,打开定时器,然后每隔有一段固定的时间就会进入一次中断(计数值等于重载值时产生中断,叫作溢出中断)。例如时钟频率是1M,那每计数一次就是1/1M = 1us,如果重载值设为5,那就是计数5次溢出一次即中断一次嘛,所以间隔的时间就是    时钟周期*重载值,1us*5 = 5us。

输入捕获模式:

        这个也很好理解,就是用来计算输入的波形高电平或低电平持续时间,设置成捕获脉冲的高电平持续时间,那当定时器对应的IO引脚,检测到上升沿时,就会将计数值清零并开始计数,当检测到下降沿时,就会触发中断,这时我们在中断里就可以取出计数值,从而知道高电平持续了多长时间。捕获脉冲低电平也是一样的原理。简言之输入捕获就是可以获取外部输入的脉冲高电平或低电平持续时间的。

PWM输入模式:

        这个其实是输入捕获模式的一个特例,输入捕获模式可以捕获高电平或低电平持续时间,而PWM输入模式可以检测出定时器相关IO引脚输入的PWM波形的占空比以及PWM周期。如下图,关注红框里的即可,它就是在上升沿的时候,计数器开始计数,然后下降沿的时候标记极性要跳转了,接着在下一个上升沿的时候触发中断。两个上升沿之间的时间就是PWM总的周期时间,而根据电平跳变前后的时间,就能算出占空比是多少了~

编码器模式:

        这个就给力了,驱动编码电机时很好用,可以读出电机转过角度对应编码器产生的脉冲数,因为电机转一圈,对应编码器产生的脉冲数是固定的嘛,所以你知道了脉冲数,就可以知道电机转了多少圈,而且如果你固定间隔地去读脉冲数,还可以知道电机的速度(路程/时间 = 速度)。

        其实原理也很简单,就是捕获到一个上升沿,计数值就加一,例如一个编码电机转一圈其编码器会产生330个脉冲,那就是有330个上升沿嘛,这样我们通过记录捕获到的上升沿的个数,就能知道电机转了多少圈了。一个编码器一般会用到定时器的两个IO口,一个是用于编码器A相一个是B相,简单理解就是,如果是电机正转,A会比B多产生一个脉冲,如果是电机反转,则A会比B少产生一个脉冲,这样两个定时器IO相互配合,我们就能确切地知道,这个编码电机是转了多少圈,而且是往哪个方向转的。那又会产生一个疑惑,定时器只有一个计数器,那计数器是计数A相IO的脉冲还是B相IO的脉冲呢?其实这是可选的,可以只记录A的也可以只记录B的,也可以两个都记录,那电机转一圈,记录到的脉冲数无非就是两倍而已。

以上说的都是定时器的输入功能,下面讲输出的功能

死区和互补输出:

高级定时器的输出可带死区和互补输出,死区的意思就是比如你输出一个PWM波,死区可以使得定时器IO实际输出的PWM波上升沿或下降沿比原来预设的要延迟一段时间再产生,互补输出就是定时器两个IO嘛,一个输出高的时候另一个就是低电平,例如下图实际输出波形1和2:

为什么设计死区呢?因为单片机的IO高电平和切换的速度很快,而电机高电平和低电平切换速度很慢(或者是电机驱动器),这就导致单片机IO输出高电平的时候,电机的IO还在低电平,这样就属于预设之外的操作了。

强制输出模式:

        最开始的时候我们有提到,定时器输出就是定时器的一个IO口,在计数值小于比较值时,IO输出高电平,计数器大于比较值时,IO输出低电平(电平关系可以互换,这个称为极性)。强制输出模式就是不管计数器和比较值大于还是小于,我设这个IO高电平它就高电平,低电平就低电平。

输出比较模式:

        这个就很好理解了,如上面所说计数值小于比较值时,IO输出高电平,计数器大于比较值时,IO输出低电平(极性可设置)

PWM模式:

        这个很常用,设置好时钟频率(计数一次多少时间),重装值(决定PWM周期),比较值(配合极性,决定占空比),最后使能自动重载,这样定时器相关的IO就会源源不断地输出自定义频率、占空比的PWM波了。其实也是输出比较模式的一个特例而已,不能说很像,只能说一模一样。

单脉冲模式:

        这个很特别,集输入与输出一体,就是定时器一个IO捕获到上升沿或者下降沿(称为触发),另一个IO就可以输出一个指定延时期限且指定宽度的脉冲,一次触发只输出一个脉冲噢。

值得一提的是,一个具有输入输出功能的定时器会有4个通道,每个通道其实对应一个IO口,一个定时器四个通道的计数值,重载值是共用的,而比较值还有输入输出等则由通道自己决定。至于基础定时器,没有输出或者输入功能,自然是没有IO口的。

代码编写:

定时器核心四要素1.时钟频率 2.重载值&&计数值&&比较值 3.中断事件 4.定时器配置和开关

先概览一下都有哪些寄存器:
控制寄存器1(TIMx_CR1:通用配置 )

控制寄存器2(TIMx_CR2:通道相关的 )

从模式控制寄存器(TIMx_SMCR)

DMA/中断使能寄存器(TIMx_DIER)

状态寄存器(TIMx_SR,中断标志)

事件生成寄存器(TIMx_EGR)

捕获/比较模式寄存器1(TIMx_CCMR1)

捕获/比较模式寄存器2(TIMx_CCMR2)

捕获/比较使能寄存器1(TIMx_CCER)

计数器(TIMx_CNT)

预分频器(TIMx_PSC)

自动重载寄存器 (TIMx_ARR)

重复计数器寄存器 (TIMx_RCR)

捕获/比较寄存器 1~4 (TIMx_CCR1~4,一个通道一个)

断路和死区寄存器 (TIMx_BDTR)

DMA 控制寄存器 (TIMx_DCR)

全传输 DMA 地址寄存器 (TIMx_DMAR)

因为高级定时器功能最多,就以高级定时器1来编写了~其它定时器也是一样的。

1.定时中断功能代码编写:

目标:

假如我们的目标是让定时器每500ms中断一次,而且计数频率设为10K---->>如果APB2时钟是84M,那么定时器时钟就是168M,时钟预分频就是84M*2 / 10k  = 8400*2 ,那么计数次数就是 0.5秒除以计数周期 = 0.5*10K = 5000次。

即预分频值设为8400*2 -1,重载值设为5000-1。

以上应该很好理解,单片机最常见的时间计算。

高级定时器一共有20个寄存器呢,我们仍然按照功能来找寄存器,够用就好,这样编码不会太复杂

1.时钟频率:

TIM1是依附于APB2的,根据时钟系统我们知到APB2是168M/2 = 84M,而且要时钟使能

寄存器TIM1->PSC是时钟预分频设为8400*2-1

RCC->APB2ENR|=1<<0;	//TIM1时钟使能 
TIM1->PSC = 8400*2-1;

2.重载值&&计数值&&比较值:

重载值设为5000-1

TIM1->ARR = 5000-1;

在这里不用写比较值~比较值是和输入/输出功能有关的

 3.中断事件

TIM1->DIER:

bit[0]        更新中断使能 1<<0

TIM1->SR:

 bit[0]        更新中断标志,要手动清零

TIMx_DIER |= 1<<0;//打开更新中断
MY_NVIC_Init(1,3,TIM1_UP_TIM10_IRQn,2);中断服务函数:
void TIM1_UP_TIM10_IRQHandler(void)
{if(TIM1->SR&0X0001)//溢出中断{}				   TIM1->SR&=~(1<<0);//清除中断标志位 	    
}

4.定时器配置和开关

相关寄存器是TIM1->CR1:

bit[7]           ARPE控制自动重载的 1<<7,这个位很关键,1的话,重载值和影子寄存器的值同步,0的话,在每次发生更新事件时才将重载值同步到影子寄存器。可以这样理解,影子寄存器是正真生效的重载值。例如你在输出pwm波时,如果中途改了参数,这个位如果是0,那么PWM波会在下一个周期再变成新参数的波形,如果是1的话,那就是当前周期就变成新参数的波形了,只是准不准就不好说了。

bit[6:5]        对齐模式选择,选边沿对齐模式,就是计数器是从0到重载值或从重载值到0这样计数                    0x0<<5

bit[4]           递增计数 0<<4

bit[3]           不使用单脉冲模式 0<<3

bit[2]           URS选择溢出、UG等事件都能触发中断/DMA 0<<2。更新(UG)事件就是重复值达到重复值寄存器设置的值时触发的事件。UG事件可以软件主动产生。更新中断会让定时器从0开始计数并且更新 重复值、重载值、时钟预分频值,溢出事件和更新事件都会触发更新中断。

bit[1]           UDIS允许溢出和UG事件 0<<1,写1的话则是只保留溢出中断,而禁止UG事件,禁止UG事件的好处是,产生的每个波形都是完整的,就算有参数改变也会在下一个周期才生效。

bit[0]           计数器使能 1<<0,这个等最后再打开

配置:
TIM1->CR1 = 0<<7 | 0x0<<5 | 0<<4 | 0<<3 | 0<<2 | 0<<1 | 0<<0 ;//傻眼,全0
打开定时器1:
TIM1->CR1 |= 1<<0;

5.完整代码://实测可用


void TIM1_Int_Init(void)
{
//1.时钟频率
RCC->APB2ENR|=1<<0;	//TIM1时钟使能 
TIM1->PSC = 8400*2-1;//2.重载值&&计数值&&比较值
TIM1->ARR = 5000-1;//3.中断事件 
TIM1->DIER |= 1<<0;//打开更新中断
MY_NVIC_Init(1,3,TIM1_UP_TIM10_IRQn,2);//4.定时器配置和开关
TIM1->CR1 = 0<<7 | 0x0<<5 | 0<<4 | 0<<3 | 0<<2 | 0<<1 | 0<<0 ;//傻眼,全0
TIM1->CR1 |= 1<<0;//打开定时器1}//中断服务函数:
void TIM1_UP_TIM10_IRQHandler(void)
{if(TIM1->SR&0X0001)//溢出中断{printf("TIM1_UP_TIM10_IRQHandler\r\n"); 		    				   				     	    	}				   TIM1->SR&=~(1<<0);//清除中断标志位 	    
}

比较通用的初始化:

//APB2 是84M
void TIM1_Int_Init(u16 arr重载值,u16 psc预分频值)
{RCC->APB2ENR|=1<<0;	//TIM1时钟使能    TIM1->ARR=arr;  	//设定计数器自动重装值 TIM1->PSC=psc;  	//预分频器	  TIM1->DIER|=1<<0;   //允许更新中断	  TIM1->CR1|=0x01;    //使能定时器1MY_NVIC_Init(1,3,TIM1_UP_TIM10_IRQn,2);	//抢占1,子优先级3,组2									 
}

运行效果:500ms中断一次

2.输入捕获模式代码编写:

        上面定时器中断还比较简单,找到几个关键的点就能写代码,但是输入捕获模式,浏览一下寄存器明显涉及的比较多,这样就需要我们将寄存器分类,然后再根据手册逐一去看哪些和功能相关,上面罗列了一堆的寄存器,现在按照定时器核心4部曲将它们分类:

1.时钟频率 
预分频器(TIMx_PSC) 2.重载值&&计数值&&比较值 
自动重载寄存器 (TIMx_ARR),
计数器(TIMx_CNT),
捕获/比较模式寄存器1(TIMx_CCMR1) 
捕获/比较模式寄存器2(TIMx_CCMR2) 
捕获/比较使能寄存器1(TIMx_CCER) 
捕获/比较寄存器 1~4 (TIMx_CCR1~4,一个通道一个), 3.中断事件 
DMA/中断使能寄存器(TIMx_DIER) 
状态寄存器(TIMx_SR,中断标志) 4.定时器配置和开关 
控制寄存器1(TIMx_CR1:通用配置 ) 
控制寄存器2(TIMx_CR2:通道相关的 )

然后查看手册,看看有没有相关的初始化或者使用的示例,一般都会有的,比较复杂,步骤比较多的都会有。且看,参考手册就有这样一个捕获的例子,后面我们会结合定时器核心思想和示例来找寄存器并配置。

下面逐一编写代码:

目标:

以100000hz(5个零)的时钟频率,去捕获TIM1通道1对应的IO的高电平持续时间(IO会事先拉低,然后用杜邦线接高电平一段时间),用 串口打印出来。

0.GPIO相关的

因为这个模式涉及到输入输出,即与GPIO有关,所以GPIO需要设置一下,还是之前提到过的GPIO复用思路:1.时钟 2.IO 3.复用啥

通过查询芯片数据手册,可以知道PE9的复用功能是TIM1_CH1

所以代码编写如下:

1.时钟
RCC->AHB1ENR|=1<<4;   	//GPIOE时钟附属于AHB1
2.IO  
GPIO_Set(GPIOE,PIN9,GPIO_MODE_AF,0,0,GPIO_PUPD_PD);//PA9,PA10,都配置为复用模式,电气配置为下拉
3.复用啥
GPIO_AF_Set(GPIOE,9,1);	//PE9,AF1 是定时器1

1.时钟频率 

 预分频器(TIMx_PSC) 

 TIM1是依附于APB2的,根据时钟系统我们知道APB2是168M/2 = 84M,则定时器频率是168M,而且要时钟使能

要让时钟频率是100000,所以预分频值应设为1680,因为168000000 / 100000 = 1680,即

这样一来,记一次数就是1/100000 = 10^-5 秒

RCC->APB2ENR|=1<<0;	//TIM1时钟使能 
TIM1->PSC = 1680-1;

2.重载值&&计数值&&比较值 

自动重载寄存器 (TIMx_ARR),

重载值自然是越大越好,这样可以采集高电平的时间,一次才更长,TIM1是16位的定时器,所以最大可以设置为0xFFFF,这样溢出一次就是65535*0.00001 = 0.65535秒

TIM1->ARR = 0xFFFF;


计数器(TIMx_CNT),这个不用设置,或者初始化为0

TIM1->CNT = 0;

这里寄存器比较多,但是我们心中有数,因为手册有提到怎么配置:
捕获/比较模式寄存器1(TIMx_CCMR1) 

bit[1:0]        通道1设为输入,且IC1映射到TI1 0x1<<0

bit[3:2]        每检测一个边沿就执行捕获 0x0<<2

bit[7:4]        不配置滤波器0x0<<4

8~15位是通道2的,和我们通道1没啥关系,不管它

TIM1->CCMR1 &=0xFF00;TIM1->CCMR1 |=0X1<<0;


捕获/比较模式寄存器2(TIMx_CCMR2) 

这个寄存器和上面的类似,只是是通道3和通道4的,不管


捕获/比较使能寄存器1(TIMx_CCER) 

bit[0]        1<<0 使能捕获

bit[1]        0<<1 上升沿捕获  1<<1 下降沿捕获

bit[2] 和 bit[3] 是关于互补输出的,不管。每一个通道有4个位,刚好这个寄存器16个位,满足4个通道的配置

TIM1->CCER |= 0<<1 | 1<< 0 ;


捕获/比较寄存器 1~4 (TIMx_CCR1~4,一个通道一个), 

TIM1->CCR1就是保存着通道1捕获的计数值

3.中断事件 

DMA/中断使能寄存器(TIMx_DIER) 

我们既要开启输入捕获中断,也要开启溢出中断~

bit[0]        更新中断使能 1<<0;

bit[1]        CC1中断时钟 1<<1;

状态寄存器(TIMx_SR,中断标志) 

TIM1->DIER |= 1<<1 | 1<<0;
MY_NVIC_Init(2,0,TIM1_CC_IRQn,2);
MY_NVIC_Init(2,1,TIM1_UP_TIM10_IRQn,2);//中断服务函数,有两个
//捕获状态
//[7]    0,没有成功的捕获;1,成功捕获到一次.
//[6]    0,还没捕获到低电平;1,已经捕获到低电平了.
//[5:0]:捕获低电平后溢出的次数
//(我们前面说溢出一次是0.65535秒嘛,这里的5个位就是记录溢出了多少次的,最大是0x3f即63)
//也就是说,最多最多可以捕获63*0.65535 = 41.28705秒的脉宽
u8  TIM1CH1_CAPTURE_STA=0;	//输入捕获状态	这个记录最后一次捕获但还没溢出的那部分时间u16	TIM1CH1_CAPTURE_VAL;	//输入捕获值(TIM1是16位)
//捕获中断
void TIM1_CC_IRQHandler(void)
{ u16 tsr;//printf("TIM1_CC_IRQHandler\r\n");tsr=TIM1->SR;if((TIM1CH1_CAPTURE_STA&0X80)==0)//还未成功捕获	{if(tsr&0x02)//捕获1发生捕获事件{	if(TIM1CH1_CAPTURE_STA&0X40)		//捕获到一个下降沿 		{TIM1CH1_CAPTURE_STA|=0X80;		//标记成功捕获到一次高电平脉宽TIM1CH1_CAPTURE_VAL=TIM1->CCR1;	//获取当前的捕获值.TIM1->CCER&=~(1<<1);			//CC1P=0 设置为上升沿捕获printf("get doooooooooooon\r\n");}else  								//还未开始,第一次捕获上升沿{TIM1CH1_CAPTURE_STA=0;			//清空TIM1CH1_CAPTURE_VAL=0;TIM1CH1_CAPTURE_STA|=0X40;		//标记捕获到了上升沿TIM1->CR1&=~(1<<0)		;    	//关掉定时器TIM1->CNT=0;					//计数器清空TIM1->CCER|=1<<1; 				//CC1P=1 设置为下降沿捕获TIM1->CR1|=0x01;    			//使能定时器printf("get uppppppppppp\r\n");}}			     	    					   }TIM1->SR&=~(1<<1);//清除溢出中断标志位    
}
//溢出中断
void TIM1_UP_TIM10_IRQHandler(void)
{ u16 tsr;tsr=TIM1->SR;//printf("TIM1_UP_TIM10_IRQHandler\r\n");if((TIM1CH1_CAPTURE_STA&0X80)==0)//还未成功捕获	{if(tsr&0X01)//溢出{	  printf("TIM1_UP_TIM10_IRQHandler\r\n");            if(TIM1CH1_CAPTURE_STA&0X40)//查看bit6,已经捕获到高电平了{if((TIM1CH1_CAPTURE_STA&0X3F)==0X3F)//高电平太长了{TIM1CH1_CAPTURE_STA|=0X80;		//标记成功捕获了一次TIM1CH1_CAPTURE_VAL=0XFFFF;}else TIM1CH1_CAPTURE_STA++;}	 }}TIM1->SR&=~(1<<0);//清除溢出中断标志位   
}

4.定时器配置和开关 

控制寄存器1(TIMx_CR1:通用配置 ) 

和上面的一样即可

bit[7]           ARPE控制自动重载的 0<<7,上面有解释

bit[6:5]        对齐模式选择,选边沿对齐模式 0x0<<5

bit[4]           递增计数 0<<4

bit[3]           不使用单脉冲模式 0<<3

bit[2]           选择溢出、UG等事件都能触发中断/DMA 0<<2。

bit[1]           允许溢出和UG事件 0<<1

bit[0]           计数器使能 1<<0,这个等最后再打开

配置:
TIM1->CR1 = 0<<7 | 0x0<<5 | 0<<4 | 0<<3 | 0<<2 | 0<<1 | 0<<0 ;//傻眼,全0
打开定时器1:
TIM1->CR1 |= 1<<0;


控制寄存器2(TIMx_CR2:通道相关的 )

这个不用管~默认值即可

完整代码:

初始化的:

void TIM1_CH1_Cap_Init(void)
{
//GPIO相关的
//1.时钟
RCC->AHB1ENR|=1<<4;   	//GPIOE时钟附属于AHB1
//2.IO  
GPIO_Set(GPIOE,PIN9,GPIO_MODE_AF,0,0,GPIO_PUPD_PD);//PA9,PA10,都配置为复用模式,电气配置为下拉
//3.复用啥
GPIO_AF_Set(GPIOE,9,1);	//PE9,AF1 是定时器1//定时器相关的
//1.时钟频率
RCC->APB2ENR|=1<<0;	//TIM1时钟使能 
TIM1->PSC = 84-1;//时钟频率1Mhz//2.重载值&&计数值&&比较值 
TIM1->ARR = 0xFFFF;//重载值
TIM1->CNT = 0;
TIM1->CCMR1 &=0xFF00;
TIM1->CCMR1 |=0X1<<0;//配置通道1为输入,一边沿一捕获,不滤波
TIM1->CCER |= 0<<1 | 1<< 0 ;//上升沿,使能捕获//3.中断事件 
TIM1->DIER |= 1<<1 | 1<<0;//使能更新及捕获中断
MY_NVIC_Init(2,0,TIM1_CC_IRQn,2);
MY_NVIC_Init(2,1,TIM1_UP_TIM10_IRQn,2);
TIM5->EGR=1<<0;//手动产生更新中断,目的是立即刷新预分频、重载值等参数//4.定时器配置和开关 
TIM1->CR1 = 0<<7 | 0x0<<5 | 0<<4 | 0<<3 | 0<<2 | 0<<1 | 0<<0 ;//傻眼,全0
TIM1->CR1 |= 1<<0;//打开定时器1:}

中断的:

看上面3.中断事件

运行效果:

因为PE9上面是设置为下拉的嘛,默认是低电平,那我把它连到我自己做的稳压板的3.3V上,如果打开稳压板总电源,就上电,PE9就会从低电平变成高电平(第一次捕获),此时PE9捕获到上升沿,开始计时,然后我关掉稳压板总电源,PE9就会从高电平变为低电平(第二次捕获),就停止捕获,并把高电平时间打印出来。

由于我两只手不一致有一丢丢误差,但是很明显是ok的~ 

3.PWM输入模式代码编写:

目标:

以10000hz(4个零)的时钟频率,去捕获TIM1通道1对应的IO输入的波形,并打印出每个PWM波,高电平持续时间,低电平持续时间,占空比,PWM波周期

同样手册说了使用流程,还是按我们的思路,把寄存器套进去。这个和上面输入捕获差不太多,应该只需要改一改就行了。

把上面的配置阔皮下来修改:

void TIM1_CH1_PWMCap_Init(void)
{
//GPIO相关的
//1.时钟
RCC->AHB1ENR|=1<<4;   	//GPIOE时钟附属于AHB1
//2.IO  
GPIO_Set(GPIOE,PIN9,GPIO_MODE_AF,0,0,GPIO_PUPD_PD);//PA9,PA10,都配置为复用模式,电气配置为下拉
//3.复用啥
GPIO_AF_Set(GPIOE,9,1);	//PE9,AF1 是定时器1//定时器相关的
//1.时钟频率
RCC->APB2ENR|=1<<0;	//TIM1时钟使能 
TIM1->PSC =16800-1;//时钟频率10000HZ,计数一次0.0001s,即100us一次//2.重载值&&计数值&&比较值?
TIM1->ARR =0xFFFF-1;//重载值 那么溢出一次最长是65535*100us = 6.5535s
TIM1->CNT = 0;
TIM1->CCMR1 &=0x0000;
//下面几句句是核心,简言之就是把通道1对应的IO,用通道1和通道2两个比较器来处理,比较器1负责捕获上升沿,比较器2负责捕获下降沿,这样两个一配合就能一次性得到PWM的核心参数--PWM周期、高电平持续时间
TIM1->CCMR1 |=0x2<<8 | 0X1<<0;//配置通道1为输入,一边沿一捕获,不滤波;通道2映射到输入1TIM1->CCER |= 1<<5 | 1<<4 | 0<<1 | 1<< 0 ;//通道1上升沿,使能捕获,通道2下降沿,使能捕获//下面两句是按照手册要求写的,简言之就是检测到上升沿就触发中断以后,执行定时器复位信号,计数值、比较值都清空,这样就可以检测下一个PWM波了
TIM1->SMCR &=~(0X7<<4 | 0x7<<0);
TIM1->SMCR |= 0X5<<4 | 0x4<<0;//触发选择:滤波后的定时器输入 1 (TI1FP1),复位模式//3.中断事件 没怎么改,就加上把通道2的比较器捕获中断打开了而已
TIM1->EGR=1<<0;//手动产生更新中断,目的是立即刷新预分频、重载值等参数
TIM1->DIER |= 1<<2 |1<<1 | 1<<0;//使能更新及捕获中断通道1和通道2
MY_NVIC_Init(2,0,TIM1_CC_IRQn,2);
MY_NVIC_Init(2,1,TIM1_UP_TIM10_IRQn,2);//4.定时器配置和开关?
TIM1->CR1 =0<<7 | 0x0<<5 |0<<4 |0<<3 |  0<<2 | 0<<1 | 0<<0 ;//傻眼,全0
TIM1->CR1 |= 1<<0;//打开定时器1:}

        配置完以后,定时器的功能就和手册给的框图一模一样了,下降沿时,记录了高电平持续时间(存在比较器CCR2中),而上升沿时,记录了整个PWM波周期(存在比较器CCR1中),这样我们只需要在捕获1(CCR1)中断发生时,去读出CCR1和CCR2的值,结合时钟频率,我们就能知道高电平是多长时间,PWM周期是多长,占空比是多少了。

中断服务函数:

//捕获中断
void TIM1_CC_IRQHandler(void)
{ u16 tsr;tsr=TIM1->SR;if((TIM1CH1_CAPTURE_STA&0X80)==0)//还未成功捕获	{if(tsr&0x02)//通道1发生捕获事件,PWM周期 上升沿{TIM1CH1_CAPTURE_STA|=0X80;		//标记成功捕获到一次高电平脉宽TIM1CH1_CAPTURE_VAL=TIM1->CCR1;	//PWM周期.TIM1CH2_CAPTURE_VAL=TIM1->CCR2;//高电平时间printf("have get pwmmmmmmmmmm\r\n");TIM1->SR&=~(1<<2 | 1<<1 );//清除捕获通道1中断标志位}}}
//溢出中断
void TIM1_UP_TIM10_IRQHandler(void)
{ u16 tsr;tsr=TIM1->SR;printf("\r\n\r\nTIM1_UP_TIM10_IRQHandler\r\n\r\n");if((TIM1CH1_CAPTURE_STA&0X80)==0)//还未成功捕获	{if(tsr&0X01)//溢出{  //printf("TIM1_UP_TIM10_IRQHandler\r\n");            }}TIM1->SR&=~(1<<0);//清除溢出中断标志位   
}

main函数:

    TIM1_CH1_Cap_Init();while(1){ 		if(TIM1CH1_CAPTURE_STA&0X80)//成功捕获到了一次高电平{printf("PWM:%d ms \t PWM-HIGH:%d ms \t Q:%f \r\n",TIM1CH1_CAPTURE_VAL/10,TIM1CH2_CAPTURE_VAL/10,((TIM1CH2_CAPTURE_VAL)*1.0)/TIM1CH1_CAPTURE_VAL);//TIM1CH1_CAPTURE_STA=0;			//开启下一次捕获}}

运行效果:

还是一样,用按键开关来模拟波形~有信号发生器的可以用信号发生器。

4.编码器模式代码编写:

目标:

用TIM1的通道1和通道2,以100000hz(5个零)的时钟频率,读取编码电机转一圈的脉冲数。

(2023/12/30 标记了一处未完成,要期末了,在复习,且先发写了一半的,剩下的考完试再写,毕竟这可是关系到奖学金我的小钱钱捏)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/823946.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

linux安装python

文章目录 前言一、下载安装包二、安装1.安装依赖2.解压3.安装4.软链接5.验证 总结 前言 本篇文章介绍linux环境下安装python。 一、下载安装包 下载地址&#xff1a;官方网站 我们以最新的标准版为例 二、安装 1.安装依赖 yum -y install openssl-devel ncurses-devel li…

mfc140u.dll丢失的解决方法,怎样修复mfc140u.dll

最近看到很多朋友在问找不到mfc140u.dll丢失怎么办&#xff1f;有什么解决方法&#xff0c;今天就给小伙伴们解答一下&#xff0c;mfc140u.dll丢失的解决办法&#xff0c;怎么修复mfc140u.dll。 一.丢失的原因 1.损坏的程序安装:在安装某个程序时&#xff0c;可能会出现意外中…

第六课:冷战和消费主义、个人计算机革命、图形用户界面(GUI)及3D图形

第六课&#xff1a;冷战和消费主义、个人计算机革命、图形用户界面&#xff08;GUI&#xff09;及3D图形 第二十四章&#xff1a;冷战和消费主义本课概括&#xff1a;政府和消费者推动了计算机的发展 第二十五章&#xff1a;个人计算机革命本集概括&#xff1a;继续讲计算机发展…

MySQL 数值函数,字符串函数与多表查询

MySQL像其他语言一样,也提供了很多库函数,分为单行函数和分组函数(聚合函数),我们这里先简易介绍一些函数,熟悉就行,知道怎么使用即可. 数值函数 三角函数 指数与对数函数 进制间的转换函数 字符串函数 注:LPAD函数是右对齐,RPAD函数是左对齐 多表查询 注:如果为表起了别名,就…

JavaScript使用教程(二):类型、值和变量

计算机程序通过操作值&#xff08;如数值3.14&#xff09;或文本&#xff08;如“Hello World”&#xff09;来工作。编程语言中这些可以表示和操作的值被称为类型&#xff0c;而一门语言支持的类型集也是这门语言最基本的特征。程序在需要把某个值保存下来以便将来使用时&…

用CSS中的动画效果做一个转动的表

<!DOCTYPE html> <html lang"en"><head><meta charset"utf-8"><title></title><style>*{margin:0;padding:0;} /*制作表的样式*/.clock{width: 500px;height: 500px;margin:0 auto;margin-top:100px;border-rad…

Post-hoc Concept Bottleneck Models (PCBM)

ICLR 2023 spotlight 文章链接&#xff1a;https://arxiv.org/abs/2205.15480 代码链接&#xff1a;https://github.com/mertyg/post-hoc-cbm 一、概述 Post-hoc CBM&#xff08;PCBM&#xff09;也是CBM大家族中的一员&#xff0c;因此它的基本逻辑与CBM一致&#xff0c;就是…

uniapp微信小程序接入友盟

一、登录友盟官网&#xff0c;按照引导注册友盟账号 二、在友盟平台新建小程序应用&#xff08;建好之后会得到appkey&#xff09; 进入小程序统计后台创建Appkey&#xff0c;按要求填写小程序名称及类型&#xff1a; 三、在微信小程序中接入SDK ---- 以下是npm 配置 1. 安装…

【docker实战】01 Linux上docker的安装

Docker CE是免费的Docker产品的新名称&#xff0c;Docker CE包含了完整的Docker平台&#xff0c;非常适合开发人员和运维团队构建容器APP。 Ubuntu 14.04/16.04&#xff08;使用 apt-get 进行安装&#xff09; # step 1: 安装必要的一些系统工具 sudo apt-get update sudo ap…

Vue中的默认插槽详解

Vue中的默认插槽详解 在 Vue 中&#xff0c;插槽&#xff08;Slot&#xff09;是一种非常强大且灵活的机制&#xff0c;用于在组件中插入内容。Vue 提供了两种类型的插槽&#xff1a;默认插槽&#xff08;Default Slot&#xff09;和具名插槽&#xff08;Named Slot&#xff09…

解决Windows11安装Docker 一直starting 的办法

Starting the Docker Engine... Docker Engine is the underlying technology that runs containers 关闭docker 管理员身份执行wsl --update后在启动。 另外&#xff0c;docker desktop-unexpected wsl error问题跟标题问题好像是同一个问题&#xff0c;我的是一直让其star…

阿里开源大模型 Qwen-72B 私有化部署

近期大家都知道阿里推出了自己的开源的大模型千问72B&#xff0c;据说对于中文非常友好&#xff0c;在开源模型里面&#xff0c;可谓是名列前茅。 千问拥有有强大的基础语言模型&#xff0c;已经针对多达 3 万亿个 token 的多语言数据进行了稳定的预训练&#xff0c;覆盖领域、…

网络运行状况监控工具

网络运行状况是网络在其操作和环境约束范围内按预期运行的能力&#xff0c;但是&#xff0c;随着云和人工智能等技术的出现&#xff0c;网络变得越来越复杂&#xff0c;维护其 IT 基础设施是一项越来越繁琐的任务。为了确保网络可靠性&#xff0c;组织需要了解每个端点的运行状…

乒乓球廉价底板及套胶评测4

球拍找到适应自己的不容易&#xff0c;因为初学者或者说业余爱好者无法确定是按打法特点选拍还是按拍子练打法特点&#xff0c;所以有时候就要孤注一掷&#xff0c;想想练球的初衷&#xff0c;不用被打的好的对手所左右。我和朋友打球的时候发现大家借力的能力越来越强&#xf…

神经网络 —— 模拟人脑的计算方式

神经网络能够反映人类大脑的行为&#xff0c;允许计算机程序识别模式&#xff0c;以及解决人工智能、机器学习和深度学习领域的常见问题。 人类发明的灵感来源有很多都是来自大自然&#xff0c;神经网络同样如此。人工神经网络是一种类似于人类神经网络的信息处理技术。但事实上…

Grafana监控数据可视化

Grafana 是一个可视化面板&#xff0c;有着非常漂亮的图表和布局展示&#xff0c;功能齐全的度量仪表盘和图形编辑器&#xff0c;支持 Graphite、zabbix、InfluxDB、Prometheus、OpenTSDB、Elasticsearch 等作为数据源&#xff0c;比 Prometheus 自带的图表展示功能强大太多&am…

Kubernetes技术与架构-集群管理

Kubernetes技术与架构提供支撑工具支持集群的规划、安装、创建以及管理。 数字证书 用户可以使用easyrsa、openssl、cfssl工具生成数字证书&#xff0c;在kubernetes集群的api server中部署数字证书用于访问鉴权 资源管理 如上所示&#xff0c;定义一个服务类service用于负…

jQuery显示,切换,隐藏的使用练习

<!DOCTYPE html> <html> <head lang"en"><meta charset"UTF-8"><title>标题</title><style>div {width: 200px;height: 200px;background-color: red;display: none;}</style> </head> <body>…

PulseGAN

研究背景 远程光电容积描记术 (rPPG) 是一种非接触式技术&#xff0c;用于测量面部视频中的心脏信号。健康监测和情绪识别等许多领域都迫切需要高质量的 rPPG 脉冲信号。然而&#xff0c;由于脉搏信号不准确的限制&#xff0c;现有的大多数rPPG方法只能用于获取平均心率&#…

算法训练营Day26

#Java #全排列 #回溯 开源学习资料 Feeling and experiences&#xff1a; 递增子序列&#xff1a;力扣题目链接 给你一个整数数组 nums &#xff0c;找出并返回所有该数组中不同的递增子序列&#xff0c;递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。 数组…