NXP JN5169 使用红外发送 / 接收

一、红外发送

1、红外发射二极管原理图

在这里插入图片描述

红外发射模块

在这里插入图片描述

红外接收二极管为 HX1838
在这里插入图片描述

2、JN5169 发射端代码

        红外发送是定时器 2 的一种特殊特性,它使用定时器产生波形用于红外远程控制应用。
        远程控制协议(比如说 Philips RC-6)使用编码的位流将 On-Off Key(OOK)调制应用于载波信号。红外发送器能够适应各种远程控制协议,这些协议具有不同载波频率、载波占空比和数据位编码要求。红外发送器使用定时器 2 来产生一个可编程的载波波形,这个波形通过 RAM 中存储的可编程位序列进行 OOK 调制。最后得到的波形输出到相关的定时器 2 输出管脚。
注意:一个典型的红外 LED 要求至少 15mA 的驱动电流。由于标准的数字输出不具备这种驱动能力,因此将会要求使用外部晶体管或 LED 驱动器来提供这种电流。

OOK调制波形的示例如下图所示。
在这里插入图片描述

        在上图这个示例中,周期性载波信号和二进制位模式 1011 的逻辑与产生最终的 OOK 调制波形,其中每个数据位的周期等于载波周期的 3 倍。

NEC 协议发送代码(JN5169 DIO12 连接红外发射模块)

uint32 TransmissionLengthInBits	= 0;		//传输序列的长度,以位为单位(1到4096)
uint32 BufferAddress[4];		//发送缓冲区

/**
 * JN5169 红外发送 API 是先发送MSB,最后是LSB
 * 而NEC协议是先发送LSB,最后是MSB
 * 所以需要反转数据位,从MSB->LSB到LSB->MSB, 所有的Bit都必须反转
 */
uint8 u8ReverseData(uint8 data)
{
	uint8 temp = data;
	uint8 len = 1 * 8 - 1;

	for (data >>= 1; data; data >>= 1){
		temp <<= 1;
		temp |= data & 1;
		len--;
	}
	temp <<= len;
	return (temp);
}

/**
 * 设置发送序列,处理数据
 * 在NEC协议中,逻辑1为2.25ms,脉冲时间560us(2.25ms / 560us = 4);逻辑0为1.12ms,脉冲时间560us(1.12ms / 560us = 2)
 * 所以我们要发送逻辑1需要设置为0b1000,逻辑0为0b10
 */
void vSetSendSequence(uint32 *dat)
{
	uint32 send_code1, send_code2, send_code3, send_code4;
	uint8  address1, address2, command1, command2;

	address1 = 0x40;						//用户码
	address2 = (~address1) & 0x000000FF;	//用户反码0xbf
	command1 = 0x56;						//数据码
	command2 = (~command1) & 0x000000FF;	//数据反码0xa9

	address1 = u8ReverseData(address1);	//反转比特位
	address2 = u8ReverseData(address2);
	command1 = u8ReverseData(command1);
	command2 = u8ReverseData(command2);

	/**
	 * NEC协议引导码是是9ms的高电平脉冲,其后是4.5ms的低电平
	 * 高电平数据位,9ms / 560us = 16
	 * 低电平数据位,4.5ms / 560us = 8
	 * 逻辑1需要设置为0b1000,逻辑0为0b10
	 */
	send_code1 = 0b11111111111111110000000000000000;//9ms的高电平脉冲,其后是4.5ms的低电平
	//0x40(0b0100 0000)反转比特位后为(0b0000 0010)
	//转为发送数据后为10 10 10 10 10 10 1000 10
	send_code1 = 0b11111111111111110000000010101010;
	send_code2 = 0b10101000100000000000000000000000;
	//0xBF(0b1011 1111)反转比特位后为(0b1111 1101)
	//转为发送数据后为1000 1000 1000 1000 1000 1000 10 1000
	send_code2 = 0b10101000101000100010001000100010;
	send_code3 = 0b00101000000000000000000000000000;
	//0x56(0b0101 0110)反转比特位后为(0b0110 1010)
	//转为发送数据后为10 1000 1000 10 1000 10 1000 10
	send_code3 = 0b00101000101000100010100010100010;
	//0xA9(0b1010 1001)反转比特位后为(0b10010101)
	//转为发送数据后为1000 10 10 1000 10 1000 10 1000
	//因为HX1838红外接收是反相输出,所需多余位补1,HX1838输出低电平
	send_code4 = 0b10001010100010100010100011111111;//最后八位补1

	*dat++ = send_code1;
	*dat++ = send_code2;
	*dat++ = send_code3;
	*dat++ = send_code4;

	TransmissionLengthInBits = 4 * 32;//总发送长度,单位为位bit
}

