51单片机_中断&定时器
51单片机_中断&定时器
一、中断的基本概念
1、什么是中断
中断系统是为使CPU具有对外界紧急事件的实时处理能力而设置的。
当中央处理机CPU正在处理某件事的时候外界发生了紧急事件请求,要求CPU暂停当前的工作,转而去处理这个紧急事件,处理完以后,再回到原来被中断的地方,继续原来的工作,这样的过程称为中断。实现这种功能的部件称为中断系统,请示CPU中断的请求源称为中断源。微型机的中断系统一般允许多个中断源,当几个中断源同时向CPU请求中断,要求为它服务的时候,这就存在CPU优先响应哪一个中断源请求的问题。通常根据中断源的轻重缓急排队,优先处理最紧急事件的中断请求源,即规定每一个中断源有一个优先级别。CPU总是先响应优先级别最高的中断请求。
当CPU正在处理一个中断源请求的时候(执行相应的中断服务程序),发生了另外一个优先级比它还高的中断源请求。如果CPU能够暂停对原来中断源的服务程序,转而去处理优先级更高的中断请求源,处理完以后,再回到原低级中断服务程序,这样的过程称为中断嵌套。这样的中断系统称为多级中断系统,没有中断嵌套功能的中断系统称为单级中断系统。
简单来说就是:
CPU在处理某一事件A时,发生了另一事件B请求CPU迅速去处理(中断发生),那么CPU就会暂停当前的工作(A事件),去执行B事件(中断响应和中断服务),然后B事件做完之后,再回到原来的事件(A事件)中继续工作。(中断的返回)。

2、中断的作用与优势
随着计算机技术的应用,人们发现中断技术不仅解决了快速主机与I/O设备的数据传送问题,而且还有具有如下的优点:
- 分时操作:CPU可以分时为多个I/O设备服务,提高了计算机的利用率。
- 实时操作:CPU能够及时处理应用系统的随机事件,系统的实时性大大增强。
- 可靠性高:CPU具有处理设备故障及掉电等突发性事件能力,从而使系统可靠性更高。
3、51单片机的中断源
80C51 的中断系统有5个中断源(80C52 有6个),两个优先级,可实现二级中断嵌套。
- 外部中断0 (INT0)
- 外部中断1 (INT1)
- 定时器/计数器0溢出中断
- 定时器/计数器1溢出中断
- 串行口中断
二、中断优先级
1、51单片机的中断优先级机制
51单片机支持多个中断源,每个中断源都有其特定的功能和用途。以下是常见的51单片机中断源及其对应的中断向量地址:
| 中断源 | 描述 | 中断向量地址 | 特殊功能寄存器 (SFR) |
|---|---|---|---|
| 外部中断0 (INT0) | 外部硬件引脚P3.2上的下降沿或低电平触发 | 0003H | IE (Interrupt Enable) 寄存器, TCON (Timer/Counter Control) 寄存器 |
| 定时器0溢出中断 (TF0) | 定时器0计数溢出时触发 | 000BH | IE 寄存器, TCON 寄存器 |
| 外部中断1 (INT1) | 外部硬件引脚P3.3上的下降沿或低电平触发 | 0013H | IE 寄存器, TCON 寄存器 |
| 定时器1溢出中断 (TF1) | 定时器1计数溢出时触发 | 001BH | IE 寄存器, TCON 寄存器 |
| 串行口中断 (RI/TI) | 串行口接收或发送完成时触发 | 0023H | IE 寄存器, SCON (Serial Control) 寄存器 |

