STM32学习笔记3 ---中断,定时器

目录

EXTI外部中断

NVIC嵌套中断向量控制器

EXTI外部中断

AFIO

旋转编码器

定时器TIM

TIM定时中断

​编辑​编辑

​编辑

TIM输出比较(OC)

引脚重映射

舵机

直流电机

TIM输入捕获(IC)

​编辑

TIM编码器接口

 附:公式相关

代码

EXTI外部中断

对射式红外传感器计次

流程总结 

旋转编码器计次

TIM定时器

定时器定时中断

 流程总结

定时器外部时钟

PWM驱动LED呼吸灯

流程总结

PWM驱动舵机

PWM驱动直流电机

输入捕获模式测 频率

流程总结 

PWMI模式测 频率 占空比

编码器接口测速

常用代码

AFIO

EXTI

NVIC

TIM

接线


EXTI外部中断

NVIC嵌套中断向量控制器

内核外设(叫号系统)

统一分配中断优先级和管理中断

NVIC优先级分组(给不同的中断(病人)分配优先级)

抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队

NVIC的中断优先级由优先级寄存器的4位(0~15)决定(16个优先级,值越小,优先级越高)

合为4


两块,先分组,再配置 


EXTI外部中断

检测GPIO电平变化,申请中断

第一个病人

支持的触发方式:上升沿(低电平变高电平时触发)/下降沿/双边沿/软件触发

支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断

AFIO

AFIO主要完成两个任务:复用功能引脚重映射、中断引脚选择

或门,多个输入全为0,输出才是0,有一个高电平1,则1

与门:多个输入全为1,输出才是1,有一个高电平0,则0

多个输入,选择一个输出

信号,外部驱动的,突发的,被动读取,转瞬即逝,不会等你

旋转编码器

用来测量位置、速度或旋转方向的装置,当其旋转轴旋转时,其输出端可以输出与旋转速度和方向对应的方波信号,读取方波信号的频率和相位信息即可得知旋转轴的速度和方向

带正交波形信号输出的编码器,是可以用来测方向的(正交输出)

PB14号引脚的电平信号就可以顺利通过AFIO,进入到后级EXTI电路了

A,B相都触发中断

只有在B相下降沿和A相低电平时,才判断为正转

在A相下降沿和B相低电平时,才判断为反转
 


PB14-----EXTI15_10 (EXTI14)

PB1-----EXTI1

PB0-----EXTI0


定时器TIM

TIM定时中断

定时触发中断,就是计数器

当这个计数器的输入是一个准确可靠的基准时钟的时候,那它在对这个基淮时钟进行计数的过程,实际上就是计时的过程(有着配备基淮时钟的一个计数器,计数也是计时了)

定时器的基淮时钟一般都是主频72MHz

16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时

具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能

TIM输出比较(OC)

定时器的输出比较模块输出一定频率和占空比的PWM波形

CNT:时基单元里的计数器

CCR:捕获/比较寄存器(输入捕获,输出比较共用)

CNT计数自增,CCR是我们给定的一个值,两者比较大小,输出置1置0,输出电平不断跳变的PWM波形

pwm波形,等效的实现模拟信号的输出(0和1间快速转换,足够快时相当于达到了一个中间的状态),具有惯性的系统中,对脉冲宽度调制,等效获得所需模拟参量(数字系统等效输出模拟量)

引脚重映射

这个TM2的CH1可以从PAO挪到PA15引脚上

舵机

舵机要求的周期时20ms

CCR    500-2500(运行过程中设置)

直流电机

电能转换为机械能,有两个电极

当电极正接时,电机正转,当电极反接时,电机反转

直流电机属于大功率器件,GPIO口无法直接驱动,需要配合电机驱动电路来操作

TB6612是一款双路H桥型的直流电机驱动芯片,可以驱动两个直流电机并且控制其转速方向

TIM输入捕获(IC)

对于一个定时器,输入捕获,输出比较只能使用一个不能同时使用,共用寄存器,共用引脚

输出比较模式下,CCR是只写的,要用SetCompare写入
输入捕获模式下,CCR是只读的,要用GetCapture读出

输入引脚电平跳变的瞬间,把CNT的值锁存到CCR中

可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数

每个高级定时器和通用定时器都拥有4个输入捕获通道,基本定时器没有输入捕获功能

TIM编码器接口

TIM的编码器接口接收正交信号(编码器产生的),自动控制CNT自增或自减