PRIVATE void vCbSysCtrl(uint32 u32Device, uint32 u32ItemBitmap)
{
	if (E_AHI_DEVICE_INFRARED == u32Device) {	//红外发送完成中断
		if (E_AHI_INFRARED_TX_MASK == u32ItemBitmap) {	//
			vPrintf("红外数据发送成功!\n");
		}
	}
}

void vInitInfrared(void)
{
	bool status;
	/**
	 * NEC协议
	 * JN5169 使用 OOK 调制波形
	 * 调制 38kHz 载波,载波周期 = 1 / 38000 * 1000000 = 26.31us
	 * 所以 u16Lo = 载波周期 / 定时器时钟周期 = 26.31us / 250ns = 105
	 * 如果我们需要调制载波占空比为 30%
	 * 所以低电平时间 u16Hi = (载波周期 * 70% )/ 定时器时钟周期 = (26.31us * 70%) / 250ns = 74
	 * 载波频率 = 1 / 载波周期 = 1 / (26.31 / 1000000) = 38.008 kHz
	 * 在NEC协议中,逻辑1为2.25ms,脉冲时间560us(2.25ms / 560us = 4);逻辑0为1.12ms,脉冲时间560us(1.12ms / 560us = 2)
	 * 所以我们可以确定NEC协议中位周期为 560us
	 * u16BitPeriodsInCarrierPeriods = 位周期 / 载波周期 = 560 / 26.31 = 21
	 */
	status = bAHI_InfraredEnable(2,	//使用时钟预分频值来递减分频外设时钟并产生定时器时钟,2 (定时器时钟周期 = 2 ^ u8Prescale / 16MHz = 4/16MHz = 250ns)
			74,	//在启动定时器后载波变高之前的定时器时钟周期数,这定义了载波低电平的持续时间,载波低电平持续时间 = 74x250ns = 18.5μs
			105,	//在启动定时器后载波再次变低之前的定时器时钟周期数,这定义了载波周期,载波周期 = 105x250ns = 26.25μs, 即,频率 = 38.095kHz
			21,		//以载波周期为单位的位周期,位周期 = 21×26.25μs = 551μs
			FALSE,	//输出信号极性,输出不反转极性
			TRUE);	//开启发送完成中断
	if(status){
		vPrintf("红外发送器配置成功!\n");
	}
	else{
		vPrintf("红外发送器配置失败!\n");
	}
	vAHI_InfraredRegisterCallback(vCbSysCtrl);//中断回调函数
}

PUBLIC void AppColdStart(void)
{
	bool status;
	/*等待系统时钟切换为外部32MHz晶振*/
	while (bAHI_GetClkSource() == TRUE);

	vAHI_WatchdogStop();
	(void) u32AHI_Init();

	vUartInit();
	vAHI_DelayXms(2000);
	vPrintf("System init...\n");

	vSetSendSequence(BufferAddress);

	vInitInfrared();

	while (1) {
		status = bAHI_InfraredStart(BufferAddress, TransmissionLengthInBits);//开启发送
		if(status){
			vPrintf("参数有效,启动发送!\n");
			vPrintf("TransmissionLengthInBits = %d \n", TransmissionLengthInBits);
		}
		else{

			vPrintf("参数无效,关闭发送!\n");
		}
		vAHI_DelayXms(1000);
	}
}

PUBLIC void AppWarmStart(void)
{
	AppColdStart();
}

效果图:
在这里插入图片描述


3、STC15W408AS 接收端代码

NEC 协议接收代码(STC15W408AS P3.2 连接红外接收模块)

#ifndef uchar
	#define uchar unsigned char
#endif
#ifndef uint
	#define uint unsigned int
#endif

//IRCOM[0] --- 存放用户码    00H
//IRCOM[1] --- 存放用户反码  BfH
//IRCOM[2] --- 存放数据码
//IRCOM[3] --- 存放数据反码

