【正点原子STM32连载】第四十章 红外遥控实验 摘自【正点原子】STM32F103 战舰开发指南V1.2

news2025/2/23 17:21:38

1)实验平台:正点原子stm32f103战舰开发板V4
2)平台购买地址:https://detail.tmall.com/item.htm?id=609294757420
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/thread-340252-1-1.html#

第四十章 红外遥控实验

本章,我们将介绍STM32F103对红外遥控器的信号解码。STM32板子上标配的红外接收头和一个小巧的红外遥控器。我们将利用STM32的输入捕获功能,解码开发板标配的红外遥控器的编码信号,并将编码后的键值在LCD模块中显示出来。
本章分为如下几个小节:
40.1 红外遥控简介
40.2 硬件设计
40.3 程序设计
40.4 下载验证

40.1 红外遥控简介

40.1.1 红外遥控技术介绍
红外遥控是一种无线、非接触控制技术,具有抗干扰能力强,信息传输可靠,功耗低,成本低,易实现等显著优点,被诸多电子设备特别是家用电器广泛采用,并越来越多的应用到计算机系统中。由于红外线遥控不具有像无线电遥控那样穿过障碍物去控制被控对象的能力,所以,在设计红外线遥控器时,不必要像无线电遥控器那样,每套(发射器和接收器)要有不同的遥控频率或编码(否则,就会隔墙控制或干扰邻居的家用电器),所以同类产品的红外线遥控器,可以有相同的遥控频率或编码,而不会出现遥控信号“串门”的情况。这对于大批量生产以及在家用电器上普及红外线遥控提供了极大的方便。由于红外线为不可见光,因此对环境影响很小,再由红外光波动波长远小于无线电波的波长,所以红外线遥控不会影响其他家用电器,也不会影响临近的无线电设备。
40.1.2 红外器件特性
红外遥控的情景中,必定会有一个红外发射端和红外接收端。在本实验中,正点原子的红外遥控器作为红外发射端,红外接收端就是板载的红外接收器,实物图可以查看40.2.3小节原理图部分。要使两者通信成功,收/发红外波长与载波频率需一致,在这里波长就是940nm,载波频率就是38kHz。
红外发射管也是属于二极管类,红外发射电路通常使用三极管控制红外发射器的导通或者截至,在导通的时候,红外发射管会发射出红外光,反之,就不会发射出红外光。虽然我们用肉眼看不到红外光,但是我们借助手机摄像头就能看到红外光。但是红外接收管的特性是当接收到红外载波信号时,OUT引脚输出低电平;假如没有接收到红外载波信号时,OUT引脚输出高电平。
红外载波信号其实就是由一个个红外载波周期组成。在频率为38KHz下,红外载波周期约等于26.3us(1s / 38KHz ≈ 26.3us)。在一个红外载波发射周期里,发射红外光时间8.77us和不发射红外光17.53us,发射红外光的占空比一般为1/3。相对的,整个周期内不发射红外光,就是载波不发射周期。在红外遥控器内已经把载波和不载波信号处理好,我们需要做的就是识别遥控器按键发射出的信号,信号也是遵循某种协议的。
40.1.3 红外编解码协议介绍
红外遥控的编码方式目前广泛使用的是:PWM(脉冲宽度调制)的NEC协议和Philips PPM(脉冲位置调制)的RC-5协议的。开发板配套的遥控器使用的是NEC协议,其特征如下:
1、8位地址和8位指令长度;
2、地址和命令2次传输(确保可靠性);
3、PWM脉冲宽度调制,以发射红外载波的占空比代表“0”和“1”;
4、载波频率为38Khz;
5、位时间为1.125ms或2.25ms;
在NEC协议中,如何为协议中的数据‘0’或者‘1’?这里分开红外接收器和红外发射器。
红外发射器:发送协议数据‘0’ = 发射载波信号560us + 不发射载波信号560us
发送协议数据‘1’ = 发射载波信号560us + 不发射载波信号1680us
红外发射器的位定义如下图所示:
在这里插入图片描述

图40.1.3.1 红外发射器位定义图
红外接收器:接收到协议数据‘0’ = 560us低电平 + 560us高电平
接收到协议数据‘1’ = 560us低电平 + 1680us高电平
红外接收器的位定义如下图所示:
在这里插入图片描述

