STM32F103C8T6第三天:pwm、sg90、超声波、距离感应按键开盖震动开盖蜂鸣器

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灯状态。
  1. RCC配置
  2. LED1灯配置
  3. 时钟数配置
  4. TIM2配置






  5. 工程配置
  6. 重写更新中断回调函数
  • 代码(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的状态
}
  1. 启动定时器
  • 在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连接到哪个定时器的哪一路?

  • 学会看产品手册:

开始实战!

  1. 设置时钟


  2. 设置定时器
  • 把极性设置为 Low,因为 LED 灯是低电平亮
  1. 配置工程

  2. 业务代码
  • 代码(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

编程实战

  • 需求: 使用超声波测距,当手离传感器距离小于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毫秒测一次距离
}
相关推荐
iCxhust25 分钟前
Keil µVision 调试指南---UART#1 模拟/调试窗口 完全使用教程
stm32·单片机·嵌入式硬件
iCxhust25 分钟前
51单片机引脚 ALE EA PSEN的用途
单片机·嵌入式硬件·51单片机
碎像30 分钟前
51单片机创建项目
单片机·嵌入式硬件·51单片机
木白CPP35 分钟前
MCU 进程内存布局详解(.text, .rodata, .data, .bss, 堆, 栈)
单片机·嵌入式硬件
Lugas Luo42 分钟前
车载录像存储性能模拟测试工具设计
linux·嵌入式硬件·测试工具
v132665623681 小时前
BK7258 wifi6音视频soc芯片应用分析
嵌入式硬件·物联网·音视频·iot·wifi6
風清掦1 小时前
【江科大STM32学习笔记-10】I2C通信协议 - 10.2 硬件 I2C 读写MPU6050
笔记·stm32·单片机·嵌入式硬件·学习
ALINX技术博客2 小时前
【黑金云课堂】FPGA技术教程Vitis开发:RTC中断讲解
单片机·嵌入式硬件·fpga开发
进击的小头2 小时前
第10篇:嵌入式芯片中断系统详解:NVIC与硬实时性优化设计
单片机·嵌入式硬件
菠萝地亚狂想曲2 小时前
Zephyr_02,SEM
单片机