【程序】AVR单片机ATMega16A使用定时器1的输入捕获功能进行红外遥控解码

本文介绍了一个基于ATMega16A单片机的红外遥控信号解析项目。通过配置单片机的定时器、中断等功能,实现对红外信号的捕捉、解码,并使用数码管动态显示解码后的用户码和操作码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/*
单片机: ATMega16A
晶振: 外部11.0592MHz (熔丝位: 低E1 高99)
红外接收头端口: PD6(ICP1)
数码管段选: PA
数码管位选: 从高到低PC0~7
LED灯: PB7~0
*/
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/sfr_defs.h>

const uint8_t seg8[] PROGMEM = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90, 0x88, 0x83, 0xc6, 0xa1, 0x86, 0x8e}; // 共阳数码管段码表
uint8_t repeat_cnt = 0; // 重复按键次数
uint16_t user_code = 0, op_code = 0; // 用户码和操作码

int main(void)
{
	// 数码管管脚配置
	DDRA = 0xff;
	PORTA = 0xff; // 先熄灭数码管
	DDRC = 0xff;

	// 数码管动态扫描定时器配置 (CTC自动重装模式)
	OCR0 = 85; // 定时时间: 2ms
	TCNT0 = OCR0; // 开启定时器后立即溢出, 点亮数码管
	TIMSK = _BV(OCIE0); // 打开定时器自动重装中断
	sei(); // 打开总中断
	TCCR0 |= _BV(WGM01) | _BV(CS02); // 开定时器0, 时钟为256分频 (基准: 23.148us)

	// PB口LED灯配置
	PORTB = ~repeat_cnt;
	DDRB = 0xff;

	// 输入捕获定时器配置
	TIMSK |= _BV(TICIE1); // 打开输入捕获中断
	TCCR1B = _BV(CS11); // 开定时器1, 时钟为8分频 (基准: 0.723us)
	TCCR1B |= _BV(ICNC1); // 打开噪声过滤功能 (可选操作)

	while (1); // 在主循环中可对按键操作进行处理
}

// 数码管动态扫描
ISR(TIMER0_COMP_vect)
{
	static uint8_t mask = _BV(PORTC7); // 从最低位开始扫描
	static uint32_t numbuf;
	sei(); // 允许输入捕获中断抢占本中断
	if (mask == _BV(PORTC7))
		numbuf = op_code | ((uint32_t)user_code << 16); // 装入要显示的数字
	// 左边显示用户码, 右边显示操作码

	PORTC = 0xff; // 熄灭数码管
	PORTA = pgm_read_byte(&seg8[numbuf & 0x0f]); // 设置显示字符 (十六进制格式)
	PORTC = ~mask; // 设置数码管位选
	numbuf >>= 4;
	if (mask == _BV(PORTC0))
		mask = _BV(PORTC7);
	else
		mask >>= 1; // 扫描下一位
}

// 只有定时器1才有输入捕获功能
ISR(TIMER1_CAPT_vect)
{
	static uint8_t IR_step = 0; // 步骤号
	static uint16_t high_time = 0, low_time = 0; // 保存高低电平的持续时间
	static uint32_t data; // 从高位到低位依次是: 操作码反码, 操作码, 用户识别码反码, 用户识别码
	
	TCNT1 = 0; // 定时器清零
	TCCR1B ^= _BV(ICES1); // 设置(翻转)下次捕获的电平
	TIFR = _BV(ICF1); // 清除可能产生的ICF1标志位, 特别注意: 由于是写1清零, 所以这里的运算符绝对不能写成|=
					  // 否则可能会同时把已经置位但还未处理的OCF0也清除掉, 从而错过一个定时器溢出中断导致数码管闪烁

	if (TCCR1B & _BV(ICES1)) // 若本次捕获的是下降沿
	{
		high_time = ICR1; // 记录高电平持续时间
		if (IR_step == 0)
		{
			if (low_time >= 11757 && low_time <= 13140)
			{
				// 低电平在8.5~9.5ms之间才合法
				if (high_time >= 5533 && high_time <= 6916)
				{
					// 若高电平在4~5ms之间, 则认为是引导码
					data = 0;
					IR_step++; // 开始读取用户码
				}
				else if (high_time >= 2075 && high_time <= 3458)
				{
					// 若高电平在1.5~2.5ms之间, 则认为是重复码
					repeat_cnt++; // 重复次数加1
					PORTB = ~repeat_cnt; // 在LED上显示出来
				}
			}
		}
		else
		{
			// 低电平长度必须在0.4~0.7ms之间
			if (low_time >= 553 && low_time <= 968)
			{
				data >>= 1;
				if (high_time > 1162)
					data |= 0x80000000; // 如果高电平长度大于0.84ms, 则认为本位为1
				if (IR_step == 32)
				{
					// 若所有位都已读取完毕, 则将解码结果保存到全局变量中
					user_code = data & 0xffff;
					op_code = data >> 16;
					IR_step = 0;
					repeat_cnt = 0; // 重复次数清零
					PORTB = ~repeat_cnt;
				}
				else
					IR_step++; // 读下一位
			}
			else
				IR_step = 0; // 低电平长度不合法, 退出
		}
	}
	else
		low_time = ICR1; // 若本次捕获的是上升沿, 则记录低电平持续时间
}

数码管显示示例:FF00E718

其中用户码:0xff+0x00=0xff

操作码:0xe7+0x18=0xff

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

巨大八爪鱼

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

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

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

打赏作者

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

抵扣说明:

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

余额充值