sbit IRIN = P3 ^ 2;		//红外接收器数据线,外部中断0

uchar flag = 1;

uchar IRCOM[7];

void init_int0(void)
{
	INT0 = 1;
	IT0 = 1;                    //设置INT0的中断类型 (1:仅下降沿 0:上升沿和下降沿)
	EX0 = 1;                    //使能INT0中断
}

void exint0(void) interrupt 0
{
	uchar i, j, N;
	uint k;
	EX0 = 0;
	Delay2ms();

	if(IRIN == 1) {
		EX0 = 1;
		return;
	} 

	k = 100;
	//确认IR信号出现
	while(!IRIN && k) {		//等IR变为高电平,跳过9ms的前导低电平信号。
		Delay200us();
		k--;
	}
	k = 100;
	while(IRIN && k) { 		//等 IR 变为低电平,跳过4.5ms的前导高电平信号。
		Delay200us();
		k--;
	}

	for(i = 0; i < 4; i++) {						//收集四组数据
		for(j = 0; j < 8; j++) {					//每组数据有8位
			k = 100;
			while(!IRIN && k) {						//等 IR 变为高电平
				Delay200us();
				k--;
			}
			k = 100;
			N = 0;
			while(IRIN && k) {						//计算IR高电平时长
				Delay200us();
				N++;
				k--;
				if(N >= 30) {
					EX0 = 1;
					return;						//0.14ms计数过长自动离开。
				}
			}									//高电平计数完毕
			IRCOM[i] = IRCOM[i] >> 1;			//数据最高位补“0”

			if(N >= 8) {
				IRCOM[i] = IRCOM[i] | 0x80;		//数据最高位补“1”
			}

			N = 0;
		}
	}

	if(IRCOM[2] != ~IRCOM[3]) {
		EX0 = 1;
		return;
	}

	IRCOM[5] = IRCOM[2] & 0x0F;     			//取键码的低四位
	IRCOM[6] = IRCOM[2] >> 4;       			//右移4次,高四位变为低四位

	if(IRCOM[5] > 9) {
		IRCOM[5] = IRCOM[5] + 0x37;
	} else
		IRCOM[5] = IRCOM[5] + 0x30;

	if(IRCOM[6] > 9) {
		IRCOM[6] = IRCOM[6] + 0x37;
	} else
		IRCOM[6] = IRCOM[6] + 0x30;
	
	flag = 0;

	EX0 = 1;
}

void main()
{
	Init_Uart();
	init_int0();
	EA = 1;
	IRIN = 1;
	
	flag = 1;

	Delay500ms();
	Delay500ms();
	Delay500ms();
	Delay500ms();

	printf("System init...\n");

	while(1){
		if(!flag){
			flag = 1;
			printf("IRCOM[0] = %x ", IRCOM[0]);
			printf("IRCOM[1] = %x ", IRCOM[1]);
			printf("IRCOM[2] = %x ", IRCOM[2]);
			printf("IRCOM[3] = %x \n", IRCOM[3]);
		}
	}
}

效果图:
在这里插入图片描述


二、红外接收

红外接收二极管为 HX1838
在这里插入图片描述

红外键盘为(遵循 NEC 协议)
在这里插入图片描述
遥控器键位码
在这里插入图片描述

示例代码:

#define IRIN		(1 << 3)			//红外接收器数据线
uint8 IRCOM[7];						//储存命令和键码

//获取当前引脚的输入电平
uint8 vGet_Bit(uint32 dio)
{
	uint8 io;
	if (dio > 0xFF) {
		dio = dio >> 8;
	}
	io = u8AHI_DioReadByte(FALSE);//DIO0-7
	if ((io & dio) == dio) {
		return (1);
	} else {
		return (0);
	}
}

