之前我写过一篇使用Arduino实现红外接收发射的博客(链接: 如何用一块 Arduino uno 板同时进行红外发送和接收)。但仅仅实现了一个中继站的功能。但为了实现真正意义上的红外自发自收,我在那篇文章提出了几个思路,但由于Arduino硬件的限制,无法实现我的思路。因此,这篇博客我将使用高级一点的硬件——STM32,实现真正意义的红外自发自收!!!
下面列出一些参考网站:
红外遥控简介
红外遥控是一种无线、非接触控制技术,具有抗干扰能力强,信息传输可靠,功耗低,成本低,易实现等显著优点,被诸多电子设备特别是家用电器广泛采用,并越来越多的应用到计算机系统中。
个人觉得,使用红外进行无线通信有几大优势。一,便宜!便宜!!便宜!!!(重要的事情强调3遍)。二,直线传输,穿透力差,无法穿墙,较低概率受到他人的干扰(某些场景的缺点却是某些场景的优点)。三,其本身不发任何类型辐射,器件功耗很小。
红外发射
注:此处直接摘抄于STM32CubeMX系列|红外遥控
红外发射装置(红外遥控器)是由键盘电路、红外编码电路、电源电路和红外发射电路组成。红外发射电路的主要元件为红外发光二极管(目前使用的红外线波长多为940nm)。红外遥控器常用载波的方式传送二进制编码,常用的载波频率为38kHz,红外遥控器将遥控信号(二进制脉冲码)调制在38kHz的载波上,经缓冲放大后送至红外发光二极管,转化为红外信号发射出去。
二进制脉冲码目前最广泛使用的是:NEC Protocol的PWM(脉冲宽度调制)和Philips RC-5 Protocol的PPM(脉冲位置调制)。本例程遥控器使用的是NEC协议,其特征如下:
- 8位地址和8位指令长度
- 地址和命令2次传输(确保可靠性)
- PWM脉冲位置调制,以发射红外载波的占空比代表0和1
- 载波频率为38kHz
位时间为1.125ms或2.25ms
NEC码的位定义:一个脉冲对应560us的连续载波,一个逻辑1传输需要2.25ms(560us脉冲+1680us低电平),一个逻辑0传输需要1.125ms(560us脉冲+560us低电平)。
而红外接收头在收到脉冲的时候为低电平,在没有脉冲的时候为高电平,这样在接收头端收到的信号为:逻辑1应该是560us低电平+1680us高电平,逻辑0应该是560us低电平+560us高电平。所以可以通过计算高电平时间判断接收到的数据是0还是1。
NEC 码位定义时序图如下图
NEC 遥控指令的数据格式为:引导码、地址码、地址反码、控制码、控制反码。引导码由一个9ms的低电平和一个4.5ms的高电平组成,地址码、地址反码、控制码、控制反码均是8位数据格式。按照低位在前,高位在后的顺序发送。采用反码是为了增加传输的可靠性(可用于校验)。数据格式如下:
stm32模拟红外发射思路及效果
下图为遥控器信号(使用逻辑分析仪查看红外接收管的电信号)
我们需要做的就是通过stm32实现红外精确的时序逻辑。同无线通信,红外发射作为调制环节,我们首先需要产生一个38KHz载波(由于载波频率不高,对应的可使用stm32的定时器PWM模式产生38KHz的方波信号),然后通过控制PWM开启关闭实现具体的时序。
由于本人偷懒,就省掉了遥控器最后面的那个结束帧信号。
下图为本人模拟遥控器信号结果(使用逻辑分析仪查看红外接收管的电信号)
红外接收
红外接收设备是由红外接收电路、红外解码、电源和应用电路组成。红外遥控接收器的主要作用是将遥控发射器发来的红外光信好转换成电信号,再放大、限幅、 检波、整形,形成遥控指令脉冲,输出至遥控微处理器。
stm32红外接收思路
由于红外接收头在没有脉冲的时候为高电平,当收到脉冲的时候为低电平,所以可以通过定时器输入捕获中断的上升和下降沿触发中断,在中断内通过计算高电平时间来判断接收到的数据是0还是1。
硬件电路
感觉较简单,就不细讲了吧。相信大家都很熟悉那个STM32F103C8T6最小系统板吧!
发射部分就是加了个NPN管放大(9014,增益带宽积15MHz)
接收部分就是直接按照红外管datasheet接就好了,也不用放大它的信号,没有必要,因为接收灵敏度取决于红外管。
下图就是我搭的电路:(很简单,一看就懂)
STM32Cube几个重要配置
管脚配置(PA1接发射管脚,PA6接接收管脚):
时钟我用的是外部晶振,主频72M。
时钟1用于产生微妙延时。
时钟2产生38KHz的PWM
时钟3用于输入捕获红外接收信号
代码部分
发射代码
我的注释是ASCII码,所以乱码,大家注意一下导入Keil改一下语言设置
void delay_us(uint16_t us){
uint16_t differ = 0xffff-us-5;
__HAL_TIM_SET_COUNTER(&htim1,differ); //É趨TIM¶¨ÊýÆ÷µÄÆðʼֵ
HAL_TIM_Base_Start(&htim1); //Æô¶¯¶¨Ê±Æ÷
while(differ < 0xffff-5){ //ÅжÏ
differ = __HAL_TIM_GET_COUNTER(&htim1); //²éѯ¶¨Ê±Æ÷µÄ¼ÆÊýÖµ
}
HAL_TIM_Base_Stop(&htim1);
}
变量声明
uint8_t send_Flag = 0;
uint32_t send_Code = 0;
uint32_t receive_Code = 0;
uint8_t receive[33] = {0};
uint8_t receive_Flag = 0;
uint8_t addr = 0x00;
uint8_t data = 0xA2;
发射时序逻辑(放在主函数即可)
if(send_Flag == 1){
send_Flag = 0;
HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);
delay_us(9000);
HAL_TIM_PWM_Stop(&htim2,TIM_CHANNEL_2);
delay_us(4500);
uint8_t iaddr = ~addr;
uint8_t idata = ~data;
send_Code = addr<<24 | iaddr<<16 | data<<8 | idata;
for(int i=31;i>=0;i--){
HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);
delay_us(560);
HAL_TIM_PWM_Stop(&htim2,TIM_CHANNEL_2);
if((send_Code>>i) & 0x01)//ÊÇ·ñ·´Ïò
delay_us(1690);
else
delay_us(560);
// if((send_Code>>i) & 0x01)//ÊÇ·ñ·´Ïò
// delay_us(560);
// else
// delay_us(1690);
}
HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);
delay_us(560);
HAL_TIM_PWM_Stop(&htim2,TIM_CHANNEL_2);
}
接收代码
记得在cube里开中断!!!
uint32_t pwm_low_level_time;
uint32_t pwm_high_level_time;
int tim_mode_raise_or_falling = 0;
int Index = 0;
int ok = 0,ok2=0;
int datawp[35] = {0};
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(tim_mode_raise_or_falling == 0)
{
pwm_low_level_time = HAL_TIM_ReadCapturedValue(&htim3,TIM_CHANNEL_1) + 1;
__HAL_TIM_SET_COUNTER(&htim3,0);
TIM_RESET_CAPTUREPOLARITY(&htim3, TIM_CHANNEL_1);
TIM_SET_CAPTUREPOLARITY(&htim3,TIM_CHANNEL_1,TIM_ICPOLARITY_FALLING);
tim_mode_raise_or_falling = 1;
ok2 = 0;
}
else if(tim_mode_raise_or_falling == 1)
{
pwm_high_level_time = HAL_TIM_ReadCapturedValue(&htim3,TIM_CHANNEL_1) + 1;
__HAL_TIM_SET_COUNTER(&htim3,0);
TIM_RESET_CAPTUREPOLARITY(&htim3, TIM_CHANNEL_1);
TIM_SET_CAPTUREPOLARITY(&htim3,TIM_CHANNEL_1,TIM_ICPOLARITY_RISING);
tim_mode_raise_or_falling = 0;
ok2 = 1;
}
uint8_t Data=0;
if(pwm_high_level_time >= 5000){
Index=0;ok = 0;
}
else if(pwm_high_level_time >= 3500 && pwm_high_level_time < 5000){
Index=0;ok = 1;
}
else if(pwm_high_level_time >= 1000 && pwm_high_level_time < 2000){
Data=1;
}
else if(pwm_high_level_time > 0 && pwm_high_level_time < 1000){
Data=0;
}
if(ok == 1 && ok2 == 1){
receive_Code <<= 1;
receive_Code += Data;
receive[Index] = Data;
datawp[Index] = pwm_high_level_time;
Index++;
if(Index>=32){
receive_Flag=1;
Index = ok = 0;
}
}
}
最终效果
接收遥控器信号
自发自收
源码链接
gif可能看不太清楚,我放在Gitee上吧。