1. 定时器介绍1(317.21)
- 软件定时(之前的定时方法)(软件延时)
- 缺点:不精确、占用CPU资源
c
void Delay500ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 4;
j = 129;
k = 119;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
定时器工作原理:
- 使用精准的时基,通过硬件的方式,实现定时功能。定时器核心就是计数器。
定时器分类:
- 基本定时器(TIM6 ~ TIM7)
- 通用定时器(TIM2 ~ TIM5)
- 高级定时器(TIM1 和 TIM8)
STM32F103C8T6定时器资源:
通用定时器介绍:
c
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.触发输入作为外部时钟或者按周期的
2. 定时器介绍1(318.22)
定时器计数模式:
定时器时钟源:
定时器溢出时间计算公式:(加一是因为计算机是从0开始计数的)
- 例如,要定时500ms(0.5s),则:PSC=7199,ARR=4999,Tclk=72M(72000000Hz)
3. 使用定时器中断点亮LED灯(319.23)
- 需求:使用定时器中断方法,每500ms翻转一次LED1灯状态。
- RCC配置
- LED1灯配置
- 时钟数配置
- TIM2配置
- 工程配置
- 重写更新中断回调函数
- 代码(6.timer_test/MDK-ARM)
c
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{//重写更新中断回调函数
if(htim->Instance == TIM2)
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8);//每过500ms翻转led1的状态
}
- 启动定时器
- 在main.c中,在定时器初始化命令之后加入以下代码:
HAL_TIM_Base_Start_IT(&htim2);
4. PWM概述(320.24)
STM32F103C8T6 PWM 资源:
- 高级定时器(TIM1):7路
- 通用定时器(TIM2~TIM4):各4路
PWM 输出模式:
- PWM 模式1:在向上计数时,一旦 CNT < CCRx 时输出为有效电平,否则为无效电平; 在向下计数时,一旦 CNT > CCRx 时输出为无效电平,否则为有效电平。
- PWM 模式2:在向上计数时,一旦 CNT < CCRx 时输出为无效电平,否则为有效电平; 在向下计数时,一旦 CNT > CCRx 时输出为有效电平,否则为无效电平。
PWM 周期与频率:
PWM 占空比:
- 由 TIMx_CCRx 寄存器决定。
5. PWM实现呼吸灯效果(321.25)
- 需求:使用PWM点亮LED1实现呼吸灯效果。
LED灯为什么可以越来越亮,越来越暗?
- 这是由不同的占空比决定的。
如何计算周期/频率?
- 假如频率为 2kHz ,则:PSC=71,ARR=499
LED1连接到哪个定时器的哪一路?
- 学会看产品手册:
开始实战!
- 设置时钟
- 设置定时器
- 把极性设置为 Low,因为 LED 灯是低电平亮
- 配置工程
- 业务代码
- 代码(7.pwm_test/MDK-ARM)
c
//main函数
// 定义变量
uint16_t pwmVal=0; //调整PWM占空比
uint8_t dir=1; //设置改变方向。1:占空比越来越大;0:占空比越来越小
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM4_Init();
// 使能 Timer4 第3通道 PWM 输出
HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_3);//打开pwm
// while循环实现呼吸灯效果
while (1)
{
HAL_Delay(1);//如果没有 可能会不亮
if (dir)
pwmVal++;//1:越来越亮
else
pwmVal--;//0:越来越暗
//pwmVal 达到 PWM 周期时,就会反向改变方向
if(pwmVal >= htim4.Init.Period)//越来越亮到顶端后
dir = 0;//变为越来越暗
else if (pwmVal <= 0)//越来越暗到底端后
dir = 1;//变为越来越亮
//修改比较值(CCRx)即修改占空比
__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_3, pwmVal);
}
6. 感应开关盖垃圾桶项目概述(322.26)
项目需求
- 检测靠近时,垃圾桶自动开盖并伴随滴一声,2 秒后关盖
- 发生震动时,垃圾桶自动开盖并伴随滴一声,2 秒后关盖
- 按下按键时,垃圾桶自动开盖并伴随滴一声,2 秒后关盖
项目框图
硬件清单
- SG90舵机,超声波模块,震动传感器,蜂鸣器
7. sg90舵机概述(324.28)
sg90舵机介绍
- PWM波的频率不能太高,大约50HZ,即周期=1/频率=1/50=0.02s,20ms左右。
确定周期/频率
- 如果周期为20ms,则 PSC=7199,ARR=199
角度控制
- 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
8. sg90舵机编程实战(323.27)
- 需求: 每隔1s,转动一个角度:0度 --> 45度 --> 90度 --> 135度 --> 180度 --> 0度
- 接线:
- STM32CubeMx工程配置
- 代码(8.sg90_test/MDK-ARM)
c
HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_3);//打开Time4 第3通道的 PWM
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);
}
9. 超声波传感器介绍及实战(325.29)
超声波传感器介绍
-
怎么让它发送波
- Trig ,给Trig端口至少10us的高电平
-
怎么知道它开始发送了
- Echo信号,由低电平跳转到高电平,表示开始发送波
-
怎么知道接收了返回波
- Echo,由高电平跳转回低电平,表示波回来了
-
怎么算时间
Echo引脚维持高电平的时间!- 波发出去的那一刻,开始启动定时器;
- 波返回来的拿一刻,开始停止定时器;
计算出中间经过多少时间。
-
怎么算距离
- 距离 = 速度 (340m/s)* 时间/2
- 距离 = 速度 (340m/s)* 时间/2
编程实战
- 需求: 使用超声波测距,当手离传感器距离小于5cm时,LED1点亮,否则保持不亮状态。
- 接线:
- Trig --- PB6
- Echo --- PB7
- LED1 --- PB8
- 定时器配置:
- 使用 TIM2 ,只用作计数功能,不用作定时。
- 将 PSC 配置为 71,则计数 1 次代表 1us 。
- 编写微秒级函数:
c
//使用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));
/* 关闭定时器2计数 */
__HAL_TIM_DISABLE(&htim2);
}
-
主函数:
//1. Trig ,给Trig端口至少10us的高电平
//2. echo由低电平跳转到高电平,表示开始发送波
//波发出去的那一下,开始启动定时器
//3. 由高电平跳转回低电平,表示波回来了
//波回来的那一下,我们开始停止定时器
//4. 计算出中间经过多少时间
//5. 距离 = 速度 (340m/s)* 时间/2(计数1次表示1us)
//每500毫秒测试一次距离 -
代码(9.sr_04_test/MDK-ARM)
c
int main()
{
int cnt = 0;
float distance = 0;
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM2_Init();
while (1)
{
//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);//从0开始计数
//3. 由高电平跳转回低电平,表示波回来了
//波回来的那一下,我们开始停止定时器
while(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_7)==GPIO_PIN_SET);
HAL_TIM_Base_Stop(&htim2);
//4. 计算出中间经过多少时间
cnt = __HAL_TIM_GetCounter(&htim2);
//5. 距离 = 速度 (340m/s)* 时间/2(计数1次表示1us)
distance = 340*100*0.000001*cnt/2; //单位:cm
if(distance < 5)
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_RESET);
else
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_SET);
//每500毫秒测试一次距离
HAL_Delay(500);
}
}
10. 封装超声波测距代码(326.30)
- 工程配置
- 代码(10.rubbish_test/MDK-ARM)
c
//使用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);
}
double get_distance()
{
int cnt = 0;
//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);//从0开始计数
//3. 由高电平跳转回低电平,表示波回来了
//波回来的那一下,我们开始停止定时器
while(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_7)==GPIO_PIN_SET);
HAL_TIM_Base_Stop(&htim2);
//4. 计算出中间经过多少时间
cnt = __HAL_TIM_GetCounter(&htim2);
//5. 距离 = 速度 (340m/s)* 时间/2(计数1次表示1us)
return (340*100*0.000001*cnt/2); //单位:cm
}
int main(void)
{
float distance = 0;
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM2_Init();
while (1)
{
distance = get_distance();
if(distance < 5)
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_RESET);
else
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_8,GPIO_PIN_SET);
//每500毫秒测试一次距离
HAL_Delay(500);
}
}
11. 实现距离感应开关盖(327.31)
- 代码(10.rubbish_test/MDK-ARM)
c
void openStatusLight()
{
//点亮LED1
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);
}
void closeStatusLight()
{
//熄灭LED1
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
HAL_Delay(150);每150毫秒测试一次距离
}
void initSG90_0()
{
HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_4); //启动定时器4
__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 5); //将舵机置为0度
}
void openDusbin()
{
__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 15); //将舵机置为90度
HAL_Delay(100);
}
void closeDusbin()
{
__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 5); //将舵机置为0度
HAL_Delay(150);//每150毫秒测一次距离
}
12. 添加按键开盖功能(328.32)
- STM32CubeMx工程配置
- 代码(10.rubbish_test/MDK-ARM)
c
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == GPIO_PIN_0)
{
openDusbin();
}
}
13. 添加震动开盖功能(329.33)
- STM32CubeMx工程配置
- 代码(10.rubbish_test/MDK-ARM)
c
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();//开灯
openDusbin();//开盖
}
}
}
14. 添加蜂鸣器(330.34)
- STM32CubeMx工程配置
- 代码(10.rubbish_test/MDK-ARM)
c
void openDusbin()
{
__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 15); //将舵机置为90度
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_4,GPIO_PIN_RESET);
HAL_Delay(100);//蜂鸣器响100ms
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_4,GPIO_PIN_SET);
HAL_Delay(2000);//开盖两秒
}
15. 解决垃圾筒抽抽Bug(331.35)
- 代码(10.rubbish_test/MDK-ARM)
c
void openDusbin()
{
if(flag == CLOSE)
{
flag = OPEN;
__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 15); //将舵机置为90度
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_4,GPIO_PIN_RESET);
HAL_Delay(100);//蜂鸣器响100ms
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_4,GPIO_PIN_SET);
}
HAL_Delay(2000);//开盖两秒
}
void closeDusbin()
{
__HAL_TIM_SetCompare(&htim4, TIM_CHANNEL_4, 5); //将舵机置为0度
flag = CLOSE;
HAL_Delay(150);//每150毫秒测一次距离
}