前言:單總線類傳感器-紅外遙控的實作
目錄
一、紅外遙控工作原理講解
1、光的基礎知識
2、紅外通信
3、紅外NEC協定詳解
二、紅外遙控執行個體講解
1、實驗目的:按下遙控按鍵,主機通過紅外接收器接收到信号并解碼,識别出按鍵的指令碼,列印出對應的按鍵符号。
2、Cubmx配置
3、實作思路分析
(1)協定分析:
(2)功能分析
(3)具體實作
(4)實驗示範和源碼
一、紅外遙控工作原理講解
1、光的基礎知識
- 光的本質:光的本質是電磁波,其傳播本質上是一種粒子振動。廣義上,光是指所有的電磁波譜。狹義上的光是人類眼睛可以看見的一種電磁波,也稱可見光。
- 光的波長:是指波在一個振動周期内傳播的距離。光的波長由光的頻率以及傳播的媒體決定,光通過不同媒體的時候,頻率不變而波長發生改變
- 光的顔色: 是由它的波長來決定的,各種顔色有各自的波長,人的眼睛能看到的可見光按波長從長到短排列,依次為紅、橙、黃、綠、青、藍、紫。
- 可見光:光整個電磁波譜包括了無線電波、紅外線、紫外線以及X射線等。它們的波長不同,其中波長在380~760納米之間就是一般的可見光。依次為紅、橙、黃、綠、青、藍、紫。比紫光波長還短的光叫紫外線,比紅光波長還長的光叫紅外線。
- 紅外線:是波長介于微波與可見光之間的電磁波,波長在1mm到760納米(nm)之間,比紅光長的非可見光。 高于絕對零度(-273.15℃)的物質都可以産生紅外線。現代實體學稱之為熱射線。我們把紅光之外的輻射叫做紅外線(紫光之外是紫外線),人的肉眼不可見。
2、紅外通信
無線遠端遙控技術: 又稱為遙控技術,是指實作對被控目标的遙遠控制,在工業控制、航空航天、家電領域應用廣泛。
紅外遙控: 是一種無線、非接觸控制技術,具有抗幹擾能力強,資訊傳輸可靠,功耗低,成本低,易實作等顯著優點,被諸多電子裝置特别是家用電器廣泛采用,并越來越多的應用到計算機和手機系統中。
紅外通訊:就是通過紅外線傳輸資料。發射器發出紅外信号,接收器接收到信号進行解析。
紅外遙控器: 紅外遙控器是利用一個紅外發光二極管,以紅外光為載體來将按鍵資訊傳遞給接收端的裝置。紅外光對于人眼是不可見的,是以使用紅外遙控器不會影響人的視覺(可以打開手機攝像頭,遙控器對着攝像頭按,可以看到遙控器發出的紅外光)。
信号調制: 日常生活環境中有很多紅外光源,太陽、蠟燭火光、白熾燈、甚至是我們的身體。這些紅外光源都可能會對我們的接收裝置産生幹擾,為了屏蔽幹擾,隻接收有效資訊,我們就需要用到調制。 通過調制我們可以把指定的數字信号轉換為特定頻率的紅外光進行發送,調制載波頻率一般在30khz到60khz之間,大多數使用的是38kHz
紅外接受器: 紅外線接收器是一種可以接收紅外信号并能獨立完成從紅外線接收到輸出與TTL電平信号相容的器件,體積和普通的塑封三極管差不多,适合于各種紅外線遙控和紅外線資料傳輸。
信号解調: 解調就是将模拟信号轉換成數字信号。紅外接收器接收到外部發射器傳過來的紅外信号後,會按照固定的協定去解析信号,并轉換成數字信号輸出。
3、紅外NEC協定詳解

二、紅外遙控執行個體講解
1、實驗目的:按下遙控按鍵,主機通過紅外接收器接收到信号并解碼,識别出按鍵的指令碼,列印出對應的按鍵符号。
所要用到的硬體資源如下: 1) 紅外接收頭,遙控接收頭在闆子上,與MCU 的連接配接原理圖如下 2) 紅外遙控器
我們紅外接收器連接配接的是PA8,一根總線實作通信協定。
2、Cubmx配置
使能led,使能168mhz時鐘,定時器Tim5用于輸入捕獲,pa8外部中斷配置
觸發中斷方式是上升沿下降沿都觸發
3、實作思路分析

