大家好,欢迎回到我们的 DSP28335 探索之旅!在上一篇教程中,我们成功点亮了 LED,完成了与这个强大芯片的第一次“视觉”交流。这一次,我们将更进一步,进行一次“听觉”互动——学习如何驱动蜂鸣器,让我们的开发板发出声音。
本篇将首先分析我们开发板上无源蜂鸣器的硬件驱动电路,然后通过配置 GPIO 并使用软件延时产生方波脉冲来驱动它。这不仅是一个有趣的实验,更是理解 DSP 信号生成和未来学习 PWM (脉冲宽度调制) 控制的绝佳前奏。
硬件分析:声音是如何产生的?
在动手编码之前,我们必须先弄清楚硬件是如何连接的,以及它需要什么样的信号才能工作。
1. 确认硬件:开发板使用的是无源蜂鸣器
在开始前,最重要的一点是明确我们面对的硬件类型。我们开发板上所使用的,是一个无源蜂鸣器 (Passive Buzzer)。
什么是无源蜂鸣器呢?你可以把它想象成一个微型的小喇叭。它内部没有振荡电路,你必须给它提供一定频率的脉冲信号(方波),它才会根据这个频率发出对应音调的声音。频率高,音调就高;频率低,音调就低。
我们之所以分析这种蜂鸣器,正是因为它给了我们用软件自由控制音调和节奏的可能,更能体现 DSP 的强大控制能力。
作为对比,我们简单提一下有源蜂鸣器 (Active Buzzer)。有源蜂鸣器内部自带了振荡源,这是另一种非常经典的开关电路。当 BUZ 信号为高电平时,电流通过 R06 流入三极管的基极(B),三极管导通,集电极(C)和发射极(E)之间近似短路,将蜂-鸣器 BZ1 的负极拉到地,蜂鸣器发声。BUZ 为低电平时,三极管截止,蜂鸣器不响。请记住,我们后续所有的实验对象,都是板载的这个无源蜂鸣器。
2. 驱动电路分析
由于 DSP 的 IO 口驱动电流有限(通常是几毫安),不足以直接驱动需要较大电流的蜂鸣器。因此,我们需要一个驱动电路来放大电流。
为了解决这些问题,DSP 提供了一个强大的外设——ePWM (增强型脉宽调制模块)。它可以在硬件层面产生精确、稳定、可调的方波,而完全不占用 CPU 时间。
在下一篇教程中,我们将把 GpioCtrlRegs.GPAMUX1.bit.GPIO6 的配置从 0 改为 1,把控制权从 GPIO 交给 ePWM 模块,实现对无源蜂鸣器音调的更高级控制。
总结
今天,我们通过分析硬件电路,将软件与硬件紧密结合,使用最基本的 GPIO 功能和软件延时,成功驱动了无源蜂鸣器。你已经掌握了:
请动手尝试修改 DELAY_US() 中的数值,比如改成 DELAY_US(500) 或 DELAY_US(1000),听听看音调会有什么变化?这个简单的实验将加深你对频率和音高关系的理解。
期待在下一篇 PWM 教程中与你再次相遇!
-
核心器件: ULN2003D 是一款达林顿晶体管阵列芯片,可以看作一个包含了多个“开关”的集合,专门用来驱动继电器、电机、LED灯带等稍大功率的负载。
-
信号流向: DSP 的 EPWM4A 引脚发出的控制信号,进入 ULN2003D 的 IN7 输入端。芯片的 OUT7 输出端连接到无源蜂鸣器 BZ1 的一端,蜂鸣器的另一端接 D5V (5V电源)。
-
工作原理: ULN2003D 是一个反相器。当 IN7 为高电平(比如3.3V)时,内部开关导通,OUT7 被拉到接近地(GND)的低电平。此时电流从 D5V -> 蜂鸣器 -> OUT7 -> GND,形成回路,蜂鸣器两端产生压差。反之,IN7 为低电平时,OUT7 悬空,回路断开。
-
控制逻辑: 要让无源蜂鸣器发声,我们需要让 IN7 高低电平快速切换,从而让蜂鸣器两端的压差不断通、断,使其振动发声。
-
3. DSP 引脚映射
-
从图中可以确定,原理图上的 EPWM4A 信号,在 DSP 上复用的是 GPIO6 引脚。它属于 GPIOA 端口组。
软件设计:谱写发声的代码
现在,我们可以开始编写软件了。今天的目标是通过快速地切换 GPIO6 的高低电平,来产生一个固定频率的方波。
1. 头文件 (beep.h)
定义蜂鸣器控制的宏,并声明初始化函数。
-
#ifndef BEEP_H_ #define BEEP_H_ #include "DSP2833x_Device.h" // DSP2833x 头文件 #include "DSP2833x_Examples.h" // DSP2833x 例子相关头文件 // 宏定义只是为了方便操作IO口,BEEP_ON不代表持续响,而是输出高电平 #define BEEP_ON (GpioDataRegs.GPASET.bit.GPIO6=1) #define BEEP_OFF (GpioDataRegs.GPACLEAR.bit.GPIO6=1) #define BEEP_TOGGLE (GpioDataRegs.GPATOGGLE.bit.GPIO6=1) void BEEP_Init(void); // 蜂鸣器初始化函数声明 #endif /* BEEP_H_ */
2. 初始化函数 (beep.c)
这个函数负责将 GPIO6 配置为通用推挽输出模式。
-
#include "beep.h" /******************************************************************************* * 函 数 名 : BEEP_Init * 函数功能 : 蜂鸣器初始化 * 输 入 : 无 * 输 出 : 无*******************************************************************************/ void BEEP_Init(void) { EALLOW; // 关闭写保护 SysCtrlRegs.PCLKCR3.bit.GPIOINENCLK = 1; // 开启GPIO时钟,必备! // 蜂鸣器引脚 (GPIO6) 配置 // 将引脚功能选为通用 GPIO 功能 (EPWM4A 是复用功能1) GpioCtrlRegs.GPAMUX1.bit.GPIO6 = 0; // 设置方向为输出 GpioCtrlRegs.GPADIR.bit.GPIO6 = 1; // 使能内部上拉 (好习惯) GpioCtrlRegs.GPAPUD.bit.GPIO6 = 0; EDIS; // 开启写保护 // 初始化时,默认关闭蜂鸣器 (输出低电平) GpioDataRegs.GPACLEAR.bit.GPIO6 = 1; }
-
GpioCtrlRegs.GPAMUX1.bit.GPIO6 = 0; 这一步至关重要。它明确告诉 DSP,我们现在要用 GPIO6 作为普通的 IO 口,而不是用它的复用功能 EPWM4A。在我们后续升级到 PWM 控制时,就需要修改这个值。
-
3. 主函数 (main.c)
在主循环中,我们不断地翻转 GPIO6 的电平,以产生方波。
-
#include "DSP2833x_Device.h" // DSP2833x Headerfile Include File #include "DSP2833x_Examples.h" // DSP2833x Examples Include File #include "leds.h" // 假设你保留了之前的 LED 代码 #include "beep.h" void main() { int i=0; InitSysCtrl(); // 初始化系统控制 DINT; // 关总中断 // 初始化外设 InitPieCtrl(); IER = 0x0000; IFR = 0x0000; InitPieVectTable(); LED_Init(); // 初始化 LED BEEP_Init(); // 初始化蜂鸣器 while(1) { i++; BEEP_TOGGLE; // 翻转蜂鸣器IO口电平 DELAY_US(100); // 延时 100 微秒 // 让 LED 闪烁得慢一些,以证明主程序在运行 if(i % 1000 == 0) { LED1_TOGGLE; } } }
在这段代码里,BEEP_TOGGLE; DELAY_US(100); 是核心。BEEP_TOGGLE 使 GPIO6 电平翻转一次(高变低或低变高),DELAY_US(100) 保持这个状态 100 微秒。因此,一个完整的高低电平周期是 100us (高) + 100us (低) = 200us。
这个方波的频率就是:f = 1 / T = 1 / 200us = 1 / 0.0002s = 5000 Hz = 5 kHz。
这个 5kHz 的信号驱动板载的无源蜂鸣器,我们就能听到清脆的“嘀”声了!
展望:更优雅的 PWM 控制
虽然我们成功地让蜂鸣器发声了,但使用 DELAY_US 的方法有几个缺点:
-
阻塞式 (Blocking): DELAY_US 是通过执行空循环来消耗时间的,在延时期间,CPU 不能做任何其他事情,浪费了宝贵的计算资源。
-
不精确: 如果系统中有中断发生,延时的时间会变得不准确,导致音调不稳定。
-
不灵活: 改变音调需要修改代码中的延时值,并且难以产生复杂的音乐旋律。
-
无源蜂鸣器驱动电路的基本原理。
-
如何将 GPIO 引脚配置为通用输出。
-
利用软件循环产生脉冲信号来制造声音。