这个编码器接口,其实就相当于是一个带有方向控制的外部时钟,它同时控制着CNT的计数时钟和计数方向

相当于带有方向控制的外部时钟,同时控制CNT的计数时钟计数方向,CNT的值表示编码器的位置,隔一段时间取一次,再清零,取出来的值表示编码器的速度(测频法测正交脉冲的频率)

CNT的值表示了编码器的位置

两个输入引脚借用了输入捕获的通道1(CH1)和通道2(CH2)

CH3,CH4不能接编码器


 附:公式相关

定时1s  ==  定时频率为1Hz

PSC 与 ARR取值0-65535

预分频器PSC

自动重装器ARR

ARR,PSC,CCR共同决定输出PWM的周期和占空比


代码

EXTI外部中断
对射式红外传感器计次

流程总结 

(这种需要实时处理的就是中断信号,让系统承认他,给他配置了中断信号的待遇,中断程序中实现了计次操作)

1.初始化

RCC (GPIOB , AFIO , EXTI(无需) , NVIC(内核的外设无需))

GPIO  (端口为输入模式(参考手册,浮空上拉或下拉))

AFIO(选择GPIOx , 连接到EXTI)

(PB14号引脚的电平信号就可以顺利通过AFIO , 进入到后级EXTI电路了)

EXTI(选择边沿触发方式--上升沿\\\\\\\\选择触发响应方式--中断响应)

NVIC(给中断选一个合适的优先级)

(进入CPU,CPU收到中断信号,跳转到中断函数执行中断程序)

2.写中断函数(EXTI15_10_IRQHandler)

中断函数名字固定,每个中断通道(EXTI15_10_IRQn)对应一个中断函数

中断标志位判断是否为1(EXTI10-EXTI15都可进来,但当前是EXTI14)

执行中断程序

清除中断标志位


main.c
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "CountSensor.h"

int main(void)
{
	
	OLED_Init();	
	CountSensor_Init();
	
	OLED_ShowString(1, 1, "Count:");//1行1列显示字符串Count:
										
	while (1)//实时获取刷新那个值,这个速度比你中断程序的外界的那个速度快
	{	
		OLED_ShowNum(1, 7, CountSensor_Get(), 5);//OLED不断刷新显示CountSensor_Get的返回值
	}
}
CountSensor.c
#include "stm32f10x.h"                  // Device header

//一个数字统计中断触发的次数
uint16_t CountSensor_Count;

void CountSensor_Init(void)//外部中断(所经所有模块)配置一下(初始化)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	//GPIO
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStructure);	
	
	//AFIO 外部中断 引脚选择
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);

    //EXTI
	EXTI_InitTypeDef EXTI_InitStructure;
	EXTI_InitStructure.EXTI_Line = EXTI_Line14;
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;	//中断模式
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;	//下降沿触发
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
	EXTI_Init(&EXTI_InitStructure);
	
	//NVIC
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//抢占响应优先级配置
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;//选择配置NVIC的EXTI15_10线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//抢占优先级配置给1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//响应优先级配置给1
	NVIC_Init(&NVIC_InitStructure);

}

//返回那个值
uint16_t CountSensor_Get(void)
{
	return CountSensor_Count;
}

//中断函数
void EXTI15_10_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line14)==SET)//读取EXTI14的标志位是否为1
	{//执行中断程序
		CountSensor_Count++;
		EXTI_ClearITPendingBit(EXTI_Line14);//清除中断标志位	
	}

}
旋转编码器计次
//main.c
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Encoder.h"

int16_t Num;			//定义待被旋转编码器调节的变量

int main(void)
{	
	OLED_Init();		
	Encoder_Init();				
	OLED_ShowString(1, 1, "Num:");				
	while (1)
	{
		Num += Encoder_Get();				//获取自上此调用此函数后,旋转编码器的增量值,并将增量值加到Num上
		OLED_ShowSignedNum(1, 5, Num, 5);	
	}
}
///Encoder.c
//旋转编码器 
#include "stm32f10x.h"                  // Device header


int16_t Encoder_Count;//带着符号的(正反转)

