这个实验本来应该是我们学习AVR单片机的第一个实例程序,但是由于一时疏忽,忘记了,现在补上。
一、电路实现
如下是驱动蜂鸣器发声的电路原理图
1、蜂鸣器是一种电子电路中常用的发声器件,蜂鸣器分为有源蜂鸣器和无源蜂鸣器两种。从外观上看这两种蜂鸣器区别不大,但是在细节上还是稍有差别的,首先一般的有源蜂鸣器在其正面贴有一片胶纸,而无源蜂鸣器则没有。再观察有引脚的那一面,有源蜂鸣器的两个引脚一高一低,而无源蜂鸣器的两个引脚的高度一样。另外有源蜂鸣器除了引脚露出外,其余部分都是封闭的,而无源蜂鸣器的引脚面可以看到有绿色的电路板。
使用电源来判别蜂鸣器的有源和无源:找个5V左右的稳压电源,将电源正极连接到蜂鸣器标有“+”符号的引脚上,用电源的负极以一定频率不断地碰触蜂鸣器的负极引脚,发出“咔咔”声的是无源蜂鸣器,发出“嘀嘀”声的是有源蜂鸣器。
使用数字表判别蜂鸣器的有源和无源:用数字表的1K以下的欧姆档测量蜂鸣器两脚间的电阻,如果电阻只有几十欧,并且颠倒数字表的表笔测量出来的电阻一样,那么可以判断为无源蜂鸣器,如果两脚间电阻大于1K,那么可以判断是有源蜂鸣器。
有源蜂鸣器直接接上额定电源就可连续发声;而无源蜂鸣器则和电磁扬声器一样,需要接在音频输出电路中才能发声。
有源蜂鸣器和无源蜂鸣器的区别就是有源蜂鸣器直接加上额定电压就能一直发声(当然要同时满足电流要求),而无源蜂鸣器需要加上脉冲电压才能持续发声。在电子设计中,常用有源蜂鸣器进行各种各样的报警提示。而用无源蜂鸣器来产生各式各样的音频信号(比如用来演奏简单的乐曲)。
2、蜂鸣器的驱动电路
上面的图中给出了本实例中使用的蜂鸣器电路,电路中使用单片机的PE5口控制三极管8550的通断来使蜂鸣器发声。为什么要使用三极管?三极管在这里的作用有二:开关作用,控制蜂鸣器电源电路的通断;提供蜂鸣器发声所需的较高电流, 单片机的I/O口驱动能力有限,而我们知道三极管有电流放大的作用,在这里就是利用三极管放大电流来使蜂鸣器获得足够的驱动电流。
下图是一种比较安全的蜂鸣器驱动电路
图中在蜂鸣器两端并联了一个二极管,这个二极管称为续流二极管,蜂鸣器本质上是一个感性元件,其电流不能瞬变,因此必须有一个续流二极管提供续流。否则,在蜂鸣器两端会产生几十伏的尖峰电压,可能损坏驱动三极管,并干扰整个电路系统的其它部分。
滤波电容C1的作用是滤波,滤除蜂鸣器电流对其它部分的影响,也可改善电源的交流阻抗,如果可能,最好是再并联一个220uF的电解电容。
二、程序实现
完整程序如下所示,本程序实现单片机系统上电后蜂鸣器发出约500ms的声音。
#include <avr/io.h> //io端口寄存器配置文件,必须包含
#include <util/delay.h> //GCC中的延时函数头文件
//函数声明
void Delayus(unsigned int lus); //us延时函数
void Delayms(unsigned int lms); //ms延时函数
int main(void) //GCC中main文件必须为返回整形值的函数,没有参数
{
unsigned char i;
PORTE |= (1 << PE5); //
DDRE |= (1 << PE5); //
Delayms(500);
PORTE &= ~(1 << PE5); //
Delayms(500);
PORTE |= (1 << PE5); //
while(1)
{
}
}
//us级别的延时函数
void Delayus(unsigned int lus)
{
while(lus--)
{
_delay_loop_2(4); //_delay_loop_2(1)是延时4个时钟周期,参数为4则延时16
//个时钟周期,本实验用16M晶体,则16个时钟周期为16/16=1us
}
}
//ms级别的延时函数
void Delayms(unsigned int lms)
{
while(lms--)
{
Delayus(1000); //延时1ms
}
}
三、程序讲解
在本实例中我们自己定义了两个延时函数:微秒级的延时函数、毫秒级的延时函数
其中在微秒级的延时函数中我们使用了这样一条语句_delay_loop_2(3); 这条语句是GCC编译环境下delay.h头文件中定义的一个延时函数,_delay_loop_2(1)实现4个时钟周期的延时,为什么我们要自己编写延时函数,而不使用delay.h中定义好的延时函数?这是因为delay.h中定义的延时函数是按照内部1MHz时钟频率编写的,而我们实际使用的始终是外部16MHz,所以如果仍旧使用这些延时函数,会造成延时的不精确。
在程序中我们还是用了如下的语句:PORTE |= (1 << PE5); PORTE &= ~(1 << PE5); 它们是什么样的语句?实现什么样的功能?
这些语句实现给端口寄存器的某些固定位赋值,其中使用了移位,相或,取反等操作。
在单片机的C语言程序中我们经常会碰到给一些寄存器的特定位赋值的操作,这是单片机C语言和标准C语言的一个明显差异。在下一讲中我们将详细讨论单片机中位操作的实现。 |