IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> STM32实战总结:HAL之PWM蜂鸣器 -> 正文阅读

[嵌入式]STM32实战总结:HAL之PWM蜂鸣器

PWM蜂鸣器基础知识参考:

51单片机外设篇:蜂鸣器_路溪非溪的博客-CSDN博客

查看原理图

根据原理图可知,只要在PA8端口,即TIM2和输入某种频率的PWM波即可发出声响。

用51的老办法,手动构造一个方波信号,也能驱动蜂鸣器。

那么,在32中,是怎么实现的呢?

PWM实现

在32中,可以通过通用定时器或者高级定时器来实现PWM,在这里的硬件电路上选择的是高级定时器TIM1(拥有通用寄存器的所有功能)来实现的。

查阅数据手册。

脉冲宽度调制(PWM)模式可以产生一个由TIMx_ARR寄存器确定频率、由TIMx_CCRx寄存器确定占空比的信号。

此功能无需产生中断,只需要控制定时器不断输出一个PWM波形即可。

其中,计数器确定频率,比较器确定占空比。

举例说明:

假如分频后的频率是1MHz,那么单个时钟周期就是1us。

此时,我如何构造一个1KHz的PWM波形?

1KHz对应的周期是1ms,那么,就需要计数1000个1us才可以。

按照占空比为50%,那么就需要在前500个计数周期内高电平(或者低电平),后500个周期内低电平(或者高电平);如此往复循环。

配置MX

配置时钟:

高级定时器的时钟来自于APB2

将PA8配置成TIM1_CH1(不止一个端口可以被配置成TIM1_CH1,比如PE9也可以被配置成TIM1_CH1,配置时要注意下)其他和PWM波无关的暂时不用管。

具体参数设置:

至此,完成MX配置。

比较简单,就是频率的设置,以及占空比的设置。

代码实现

实现功能:通过按键KEY1实现蜂鸣器的开启和关闭,通过基本定时器TIM6改变蜂鸣器的频率。

PWM波相关函数,在文件stm32f1xx_hal_tim.h中:

/** @addtogroup TIM_Exported_Functions_Group3 TIM PWM functions
  *  @brief   TIM PWM functions
  * @{
  */
/* Timer PWM functions ********************************************************/
HAL_StatusTypeDef HAL_TIM_PWM_Init(TIM_HandleTypeDef *htim);
HAL_StatusTypeDef HAL_TIM_PWM_DeInit(TIM_HandleTypeDef *htim);
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim);
void HAL_TIM_PWM_MspDeInit(TIM_HandleTypeDef *htim);
/* Blocking mode: Polling */
HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel);
HAL_StatusTypeDef HAL_TIM_PWM_Stop(TIM_HandleTypeDef *htim, uint32_t Channel);
/* Non-Blocking mode: Interrupt */
HAL_StatusTypeDef HAL_TIM_PWM_Start_IT(TIM_HandleTypeDef *htim, uint32_t Channel);
HAL_StatusTypeDef HAL_TIM_PWM_Stop_IT(TIM_HandleTypeDef *htim, uint32_t Channel);
/* Non-Blocking mode: DMA */
HAL_StatusTypeDef HAL_TIM_PWM_Start_DMA(TIM_HandleTypeDef *htim, uint32_t Channel, uint32_t *pData, uint16_t Length);
HAL_StatusTypeDef HAL_TIM_PWM_Stop_DMA(TIM_HandleTypeDef *htim, uint32_t Channel);

这里,我们重点关注这个函数:

HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel);

创建文件,buzzer.c和buzzer.h

buzzer.h

#ifndef _BUZZER_H_
#define _BUZZER_H_

typedef enum
{
    CLOSE,OPEN
} buzzer_state;

typedef struct
{
    buzzer_state buzzerState;
    
    void (*buzzerOpen)(void);   
    void (*buzzerClose)(void);   
} buzzer_handler;

extern buzzer_state buzzerState;
extern buzzer_handler buzzerHandler;

#endif

buzzer.c

#include "myapplication.h"
    
static void BuzzerOpen(void);
static void BuzzerClose(void);

buzzer_handler buzzerHandler =
{
    CLOSE,
    BuzzerOpen,
    BuzzerClose
};

static void BuzzerOpen(void)
{
    HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);//这里的通道别搞错了,我一开始写成了HAL_TIM_ACTIVE_CHANNEL_1
}

static void BuzzerClose(void)
{
    HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_1);
}