void Encoder_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	//GPIO
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStructure);	
	
	//AFIO 外部中断 引脚选择
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);

    //EXTI
	EXTI_InitTypeDef EXTI_InitStructure;
	EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1;
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;	//中断模式
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;	//下降沿触发
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
	EXTI_Init(&EXTI_InitStructure);
	
	//NVIC
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//抢占响应优先级配置
	//+要对两个通道分别设置优先级
	NVIC_InitTypeDef NVIC_InitStructure;
	
	NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;//选择配置NVIC的EXTI15_10线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;	
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//抢占优先级配置给1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//响应优先级配置给1
	NVIC_Init(&NVIC_InitStructure);
	
	
	NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;//选择配置NVIC的EXTI15_10线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;	
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//抢占优先级配置给1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;//响应优先级配置给1
	NVIC_Init(&NVIC_InitStructure);


}


int16_t Encoder_Get(void)//返回值
{
	//返回Encoder_Count后清零,所以返回的是每次的增量
	
	int16_t Temp;
	Temp = Encoder_Count;
	Encoder_Count = 0;
	return Temp;
}




//第一个中断函数
void EXTI0_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line0) == SET)//判断是否是外部中断0号线触发的中断
	{
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)//PB0的下降沿触发中断,检测另一相PB1的电平,判断旋转方向
		{
			Encoder_Count --;//反转,计数变量自减
		}
		EXTI_ClearITPendingBit(EXTI_Line0);		
	}
}
//第二个中断函数
void EXTI1_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line1) == SET)		
	{

		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)//PB1的下降沿触发中断,检测另一相PB0的电平
		{
			Encoder_Count ++;//正转
		}
		EXTI_ClearITPendingBit(EXTI_Line1);			
													
	}
}

TIM定时器
定时器定时中断

 流程总结

1.初始化

RCC开启时钟(通用定时器TIM2(APB1)

选择时基单元时钟(选择定时器的时钟源):RCC内部时钟

配置时基单元

使能 更新中断(中断输出配置):(UI,与更新事件对应的那个)

NVIC(打开定时器中断的通道,并分配一个优先级)(两块,分组,配置)

启动(使能)定时器(运行控制)

2.中断函数

检查中断标志位

清除标志位


main.c
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"

uint16_t Num;			//定义在定时器中断里自增的变量

int main(void)
{
	
	OLED_Init();		
	Timer_Init();		//定时中断初始化
	
	/*显示静态字符串*/
	OLED_ShowString(1, 1, "Num:");			//1行1列显示字符串Num:
	
	while (1)
	{
		OLED_ShowNum(1, 5, Num, 5);			//不断刷新显示Num变量
	}
}

//定时器中断函数
void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)//查看标志位
	{
		Num++;
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);//清楚标志位
	}
}
Timer.c
#include "stm32f10x.h"                  // Device header

void Timer_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	//选择定时器的时钟源
	TIM_InternalClockConfig(TIM2);//设置TIM2定时器的时基单元的时钟源是内部时钟
	
	//配置(初始化)时基单元
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//和滤波器有关的一个参数
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	//计数器的计数模式向上计数
	TIM_TimeBaseInitStructure.TIM_Period = 10000-1;//ARR自动重装器的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 7200-1;//PSC预分频器的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重复计数器的值(高级定时器才有的)(这里无需)
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
	
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);//避免刚初始化完就进中断的问题,让定时器从0开始					//清除定时器更新标志位

	
	//使能更新中断(开启更新中断到NVIC的通路)
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
	
	//NVIC
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//抢占响应优先级配置
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;//选择配置NVIC的EXTI15_10线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;//抢占优先级配置给1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//响应优先级配置给1
	NVIC_Init(&NVIC_InitStructure);	


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

}


/*
//定时器中断函数
void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)//查看标志位
	{
		
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);//清楚标志位
	}
}
*/
定时器外部时钟

对射式红外传感器作为一个外部时钟

1.通过ETR引脚的外部时钟模式2(内部时钟改为外部时钟)

2.增加GPIO时钟及初始化

3.预分频自动重装值改小一点

/*//选择定时器的时钟源
	TIM_InternalClockConfig(TIM2);//设置TIM2定时器的时基单元的时钟源是内部时钟
*/
//参:外部触发预分频器。外部触发极性。滤波器相关
TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x00);

PWM驱动LED呼吸灯

流程总结

GPIO的PA0引脚输出PWM波驱动LED)(LED变成了高电平点亮(更直观一点)(占空比越大,LED越亮,占空比越小。LED就越暗))

运行过程中不断更改CCR的值,以更改占空比,占空比越大,LED越亮

1.初始化

RCC开启时钟(TIM外设和GPIO外设)

初始化时基单元

