51单片机学习总结
一、串口
1.LED点亮(跑灯)
(1)亮
#include<reg52.h>
sbit led = P2^0;
void main()
{
while(1)
{
led = 0;//必须是低电平才发光,看电路图
}
}
(2)跑灯
#include<reg52.h>
#include<intrins.h> //记住此函数库,用于移位
typedef unsigned int u16;
typedef unsigned char u8;
#define led P2 //此处用宏定义,sbit用于做单口的定义
void delay(u16 i)
{
while(i--);
}
void main()
{
u16 i;
led = 0xfe; //1111 1110,LED1亮,剩下的不亮
delay(50000); //此处一定要延时
for(i=0;i<7;i++)
{
led = _crol_(led,1); //左移一位灯
delay(50000); //延时闪烁,要不没有差距
}
for(i=0;i<7;i++)
{
led = _cror_(led,1); //右移一位灯
delay(50000);
}
}
2.独立键盘
#include<reg52.h>
typedef unsigned char u8;
typedef unsigned int u16;
sbit led = P2^0;
sbit k = P3^3;
void delay(u16 i)
{
while(i--);
}
void jianpan() //最好重新构建函数
{
if(k==0) //第一次判断k的值
{
delay(5000); //消抖
if(k==0) //第二次判断k的值
{
led = ~led; //令led取反,初始值为0
}
while(!k); //判断按键是否松开
}
}
void main()
{
while(1)
jianpan();
}
3.矩阵键盘(74HC245用于数码管,三态缓冲TTL门,P0控制A)
#include "reg52.h"
typedef unsigned int u16;
typedef unsigned char u8;
#define GPIO_DIG P0 //数码管用到了P0口
#define GPIO_KEY P1 //矩阵键盘用到了P1口
sbit LSA=P2^2; //38译码器的三个输入端
sbit LSB=P2^3;
sbit LSC=P2^4;
u8 KeyValue; //用来存放读取到的键值,也就是哪一个被摁下
u8 code smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//显示0~F的值此处是段选,一个数码管的各个段位亮
void delay(u16 i) //延时函数,i=1时,延时1us
{
while(i--);
}
void KeyDown(void)
{
char a=0;
GPIO_KEY=0x0f; //初始化矩阵行和列的值,先行后列
if(GPIO_KEY!=0x0f)//读取按键是否按下,如果按下就不是0x0f了
{
delay(1000);//延时10ms进行消抖
if(GPIO_KEY!=0x0f)//再次检测键盘是否按下
{
//总共有16种组合,也就是16个键
//测试列,先确定是哪一列
GPIO_KEY=0X0F;
switch(GPIO_KEY)
{
case(0X07): KeyValue=0;break; //1列
case(0X0b): KeyValue=1;break;
case(0X0d): KeyValue=2;break;
case(0X0e): KeyValue=3;break;
}
//测试行
GPIO_KEY=0XF0;
switch(GPIO_KEY)
{
case(0X70): KeyValue=KeyValue;break; //1行
case(0Xb0): KeyValue=KeyValue+4;break;
case(0Xd0): KeyValue=KeyValue+8;break;
case(0Xe0): KeyValue=KeyValue+12;break;
}
while((a<50)&&(GPIO_KEY!=0xf0)) //检测按键松手检测
{
delay(1000);
a++;
}
}
}
}
void main()
{
LSA=0; //给一个数码管提供位选,第一个数码管亮
LSB=0;
LSC=0;
while(1)
{
KeyDown(); //按键判断函数
GPIO_DIG=smgduan[KeyValue];
}
}
4.蜂鸣器
#include<reg52.h>
typedef unsigned char u8;
typedef unsigned int u16;
sbit beep = P1^5;//蜂鸣器连着单片机的15口
void delay(u16 i)
{
while(i--);
}
void main()
{
while(1)
{
beep = ~beep;
delay(100);
}
}
5.数码管显示数据(静态、动态,74hc573锁存器控制段选P0,也就是abcdefgdp的亮灭)
(1)静态(38译码器,三输入八输出,单片机22,23,24口输入)
#include<reg52.h>
typedef unsigned char u8;
typedef unsigned int u16;
sbit LSA = P2^2;
sbit LSB = P2^3;
sbit LSC = P2^4;
u8 code smg[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//分别代表0—F,其中0x3f=0011 1111(dp g f e d c b a段)
void main()
{
LSA=0;
LSB=0;
LSC=0;
P0=smg[0];
while(1);
}
(2)动态(38译码器,三输入八输出,单片机22,23,24口控制输入,分别可以显示Y0-Y7的输出,一定要消隐)
#include "reg52.h"
typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;
u8 code smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//显示0~F的值
void delay(u16 i)
{
while(i--);
}
void DigDisplay()
{
u8 i;
for(i=0;i<8;i++)
{
switch(i) //位选,选择点亮的数码管,
{
case(0):
LSA=0;LSB=0;LSC=0; break;//显示第0位
case(1):
LSA=1;LSB=0;LSC=0; break;//显示第1位
case(2):
LSA=0;LSB=1;LSC=0; break;//显示第2位
case(3):
LSA=1;LSB=1;LSC=0; break;//显示第3位
case(4):
LSA=0;LSB=0;LSC=1; break;//显示第4位
case(5):
LSA=1;LSB=0;LSC=1; break;//显示第5位
case(6):
LSA=0;LSB=1;LSC=1; break;//显示第6位
case(7):
LSA=1;LSB=1;LSC=1; break;//显示第7位
}
P0=smgduan[i];//发送段码
delay(100); //间隔一段时间扫描
P0=0x00;//消隐
}
}
void main()
{
while(1)
{
DigDisplay(); //数码管显示函数
}
}
6.点阵(用74HC595,其中11、12管脚分别代表SRCLK(移位寄存器时钟输入P36),RCLK(存储寄存器时钟输入P35),SER(移位的时候应用P34))
位选:选定这个数码管或者点阵这一列LED灯
段选:选定具体数码管的哪个LED段(a-g)或点阵哪一个灯
#include "reg51.h"
#include<intrins.h>
typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;
sbit SRCLK=P3^6;
sbit RCLK=P3^5;
sbit SER=P3^4;
u8 ledduan[]={0x00,0x00,0x3e,0x41,0x41,0x41,0x3e,0x00};
u8 ledwei[]={0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe};
void delay(u16 i)
{
while(i--);
}
void hc595_sendbite(u8 bite)
{
u8 a;
SRCLK=0; //数据移位和存储都是零
RCLK=0;
for(a=0;a<8;a++)
{
SER=bite>>7; //移七位,代表Q7‘输出
bite<<=1;//表示字节左移一位,分别表示移到前一位寄存器
SRCLK=1; //移位寄存器换成高电平有效
_nop_(); //经过nop空指令,pc加一加一,做短暂延时调整
_nop_();
SRCLK=0; //移位寄存器恢复低电平,为下一次循环移位做准备
}
RCLK=1; //存储寄存器始终有效
_nop_();
_nop_();
RCLK=0; //最终手动软件清零
}
void main()
{
u8 i;
while(1)
{
P0=0x7f; //初始化,第一列灯
for(i=0;i<8;i++)
{
P0=ledwei[i]; //位选,选中第一列灯
hc595_sendbite(ledduan[i]); //发送段选数据
delay(100); //延时
hc595_sendbite(0x00); //字节消隐 bite是一个16位字节数据
}
}
}
二、中断、定时/计数器、通信
1.系统中断
中断只需要考虑选择外部中断IT0(P32)或IT1(P33)是0(电信号触发方式)还是1(下降沿触发方式)
Eg:外部中断0
EA=1;//中断总开关
EX0=1;//开外部中断0
IT0=0/1;//外部触发方式,多为下降沿1触发
中断服务函数
void int0 () interrupt 0 using 1(2)
{
do anything that you want
}
(外部中断0和1)
#include "reg52.h"
typedef unsigned int u16;
typedef unsigned char u8;
sbit k3=P3^2; //定义按键K3
sbit led=P2^0; //定义P20口是led
void delay(u16 i)
{
while(i--);
}
void Int0Init()
{
//设置INT0,初始化
IT0=1;//跳变沿出发方式(下降沿)
EX0=1;//打开INT0的中断允许。
EA=1;//打开总中断
}
void main() //main函数里每次都调用函数加一个死循环,一般不改变,改变中断或定时器、初始化的内容
{
Int0Init(); // 设置外部中断0
while(1);
}
void Int0() interrupt 0 //外部中断0的中断函数,外部中断1是2
{
delay(1000); //延时消抖
if(k3==0)
{
led=~led;
}
}
2.定时器和计数器(高8位THx和低8位TLx两个8位寄存器控制,16位)
(1)TCON(控制启动或中断寄存器)
低4位控制外部中断、高4位控制定时器受中断影响开启或关闭的状态
TFx中断请求标志位(硬件自动清零)
TRx开启或断开定时/、计数器(用软件控制,置1时开启)
(2)TMOD(方式选择寄存器,其中M0M1表示四种方式)
低四位控制T0,高四位控制T1,GATE门控位(GATE=0,TRx为1才行)(GATE=1,TRx为1,外部中断INTx为1才行)
C/T位表示模式选择(0定时、1计数)
M1M0掌握(01 方式1 16位定时/计数)(10 方式2 8位自动重装定时/计数)
(3)初始化步骤
/*定时器0
实现现象:下载程序后D1小灯循环点亮1秒,熄灭1秒。使用单片机内部定时器可以实现准确延时。*/
#include "reg52.h"
typedef unsigned int u16;
typedef unsigned char u8;
sbit led=P2^0; //定义P20口是led
void Timer0Init()
{
TMOD|=0X01;//选择为定时器0模式,工作方式1,仅用TR0打开启动
TH0=0XFC; //给定时器赋初值,定时1ms
TL0=0X18;
ET0=1;//打开定时器0中断允许,与上面中断有关
EA=1;//打开总中断
TR0=1;//打开定时器
}
void main()
{
Timer0Init(); //定时器0初始化
while(1);
}
void Timer0() interrupt 1
{
static u16 i;
TH0=0XFC; //给定时器赋初值,定时1ms
TL0=0X18;
i++;
if(i==1000)
{
i=0;
led=~led;
}
}
3.定时器中断
/*定时器1
实现现象:下载程序后数码管最后一位间隔一秒循环显示0-F。使用单片机内部定时器可以实现准确延时。
数码管亮了,定时1ms后产生中断。
*/
#include "reg52.h"
typedef unsigned int u16;
typedef unsigned char u8;
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;
u8 code smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//显示0~F的值
u8 n=0;
void Timer1Init()
{
TMOD|=0X10;//选择为定时器1模式,工作方式1,仅用TR1打开启动。
TH1=0XFC; //给定时器赋初值,定时1ms,FC18是固定1ms
TL1=0X18;
ET1=1;//打开定时器1中断允许
EA=1;//打开总中断
TR1=1;//打开定时器
}
void main()
{
LSA=0;
LSB=0;
LSC=0;
Timer1Init(); //定时器1初始化
while(1);
}
void Timer1() interrupt 3 //定时/计数0是1,定时/计数1是3
{
static u16 i;
TH1=0XFC; //还得给定时器赋初值,定时1ms
TL1=0X18;
i++;
if(i==1000)
{
i=0;
P0=smgduan[n++];
if(n==16)n=0;
}
}
4.串口通信
波特率:每秒传输的二进制代码位数(eg:每秒钟传送240个字符,而每个字符格式包含10位(1个起始位、1个停止位、8个数据位),这时的比特率为:10位×240个/秒 = 2400 bps)
SCON(控制寄存器,设定串口工作方式、接收发送控制以及设置状态标志):
SM0(7)、SM1(6)表示串行口工作方式(4种),SM2(5)表示多机通信控制位,REN(4)表示串行接收位(0时禁止接收、1时允许接收),TI表示发送中断标志位(必用软件清零),RI表示接收中断标志位(内部硬件置1,外部软件清零)。
中级实验
秒表(定时器+中断)
#include "reg52.h"
typedef unsigned int u16;
typedef unsigned char u8;
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;
u8 code smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//显示0~F的值
u16 s;
u8 sec,mb[3];
void Timer0Init()
{
TMOD|=0X01;//选择为定时器0模式,工作方式1,仅用TR0打开启动。
TH0=0XFC; //给定时器赋初值,定时1ms
TL0=0X18;
TR0=1;//打开定时器
}
void delay(u16 i)
{
while(i--);
}
void DigDisplay()
{
u8 i;
for(i=0;i<3;i++)
{
switch(i) //位选,选择点亮的数码管,
{
case(0):
LSA=0;LSB=0;LSC=0; break;//显示第0位
case(1):
LSA=1;LSB=0;LSC=0; break;//显示第1位
case(2):
LSA=0;LSB=1;LSC=0; break;//显示第2位
}
P0=smgduan[mb[i]];//发送段码
delay(1); //间隔一段时间扫描
P0=0x00;//消隐
}
}
void main()
{
Timer0Init();
while(1)
{
if(TF0==1)
{
TF0=0;
TH0=0XFC; //给定时器赋初值,定时1ms
TL0=0X18;
s++;
}
if(s==1000) //到达1s时间
{
s=0;
sec++;
if(sec==1000)sec=0; //计时到100秒后重新开始
}
mb[0]=sec%10; //秒表个位
mb[1]=sec/10%10; //秒表十位
mb[2]=sec/100;
DigDisplay();
}
}