1、中断的引出
我们写个代码,实现单片机每隔1s给PC的上位机发送一段字符串,然后我们也能通过上位机控制单片机的LED1的亮灭。
代码①:
向上位机每隔1s不断的发送一段字符串
#include <REGX52.H>
#include "intrins.h"
sfr AUXR = 0X8E;
void Delay1000ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 8;
j = 1;
k = 243;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void UartInit(void) //波特率9600
{
PCON &= 0x7F; //波特率不倍速
SCON = 0x40; //8位数据,可变波特率
AUXR = 0x01;
TMOD &= 0x0F; //清除定时器1模式位
TMOD |= 0x20; //设定定时器1为8位自动重装方式
TL1 = 0xFD; //设定定时初值
TH1 = 0xFD; //设定定时器重装值
TR1 = 1; //启动定时器1
}
void sendByte(char data_sj)
{
SBUF = data_sj;//单片机开始给SBUF里面写入数据
while(!TI);
TI = 0;
}
void sendString(char *str)
{
while(*str != '\0')
{
sendByte(*str);
str++;
}
}
void main()
{
UartInit();
while(1)
{
sendString("wohaoshuai\r\n");
Delay1000ms();
}
}
代码②:通过上位机来控制LED1的亮灭
#include <REGX52.H>
#include "intrins.h"
sfr AUXR = 0X8E;
sbit LED1 = P3^7;
void Delay300ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 3;
j = 26;
k = 223;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void UartInit(void) //波特率9600
{
PCON &= 0x7F; //波特率不倍速
SCON = 0x50; //8位数据,可变波特率,且允许接收数据
AUXR = 0x01;
TMOD &= 0x0F; //清除定时器1模式位
TMOD |= 0x20; //设定定时器1为8位自动重装方式
TL1 = 0xFD; //设定定时初值
TH1 = 0xFD; //设定定时器重装值
TR1 = 1; //启动定时器1
}
void main()
{
char mark;
LED1 = 1;
UartInit();
while(1)
{
Delay300ms();//先给硬件一点准备时间
if(RI == 1)//判断单片机的SBUF是否写入数据成功,已经写入成功这变为1,然后个RI手动置0后,以便为第二次写入做标志
{
RI = 0;
mark = SBUF;//将SBUF里面的数据存储在变量mark中
if(mark == 'o')
{
LED1 = 0;
}
if(mark == 'c')
{
LED1 = 1;
}
}
}
}
最终功能实现的代码③:
#include <REGX52.H>
#include "intrins.h"
sfr AUXR = 0X8E;
sbit LED1 = P3^7;
void Delay1000ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 8;
j = 1;
k = 243;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void Delay300ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 3;
j = 26;
k = 223;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void UartInit(void) //波特率9600
{
PCON &= 0x7F; //波特率不倍速
SCON = 0x40; //8位数据,可变波特率
REN = 1;
AUXR = 0x01;
TMOD &= 0x0F; //清除定时器1模式位
TMOD |= 0x20; //设定定时器1为8位自动重装方式
TL1 = 0xFD; //设定定时初值
TH1 = 0xFD; //设定定时器重装值
TR1 = 1; //启动定时器1
}
void sendByte(char data_sj)
{
SBUF = data_sj;
while(!TI);
TI = 0;
}
void sendString(char *str)
{
while(*str != '\0')
{
sendByte(*str);
str++;
}
}
void main()
{
char mark;
LED1 = 1;
UartInit();
while(1)
{
Delay300ms();//先给硬件一点准备的时间
sendString("wohaoshuai\r\n");
Delay1000ms();
if(RI == 1)
{
RI = 0;
mark = SBUF;
if(mark == 'o')
{
LED1 = 0;
}
if(mark == 'c')
{
LED1 = 1;
}
}
}
}
其实就是代码①和代码②的结合。
但是我们发现一个BUG,就是上位机个单片机发送一个指令控制LED1时,LED1要过大约1s钟才会做出相应的动作。导致控制LED1的指令不灵敏。
这是为什么喃?
-
原因:主要因为是主函数里面的Delay1000ms()导致的,因为可能指令到来的时候,单片机还在执行Delay函数。
-
例如:把单片机比作我们,主函数的代码比作我们要做的事情,sendString()函数比作我们看电视,delay()函数比作我们数数。if()比作出门检测门口邮箱是否来通知。if后面{ }中的内容就是通知要我们做的事情。
-
整个过程就是:我们看电视->看完电视数1秒钟的数->数完数去检查一下邮箱是否有通知(如果有通知就执行通知里面的事情)->做完通知里面的事然后继续看电视。这样循环往复。
-
这个通知是外界给我们的,比如邮递员。我们并不知道邮递员什么时候给邮箱里面发送通知,所以我们只能每隔一段时间去查看邮箱里面是否有通知。
-
当我们还在看电视的时候,通知已经到邮箱里面了,这不代表通知来了,我们就必须去查看邮箱。我们也要等数完数才会去查看邮箱。所以通知里面让我们做的事情,并不会因为通知发送到邮箱而立马发生,也只有我们查看了邮箱后才会发生。
所以,上位机给单片机发送指令(就相当于给邮箱里面发送通知,比如让LED1亮/灭),而发送指令的时候,可能单片机还在执行其他的事情(比如我们看电视),单片机还没有执行到检测if()(我们还没有去查看邮箱),所以LED1灯不会立马做出反应,只有等到单片机执行到if()时,查看到指令,才会让LED1亮/灭。
解决方法:
- 使用串口中断,中断就相当于邮递员个邮箱里面发送通知的时候,按了一下门铃,给我们一个信息,让我们知道通知来了。让我们先执行通知里面的事情,做完通知里面的事情后,再回来继续接着做上次没有作为的事情。
- 通知里面的事情就是所谓单片机里面的中断函数里面的程序,一般情况下,在不影响主函数里面的程序,中断函数里面的程序执行速度是十分迅速的。
2、串口中断
- 如图上图串口中断源是UART,其中序列号位4。中断的标志位是TI和RI
- 开启串口中断:ES = 1;EA = 1;
- 由上可知,中断函数的序列号只有一个,所以可见,串口发送数据发生的中断和串口接收数据发生的中断是同一个函数
- 单片机串口接收数据中断的触发:并不是将SBUF里面的数据承接完给mark开始的。而是由上位机给SBUF写入数据完毕的那个时候开始
- 单片机串口发送数据中断的触发:是单片机向SBUF里面写入数据完毕开始的。可见都是向SBUF里面写入数据完毕开始的。
如上图所示,串口中断有2个标志位,如果开启的串口中断,定义了中断函数,当单片机接收到数据的时候,RI会自动变为1,所以我们通过判断RI的值来执行的发送数据的中断还是接收数据的中断。
#include <REGX52.H>
#include "intrins.h"
sfr AUXR = 0X8E;
sbit LED1 = P3^7;
char mark;
void Delay1000ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 8;
j = 1;
k = 243;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void Delay300ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 3;
j = 26;
k = 223;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void UartInit(void) //波特率9600
{
PCON &= 0x7F; //波特率不倍速
SCON = 0x40; //8位数据,可变波特率
REN = 1;
AUXR = 0x01;
TMOD &= 0x0F; //清除定时器1模式位
TMOD |= 0x20; //设定定时器1为8位自动重装方式
TL1 = 0xFD; //设定定时初值
TH1 = 0xFD; //设定定时器重装值
TR1 = 1; //启动定时器1
ES = 1;//开启串口中断
EA = 1;//开启中断总开关
}
void sendByte(char data_sj)
{
SBUF = data_sj;
while(!TI);
TI = 0;
}
void sendString(char *str)
{
while(*str != '\0')
{
sendByte(*str);
str++;
}
}
void main()
{
LED1 = 1;
UartInit();
while(1)
{
Delay300ms();
sendString("wohaoshuai\r\n");
Delay1000ms();
}
}
void UART_Handler() interrupt 4//从上位机给SBUF里面写入数据,触发中断函数
{
if(RI == 1) //上位机写入数据完毕后,RI变为1
{
RI = 0; //让RI变为0以便下次写入数据数据
mark = SBUF; //将SBUF里面的数据存储在变量mark中
if(mark == 'o')
{
LED1 = 0;
}
if(mark == 'c')
{
LED1 = 1;
}
}
}
3、发送字符串控制LED1的亮灭
#include <REGX52.H>
#include "intrins.h"
#include <string.h>
sfr AUXR = 0X8E;
sbit LED1 = P3^7;
char mark[12];//定义mark数组用来承接字符串
void Delay1000ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 8;
j = 1;
k = 243;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void Delay300ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 3;
j = 26;
k = 223;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void UartInit(void) //波特率9600
{
PCON &= 0x7F; //波特率不倍速
SCON = 0x40; //8位数据,可变波特率
REN = 1;
AUXR = 0x01;
TMOD &= 0x0F; //清除定时器1模式位
TMOD |= 0x20; //设定定时器1为8位自动重装方式
TL1 = 0xFD; //设定定时初值
TH1 = 0xFD; //设定定时器重装值
TR1 = 1; //启动定时器1
ES = 1;//开启串口中断
EA = 1;//开启中断总开关
}
void sendByte(char data_sj)
{
SBUF = data_sj;
while(!TI);
TI = 0;
}
void sendString(char *str)
{
while(*str != '\0')
{
sendByte(*str);
str++;
}
}
void main()
{
LED1 = 1;
UartInit();
while(1)
{
Delay300ms();
sendString("wohaoshuai\r\n");
Delay1000ms();
}
}
void UART_Handler() interrupt 4
{
static int i = 0;//定义一个静态变量
if(RI == 1)
{
RI = 0;
mark[i] = SBUF;
i++;
if(i == 12)
i = 0;
if(strstr(mark,"op"))
{
LED1 = 0;
i = 0;
memset(mark,'\0',12);
}
if(strstr(mark,"cl"))
{
LED1 = 1;
i = 0;
memset(mark,'\0',12);
}
}
}
void UART_Handler() interrupt 4
{
static int i = 0;//定义一个静态变量
if(RI == 1)
{
RI = 0;
mark[i] = SBUF;
i++;
if(i == 12)
i = 0;
if(strstr(mark,"op"))
{
LED1 = 0;
i = 0;
memset(mark,'\0',12);
}
if(strstr(mark,"cl"))
{
LED1 = 1;
i = 0;
memset(mark,'\0',12);
}
}
假设上位机发送open字符串,上位机也是将open的每个字符一个一个按顺序的写入SBUF的。将第一个o写入后,执行中断函数,SBUF将o存放在mark数组的第一位,然后上位机将第二个数据p写入SBUF,执行中断函数,SBUF将p存放在mark数组的第二位。这样依次进行。由此可见,每写入一个数据,就会执行一次中断函数。同理:单片机每次向外发送数据后,都会调用一次中断函数