图40.1.3.2 红外接收器位定义图
NEC遥控指令的数据格式为:同步码头、地址码、地址反码、控制码、控制反码。同步码由一个9ms的低电平和一个4.5ms的高电平组成,地址码、地址反码、控制码、控制反码均是8位数据格式。按照低位在前,高位在后的顺序发送。采用反码是为了增加传输的可靠性(可用于校验)。
我们遥控器的按键▽按下时,从红外接收头端收到的波形如图40.1.3.3所示:
在这里插入图片描述

图40.1.3.3 按键▽所对应的红外波形
从上图中可以看到,其地址码为0,控制码为21(正确解码后00010101)。可以看到在100ms之后,我们还收到了几个脉冲,这是NEC码规定的连发码(由9ms低电平+2.25ms高电平+0.56ms低电平+97.94ms高电平组成),如果在一帧数据发送完毕之后,按键仍然没有放开,则发射重复码,即连发码可以通过统计连发码的次数来标记按键按下的长短/次数。
第二十一章我们曾经介绍过利用输入捕获来测量高电平的脉宽,本章解码红外遥控信号,刚好可以利用输入捕获的这个功能来实现遥控解码。关于输入捕获的介绍,请参考第二十一章的内容。
40.2 硬件设计

  1. 例程功能
    本实验开机在LCD上显示一些信息之后,即进入等待红外触发,如果接收到正确的红外信号,则解码,并在LCD上显示键值和所代表的意义,以及按键次数等信息。LED0闪烁用于提示程序正在运行。
  2. 硬件资源
    1)LED灯
    LED0 – PB5
    2)红外接收头
    REMOTE_IN – PB9
    3)正点原子红外遥控器
    4)串口1(PA9/PA10连接在板载USB转串口芯片CH340上面)
    5)正点原子 2.8/3.5/4.3/7/10寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)
  3. 原理图
    红外遥控接收头与STM32的连接关系,如下图所示:
    在这里插入图片描述

图40.2.1 红外遥控接收头与STM32的连接电路图
红外遥控接收头连接在STM32的PB9(TIM4_CH4)上,进行本实验时不需要额外连线。程序将TIM4_CH4设计输入捕获,然后将接收到的脉冲信号解码就可以了。
开发板配套的红外遥控器外观如图40.2.2所示。
在这里插入图片描述

图40.2.2 红外遥控器
开发板上接收红外遥控器信号的红外管外观如图40.2.3所示。使用时需要遥控器有红外管的一端对准开发板上的红外管才能正确收到信号。
在这里插入图片描述

图40.2.3 开发板上的红外接收管位置
40.3 程序设计
由于红外遥控实验采用的是定时器的输入捕获功能,所以这里大家就需要往前面定时器章节输入捕获实验中重温一下输入捕获功能的配置。下面我们也把红外遥控的配置步骤讲解一下。
红外遥控配置步骤
1)初始化TIMx,设置TIMx的ARR和PSC等参数
HAL库通过调用定时器输入捕获初始化函数HAL_TIM_IC_Init完成对定时器参数初始化。
注意:该函数会调用:HAL_TIM_IC_MspInit函数来完成对定时器底层以及其输入通道IO的初始化,包括:定时器及GPIO时钟使能、GPIO模式设置、中断设置等。
2)开启TIMx和输入通道的GPIO时钟,配置该IO口的复用功能输入
首先开启TIMx的时钟,然后配置GPIO为复用功能输出。本实验我们默认用到定时器4通道4,对应IO是PB9,它们的时钟开启方法如下:
__HAL_RCC_TIM4_CLK_ENABLE(); /* 使能定时器4 /
__HAL_RCC_GPIOB_CLK_ENABLE(); /
开启GPIOB时钟 */
IO口复用功能是通过函数HAL_GPIO_Init来配置的。
3)设置TIMx_CHy的输入捕获模式,开启输入捕获
在HAL库中,定时器的输入捕获模式是通过HAL_TIM_IC_ConfigChannel函数来设置定时器某个通道为输入捕获通道,包括映射关系,输入滤波和输入分频等。
4)使能定时器更新中断,开启捕获功能以及捕获中断,配置定时器中断优先级
通过__HAL_TIM_ENABLE_IT函数使能定时器更新中断。
通过HAL_TIM_IC_Start_IT函数使能定时器并开启捕获功能以及捕获中断。
通过HAL_NVIC_EnableIRQ函数使能定时器中断。
通过HAL_NVIC_SetPriority函数设置中断优先级。
5)编写中断服务函数
定时器4中断服务函数为:TIM4_IRQHandler,当发生中断的时候,程序就会执行中断服务函数。HAL库为了使用方便,提供了一个定时器中断通用处理函数HAL_TIM_IRQHandler,该函数会调用一些定时器相关的回调函数,用于给用户处理定时器中断到了之后,需要处理的程序。本实验我们除了用到更新(溢出)中断回调函数HAL_TIM_PeriodElapsedCallback之外,还要用到捕获中断回调函数HAL_TIM_IC_CaptureCallback。详见本例程源码。
40.3.1 程序流程图
在这里插入图片描述

