一、中断
首先要明白什么是中断程序。
书上说:中断程序是一种特殊的程序运行方式,中断向量是这个中断程序运行的入口,一个中断程序包括中断请求,中断响应,中断关闭三步走。定义很严谨,用我的理解来讲中断其实可以类比我们日常生活中的“随时响应”
比如说:目前正在努力学习(主函数执行其任务),突然肚子咕咕叫起来(中断请求),肚子叫是随时都有可能发生的,也有可能是每次饭后隔3个小时又饿了,反正就是在满足肚子饿了的条件下才会叫(中断开关和条件),提醒我们要进食了(中断响应)。吃完饭后我们回去继续学习(中断返回)。
从程序的角度来看,一旦打开中断开关,中断就会开始判断条件,满足条件之后执行中断里面的程序内容。理清楚先后顺序对于理解后面的大规模程序非常重要。
二、中断几种类型
在C51/C52中有3大类中断服务:定时器计数器(简称定计,计数最小单位为1次,计时单位为1us)、串口通信(串口)、功率控制
六个中断控制寄存器。TCON是控制定时器的启停,溢出和中断情况、SCON串口控制、PCON功率控制、中断允许IE、中断优先级IP、TMOD定计控制端口。其中:TCON、IE最好位寻址,其余的最好还是字节寻址。
控制这几个口直接采用字节寻址的方式比如:TMOD=0x01代表设置定时器T0工作于方式1。
位寻址那就是直接 TR0=1;ET0=1; //对TCON的TR0位直接控制置1,ET0=1打开定时器0的开关
注意:C52/51的P3端口就是不同中断的控制口(见前几篇文章的C51引脚配图)。
2.1 TCON和TMOD
细节看这个:(9条消息) 51单片机---IE寄存器,TCON寄存器,TMOD寄存器_IMET-的博客-CSDN博客_ie寄存器
——1.TMOD的Gate位一般用来测量时序长度,所以大部分时间不用,赋0。
——2.写程序时:一般先开启TMOD,设置好计数值TH和TL,选择TCON和IE要打开的开关,最后一个EA开启总中断即可完成。
——3.计数值的设置问题:1.用STC-ISP提供的功能计算 2.TH=(当前选择的工作方式的计数/计时最大值 - 目标计数/计时值)/256 TL = (当前选择的工作方式的计数/计时最大值 - 目标计数/计时值)% 256。写程序时,除了方式2,计数值的设置需要在初始化和中断函数两个地方都写上。
——4.TCON中的TF:定计计数满溢出TF变成1,使用中断会自动清零,比较方便。但是要是用查询的方式(也就是自己写的函数)完成定时器计数器任务就需要人工清零。
——5.四种计数方式的对比:
方式0:13位定时计数方式,最大计数值为2^13=8192,定时8192个机器周期。此方式已经不再用了,是为了和以前的单片机兼容,初学者不用掌握。
方式1:16位定时计数方式,最大计数值为2^16=65536,定时65536个机器周期。此方式可实现最大的定时时间和最大计数次数。是最常用方式之一。
方式2:8位自动重装计数方式,最大计数值为2^8=256,定时256个机器周期。此方式工作时定时或计数到了不用重装初值,而且TH和TL设置的数据大小一样。另外在串口通讯时常用此方式。是最常用方式。
方式3:特殊工作方式。将定时器0分成两个8位功能不全的定时计数器,要占用T1部分功能。也不常用。
main(){
TMOD = 0X01;
TH0 = (65535 - 50000) / 256; //计时50000us,50ms
TL0 = (65535 - 50000) % 256;
EA = 1; //总中断
ET0 = 1; //T0定计中断
TR0 = 1; //开始工作
while(1);
}
2.2 SCON
数据传输:对于数据的发送,我们都是把数据转换成一串串的字节。比如发送“HelloWorld”,计算机会先对其采用ASCALL编码(不同的编码方式),比如变成:0xb3(H),0x23(e),0x43(e),0xf6(l),0x7d(l)...发送一个字节最后再组装。而衡量发送字节的快慢这是波特率。
波特率:单位时间内(一般1s)发送的二进制数据位数。例如9600b/s,每秒发9600位,算成字节就是9600/8=1200个字节。
SCON的产生和定计T1密切相关,有一个SCON对应的定计方式选择和初值选择表,可以直接参考。也可以使用波特率计算器,我放上来,不要积分。
(9条消息) 51波特率初值设定计算器-嵌入式文档类资源-CSDN文库
细节参考这个博主的文章。
(9条消息) TMOD、SCON、PCON寄存器的配置_逸凌Time的博客-CSDN博客_scon寄存器
——1.每个串口的工作方式发送的帧不同,直接影响TB8和RB8。方式0:扩展并行IO口;方式1:双机通信;方式2:双机和多机通信;方式3:同上。
——2.实际使用起来,用定计T1方式2;且发送数据用查询方式,接收数据用中断方式。如下:
#include "reg52.h" //接收数据程序
sbit led=P0^0; //将单片机的P0.0端口定义为led
void UsartInit() //串口初始化函数,新手可以直接写在main函数里,熟悉再模块化
{
SCON=0X50; //设置为工作方式1
TMOD=0X20; //设置计数器工作方式2
PCON=0X80; //波特率加倍
TH1=0XF4; //计数器初始值设置,注意波特率是4800的
TL1=0XF4;
ES=1; //打开接收中断
EA=1; //打开总中断
TR1=1; //打开计数器
}
void main()
{
UsartInit(); // 串口初始化
while(1);
}
void Usart() interrupt 4 //进入中断服务函数
{
char receiveData;
receiveData=SBUF;//出去接收到的数据,此时SBUF存放接收数据
RI = 0;//清除接收中断标志位
if(receiveData=='1')
{
led=1; //接收1时,打开LED灯
}
if(receiveData=='0')
{
led=0; //接收0时,关闭LED灯
}
SBUF=receiveData;
while(!TI); //等待发送数据完成
TI=0; //清除发送完成标志位
}
发送数据用查询方法:
#include
//发送数据查询方式
void delay(unsigned char x)
{
int i,j;
for(i=0;i
同时收发程序:
#include
sbit LED1 = P1^0;
sbit LED2 = P1^1;
unsigned char UART_buff;
bit New_rec = 0;
bit Send_ed = 1;
void delay(unsigned char x)
{
int i,j;
for(i=0;i
2.3 中断的使用方法概括
void nameoffunction() interrupt n using m
{}
中断函数必须void;name就是中断函数的名字;interrupt 是关键字不能少,后面的n是中断号;using m是用几号工作寄存器。
中断号:n=0:INT0外部中断0控制号;n=1:定计T0中断控制口;n=2:外部中断INT1控制;n=3:T1定计控制口;n=4:串口中断控制口;【n=5:T2定计控制口,T2定时计数器其C51/52单片机都没有】
工作寄存器:将工作寄存器内容保存到堆栈中,中断函数返回前进行恢复。m=0~3,有四个,不能混用,可以省略不指定,程序会自动分配。