AT89C51使用示例(Proteus & Keil)
AT89C51使用示例(Proteus & Keil)
AT89C51单片机是嵌入式系统中常用的基础型单片机,其操作简单,功能强大。本文将通过Proteus和Keil软件的配合,详细介绍AT89C51单片机的基础功能,包括最小系统、IO口、计数器和中断等。通过本文的学习,读者可以快速上手理解AT89C51单片机的基础功能。
1. 最小系统
最小系统是单片机能够正常工作的最基本电路,主要包括以下四个部分:
- 时钟电路:XTAL1和XTAL2连接晶振,负责产生稳定的频率,为单片机提供时钟信号。
- 复位电路:RES端口用于高电平复位。
- 电源电路:VCC端口为单片机提供工作电压。
- 程序下载:EA端口当该端口为高电平时,使用片内ROM。
2. IO口
在仿真启动时可观察到P1,P2,P3口均对外输出高电平,而P0口对外不输出电压。
在Proteus中,高电平显示为红色,低电平为蓝色,无电为灰色。
2.1 P0上拉电阻
可以通过添加一个排阻并添加电源来实现P0口的高电平输出。本质上其他端口实现高电平输出也是使用与此相同的方式,即通过将端口与电源和电阻串联来实现。
2.2 端口的编程定义
可以使用以下代码来定义每一个端口的名称:
sbit KEY1 = P2^5;
sbit KEY2 = P2^1;
sbit LED1 = P0^0;
sbit LED2 = P0^7;
2.3 IO示例
以下是一个示例程序,通过P2口的单刀单掷开关来控制P0口的LED灯:
#include "REG51.h"
sbit KEY1 = P2^5;
sbit KEY2 = P2^1;
sbit LED1 = P0^0;
sbit LED2 = P0^7;
void main(void) {
int scanNum1 = 1;
int scanNum2 = 1;
while(1)
{
if(KEY1 == 0 && scanNum1%2 == 1)
{
scanNum1++;
LED1 = ~LED1;
}
if(KEY1 == 1 && scanNum1%2 == 0)
{
scanNum1++;
LED1 = ~LED1;
}
if(KEY2 == 0 && scanNum2%2 == 1)
{
scanNum2++;
LED2 = ~LED2;
}
if(KEY2 == 1 && scanNum2%2 == 0)
{
scanNum2++;
LED2 = ~LED2;
}
}
}
3. 计数器
3.1 定时器与计数器的区别
定时器是对内部机器周期(周期信号)的计数,计数器是对外部输入引脚信号(可以是非周期信号)的计数。
3.2 计数器的初始化步骤
- TMOD 寄存器的设定
- 计数器的计数初值
- 中断系统的管理
- 计数器的启动
3.3 TH0 TL0 的初始化
TH0 和 TL0 分别为计数器计数值的高八位和底八位。因为计数器是对机器内部周期进行计数,单片机经过一个震荡周期会进行12次计数(计数器计数值的最小单位即是一个震荡周期的1/12)。因此计数器计数10000即是1ms。由于单片机中使用二进制来保存数据,TH0 和 TL0 分别只能存储八位二进制数,那么计数器的储值上限是二进制的 1111 1111 1111 1111,转化为十进制为 65535。换句话说,计数器的计数时长为6.5ms,想要获得更长的计数时间可以在代码中定义其他变量来辅助计数。
3.4 计数器的使用示例
通过计数器逐个点亮P2口的LED灯,每个灯的点亮间隔为1s:
#include "REG51.h"
sbit LED1 = P2^0;
sbit LED2 = P2^1;
sbit LED3 = P2^2;
sbit LED4 = P2^3;
sbit LED5 = P2^4;
sbit LED6 = P2^5;
sbit LED7 = P2^6;
sbit LED8 = P2^7;
int count;
void main(void) {
TMOD = 0x01;
TH0 = (65536 - 10000)/256; //定时器初值
TL0 = (65536 - 10000)%256;
EA = 1; //打开中断允许总开关
ET0 = 1; //定时器0中断允许
TR0 = 1; //定时器0启动控制
while(1)
{
}
}
void time0_int(void) interrupt 1 {
TH0 = (65536 - 10000)/256;
TL0 = (65536 - 10000)%256;
count++;
if(count == 100)
{
LED1 = ~LED1;
}
else if(count == 200)
{
LED2 = ~LED2;
}
else if(count == 300)
{
LED3 = ~LED3;
}
else if(count == 400)
{
LED4 = ~LED4;
}
else if(count == 500)
{
LED5 = ~LED5;
}
else if(count == 600)
{
LED6 = ~LED6;
}
else if(count == 700)
{
LED7 = ~LED7;
}
else if(count == 800)
{
LED8 = ~LED8;
}
else if(count == 900)
{
P2 = 0xFF;
count = 0;
}
}
4. 中断
4.1 中断类型
- 外部中断
- 定时器/计数器中断
- 串口中断
区别:即触发中断的条件不一样。外部中断触发条件时管脚出现下降沿(由输出高电平变为输出低电平);定时器/计数器触发条件为定时器/计数器溢出;串口中断的触发条件时串口完成一帧数据的接收或发送。
4.2 中断示例
以下为一个中断示例:
采用外部中断和计数器中断来控制P2口的LED灯:
- 当计数器溢出时,P2口的TIMER灯状态翻转
- 当中断引脚为低电平时,P2口的INTER灯状态翻转
#include "REG51.h"
sbit TIMER = P2^2;
sbit INTER = P2^7;
int count;
//中断初始化
static void InitInterrupt(void)
{
IT0 = 1; //设置外部中断0的触发方式为低电平触发
EX0 = 1; //打开外部中断0的中断允许
EA = 1; //打开中断的总允许
}
static void InitTimer(void)
{
TMOD = 0x01; //设置计数模式
TH0 = (65536 - 10000)/256; //设置计数器计数初值
TL0 = (65536 - 10000)%256; //
EA = 1; //打开中断的总允许
ET0 = 1; //定时器0中断允许
TR0 = 1; //定时器0启动
}
void main(void) {
InitTimer();
InitInterrupt();
while(1) {}
}
void time0_int(void) interrupt 1 {
TH0 = (65536 - 10000)/256;
TL0 = (65536 - 10000)%256;
count++;
if(count%100 == 0) TIMER = ~TIMER;
if(count%200 == 0) {TIMER = ~TIMER; count = 0;}
}
void Externa10_Handler() interrupt 0
{
INTER = ~INTER;
}