理论
预分频寄存器(TIMx_PSC):由于时钟源为:72MHz,T = 1/f = 1/72MHz,由于不好计算周期时间,则需要分频,若分72则T = 1/1MHz = 1us(1MHz = 一百万秒)
计数方式:向上(递增到某个数触发中断)、向下(递减到某一个数触发中断)、中心计数(递增到某一个数触发中断,再递减到某一个数触发中断)
比较值(参考:链接):
PWM1模式:若计数值小于有效值则高电平,计数值大于有效值则低电平,可以控制比较值来调节占空比
PWM2模式:与PWM1模式相反,计数值小于有效值则低电平,大于有效值则高电平
复用:Pin脚本身除了支持普通GPIO功能之外,还支持别的功能(使用别的功能就叫复用)
重映射:Pin脚本身不支持这些功能,配置重映射寄存器,使其具备别的功能
定时器分类
型号:STM32F103ZET6
基本定时器:TIM6、TIM7
通用定时器:TIM2~TIM5
高级定时器:TIM1、TIM8
代码编写
定时器中断实验:LED灯一秒闪烁一次、LED灯两秒闪烁一次、计算单片机运行时间、串口5秒返回单片机运行时间
PWM信号输出:呼吸灯、电机(由慢到快)、舵机
输入捕获:检测信号脉冲宽度(舵机脉冲宽度)
LED灯、UART、定时器、PWM配置
LED灯配置请看:链接
串口配置请看:链接
配置定时器:
72/7200 = 0.01,T = 1/f = 1/0.01MHz = 100us,计数10000,触发中断,100us * 10000 = 1s,1s = 1000000us,但单片机都是以0开始,所以分频值以及计数值都 -1
配置定时器向上计数溢出中断NVIC(嵌套向量中断控制器)
定时器(PWM)控制LED灯配置
LED(PB5)引脚说明:
所以配置定时器3,通道二,但配置完发现图中不符,需手动修改
定时器(PWM)控制电机配置
配置PB4引脚PWM控制电机速度
定时器(PWM)控制舵机配置
舵机脉冲周期:20ms
输入捕获定时器配置
配置定时器全局中断NVIC
LED灯闪烁、串口输出运行时间
LED1每隔一秒电平翻转一次,LED2每隔两秒电平翻转一次,串口输出单片机运行时间
Cube IDE代码
main.c
/* USER CODE BEGIN Includes */
#include <stdio.h> //27行
#include <string.h> //28行
/* USER CODE END Includes */
/* USER CODE BEGIN 0 */
uint32_t G_Timer_Count = 0; //59行
uint8_t UART_Count[200];
uint8_t UART_Flag = 0;
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim1); //开启定时器,97行
/* USER CODE END 2 */
if(UART_Flag) //104行
{
sprintf(UART_Count,"MCU run time is %lus",G_Timer_Count);
HAL_UART_Transmit(&huart1, UART_Count, strlen(UART_Count), 1000);
UART_Flag = 0; //打印一次后标志=0,等中断五次(5s)后
}
/* USER CODE BEGIN 4 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) //定时器触发函数,157行
{
if(htim == &htim1)
{
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
G_Timer_Count++;
if((G_Timer_Count % 5) == 0)
UART_Flag = 1; //每隔五秒,让标志=1
if((G_Timer_Count % 2) == 0) //LED2每隔两秒翻转电平
HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);
}
}
/* USER CODE END 4 */
PWM信号LED呼吸灯、电机、舵机
理论参考:链接
PWM信号控制LED,产生一个呼吸灯效果,以及对电机控制,控制速度由慢到快
Cube IDE代码
main.c
/* USER CODE BEGIN PV */
uint8_t PWM_Value = 0,LED_PWM_Value = 0,SG_PWM = 5; //47行
/* USER CODE END PV */
/* USER CODE BEGIN 2 */
//参数1:定时器句柄(指向配置好的定时器结构体的指针),参数2:定时器通道,93行
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2); //开启定时器3,通道2 PWM
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1); //开启定时器3,通道1 PWM
HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_1); //开启定时器4,通道1 PWM
/* USER CODE END 2 */
//103行
PWM_Value++;
PWM_Value = PWM_Value % 200;
if(PWM_Value > 99)
LED_PWM_Value = 200 - PWM_Value; //大于99时,从大到小(100,99...)
else
LED_PWM_Value = PWM_Value;
HAL_Delay(25);
//参数1:定时器句柄,参数2:定时器通道,参数3:比较值
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, LED_PWM_Value); //设置LED比较值
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, LED_PWM_Value); //设置电机比较值
if(PWM_Value % 30 == 0)
{
//每0.1ms计数值+1则0.5ms,比较值模式1,则占空比(高电平)0.5ms
SG_PWM += 5;
if(SG_PWM > 25)
SG_PWM = 5;
__HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_1, SG_PWM); //设置舵机比较值
}
输入捕获(IC)
捕获高电平持续时间
Cube IDE代码
捕获PWM高电平的时间
main.c
/* USER CODE BEGIN PTD */
uint32_t ic_state=0,period_count=0,ic_count=0,us_count; //32行
/* USER CODE END PTD */
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim2); //开启普通定时器中断
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_2); //开启捕获定时器中断
printf("Please connect MCU51 P2.0,and let SG90 run to detect signal!\n"); //串口输出捕获的时间(记得配置串口重定向)
/* USER CODE END 2 */
if(ic_state== 2 ) //103行(while里)
{
/* 始终设置的是1us触发计数值加1
* 一个周期大概65ms,若大于65只有捕获中断函数不好使
* 若period_count为1,则代表过了一个中断周期,时间(1*65535)us
* 若没有到整个中断周期时间则剩下的为:ic_count
* 所以两个相加等于整个高电平时间*/
us_count = ic_count + period_count * 0xFFFF;
printf("High level duration: %ldus \n",us_count); //(float)(us_count)/1000.000
ic_state = 0; //再将中间键恢复0,方便下次上升沿触发中断计时
}
/* USER CODE BEGIN 4 */
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) //输入捕获触发函数,161行
{
if(TIM2 == htim->Instance)
{
if ( ic_state == 0 )
{
__HAL_TIM_SET_COUNTER(htim,0); //将定时器从零开始计时
__HAL_TIM_SET_CAPTUREPOLARITY(&htim2, TIM_CHANNEL_2, TIM_INPUTCHANNELPOLARITY_FALLING); //设置成下降沿触发
ic_state = 1; //下次触发中断时(下降沿)则运行else
period_count = 0;
ic_count = 0;
}
else
{
ic_count = HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_2); //读取定时器计数值
__HAL_TIM_SET_CAPTUREPOLARITY(&htim2, TIM_CHANNEL_2, TIM_INPUTCHANNELPOLARITY_RISING); //设置成上降沿触发
ic_state = 2; //进入while里面的判断
}
}
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) //普通定时器中断触发函数
{
if(TIM2 == htim->Instance)
{
if(ic_state==1)
{
if(period_count==0XFFFF) //一次周期大约65ms,若进入这个需要(65*65535)ms
{
ic_state=2;
ic_count=0XFFFF; //返回一个超大数显示错误
}
else
period_count++;
}
}
}
/* USER CODE END 4 */