初始化输出比较单元(里面包括这个CCR的值、输出比较模式(PWM1)、极性选择、输出使能这些参数)

(PA0口对应第一个输出比较通道OC1)

初始化GPIO(把PWM对应的GPIO口,初始化为复用推挽输出的配置)        

运行控制:启动计数器

2.更改CCR的值


/main.c
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
uint8_t i;			//定义for循环的变量

int main(void)
{
	OLED_Init();		//OLED初始化
	PWM_Init();			//PWM初始化	
	while (1)
	{
		for (i = 0; i <= 100; i++)//逐渐变亮
		{
			PWM_SetCompare1(i);			
			Delay_ms(10);//非常关键!!!!!				
		}
		for (i = 0; i <= 100; i++)//逐渐变暗
		{
			PWM_SetCompare1(100 - i);	
			Delay_ms(10);				
		}
	}
}
 PWM.C
#include "stm32f10x.h"                  // Device header
void PWM_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟
	
	//GPIO
	GPIO_InitTypeDef GPIO_InitStructure;	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;		
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);								
																					
	TIM_InternalClockConfig(TIM2);//选择TIM2定时器的时钟为内部时钟	
	//时基单元初始化
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				//定义结构体变量
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;     //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数
	TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;//ARR					//计数周期,即ARR的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;//PSC				//预分频器,即PSC的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            //重复计数器,高级定时器才会用到
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);	
	//初始化输出比较通道
	TIM_OCInitTypeDef TIM_OCInitStructure;
	TIM_OCStructInit(&TIM_OCInitStructure);	//给结构体赋初始值(不想列出所有成员时可以用)	
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//设置输出比较模式	
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//设置输出比较极性
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//设置输出使能
	TIM_OCInitStructure.TIM_Pulse = 0;//CCR
	TIM_OC1Init(TIM2, &TIM_OCInitStructure);
	//启动定时器
	TIM_Cmd(TIM2, ENABLE);			
}
//根据传入的参数更改CCR的值
void PWM_SetCompare1(uint16_t Compare)
{
	TIM_SetCompare1(TIM2, Compare);//用于单独更改通道1的CCR值
}
PWM驱动舵机

PA1口   通道2

(同一个定时器的不同通道,公用计数器,频率一样,占空比由CCR决定,可各自设置,由于计数器更新。所有PWM同时跳变,相位同步)

运行过程中设置CCR

//main.c 
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Servo.h"
#include "Key.h"
uint8_t KeyNum;			//按键键码
float Angle;			//定义角度变量
int main(void)
{
	//模块初始化
	OLED_Init();		//OLED初始化
	Servo_Init();		//舵机初始化
	Key_Init();			//按键初始化	
	//显示静态字符串
	OLED_ShowString(1, 1, "Angle:");	//1行1列显示字符串Angle:
	
	while (1)
	{
		KeyNum = Key_GetNum();			
		if (KeyNum == 1)				//按键按下
		{
			Angle += 30;				//角度变量自增30
			if (Angle > 180)			//角度变量超过180后
			{
				Angle = 0;				//角度变量归零
			}
		}
		Servo_SetAngle(Angle);			//设置舵机的角度为角度变量
		OLED_ShowNum(1, 7, Angle, 3);	
	}
}
/Servo.c
#include "stm32f10x.h"                  // Device header
void Servo_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);						
	TIM_InternalClockConfig(TIM2);			
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;    
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; 
	TIM_TimeBaseInitStructure.TIM_Period = 20000 - 1;				
	TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;				
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);            
	TIM_OCInitTypeDef TIM_OCInitStructure;							
	TIM_OCStructInit(&TIM_OCInitStructure);                        	                                                              	                                                                //避免结构体初值不确定的问题
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;               
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;       
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;   
	TIM_OCInitStructure.TIM_Pulse = 0;								
	TIM_OC2Init(TIM2, &TIM_OCInitStructure);                      		
	TIM_Cmd(TIM2, ENABLE);			
}
//根据传入的参数更改CCR的值
void PWM_SetCompare2(uint16_t Compare)
{
	TIM_SetCompare2(TIM2, Compare);//用于单独更改通道1的CCR值
}
void Servo_SetAngle(float Angle)//舵机设置角度
{
	PWM_SetCompare2(Angle / 180 * 2000 + 500);	//设置占空比
												//将角度线性变换,对应到舵机要求的占空比范围上
}

PWM驱动直流电机

TB6612电机驱动模块