图40.3.2.1 红外遥控实验程序流程图
40.3.2 程序解析

  1. REMOTE驱动代码
    这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。REMOTE驱动源码包括两个文件:remote.c和remote.h。remote.h和前面定时器输入捕获功能的.h头文件代码相似,这里就不介绍了,详见本例程源码。
    下面我们直接介绍remote.c的程序,下面是与红外遥控初始化相关的函数,其定义如下:
    TIM_HandleTypeDef g_tim4_handle; /* 定时器4句柄 */
/**
 * @brief      	红外遥控初始化
 * @note     	设置IO以及定时器的输入捕获
 * @param      	无
 * @retval     	无
 */
void remote_init(void)
{
    TIM_IC_InitTypeDef tim_ic_init_handle;

    g_tim4_handle.Instance = REMOTE_IN_TIMX;                	/* 通用定时器4 */
    g_tim4_handle.Init.Prescaler = (72-1);    /* 预分频器,1M的计数频率,1us加1 */
    g_tim4_handle.Init.CounterMode = TIM_COUNTERMODE_UP;  	/* 向上计数器 */
    g_tim4_handle.Init.Period = 10000;                        	/* 自动装载值 */
    g_tim4_handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    HAL_TIM_IC_Init(&g_tim4_handle);
    
    /* 初始化TIM4输入捕获参数 */
    tim_ic_init_handle.ICPolarity = TIM_ICPOLARITY_RISING;      /* 上升沿捕获 */
    tim_ic_init_handle.ICSelection = TIM_ICSELECTION_DIRECTTI; /* 映射到TI4上 */
    tim_ic_init_handle.ICPrescaler = TIM_ICPSC_DIV1;  			/* 不分频 */
    tim_ic_init_handle.ICFilter = 0x03;        			/* 8个定时器时钟周期滤波 */
HAL_TIM_IC_ConfigChannel(&g_tim4_handle, &tim_ic_init_handle, 
REMOTE_IN_TIMX_CHY);		/* 配置TIM4通道4 */
    HAL_TIM_IC_Start_IT(&g_tim4_handle, REMOTE_IN_TIMX_CHY);/* 开始捕获TIM通道 */
    __HAL_TIM_ENABLE_IT(&g_tim4_handle, TIM_IT_UPDATE);      /* 使能更新中断 */
}

/**
 * @brief      	定时器4底层驱动,时钟使能,引脚配置
 * @param       	htim:定时器句柄
 * @note        	此函数会被HAL_TIM_IC_Init()调用
 * @retval      	无
 */
void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == REMOTE_IN_TIMX)
    {
        GPIO_InitTypeDef gpio_init_struct;

        REMOTE_IN_GPIO_CLK_ENABLE();       	/* 红外接入引脚GPIO时钟使能 */
        REMOTE_IN_TIMX_CHY_CLK_ENABLE();  	/* 定时器时钟使能 */
/* 这里用的是PB9/TIM4_CH4,参考AFIO_MAPR寄存器的设置 */
        __HAL_AFIO_REMAP_TIM4_DISABLE();  

        gpio_init_struct.Pin = REMOTE_IN_GPIO_PIN;
        gpio_init_struct.Mode = GPIO_MODE_AF_INPUT;         		/* 复用输入 */
        gpio_init_struct.Pull = GPIO_PULLUP;                     	/* 上拉 */
        gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;         	/* 高速 */
        HAL_GPIO_Init(REMOTE_IN_GPIO_PORT, &gpio_init_struct);	/* 定时器通道引脚 */

/* 设置中断优先级,抢占优先级1,子优先级3 */
        HAL_NVIC_SetPriority(REMOTE_IN_TIMX_IRQn, 1, 3);
        HAL_NVIC_EnableIRQ(REMOTE_IN_TIMX_IRQn);                	/* 开启ITM4中断 */
    }
}