一、(P3.2),可由 IT0 (TCON.0) 选择其为低电平有效还是下降沿有效。当CPU检测到 P3.2 引脚上出现有效的中断信号,中断标志 IE0 (TCON.1) 置1,向CPU申请中断。
二、 (P3.3),可由 IT1 (TCON.2) 选择其为低电平有效还是下降沿有效。当CPU检测到 P3.3 引脚上出现有效的中断信号,中断标志 IE0 (TCON.3) 置1,向CPU申请中断。
三、TF0 (TCON.5),片内定时器/计数器 T0 溢出中断请求标志。当定时/计数器 T0 发生溢出时,置位 TF0,并向 CPU 申请中断。
四、TF1 (TCON.7),片内定时/计数器 T1 溢出中断请求标志。当定时/计数器 T1 发生溢出时,置位 TF1,并向 CPU 申请中断。
五、RI (SCON.0) 或 TI (SCON.1),串行口中断请求标志。当串行口接收完一帧串行数据时置位 RI 或当串行口发送完一帧串行数据时置位 TI,向 CPU 申请中断。
2、中断源的配置
每个中断源都需要通过特殊功能寄存器(SFR)进行配置,以启用或禁用相应的中断。主要的SFR包括:
- IE (Interrupt Enable) 寄存器:用于全局使能和个别中断源的使能。
- TCON (Timer/Counter Control) 寄存器:用于控制定时器和外部中断的状态。
- SCON (Serial Control) 寄存器:用于控制串行通信的状态。
例如,要启用外部中断0 (INT0),可以在程序中添加以下代码:
// 全局中断使能
EA = 1;
// 使能外部中断0
EX0 = 1;
3、设置和修改中断优先级的方法
中断优先寄存器 IP
| 位 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
|---|---|---|---|---|---|---|---|---|
| 功能 | / | / | / | PS | PT1 | PX1 | PT0 | PX0 |
IP寄存器用于设置每个中断源的优先级。IP寄存器的地址是B8H,其各位定义如下:
| 位 | 描述 | 默认值 | 功能 |
|---|---|---|---|
| PS | 串行口中断 (RI/TI) 的优先级 | 0 | 0=低优先级, 1=高优先级 |
| PT1 | 定时器1 (TF1) 的优先级 | 0 | 0=低优先级, 1=高优先级 |
| PX1 | 外部中断1 (INT1) 的优先级 | 0 | 0=低优先级, 1=高优先级 |
| PT0 | 定时器0 (TF0) 的优先级 | 0 | 0=低优先级, 1=高优先级 |
| PX0 | 外部中断0 (INT0) 的优先级 | 0 | 0=低优先级, 1=高优先级 |
4、示例代码
以下是如何在程序中配置IP寄存器的示例代码:
#include <reg51.h>
void main() {
// 将外部中断0 (INT0) 设置为高优先级
IP |= 0x01; // 设置 PX0 位为 1
// 将定时器0 (TF0) 设置为高优先级
IP |= 0x04; // 设置 PT0 位为 1
// 将外部中断1 (INT1) 设置为低优先级
IP &= ~0x02; // 清除 PX1 位
// 将定时器1 (TF1) 设置为低优先级
IP &= ~0x08; // 清除 PT1 位
// 将串行口中断 (RI/TI) 设置为低优先级
IP &= ~0x10; // 清除 PS 位
// 其他初始化代码...
}
5、高优先级中断抢占低优先级中断
- 高优先级中断可以抢占低优先级中断:如果当前正在处理一个低优先级中断,而此时发生了高优先级中断,CPU会暂停当前的低优先级中断服务程序(ISR),转而处理高优先级中断。
- 同级中断不能相互抢占:如果两个中断源具有相同的优先级,那么先发生的中断会被优先处理,后发生的中断需要等待前一个中断处理完毕后才能被响应。
- 嵌套中断:在某些情况下,可以允许中断嵌套,即在一个中断服务程序中再次进入另一个中断。这通常需要手动管理中断标志位和返回状态。
三、中断使能与禁止
中断允许寄存器 IE
| 位 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
|---|---|---|---|---|---|---|---|---|
| 功能 | EA | / | / | ES | ET1 | EX1 | ET0 | EX0 |
| 位 | 描述 | 默认值 | 功能 |
|---|---|---|---|
| EA | 全局中断使能 | 0 | 0=禁止所有中断, 1=允许所有已使能的中断 |
| ES | 串行口中断 (RI/TI) 使能 | 0 | 0=禁止串行口中断, 1=允许串行口中断 |
| ET1 | 定时器1 (TF1) 中断使能 | 0 | 0=禁止定时器1中断, 1=允许定时器1中断 |
| EX1 | 外部中断1 (INT1) 使能 | 0 | 0=禁止外部中断1, 1=允许外部中断1 |
| ET0 | 定时器0 (TF0) 中断使能 | 0 | 0=禁止定时器0中断, 1=允许定时器0中断 |
| EX0 | 外部中断0 (INT0) 使能 | 0 | 0=禁止外部中断0, 1=允许外部中断0 |
1、示例代码
#include <reg51.h>
void main() {
// 启用全局中断
EA = 1;
// 启用外部中断0 (INT0)
EX0 = 1;
// 启用定时器0 (TF0) 中断
ET0 = 1;
// 禁用外部中断1 (INT1)
EX1 = 0;
// 禁用定时器1 (TF1) 中断
ET1 = 0;
// 禁用串行口中断 (RI/TI)
ES = 0;
// 其他初始化代码...
}

