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;
}