1.定时器介绍
1.1 工作原理
使用精准的时基,通过硬件的方式,实现定时功能。
1.2 定时器分类
- 基本定时器(TIM6~TIM7)
- 通用定时器(TIM2~TIM5)
- 高级定时器(TIM1和TIM8)
1.3 通用定时器介绍
1) 16 位向上、向下、向上/向下自动装载计数器(TIMx_CNT)。
2) 16 位可编程(可以实时修改)预分频器(TIMx_PSC),计数器时钟频率的分频系数为 1~65535 之间的任意数值。
3)4 个独立通道(TIMx_CH1~4),这些通道可以用来作为:
A.输入捕获
B.输出比较
C.PWM 生成(边缘或中间对齐模式)
D.单脉冲模式输出
4)可使用外部信号(TIMx_ETR)控制定时器和定时器互连(可以用 1 个定时器控制另外一个定时器)的同步电路。
5)如下事件发生时产生中断/DMA:
A.更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
B.触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
C.输入捕获
D.输出比较
E.支持针对定位的增量(正交)编码器和霍尔传感器电路
F.触发输入作为外部时钟或者按周期的电流管理
1.4 定时器计数模式
1.5 定时器溢出时间计算公式
例如,要定时500ms,则:PSC=7199,ARR=4999,Tclk=72M(PSC和ARR的值没有规定 只需要预先想好溢出时间的值 通过公式能够整除得到即可)
1.6 定时器中断实验
需求:使用定时器中断方法,每500ms翻转一次LED1灯状态。
1. RCC配置
2. LED1灯配置
3. 时钟数配置
4. TIM2配置
5. 工程配置
6. 重写更新中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if(htim->Instance == TIM2){HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);//翻转电平}
}
7. 启动定时器
在main.c中,在定时器初始化命令之后加入以下代码:
HAL_TIM_Base_Start_IT(&htim2);
2.PWM介绍
2.1 STM32F103C8T6 PWM资源
高级定时器(TIM1):7路
通用定时器(TIM2~TIM4):各4路
2.2 PWM输出模式
- PWM模式1:在向上计数时,一旦 CNT < CCRx 时输出为有效电平,否则为无效电平; 在向下计数时,一旦 CNT > CCRx 时输出为无效电平,否则为有效电平。
- PWM模式2:在向上计数时,一旦 CNT < CCRx 时输出为无效电平,否则为有效电平; 在向下计数时,一旦 CNT > CCRx 时输出为有效电平,否则为无效电平。
2.3 PWM周期与频率
2.4 PWM占空比
由TIMx_CCRx寄存器决定。
2.5 PWM实验
需求:使用PWM点亮LED1实现呼吸灯效果。
2.5.1 LED灯为什么可以越来越亮,越来越暗?
这是由不同的占空比决定的。
2.5.2 如何计算周期/频率?
假如频率为 2kHz ,则:PSC=71,ARR=499
2.5.3 LED1连接到哪个定时器的哪一路?
学会看产品手册
2.6 PWM呼吸灯实验
1. 设置时钟
2. 设置定时器
3. 配置工程
4. 业务代码
// 定义变量
uint16_t pwmVal=0; //调整PWM占空比
uint8_t dir=1; //设置改变方向。1:占空比越来越大;0:占空比越来越小
// 使能 Timer4 第3通道 PWM 输出
HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_3);
// while循环实现呼吸灯效果
while (1)
{HAL_Delay(1);if(dir){pwmVal++;if(pwmVal >= 499){dir = 0;}}else{pwmVal--;if(pwmVal == 0){dir = 1;}}//修改比较值,修改占空比__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_3, pwmVal);
}
3.项目需求
- 检测靠近时,垃圾桶自动开盖并伴随滴一声,2秒后关盖
- 发生震动时,垃圾桶自动开盖并伴随滴一声,2秒后关盖
- 按下按键时,垃圾桶自动开盖并伴随滴一声,2秒后关盖
4.项目框图
5.sg90舵机介绍及实战
PWM波的频率不能太高,大约50HZ,即周期=1/频率=1/50=0.02s,20ms左右。
5.1 确定频率/周期
如果周期为20ms,则 PSC=7199,ARR=199
5.2 角度控制
0.5ms-------------0度; 2.5% 对应函数中CCRx为5
1.0ms------------45度; 5.0% 对应函数中CCRx为10
1.5ms------------90度; 7.5% 对应函数中CCRx为15
2.0ms-----------135度; 10.0% 对应函数中CCRx为20
2.5ms-----------180度; 12.5% 对应函数中CCRx为25
5.3 CCRx计算
以0度为例:
这里的CCRx就是有效电平 上下图对比可知CCRx对应0.5ms ARR对应20ms 呈现比例关系 但在上面我们设置ARR=200 所以通过比例式得到:
5.4 编程实现
5.4.1 需求
每隔1s,转动一个角度:0度 --> 45度 --> 90度 --> 135度 --> 180度 --> 0度
5.4.2 硬件接线
5.4.3 代码
HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_3);
while (1)
{HAL_Delay(1000);__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_3, 5);HAL_Delay(1000);__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_3, 10);HAL_Delay(1000);__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_3, 15);HAL_Delay(1000);__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_3, 20);HAL_Delay(1000);__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_3, 25);
}
6.超声波模块介绍及实战
6.1 超声波传感器介绍
6.2 编程实现
6.2.1 需求
使用超声波测距,当手离传感器距离小于5cm时,LED1点亮,否则保持不亮状态。
6.2.2 接线
Trig --- PB6
Echo --- PB7
LED1 --- PB8
6.2.3 定时器配置
6.2.4 编写微妙级函数
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
//使用TIM2来做us级延时函数
void TIM2_Delay_us(uint16_t n_us)
{/* 使能定时器2计数 */__HAL_TIM_ENABLE(&htim2);__HAL_TIM_SetCounter(&htim2, 0);while(__HAL_TIM_GetCounter(&htim2) < ((1 * n_us)-1) );/* 关闭定时器2计数 */__HAL_TIM_DISABLE(&htim2);
}/* USER CODE END 0 */
6.2.5 主函数
//1. Trig ,给Trig端口至少10us的高电平
//2. echo由低电平跳转到高电平,表示开始发送波
//波发出去的那一下,开始启动定时器
//3. 由高电平跳转回低电平,表示波回来了
//波回来的那一下,我们开始停止定时器
//4. 计算出中间经过多少时间
//5. 距离 = 速度 (340m/s)* 时间/2(计数1次表示1us) //每500毫秒测试一次距离
/* USER CODE BEGIN 1 */int cnt=0;float distance=0;/* USER CODE END 1 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 *///1. Trig ,给Trig端口至少10us的高电平HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_SET);//拉高TIM2_Delay_us(20);HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_RESET);//拉低//2. echo由低电平跳转到高电平,表示开始发送波//波发出去的那一下,开始启动定时器while(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_7)==GPIO_PIN_RESET);HAL_TIM_Base_Start(&htim2);__HAL_TIM_SetCounter(&htim2, 0);//3. 由高电平跳转回低电平,表示波回来了//波回来的那一下,我们开始停止定时器while(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_7)==GPIO_PIN_SET);HAL_TIM_Base_Stop(&htim2);//4. 计算出中间经过多少时间time_us = __HAL_TIM_GetCounter(&htim2);//5. 距离 = 速度 (340m/s)* 时间/2(计数1次表示1us)distance = time_us * 340/2 * 0.000001 * 100; //单位:cm//距离小于10,点灯if(distance < 5)HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_RESET);elseHAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_SET);//每500毫秒测试一次距离HAL_Delay(500);}/* USER CODE END 3 */
7.项目设计与实现
7.1 项目设计
超声波模块:
Trig -- PB6
Echo -- PB7
sg90舵机:
PWM -- PB9
按键:
KEY1 -- PA0
LED灯:
LED1 -- PB8
震动传感器:
D0 -- PB5
VCC -- 5V
蜂鸣器:
IO -- PB4
VCC -- 3V3
7.2 项目实现
/* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV */
float distance;
#define OPEN 1
#define CLOSE 0
/* USER CODE END PV *//* USER CODE BEGIN PV */
char flag = CLOSE;
/* USER CODE END PV *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 *///测距获取信息
float get_distance()
{//1. Trig ,给Trig端口至少10us的高电平HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_SET);//拉高TIM2_Delay_us(20);HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_RESET);//拉低//2. echo由低电平跳转到高电平,表示开始发送波//波发出去的那一下,开始启动定时器while(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_7)==GPIO_PIN_RESET);HAL_TIM_Base_Start(&htim2);__HAL_TIM_SetCounter(&htim2, 0);//3. 由高电平跳转回低电平,表示波回来了//波回来的那一下,我们开始停止定时器while(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_7)==GPIO_PIN_SET);HAL_TIM_Base_Stop(&htim2);//4. 计算出中间经过多少时间 //5. 距离 = 速度 (340m/s)* 时间/2(计数1次表示1us)return __HAL_TIM_GetCounter(&htim2) * 340/2 * 0.000001 * 100; //单位:cm
}void openStatusLight()
{HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_RESET);//打开LED1
}void closeStatusLight()
{HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_SET);//熄灭LED1
}void initSG90()
{HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_4);__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 5);//将舵机设罿0度
}//开盖,亮指示灯,蜂鸣器响
void openDubsin()
{if(flag == CLOSE){__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 15);//打开垃圾盖 舵机设置90度HAL_GPIO_WritePin(GPIOB,GPIO_PIN_4,GPIO_PIN_RESET);HAL_Delay(100);HAL_GPIO_WritePin(GPIOB,GPIO_PIN_4,GPIO_PIN_SET);flag = OPEN;}HAL_Delay(2000);
}//关盖,灭指示灯,每100ms检测一次
void closeDubsin()
{__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 5);flag = CLOSE;HAL_Delay(150);
}//按键、震动传感器中断
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{if(GPIO_Pin == GPIO_PIN_0 || GPIO_Pin == GPIO_PIN_5){if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET ||HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_5) == GPIO_PIN_RESET){openStatusLight();openDubsin();//这里只需要设置打弿的原因:按键或振动进入中断 打开垃圾盖后逿出中断 当执行到whlie时 超声波模块会不断进行测距 当大于10cm时 垃圾盖会自动关上}} }
}//使用TIM2来做us级延时函数
void TIM2_Delay_us(uint16_t n_us)
{/* 使能定时器2计数 */__HAL_TIM_ENABLE(&htim2);__HAL_TIM_SetCounter(&htim2, 0);while(__HAL_TIM_GetCounter(&htim2) < ((1 * n_us)-1) );/* 关闭定时器2计数 */__HAL_TIM_DISABLE(&htim2);
}/* USER CODE END 0 */
//===================================================================/* USER CODE BEGIN SysInit */HAL_NVIC_SetPriority(SysTick_IRQn,0,0);//提高滴答定时器优先级/* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_TIM2_Init();MX_TIM4_Init();/* USER CODE BEGIN 2 */initSG90();//舵机初始化 其函数内有使能开始输出PWM信号HAL_NVIC_SetPriority(SysTick_IRQn,0,0);//提高滴答定时器(Delay函数)的中断优先级(提升到0/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 *///距离检测开关盖distance = get_distance();//超声波测距if(distance < 10)//小于10cm 打开垃圾盖同时LED1开启{openStatusLight();openDubsin();}else//大于10cm 关闭垃圾盖同时LED1熄灭{closeStatusLight();closeDubsin();}}/* USER CODE END 3 */