51单片机2(按键,外部中断,定时器中断,PWM与蜂鸣器)

发布于:2025-09-04 ⋅ 阅读:(19) ⋅ 点赞:(0)

1.按键模块

        以按键k1为例:两个引脚被接到GND和P1_4引脚,当K1按键被按下时,P1_4引脚会和GND短路到一起,P1_4引脚会呈现低电平。

按键初始化:

//按键初始化
void Key_Init(void)
{
	P1 |= (0x0f << 4);
	P3 |= (1 << 5);
}

检测那个按键按下:

int Key_Press(void)
{
	int ret = 0;
	if((P1 & (1 << 4)) == 0)
	{
		ret = 1;
	}
	else if ((P1 & (1 << 5)) == 0)
	{
		ret = 2;
	}
	else if ((P1 & (1 << 6)) == 0)
	{
		ret = 3;
	}
	else if ((P1 & (1 << 7)) == 0)
	{
		ret = 4;
	}
	else if ((P3 & (1 << 5)) == 0)
	{
		ret = 5;
	}
}

我们可以以此写一个函数:那个按键亮起就让数码管显示那个数字,如k1亮起数码管显示1,如果没有按钮按下则显示0(Diditer_Show函数在上一篇文章有展示)

#include <reg51.h>
#include <key.h>
#include <digiter.h>
#include <delay.h>
int main(void)
{
	int ret = 0;
	Key_Init();
	while(1)
	{
		ret = Key_Press();
		Digiter_Show(ret);
		P0 = 0;
	}
	return 0;
}

2.中断

        中断概念:CPU在执行一个任务时,被外界更为紧急的事件打断,转而去执行更为紧急的任务,执行完后再回到刚才的地方继续向下执行,这一过程叫做中断。

        中断源:打断CPU执行当前任务的事件/源头叫做中断源。

        中断源分类外部中断0、外部中断1、定时器0、定时器1、串口。

        中断优先级:CPU再去处理中断任务时候,会去比较多个中断的优先级,优先去处理优先级高的中断

        默认优先级:

         中断嵌套:处理一个中断时,再嵌套另外的中断;51单片机只允许嵌套2层。

        中断处理流程

        (1)中断源发出中断请求

        (2)检查CPU是否响应中断及该中断源是否被屏蔽

        (3)比较中断优先级

        (4)保护现场

        (5)执行中断服务函数(回调函数)

        (6)恢复现场

2.1外部中断

        单片机上的引脚电平变化所引发的中断(INT0(P3-2)、INT1(P3-3))

寄存器相关配置:

1. IE寄存器(中断允许寄存器)

        (1)将IE寄存器中的bit7,EA置1,代表CPU能够响应所有中断

        (2)将IE寄存器中的bit0,EX0置1,代表允许外部中断0产生中断

2. TCON寄存器(定时器寄存器)
    
        (1)将TCON寄存器的bit1,IE0置1,代表向CPU发起中断请求,CPU响应完中断请求后,硬件清“0”

        (2)将TCON寄存器的bit0,IT0置1,代表外部中断0下降沿触发中断
 

知道相关寄存器内容后,我们来对外部中断0进行配置:

//中断配置
void int0_init(void)
{
	P3 |= (1 << 2);//将外部中断0对应的引脚置1
	IE |= (1 << 7);//CPU能够响应所有中断   总开关
	IE |= (1 << 0);//外部中断0能够发起中断   子开关
	TCON |= (1 << 0);//外部中断0:P3_2引脚中断触发方式->下降沿触发
}

再编写中断处理函数,即发生中断后所执行的函数内容:

//外部中断0的处理函数,每次发生中断g_i++,无需声明
unsigned int g_i = 0;
void Int0_Handler(void)  interrupt 0
{
	g_i++;
}

每发生一次中断,g_i都会进行自加,我们将g_i传参进Digiter_Show函数中,使得中断次数在数码管上显示:

#include <reg51.h>
#include <key.h>
#include <digiter.h>
#include <delay.h>
int main(void)
{
	int ret = 0;
	Key_Init();
	int0_init();
	while(1)
	{
		Digiter_Show(g_i);	
	}
	return 0;
}

        用杜邦线短接P3.2和GND,每次短接,都会产生下降沿触发外部中断0,数码管也会显示中断次数。

2.2定时器中断

定时器:能够产生一个精准的定时,不同外设对时序的要求高(高电平和低电平时间是精准的)

        51单片机内部有两个定时器,分别为timer0、timer1,所使用自增型定时器(计数器 16位),因此其自增范围为0~2^16=65535。

         晶振,晶体振荡器(12MHZ / 11.0592MHZ)

         由于51单片机达不到12MHZ,将12MHZ进行12分频,12MHZ/12 = 1MHZ

         51单片机完成一条指令运算:1/1MHZ = 1us。

因此我们要想定时1ms,需要51单片机完成1000次计时,由于其为自增型,因此我们需要将初始值设为65535-1000=64535。在执行1000次运算后定时器溢出,也就是1ms后中断。


定时器相关寄存器配置

TCON寄存器:

            (1)bit4置1,TMOD寄存器中的Gate位清0, 代表允许定时器开始计数,