remote_init函数主要是对红外遥控使用到的定时器4和定时器通道4进行相关配置,关于定时器4通道4的IO放在回调函数HAL_TIM_IC_MspInit中初始化。
在remote_init函数中,通过调用HAL_TIM_IC_Init函数初始化定时器的ARR和PSC等参数;通过调用HAL_TIM_IC_ConfigChannel函数配置映射关系,滤波和分频等;最后调用HAL_TIM_IC_Start_IT和__HAL_TIM_ENABLE_IT分别使能捕获通道和使能定时器中断。
在HAL_TIM_IC_MspInit函数中主要通过HAL_GPIO_Init函数对定时器输入通道的GPIO口进行配置,最后还需要设置中断抢占优先级和响应优先级。
通过上面两个函数的配置后,定时器的输入捕获已经初始化完成,接下来我们还需要作一些接收处理,下面先介绍一下三个变量。

/* 遥控器接收状态
 * [7]  : 收到了引导码标志
 * [6]  : 得到了一个按键的所有信息
 * [5]  : 保留
 * [4]  : 标记上升沿是否已经被捕获
 * [3:0]: 溢出计时器
 */
uint8_t g_remote_sta = 0;
uint32_t g_remote_data = 0; 	/* 红外接收到的数据 */
uint8_t  g_remote_cnt = 0;  	/* 按键按下的次数 */

这三个变量用于辅助实现高电平的捕获。其中g_remote_sta是用来记录捕获状态,这个变量,我们把它当成一个寄存器来使用。对其各位进行定义,描述如下表所示:
g_remote_sta
bit7 bit6 bit5 bit4 Bit3~0
收到引导码 得到一个按键所有信息 保留 标记上升沿是否已经被捕获 溢出计时器
表40.3.2.1 g_remote_sta各位描述
变量g_remote_data用于存放红外接收到的数据,而g_remote_cnt是存放按键按下的次数。
下面开始看中断服务函数里面的逻辑程序,HAL_TIM_IRQHandler函数会调用下面两个回调函数,我们的逻辑代码就是放在回调函数里,函数的定义如下:

/**
 * @brief      	定时器输入捕获中断回调函数
 * @param      	htim:定时器句柄
 * @retval     	无
 */
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == REMOTE_IN_TIMX)
    {
        uint16_t dval;	/* 下降沿时计数器的值 */
        
        if (RDATA)    	/* 上升沿捕获 */
        {
            __HAL_TIM_SET_CAPTUREPOLARITY(&g_tim4_handle,REMOTE_IN_TIMX_CHY,
TIM_INPUTCHANNELPOLARITY_FALLING);	/* 设置为下降沿捕获 */
            __HAL_TIM_SET_COUNTER(&g_tim4_handle, 0);  	/* 清空定时器计数器值 */
            g_remote_sta |= 0X10;               			/* 标记上升沿已经被捕获 */
        }
        else           /* 下降沿捕获 */
        {   /* 读取CCR4也可以清CC4IF标志位 */
            dval = HAL_TIM_ReadCapturedValue(&g_tim4_handle, REMOTE_IN_TIMX_CHY);                
            __HAL_TIM_SET_CAPTUREPOLARITY(&g_tim4_handle, REMOTE_IN_TIMX_CHY, 
TIM_INPUTCHANNELPOLARITY_RISING);/* 配置TIM4通道4上升沿捕获 */

            if (g_remote_sta & 0X10)        		/* 完成一次高电平捕获 */
            {
                if (g_remote_sta & 0X80)    		/* 接收到了引导码 */
                {
                    if (dval > 300 && dval < 800)	/* 560为标准值,560us */
                    {
                        g_remote_data >>= 1;		/* 右移一位. */
                        g_remote_data &= ~0x80000000;	/* 接收到0 */
                    }
                    else if (dval > 1400 && dval < 1800)   /* 1680为标准值,1680us */
                    {
                        g_remote_data >>= 1;		/* 右移一位 */
                        g_remote_data |= 0x80000000;	/* 接收到1 */
                    }
                    else if (dval > 2000 && dval < 3000)   
                    {   /* 得到按键键值增加的信息 2250为标准值2.25ms */
                        g_remote_cnt++;         	/* 按键次数增加1次 */
                        g_remote_sta &= 0XF0; 	/* 清空计时器 */
                    }
                }
                else if (dval > 4200 && dval < 4700)    /* 4500为标准值4.5ms */
                {
                    g_remote_sta |= 1 << 7;	/* 标记成功接收到了引导码 */
                    g_remote_cnt = 0;        	/* 清除按键次数计数器 */
                }
            }
            g_remote_sta &= ~(1<<4);
        }
    }
}

