一篇文章教会你红外发送模块发送红外信号,附STM32代码示例
目录
一、NEC协议介绍:
二 、NEC协议特点:
(1)8位地址和8位命令长度:
(2)扩展模式可用,加倍地址大小:
(3)地址和命令都传输两次以提高可靠性:
(4)脉冲距离调制(Pulse Distance Modulation):
(5)载波频率为38kHz:
(6)位时间是1.125ms或2.25ms:
(7) 完整的数据传输格式:
三、NEC协议:
四、扩展NEC协议:
五、所使用的红外发射和接收模块:
六、示例代码(STM32):
(1)初始化红外发射模块的GPIO端口和定时器生成38khz载波:
(2)发送引导码:
(3)发送NEC数据位0/1:
(4)NEC数据发送一个字节:
(5)发送完整数据:
七、效果演示及代码:
一、NEC协议介绍:
NEC协议是一种广泛使用的红外通信协议,它由NEC公司(现在称为Renesas)开发。这种协议也被称为日本格式,尽管它在全球范围内都有应用。NEC协议被用于多种设备,包括VCR、电视、投影仪等,其中NEC公司制造了遥控器的IC芯片。
二 、NEC协议特点:
(1)8位地址和8位命令长度:
(2)扩展模式可用,加倍地址大小:
(3)地址和命令都传输两次以提高可靠性:
(4)脉冲距离调制(Pulse Distance Modulation):
(5)载波频率为38kHz:
(6)位时间是1.125ms或2.25ms:
(7) 完整的数据传输格式:
三、NEC协议:
上图展示了NEC协议的一个典型脉冲序列。根据这个协议,最低位(LSB)首先被传输。在这个例子中,地址 0x59 和命令0x16 被传输。消息的开始是一个9ms的AGC脉冲,这个脉冲被用来设置早期红外接收器的增益。这个AGC脉冲之后是一个4.5ms的间隙,紧接着是地址和命令的传输。地址和命令各传输两次,第二次所有位都取反,可以用于验证接收到的消息。由于每个位都以其取反的长度重复,因此总的传输时间是固定的。如果你不关心这种可靠性,可以忽略取反的值,或者你可以将地址和命令各扩展到16位! 请注意,在消息的末尾必须跟随一个额外的560µs脉冲,以便能够确定最后一个位的值。
即使遥控器上的按键被按住,命令也只传输一次。只要按键保持按下状态,每110毫秒就会传输一次重复码。这个重复码仅仅是一个9毫秒的AGC脉冲,后面跟着一个2.25毫秒的间隙和一个560微秒的脉冲。
四、扩展NEC协议:
NEC协议由于其广泛的应用,很快所有的地址可能值都被用完了。为了解决这个问题,通过牺牲地址的冗余性,地址范围从256个可能的值扩展到了大约65000个不同的值。这样,地址范围就从8位扩展到了16位,而没有改变协议的其他属性。通过这种方式扩展地址范围,消息的总传输时间就不再是固定的了,它现在取决于消息中1和0的总数。如果你想保持总消息时间的恒定,你需要确保地址字段中的1的数量是8个(这也自动意味着0的数量也是8个)。这将减少不同地址的最大数量到大约13000个。
请注意,扩展协议中有256个地址值是无效的,因为它们实际上是标准的NEC协议地址。当低字节正好是高字节的相反数时,它就不是有效的扩展地址。
五、所使用的红外发射和接收模块:
红外接收模块使用教程:
一篇文章教会你红外接收模块接收红外遥控信号,附STM32代码示例
六、示例代码(STM32):
(1)初始化红外发射模块的GPIO端口和定时器生成38khz载波:
// 初始化红外发射管所在的GPIO端口
void IR_Init(void) {
// 使能GPIOB端口的时钟,因为红外发射管通常连接到GPIO端口
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
// 使能定时器3(TIM3)的时钟,因为需要使用定时器来生成精确的PWM信号
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
// 定义GPIO初始化结构体
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // 选择GPIOB的第0脚作为红外发射管的控制引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 设置GPIO模式为复用推挽输出,适合PWM输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 设置GPIO速度为50MHz,确保信号变化足够快
GPIO_Init(GPIOB, &GPIO_InitStructure); // 应用配置到GPIOB的第0脚
// 选择TIM3为内部时钟源,若不调用此函数,TIM默认也为内部时钟
TIM_InternalClockConfig(TIM3);
// 定义定时器基本初始化结构体
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Prescaler = 72-1; // 设置预分频器为71,用于降低定时器的计数速度
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 设置计数模式为向上计数
// 设置自动重载寄存器的值,这会影响PWM的频率
// 这里设置为26-1,即26,定时器计数到26后重置为0
TIM_TimeBaseStructure.TIM_Period = 26-1;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 设置时钟分频,这里不分频
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); // 应用这些配置到TIM3
// 定义PWM输出初始化结构体
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; // 设置输出比较模式为PWM模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 设置输出状态为使能
TIM_OCInitStructure.TIM_Pulse = 0; // 设置PWM脉冲宽度初始值为0,这将被用来调整PWM占空比
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; // 设置输出极性为高,这意味着PWM信号在活动时为高电平
TIM_OC3Init(TIM3, &TIM_OCInitStructure); // 应用这些配置到TIM3的通道3
TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable); // 使能预装载,允许在更新事件时更新比较值
TIM_Cmd(TIM3, ENABLE); // 使能TIM3
首先配置定时器TIM3,在配置里面TIM默认的内部时钟为72MHz,设置预分频器为71,那么TIM的时钟频率为1MHz,也就是计数频率1us,计数周期修改成了25,也就是26个周期(0也算一个),经过换算可以得出1MHz / 26 ≈ 38.4615KHz,这个跟红外接收模块的频率38KHz差不多能匹配,我所使用的红外接收模块接收时需要38KHz的载波,所以配置成38KHz,市面上也有不是38KHz的红外接收模块,可以自行修改载波频率。
(2)发送引导码:
// 发送NEC引导码
void IR_SendLeader(void) {
TIM_SetCompare3(TIM3, 9); // 产生9ms的高电平
Delay_us(9000);
TIM_SetCompare3(TIM3, 0); // 产生4.5ms的低电平
Delay_us(4500);
}
引导码由9ms的低电平+4.5ms的高电平组成。红外接收模块在接收到红外信号时通常输出的是低电平。这是因为红外接收模块通常包含一个红外光电二极管,它在接收到红外光时会产生电流,从而触发一个开关(通常是三极管或MOSFET)导通。当这个开关导通时,它会将接收模块的输出引脚从高电平拉低到低电平。这里的引导码是相对于红外接收模块来说是9ms的低电平+4.5ms的高电平。推荐的载波占空比是1/4或1/3,26的1/3大约是9左右。
(3)发送NEC数据位0/1:
// 发送NEC数据位0/1
void IR_SendBit(uint8_t bit) {
if (bit) { //数据位1
TIM_SetCompare3(TIM3, 9); // 产生560us的高电平
Delay_us(560);
TIM_SetCompare3(TIM3, 0); // 产生1680us的低电平
Delay_us(1680);
} else { //数据位0
TIM_SetCompare3(TIM3, 9); // 产生560us的高电平
Delay_us(560);
TIM_SetCompare3(TIM3, 0); // 产生560us的低电平
Delay_us(560);
}
}
(4)NEC数据发送一个字节:
// NEC数据发送一个字节,从高位开始
void IR_WriteByte(uint8_t byte) {
for (uint8_t i = 0; i < 8; i++) {
// 提取当前位的值
uint8_t bit = (byte >> (7 - i)) & 0x01;
// 写入这一位
IR_SendBit(bit);
}
}
在NEC红外通信协议中,数据是按照先发送高位(MSB)再发送低位(LSB)的顺序进行传输的。这意味着在发送每个字节时,最高位(MSB)会首先被发送,然后依次是次高、次低…直到最低位(LSB)。
(5)发送完整数据:
// 发送NEC数据包
void IR_SendNEC(uint8_t data, uint8_t address) {
IR_SendLeader(); // 发送引导码
IR_SendByte(address); // 发送地址码
IR_SendByte(~address); // 发送地址反码
IR_SendByte(data); // 发送命令码
IR_SendByte(~data); // 发送命令反码
TIM_SetCompare3(TIM3, 9); // 发送结束码
Delay_us(560);
TIM_SetCompare3(TIM3, 0);
}
在消息的末尾必须跟随一个额外的560µs脉冲,以便能够确定最后一个位的值。
七、效果演示及代码:
通过网盘分享的文件:19- 红外接收模块接收红外发送模块信号
链接: https://pan.baidu.com/s/1IaBiTvzRT6ym3xIFY6TPLA?pwd=xfks 提取码: xfks
作者:The_xzs