日后填坑。
无线通信模块
2.4G
基本介绍
以NRF24L01为例。
NRF24L01是一款2.4GHz的无线收发模块,支持SPI通信协议,具有低功耗、高数据速率(250kbps-2Mbps)和多设备通信能力。
它可以同时与最多6个其他模块通信,适合用于小型网络和远程控制应用
使用方法
- 初始化:配置SPI通信和GPIO引脚,设置工作模式(发送或接收)、地址、频率等参数。
- 检测模块:通过SPI写入已知数据,再读取并验证数据是否一致,判断模块是否存在。
- 发送数据:进入发送模式,写入数据到缓冲区,启动发送,等待发送完成并检查状态。
- 接收数据:进入接收模式,等待接收完成,读取数据并检查状态。
- 模式切换:通过CE引脚控制模块在发送和接收模式之间切换。
日后填坑。
注意:
数据通道0 是唯一的一个可以配置为40 位自身地址的数据通道。1~5 数据通道都为8 位自身地址和32 位公用地址。
示例代码
基于STM32 HAL库
.c文件
extern SPI_HandleTypeDef g_spi2_handler; /* SPI2句柄 */
//const uint8_t TX_ADDRESS[TX_ADR_WIDTH] = {0x34, 0x43, 0x10, 0x10, 0x01}; /* 发送地址 */
//const uint8_t RX_ADDRESS[RX_ADR_WIDTH] = {0x34, 0x43, 0x10, 0x10, 0x01}; /* 接收地址 *///这里设置是通道0 因此地址可以自己定义
const uint8_t TX_ADDRESS[TX_ADR_WIDTH] = {0x11, 0x11, 0x66, 0x66, 0x00}; /* 发送地址 */
const uint8_t RX_ADDRESS[RX_ADR_WIDTH] = {0x11, 0x11, 0x66, 0x66, 0x00}; /* 接收地址 *//*** @brief 针对NRF24L01修改SPI2驱动* @param 无* @retval 无*/
void nrf24l01_spi_init(void)
{__HAL_SPI_DISABLE(&g_spi2_handler); /* 先关闭SPI2 */g_spi2_handler.Init.CLKPolarity = SPI_POLARITY_LOW; /* 串行同步时钟的空闲状态为低电平 */g_spi2_handler.Init.CLKPhase = SPI_PHASE_1EDGE; /* 串行同步时钟的第1个跳变沿(上升或下降)数据被采样 */HAL_SPI_Init(&g_spi2_handler);__HAL_SPI_ENABLE(&g_spi2_handler); /* 使能SPI2 */
}/*** @brief 初始化24L01的IO口* @note 将SPI2模式改成SCK空闲低电平,及SPI 模式0* @param 无* @retval 无*/
void nrf24l01_init(void)
{GPIO_InitTypeDef gpio_init_struct;NRF24L01_CE_GPIO_CLK_ENABLE(); /* CE脚时钟使能 */NRF24L01_CSN_GPIO_CLK_ENABLE(); /* CSN脚时钟使能 */NRF24L01_IRQ_GPIO_CLK_ENABLE(); /* IRQ脚时钟使能 */gpio_init_struct.Pin = NRF24L01_CE_GPIO_PIN;gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽输出 */gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */HAL_GPIO_Init(NRF24L01_CE_GPIO_PORT, &gpio_init_struct); /* 初始化CE引脚 */gpio_init_struct.Pin = NRF24L01_CSN_GPIO_PIN;HAL_GPIO_Init(NRF24L01_CSN_GPIO_PORT, &gpio_init_struct);/* 初始化CSN引脚 */gpio_init_struct.Pin = NRF24L01_IRQ_GPIO_PIN;gpio_init_struct.Mode = GPIO_MODE_INPUT; /* 输入 */gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */HAL_GPIO_Init(NRF24L01_IRQ_GPIO_PORT, &gpio_init_struct);/* 初始化CE引脚 */spi2_init(); /* 初始化SPI2 */nrf24l01_spi_init(); /* 针对NRF的特点修改SPI的设置 */NRF24L01_CE(0); /* 使能24L01 */NRF24L01_CSN(1); /* SPI片选取消 */
}/*** @brief 检测24L01是否存在* @param 无* @retval 0, 成功; 1, 失败;*/
uint8_t nrf24l01_check(void)
{uint8_t buf[5] = {0XA5, 0XA5, 0XA5, 0XA5, 0XA5};uint8_t i;spi2_set_speed(SPI_SPEED_32); /* spi速度为7.5Mhz(24L01的最大SPI时钟为10Mhz) */nrf24l01_write_buf(NRF_WRITE_REG + TX_ADDR, buf, 5); /* 写入5个字节的地址. */nrf24l01_read_buf(TX_ADDR, buf, 5); /* 读出写入的地址 */for (i = 0; i < 5; i++){if (buf[i] != 0XA5) break;}if (i != 5) return 1; /* 检测24L01错误 */return 0; /* 检测到24L01 */
}/*** @brief NRF24L01写寄存器* @param reg : 寄存器地址* @param value : 写入寄存器的值* @retval 状态寄存器值*/
static uint8_t nrf24l01_write_reg(uint8_t reg, uint8_t value)
{uint8_t status;NRF24L01_CSN(0); /* 使能SPI传输 */status = spi2_read_write_byte(reg); /* 发送寄存器号 */spi2_read_write_byte(value); /* 写入寄存器的值 */NRF24L01_CSN(1); /* 禁止SPI传输 */return status; /* 返回状态值 */
}/*** @brief NRF24L01读寄存器* @param reg : 寄存器地址* @retval 读取到的寄存器值;*/
static uint8_t nrf24l01_read_reg(uint8_t reg)
{uint8_t reg_val;NRF24L01_CSN(0); /* 使能SPI传输 */spi2_read_write_byte(reg); /* 发送寄存器号 */reg_val = spi2_read_write_byte(0XFF); /* 读取寄存器内容 */NRF24L01_CSN(1); /* 禁止SPI传输 */return reg_val; /* 返回状态值 */
}/*** @brief 在指定位置读出指定长度的数据* @param reg : 寄存器地址* @param pbuf : 数据指针* @param len : 数据长度* @retval 状态寄存器值*/
static uint8_t nrf24l01_read_buf(uint8_t reg, uint8_t *pbuf, uint8_t len)
{uint8_t status, i;NRF24L01_CSN(0); /* 使能SPI传输 */status = spi2_read_write_byte(reg); /* 发送寄存器值(位置),并读取状态值 */for (i = 0; i < len; i++){pbuf[i] = spi2_read_write_byte(0XFF); /* 读出数据 */}NRF24L01_CSN(1); /* 关闭SPI传输 */return status; /* 返回读到的状态值 */
}/*** @brief 在指定位置写指定长度的数据* @param reg : 寄存器地址* @param pbuf : 数据指针* @param len : 数据长度* @retval 状态寄存器值*/
static uint8_t nrf24l01_write_buf(uint8_t reg, uint8_t *pbuf, uint8_t len)
{uint8_t status, i;NRF24L01_CSN(0); /* 使能SPI传输 */status = spi2_read_write_byte(reg);/* 发送寄存器值(位置),并读取状态值 */for (i = 0; i < len; i++){spi2_read_write_byte(*pbuf++); /* 写入数据 */}NRF24L01_CSN(1); /* 关闭SPI传输 */return status; /* 返回读到的状态值 */
}/*** @brief 启动NRF24L01发送一次数据(数据长度 = TX_PLOAD_WIDTH)* @param ptxbuf : 待发送数据首地址* @retval 发送完成状态* @arg 0 : 发送成功* @arg 1 : 达到最大发送次数,失败* @arg 0XFF : 其他错误*/
uint8_t nrf24l01_tx_packet(uint8_t *ptxbuf)
{uint8_t sta;uint8_t rval = 0XFF;NRF24L01_CE(0);nrf24l01_write_buf(WR_TX_PLOAD, ptxbuf, TX_PLOAD_WIDTH); /* 写数据到TX BUF TX_PLOAD_WIDTH个字节 */NRF24L01_CE(1); /* 启动发送 */while (NRF24L01_IRQ != 0); /* 等待发送完成 */sta = nrf24l01_read_reg(STATUS); /* 读取状态寄存器的值 */nrf24l01_write_reg(NRF_WRITE_REG + STATUS, sta); /* 清除TX_DS或MAX_RT中断标志 */if (sta & MAX_TX) /* 达到最大重发次数 */{nrf24l01_write_reg(FLUSH_TX, 0xff); /* 清除TX FIFO寄存器 */rval = 1;}if (sta & TX_OK)/* 发送完成 */{rval = 0; /* 标记发送成功 */}return rval; /* 返回结果 */
}/*** @brief 启动NRF24L01接收一次数据(数据长度 = RX_PLOAD_WIDTH)* @param prxbuf : 接收数据缓冲区首地址* @retval 接收完成状态* @arg 0 : 接收成功* @arg 1 : 失败*/
uint8_t nrf24l01_rx_packet(uint8_t *prxbuf)
{uint8_t sta;uint8_t rval = 1;sta = nrf24l01_read_reg(STATUS); /* 读取状态寄存器的值 */nrf24l01_write_reg(NRF_WRITE_REG + STATUS, sta); /* 清除RX_OK中断标志 */if (sta & RX_OK) /* 接收到数据 */{nrf24l01_read_buf(RD_RX_PLOAD, prxbuf, RX_PLOAD_WIDTH); /* 读取数据 */nrf24l01_write_reg(FLUSH_RX, 0xff); /* 清除RX FIFO寄存器 */rval = 0; /* 标记接收完成 */}return rval; /* 返回结果 */
}/*** @brief NRF24L01进入接收模式* @note 设置RX地址,写RX数据宽度,选择RF频道,波特率* 当CE变高后,即进入RX模式,并可以接收数据了* @param 无* @retval 无*/
void nrf24l01_rx_mode(void)
{NRF24L01_CE(0);nrf24l01_write_buf(NRF_WRITE_REG + RX_ADDR_P0, (uint8_t *)RX_ADDRESS, RX_ADR_WIDTH); /* 写RX节点地址 */nrf24l01_write_reg(NRF_WRITE_REG + EN_AA, 0x01); /* 使能通道0的自动应答 */nrf24l01_write_reg(NRF_WRITE_REG + EN_RXADDR, 0x01); /* 使能通道0的接收地址 */nrf24l01_write_reg(NRF_WRITE_REG + RF_CH, 40); /* 设置RF通信频率 */nrf24l01_write_reg(NRF_WRITE_REG + RX_PW_P0, RX_PLOAD_WIDTH); /* 选择通道0的有效数据宽度 */nrf24l01_write_reg(NRF_WRITE_REG + RF_SETUP, 0x0f); /* 设置TX发射参数,0db增益,2Mbps */nrf24l01_write_reg(NRF_WRITE_REG + CONFIG, 0x0f); /* 配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式 */NRF24L01_CE(1); /* CE为高,进入接收模式 */
}/*** @brief NRF24L01进入发送模式* @note 设置TX地址,写TX数据宽度,设置RX自动应答的地址,填充TX发送数据,选择RF频道,波特率和* PWR_UP,CRC使能* 当CE变高后,即进入TX模式,并可以发送数据了, CE为高大于10us,则启动发送.* @param 无* @retval 无*/
void nrf24l01_tx_mode(void)
{NRF24L01_CE(0);nrf24l01_write_buf(NRF_WRITE_REG + TX_ADDR, (uint8_t *)TX_ADDRESS, TX_ADR_WIDTH); /* 写TX节点地址 */nrf24l01_write_buf(NRF_WRITE_REG + RX_ADDR_P0, (uint8_t *)RX_ADDRESS, RX_ADR_WIDTH); /* 设置RX节点地址,主要为了使能ACK */nrf24l01_write_reg(NRF_WRITE_REG + EN_AA, 0x01); /* 使能通道0的自动应答 */nrf24l01_write_reg(NRF_WRITE_REG + EN_RXADDR, 0x01); /* 使能通道0的接收地址 */nrf24l01_write_reg(NRF_WRITE_REG + SETUP_RETR, 0x1a); /* 设置自动重发间隔时间:500us + 86us;最大自动重发次数:10次 */nrf24l01_write_reg(NRF_WRITE_REG + RF_CH, 40); /* 设置RF通道为40 */nrf24l01_write_reg(NRF_WRITE_REG + RF_SETUP, 0x0f); /* 设置TX发射参数,0db增益,2Mbps */nrf24l01_write_reg(NRF_WRITE_REG + CONFIG, 0x0e); /* 配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式,开启所有中断 */NRF24L01_CE(1); /* CE为高,10us后启动发送 */
}
.h文件
#ifndef __24L01_H
#define __24L01_H#include "./SYSTEM/sys/sys.h"/******************************************************************************************/
/* NRF24L01 操作引脚 定义(不包含SPI_SCK/MISO/MISO等三根线) */#define NRF24L01_CE_GPIO_PORT GPIOG
#define NRF24L01_CE_GPIO_PIN GPIO_PIN_8
#define NRF24L01_CE_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOG_CLK_ENABLE(); }while(0) /* PG口时钟使能 */#define NRF24L01_CSN_GPIO_PORT GPIOG
#define NRF24L01_CSN_GPIO_PIN GPIO_PIN_7
#define NRF24L01_CSN_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOG_CLK_ENABLE(); }while(0) /* PE口时钟使能 */#define NRF24L01_IRQ_GPIO_PORT GPIOG
#define NRF24L01_IRQ_GPIO_PIN GPIO_PIN_6
#define NRF24L01_IRQ_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOG_CLK_ENABLE(); }while(0) /* PG口时钟使能 *//******************************************************************************************//* 24L01操作线 */
#define NRF24L01_CE(x) do{ x ? \HAL_GPIO_WritePin(NRF24L01_CE_GPIO_PORT, NRF24L01_CE_GPIO_PIN, GPIO_PIN_SET) : \HAL_GPIO_WritePin(NRF24L01_CE_GPIO_PORT, NRF24L01_CE_GPIO_PIN, GPIO_PIN_RESET); \}while(0) /* 24L01模式选择信号 */#define NRF24L01_CSN(x) do{ x ? \HAL_GPIO_WritePin(NRF24L01_CSN_GPIO_PORT, NRF24L01_CSN_GPIO_PIN, GPIO_PIN_SET) : \HAL_GPIO_WritePin(NRF24L01_CSN_GPIO_PORT, NRF24L01_CSN_GPIO_PIN, GPIO_PIN_RESET); \}while(0) /* 24L01片选信号 */#define NRF24L01_IRQ HAL_GPIO_ReadPin(NRF24L01_IRQ_GPIO_PORT, NRF24L01_IRQ_GPIO_PIN) /* IRQ主机数据输入 *//* 24L01发送接收数据宽度定义 * 用户必须根据实际情况设置正确的数据宽度和数据长度* 发送端&接收端必须保持一致, 否则将导致通信失败!!!!*/
#define TX_ADR_WIDTH 5 /* 5字节的地址宽度 */
#define RX_ADR_WIDTH 5 /* 5字节的地址宽度 */
#define TX_PLOAD_WIDTH 32 /* 32字节的用户数据宽度 */
#define RX_PLOAD_WIDTH 32 /* 32字节的用户数据宽度 *//******************************************************************************************/
/* NRF24L01寄存器操作命令 */
#define NRF_READ_REG 0x00 /* 读配置寄存器,低5位为寄存器地址 */
#define NRF_WRITE_REG 0x20 /* 写配置寄存器,低5位为寄存器地址 */
#define RD_RX_PLOAD 0x61 /* 读RX有效数据,1~32字节 */
#define WR_TX_PLOAD 0xA0 /* 写TX有效数据,1~32字节 */
#define FLUSH_TX 0xE1 /* 清除TX FIFO寄存器.发射模式下用 */
#define FLUSH_RX 0xE2 /* 清除RX FIFO寄存器.接收模式下用 */
#define REUSE_TX_PL 0xE3 /* 重新使用上一包数据,CE为高,数据包被不断发送. */
#define NOP 0xFF /* 空操作,可以用来读状态寄存器 *//* SPI(NRF24L01)寄存器地址 */
#define CONFIG 0x00 /* 配置寄存器地址;bit0:1接收模式,0发射模式;bit1:电选择;bit2:CRC模式;bit3:CRC使能; *//* bit4:中断MAX_RT(达到最大重发次数中断)使能;bit5:中断TX_DS使能;bit6:中断RX_DR使能 */
#define EN_AA 0x01 /* 使能自动应答功能 bit0~5,对应通道0~5 */
#define EN_RXADDR 0x02 /* 接收地址允许,bit0~5,对应通道0~5 */
#define SETUP_AW 0x03 /* 设置地址宽度(所有数据通道):bit1,0:00,3字节;01,4字节;02,5字节; */
#define SETUP_RETR 0x04 /* 建立自动重发;bit3:0,自动重发计数器;bit7:4,自动重发延时 250*x+86us */
#define RF_CH 0x05 /* RF通道,bit6:0,工作通道频率; */
#define RF_SETUP 0x06 /* RF寄存器;bit3:传输速率(0:1Mbps,1:2Mbps);bit2:1,发射功率;bit0:低噪声放大器增益 */
#define STATUS 0x07 /* 状态寄存器;bit0:TX FIFO满标志;bit3:1,接收数据通道号(最大:6);bit4,达到最多次重发 *//* bit5:数据发送完成中断;bit6:接收数据中断; */
#define MAX_TX 0x10 /* 达到最大发送次数中断 */
#define TX_OK 0x20 /* TX发送完成中断 */
#define RX_OK 0x40 /* 接收到数据中断 */#define OBSERVE_TX 0x08 /* 发送检测寄存器,bit7:4,数据包丢失计数器;bit3:0,重发计数器 */
#define CD 0x09 /* 载波检测寄存器,bit0,载波检测; */
#define RX_ADDR_P0 0x0A /* 数据通道0接收地址,最大长度5个字节,低字节在前 */
#define RX_ADDR_P1 0x0B /* 数据通道1接收地址,最大长度5个字节,低字节在前 */
#define RX_ADDR_P2 0x0C /* 数据通道2接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等; */
#define RX_ADDR_P3 0x0D /* 数据通道3接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等; */
#define RX_ADDR_P4 0x0E /* 数据通道4接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等; */
#define RX_ADDR_P5 0x0F /* 数据通道5接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等; */
#define TX_ADDR 0x10 /* 发送地址(低字节在前),ShockBurstTM模式下,RX_ADDR_P0与此地址相等 */
#define RX_PW_P0 0x11 /* 接收数据通道0有效数据宽度(1~32字节),设置为0则非法 */
#define RX_PW_P1 0x12 /* 接收数据通道1有效数据宽度(1~32字节),设置为0则非法 */
#define RX_PW_P2 0x13 /* 接收数据通道2有效数据宽度(1~32字节),设置为0则非法 */
#define RX_PW_P3 0x14 /* 接收数据通道3有效数据宽度(1~32字节),设置为0则非法 */
#define RX_PW_P4 0x15 /* 接收数据通道4有效数据宽度(1~32字节),设置为0则非法 */
#define RX_PW_P5 0x16 /* 接收数据通道5有效数据宽度(1~32字节),设置为0则非法 */
#define NRF_FIFO_STATUS 0x17 /* FIFO状态寄存器;bit0,RX FIFO寄存器空标志;bit1,RX FIFO满标志;bit2,3,保留 *//* bit4,TX FIFO空标志;bit5,TX FIFO满标志;bit6,1,循环发送上一数据包.0,不循环; */
/******************************************************************************************//* 函数申明 */
static uint8_t nrf24l01_write_buf(uint8_t reg, uint8_t *pbuf, uint8_t uint8_ts); /* 写数据区 */
static uint8_t nrf24l01_read_buf(uint8_t reg, uint8_t *pbuf, uint8_t uint8_ts); /* 读数据区 */void nrf24l01_spi_init(void); /* 针对NRF24L01修改SPI2驱动 */
void nrf24l01_init(void); /* 初始化 */
void nrf24l01_rx_mode(void); /* 配置为接收模式 */
void nrf24l01_tx_mode(void); /* 配置为发送模式 */
uint8_t nrf24l01_check(void); /* 检查24L01是否存在 */
uint8_t nrf24l01_tx_packet(uint8_t *ptxbuf); /* 发送一个包的数据 */
uint8_t nrf24l01_rx_packet(uint8_t *prxbuf); /* 接收一个包的数据 */#endif
蓝牙模块
基本介绍
使用方法
示例代码
WIFI模块
基本介绍
以ESP8266为例。
使用方法
示例代码
红外遥控模块
基本介绍
红外遥控的基本概念
红外遥控是一种通过红外线进行无线通信的技术,广泛应用于遥控器、智能家居设备等。红外遥控器通过发射红外信号来控制设备,而接收端(如STM32)通过红外接收模块解码这些信号。
红外遥控的工作原理
-
发射端:遥控器将按键信息编码为特定的红外信号(通常是调制的载波信号,如38kHz)。
-
接收端:红外接收模块(如TSOP系列)接收红外信号,并将其转换为电信号。
-
解码:STM32通过定时器捕获信号的上升沿和下降沿,计算时间差来解析信号。
简单理解:通过判断IO的高电平的时间来确定是逻辑0还是逻辑1。
使用方法
接收顺序:引导码、地址码、地址反码、控制码、控制反码
- 初始化定时器4,设置输入捕获参数,包括捕获极性、输入通道选择、输入分频和滤波器设置。
- 在定时器中断回调函数中,更新接收状态,检查是否完成按键信息采集,并根据计数器的值更新状态。
- 在输入捕获中断回调函数中,捕获上升沿和下降沿的时间差,判断信号类型(引导码、0、1或按键重复信息),更新接收数据和状态。
- 在按键扫描函数中,验证接收到的数据,包括地址码和数据码的正确性,返回按键值。
示例代码
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; /* IC1F=0003 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(); /* 定时器时钟使能 */__HAL_AFIO_REMAP_TIM4_DISABLE(); /* 这里用的是PB9/TIM4_CH4,参考AFIO_MAPR寄存器的设置 */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); /* 初始化定时器通道引脚 */HAL_NVIC_SetPriority(REMOTE_IN_TIMX_IRQn, 1, 3); /* 设置中断优先级,抢占优先级1,子优先级3 */HAL_NVIC_EnableIRQ(REMOTE_IN_TIMX_IRQn); /* 开启ITM4中断 */}}/* 遥控器接收状态* [7] : 收到了引导码标志* [6] : 得到了一个按键的所有信息* [5] : 保留* [4] : 标记上升沿是否已经被捕获* [3:0]: 溢出计时器*/
uint8_t g_remote_sta = 0;
uint32_t g_remote_data = 0; /* 红外接收到的数据 */
uint8_t g_remote_cnt = 0; /* 按键按下的次数 *//*** @brief 定时器中断服务函数* @param 无* @retval 无*/
void REMOTE_IN_TIMX_IRQHandler(void)
{HAL_TIM_IRQHandler(&g_tim4_handle); /* 定时器共用处理函数 */
}/*** @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; /* 清空计数器 */}}}
}/*** @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);//CC4P=1 设置为下降沿捕获__HAL_TIM_SET_COUNTER(&g_tim4_handle, 0); /* 清空定时器值 */g_remote_sta |= 0X10; /* 标记上升沿已经被捕获 */}else /* 下降沿捕获 */{dval=HAL_TIM_ReadCapturedValue(&g_tim4_handle, REMOTE_IN_TIMX_CHY); /* 读取CCR4也可以清CC4IF标志位 */__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) /* 得到按键键值增加的信息 2500为标准值2.5ms */{//这里可理解为长按状态,遥控器持续发送一个特定波形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);}}
}/*** @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;//提取 g_remote_data 的第 16 到第 23 位(即第 3 个字节)。t2 = (g_remote_data >> 24) & 0xff;//提取 g_remote_data 的第 24 到第 31 位(即第 4 个字节)。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;
}