2、中断控制LED灯
中断的响应条件:
- 中断源有中断请求
- 此中断的中断允许位为 1
- CPU 开中断 (即EA = 1)
同时满足时,CPU才有可能响应中断。
#include <REGX52.H>
unsigned char flag = 0; // 标志位定义,初始化为0
void Delay(unsigned int xms); // 延时函数
void main()
{
P2 = 0xfe; // 初始点亮LED 1111 1110
IT0 = 1; // INT0 下降沿触发
IT1 = 1; // INT1 下降沿触发
EA = 1; // 总中断允许
EX0 = 1; // 开启 INT0 中断
EX1 = 1; // 开启 INT1 中断
while(1)
{
if(flag == 1)
{
P2 = 0xfe; // 点亮LED
flag = 0; // 清除标志
}
if(flag == 2)
{
P2 = 0xFF; // 熄灭LED
flag = 0; // 清除标志
}
}
}
// INT0 中断函数(中断号 0)
void INT0_ISR() interrupt 0
{
// 不需要手动开关中断,51单片机会自动处理
flag = 1; // 设置为模式1
}
// INT1 中断函数(中断号 2)
void INT1_ISR() interrupt 2
{
flag = 2; // 设置为模式2
}
void Delay(unsigned int xms)
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
3、外部中断 1 控制流水灯
#include <REGX52.H>
#include <INTRINS.H> // 包含移位函数头文件
unsigned char flag = 0; // 标志位定义,初始化为0
void Delay(unsigned int xms); // 延时函数
void main()
{
P2 = 0xfe; // 初始点亮LED 1111 1110
IT1 = 1; // INT1 下降沿触发
EA = 1; // 总中断允许
EX1 = 1; // 开启 INT1 中断
while(1)
{
while(flag == 0)
{
P2 = _crol_(P2,1);
Delay(500);
}
}
}
// INT1 中断函数(中断号 2)
void INT1_ISR() interrupt 2
{
EX0 = 0;
flag = !flag;
EX0 = 1;
IE1 = 0; // 清除INT1中断标志
}
void Delay(unsigned int xms)
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
四、定时器中断
定时器/计数器的工作原理
STC89C5X 单片机内有两个可编程的定时/计数器 T0、T1 和一个特殊功能定时器 T2。定时/计数器的实质是加 1 计数器(16 位),由高 8 位和低 8 位两个寄存器 THx 和 TLx 组成。
它随着计数器的输入脉冲进行自加 1,也就是每来一个脉冲,计数器就自动加 1,当加到计数器为全 1 时,再输入一个脉冲就使计数器回零,且计数器的溢出使相应的中断标志位置 1,向 CPU 发出中断请求(定时/计数器中断允许时)。
如果定时/计数器工作于定时模式,则表示定时时间已到;如果工作于计数模式,则表示计数值已满。可见,由溢出时计数器的值减去计数初值才是加 1 计数器的计数值。









