外部中断
可能是定时器T1/T0方式0(16位可自动重装初值)或者定时器T2(只有16位可自动重装初值) T1/T0与T2除了控制寄存器的配置有些不一样,其余定时初值等都是一样的.
●12分频:
t = ( 2 n − a ) ∗ 12 f s y s t=\frac{(2^n-a)*12}{f_{sys}} t=fsys(2n−a)∗12
●不分频:
t = ( 2 n − a ) ∗ 1 f s y s t=\frac{(2^n-a)*1}{f_{sys}} t=fsys(2n−a)∗1
时间t的单位位秒; (举例为50ms) 定时器应该只会考16位的,所以n=16; 系统时钟 f s y s f_{sys} fsys应该是12MHz,即 12 × 1 0 6 12×10^6 12×106Hz 若采用12分频,可算得计数初值十进制数a=15536,换算成二进制数位 a = ( 11110010110000 ) 2 a=(11 1100 1011 0000)_2 a=(11110010110000)2
课本P31 系统时钟 f s y s f_{sys} fsys =主时钟 f o s c f_{osc} fosc / N N为分频系数,可以通过时钟分频寄存器CLK_DIV进行改变,默认为1.
● ●方法一: 代码:
#include<STC15.H> #define uchar unsigned char #define uint unsigned int uchar i=0; uchar j; void GPIO(void); //IO口初始化函数 void T2_Init(void); //定时器2初始化函数50毫秒@12Hz void main(void) { GPIO(); //IO口初始化 T2_Init();//定时器2初始化 while(1); //用原地踏步模拟主程序 } void GPIO(void) //IO口初始化函数 { P0M1=0; P0M0=0; P1M1=0; P1M0=0; P2M1=0; P2M0=0; P3M1=0; P3M0=0; } //---------------定时器2初始化函数-----&中断方法------// void T2_Init(void) //50毫秒@12Hz { AUXR &= 0xFB; //定时器时钟12T模式 T2L = 0xB0; //设置定时初值 T2H = 0x3C; //设置定时初值 EA = 1; //开总中断 IE2 |= 0X04; //开定时器2中断允许控制位 AUXR |= 0x10; //定时器2开始计时 } //------定时器二中断服务函数0----------// void T2_ISR(void) interrupt 12 { i++; j=i/20; if(j==0||j==1) { P46=0; } if(j==2) { P46=1; } if(j==3) i=0; }运行结果: (波形图占空比,周期符合要求,但不是水平的原因可能是IO口内部RC充放电) ●方法二: 方法二改变了系统时钟 f s y s f_{sys} fsys ,来获得较长的时间.定时初值的计算方法与上边所说一样,只是需要提前将系统时钟 f s y s f_{sys} fsys按照公式算出.可以得到定时初值a= ( 3036 ) 10 (3036)_{10} (3036)10
#define x 3036 //定时1秒的初值老师在程序中直接将3036用#define宏定义为x,(不加前缀声明数字类型时,默认为10进制数,编辑器在运行时统一将数字转化为二进制)
T2L = x; //设置定时初值 T2H = x>>8; //设置定时初值T2L和T2H都是8位状态寄存器,只有8位,只能存8个数.上边第一句将x赋值给T2L,运行时编辑器自动将x转化为二进制数1011 1101 1100,T2L寄存器只能保存二进制数的低八位. x右移8位,高位用0补齐,x>>8的结果位0000 1011,再将右移的结果赋值给T2H. 这种通过宏定义来赋值,是为了后续该程序容易改,考试没太有必要用,直接对T2L和T2H赋十六进制值就行.
#define uchar unsigned char #define uint unsigned int #define x 3036 //定时1秒的初值 uchar i=0; void GPIO(void); //IO口初始化函数 void T2_Init(void); //定时器2初始化函数50毫秒@12Hz void main(void) { GPIO(); //IO口初始化 T2_Init();//定时器2初始化 while(1); //用原地踏步模拟主程序 } void GPIO(void) //IO口初始化函数 { P0M1=0; P0M0=0; P1M1=0; P1M0=0; P2M1=0; P2M0=0; P3M1=0; P3M0=0; } //---------------定时器2初始化函数-----&中断方法------// void T2_Init(void) //50毫秒@12Hz { CLK_DIV=0x04; //系统时钟为主时钟的16分频 AUXR &= 0xFB; //定时器时钟12T模式 T2L = x; //设置定时初值 T2H = x>>8; //设置定时初值 EA = 1; //开总中断 IE2 |= 0X04; //开定时器2中断允许控制位 AUXR |= 0x10; //定时器2开始计时 } void T2_ISR(void) interrupt 12 { i++; if(i==1||i==2) { P46=0; } if(i==3) { P46=1; i=0; } }运行结果: 多次改变时钟分频寄存器CLK_DIV的值,运行结果并未产生肉眼可见的变化.不知道是代码问题还又是仿真的BUG. ●方法一,二对比 方法一是先定时一个比较小的时间,之后定时器溢出,触发中断,进入定时器中断服务函数,循环变量i再中断函数里累加.判断循环变量i是否达到预定值,多次执行中断服务函数,产生误差时间.如果定时器定时时间比较小,需要较多次数地执行中断服务函数,误差时间就会肉眼可见. 所以在使用方法一时定时器定时时间应该尽可能的大,以减小误差. 方法二,多配置一个寄存器,增加犯错概率.😷 可提前将常用定时时间准备好
1、在系统编程(In System Programming,ISP) 在应用编程(In Application Programming,IAP)P14 2、STC单片机命名规则P15 ●工作电压:F、L、W ●程序空间大小(程序储存器)
1、任何一个中央处理单元CPU都包含有控制器和运算器两大基本模块 2、通过程序计数器(Program Counter,PC)从程序存储器中源源不断地取出所要执行的代码。因此,程序计数器PC是CPU中最基本的控制部分。PC的特点就是总是指向下一条所要执行的指令的地址空间。P22 3、程序计数器的宽度为16位。也就是说,地址深度为216,地址的范围为0~65535,即64K。因此,程序存储器的深度最大为64K。 4、数据指针(Dual Data Pointer,DPTR)是一个16位的专用寄存器。 由DPL(低8位)和DPH(高8位)组成,其地址为82H(DPL,低字节)和83H(DPH,高字节)。DPTR是8051中唯一可以直接进行16位操作的寄存器。此外,也可以按照字节分别对DPH和DPL进行操作。P25 5、在STC单片机中,用于控制指向存储空间位置的是一个堆栈指针(Stack Pointer,SP)它实际上就是一个8位的专用寄存器。该寄存器的内容就是栈顶的地址,也就是用于表示当前栈顶在内部RAM块中的位置。其作用主要用于保存现场。先进后出,后进先出P25 6、指针类寄存器3个,SP;DPTR;PC 7、运算器:8位算术逻辑单元ALU(核心);累加器ACC;B寄存器;程序状态字PSW P21 8、指令通道??? 9、 10、程序存储器16位的PC指向下一条要执行的指令。 CPU只能通过使用MOVC指令,从程序空间读取数据。 当单片机复位后,程序计数器(PC)的内容为0x0000。 因此,从程序存储器地址为0x0000的地方开始执行程序。另外,中断服务程序的入口地址(也称为中断向量)也放在程序存储单元。 11、STC系列单片机内部提供了大容量的数据Flash存储器,用于实现电可擦除的**只读存储器(EEPROM)**功能。 数据Flash存储器和程序Flash存储器空间是分开的。 12、
1、中断请求,中断响应,中断服务,中断返回 2、 3、 4、 5、