找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 11883|回复: 10
收起左侧

第18章-单片机红外通信

  [复制链接]
ID:111078 发表于 2016-3-28 23:22 | 显示全部楼层 |阅读模式
51单片机轻松入门—基于STC15W4K系列(C语言版)
李友全
编著 详见:http://www.51hei.com/bbs/dpj-37954-1.html

第18章 红外通信
1 电路图
2 程序移植
0.png
红外遥控普遍运用在家用电器上,在工业控制中,对于存在高压、辐射、有毒
气体、粉尘等场合,可以使用红外遥控。


1电路图

1号单片机与红外接收头连接电路如图所示,红外接收头一般都可互换使用。

1.png


2 程序移植

例18.4 红外接收数据,使用一个定时器模拟外中断方式,并通过串口发送接收到的用户码 与键码,R/C时钟:22.1184MHz,波特率9600, 要求串口助手按字符格式显示。 程序优点:通用性极强,可使用任意IO口接收红外数据,红外接收部分自适应R/C时钟频率
5~35M,模拟串口输出部分需要根据R/C时钟频率调整延时函数参数,此程序移植时只需更
改红外接收引脚定义与模拟串口发送引脚即可。

#include "STC15W4K.H"

#define MAIN_Fosc 22.1184 // 定义主时钟, 红外接收会自动适应5~36MHZ,

#define User_code 0xFD02 // 定义红外接收用户码 sbit Ir_Pin = P3^6; // 定义红外接收输入端口 sbit TXD1 = P3^1; // 定义模拟串口发送脚 void InitTimer0(void)

{
TMOD = 0x01; // 16位计数方式. TH0 = Timer0_Reload / 256;
TL0 = Timer0_Reload % 256; ET0 = 1;
TR0 = 1; EA = 1;
}
void main(void)
{
InitTimer0(); // 初始化Timer0
PrintString("定时器0初始化完毕\r\n"); // 上电后串口发送一条提示信息
while(1)
{
if(IR_OK) // 接收到一帧完整的红外数据
{
PrintString("红外键码: 0x"); // 提示红外键码 Tx1Send(HEX2ASCII(IR_code >> 4)); // 键码高半字节 Tx1Send(HEX2ASCII(IR_code)); // 键码低半字节 if(IrUserErr) // 用户码错误,则发送用户码
{
Tx1Send(' '); // 发空格 Tx1Send(' '); // 发空格 PrintString("用户码: 0x"); // 提示用户码 Tx1Send(HEX2ASCII(IR_UserH >> 4)); // 用户码高字节的高半字节 Tx1Send(HEX2ASCII(IR_UserH)); // 用户码高字节的低半字节 Tx1Send(HEX2ASCII(IR_UserL >> 4)); // 用户码低字节的高半字节 Tx1Send(HEX2ASCII(IR_UserL)); // 用户码低字节的低半字节
}
Tx1Send(0x0d); // 发回车 Tx1Send(0x0a); // 发回车
IR_OK = 0; // 清除IR键按下标志
}
}
}

程序运行结果如下图所示。
2.png
只要单片机能获取红外键码,单片机就可根据不同的键码执行不同的控制功能。

