========================== 一 =========================
/**
* 利用用定时器中断闪烁LED
*/
#include <reg52.h>
#include <stdio.h>
#define uchar unsigned char
#define uint unsigned int
#define ulong unsigned long
// 10个LED
sbit P10 = P1^0;
sbit P11 = P1^1;
sbit P12 = P1^2;
sbit P13 = P1^3;
sbit P14 = P1^4;
sbit P15 = P1^5;
sbit P16 = P1^6;
sbit P17 = P1^7;
// 定义开关. 低电平有效.
sbit K1 = P3^2;// 按键K1,INT1是外中断1的引入口
sbit K2 = P3^3;
sbit K3 = P3^4;
sbit K4 = P3^5;
uchar t = 250; // 计数器溢出次数.
void main( void )
{
TMOD = 0x01; // 定时器1,16位工作方式
TR1 = 1; // 启动定时器1
ET1 = 1; // 打开定时器1中断
//EX1 = 1; // 打开外中断1
// TCON = 0x00;
EA = 1; // 打开总中断
while( 1 )
{
;// 主程序在这里就不断自循环,实际应用中,这里是做主要工作
}
}
/**
* 定时器1中断. 定时器1一直在不断自加. 当溢出时, 该中断函数
* 会被执行, 该中断函数再一次置0
*/
void timer1( void ) interrupt 3 // 定时器1中断是3号
{
TF1 = 0; // 定时器1溢出标志位.
// 写入定时器1初始值0x0006
TH1 = 0x00;
TL1 = 0x06;
if((t--) == 0)
{
P10 = ~P10; // 反转LED灯的亮灭
t = 250;
}
}
总结:
网上诸多搞MCU的前辈说51里头, 中断, 定时/计数器, 串口通信是三个硬骨头. 啃过去了就算51芯片登堂窥奥了. 果不其然, 的确是需要下一番功夫, 和之前的内容相比, 中断跟定时/计数器的确灵活和重要.
无甚好总结的了, 翻书.. . .
=============================== 二 ==================================
/**
* 利用定时器中断实现跑马灯.
*/
#include <stdio.h>
#include <reg51.h>
#define uchar unsigned char
#define uint unsigned int
#define ulong unsigned long
// 10个LED
sbit P10 = P1^0;
sbit P11 = P1^1;
sbit P12 = P1^2;
sbit P13 = P1^3;
sbit P14 = P1^4;
sbit P15 = P1^5;
sbit P16 = P1^6;
sbit P17 = P1^7;
// 定义开关. 低电平有效.
sbit K1 = P3^2;// 按键K1,INT1是外中断1的引入口
sbit K2 = P3^3;
sbit K3 = P3^4;
sbit K4 = P3^5;
uchar ledp[8] = {0xFE, 0xFC, 0xF8, 0xF0, 0xE0, 0xC0, 0x80, 0x00};
uchar i=0, t=50;// i记录P1口取哪个数据填P1. t表示计数器溢出51次才换下个花式
void main( void )
{
TMOD = 0x01; // 定时器0,16位工作方式
TR0 = 1; // 启动定时器0
ET0 = 1; // 打开定时器0中断
// TCON = 0x00;
EA = 1; // 打开总中断
while( 1 )
{
P1 = ledp[i];
}
}
/**
* 中断, 切换LED花式灯
*/
void timer0( void ) interrupt 1 // 定时器0中断是1号
{
TF0 = 0; // 定时器0溢出标志位.
TH0 = 0x00; // 写入定时器0初始值0x0000
TL0 = 0x00;
t --;
if(t == 0)
{
t = 50;
i++;
if(i == 8)
{
i = 0;
}
}
}
================================= 三 ==================================
/**
================== 精确定时 ==================
每经过一个机器周期, 计数器自加1.
因此计时器溢出所消耗的时间为 :
t = ( pow(2, 16) - 计数器初值 ) * 机器周期
又 机器周期 T = 晶振周期 * 12(一个机器周期==12个晶振周期);
所以 t = ( pow(2, 16) - 初值 ) * (晶振周期 * 12);
仿真器的晶振是22118400HZ,每秒钟可以执行1843200个机器周期。
而T2每次溢出最多65536个机器周期。我们尽量应该让溢出中断的
次数最少,这样对主程序的干扰也就最小。
选择每秒中断24次,每次溢出1843200/24=76800个机器周期,超出65536,无效。
选择每秒中断28次,每次溢出1843200/28=65828.5714285714个机器周期,
超出65536且不是整数,无效。
选择每秒中断29次,每次溢出1843200/29=63558.6206896552个机器周期, 不是整数
选择每秒中断30次,每次溢出1843200/30=61440个机器周期
选择每秒中断32次,每次溢出1843200/32=57600个机器周期
选择每秒中断36次,每次溢出1843200/36=51200个机器周期
选择每秒中断40次,每次溢出1843200/40=46080个机器周期
从上面可以看到我们可以选择方式有很多,但是最佳的是每秒中断30次,
每次溢出61440个机器周期。也就是赋定时器T2初值65536-61440=4096,
换成十六进制就是0x1000。
从上面的计算也可以看出晶振2118400Hz的好处,它可以整除的倍数多,
要准确定时非常方便。更常见的应用是在串口波特率上,使用22118400HZ
可以输出最多准确的标准波特率
note:
我使用的是 Keil uVision3V3.53 + ledkey.dll 搭建的仿真环境.
并没有将程序烧入单片机中, 因此使用的并非真实的22118400HZ晶振.
而是使用了我的机器上的晶振. 由于电脑比单片机配置高很多, 时钟
快很多. 因此在电脑上无法查看到真实的运行结果. 应该写入单片机
后会正常.(使用ledkey.dll会采用本机时钟, 而非你在keil中设置的
晶振)
*/
#include <reg51.h>
#include <stdio.h>
#define uchar unsigned char
#define uint unsigned int
#define ulong unsigned long
// 10个LED
sbit P10 = P1^0;
sbit P11 = P1^1;
sbit P12 = P1^2;
sbit P13 = P1^3;
sbit P14 = P1^4;
sbit P15 = P1^5;
sbit P16 = P1^6;
sbit P17 = P1^7;
// 定义开关. 低电平有效.
sbit K1 = P3^2;// 按键K1,INT1是外中断1的引入口
sbit K2 = P3^3;
sbit K3 = P3^4;
sbit K4 = P3^5;
uchar t = 30; // 每秒中断30次.
char code dx516[3] _at_ 0x003b;//这是为了仿真设置的
void main( void )
{
TMOD = 0x01; // 定时器1,16位工作方式
TR1 = 1; // 启动定时器1
ET1 = 1; // 打开定时器1中断
// TCON = 0x00;
EA = 1; // 打开总中断
while( 1 )
{
;
}
}
/**
* 定时器1中断. 计数器1一直在不断自加. 当溢出时, 该中断函数
* 会被执行, 该中断函数再一次置0
*/
void timer1( void ) interrupt 3 // 定时器1中断是3号
{
TF1 = 0; // 定时器1溢出标志位.
TH1 = 0x4c; // 写入定时器1初始值0x1000(4096)
TL1 = 0x00;
t --;
if(t == 0)
{
t = 30;
P10 = ~P10;
}
}