现在我们来介绍一下,捕获高电平脉宽的思路:首先,设置TIM4_CH4捕获上升沿,然后等待上升沿中断到来,当捕获到上升沿中断,设置该通道为下降沿捕获,清除TIM4_CNT寄存器的值,最后把g_remote_sta的位4置1,表示已经捕获到高电平,等待下降沿到来。当下降沿到来的时候,读取此时定时器计数器的值到dval中并设置该通道为上升沿捕获,然后判断dval的值属于哪个类型(引导码,数据0,数据1或者重发码),相对应就把g_remote_sta相关位进行调整。例如,一开始识别为引导码的情况,就需要把g_remote_sta第7位置1。当检测到重复码,就把按键次数增量存放在g_remote_cnt变量中。

/**
 * @brief      	定时器更新中断回调函数
 * @param      	htim:定时器句柄
 * @retval     	无
 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == REMOTE_IN_TIMX)
    {
        if (g_remote_sta & 0x80)     			/* 上次有数据被接收到了 */
        {
            g_remote_sta &= ~0X10;   			/* 取消上升沿已经被捕获标记 */

            if ((g_remote_sta & 0X0F) == 0X00)
            {
                g_remote_sta |= 1 << 6; 		/* 标记已经完成一次按键的键值信息采集 */
            }
            
            if ((g_remote_sta & 0X0F) < 14)
            {
                g_remote_sta++;
            }
            else
            {
                g_remote_sta &= ~(1 << 7);  	/* 清空引导标识 */
                g_remote_sta &= 0XF0;        	/* 清空计数器 */
            }
        }
    }
}

定时器更新中断回调函数主要是对标志位进行管理。在函数内通过g_remote_sta标志的判断,主要思路就是:在接收到引导码的前提下,对g_remote_sta状态进行判断并在符合条件下进行运算,这里主要就做了两件事:标记完成一次按键信息采集和是否松开按键(即没有接收到数据)。当完成一次按键信息采集时,g_remote_data已经存放了控制反码、控制码、地址反码、地址码。那为啥可以检测是否可以松开按键?是因为接收到重发码的情况下会清空计数器,所以说当我们松开按键接收不到重发码时,溢出中断次数增多最终会导致g_remote_sta&0x0f值大于14,进而就可以把引导码,计数器清空,便于下一次的接收。

/**
 * @brief      	处理红外按键(类似按键扫描)
 * @param       	无
 * @retval     	0   , 没有任何按键按下
 *              	其他, 按下的按键键值
 */
uint8_t remote_scan(void)
{
    uint8_t sta = 0;
    uint8_t t1, t2;

    if (g_remote_sta & (1 << 6))           	/* 得到一个按键的所有信息了 */
    {
        t1 = g_remote_data;           			/* 得到地址码 */
        t2 = (g_remote_data >> 8) & 0xff; 	/* 得到地址反码 */

        if ((t1 == (uint8_t)~t2) && t1 == REMOTE_ID)   
        {   /* 检验遥控识别码(ID)及地址 */
            t1 = (g_remote_data >> 16) & 0xff;
            t2 = (g_remote_data >> 24) & 0xff;
            if (t1 == (uint8_t)~t2)
            {
                sta = t1;           			/* 键值正确 */
            }
        }

        if ((sta == 0) || ((g_remote_sta & 0X80) == 0)) 
        {   /* 按键数据错误/遥控已经没有按下了 */
            g_remote_sta &= ~(1 << 6);  		/* 清除接收到有效按键标识 */
            g_remote_cnt = 0;           		/* 清除按键次数计数器 */
        }
    }

    return sta;
}