PIN2,通道3

输入捕获模式测 频率

(只能测高低电平数字信号)(测周法)  测量信号从PA6输入  待测PWM信号自己生成

通过ARR调节频率,会影响占空比,所以通过调节PSC

ARR定-

在PWM呼吸灯程序上,加一个模块单独修改PSC


流程总结 

1.PA0口输出频率为1kHz占空比50%的待测信号

2.输入捕获初始化

RCC开启时钟(TIM和GPIO的)(TIM2输出PWM,所以用TIM3输入捕获,TIM3--CH1--PA6)

GPIO初始化,输入模式(上拉或浮空)

配置时基单元,让CNT计数器在内部时钟的驱动下自增运行(自动重装值要设置大点防止计数溢出)

配置输入捕获单元(滤波器,极性(上升沿触发),直连或交叉通道,分频器(不分频每次触发都有效))(结构体)(TIM3通道一)

主从模式的相关配置

选择从模式(TRGI)的触发源(TI1FP1)

选择触发后执行的操作(从模式为Reset)

开启定时器(TIM_Cmd)

3.测周法公式配置f_x=f_c / N

(f_c当前是1000000Hz(f_c=72M /(PSC+1)),N为CCR的值)


void PWM_SetPrescaler(uint16_t Prescaler)
{
	TIM_PrescalerConfig(TIM2, Prescaler, TIM_PSCReloadMode_Immediate);		//设置PSC的值
}
main.c
//-----------------------输入捕获测频率-----------------------------
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
#include "IC.h"
int main(void)
{	OLED_Init();		
	PWM_Init();			
	IC_Init();				
	OLED_ShowString(1, 1, "Freq:00000Hz");			
	//PWM模块提供输入捕获的测试信号
	PWM_SetPrescaler(720 - 1);					//PWM频率Freq = 72M / (PSC + 1) / 100
	PWM_SetCompare1(50);						//PWM占空比Duty = CCR / 100
	while (1)
	{	OLED_ShowNum(1, 6, IC_GetFreq(), 5);	//不断刷新显示输入捕获测得的频率
	}
}
///IC.c
#include "stm32f10x.h"                  // Device header
void IC_Init(void)
{	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);			//开启TIM2的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟	
	//GPIO
	GPIO_InitTypeDef GPIO_InitStructure;	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;		
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);																												            
    TIM_InternalClockConfig(TIM3);//选择TIM2定时器的时钟为内部时钟	
	//时基单元初始化
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				//定义结构体变量
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;     //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数
	TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1;//ARR					//计数周期,即ARR的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;//PSC				//预分频器,即PSC的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            //重复计数器,高级定时器才会用到
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);	
	//初始化输入捕获单元
	TIM_ICInitTypeDef TIM_ICInitStructure;							
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;//选择配置定时器通道1
	TIM_ICInitStructure.TIM_ICFilter = 0xF;//输入滤波器参数,可以过滤信号抖动
	TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//上升沿触发
	TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;//不分频
	TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;//直连通道
	TIM_ICInit(TIM3, &TIM_ICInitStructure);		
	//触发源选择
	TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);	
	//选择从模式
	TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);			
	//启动定时器
	TIM_Cmd(TIM3, ENABLE);
}	
//测周法公式配置f_x=f_c / N
uint32_t IC_GetFreq(void)
{	return 1000000 / (TIM_GetCapture1(TIM3) + 1);//不执行+1的操作也可
}

PWMI模式测 频率 占空比

输入捕获初始化的部分升级,配置成两个通道同时捕获同一个引脚的模式

	/*PWMI模式初始化*/
	TIM_ICInitTypeDef TIM_ICInitStructure;							//定义结构体变量
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;				//选择配置定时器通道1
	TIM_ICInitStructure.TIM_ICFilter = 0xF;							//输入滤波器参数,可以过滤信号抖动
	TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;		//极性,选择为上升沿触发捕获
	TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;			//捕获预分频,选择不分频,每次信号都触发捕获
	TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;	//输入信号交叉,选择直通,不交叉
	TIM_PWMIConfig(TIM3, &TIM_ICInitStructure);	
/
//获取占空比
uint32_t IC_GetDuty(void)
{
	return (TIM_GetCapture2(TIM3) + 1) * 100 / (TIM_GetCapture1(TIM3) + 1);	//占空比Duty = CCR2 / CCR1 * 100,这里不执行+1的操作也可
}

