日后填坑。
無線通信模塊
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;
}