程序:
1.1—IR查询方式解码:
  1. /****************《51单片机轻松入门-基于STC15W4K系列》配套例程 *************
  2. ★★★★★★★★★★★★★★★★★★★★★★★★
  3. 《51单片机轻松入门-基于STC15W4K系列》 一书已经由北航出版社正式出版发行。
  4.   作者亲手创作的与教材配套的51双核实验板(2个MCU)对程序下载、调试、仿真方便,不需要外部
  5.   仿真器与编程器,这种设计方式彻底解决了系统中多个最高优先级谁也不能让谁的中断竞争问题。
  6.   QQ群:STC51-STM32(3) :515624099 或 STC51-STM32(2):99794374。
  7.         验证信息:STC15单片机
  8.   邮箱:xgliyouquan@126.com
  9.   ★★★★★★★★★★★★★★★★★★★★★★★★*/

  10. ////////////////////////////////////////////////////////////
  11. //   红外接收数据,查询方式,并通过串口发送输出  
  12. //   晶振:22.1184MHz         ,波特率:9600
  13. ////////////////////////////////////////////////////////////
  14. #include "STC15W4K.H"
  15. sbit Ir_Pin = P3^6;             // 红外接收头信号输出脚
  16. unsigned char Ir_Buf[4]; // 用于保存解码结果(Ir_Buf[0]--用户码L,Ir_Buf[3] --键反码)

  17. // 获取低电平时间 (其实是16位计数器的计数值,STC15系列定时器默认为16位自动重装方式)
  18. unsigned int Ir_Get_Low()
  19. {
  20.         TL0 = 0;                 // 清空16位计数器0
  21.         TH0 = 0;                 // 清空16位计数器0
  22.         TR0 = 1;                 // 计数器0开始运行
  23.         while (!Ir_Pin && (TH0<0x80));          // 信号引脚变成高或低电平时间>17ms退出(只要>12ms即可)
  24.                                       // 0x8000=32768,  32768*0.54253uS=17777.62 uS            
  25.         TR0 = 0;                          // 这里 ! 优先级大于&&     
  26.         return (TH0 * 256 + TL0);                  // 返回16位计数器的计数值。
  27. }

  28. // 获取高电平时间(其实是16位计数器的计数值,STC15系列定时器默认为16位自动重装方式)
  29. unsigned int Ir_Get_High()
  30. {
  31.         TL0 = 0;                    // 清空16位计数器0
  32.         TH0 = 0;                    // 清空16位计数器0
  33.         TR0 = 1;
  34.         while (Ir_Pin && (TH0<0x40));           // 信号引脚变成低电平或高电平时间>17ms退出
  35.         TR0 = 0;
  36.         return (TH0 * 256 + TL0);
  37. }  

  38. void UART_init(void)                      // 9600@22.1184MHz
  39. {                  
  40.         //下面代码设置定时器1
  41.         TMOD = 0x20;           // 0010 0000 定时器1工作于方式2(8位自动重装方式)
  42.         TH1  = 0xFA;           // 波特率:57600 /22.1184MHZ
  43.         TL1  = 0xFA;           // 波特率:57600 /22.1184MHZ
  44.         TR1  = 1;
  45.         //下面代码设置定串口
  46.         AUXR = 0x00;             // 很关键,使用定时器1作为波特率发生器,S1ST2=0
  47.         SCON = 0x50;         // 0101 0000 SM0.SM1=01(最普遍的8位通信),REN=1(允许接收)
  48. }        

  49. //  UART发送一字节        
  50. void UART_Send_Byte(unsigned char dat)
  51. {
  52.         SBUF = dat;
  53.         while (TI == 0);
  54.         TI = 0;             // 此句可以不要,不影响后面数据的发送,只供代码查询数据是否发送完成
  55. }

  56. void main()
  57. {
  58.         unsigned int time;
  59.         unsigned char i,j;
  60.         UART_init();
  61.         UART_Send_Byte(0X55);                // 测试串口工作是否正常
  62.         while (1)
  63.         {
  64. start:
  65.                 ///////////// 接收同步信号 ///////////
  66.                 while (Ir_Pin);                    // 等待低电平出现
  67.                 time = Ir_Get_Low();        // 低电平区间16位计数器的计数值
  68.                 if ((time < 15667) || (time > 17510))   goto start;        
  69.                                                         // 引导脉冲低电平8500~9500us,T=12/22.1184=0.54253uS
  70.                                                         // 8500/0.54253uS=15667.3    9500/0.54253uS=17510.5
  71.                 time = Ir_Get_High();
  72.                 if ((time < 7372) || (time > 9216))  goto start;   // 引导脉冲高电平4000-5000us
  73.                                                                                      // 4000/0.54253uS=7372.8    5000/0.54253uS=9216
  74.                 ////////// 接收后续的4 字节数据        ////////
  75.                 for (i=0; i<4; i++)                  // 4个字节
  76.                 {
  77.                         for (j=0; j<8; j++)              // 每个字节8位
  78.                         {
  79.                                 time = Ir_Get_Low();                 // 接收每位560us 低电平
  80.                                 if ((time < 626) || (time > 1438))   goto start;                 // 340-780us
  81.                                                                                  // 340/0.54253uS=626.7    780/0.54253uS=1437.7
  82.                                 time = Ir_Get_High();                    // 接收每位560us或1690us高电平时间
  83.                                 if ((time>626) && (time<1438))  // 时间范围为340-780us(中心值560us),
  84.                     {
  85.                          Ir_Buf[i] >>= 1;                // 因低位在先,所以数据右移,移入的最高位为0
  86.                                 }
  87.                     else if ((time>2728) && (time<3502))
  88.                 {                           // 时间判定范围为1480~1900us(中心值1690us)
  89.                                                                             // 1480/0.54253uS=2727.9   1900/0.54253uS=3502.1
  90.                                         Ir_Buf[i] >>= 1;                // 因低位在先,所以数据右移,移入的最高位为0
  91.                                         Ir_Buf[i] |= 0x80;          // 最高位置1
  92.                                 }
  93.                     else                            // 不在上述范围内则说明为误码,直接退出
  94.                                 {
  95.                                         goto start;        
  96.                                 }
  97.                         }
  98.                 }
  99.                 UART_Send_Byte(Ir_Buf[0]);                // 用户码低字节
  100.                 UART_Send_Byte(Ir_Buf[1]);                // 用户码高字节
  101.                 UART_Send_Byte(Ir_Buf[2]);                // 键码
  102.                 UART_Send_Byte(Ir_Buf[3]);                // 键反码
  103.         }
  104. }  
