写在前面:51单片机有一些基础理论概念,大家一般都已经了解或者在百度上能查找到详细说明,博主就会省略,所以以下内容比较精炼,大家看完之后都能上手敲出代码,控制51单片机。但是想要融会贯通,真正精通51单片机,还需要大家私下不断编写代码,烧录程序。
一、LED
LED点亮,闪烁,流水灯见之前的博客。
二、蜂鸣器
蜂鸣器见之前博客。
三、 数码管
见之前博客(上传word文档形式)。
四、键盘
见之前博客(上传word文档形式)。
五、8*8点阵屏
见之前博客(上传word文档形式)。
以上五个板块为51单片机基础中的基础,下面我们来看51单片机中的基础中的中级内容。
六、中断系统
1.什么是中断:
计算机执行某程序时,发生了紧急事件或有特殊请求,CPU暂停某程序的执行,转而去处理上述事件或请求,处理完毕后再重新执行某程序的过程叫做中断。
2.实现外部中断:
言简意赅,需要先进行外部中断1初始化,再编写中断服务函数。中断初始化:a.打开总中断,然后b.开外部中断,c.配置外部中断触发方式。实现代码如下:(以开外部中断1为例)
void int1Init()
{
EA=1;//开总中断
EX1=1;//开外部中断1
IT1=1;//外部中断1下降沿触发
}
之后在主函数中引用此初始化函数即可。
初始化之后需要编写中断服务函数。
void int1() interrupt 2
{
P1=~P1;//点亮/熄灭8颗LED灯
}
中断小结:主函数中引用中断初始化函数,之后一旦触发中断,即CPU停下正在处理的事,执行中断服务函数。(关于中断初始化函数中控制位的说明,我会在文章最后附上STC89C52使用手册,控制位记不住在手册中查询即可)
七、定时计数器
1.51单片机上的定时计数器:
51单片机有2个16位定时器/计数器:定时器0(T0为P3.4)和定时器1(T1为P3.5),这里所说的16位是指定时/计数器内部分别有16位的计数寄存器。
当工作在定时模式时,每经过一个机器周期内部的16位计数寄存器的值就会加1,当这个寄存器装满时溢出。我们可以算出工作在定时模式时最高单次定时时间为65535*1.085us=时间(单位us)当工作在计数器模式时,T0(P3.4引脚),T1(P3.5引脚)每来一个脉冲计数寄存器加1。
2.基本步骤
定时器定时
a.定时器初始化:启动定时/计数器(通过TCON控制器),设置定时/计数器工作模式(通过TMOD控制器)
b.查询定时/计数器是否溢出(读TCON内TF位)
定时器初始化
void timer0Init()
{
TR0=1;//启动定时器0
TMOD=0x01;//配置工作模式为工作模式1,定时模式
TH0=0x4b;
TL0=0xfd;//定时50ms
}
if(TF0==1)
{
TF0=0;//软件清零
TH0=0x4b;
TL0=0xfd;//重新赋初值
mes++;//50ms到
}
定时器计数:这里我们用定时器1做计数器,即使用定时器1的定时功能,然后用定时器0做计数器使用。
初始化函数
void timer0Init()
{
TR0=1;
TMOD=0x50;//工作模式1,计数模式
TH0=0;
TL0=0;//从0次开始
}
主函数引用
通过计数器每500ms使LED=~LED,产生一次方波,计数器加1,期间不断进行display函数的扫描。
while(1)
{
if(TF1==1)
{
TF=0;
TH1=0x4b;
TL1=0xfd;
mes++;
if(mes==10)
{
mes=0;
LED=~LED;//之前sbit LED=P1;
}
}
display(TL0);
}
定时器中断:
初始化:需要启动相应中断和启动定时器
void timer0Init()
{
EA=1;
ET0=1;
TR0=1;
TMOD=0x01;
TH0=0xed;
TL0=0xff;
}
根据工作模式,中断服务函数里需要设置初值:
void timer0 () interrupt 1//中断入口
{
TH0=0xed;
TL0=0xff;
dispaly(num);//数码管显示函数
}
PS:以下内容为实现的基础代码及注释,文章最后会上传STC89C52使用手册和开发板原理图,各位浏览资料相应位置结合代码即可理解(STC89C52使用手册,开发板原理图借用“清翔MCS51开发板资料”)
八、串口通信
1.基础步骤:
串口初始化、串口中断函数
串口初始化:
void UARTInit()
{
EA=1;//打开总中断
ES=1;//打开串口中断
REN=1;//串口允许接收
SM1=1; SM0=0;//串口工作方式1,8位UART波特率可变
TR1=1;
TMOD|=0x20;
TH1=0xfd;
TL1=0xfd;//设置波特率为9600
}
波特率=2^SMOD·fosc/32·12·(256-T初)
串口中断函数:
void UART() interrupt 4//触发中断:接收标志位或者发送标志位溢出
{
uchar temp;//之前宏定义#define uchar unsigned char
if(RI)//判断接受是否完成
{
RI=0;//软件清零RI位
num=SBUF;
temp=num;
SBUF=++temp;
}
if(TI)
TI=0;
}
九、 IIC总线EEPROM
有2个主功能函数,6主功能辅助函数
//起始信号
void I2cStar()
{
SCL=1;
SDA=1;
delay5us();
SDA=0
delay5us();
}
//停止信号
void I2cStop()
{
SCL=0;
SDA=0;
SCL=1;
delay5us();
SDA=1;
delay5us();
}
//读应答
bit ReadACK()
{
SCL=0;
SCL=1;
delay5us();
if(SDA)
{
SCL=0;
return (1);
}
else
{
SCL=0;
return (0);
}
}
//写应答
void SendACK(bit i)
{
SCL=0;
if(i)
SDA=1;
else
SDA=0;
SCL=1;
delay5us();
SCL=0;
SDA=1;
}
//发送一个字节
void I2cSendByte(uchar dat)
{
uchar i;
for(i=0;i<8;i++)
{
SCL=0;
if(dat&0x80)//I2c通讯从最高位传输数据
SDA=1;
else
SDA=0;
SCL=1;
dat<<=1;
}
SCL=0;
SDA=1;
}
//接收一个字节
uchar I2cReadByte()
{
uchar i,dat;
for(i=0;i<8;i++)
{
dat=_crol_(dat,1);
SCL=0;
SCL=1;
if(SDA)
dat|=0x01;
else
dat|=0x00;
}
return (dat);
}
//写入一个字节
void At24c02WriteByte(uchar ADDR,uchar dat)
{
I2cStar();
I2cSendByte(At24c02ADDR+I2cWrite);
ReadACK();
I2cSendByte(ADDR);
ReadACK();
I2cSendByte(dat);
ReadACK();
I2cStop();
}
//读出一个字节
uchar At24c02ReadByte(uchar ADDR)
{
uchar dat;
I2cStar();
I2cSendByte(At24c02ADDR+I2cWrite);
ReadACK();
I2cSendByte(ADDR);
ReadACK();
I2cStar();
I2cSendByte(At24c02ADDR+I2cRead);
ReadACK();
dat=I2cReadByte();
SendACK(1);
I2cStop();
return (dat);
}
归结为:PS:()中内容为从机操作
写入一字节步骤:S,器件地址+0,(A),写入首地址,(A),Dat,(A),P
读出一字节步骤:S,器件地址+0,(A),读出首地址,(A),S,器件地址+1,(A),(Dat),NA,P
十、ADDA数模转换
90%类似EEPROM操作。(九中前6个函数一样,后2个函数步骤有细微差异)
写入一字节:S,器件地址+0,(A),发送控制字节,(A),Dat,(A),P
读出一字节:S,器件地址+0,(A),发送控制字节,(A),S,器件地址+1;(A),(Dat),NA,P
十一、数字温度传感器DS18B20
包括:初始化函数,写一字节函数,读一字节函数
bit ds_Init()
{
bit i;
DS=1;
_nop_();
DS=0;
delay_us(75);//拉低总线499.45us
DS=1;
delay_us(4);
i=DS;
delay_us(20);
DS=1;
_nop_();
return (i);
}
void write_byte(uchar dat)
{
uchar i;
for(i=0;i<8;i++)
{
DS = 0;
_nop_();//产生写时序
DS = dat & 0x01;
Delay_us(10);//76.95us
DS = 1; //释放总线准备下一次数据写入
_nop_();
dat >>= 1;
}
}
uchar read_byte()
{
uchar i, j, dat;
for(i=0;i<8;i++)
{
DS = 0;
_nop_();//产生读时序
DS = 1;
_nop_();//释放总线
j = DS;
Delay_us(10);//76.95us
DS = 1;
_nop_();
dat = (j<<7)|(dat>>1);
}
return (dat);
}
void main()
{
uint i;
uchar L, M;
/* ds_init();//初始化DS18B20
write_byte(0xcc);//发送跳跃ROM指令
write_byte(0x4e);//写暂存器指令
write_byte(0x7f);
write_byte(0xf7);
write_byte(0x1f);//配置工作在9位模式下
ds_init();//初始化DS18B20
write_byte(0xcc);//发送跳跃ROM指令
write_byte(0x48);*/
while(1)
{
ds_init();//初始化DS18B20
write_byte(0xcc);//发送跳跃ROM指令
write_byte(0x44);//发送温度转换指令
ds_init();//初始化DS18B20
write_byte(0xcc);//发送跳跃ROM指令
write_byte(0xbe);//读取DS18B20暂存器值
L = read_byte();
M = read_byte();
i = M;
i <<= 8;
i |= L;
i = i * 0.0625 * 10 + 0.5;
Display(i);
}
}
注意,主机和DS18B20做任何通讯之前都需要对其初始化
操作一般步骤:初始化,ROM指令,操作指令
十二、红外通讯
//初始化函数
void init()
{
TMOD = 0x22;
TH0 = 0x00;
TL0 = 0x00;
EA = 1;
ET0 = 1;
TR0 = 1;
IT0 = 1;
EX0 = 1;
TH1 = 0xfd;
TL1 = 0xfd;
TR1 = 1;
SM1 = 1;
ES=1;
}
void time0() interrupt 1 //定时器0中断服务函数
{
IRtime++;
}
void int0() interrupt 0
{
static uchar i;
static bit startflag;
if(startflag)
{
if( (IRtime < 53) && (IRtime >= 32) ) i = 0;
IRdata[i] = IRtime;
IRtime = 0;
i++;
if(i == 33)
{
IRok = 1;
i = 0;
}
}
else
{
IRtime = 0;
startflag = 1;
}
}
void IRcordpro() //解码
{
uchar i, j, k, cord, value;
k = 1;
for(i = 0; i < 4; i++)
{
for(j = 0; j < 8; j++)
{
cord = IRdata[k];
if(cord > 5)
value = value | 0x80;
if(j < 7)
{
value = value >> 1;
}
k++;
}
IRcord[i] = value;
value = 0;
}
IRpro_ok = 1;
}
void main()
{
uchar i;
init();
while(1)
{
if(IRok)
{
IRcordpro();
IRok = 0;
if(IRpro_ok)
{
for(i = 0; i < 4; i++)
{
SBUF = IRcord[i];
while(!TI);
TI = 0;
}
IRpro_ok = 0;
}
}
}
}
红外通讯:脉宽检测,解码,传输数据
十三、实时时钟DS1302
//写DS1302数据
void Write_DS1302_DAT(uchar cmd, uchar dat)
{
uchar i;
TRST = 0;
TSCLK = 0;
TRST = 1;
for(i = 0; i < 8; i++)
{
TSCLK = 0;
TIO = cmd & 0x01;
TSCLK = 1;
cmd >>=1;
}
for(i = 0; i < 8; i++)
{
TSCLK = 0;
TIO = dat & 0x01;
TSCLK = 1;
dat >>= 1;
}
}
//读DS1302数据
uchar Read_DS1302_DAT(uchar cmd)
{
uchar i, dat;
TRST = 0;
TSCLK = 0;
TRST = 1;
for(i = 0; i < 8; i++)
{
TSCLK = 0;
TIO = cmd & 0x01;
TSCLK = 1;
cmd >>=1;
}
for(i = 0; i < 8; i++)
{
TSCLK = 0;
dat >>= 1;
if(TIO) dat |= 0x80;
TSCLK = 1;
}
return dat;
}
//数据转BCD码
uchar Dat_Chg_BCD(uchar dat)
{
uchar dat1, dat2;
dat1 = dat / 10;
dat2 = dat % 10;
dat2 = dat2 + dat1 * 16;
return dat2;
}
//BCD码转换为数据
uchar BCD_Chg_Dat(uchar dat)
{
uchar dat1, dat2;
dat1 = dat / 16;
dat2 = dat % 16;
dat2 = dat2 + dat1 * 10;
return dat2;
}
void main()
{
uchar i;
uchar Sec, Min, Hour;
Write_DS1302_DAT(0x8e, 0);
Write_DS1302_DAT(0x80, Dat_Chg_BCD(30));
Write_DS1302_DAT(0x82, Dat_Chg_BCD(15));
Write_DS1302_DAT(0x84, Dat_Chg_BCD(19));
Write_DS1302_DAT(0x8e, 0x80);
while(1)
{
Write_DS1302_DAT(0x8e, 0);
Sec = BCD_Chg_Dat(Read_DS1302_DAT(0x81));
Min = BCD_Chg_Dat(Read_DS1302_DAT(0x83));
Hour = BCD_Chg_Dat(Read_DS1302_DAT(0x85));
Write_DS1302_DAT(0x8e, 0x80);
for(i = 0; i < 50; i++)
Display(Hour, Min, Sec);
}
}
十四、LCD1602液晶
//判断液晶忙
void Read_Busy()
{
uchar busy;
P0 = 0xff;
RS = 0;
RW = 1;
do
{
EN = 1;
busy = P0;
EN = 0;
}while(busy & 0x80);
}
//写LCD1602命令一个字节
void Write_Cmd(uchar cmd)
{
Read_Busy();//判断忙
RS = 0;
RW = 0;
P0 = cmd;
EN = 1;
EN = 0;
}
//写一个字节数据
void Write_Dat(uchar dat)
{
Read_Busy();
RS = 1;
RW = 0;
P0 = dat;
EN = 1;
EN = 0;
}
void main()
{
Write_Cmd(0x38);
Write_Cmd(0x0f);
Write_Cmd(0x01);
Write_Cmd(0x06);
Write_Cmd(0x80 | 0x06);
Write_Dat(1 + '0');
Write_Dat(2 + '0');
Write_Dat(3 + '0');
Write_Dat(4 + '0');
Write_Dat(5 + '0');
while(1);
}
十五、手机蓝牙控制51最小系统
void UART_INIT()
{
SM0 = 0;
SM1 = 1;
REN = 1;
EA = 1;
ES = 1;
TMOD = 0x20;
TH1 = 0xfd;
TL1 = 0xfd;
TR1 = 1;
}
//串口中断
void UART_SER() interrupt 4
{
if(RI)
{
RI = 0;
switch(SBUF)
{
case 0x01: LED1 = 0; break;
case 0x02: LED1 = 1; break;
}
}
}
void main()
{
UART_INIT();
while(1);
}
以上就是51单片机的基础内容(湿度传感器,电机驱动,蓝牙,WiFi四个板块博主也会在近期写出相应博客),后面一些章节因为时间匆忙,只有代码,各位可以结合以下博主上传的开发板原理图资料自己消化理解,结合实际,相信大家一定可以学好51单片机,共勉!
PS:开发板原理图是借用的此清翔MCS51开发板资料。
STC89C52使用手册大家可以在网络上找以下资源,里面内容也是非常重要的。