PRIVATE void vCbSysCtrl(uint32 u32Device, uint32 u32ItemBitmap)
{
	uint8 i, j, N;
	uint16 k;
	if (E_AHI_DEVICE_SYSCTRL == u32Device) {
		if (E_AHI_DIO3_INT == u32ItemBitmap) {
			vAHI_DioInterruptEnable(0, IRIN);//关中断
			//vAHI_DelayXms(2);
			if (vGet_Bit(IRIN) == 1) {
				//vPrintf("Error0!\n");
				vAHI_DioInterruptEdge(0, IRIN); //下降沿触发
				vAHI_DioInterruptEnable(IRIN, 0);
				for (i = 0; i < 7; i++) {
					IRCOM[i] = 0x00;
				}
				return;
			}
			k = 100;
			//确认IR信号出现
			while (vGet_Bit(IRIN) == 0 && k) {		//等IR变为高电平,跳过9ms的前导低电平信号。
				vAHI_DelayXus(200);
				k--;
			}
			k = 100;
			while (vGet_Bit(IRIN) == 1 && k) { //等 IR 变为低电平,跳过4.5ms的前导高电平信号。
				vAHI_DelayXus(200);
				k--;
			}
			for (i = 0; i < 7; i++) {
				IRCOM[i] = 0x00;
			}
			for (i = 0; i < 4; i++) {						//收集四组数据
				for (j = 0; j < 8; j++) {					//每组数据有8位
					k = 100;
					while (vGet_Bit(IRIN) == 0 && k) {				//等 IR 变为高电平
						vAHI_DelayXus(200);
						k--;
					}
					k = 100;
					N = 0;
					while (vGet_Bit(IRIN) == 1 && k) {				//计算IR高电平时长
						vAHI_DelayXus(200);
						N++;
						k--;
						if (N >= 30) {
							//vPrintf("Error1!\n");
							vAHI_DioInterruptEdge(0, IRIN); //下降沿触发
							vAHI_DioInterruptEnable(IRIN, 0);
							for (i = 0; i < 7; i++) {
								IRCOM[i] = 0x00;
							}
							return;//0.14ms计数过长自动离开。
						}
					}									//高电平计数完毕
					IRCOM[i] = IRCOM[i] >> 1;			//数据最高位补“0”
					if (N >= 8) {
						IRCOM[i] = IRCOM[i] | 0x80;		//数据最高位补“1”
					}
					N = 0;
				}
			}
			if (IRCOM[2] == ((~IRCOM[3]) & 0x000000FF)) {
				IRCOM[5] = IRCOM[2] & 0x0F;     			//取键码的低四位
				IRCOM[6] = IRCOM[2] >> 4;       			//右移4次,高四位变为低四位

				if (IRCOM[5] > 9) {
					IRCOM[5] = IRCOM[5] + 0x37;
				} else {
					IRCOM[5] = IRCOM[5] + 0x30;
				}
				if (IRCOM[6] > 9) {
					IRCOM[6] = IRCOM[6] + 0x37;
				} else {
					IRCOM[6] = IRCOM[6] + 0x30;
				}
				vPrintf("Receive Key Code: %c%c\n", IRCOM[6], IRCOM[5]);
			}
			for (i = 0; i < 7; i++) {
				IRCOM[i] = 0x00;
			}
			vAHI_DioInterruptEdge(0, IRIN); //下降沿触发
			vAHI_DioInterruptEnable(IRIN, 0);
		}
	}
}

//初始化DIO
void vInitDio(void)
{
	//上拉输入
	vAHI_DioSetDirection(IRIN, 0);
	vAHI_DioSetPullup(IRIN, 0);

	vAHI_DioInterruptEdge(0, IRIN); //下降沿触发
	vAHI_DioInterruptEnable(IRIN, 0);
	vAHI_SysCtrlRegisterCallback(vCbSysCtrl);
}

uint8 vGetDioBit(uint32 dio)
{
	uint8 bit;
	for(bit = 0; bit < 32; bit++){
		if(dio & 0x01){
			break;
		}
		dio = dio >> 1;
	}
	return (bit);
}

PUBLIC void AppColdStart(void)
{
	/*等待系统时钟切换为外部32MHz晶振*/
	while (bAHI_GetClkSource() == TRUE);

	vAHI_WatchdogStop();
	(void) u32AHI_Init();

	vUartInit();
	vInitDio();

	vAHI_DelayXms(2000);
	vPrintf("System init...\n");
	vPrintf("IRIN DIO: DIO%d\n", vGetDioBit(IRIN));

	while (1) {

	}
}

PUBLIC void AppWarmStart(void)
{
	AppColdStart();
}

效果图:

在这里插入图片描述

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

菠萝蚊鸭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值