OLED显示红外遥控键码
基本原理
本遥控器的编码是NEC编码,为PWM(脉冲宽度调制)。
发射红外载波的时间固定,通过改变不发射载波的时间来改变占空比。
- 逻辑“0”是由0.56ms的38KHZ载波和0.560ms的无载波间隔组成;
- 逻辑“1”是由0.56ms的38KHZ载波和1.685m 的无载波间隔组成;
- 结束位是0.56ms的38K载波。
- 重复码由9ms红外脉冲和2.25ms的无红外脉冲以及560us的红外脉冲组成。
若发了一次命令码之后,一直按住遥控器按键并未松手,遥控器发射端将不会再发送命令码,而是每隔 110ms 时间,发送一段重复码。

过程分析
初始化部分
通用配置过程:
- 将相关的gpio和硬件挂载到RCC
- 初始化GPIO
- 初始化TIM时基单元
- 初始化IC输入捕获
- 初始化NVIC中断管理
- 实现中断处理函数
- 启用中断
- 启动定时器

TIM3的CH1通道位于PA6引脚。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode =GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed =GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
配置时基单元。arr和psc作为函数参数传入,支持动态调整。
在本文中,分频系数为72,这样标准周期就是1us,方便后续计算。
TIM_InternalClockConfig(TIM3);
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_Period = arr - 1;
TIM_TimeBaseStructure.TIM_Prescaler = psc - 1;
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);
配置输入捕获。将TIM_ICInitStructure.TIM_ICFilter设置为0xf是为了对输入捕获信号进行滤波,滤波时间为8个时钟周期。
- 对应16个采样点的平均,意味着最终捕获值是16个相邻采样值的平均值。
- 这相当于对输入信号进行8个时钟周期的滤波,因为每个采样周期占用一个时钟周期。
TIM_ICInitTypeDef TIM_ICInitStructure;
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
TIM_ICInitStructure.TIM_ICFilter = 0xf;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_CKD_DIV1;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInit(TIM3,&TIM_ICInitStructure);
配置中断优先级。中断优先级的配置方案需要在主函数中设置:NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_Init(&NVIC_InitStructure);
在之前的《PWM输入/输出》中,输入捕获采用的是事件方式:
TIM_SelectInputTrigger(TIM3,TIM_TS_TI1FP1);
TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset);
当时没有设置中断,设置了事件。事件由硬件自动完成,不需要我们实现中断处理函数,只需要指定处理方式即可。
我们这次用不到事件。
涉及各种码的判断,以及信号处理阶段的判断,硬件事件难以处理这种复杂任务,需要通过中断实现。
启用中断标志位,在TIM3的CC1通道出现下降沿时,自动触发TIM_IT_CC1中断:
TIM_ITConfig(TIM3,TIM_IT_CC1,ENABLE);
配置完毕,启动TIM定时器:
TIM_Cmd(TIM3,ENABLE);
中断处理函数
通过输入捕获,判断两个下降沿之间的时间间隔:
- 引导码:13.5ms
- 重复码:11.25ms
- 逻辑0:1.12ms
- 逻辑1:2.245ms
将输入捕获通道的GPIO设置为上拉输入。在没有外界信号时会自动上拉到高电平。
时间间隔通过TIM_GetCounter(TIM3)获取。TIM定时器设置的标准周期为1us,返回值即为下降沿之间的时间间隔,单位为us。
获取之后通过TIM_SetCounter(TIM3,0);设置寄存器值为0,相当于重新开始计数。由于一个周期的开始和结束都是下降沿,所以上一个周期的结束接着就是下一个周期的开始,计数值误差可以接受。
操作类似于一个状态机:
- 初始处于空闲状态,TIM定时器的值是无意义的。在第一个下降沿到来时,设置TIM寄存器值为0,开始计数,进入准备状态。
- 准备状态下,第二个下降沿到来时,根据时长判断是引导码还是重复码。重复码则回到空闲状态。引导码还需要读取具体的指令,进入接受状态。
- 接收状态下,逐位接收,共32位。接收结束后回到空闲状态。等待下一个响应。
为什么引导码和重复码在同一个状态下判断?
对于上面四种码的时间间隔,很明显:引导码与重复码相近,逻辑0和逻辑1相近。
如果同一个阶段去判断时间间隔差距过大的码,可能造成数据遗漏。
如何按位接收?
需要用到指针,通过模运算、位运算实现。
指令一共有32位,需要一个char[4]类型的数组。
创建一个指针,其实是整型,
- 对8做除法,用于对指定下标的char赋值
- 对8取模之后,值介于
[0,7],为指定位赋值
这种做法在存储器扩展、cache映射中都有应用,映射的常用方式就是取模。
如果学过,应该不会陌生。
数据校验
NEC编码的数据包括:
8位地址码+8位地址码的反码+8位命令码+8位命令码的反码
验证操作就是将反码部分按位取反,判断是否相等。
Addr_Temp = ~IR_DATA[1];
Cmd_Temp = ~IR_DATA[3];
if (IR_DATA[0] == Addr_Temp && IR_DATA[2] == Cmd_Temp) //数据验证
{IR_Address = IR_DATA[0];IR_Command = IR_DATA[2];IR_DataFlag = 1;
}
代码实现
到目前,需要创建如下变量:
uint8_t IR_State:记录当前状态:空闲/准备/接收uint16_t IR_Time:下降沿间隔时间:引导/重复/0/1uint8_t IR_RepeatFlag,IR_DataFlag:数据状态:重复/有数据uint8_t IR_DATA[4]:数据:地址码+地址码反码+命令码+命令码反码uint8_t IR_pData:指针:对IR_DATA进行段选和位选
void TIM3_IRQHandler()
{uint8_t Addr_Temp,Cmd_Temp;if (TIM_GetITStatus(TIM3,TIM_FLAG_CC1) != RESET){if (IR_State == 0) //空闲态{TIM_SetCounter(TIM3,0);IR_State = 1;}else if (IR_State == 1) //准备态{IR_Time = TIM_GetCounter(TIM3);if (IR_Time > 13500 - 500 && IR_Time < 13500+500) //Start信号{IR_State = 2;}else if (IR_Time > 11250 - 500 && IR_Time < 11250+500) //Repeat信号{IR_State = 0;IR_RepeatFlag = 1;}else{}TIM_SetCounter(TIM3,0);}else if (IR_State == 2) //接收态{IR_Time = TIM_GetCounter(TIM3);if (IR_Time > 1120 -500 && IR_Time < 1120+500) //逻辑0{IR_DATA[IR_pData/8] &= ~(0x01 << (IR_pData %8));IR_pData++;}else if (IR_Time > 2250 -500 && IR_Time < 2250 + 500) //逻辑1{IR_DATA[IR_pData/8] |= (0x01 << (IR_pData %8));IR_pData++;}else {IR_pData = 0;IR_State = 1;}if (IR_pData>=32) //如果接收完32位数据{IR_pData = 0;Addr_Temp = ~IR_DATA[1];Cmd_Temp = ~IR_DATA[3];if (IR_DATA[0] == Addr_Temp && IR_DATA[2] == Cmd_Temp) //数据验证{IR_Address = IR_DATA[0];IR_Command = IR_DATA[2];IR_DataFlag = 1;}IR_State = 0;}TIM_SetCounter(TIM3,0);}else{}TIM_ClearITPendingBit(TIM3,TIM_IT_CC1);}
}
实验效果
飞线
红外部分并不直接与PA6相连,需要飞线。
用杜邦线连接IF和TIM3的CH1所在的PA6即可。

在主函数中调用
uint8_t Address;
uint8_t Command;
uint8_t Num;
int main(void)
{NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);Remote_Init(30000,72);OLED_Init();OLED_ShowString(1,1,"ADDR CMD NUM");OLED_ShowString(2,1,"00 00 000");while(1){if (IR_GetDataFlag() || IR_GetRepeatFlag()){Address = IR_GetAddress();Command = IR_GetCommand();OLED_ShowHexNum(2,1,Address,2);OLED_ShowHexNum(2,6,Command,2);if (Command == IR_VOL_ADD){Num++;}if(Command == IR_VOL_MINUS){Num--;}OLED_ShowNum(2,10,Num,3);}}
}
VID_20240215_014048
参考
- STM32F103C8T6引脚定义.xlsx
- stm32 使用说明+笔记(必读).pdf
- 32版开发板原理图.pdf
相关文章:
OLED显示红外遥控键码
基本原理 本遥控器的编码是NEC编码,为PWM(脉冲宽度调制)。 发射红外载波的时间固定,通过改变不发射载波的时间来改变占空比。 逻辑“0”是由0.56ms的38KHZ载波和0.560ms的无载波间隔组成;逻辑“1”是由0.56ms的38KHZ…...
LabVIEW智能温度监控系统
LabVIEW智能温度监控系统 介绍了一个基于LabVIEW的智能温度监控系统,实现对工业环境中温度的实时监控与调控。通过集成传感器技术和LabVIEW软件平台,系统能够自动检测环境温度,及时响应温度变化,并通过图形用户界面(GUI)为用户提…...
专业140+总分420+浙江大学842信号系统与数字电路考研经验电子信息与通信,真题,大纲,参考书。
今年考研已经结束,初试专业课842信号系统与数字电路140,总分420,很幸运实现了自己的目标,被浙大录取,这在高考是想都不敢想的学校,在考研时实现了,所以大家也要有信心,通过自己努力实…...
C语言学习day15:数组强化训练
题目一: 称体重:分别给10个值,来获得最大值 思路: 定义数组,给数组内赋10个值第一个下标的值与第二个下标的值进行比较定义max,将比较得来的较大的值赋值给max一直比较直到比较到最后一个下标࿰…...
缓存穿透、缓存击穿与缓存雪崩
缓存穿透、缓存击穿与缓存雪崩 1.本质区别 缓存穿透指的是数据库不存在数据,导致无法缓存,每次查询都查数据库,数据库压垮 缓存击穿指的是缓存键值对key过期了,key过期期间,大量请求访问,不经过缓存&…...
一周学会Django5 Python Web开发-项目配置settings.py文件-模版配置
锋哥原创的Python Web开发 Django5视频教程: 2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~共计17条视频,包括:2024版 Django5 Python we…...
CF1845 D. Rating System [思维题+数形结合]
传送门:CF [前题提要]:自己在做这道题的时候思路完全想错方向,导致怎么做都做不出来,看了题解之后感觉数形结合的思考方式挺好的(或者这种做法挺典的),故写篇题解记录一下 题目很简单,不再解释.先不考虑 k k k,想想是一种什么情况?很显然应该是跟下图一样是一个折线图的变化.…...
HeidiSQL安装配置(基于小皮面板(phpstudy))连接MySQL
下载资源 对于这款图形化工具,博主建议通过小皮面板(phpstudy)来下载即可,也是防止你下载到钓鱼软件,小皮面板(phpstudy)如果你不懂是什么,请看下面链接这篇博客 第二篇:…...
【蓝桥2013】错误票据
错误票据 题目描述 某涉密单位下发了某种票据,并要在年终全部收回。 每张票据有唯一的 ID 号。全年所有票据的 ID 号是连续的,但 ID 的开始数码是随机选定的。 因为工作人员疏忽,在录入 ID 号的时候发生了一处错误,造成了某个…...
nvm对node版本进行管理及疑难解决,vue项目搭建与启动
一、nvm安装与node版本管理 nvm安装 1、nvm地址:https://github.com/coreybutler/nvm-windows/releases 2、无需配置安装包,nvm-setup-v1.1.10.zip 解压后双击nvm-setup.exe,选择安装路径,一路next即可 打开dos窗口输入nvm vers…...
Redisson分布式锁 原理 + 运用 记录
Redisson 分布式锁 简单入门 pom <dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.13.6</version></dependency>配置类 package com.hmdp.config;import org.redisson.Redisson;…...
Spring Boot 笔记 021 项目部署
1.1 引入坐标,并双击package打包成jar包 1.2 在服务器上运行jar包 1.3 使用postman测试 2.1 运行配置 2.1.1 命令更改端口 java -jar big-event-1.0-SNAPSHOT.jar --server.port7777 2.1.2 环境变量更新(略) 2.1.3 外部配置文件,…...
新技术革命开始了,Sora一出,所有的视频人、电影人都下岗
Sora一出,所有的视频人、电影人都下岗! Sora直接用文本制作长达60秒的视频长镜头,也就是说,将来,只需要输入分镜脚本,电影就可以制作出来,不再需要几十人几百人声势浩大地去“拍”了,…...
【FPGA开发】Modelsim和Vivado的使用
本篇文章包含的内容 一、FPGA工程文件结构二、Modelsim的使用三、Vivado的使用3.1 建立工程3.2 分析 RTL ANALYSIS3.2.1 .xdc约束(Constraints)文件的产生 3.3 综合 SYNTHESIS3.4 执行 IMPLEMENTATION3.5 烧录程序3.6 程序固化3.6.1 SPI约束3.6.2 .bin文…...
现代浏览器对 es模块 【esm】原生支持
现代浏览器对 ES(ECMAScript)模块的原生支持是指浏览器可以直接解析和执行 JavaScript 文件中的 ES 模块语法,无需额外的工具或转换。 具体来说,当浏览器遇到 import 和 export 关键字时,会将其识别为 ES 模块语法&…...
修改SpringBoot中默认依赖版本
例如SpringBoot2.7.2中ElasticSearch版本是7.17.4 我希望把它变成7.6.1...
网络安全最典型基础靶场-DVWA-本地搭建与初始化
写在前面: 之前也打过这个 DVWA 靶场,但是是在虚拟机环境下的一个小块分区靶场; 本篇博客主要介绍在本地搭建 DVWA 靶场以及靶场的初始化,后续会陆续更新通关教程。 由于我们是在本地搭建,则需要基于你已经装好 phpstu…...
算法-----高精度2(高精度乘法,高精度除法,高精度斐波那锲数列)
高精度乘法 对于高精度乘法来说似乎不像高精度加减法那样简单了,我们似乎得一个一个加了,因为我们都知道 abaaaaa…a(b个a)。如果真要这要的话那1e9*1e9不得超时啊,所以不能这样,我们还是得从乘法竖式入手 这样看似乎看不出来什…...
windows vs 自己编译源码 leveldb 然后使用自己编译的文件
1 准备源码文件 1.1 第一种方法 git下载源码 vs项目中git leveldb源码和git third_party googletest-CSDN博客 1.2 第二种方法 手动下载 然后把第三方的源码下载 复制到 third_party 对应的文件夹中 没有文件夹 third_party -> powershell mkdir third_party 2 编译lev…...
基于GPT一键完成数据分析全流程的AI Agent: Streamline Analyst
大型语言模型(LLM)的兴起不仅为获取知识和解决问题开辟了新的可能性,而且催生了一些新型智能系统,例如旨在辅助用户完成特定任务的AI Copilot以及旨在自动化和自主执行复杂任务的AI Agent,使得编程、创作等任务变得高效…...
网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...
练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...
【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)
要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况,可以通过以下几种方式模拟或触发: 1. 增加CPU负载 运行大量计算密集型任务,例如: 使用多线程循环执行复杂计算(如数学运算、加密解密等)。运行图…...
聊一聊接口测试的意义有哪些?
目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开,首…...
用机器学习破解新能源领域的“弃风”难题
音乐发烧友深有体会,玩音乐的本质就是玩电网。火电声音偏暖,水电偏冷,风电偏空旷。至于太阳能发的电,则略显朦胧和单薄。 不知你是否有感觉,近两年家里的音响声音越来越冷,听起来越来越单薄? —…...
Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...
Linux部署私有文件管理系统MinIO
最近需要用到一个文件管理服务,但是又不想花钱,所以就想着自己搭建一个,刚好我们用的一个开源框架已经集成了MinIO,所以就选了这个 我这边对文件服务性能要求不是太高,单机版就可以 安装非常简单,几个命令就…...