remote_scan函数是用来扫描解码结果的,相当于我们的按键扫描,输入捕获解码的红外数据,通过该函数传送给其他程序。
2. main.c代码
在main.c里面编写如下代码:

int main(void)
{
    uint8_t key;
    uint8_t t = 0;
    char *str = 0;

    HAL_Init();                           		/* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9);		/* 设置时钟, 72Mhz */
    delay_init(72);                      		/* 延时初始化 */
    usart_init(115200);                		/* 串口初始化为115200 */
    led_init();                         		/* 初始化LED */
    lcd_init();                             		/* 初始化LCD */
remote_init();                         		/* 红外接收初始化 */

    lcd_show_string(30,  50, 200, 16, 16, "STM32", RED);
    lcd_show_string(30,  70, 200, 16, 16, "REMOTE TEST", RED);
    lcd_show_string(30,  90, 200, 16, 16, "ATOM@ALIENTEK", RED);
    lcd_show_string(30, 110, 200, 16, 16, "KEYVAL:", RED);
    lcd_show_string(30, 130, 200, 16, 16, "KEYCNT:", RED);
    lcd_show_string(30, 150, 200, 16, 16, "SYMBOL:", RED);

    while (1)
    {
        key = remote_scan();

        if (key)
        {
            lcd_show_num(86, 110, key, 3, 16, BLUE);          	/* 显示键值 */
            lcd_show_num(86, 130, g_remote_cnt, 3, 16, BLUE); 	/* 显示按键次数 */

            switch (key)
            {
                case 0:	str = "ERROR";		break;
                case 69: 	str = "POWER";      	break;
                case 70:  	str = "UP";          	break;
                case 64:  	str = "PLAY";       	break;
                case 71: 	str = "ALIENTEK";  	break;
                case 67: 	str = "RIGHT";      	break;
                case 68: 	str = "LEFT";       	break;
                case 7:	str = "VOL-";       	break;
                case 21: 	str = "DOWN";       	break;
                case 9:	str = "VOL+";       	break;
                case 22:	str = "1";           	break;
                case 25: 	str = "2";          	break;
                case 13: 	str = "3";          	break;
                case 12:  	str = "4";          	break;
                case 24:  	str = "5";          	break;
                case 94: 	str = "6";          	break;
                case 8:  	str = "7";          	break;
                case 28:  	str = "8";          	break;
                case 90:  	str = "9";          	break;
                case 66:  	str = "0";          	break;
                case 74:  	str = "DELETE";    	break;
            }

            lcd_fill(86, 150, 116 + 8 * 8, 170 + 16, WHITE);    /* 清楚之前的显示 */
            lcd_show_string(86, 150, 200, 16, 16, str, BLUE);   /* 显示SYMBOL */
        }
        else
        {
            delay_ms(10);
        }

        t++;

        if (t == 20)
        {
            t = 0;
            LED0_TOGGLE();  /* LED0闪烁 */
        }
    }
}

main函数代码比较简单,主要是通过remote_scan函数获得红外遥控输入的数据(控制码),然后显示在LCD上面。正点原子红外遥控器按键对应的控制码图如下图所示。
在这里插入图片描述

图40.3.2.1 红外遥控器按键对应的控制码图(十六进制数)
特别注意:上图中的控制码数值是十六进制的,而我们代码中使用的是十进制的表示方式。
此外,正点原子红外遥控器的地址码是0。
40.4 下载验证
将程序下载到开发板后,可以看到LED0不停的闪烁,提示程序已经在运行了。LCD显示的内容如图40.4.1所示:
在这里插入图片描述

图40.4.1 程序运行效果图
此时我们通过遥控器按下不同的按键,则可以看到LCD上显示了不同按键的键值以及按键次数和对应的遥控器上的符号。如图40.4.2所示。
在这里插入图片描述

