目录
[3. 硬件设计](#3. 硬件设计)
[4. 软件设计](#4. 软件设计)
[4.1 smg.c 与 smg.h](#4.1 smg.c 与 smg.h)
[4.2 ired.c 与 ired.h](#4.2 ired.c 与 ired.h)
[4.2.1 NEC 红外协议基础](#4.2.1 NEC 红外协议基础)
[4.2.2 全局变量](#4.2.2 全局变量)
[4.2.3 初始化函数](#4.2.3 初始化函数)
[4.2.4 中断服务函数](#4.2.4 中断服务函数)
[4.3 main.c](#4.3 main.c)
[4.4 public.c 与 public.h](#4.4 public.c 与 public.h)
3. 硬件设计
① 动态数码管
② 红外接收头和遥控器
红外接收头和遥控器是一体的,内部结构不用管,下面是开发板上红外接收模块电路

红外接收头的输出管脚接至 P3.2 管脚,为了保证红外接收头输出管脚默认为高电平,
需外接一个 10K 上拉电阻
4. 软件设计
实验目的:数码管上显示红外解码遥控器键值
4.1 smg.c 与 smg.h
smg.c
cpp
#include "smg.h"
//共阴极数码管显示 0-F 的段码数据
u8 gsmg_code[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
/*******************************************************************************
* 函 数 名 : smg_display
* 函数功能 : 动态数码管显示
* 输 入 : dat:要显示的数据
pos:从左开始第几个位置开始显示,范围1-8
* 输 出 : 无
*******************************************************************************/
void smg_display(u8 dat[],u8 pos)
{
u8 i=0;
u8 pos_temp=pos-1;//转换为数组对应索引
for(i=pos_temp;i<8;i++)
{
switch(i)//位选
{
case 0: LSC=1;LSB=1;LSA=1;break;
case 1: LSC=1;LSB=1;LSA=0;break;
case 2: LSC=1;LSB=0;LSA=1;break;
case 3: LSC=1;LSB=0;LSA=0;break;
case 4: LSC=0;LSB=1;LSA=1;break;
case 5: LSC=0;LSB=1;LSA=0;break;
case 6: LSC=0;LSB=0;LSA=1;break;
case 7: LSC=0;LSB=0;LSA=0;break;
}
SMG_A_DP_PORT=dat[i-pos_temp];//传送段选数据
delay_10us(100);//延时一段时间,等待显示稳定
SMG_A_DP_PORT=0x00; //消影
}
}
smg.h
cpp
#ifndef _smg_H
#define _smg_H
#include "public.h"
#define SMG_A_DP_PORT P0 //使用宏定义数码管段码口
//定义数码管位选信号控制脚
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;
extern u8 gsmg_code[17];
void smg_display(u8 dat[],u8 pos);
#endif
代码理解如下:
pos: 指定显示的数码管的起始位置
dat[ ]:显示的内容
① 数码管从第 i+1 个数码管开始显示,一直到第 8个,为什么结果只有几个灯亮?
cpp
for(i=pos_temp;i<8;i++)
② 如何进行该循环?
cpp
for(i=pos_temp;i<8;i++)
{
switch(i)//位选
{
case 0: LSC=1;LSB=1;LSA=1;break;
case 1: LSC=1;LSB=1;LSA=0;break;
case 2: LSC=1;LSB=0;LSA=1;break;
case 3: LSC=1;LSB=0;LSA=0;break;
case 4: LSC=0;LSB=1;LSA=1;break;
case 5: LSC=0;LSB=1;LSA=0;break;
case 6: LSC=0;LSB=0;LSA=1;break;
case 7: LSC=0;LSB=0;LSA=0;break;
}
SMG_A_DP_PORT=dat[i-pos_temp];//传送段选数据
delay_10us(100);//延时一段时间,等待显示稳定
SMG_A_DP_PORT=0x00; //消影
}
4.2 ired.c 与 ired.h
ired.c
核心逻辑:
中断触发→检测引导码→逐位解析 4 字节数据(按 NEC 时序区分 0/1、处理位顺序)→校验控制码→缓存有效数据
本质是把红外接收头的模拟电平信号,按 NEC 协议规则转换为可识别的 4 字节数字码,供主程序判断 "按下了哪个按键"(比如读取 gired_data[2] 的值,判断是电源键 / 音量键等)
这是 ired.c 文件
cpp
#include "ired.h"
u8 gired_data[4];//存储4个字节接收码(地址码+地址反码+控制码+控制反码)
/*******************************************************************************
* 函 数 名 : ired_init
* 函数功能 : 红外端口初始化函数,外部中断0配置
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void ired_init(void)
{
IT0=1; //下降沿触发
EX0=1; //打开中断0允许
EA=1; //打开总中断
IRED=1; //初始化端口
}
void ired() interrupt 0 //外部中断0服务函数
{
u8 ired_high_time=0;
u16 time_cnt=0;
u8 i=0,j=0;
if(IRED==0)
{
time_cnt=1000;
while((!IRED)&&(time_cnt))//等待引导信号9ms低电平结束,若超过10ms强制退出
{
delay_10us(1);//延时约10us
time_cnt--;
if(time_cnt==0)return;
}
if(IRED)//引导信号9ms低电平已过,进入4.5ms高电平
{
time_cnt=500;
while(IRED&&time_cnt)//等待引导信号4.5ms高电平结束,若超过5ms强制退出
{
delay_10us(1);
time_cnt--;
if(time_cnt==0)return;
}
for(i=0;i<4;i++)//循环4次,读取4个字节数据
{
for(j=0;j<8;j++)//循环8次读取每位数据即一个字节
{
time_cnt=600;
while((IRED==0)&&time_cnt)//等待数据1或0前面的0.56ms结束,若超过6ms强制退出
{
delay_10us(1);
time_cnt--;
if(time_cnt==0)return;
}
time_cnt=20;
while(IRED)//等待数据1或0后面的高电平结束,若超过2ms强制退出
{
delay_10us(10);//约0.1ms
ired_high_time++;
if(ired_high_time>20)return;
}
gired_data[i]>>=1;//先读取的为低位,然后是高位
if(ired_high_time>=8)//如果高电平时间大于0.8ms,数据则为1,否则为0
gired_data[i]|=0x80;
ired_high_time=0;//重新清零,等待下一次计算时间
}
}
}
if(gired_data[2]!=~gired_data[3])//校验控制码与反码,错误则返回
{
for(i=0;i<4;i++)
gired_data[i]=0;
return;
}
}
}
ired.h
cpp
#ifndef _ired_H
#define _ired_H
#include "public.h"
//管脚定义
sbit IRED=P3^2;
//声明变量
extern u8 gired_data[4];
//函数声明
void ired_init(void);
#endif
代码理解如下:
4.2.1 NEC 红外协议基础
红外通信是用红外线(不可见光,波长约 940nm)传输数据,就像两个人用暗号说话:
「说话的人」:遥控器里的红外发射管(通电后发射 38kHz 的红外光);
「听话的人」:电视/空调里的红外接收头(只认 38kHz 的红外光,过滤阳光 / 灯光干扰);
「暗号规则」:就是 NEC 协议(规定 "怎么说""怎么认")。

4.2.2 全局变量
缓存红外接收的一帧完整数据
cpp
u8 gired_data[4];//存储4个字节接收码(地址码+地址反码+控制码+控制反码)
gired_data[0]:红外遥控的地址码(比如电视 / 空调的设备地址);gired_data[1]:地址码的反码(用于校验地址是否正确,代码中未校验,仅校验了控制码);gired_data[2]:控制码(比如 "音量 +""电源" 等按键对应的码值);gired_data[3]:控制码的反码(核心校验位)。
4.2.3 初始化函数
ired_init():红外接收头(如 HS0038)无信号时输出高电平,收到红外信号(引导码低电平)时会产生下降沿,触发外部中断 0,进入解析流程。
cpp
void ired_init(void)
{
IT0=1; //下降沿触发:外部中断0由引脚电平"高→低"触发(红外引导码开始是低电平,符合触发条件)
EX0=1; //打开外部中断0的允许开关
EA=1; //打开总中断(51单片机所有中断的总开关,必须开启)
IRED=1; //初始化红外接收引脚为高电平(红外接收头无信号时输出高电平)
}
4.2.4 中断服务函数
ired()(核心解析逻辑)
这份代码是51 单片机基于外部中断的 NEC 红外协议解码函数,整体思路可总结为:
检测引导码(9ms+4.5ms)→ 过滤干扰;
逐位解析 4 字节数据(地址码、地址反码、数据码、数据反码);
反码校验(地址码和地址反码匹配、数据码和数据反码匹配);
校验通过后,把数据码存到gired_data[2]里;
这个思路是嵌入式 "硬件时序解析" 的典型范式:先过滤无效信号,再解析有效数据,最后校验兜底,全程防卡死
1. 「引导码」
接收端(描述 2):先检查是不是「9ms+4.5ms」的引导码("双层过滤筛除干扰")。
✅ 对应逻辑:发射端发引导码是为了 "标记有效信号开头",接收端先校验引导码,是为了过滤掉不是 NEC 协议的干扰信号(比如阳光、灯光的杂波)------ 本质是同一回事的 "发" 和 "验"。
2. 「4 字节数据」
接收端(描述 2):"按 NEC 数据位时序逐位解析 4 字节数据"------ 解析的就是发射端发的这 4 字节(8 位 = 1 字节,4×8=32 位)。
3. 「反码校验」的对应
接收端(描述 2):"通过反码校验保证数据正确"------ 就是核对 "地址码和地址反码是否互补、数据码和数据反码是否互补"。
4. 「超时保护」的补充(为什么发射端没提)
"全程用超时保护避免程序卡死",是接收端专属的逻辑(发射端不需要):
- 比如电视在解析信号时,如果解析到一半,信号突然断了(比如遥控器被挡住),总不能一直等着解析,会导致电视的程序卡死;
- 所以接收端会设置 "超时时间"(比如超过 10ms 没收到下一位信号,就判定信号无效,直接放弃解析);
- 发射端只需要按固定时序发信号就行,不存在 "等信号" 的情况,所以这是接收端的 "额外保护逻辑"。
5. 中断
NEC 协议解析的核心是 "检测电平跳变(比如发射→不发射)+ 计算跳变之间的时长",对应 51 单片机的两类中断:

中断函数是 "特殊函数",格式固定,单片机检测到中断信号会自动跳转到这里执行:

interrupt 中断号:指定是哪类中断(51 的固定规则):- 外部中断 0 → 中断号 0;
- 定时器 0 中断 → 中断号 1;
- 外部中断 1 → 中断号 2;
- 定时器 1 中断 → 中断号 3;
- 不需要返回值(void),也不能传参;
using 寄存器组:可选,一般省略。
6. 外部中断 0 函数(ired_int)
红外接收头的电平每跳变一次(比如从低→高、高→低),就会触发该中断函数
cpp
void ired() interrupt 0 //外部中断 0 服务函数,中断号为 0
{
// 局部变量定义
u8 ired_high_time=0; // 统计数据位高电平的时长(判断是0/1)
u16 time_cnt=0; // 超时计数器(防止死等)
u8 i=0,j=0;
// 第一步:检测引导码的起始低电平(必须先有低电平才是有效信号)
if(IRED==0)
{
time_cnt=1000;
while((!IRED)&&(time_cnt))//低电平结束 或 超时,则退出循环
{
delay_10us(1);//每次延时约10us
time_cnt--; //待 time_cnt 执行完用时大约 10ms
if(time_cnt==0) return;
}
if(IRED)//引导信号9ms低电平已过,进入4.5ms高电平
{
time_cnt=500;
while(IRED&&time_cnt)//等待引导信号4.5ms高电平结束,若超过5ms强制退出
{
delay_10us(1);
time_cnt--;
if(time_cnt==0) return;
}
for(i=0;i<4;i++)//循环4次,读取4个字节数据
{
for(j=0;j<8;j++)//循环8次读取每位数据即一个字节
{
time_cnt=600;
while((IRED==0)&&time_cnt)//等待数据1或0前面的0.56ms结束,若超过6ms强制退出
{
delay_10us(1);
time_cnt--;
if(time_cnt==0)return;
}
time_cnt = 20;
while(IRED)//等待数据1/0后面的高电平结束,若超过 2ms 强制退出
{
delay_10us(10); // 约0.1ms
ired_high_time++; // 统计高电平持续的"0.1ms个数"
if(ired_high_time>20) return;
}
gired_data[i] >>= 1;//先读取的为低位,然后是高位
if(ired_high_time >= 8)//若高电平时间大于0.8ms,数据则为1,则把1置最高位
gired_data[i] |= 0x80;//把其最高位置1,其余位保持不变(0和任何位或都不变,1和任何位或都为1)
ired_high_time=0;//重新清零,等待下一次计算时间
}
}
}
if(gired_data[2]!=~gired_data[3])//校验控制码与反码,错误则返回
{
for(i=0;i<4;i++)
gired_data[i]=0;
return;
}
}
}
以 10μs 为时间粒度,等待红外接收头的 9ms( NEC 协议要求引导码低电平是 9ms**) 低电平结束;若等待时间超过 10ms(判定为无效信号),则直接退出中断函数,避免程序卡死。**
这是红外解码中 "过滤无效信号、保证时序准确性" 的关键环节,核心是「时序检测 + 超时保护」的组合设计 ------ 这也是嵌入式代码中处理硬件时序的典型思路。
红外接收头可能收到干扰信号(比如灯光),只有 "9ms 低 + 4.5ms 高" 的引导码才是 NEC 协议的有效帧开头,过滤无效干扰。
① 嵌入式代码中,while 循环的条件往往是 "硬件状态 + 超时保护 " 的组合,硬件状态(这里的!IRED)是核心,超时保护(time_cnt)是兜底;
任何嵌入式时序检测的代码,都不能脱离 "硬件电平 与 状态判断"
② 正常情况下:9ms 低电平后变为高电平便退出循环,不会低电平持续到 10 ms
③ 超时保护是核心 :若没有 time_cnt 超时机制,一旦红外引脚因短路 / 强干扰一直为低电平,程序会永远卡在 while 循环中,单片机无法执行其他任务 ------ 这是嵌入式代码的 "鲁棒性设计"(避免死循环)。
④ 时间余量合理 :超时设为 10ms(比 9ms 多 1ms),而非刚好 9ms:因为实际硬件中,延时函数可能有 ±10% 的误差(比如delay_10us(1)实际延时 11μs),留余量能避免 "标准 9ms 低电平却被误判为超时"。
⑤ delay_10us(1):红外时序的最小单位是 0.56ms(560μs),用 10μs 的粒度检测,既能保证精度,又不会因粒度太细导致循环次数过多(比如用 1μs 粒度的话,9ms 需要 9000 次循环,效率低)
cpp
time_cnt=1000;
while((!IRED)&&(time_cnt))//低电平结束 或 超时,则退出循环
{
delay_10us(1);//每次延时约10us
time_cnt--; //待 time_cnt 执行完用时大约 10ms
if(time_cnt==0) return;
}
这是 NEC 红外协议引导码第二阶段 ------4.5ms 高电平 的检测逻辑,同时加入 5ms 超时保护:
等待引导码的 4.5ms 高电平自然结束(红外接收引脚 IRED 从高→低);
cpp
if(IRED)//引导信号9ms低电平已过,进入4.5ms高电平
{
time_cnt=500;
while(IRED&&time_cnt)//等待引导信号4.5ms高电平结束,若超过5ms强制退出
{
delay_10us(1);
time_cnt--;
if(time_cnt==0) return;
}
该代码与前序" 9ms 低电平检测 "完全遵循同一套嵌入式时序模板,抓住 "电平条件对应协议时序,计数器对应超时保护" 这两个核心,这也是所有嵌入式硬件时序解析的通用思路。,仅电平判断相反,可总结通用范式:
cpp
// 通用:检测"目标电平时长",加"略大的超时保护"
time_cnt = 超时阈值; // 目标时长×10μs × 1.1(余量)
while(目标电平条件 && time_cnt)
{
delay_10us(1); // 固定时间粒度
time_cnt--;
if(time_cnt==0) return; // 超时退出
}
下面是 for 循环代码:
cpp
for(i=0;i<4;i++)//循环4次,读取4个字节数据
{
for(j=0;j<8;j++)//循环8次读取每位数据即一个字节
{
time_cnt=600;
while((IRED==0)&&time_cnt)//等待数据1或0前面的0.56ms结束,若超过6ms强制退出
{
delay_10us(1);
time_cnt--;
if(time_cnt==0)return;
}
time_cnt=20;
while(IRED)//等待数据1或0后面的高电平结束,若超过2ms强制退出
{
delay_10us(10);//约0.1ms
ired_high_time++;
if(ired_high_time>20)return;
}
gired_data[i]>>=1;//先读取的为低位,然后是高位
if(ired_high_time>=8)//如果高电平时间大于0.8ms,数据则为1,否则为0
gired_data[i]|=0x80;
ired_high_time=0;//重新清零,等待下一次计算时间
}
}
① NEC 协议数据位的时序规则
核心逻辑:低电平时长固定,通过高电平时长区分 0 和 1

②
cpp
time_cnt=20;
while(IRED)//等待数据1/0后面的高电平结束,若超过 2ms 强制退出
{
delay_10us(10); // 约0.1ms
ired_high_time++; // 统计高电平持续的"0.1ms个数"
if(ired_high_time>20) return;
}
这段代码的核心目标不是 "单纯等待高电平结束",而是 **"统计高电平的持续时长" ** ------ 通用模板的 "time_cnt--" 是 "倒计时"(关注剩余时间),而这里需要 "正计时"(关注已持续时间),因此调整了写法
通用模板(如引导码检测)的目标是 "等电平结束,不关心具体时长",只需 "超时就退出",因此用time_cnt 倒计时即可;
这段代码(数据位检测)的目标是 "统计高电平到底持续了多久"(用来区分 0/1),因此需要ired_high_time 正计时(从 0 开始累加),直接获取时长数值
cpp
gired_data[i]>>=1;//先读取的为低位,然后是高位
if(ired_high_time>=8)//如果高电平时间大于0.8ms,数据则为1,否则为0
gired_data[i]|=0x80;
ired_high_time=0;//重新清零,等待下一次计算时间
① |= 0x80 的位运算含义
0x80的二进制是1000 0000(仅最高位为 1,其余位为 0);
|=是 "按位或赋值运算符":只把gired_data[i]的最高位置 1,其余位保持不变(因为 0 和任何位或都不变,1 和任何位或都为 1)。
② 为什么要 "置最高位"?
因为我们先做了 gired_data[i] >>= 1(右移),后续每解析一位都会右移一次 ------先把 1 放到最高位,后续右移会逐步把这个 1 "推" 到低位,刚好适配 "先传低位" 的规则。
比如:
- 第 1 次解析出 "1"(最低位):置最高位→
1000 0000; - 第 2 次解析出 "0":右移→
0100 0000,不置 1→保持0100 0000; - 第 3 次解析出 "0":右移→
0010 0000,不置 1→保持0010 0000; - ......
- 第 8 次解析完成:最终
0000 0001(刚好是先传的低位 1 落到最低位)。
cpp
if(gired_data[2]!=~gired_data[3])//校验控制码与反码,错误则返回
{
for(i=0;i<4;i++)
gired_data[i]=0;
return;
}
① 核心背景:NEC 协议的反码校验规则
NEC 红外协议的一帧数据包含 4 字节:
gired_data[0]:地址码(设备唯一标识,比如电视 / 空调的地址)
gired_data[1]:地址反码(地址码的按位取反)
gired_data[2]:控制码(按键功能,比如 "电源""音量 +")
gired_data[3]:控制反码(控制码的按位取反)
协议设计反码的核心目的:校验数据传输是否出错------ 红外信号易受灯光、距离干扰,传输过程中可能出现位翻转(比如 1 变 0),通过 "原码≠~ 反码" 可快速判定数据无效。
② 校验失败的处理:for(i=0;i<4;i++) gired_data[i]=0
作用:清空整个红外数据数组(地址码、地址反码、控制码、控制反码全部置 0);
必要性:若数据出错,保留错误值会导致主程序误判(比如误识别成其他按键),清空后主程序可通过 "gired_data[2]==0" 判定 "无有效按键"。
③ 强制退出:return
作用:直接退出整个红外中断服务函数,不再执行后续任何逻辑;
逻辑:既然控制码校验失败,说明整帧数据都不可信,无需继续处理,直接退出以节省 CPU 资源。
③ 为什么只校验控制码,不校验地址码?
代码中仅校验了gired_data[2](控制码)和gired_data[3](控制反码),未校验gired_data[0](地址码)和gired_data[1](地址反码),原因:
- 简化逻辑:地址码用于区分设备(比如只接收 "自家遥控器" 的信号),若仅做 "按键功能识别",可省略地址码校验;
- 资源节省:红外中断函数需快速执行,减少校验步骤可降低 CPU 占用;
- 可优化:工业级代码会补充地址码校验(
if(gired_data[0]!=~gired_data[1])),进一步保证数据有效性。
④ 清空数组的必要性
若校验失败但不清空数组,gired_data中会残留错误数据:
- 比如控制码错成
0x13,主程序可能误判为 "音量 +"(本该是 "电源"); - 清空为 0 后,主程序可通过 "
gired_data[2]==0" 过滤无效按键,避免误操作。
4.3 main.c
cpp
/**************************************************************************************
实验名称:红外遥控实验
接线说明:
实验现象:下载程序后,数码管上显示数码管上显示红外解码遥控器键值
注意事项:红外接收头凸起处要与PCB板接口凸起丝印处对应
***************************************************************************************/
#include "public.h"
#include "smg.h"
#include "ired.h"
/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void main()
{
u8 ired_buf[3];//定义数组:存3个数码管段码(高4位、低4位、字母H)
ired_init();//红外初始化
/*初始化做了啥?→ 配置红外接收头的IO口为输入、开启定时器/中断,
让单片机能检测NEC协议的时序(9ms引导码、0/1的时长等),为解码做准备*/
while(1)
{
//将控制码高 4 位转换为数码管段码
ired_buf[0]=gsmg_code[gired_data[2]/16];
//将控制码低 4 位转换为数码管段码
ired_buf[1]=gsmg_code[gired_data[2]%16];
//显示 H 的段码:0X76 是共阴极数码管显示 H 的固定编码
ired_buf[2]=0X76;
//把3个段码显示到数码管上(6:第六个数码管开始显示)
smg_display(ired_buf,6);
}
}
① 数据码拆分为高低 4 位的原因
cpp
//将控制码高 4 位转换为数码管段码
ired_buf[0]=gsmg_code[gired_data[2]/16];
//将控制码低 4 位转换为数码管段码
ired_buf[1]=gsmg_code[gired_data[2]%16];
比如数据是 0x12(0001 0010),0x12/16=1(高四位:0001),gsmg_code[1] 就是"数字1"对应的数码管段码(0x06)
0x12%16=2(低四位:0010),gsmg_code[2] 就是"数字2"对应的数码管段码(0x5B)
数码管一般是 "一位显示一个数字 / 字母",而 NEC 数据码是 8 位(1 字节,比如 0x12),拆成高 4 位(1)和低 4 位(2),就能用两位数码管分别显示,再加一位显示 "H"(表示十六进制),最终显示 "12H"。
② 实验现象的底层逻辑
按遥控器电源键→遥控器发 NEC 信号(引导码 + 地址码 + 数据码 0x01 + 反码);
单片机接收并解码→gired_data[2] = 0x01;
代码计算:0x01/16=0(高 4 位)、0x01%16=1(低 4 位);
数码管显示:0(第一位)、1(第二位)、H(第三位)→ 最终看到 "01H"。
③ 中断函数和主函数的关联(核心!)
cpp
ired_buf[0]=gsmg_code[gired_data[2]/16]; // 取数据码高4位
ired_buf[1]=gsmg_code[gired_data[2]%16]; // 取数据码低4位
gired_data[2]的值是中断函数解析后写入的!- 主函数只需要在死循环里读取
gired_data[2],不需要关心解析过程(中断在后台自动处理) - 中断函数是 "后台工作者",主函数是 "前台展示者"------ 中断解析出按键值,主函数把值显示到数码管。
4.4 public.c 与 public.h
cpp
#ifndef _public_H
#define _public_H
#include "reg52.h"
typedef unsigned int u16; //对系统默认数据类型进行重定义
typedef unsigned char u8;
typedef unsigned long u32;
void delay_10us(u16 ten_us);
void delay_ms(u16 ms);
#endif
cpp
#include "public.h"
/*******************************************************************************
* 函 数 名 : delay_10us
* 函数功能 : 延时函数,ten_us=1时,大约延时10us
* 输 入 : ten_us
* 输 出 : 无
*******************************************************************************/
void delay_10us(u16 ten_us)
{
while(ten_us--);
}
/*******************************************************************************
* 函 数 名 : delay_ms
* 函数功能 : ms延时函数,ms=1时,大约延时1ms
* 输 入 : ms:ms延时时间
* 输 出 : 无
*******************************************************************************/
void delay_ms(u16 ms)
{
u16 i,j;
for(i=ms;i>0;i--)
for(j=110;j>0;j--);
}