(1)協定分析:
紅外遙控傳輸的資料格式:位址碼+位址反碼+指令碼+指令反碼
根據協定規定,每個位址碼或指令碼8位,一條資料一共32位資料。
- 我們定義一個結構體Remote_Infrared_data_struct,定義4個成員來表示位址碼和指令碼。
- 再定義一個共同體Remote_Infrared_data_union,定義2個成員,一個儲存上面的結構體和完整32位資料。不同資料類型的資料可以使用共同的存儲區域
(2)功能分析
開始拉低9ms,接着是一個4.5ms的高脈沖,通知器件開始傳送資料了接着是發送4個8位二進制碼,第一二個是遙控識别碼(REMOTE_ID),第一個為正碼(0),第二個為反碼(255),接着兩個資料是鍵值,第一個為正碼第二個為反碼.發送完後40ms,遙控再發送一個9ms低,2ms高的脈沖,表示按鍵的次數,出現一次則證明隻按下了一次,如果出現多次,則可以認為是持續按下該鍵。
處理紅外接收
定義一個标志位 bBitCounter 記錄鍵盤幀的位數。每次信号進來都加1,代表進來的次數。通過bBitCounter次數和計數器時長來判定起始碼和資料碼。最後讀取按鍵值。
(3)具體實作
RomoteInfrared.h
#include "stm32f4xx_hal.h"
#define Remote_Infrared_DAT_INPUT HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_8)
typedef struct _Remote_Infrared_data_struct //定義紅外線接收到的資料結構體類型
{
uint8_t bKeyCodeNot; //按鍵的的ASIIC碼值 按鍵反碼
uint8_t bKeyCode; //shift鍵按下标志 按鍵碼
uint8_t bIDNot; //斷碼标志位 使用者反碼
uint8_t bID; //新鍵标志位 使用者碼
}Remote_Infrared_data_struct;
typedef union _Remote_Infrared_data_union //定義紅外線接收到的資料結構體類型
{
Remote_Infrared_data_struct RemoteInfraredDataStruct; //上面定義的紅外線接收到的資料結構體類型
uint32_t uiRemoteInfraredData; // 記錄 使用者碼使用者反碼按鍵碼按鍵反碼
}Remote_Infrared_data_union;
void Remote_Infrared_KEY_ISR(void);
uint8_t Remote_Infrared_KeyDeCode(void);
RomoteInfrared.c
#include "RemoteInfrared.h"
#define REPEAT_KEY 0xEE
extern __IO uint32_t GlobalTimingDelay100us;
extern __IO uint32_t GlobalTimingDelay100usTx;
__IO uint32_t FlagGotKey = 0;
__IO Remote_Infrared_data_union RemoteInfrareddata;
/************************************************************************
//處理紅外接收
-------------------------協定--------------------------
開始拉低9ms,接着是一個4.5ms的高脈沖,通知器件開始傳送資料了
接着是發送4個8位二進制碼,第一二個是遙控識别碼(REMOTE_ID),第一個為
正碼(0),第二個為反碼(255),接着兩個資料是鍵值,第一個為正碼
第二個為反碼.發送完後40ms,遙控再發送一個9ms低,2ms高的脈沖,
表示按鍵的次數,出現一次則證明隻按下了一次,如果出現多次,則可
以認為是持續按下該鍵.
*名稱: Remote_Infrared_KEY_ISR(INT11_vect )
*功能: INT0中斷服務程式
*參數: 無
*傳回: 無
*************************************************************************/
// 檢測脈沖寬度最長脈寬為5ms
const uint32_t TIME_DELAY_6MS = 60;
const uint32_t TIME_DELAY_10MS = 100;
//按鍵中斷服務程式ISR
void Remote_Infrared_KEY_ISR(void)
{
static __IO uint8_t bBitCounter = 0; //鍵盤幀位計數
static __IO uint32_t bKeyCode = 0;
bBitCounter++; //每次信号進來都加加,代表進來的次數
if(bBitCounter == 1) // 開始拉低9ms
{
if(Remote_Infrared_DAT_INPUT) // 高電平無效
{
bBitCounter = 0;
}
else
{
GlobalTimingDelay100us = TIME_DELAY_10MS; //100賦給變量 ,這個變量會在中斷函數裡減
}
}
else if(bBitCounter == 2) // 4.5ms的高脈沖
{
if(Remote_Infrared_DAT_INPUT)
{
if((GlobalTimingDelay100us > 2) && (GlobalTimingDelay100us < 18)) //GlobalTimingDelay100us檢查到減到什麼程度了 100-90應該是10 2-18範圍内
{
GlobalTimingDelay100us = TIME_DELAY_6MS;
}
else
{
bBitCounter = 0;
//printf(".");
}
}
else
{
bBitCounter = 0;
}
}
else if(bBitCounter == 3) // 4.5ms的高脈沖
{
if(Remote_Infrared_DAT_INPUT)
{
bBitCounter = 0;
}
else
{
if((GlobalTimingDelay100us > 5) && (GlobalTimingDelay100us < 20)) //起始碼 4.5ms 5<60-45<20
{
GlobalTimingDelay100us = TIME_DELAY_6MS;
//printf("引導碼");
}
else if((GlobalTimingDelay100us > 32) && (GlobalTimingDelay100us < 46)) //重複碼 2.25ms
{
//重複碼
bBitCounter = 0; //檢測到結束信号 次數置0
RemoteInfrareddata.uiRemoteInfraredData = bKeyCode; //把按鍵碼儲存到結構體
//RemoteInfrareddata.uiRemoteInfraredData = REPEAT_KEY;
bBitCounter = 0;
FlagGotKey = 1;
}
else
{
bBitCounter = 0;
//printf("%d&", GlobalTimingDelay100us);
}
}
}
else if(bBitCounter > 3 && bBitCounter < 68) //接收8位資料
{
if(Remote_Infrared_DAT_INPUT) //檢測資料脈沖低電平的時間
{
if((GlobalTimingDelay100us > 50) && (GlobalTimingDelay100us < 58))
{
GlobalTimingDelay100us = TIME_DELAY_6MS;
}
else
{
bBitCounter = 0;
//printf("#");
}
}
else
{
if((GlobalTimingDelay100us > 50) && (GlobalTimingDelay100us < 58)) // '0' 0.56ms左右
{
GlobalTimingDelay100us = TIME_DELAY_6MS;
bKeyCode <<= 1; // MSB First
bKeyCode += 0x00;
}
else if((GlobalTimingDelay100us > 40) && (GlobalTimingDelay100us < 48)) //'1' 1.685ms左右
{
GlobalTimingDelay100us = TIME_DELAY_6MS;
bKeyCode <<= 1; // MSB First
bKeyCode += 0x01;
}
else
{
bBitCounter = 0;
}
}
if(bBitCounter == 67)
{
RemoteInfrareddata.uiRemoteInfraredData = bKeyCode;
bBitCounter = 0;
FlagGotKey = 1;
//printf("KeyCode = 0x%X", bKeyCode);
}
}
else
{
bBitCounter = 0;
//printf("KeyCode = 0x%X", bKeyCode);
}
}
/************************************************************************
*名稱: unsigned char Remote_Infrared_KeyDeCode(unsigned char bKeyCode)
*功能: PS2鍵盤解碼程式
*參數: bKeyCode 鍵盤碼
*傳回: 按鍵的ASIIC碼
************************************************************************/
uint8_t Remote_Infrared_KeyDeCode(void)
{
// uint8_t Key = 0xFF;
if (FlagGotKey == 1)//通碼 0沒按,1按了,外部會判斷解析按沒按并傳回值
{
FlagGotKey = 0;//友善下次再進來
if((RemoteInfrareddata.RemoteInfraredDataStruct.bID == (uint8_t)~ RemoteInfrareddata.RemoteInfraredDataStruct.bIDNot) //校驗使用者碼
&& (RemoteInfrareddata.RemoteInfraredDataStruct.bKeyCode == (uint8_t)~ RemoteInfrareddata.RemoteInfraredDataStruct.bKeyCodeNot)) //校驗按鍵碼
{
printf("\n\r IR Receive KeyCode = 0x%02X, ", RemoteInfrareddata.RemoteInfraredDataStruct.bKeyCode);
switch(RemoteInfrareddata.RemoteInfraredDataStruct.bKeyCode) //提取按鍵碼
{
case 0: printf("ERROR");
break;
case 162:printf("POWER");
break;
case 98:printf("UP");
break;
case 2:printf("PLAY");
break;
case 226:printf("ALIENTEK");
break;
case 194:printf("RIGHT");
break;
case 34:printf("LEFT");
break;
case 224:printf("VOL-");
break;
case 168:printf("DOWN");
break;
case 144:printf("VOL+");
break;
case 104:
printf("1");
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9, GPIO_PIN_RESET);
break;
case 152:printf("2");
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9, GPIO_PIN_SET);
break;
case 176:printf("3");
break;
case 48:printf("4");
break;
case 24:printf("5");
break;
case 122:printf("6");
break;
case 16:printf("7");
break;
case 56:printf("8");
break;
case 90:printf("9");
break;
case 66:printf("0");
break;
case 82:printf("DELETE");
break;
default:
printf("Unknown key!");
}
}
else
{
printf("\n\r ERR 0x%08X", RemoteInfrareddata.uiRemoteInfraredData);
}
}
return RemoteInfrareddata.RemoteInfraredDataStruct.bKeyCode;
}
main.c
#include "main.h"
#include "stm32f4xx_hal.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
/* USER CODE BEGIN Includes */
#include<stdio.h>
#include "RemoteInfrared.h"
/* USER CODE END Includes */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
__IO uint32_t GlobalTimingDelay100us;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/
/* USER CODE END PFP */
/* USER CODE BEGIN 0 */
int fputc(int ch,FILE *p)
{
while(!(USART1->SR & (1<<7)));
USART1->DR = (uint8_t)ch;
return ch;
}
/* USER CODE BEGIN 0 */
int fputc(int ch,FILE *p)
{
while(!(USART1->SR & (1<<7)));
USART1->DR = (uint8_t)ch;
return ch;
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
*
* @retval None
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration----------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_TIM5_Init();
/* USER CODE BEGIN 2 */
/*設定SYSTICK為 100us中斷一次 */
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/10000); //原本主頻/1000得到的是1ms中斷,因為我們需要用到計時器100us計數,是以我們除10000,得到的是100us計數1次
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
printf("remote test");
printf("\n\r");
printf("\n\r-------------------------------------------------");
printf("\n\r FS-STM32開發闆 IR紅外線接收實驗程式");
printf("\n\r 請将紅外線接收頭連接配接到開發闆的對應接口");
printf("\n\r 然後用紅外遙控器進行控制,注意序列槽輸出");
printf("\n\r-------------------------------------------------");
printf("\n\r----------------- 協定如下 ----------------------");
printf("\n\r 首先是引導碼: 開始拉低9ms,接着一個4.5ms的高脈沖.");
printf("\n\r 引導碼的作用是通知接收方器準備接收資料;");
printf("\n\r 引導碼之後是4個位元組的二進制碼, 其中前兩個位元組是");
printf("\n\r 遙控識别碼(ID), 第一個為正碼,第二個為反碼,");
printf("\n\r 後兩位元組是鍵值, 第一個為正碼,第二個為反碼.");
printf("\n\r 最後可能有持續按下通知, 上述資料發送完後如果剛才");
printf("\n\r 的按鍵持續被按下, 則發送9ms低,2ms高的脈沖。");
printf("\n\r---------------- 載波為38kHz --------------------");
printf("\n\r 傳輸一個邏輯1需要2.25ms(560us低電平+1680us高電平)");
printf("\n\r 傳輸一個邏輯0需要1.125ms(560us低電平+560us高電平)");
printf("\n\r-------------------------------------------------");
printf("\n\r----- 本實驗在中斷中檢測接收IR紅外線的資料 ------\n\r");
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_8, GPIO_PIN_RESET);
HAL_Delay(1000);
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_8, GPIO_PIN_SET);
HAL_Delay(1000);
Remote_Infrared_KeyDeCode();
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/* USER CODE BEGIN 4 */
void HAL_SYSTICK_Callback(void)
{
if(GlobalTimingDelay100us != 0)
{
GlobalTimingDelay100us--;
}
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
Remote_Infrared_KEY_ISR();
}
/* USER CODE END 4 */
(4)實驗示範和源碼
後期上傳實驗效果和源碼