图40.4.2 解码成功

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/685987.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

管理类联考——逻辑——知识篇——形式逻辑——二、性质模态——haimian

性质 考点分析 定义分类 思维导图 模态 考点分析 削弱 年度 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023题量112 定义分类 断定事物发生可能性的命题就是模态命题。 按照可能性的高低&#xff0c;我们可以将模态命题分为如下几类&#xff1a; ①必然发…

Android12 设置系统默认不休眠-迅为RK3588开发板

修改文件&#xff1a; device/rockchip/rk3588/overlay/frameworks/base/packages/SettingsProvider/res/values/defaults. xml 文件&#xff0c;如下图所示&#xff1a; - 60000 0x7fffffff 更多详情 公众Hao/B站&#xff1a;迅为电子

MFC第九天 CRect类的封装和窗口坐标系转换及获取窗口ID 以及对CTime类与COleDateTime类简介

文章目录 CRect类的封装、窗口坐标系转换及获取窗口ID 、CTime类与COleDateTime类简介CRect类的封装窗口坐标系转换及获取窗口IDCTime类与COleDateTime类简介关于设置日期控件上的时间问题 CRect类的封装、窗口坐标系转换及获取窗口ID 、CTime类与COleDateTime类简介 CRect类的…

300黑客共闯沙盒|赛宁数字化靶场助力第十六届全国大学生信息安全竞赛

6月24日&#xff0c;由中央网信办网络安全协调局指导、教育部高等学校网络空间安全专业教学指导委员会主办、福州大学承办的第十六届全国大学生信息安全竞赛—创新实践能力赛华东南分区选拔赛&#xff08;简称“分区赛”&#xff09;圆满结束。赛宁网安基于数字化靶场打造的“赛…

3、数仓之采集工具MaxWell(MaxWell简介、MaxWell原理、MaxWell部署、MaxWell使用)

1、Maxw简介 1.1 MaxWell概述 Maxwell 是由美国Zendesk公司开源&#xff0c;用Java编写的MySQL变更数据抓取软件。它会实时监控Mysql数据库的数据变更操作&#xff08;包括insert、update、delete&#xff09;&#xff0c;并将变更数据以 JSON 格式发送给 Kafka、Kinesi等流数…

100页干货!一文看懂10+行业领域发展趋势

导读&#xff1a; 当前全球经济环境及疫情的变化&#xff0c;使得中国各行业的发展呈现向好趋势&#xff0c;但市场仍充满诸多不确定性。 在今年伊始&#xff0c;罗兰贝格重磅发布了《“预见2023”中国行业趋势报告》&#xff08;文中简称《报告》&#xff09;&#xff0c;囊…

学生台灯怎么选对眼睛好的?看完再买不踩坑!

我们都知道&#xff0c;对眼睛最好的光就是自然的太阳光&#xff0c;但并不是每时每刻都能享受到太阳光般的光源&#xff0c;所以现在有很多台灯的出现&#xff0c;而护眼台灯是对眼睛最好的&#xff0c;不过护眼台灯的挑选也有一些需要注意的细节&#xff01; 1、全光谱&#…

Lancet Microbe -- 新冠轻症者可能是“超级传播者”

一项对有意感染SARS-CoV-2的人群的研究提供了关于病毒传播的丰富见解&#xff0c;比如&#xff0c;某些特定人群是“超级传播者”&#xff0c;他们向空气中释放的病毒远比其他人多。 该论文描述了一项具有争议的“挑战性研究”的数据&#xff0c;在这项研究中&#xff0c;科学家…

Sangfor华东天勇战队:AspectJWeaver反序列化利用链

依赖&#xff1a; <dependencies><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.2</version></dependency> </dependencies>测试类&#xff1a; import java…

基于Matlab实现SVM算法的手写字体识别(附上完整仿真源码+数据 )

手写字体识别是一个重要的人工智能应用领域。在本文中&#xff0c;我们将展示如何使用MATLAB实现手写数字的识别。 首先&#xff0c;我们需要准备一个手写数字数据集。在本文中&#xff0c;我们将使用MNIST数据集&#xff0c;这是一个广泛使用的手写数字数据集&#xff0c;包含…