TMOD寄存器:

            (1)定时器0->低四位清0

            (2)将TMOD寄存器中的M0,bit0置1,代表定时器0工作在16位定时器/计数器模式

IE寄存器:

            (1)将IE寄存器中的bit7置1,代表CPU能够响应所有中断
            
            (2)将IE寄存器中的bit1置1,代表允许定时器0产生中断

配置流程:

        1. 先配置TMOD模式选择寄存器,将低四位清0,再将bit0置1代表工作在16位定时器

        2. 向TH0和TL0中装入定时器的初值(1ms -> 64535)
    
        3.  将TCON寄存器中的bit6置1,代表允许定时器开始计数

        4.  将IE寄存器中的bit7和bit1置1,开启中断总开关和定时器0的子开关

        5. 编写定时器0的中断服务函数
我们对定时器进行配置:

// 定时器0初始化函数
void Timer0_Init(void)
{
	TMOD &= ~(0x0F << 0);    // TMOD寄存器低四位清0
	TMOD |= (1 << 0);		 // 定时器工作在16位定时器模式

	TH0 = 64535 >> 8;
	TL0 = 64535;

	TCON |= (1 << 4);    // 允许定时器0开始计数

	IE |= (1 << 7) | (1 << 1);   // 允许CPU响应中断及定时器0产生中断
}

中断服务函数:每过1s对所有LED灯进行一次反转。

void Timer0_handler(void)  interrupt 1
{
 	TH0 = 64535 >> 8;
	TL0 = 64535;
	g_i++;
	if (g_i >= 1000)
	{
		LED_Nor();
		g_i = 0;
	}
}

写入主函数:

#include <reg51.h>
#include "timer.h"
#include "led.h"
int main(void)
{
	Timer0_Init();

	while (1)
	{
	
	}

	return 0;
}

3.蜂鸣器模块

                        

蜂鸣器

            震荡源  -> 声音(波)-> 音调不同 -> 波的频率发生变化 -> 高音 高频  低音 低频   音量不同 -> 波的振幅 -> 能量

            有源蜂鸣器:存在震荡源,通电后蜂鸣器会发出持续频率的声音。

            无源蜂鸣器:不存在震荡源,通电后蜂鸣器不会发出声音,需要给蜂鸣器一个震荡。

PWM:脉冲宽度调制,能够让引脚产生一个方波,周期性的让引脚的电平发生翻转。

        PWM周期:一个方波所经历的周期。(从上升沿到上升沿所经历的时间/从下降沿到下降沿所经历的时间)

        PWM占空比:在一个周期内高电平所占的比例。

我们分别调制200,400,600,800,1000hz的蜂鸣器发声频率

以200为例

                PWM蜂鸣器按照频率200HZ,占空比50%工作
                频率:200HZ
                时间:17公0==0.005s
                定时:0.005s/2-0.0025s
                定时器初值:
                品振:12MHZ*10^6=12,000,000HZ分频:12,000,000HZ/12=1000000HZ
                时间:1/1000000HZ=0.000001s
                定时器初值:
                0.0025/0.000001=2500
                65535-2500=63035
                TH0=63035 >> 8;TL0=63035;

调制好的频率对应的计数器初始值:

#ifndef TIMER_H__
#define TIMER_H__
extern void Timer0_Init(void);
extern unsigned short g_i1;
#define HZ200  63035
#define HZ400  64285
#define HZ600  64703
#define HZ800  64910
#define HZ1000 65035
#endif

定时器初始化以及中断服务:

#include <reg51.h>
#include "led.h"
unsigned short g_i1 = 0;
// 定时器0中断服务函数
void Timer0_handler(void)  interrupt 1
{
 	TH0 = g_i1 >> 8;
	TL0 = g_i1;
	
	P2 ^= (1 << 1); //蜂鸣器反转信号        
}

// 定时器0初始化函数
void Timer0_Init(void)
{
	TMOD &= ~(0x0F << 0);    // TMOD寄存器低四位清0
	TMOD |= (1 << 0);		 // 定时器工作在16位定时器模式

	TH0 = g_i1 >> 8;
	TL0 = g_i1;

	TCON |= (1 << 4);    // 允许定时器0开始计数

	IE |= (1 << 7) | (1 << 1);   // 允许CPU响应中断及定时器0产生中断
}

主函数调用:当按键按下后蜂鸣器会发出对应声音:

#include <reg51.h>
#include "timer.h"
#include "led.h"
#include "key.h"
int main(void)
{
	int set = 0;
	Key_Init();
	Timer0_Init();
	
	while (1)
	{
		set = Key_Press();
		if (set ==0)
		{
			TCON = 0;
		}
		else if(set == 1)
		{
			TCON |= (1 << 4);
			g_i1 = HZ200;
		}
		else if(set == 2)
		{
			TCON |= (1 << 4);
			g_i1 = HZ400;
		}
		else if(set == 3)
		{
			TCON |= (1 << 4);
			g_i1 = HZ600;
		}
		else if(set == 4)
		{
			TCON |= (1 << 4);
			g_i1 = HZ800;
		}
		else if(set == 5)
		{
			TCON |= (1 << 4);
			g_i1 = HZ1000;
		}
		
	}

	return 0;
}


网站公告

今日签到

点亮在社区的每一天
去签到