再新增key.c和key.h

key.h

#ifndef _KEY_H_
#define _KEY_H_

typedef struct
{
    void (*key1BuzzerSORP)(void);    
} key_handler;

extern key_handler keyHandler;

#endif

key.c

#include "myapplication.h"
    
static void Key1BuzzerSORP(void);
        
key_handler keyHandler =
{
    Key1BuzzerSORP
};

static void Key1BuzzerSORP(void)
{
    if(buzzerHandler.buzzerState == CLOSE)
    {
        buzzerHandler.buzzerOpen();
        buzzerHandler.buzzerState = OPEN;
    }
    else
    {
        buzzerHandler.buzzerClose();
        buzzerHandler.buzzerState = CLOSE;
    }
}

之后,在按键1的外部中断函数中调用按键1处理函数

//重写外部中断函数
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	switch(GPIO_Pin)
	{
		case KEY0_Pin :
			keyHandler.key1BuzzerSORP();
            led_operater_middle.ledMiddle(LED1, LedExtinguish);
			break;
		case KEY1_Pin :
			printf("second key is running.\n\r");
			led_operater_middle.ledMiddle(LED2, LedExtinguish);
			break;
		case KEY2_Pin :
			printf("third key is running.\n\r");
			led_operater_middle.ledMiddle(LED3, LedExtinguish);
			break;
		case KEY3_Pin :
			printf("forth key is running.\n\r");
			relayObj.relayOpen();
			break;
		default:
			printf("key fault!please click right key.\n\r");	
	}
}

到了这里,就完成了按键一控制蜂鸣器开启或者关闭的功能。

接下来,需要实现改变蜂鸣器频率的功能。

蜂鸣器频率的改变取决于装载值和比较值。

当前的装载值是999,比较值是498,也就是说,周期是1ms,占空比50%。

为了改变这两个值,就需要去改变相应的寄存器的值。

当前设置,一旦开启,每1秒改变一次频率。让频率往更大的方向去变化(越来越尖锐),频率越大,周期越短,装载值也就越小。

那么,就每隔1秒让装载值减少100,直到减少到499,就再循环回去从999开始。

相对应的,比较值都是一半,保证占空比都是50%。

那么,怎么去改变这两个寄存器的值呢?

通过寄存器的结构体指针去访问对应的寄存器。

TIM1 -> ARR来改变装载值;

TIM1 -> CCR1来改变通道1的比较值。

TIM1是个结构体指针的宏定义。

那么,我们就再TIM6的回调函数中实现这个功能。

//重写TIM6中断调用函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if((htim->Instance) == (htim6.Instance) & buzzerHandler.buzzerState == OPEN)
    {          
        if(++circleCount  == TIME_COUNT_1S)
        {
            //改变频率
            TIM1->ARR -= 100;
            if(TIM1->ARR < 100)
            {
                TIM1->ARR = 999;
            }
            TIM1->CCR1 = (TIM1->ARR - 1)/2;
            printf("current arr is %d\n\r", TIM1->ARR);
            circleCount = 0;
        }
    }
}

注意:别忘了开启定时器6。

尝试发出do、re、mi、fa、sol、la、si

以国际标准音A-la-440HZ为准:

do的频率为261.6HZ,相应的计数值为3822

re的频率为293.6HZ,相应的计数值为3406

mi的频率为329.6HZ,相应的计数值为3034

fa的频率为349.2HZ,相应的计数值为2864

sol的频率为392HZ,相应的计数值为2551

la的频率为440HZ,相应的计数值为2272

si的频率为493.8HZ,相应的计数值为2025

//重写TIM6中断调用函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{ 
    if((htim->Instance) == (htim6.Instance) & buzzerHandler.buzzerState == OPEN)
    {        
        uint16_t fluency[] = {3822, 3406, 3034, 2864, 2552, 2272, 2026};   
        static uint8_t fluencyCount = 0;
        
        if(++circleCount  == TIME_COUNT_1S)
        {
            //改变频率
            TIM1->ARR = fluency[fluencyCount];
            if(fluencyCount++ == 6)
            {
                fluencyCount = 0;
            }
            TIM1->CCR1 = TIM1->ARR / 2;
            printf("current arr is %d\n\r", TIM1->ARR);
            circleCount = 0;
        }
    }
}
  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-09-13 11:33:13  更:2022-09-13 11:34:26 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 22:33:15-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码