界面组件DevExpress WinForm v23.1新版亮点 - 皮肤矢量图标全新升级

DevExpress WinForms拥有180组件和UI库&#xff0c;能为Windows Forms平台创建具有影响力的业务解决方案。DevExpress WinForms能完美构建流畅、美观且易于使用的应用程序&#xff0c;无论是Office风格的界面&#xff0c;还是分析处理大批量的业务数据&#xff0c;它都能轻松胜…

【机器学习】正则化对过拟合和欠拟合的影响

模型过拟合和欠拟合的图像特征 偏差大表示欠拟合&#xff0c;而方差大表示过拟合&#xff0c;我们这一节再深入探讨下过拟合和欠拟合问题。一个经典的图如下&#xff1a; 其中d1为欠拟合&#xff0c;d4为过拟合&#xff0c;而d2则刚刚好。回顾下刚刚说的使用训练集和交叉验证…

JavaScript知识点DOM 模型详细讲解

DOM 模型 DOM 全称是 Document Object Model 文档对象模型 大白话&#xff0c;就是把文档中的标签&#xff0c;属性&#xff0c;文本&#xff0c;转换成为对象来管理。 Document 对象 Document对象的理解&#xff1a; ​ 第一点&#xff1a;Document 它管理了所有的 HTML 文…

三次登录验证和验证码功能实现

三次登录验证和验证码功能实现 最近手头上的事忙的差不多了&#xff0c;就想着自己写写小demo玩一下&#xff0c;结果突然看到我们旧系统的登录好像有点拉胯&#xff0c;然后就自己写了个小demo&#xff0c;指不定哪天就用上了呢 一、pom文件 首先当然是pom文件啦&#xff0…

【JS】1724- 重学 JavaScript API - Drag and Drop API

❝ 前期回顾&#xff1a; 1. Page Visibility API 2. Broadcast Channel API 3. Beacon API 4. Resize Observer API 5. Clipboard API 6. Fetch API 7. Performance API 8. WebStorage API 9. WebSockets API 10. Fullscreen API 11. Geolocation API ❞ &#x1f3dd; 1. 快速…

ThinkPHP6.0 数据迁移工具 migration 入门使用教程

文章目录 安装数据库迁移工具创建迁移文件执行迁移回滚参考资料 开始前需要做好的准备工作&#xff1a; 搭建好 PHP 开发环境&#xff08;推荐 phpstudy&#xff0c;PHP>7.2.5&#xff0c;MySql5.7.x&#xff09;。安装好 ThinkPHP6.0&#xff0c;并做配置可正常连接到 MySq…

docker安装nginx,发布部署vue项目

场景 前后端项目&#xff0c;实现前后端简单部署到服务器。前端vue&#xff0c;后端springboot。服务器ubuntu&#xff08;18.04&#xff09;<linux系统同理>. 后端通过(nohup java -jar xxx.jar &) 指令简单部署。该文主要说明部署前端vue项目。 部署vue需要安装ng…

一文看懂51单片机和stm32区别,怎么用怎么学怎么选

一文看懂51单片机和stm32区别&#xff0c;怎么用怎么学怎么选 对于初学单片机的童鞋而言&#xff0c;开始会有这样的疑问&#xff1f;到底选哪个怎么选呢&#xff1f; 1、工业控制51优于stm&#xff1f; 2、没区别,51就是个入门级,不过也有贵的,我用的就是51,用的还可以&…

PG系列4:linux下编译安装PG15

文章目录 一. 源码安装1.1 下载并解压1.2 安装依赖包1.3 开始编译安装1.4 创建用户1.5 创建目录及修改权限1.6 设置环境变量1.7 初始化数据库1.8 启动和关闭数据库 二. 验证2.1 查看数据库后台进程2.2 验证和登陆数据库2.3 查看数据库版本2.4 查看数据库运行状态2.5 修改白名单…

Sangfor华东天勇战队:h2数据库console命令执行( CVE-2021-42392 漏洞)

漏洞版本 1.1.100 < H2 Console < 2.0.204 漏洞复现 此处复现版本1.4.197 启动项目如下 在Driver Class中输入javax.naming.InitialContext 在JDBCURL中输入jndi注入恶意链接 生成链接命令&#xff1a; java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C …