在函数里,会自动把剩下的一个通道初始化成相反的配置

比如我这里传入通道1,直连,上升沿

那函数里面就会顺带配置通道2,交叉,下降沿
 

编码器接口测速

(定时器定时中断)

1.初始化

RCC开启时钟(GPIO,TIM)

配置GPIO,时基单元

配置输入捕获单元(滤波器,极性选择)

启动定时器

(测编码器位置,直接读CNT值,测编码器速度和方向,设置匝门时间,取出CNT并清零)

	/*定时器编码器接口配置*/
TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);

常用代码

AFIO
//复位,清除所有操作
void GPIO_AFIODeInit(void);
//配置AFIO时间输出功能
void GPIO_EventOutputConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
void GPIO_EventOutputCmd(FunctionalState NewState);
//引脚重映射
void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);
//(外部中断)配置AFIO数据选择器,选择中断引脚
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
EXTI
void EXTI_DeInit(void);
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
void EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct);
//软件触发外部中断(外部引脚触发中断时不需要)
void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line);
//读取标志位
//主程序
FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line);//获取指定的标志位是否被置1
void EXTI_ClearFlag(uint32_t EXTI_Line);//清除置1的标志位
//中断函数
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);获取中断标志位是否被置1
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);//清除中断挂起标志位
NVIC

(misc.h)

//中断分组
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);

void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
//设置中断向量表
void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset);
//系统低功耗配置
void NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState NewState);
TIM
void TIM_DeInit(TIM_TypeDef* TIMx);//恢复默认配置
//时基单元初始化
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);//同before
//使能计数器(运行控制)
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
//使能中断输出信号(中断输出控制)
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
//(时基单元的时钟选择部分)
void TIM_InternalClockConfig(TIM_TypeDef* TIMx);//选择内部时钟
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);//选择ITRx
其他定时器的时钟
void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,
                                uint16_t TIM_ICPolarity, uint16_t ICFilter);//选择TIx
捕获通道的时钟
void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
                             uint16_t ExtTRGFilter);//选择ETR通过外部时钟模式1输入的时钟
void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, 
                             uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);//选择ETR通过外部时钟模式2输入的时钟
//单独用来配置ETR引脚的预分频器、极性、滤波器这些参数的
void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
                   uint16_t ExtTRGFilter);
//单独写预分频值的
void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);
//改变计数器的计数模式
void TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode);
//自动重装器预装功能配置
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
//给计数器写入一个值
void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);
//给自动重装器写一个值
void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload);
//获取当前计数器的值
uint16_t TIM_GetCounter(TIM_TypeDef* TIMx);
//获取当前预分频器的值
uint16_t TIM_GetPrescaler(TIM_TypeDef* TIMx);
//获取标志位,清除标志位
FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);


//配置输出比较模块
void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC2Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC3Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC4Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
//给输出比较结构体赋默认值
void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct);
//配置强制输出模式
void TIM_ForcedOC1Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC2Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC3Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
void TIM_ForcedOC4Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
//单独更改CCR寄存器值
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);
void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);
void TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3);
void TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4);
//仅高级定时器,输出PWM时需调用
void TIM_CtrlPWMOutputs(TIM_TypeDef* TIMx, FunctionalState NewState);
//给结构体赋初始值
void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct);

//结构体配置初始化输入捕获单元(四个通道共用了一个函数(可能有交叉通道的配置))(一次配置一个通道)
void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
//初始化输入捕获单元(两个通道)
void TIM_PWMIConfig(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
void TIM_Ivoid TIM_SelectInputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
//3主从触发
//选择输入触发源TRGI
CStructInit(TIM_ICInitTypeDef* TIM_ICInitStruct);
//选择输出触发源TRGO
void TIM_SelectOutputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_TRGOSource);
//选择从模式
void TIM_SelectSlaveMode(TIM_TypeDef* TIMx, uint16_t TIM_SlaveMode);
//配置通道里的分频器
void TIM_SetIC1Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetIC2Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetIC3Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetIC4Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
//读取四个通道的CCR
uint16_t TIM_GetCapture1(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture2(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture3(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture4(TIM_TypeDef* TIMx);

接线

对射式红外传感器计次

旋转编码器计次

定时器定时中断

定时器外部时钟

PWM驱动LED呼吸灯

PWM驱动舵机

PWM驱动直流电机

输入捕获模式测频率    PWMI模式测 频率 占空比

编码器接口测速

  • 8
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值