复制代码

2.3—IR-中断方式(复杂的)
  1. /****************《51单片机轻松入门-基于STC15W4K系列》配套例程 *************
  2. ★★★★★★★★★★★★★★★★★★★★★★★★
  3. 《51单片机轻松入门-基于STC15W4K系列》 一书已经由北航出版社正式出版发行。
  4.   作者亲手创作的与教材配套的51双核实验板(2个MCU)对程序下载、调试、仿真方便,不需要外部
  5.   仿真器与编程器,这种设计方式彻底解决了系统中多个最高优先级谁也不能让谁的中断竞争问题。
  6.   QQ群:STC51-STM32(3) :515624099 或 STC51-STM32(2):99794374。
  7.         验证信息:STC15单片机
  8.   邮箱:xgliyouquan@126.com
  9.   ★★★★★★★★★★★★★★★★★★★★★★★★*/

  10. ////////////////////////////   main.c  ////////////////////////////////////////
  11. // 红外接收数据,中断方式,并通过串口发送接收到的4字节,晶振:22.118400 MHz
  12. // 接收头信号引脚P3.6,串口波特率9600
  13. ///////////////////////////////////////////////////////////////////////////
  14. #include "STC15W4K.H"
  15. sbit Ir_Pin = P3^6;                 // 红外接收头信号输出脚

  16. unsigned char irtime;                 // 红外时间
  17. unsigned char irdata[33];         // 1位引导码+32位数据=33
  18. unsigned char bitnum;                 // 32位数据中第几位
  19. unsigned char startflag;         // 开始接收标志,长时间空闲状态出现第1个下降沿就置1
  20. unsigned char irok;                         // 一桢数据接收完成标志,完成时值为1,否则为0
  21. unsigned char Ir_Buf[4];         // 存放接收到的4字节码(用户码L + 用户码H + 键码 + 键反码),Ir_Buf[0]是用户码L
  22. unsigned char disnum[8];           //

  23. void UART_init(void)                      // 9600@22.1184MHz
  24. {                  
  25.         //下面代码设置定时器1
  26.         TMOD&= 0x0F;
  27.         TMOD |= 0x20;           // 0010 0000 定时器1工作于方式2(8位自动重装方式)
  28.         TH1  = 0xFA;           // 波特率:9600 /22.1184MHZ
  29.         TL1  = 0xFA;           // 波特率:9600 /22.1184MHZ
  30.         TR1  = 1;
  31.         //下面代码设置定串口
  32.         AUXR = 0x00;             // 很关键,使用定时器1作为波特率发生器,S1ST2=0
  33.         SCON = 0x50;         // 0101 0000 SM0.SM1=01(最普遍的8位通信),REN=1(允许接收)
  34. }        

  35. //  UART发送一字节        
  36. void UART_Send_Byte(unsigned char dat)
  37. {
  38.         SBUF = dat;
  39.         while (TI == 0);
  40.         TI = 0;             // 此句可以不要,不影响后面数据的发送,只供代码查询数据是否发送完成
  41. }

  42. // 定时器0初始化
  43. void Timer0_init(void)           
  44. {
  45.         TMOD&=0xF0;
  46.         TMOD|=0x02;               // 8位自动重装方式
  47.          TH0=0x00;                   // T=12/22.1184=0.54253uS,256*0.54253 =        138.88768 uS
  48.          TL0=0x00;
  49.         ET0=1;             // 开中断
  50.         EA=1;                           // 开总中断
  51.         TR0=1;
  52. }

  53. // 外部中断2初始化
  54. void Int2_init()      // P3.6引脚即为外部中断2
  55. {
  56.         INT_CLKO|=0x10;          // 开启外中断2

  57.         EA = 1;                          // 总开关
  58. }  

  59. void irpros(void)         // 33位红外数据转4字节码值
  60. {
  61.         unsigned char mun,k,i,j;
  62.         k=1;                              // k=0存放的引导码,这里不管
  63.         for(j=0;j<4;j++)
  64.         {
  65.                 for(i=0;i<8;i++)
  66.                 {
  67.                         mun=mun>>1;                  // 右移7次,发送端输出的原始二进制数据的每个字节都是低位在前,高位在后。
  68.                         if(irdata[k]>12)  // 数值0对应irtime值位1.12/0.138=8.12   
  69.                         {                                  // 数值1对应irtime值位2.25/0.138=16.3 ,取中值12区分
  70.                                 mun=mun | 0x80;
  71.                         }
  72.                         k++;
  73.                 }
  74.                 Ir_Buf[j]=mun;
  75.         }                                                  // 33位数据转4字节完成                                    
  76. }

  77. void irwork(void)                  // 4字节红外数据发串口输出
  78. {
  79.         UART_Send_Byte(Ir_Buf[0]);           // 用户码低字节
  80.         UART_Send_Byte(Ir_Buf[1]);           // 用户码高字节
  81.         UART_Send_Byte(Ir_Buf[2]);           // 键码
  82.         UART_Send_Byte(Ir_Buf[3]);           // 键反码                                                               
  83. }

  84. void main()
  85. {
  86.         UART_init();
  87.         Timer0_init();                          // 让定时器0开始自由计数
  88.         Int2_init();                          // 外中断 2 初始化(红外接收引脚)
  89.         while(1)
  90.         {
  91.                 if(irok==1)                          // 一桢数据接收完成标志,完成此值为1,否则为0
  92.                 {
  93.                         irpros();                  // 33位红外数据转4字节红外码值
  94.                         irwork();                  // 若用数码管显示,需要将2进制转换成16进制
  95.                         irok=0;
  96.                 }
  97.         }                        
  98. }

  99. void timer0 () interrupt 1
  100. {
  101.         irtime++;           // 0--255自由循环计数 ,//T=12/11.0592=1.08506uS
  102. }                                   // 自动重装计数初值255,irtime里面的1代表256* 1.08506uS=277.77uS
  103.                                    // 最大计数时间 256*        277.77=71109.12uS= 71mS         

  104. //中断函数功能:利用信号下降沿触发对脉冲周期计时,最多计33个脉冲
  105. void int2_isr() interrupt 10      // 外部中断2中断函数
  106. {
  107.         P35=!P35;
  108.         if(startflag)
  109.         {
  110.                 if(irtime>90&&irtime<104)  // 12.5-14.5ms引导码        (8500+4000)--(9500+5000)=12.5--14.5mS
  111.                 {                                                  // 12500/138.89=90        14500/138.89=104
  112.                         bitnum=0;
  113.                 }
  114.                 irdata[bitnum]=irtime;          // 第一次引导码时间存入irdata[0]
  115.                 irtime=0;                                  // 清零下次备用
  116.                 TL0=0;
  117.                 bitnum++;
  118.                 if(bitnum==33)                          // 数组范围0--32
  119.                 {
  120.                         bitnum=0;
  121.                         irok=1;                                  // 一桢数据接收完成标志
  122.                 }
  123.         }
  124.         else
  125.         {
  126.                 irtime=0;                 // 长时间空闲状态中出现第1个下降沿,清空计数器不确定的计数值
  127.                 TL0=0;           // TH0在初始化时设置的0,可以不作处理,定时器0虽为8位自动重装方式,
  128.                 startflag=1;         // 但这里的运用不是在定时器溢出时处理数据,所以需要手动清零操作。
  129.         }
  130. }
