51单片机
第一章:C51单片机最小系统
1.1 单片机内部硬件结构
AT89C51
1.2 硬件参数
AT89C51 | AT89C52 |
---|---|
8位微处理器(CPU:运算器,控制器) | 8位微处理器(CPU:运算器,控制器) |
数据存储器(128B RAM) | 数据存储器(512B RAM) |
程序存储器(4KB Flash ROM) | 程序存储器(8KB Flash ROM) |
4个8位可编程并行I/O口(P0、P1、P2和P3) | 4个8位可编程并行I/O口(P0、P1、P2和P3) |
1个全双工异步串行口 | 1个全双工异步串行口 |
2个可编程的16位定时器/计数器(T0、T1) | 3个可编程的16位定时器/计数器(T0、T1、T2) |
中断系统具有5个中断源、5个中断向量 | 中断系统具有8个中断源、8个中断向量 |
直插型芯片实物图:
贴片型芯片实物图:
常见的其他型号:
不同型号的51单片机有不同的资源,后面涉及的资源全部以经典型号AT89C51为准
1.3 单片机各引脚引脚功能
P0口:P0.0-P0.7引脚
为漏极开路的8位并行I/O口。作为输出口时,每个引脚可驱动8个LS型TTL负载。AT89C52扩展外部存储器及I/O脚芯片时,P0口为分时复用的低8位地址/数据总线。在向P0口写入"1"后就成为高阻态输入口。
当P0为通用I/O口使用时,需外加上拉电阻,这时为准双向口
P1口:P1.0-P1.7引脚
为准双向I/O口,引脚内部有上拉电阻。
P2口:P1.0-P1.7引脚
为准双向I/O口,引脚内部有上拉电阻。
当单片访问外部存储器时,P2作为高8位地址总线使用,输出高8位地址。
P3口:P1.0-P1.7引脚
P3口通用I/O口,内部有上拉电阻。它还具有第二种功能
引脚 | 第二功能 |
---|---|
P3.0 | PXD (串行口输入) |
P3.1 | RTXD (串行口输出) |
P3.2 | INT0 (外部中断0输入) |
P3.3 | INT1 (外部中断1输入) |
P3.4 | T0 (定时器/记数器0的外部输入) |
P3.5 | T1 (定时器/记数器1的外部输入) |
P3.6 | WR (片外数据存储器 “写选通控制” 输出) |
P3.7 | RD (片外数据存储器 “读选通控制” 输出) |
1.4 C51单片机最小系统
电源电路: | 供电:VCC为+5V电源,VSS接地 |
---|---|
时钟电路: | XTAL1、XTAL2:接晶振(常用频率:12MHZ、11.0592)和30pf的电容。 |
复位电路: | RST:复位信号输入,在引脚加上持续时间大于2个机器周期的高电平,可使单片机复位。正常工作时,此引脚为低电平。 |
选择ROM: | EA:片内片外程序存储器选择。当EA=1;选择片内程序存储器。EA=0;选择片外程序存储器。 |
1.5 C51单片机的时钟
时钟周期 | 时钟控制信号的基本时间单位。若单片使用的晶振频率为Fosc,则时钟周期Tosc=1/Fosc。如fosc=12MH,Tosc=1/12us |
---|---|
状态周期 | 每两个时钟周期为一个状态周期。即S=Tosc*2 |
机器周期 | CPU完成一个基本操作所需要时间为机器周期。每12个时钟周期为一个机器周期。晶振为12MHZ,则机器周期为 T=12 * 1/fosc =1us |
指令周期 | 执行条指令所需的时间。简单的字节指令,只需一个指令周期,而有些复杂的指令(如乘、除指令)需要两个或多个指令周期。 |
第二章:IO口控制应用
2.1 IO口控制
单片机IO口的状态总是0和1(高电平和低电平)
2.1.1 控制1位IO口
方法1:(推荐)
sbit 引脚名=端口寄存器^位置
/*引脚定义*/ sbit LED=P1^4 //定义P1并行口第5位引脚0名字为LED引脚
/*输出高电平*/ LED=0; //为此引脚置位为0
/*输出低电平*/ LED=1; //为此引脚置位为1
方法2
P1 |=(1<<5) //为P1并行口第5为引脚置位为1
P1 &=~(1<<5) //为P1并行口第5为引脚置位为0
2.1.2 引脚输入检测
if(LED==1) {执行代码} //检测引脚是否输入高电平
if(LED==0) {执行代码} //检测引脚是否输入高电平
2.1.3 延时函数
void delay_ms(unsigned int xms)
{
unsigned char i,j;
for(i=0;i<xms;i++)
for(j=0;j<110;j++);
}
2.2 简易流水灯
各有千秋。
方法1 一位一位去控制
#include <reg51.h>
/***** 端口宏定义 *****/
/***** 引脚定义 *****/
sbit LED0=P1^0;
sbit LED1=P1^1;
sbit LED2=P1^2;
sbit LED3=P1^3;
sbit LED4=P1^4;
sbit LED5=P1^5;
sbit LED6=P1^6;
sbit LED7=P1^7;
/***** 函数声明 *****/
void delay_ms(unsigned int xms);
/***** 变量定义 *****/
/***** 主函数 *****/
void main()
{
while(1)
{
LED0=0;
delay_ms(200);
LED0=1;
delay_ms(200);
LED1=0;
delay_ms(200);
LED1=1;
delay_ms(200);
LED2=0;
delay_ms(200);
LED2=1;
delay_ms(200);
LED3=0;
delay_ms(200);
LED3=1;
delay_ms(200);
LED4=0;
delay_ms(200);
LED4=1;
delay_ms(200);
LED5=0;
delay_ms(200);
LED5=1;
delay_ms(200);
LED6=0;
delay_ms(200);
LED6=1;
delay_ms(200);
LED7=0;
delay_ms(200);
LED7=1;
delay_ms(200);
}
}
void delay_ms(unsigned int xms)
{
unsigned char i,j;
for(i=0;i<xms;i++)
for(j=0;j<110;j++);
}
方法2 数组的方式去控制
#include <reg51.h>
/***** 端口宏定义 *****/
#define LED P1
/***** 函数声明 *****/
void delay_ms(unsigned int xms);
/***** 变量定义 *****/
char ledDat[8] ={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};
unsigned char i = 0;
/***** 主函数 *****/
void main()
{
while(1)
{
for(i=0;i<8;i++)
{
LED=ledDat[i];
delay_ms(200);
LED=0xff;
delay_ms(200);
}
}
}
void delay_ms(unsigned int xms)
{
unsigned char i,j;
for(i=0;i<xms;i++)
for(j=0;j<110;j++);
}
方法3 位运算的方式去控制
#include <reg51.h>
/***** 端口宏定义 *****/
#define LED P1
/***** 函数声明 *****/
void delay_ms(unsigned int xms);
/***** 变量定义 *****/
unsigned char i = 0;
/***** 主函数 *****/
void main()
{
while(1)
{
unsigned char t = 1;
for(i=0;i<8;i++)
{
LED=~(t);
delay_ms(200);
LED=0xff;
delay_ms(200);
t=t<<1;
}
}
}
void delay_ms(unsigned int xms)
{
unsigned char i,j;
for(i=0;i<xms;i++)
for(j=0;j<110;j++);
}
2.3 独立按键控制LED灯
按键按下后、led亮,按键再次按下,led灭。
#include <reg51.h>
/***** 端口宏定义 *****/
/***** 引脚定义 *****/
sbit KEY1=P2^0; //按键在P2.0引脚
sbit LED1=P1^0; //LED在P1.0引脚
/***** 函数声明 *****/
void delay_ms(unsigned int xms);
/***** 变量定义 *****/
unsigned char ledFlag = 0;
/***** 主函数 *****/
void main()
{
while(1)
{
if(KEY1 == 0)
{
delay_ms(10); // 软件消抖
if(KEY1 == 0)
{
while(KEY1==0); // 等待按键释放
ledFlag = !ledFlag;
}
}
if(ledFlag)//ledFlag=1,led灯亮
{
LED1 = 0;
}
else
{
LED1=1;
}
}
}
void delay_ms(unsigned int xms)
{
unsigned char i,j;
for(i=0;i<xms;i++)
for(j=0;j<110;j++);
}
2.4:数码管
2.4.1 单位数码管
八位共阳极数码管:逐渐显示0-f
#include <reg51.h>
/***** 端口宏定义 *****/
#define SEGCA P1
/***** 引脚定义 *****/
/***** 函数声明 *****/
void delay_ms(unsigned int xms);
/***** 变量定义 *****/
/*0,1,2,3,4,5,6,7,8,9,A,b,C,d,E,F*/
unsigned char segcaDat[16] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E};
unsigned char i=0;
/***** 主函数 *****/
void main()
{
while(1)
{
for(i=0;i<16;i++)
{
SEGCA=segcaDat[i];
delay_ms(200);
SEGCA=0xff;
delay_ms(200);
}
}
}
void delay_ms(unsigned int xms)
{
unsigned char i,j;
for(i=0;i<xms;i++)
for(j=0;j<110;j++);
}
2.4.2多位数码管动态显示
四位共阳极PNP驱动数码管
头文件
#ifndef __SEG_H__
#define __SEG_H__
#include <reg51.h>
#define Duan_PORT P1 /*段码线端口宏定义*/
/***** 引脚的定义 *****/
sbit WEI1=P2^0; /*控制第一位数码管的引脚*/
sbit WEI2=P2^1; /*控制第二位数码管的引脚*/
sbit WEI3=P2^2; /*控制第三位数码管的引脚*/
sbit WEI4=P2^3; /*控制第四位数码管的引脚*/
/*显示四位整数 显示数据 延时时间*/
void display_four(int displayNum, unsigned char delay_ms);
/*显示四位小数 显示数据 小数点位置 延时时间*/
void display_float(int displayNum, unsigned char float_position,unsigned char delay_ms);
/*独位显示整数 显示数据 显示位置 延时时间*/
void display_data(int displayNum, unsigned char display_position,unsigned char delay_ms);
#endif
c文件
#include "seg.h"
#include "delay.h"
/***** 变量定义 *****/
unsigned char seg_dat[] = {0xC0,0xF9,0xA4,0xB0,0x99,
/*0,1,2,3,4,5,6,7,8,9,*/ 0x92,0x82,0xF8,0x80,0x90};
void display_four(int displayNum,unsigned char delay_ms) /* 数码管显示四位整数 */
{
WEI1=0;
Duan_PORT=seg_dat[displayNum/1000%10];
delay_xms(delay_ms);
WEI1=1;
Duan_PORT=0xff;
WEI2=0;
Duan_PORT=seg_dat[displayNum/100%10];
delay_xms(delay_ms);
WEI2=1;
Duan_PORT=0xff;
WEI3=0;
Duan_PORT=seg_dat[displayNum/10%10];
delay_xms(delay_ms);
WEI3=1;
Duan_PORT=0xff;
WEI4=0;
Duan_PORT=seg_dat[displayNum%10];
delay_xms(delay_ms);
WEI4=1;
Duan_PORT=0xff;
}
void display_float(int displayNum, unsigned char float_position,unsigned char delay_ms)
{
WEI1=0;
if(float_position == 1) {Duan_PORT=seg_dat[displayNum/1000]&0x7f;}
else {Duan_PORT=seg_dat[displayNum/1000];}
delay_xms(delay_ms);
WEI1=1;
Duan_PORT=0xff;
WEI2=0;
if(float_position == 2) {Duan_PORT=seg_dat[displayNum/100%10]&0x7f;}
else {Duan_PORT=seg_dat[displayNum/100%10];}
delay_xms(delay_ms);
WEI2=1;
Duan_PORT=0xff;
WEI3=0;
if(float_position == 3) {Duan_PORT=seg_dat[displayNum/10%10]&0x7f;}
else {Duan_PORT=seg_dat[displayNum/10%10]; }
delay_xms(delay_ms);
WEI3=1;
Duan_PORT=0xff;
WEI4=0;
Duan_PORT=seg_dat[displayNum%10];
delay_xms(delay_ms);
WEI4=1;
Duan_PORT=0xff;
}
void display_data(int displayNum, unsigned char display_position,unsigned char delay_ms)
{
if(display_position == 1)
{
WEI1=0;
Duan_PORT=seg_dat[displayNum];
delay_xms(delay_ms);
WEI1=1;
} else if(display_position == 2) {
WEI2=0;
Duan_PORT=seg_dat[displayNum];
delay_xms(delay_ms);
WEI2=1;
}else if(display_position == 3) {
WEI3=0;
Duan_PORT=seg_dat[displayNum];
delay_xms(delay_ms);
WEI3=1;
} else if(display_position == 4) {
WEI4=0;
Duan_PORT=seg_dat[displayNum];
delay_xms(delay_ms);
WEI4=1;
}
Duan_PORT=0xff;
}
2.5:LCD1602液晶屏
h文件
#ifndef __LCD1602_H__
#define __LCD1602_H__
#include <reg51.h>
/***** 端口宏定义 *****/
#define LCD_PORT P0
/***** 引脚定义 *****/
sbit RS=P1^0;
sbit RW=P1^1;
sbit E=P1^2;
/***** 函数声明 *****/
/********** 底层驱动函数 **********/
void init_1602(); /* 初始化函数 */
void write_cmd(unsigned char dat); /* 写命令 */
void read_cmd(unsigned char dat); /* 读命令 */
void write_dat(unsigned char dat); /* 写数据 */
void read_dat(unsigned char dat); /* 读命令 */
/********** 基本应用函数 **********/
// 显示单个字符 显示数据 行位置(0-1) 列位置(0-15)
void display_char(unsigned char dat, unsigned char row, unsigned char column);
// 显示字符串 显示数据(长度:1-16) 行位置(0-1) 列位置(0-15)
void display_string(unsigned char *p_str, unsigned char row, unsigned char column);
// 显示两位整数 显示数据(0-99) 行位置(0-1) 列位置(0-15)
void display_2Digit(unsigned int digit, unsigned char row,unsigned char column);
/********** 用户函数 **********/
// 双行滚动显示字符串 显示数据 行位置(0-1) 列位置(0-1) 间隔时间(ms)
void roll_string(unsigned char *p_str, unsigned char row, unsigned char column, unsigned int spt_ms);
#endif
c文件
#include "lcd1602.h"
void lcd_delay_ms(unsigned int xms)
{
unsigned int i,j;
for(i=0;i<xms;i++)
{
for(j=0;j<110;j++);
}
}
void init_1602()
{
write_cmd(0x38); /* 16*2显示,5*7点阵,8位数据接口 */
write_cmd(0x08); /* 显示关闭*/
write_cmd(0x01); /* 清屏 */
write_cmd(0x06); /* 文字不动,地址自动+1 */
write_cmd(0x0c); /* 显示器开,光标关闭 */
//write_cmd(0x0f); /* 显示器开,光标显示 闪烁 */
}
void write_cmd(unsigned char dat)
{
RS=0;
RW=0;
LCD_PORT = dat;
E=0;
lcd_delay_ms(1);//延时
E=1;
lcd_delay_ms(1);//延时
E=0;
}
void read_cmd(unsigned char dat)
{
RS=0;
RW=1;
LCD_PORT=dat;
E=0;
lcd_delay_ms(1);//延时
E=1;
lcd_delay_ms(1);//延时
E=0;
}
void write_dat(unsigned char dat)
{
RS=1;
RW=0;
LCD_PORT = dat;
E=0;
lcd_delay_ms(1);//延时
E=1;
lcd_delay_ms(1);//延时
E=0;
}
void read_dat(unsigned char dat)
{
RS=1;
RW=1;
LCD_PORT=dat;
E=0;
lcd_delay_ms(1);//延时
E=1;
lcd_delay_ms(1);//延时
E=0;
}
void display_char(unsigned char dat, unsigned char row, unsigned char column)
{
if(row ==0)write_cmd(0x80+column);
else if(row ==1)write_cmd(0xc0+column);
write_dat(dat);
}
void display_string(unsigned char *p_str, unsigned char row, unsigned char column)
{
unsigned char temp_cmd = 0x00;
if(row ==0) temp_cmd = 0x80;
else if(row ==1) temp_cmd =0xc0;
while(*p_str!='\0')
{
write_cmd(temp_cmd+(column++));
write_dat((*p_str++));
}
}
void roll_string(unsigned char *p_str, unsigned char row, unsigned char column, unsigned int spt_ms)
{
unsigned char temp_cmd = 0x00;
//write_cmd(0x0f); //显示器开,光标显示 闪烁
if(row ==0) temp_cmd = 0x80;
else if(row ==1) temp_cmd =0xc0;
while(*p_str!='\0')
{
write_cmd(temp_cmd+(column++));
write_dat((*p_str++));
if(column>=16 && temp_cmd == 0x80)
{
column=0;
temp_cmd = 0xc0;
lcd_delay_ms(spt_ms); //滚动的延时
display_string(" ",1,0);
}
else if (column>=16 && temp_cmd == 0xc0)
{
column=0;
temp_cmd=0x80;
lcd_delay_ms(spt_ms); //滚动的延时
display_string(" ",0,0);
}
}
lcd_delay_ms(spt_ms); //滚动的延时
write_cmd(0x01);/*清屏*/
write_cmd(0x0c); //显示器开,光标关闭
}
void display_2Digit(unsigned int digit,unsigned char row,unsigned char column)
{
display_char((digit/10%10)+0x30,row,column);
display_char((digit%10)+0x30,row,column+1);
}
2.6:矩阵键盘
h文件
#ifndef __MATRIX4X4_H__
#define __MATRIX4X4_H__
#include <reg51.h>
/***** 端口宏定义 *****/
#define KEY_PORT P2
/***** 函数声明 *****/
//键盘哪个按键被按下返回按键 【序号(1-16)】,没有按键按下返回0
unsigned char key_scan();
//读键盘功能 重新定义按键返回值 没有按键按下,返回255
unsigned char read_key();
#endif
c文件
#include "matrix4X4.h"
void matrix_delay_ms(unsigned int xms)
{
unsigned int i,j;
for(i=0;i<xms;i++)
{
for(j=0;j<110;j++);
}
}
unsigned char key_scan()
{
unsigned char temp=0,row=0,column=0,i=0;
KEY_PORT = 0x0f;
matrix_delay_ms(1);
temp=KEY_PORT;
if(temp!=0x0f)
{
switch (temp)
{
case 0x0e: //0000 1110
row=1; break;
case 0x0d: //0000 1101
row=2; break;
case 0x0b: //0000 1011
row=3; break;
case 0x07: //0000 0111
row=4; break;
default:break;
}
KEY_PORT=0xf0;
matrix_delay_ms(1);
temp=KEY_PORT;
if(temp!=0xf0)
{
switch (temp)
{
case 0xe0: //1110 0000
column=1; break;
case 0xd0: //1101 0000
column=2; break;
case 0xb0: //1011 0000
column=3; break;
case 0x70: //0111 0000
column=4; break;
default:break;
}
}
while(KEY_PORT!=0xf0 && i<255){matrix_delay_ms(1);i++;}
}
if(row!=0&&column!=0){ return ( (row-1)*4+column ) ;}
else {return 0;}
}
unsigned char read_key()
{
unsigned char temp=0,key_char=255;
temp=key_scan();
if(temp!=0)
{
switch(temp)
{
case 1:key_char=1;break;
case 2:key_char=2;break;
case 3:key_char=3;break;
case 4:key_char='A';break;
case 5:key_char=4;break;
case 6:key_char=5;break;
case 7:key_char=6;break;
case 8:key_char='B';break;
case 9:key_char=7;break;
case 10:key_char=8;break;
case 11:key_char=9;break;
case 12:key_char='C';break;
case 13:key_char='R';break;
case 14:key_char=0;break;
case 15:key_char='Y';break;
case 16:key_char='D';break;
}
}
else
{
key_char=255;
}
return key_char;
}