五、实践应用
1)定时器实现LED灯闪烁
#include <REGX52.H>
#define uchar unsigned char
#define uint unsigned int
uint num = 0;
sbit led0 = P2^0;
void main()
{
TMOD = 0x01; // 设置定时器 T0 工作方式 1
TH0 = (65536 - 18432) / 256; // 设置定时 500 ms 初值高 8 位
TL0 = (65536 - 18432) % 256; // 设置定时 500 ms 初值低 8 位
IE = 0x82; // 允许定时器 T0 工作
TR0 = 1; // T0运行位设置
// 机器周期 = 12 / 11.0592MHz ≈ 1.085μs
// 定时时间 = 18432 × 1.085μs ≈ 19999.872μs ≈ 20.000ms
while(1)
{
if(num == 25) // 20 * 25 = 500ms
{
num = 0;
led0 = ~led0;
}
}
}
// 定时器中断程序
void T0_TIMER() interrupt 1
{
TH0 = (65536 - 18432) / 256; // 设置定时 500 ms 初值高 8 位
TL0 = (65536 - 18432) % 256; // 设置定时 500 ms 初值低 8 位
num++;
}
常用定时器初值速查表(12MHz晶振)
| 定时时间 | 计数值 | 初值(十进制) | TH0 | TL0 |
|---|---|---|---|---|
| 1ms | 1000 | 64536 | 252 | 24 |
| 5ms | 5000 | 60536 | 236 | 120 |
| 10ms | 10000 | 55536 | 217 | 16 |
| 18.432ms | 18432 | 47104 | 184 | 0 |
| 20ms | 20000 | 45536 | 177 | 224 |
| 50ms | 50000 | 15536 | 60 | 176 |
| 65.536ms | 65536 | 0 | 0 | 0 |
/**
* @file Timer0_LED_Blink.c
* @brief 使用定时器0实现1秒LED闪烁
* @details
* - 系统时钟:12MHz,每个机器周期1μs
* - 定时器0:模式1(16位定时器)
* - 定时时间:1ms
* - LED闪烁周期:1秒(1000次中断翻转一次)
*/
#include <REGX52.H> // 包含51单片机寄存器定义头文件
/* 全局变量定义 */
unsigned int T0Count = 0; // 定时器中断次数计数器,用于累计1ms中断次数
/* 函数声明 */
void Timer0_Init(void); // 定时器0初始化函数
/**
* @brief 主函数
* @note 初始化定时器后进入无限循环,LED闪烁由中断处理
*/
void main(void)
{
Timer0_Init(); // 调用定时器初始化函数
while(1) // 主循环
{
// 此处可以添加其他非时间敏感的任务
// LED闪烁在中断服务程序中处理,不占用主循环时间
}
}
/**
* @brief 定时器0初始化函数
* @details 配置定时器0为模式1(16位定时器),定时1ms
* 配置步骤:
* 1. 设置定时器模式
* 2. 设置定时器初值
* 3. 使能中断
* 4. 启动定时器
*/
void Timer0_Init(void)
{
/* 第一步:设置定时器0工作模式 */
// TMOD寄存器:定时器模式寄存器
// TMOD = 0x01 对应二进制 0000 0001
// 低4位用于定时器0,高4位用于定时器1
// 定时器0设置为模式1:16位定时器模式(TH0+TL0)
TMOD = 0x01;
/* 第二步:设置定时器初值 */
// TF0:定时器0溢出标志,初始清零
TF0 = 0;
// 计算并设置定时器初值(1ms定时,12MHz晶振)
// 计算公式:初值 = 65536 - (定时时间/机器周期)
// 定时时间 = 1ms = 1000μs
// 机器周期 = 1μs(12MHz晶振)
// 初值 = 65536 - 1000 = 64536 = 0xFC18
// 注意:原代码使用64535,这里修正为标准的64536
// TH0 = 高8位 = 0xFC = 64536 / 256 = 252
// TL0 = 低8位 = 0x18 = 64536 % 256 = 24
TH0 = 64536 / 256; // 设置高8位
TL0 = 64536 % 256; // 设置低8位
/* 第三步:使能中断 */
// ET0:定时器0中断使能位,置1使能
ET0 = 1;
// EA:总中断使能位,置1打开总中断
EA = 1;
// PT0:定时器0中断优先级,0为低优先级,1为高优先级
PT0 = 0; // 设置为低优先级
/* 第四步:启动定时器0 */
// TR0:定时器0运行控制位,置1启动定时器
TR0 = 1;
}
/**
* @brief 定时器0中断服务函数
* @details 每1ms执行一次,累计1000次后翻转LED状态
* @note interrupt 1 表示定时器0中断(中断向量号为1)
*/
void Timer0_Routine(void) interrupt 1
{
/* 第一步:重新装入定时器初值 */
// 模式1不是自动重装模式,需要手动重装初值
TH0 = 64536 / 256; // 重新装入高8位
TL0 = 64536 % 256; // 重新装入低8位
/* 第二步:中断次数计数 */
T0Count++; // 中断次数加1
/* 第三步:判断是否达到1000次(1秒) */
if (T0Count >= 1000) // 1000次×1ms = 1000ms = 1秒
{
T0Count = 0; // 计数器清零
/* 第四步:翻转LED状态 */
// P2_0是P2口的第0位,对应一个LED
// ~P2_0:读取P2_0的当前值并取反,实现翻转
P2_0 = ~P2_0; // LED状态翻转
}
}