复制代码
IR中断方式(简单版)的单片机红外解码程序:http://www.51hei.com/bbs/dpj-47283-1.html
只用一个单片机定时器的红外接收解码程序:http://www.51hei.com/bbs/dpj-47284-1.html

全部完整的4个源码请下载附件: 第18章 红外通信.rar (84.33 KB, 下载次数: 165)

评分

参与人数 1黑币 +8 收起 理由
xiou + 8 很给力!

查看全部评分

回复

使用道具 举报

ID:138443 发表于 2016-10-4 23:16 | 显示全部楼层
太好了,谢谢
回复

使用道具 举报

ID:277420 发表于 2018-7-10 15:25 | 显示全部楼层
路过学习学习。。。。。。。。。。
回复

使用道具 举报

ID:69115 发表于 2018-10-20 10:16 | 显示全部楼层
不错,很好的应用实例
回复

使用道具 举报

ID:332997 发表于 2018-11-1 09:13 | 显示全部楼层
很给力
回复

使用道具 举报

ID:420172 发表于 2018-11-3 18:01 | 显示全部楼层
很实用,谢谢
回复

使用道具 举报

ID:71297 发表于 2019-1-24 22:59 | 显示全部楼层
刚好,需要用到,谢谢了
回复

使用道具 举报

ID:621994 发表于 2019-11-30 21:52 | 显示全部楼层
你好我是小白 请问一下:我用IR中断的形式写的串口显示按键码的程序 ,内容上面大致和您的大致相同,可是在按下同一个按键两次后就显示其他的数码(不是正常的00 ff 开头的数码) 我开始以为是程序的问题 但是第二次的显示还是正确的第三次错误,我就想不出来是什么原因了,您能指导指导吗? 必要的话可以叫我贴出程序出来 谢谢! 各位看到的朋友如果可以的话也请帮帮忙,。感谢。
回复

使用道具 举报

ID:398219 发表于 2020-2-17 13:19 | 显示全部楼层
路过,学习大神的编程思路
回复

使用道具 举报

ID:725238 发表于 2020-4-24 15:06 来自手机 | 显示全部楼层
jpg阿福 发表于 2019-11-30 21:52
你好我是小白 请问一下:我用IR中断的形式写的串口显示按键码的程序 ,内容上面大致和您的大致相同,可是在 ...

我比你白,是不是按键按下时连续发射信号?该搞个松开按键才发射的程序。或是你那个变量发射后没复位?发射完复位
回复

使用道具 举报

ID:965189 发表于 2021-11-16 11:57 | 显示全部楼层
没看见连续键的解码部份。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表