2)设计定时器流水灯
/**
* @file LED_Control.c
* @brief 使用定时器和按键控制LED流水灯
* @details
* - 使用Timer0实现500ms定时
* - 按键K1切换LED流动方向
* - 晶振:11.0592MHz
*/
#include <REGX52.H>
#include <INTRINS.H>
// 类型重定义
#define uchar unsigned char
#define uint unsigned int
// 全局变量
uchar KeyNum = 0; // 按键键值
uchar LEDMode = 0; // LED模式:0=左移,1=右移
sbit led0 = P2^0; // P2.0引脚
// 函数声明
void Timer0_Init(void); // 定时器0初始化
uchar Key(void); // 按键扫描函数
void Delay(uint xms); // 延时函数
/**
* @brief 主函数
*/
void main(void)
{
P2 = 0xFE; // 初始状态:P2.0亮,其他灭(1111 1110)
Timer0_Init(); // 初始化定时器0
while(1) // 主循环
{
KeyNum = Key(); // 获取按键键值
if(KeyNum) // 如果有按键按下
{
if(KeyNum == 1) // 如果K1按键按下
{
LEDMode++; // 模式切换
if(LEDMode >= 2) // 只有0和1两种模式
{
LEDMode = 0;
}
}
// 可以添加其他按键的处理
}
// 可以添加其他主循环任务
}
}
/**
* @brief 定时器0初始化
* @note 使用11.0592MHz晶振,定时20ms
*/
void Timer0_Init(void)
{
TMOD &= 0xF0; // 清零Timer0位
TMOD |= 0x01; // 设置Timer0为模式1(16位定时器)
// 计算20ms定时初值(11.0592MHz)
// 机器周期 = 12 / 11.0592MHz ≈ 1.085μs
// 20ms = 20000μs,需要计数值 = 20000 / 1.085 ≈ 18432
// 初值 = 65536 - 18432 = 47104 = 0xB800
TH0 = 0xB8; // 高8位:0xB8 = 184
TL0 = 0x00; // 低8位:0x00 = 0
TF0 = 0; // 清除溢出标志
ET0 = 1; // 允许Timer0中断
EA = 1; // 开总中断
TR0 = 1; // 启动定时器
}
/**
* @brief 定时器0中断服务函数
* @note 每20ms中断一次,通过计数实现500ms定时
*/
void T0_TIMER() interrupt 1
{
static uint T0Count = 0; // 中断次数计数器
// 重新装入初值(20ms定时)
TH0 = 0xB8; // 高8位
TL0 = 0x00; // 低8位
T0Count++; // 中断次数加1
if(T0Count >= 25) // 20ms × 25 = 500ms
{
T0Count = 0; // 计数器清零
// 根据模式控制LED流动
if(LEDMode == 0) // 模式0:左移
{
P2 = _crol_(P2, 1); // 循环左移1位
}
else if(LEDMode == 1) // 模式1:右移
{
P2 = _cror_(P2, 1); // 循环右移1位
}
}
}
/**
* @brief 按键扫描函数
* @return 按键键值:1=K1, 2=K2, 3=K3, 4=K4, 0=无按键
*/
uchar Key(void)
{
uchar KeyNumber = 0;
// 检测K1(P3.1)
if(P3_1 == 0)
{
Delay(20); // 消抖延时
while(P3_1 == 0); // 等待按键释放
Delay(20); // 释放消抖
KeyNumber = 1;
}
// 检测K2(P3.0)
if(P3_0 == 0)
{
Delay(20);
while(P3_0 == 0);
Delay(20);
KeyNumber = 2;
}
// 检测K3(P3.2)
if(P3_2 == 0)
{
Delay(20);
while(P3_2 == 0);
Delay(20);
KeyNumber = 3;
}
// 检测K4(P3.3)
if(P3_3 == 0)
{
Delay(20);
while(P3_3 == 0);
Delay(20);
KeyNumber = 4;
}
return KeyNumber;
}
/**
* @brief 延时函数
* @param xms 延时毫秒数
* @note 粗略延时,不精确
*/
void Delay(uint xms)
{
uint i, j;
for(i = xms; i > 0; i--)
{
for(j = 110; j > 0; j--);
}
}
3) 定时器时钟
main.c
#include <REGX52.H>
#include "LCD1602.h"
#include "Timer0.h"
unsigned char Sec,Min,Hour;
void main()
{
LCD_Init();
Timer0Init();
LCD_ShowString(1,1,"Clock:"); //上电显示静态字符串
LCD_ShowString(2,1," : :");
while(1)
{
LCD_ShowNum(2,1,Hour,2); //显示时分秒
LCD_ShowNum(2,4,Min,2);
LCD_ShowNum(2,7,Sec,2);
}
}
void Timer0_Routine() interrupt 1
{
static unsigned int T0Count;
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
T0Count++;
if(T0Count>=1000) //定时器分频,1s
{
T0Count=0;
Sec++; //1秒到,Sec自增
if(Sec>=60)
{
Sec=0; //60秒到,Sec清0,Min自增
Min++;
if(Min>=60)
{
Min=0; //60分钟到,Min清0,Hour自增
Hour++;
if(Hour>=24)
{
Hour=0; //24小时到,Hour清0
}
}
}
}
}
LCD1602.c
#include <REGX52.H>
//引脚定义
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_E=P2^7;
#define LCD_DataPort P0
/**
* @brief LCD1602延时函数,12MHz调用可延时1ms
* @param 无
* @retval 无
*/
void LCD_Delay() //@12.000MHz 1ms
{
unsigned char i, j;
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
/**
* @brief LCD1602写命令
* @param Command 要写入的命令
* @retval 无
*/
void LCD_WriteCommand(unsigned char Command)
{
LCD_RS=0;
LCD_RW=0;
LCD_DataPort=Command;
LCD_E=1;
LCD_Delay();
LCD_E=0;
LCD_Delay();
}
/**
* @brief LCD1602写数据
* @param Data 要写入的数据
* @retval 无
*/
void LCD_WriteData(unsigned char Data)
{
LCD_RS=1;
LCD_RW=0;
LCD_DataPort=Data;
LCD_E=1;
LCD_Delay();
LCD_E=0;
LCD_Delay();
}
/**
* @brief LCD1602初始化函数
* @param 无
* @retval 无
*/
void LCD_Init(void)
{
LCD_WriteCommand(0x38);
LCD_WriteCommand(0x0C);
LCD_WriteCommand(0x06);
LCD_WriteCommand(0x01);
}
/**
* @brief LCD1602设置光标位置
* @param Line 行位置,范围:1~2
* @param Column 列位置,范围:1~16
* @retval 无
*/
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
if(Line==1)
{
LCD_WriteCommand(0x80|(Column-1));
}
else
{
LCD_WriteCommand(0x80|(Column-1)+0x40);
}
}
/**
* @brief 在LCD1602指定位置上显示一个字符
* @param Line 行位置,范围:1~2
* @param Column 列位置,范围:1~16
* @param Char 要显示的字符
* @retval 无
*/
void LCD_ShowChar(unsigned char Line,unsigned char Column,unsigned char Char)
{
LCD_SetCursor(Line,Column);
LCD_WriteData(Char);
}
/**
* @brief 在LCD1602指定位置开始显示所给字符串
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param String 要显示的字符串
* @retval 无
*/
void LCD_ShowString(unsigned char Line,unsigned char Column,unsigned char *String)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=0;String[i]!='\0';i++)
{
LCD_WriteData(String[i]);
}
}
/**
* @brief 返回值=X的Y次方
*/
int LCD_Pow(int X,int Y)
{
unsigned char i;
int Result=1;
for(i=0;i<Y;i++)
{
Result*=X;
}
return Result;
}
/**
* @brief 在LCD1602指定位置开始显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~65535
* @param Length 要显示数字的长度,范围:1~5
* @retval 无
*/
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
LCD_WriteData('0'+Number/LCD_Pow(10,i-1)%10);
}
}
/**
* @brief 在LCD1602指定位置开始以有符号十进制显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:-32768~32767
* @param Length 要显示数字的长度,范围:1~5
* @retval 无
*/
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{
unsigned char i;
unsigned int Number1;
LCD_SetCursor(Line,Column);
if(Number>=0)
{
LCD_WriteData('+');
Number1=Number;
}
else
{
LCD_WriteData('-');
Number1=-Number;
}
for(i=Length;i>0;i--)
{
LCD_WriteData('0'+Number1/LCD_Pow(10,i-1)%10);
}
}
/**
* @brief 在LCD1602指定位置开始以十六进制显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~0xFFFF
* @param Length 要显示数字的长度,范围:1~4
* @retval 无
*/
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i;
unsigned char SingleNumber;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
SingleNumber=Number/LCD_Pow(16,i-1)%16;
if(SingleNumber<10)
{
LCD_WriteData('0'+SingleNumber);
}
else
{
LCD_WriteData('A'+SingleNumber-10);
}
}
}
/**
* @brief 在LCD1602指定位置开始以二进制显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~1111 1111 1111 1111
* @param Length 要显示数字的长度,范围:1~16
* @retval 无
*/
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
LCD_WriteData('0'+Number/LCD_Pow(2,i-1)%2);
}
}
LCD1602.h
#ifndef __LCD1602_H__
#define __LCD1602_H__
void LCD_Init(void);
void LCD_ShowChar(unsigned char Line,unsigned char Column,unsigned char Char);
void LCD_ShowString(unsigned char Line,unsigned char Column,unsigned char *String);
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
#endif
Time0.c
#include <REGX52.H>
/**
* @brief 定时器0初始化,1毫秒@12.000MHz
* @param 无
* @retval 无
*/
void Timer0Init(void)
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0=1;
EA=1;
PT0=0;
}
Time0.h
#ifndef __TIME0_H__
#define __TIME0_H__
void Timer0_Init(void);
#endif

浙公网安备 33010602011771号